emmental/emmental/tracklist/__init__.py

162 lines
6.6 KiB
Python

# Copyright 2022 (c) Anna Schumaker.
"""A card for displaying a list of tracks."""
from gi.repository import GObject
from gi.repository import GLib
from gi.repository import Gio
from gi.repository import Gtk
from ..action import ActionEntry
from ..playlist.playlist import Playlist
from ..playlist.previous import Previous
from .. import db
from .. import entry
from . import buttons
from . import footer
from . import selection
from . import trackview
class Card(Gtk.Box):
"""Our Tracklist."""
sql = GObject.Property(type=db.Connection)
def __init__(self, sql: db.Connection, **kwargs):
"""Set up the Tracklist widget."""
super().__init__(sql=sql, orientation=Gtk.Orientation.VERTICAL,
**kwargs)
self._top_left = Gtk.Box()
self._top_right = Gtk.Box(sensitive=False)
self._top_box = Gtk.CenterBox(margin_start=6, margin_end=6)
self._filter = entry.Filter("tracks", hexpand=True,
margin_start=100, margin_end=100)
self._trackview = trackview.TrackView(sql)
self._osd = selection.OSD(sql, self._trackview.selection_model,
child=self._trackview)
self._visible_cols = buttons.VisibleColumns(self._trackview.columns)
self._unselect = Gtk.Button(icon_name="edit-select-none-symbolic",
tooltip_text="unselect all tracks",
has_frame=False, sensitive=False)
self._loop = buttons.LoopButton()
self._shuffle = buttons.ShuffleButton()
self._sort = buttons.SortButton()
self._footer = footer.Footer(margin_start=6, margin_end=6,
margin_top=6, margin_bottom=6)
self._top_left.append(self._visible_cols)
self._top_left.append(self._unselect)
self._top_right.append(self._loop)
self._top_right.append(self._shuffle)
self._top_right.append(self._sort)
self._top_box.set_start_widget(self._top_left)
self._top_box.set_center_widget(self._filter)
self._top_box.set_end_widget(self._top_right)
self.append(self._top_box)
self.append(Gtk.Separator())
self.append(self._osd)
self.append(Gtk.Separator())
self.append(self._footer)
self._osd.bind_property("have-selected", self._trackview,
"have-selected")
self._osd.bind_property("n-selected", self._trackview, "n-selected")
self._trackview.bind_property("n-tracks", self._footer, "count")
self._trackview.bind_property("n-selected", self._footer, "selected")
self._trackview.bind_property("runtime", self._footer, "runtime")
self._trackview.bind_property("have-selected", self._unselect,
"sensitive")
self._filter.connect("search-changed", self.__search_changed)
self._unselect.connect("clicked", self._osd.clear_selection)
self._loop.connect("notify::state", self.__update_loop_state)
self._shuffle.connect("notify::active", self.__update_shuffle_state)
self._sort.connect("notify::sort-order", self.__update_sort_order)
self._top_box.add_css_class("toolbar")
self.add_css_class("card")
def __playlist_notify(self, playlist: Playlist, param) -> None:
match param.name:
case "loop":
self._loop.state = playlist.loop
case "playlist":
self.__set_button_state()
case "shuffle":
self._shuffle.active = playlist.shuffle
case "sort-order":
self._sort.sort_order = playlist.sort_order
def __set_button_state(self) -> None:
can_disable = self.playlist.playlist != self.sql.playlists.collection
self._loop.can_disable = can_disable
self._loop.state = self.playlist.loop
self._shuffle.active = self.playlist.shuffle
self._sort.set_sort_order(self.playlist.sort_order)
self._osd.reset()
def __update_loop_state(self, loop: buttons.LoopButton, param) -> None:
if self.playlist.loop != loop.state:
self.playlist.loop = loop.state
def __update_shuffle_state(self, shuffle: buttons.ShuffleButton,
param) -> None:
if self.playlist.shuffle != shuffle.active:
self.playlist.shuffle = shuffle.active
def __update_sort_order(self, sort: buttons.SortButton, param) -> None:
if self.playlist.sort_order != sort.sort_order:
self.playlist.sort_order = sort.sort_order
def __search_changed(self, filter: entry.Filter) -> None:
self.sql.tracks.filter(filter.get_query())
def __scroll_idle(self, track: db.tracks.Track) -> bool:
self._trackview.scroll_to_track(track)
return GLib.SOURCE_REMOVE
def scroll_to_track(self, track: db.tracks.Track) -> None:
"""Scroll to the requested Track."""
if self.playlist is not None:
GLib.idle_add(self.__scroll_idle, track)
@GObject.Property(type=Gio.ListModel)
def columns(self) -> Gio.ListModel:
"""Get the columns displayed in the Tracklist."""
return self._trackview.columns
@GObject.Property(type=Playlist)
def playlist(self) -> Playlist:
"""Get the currently visible Playlist."""
return self._trackview.playlist
@playlist.setter
def playlist(self, newval: Playlist) -> None:
if self._trackview.playlist is not None:
self._trackview.playlist.disconnect_by_func(self.__playlist_notify)
self._trackview.playlist = newval
self._osd.playlist = newval
if newval is not None:
self._top_right.set_sensitive(not isinstance(newval, Previous))
self.__set_button_state()
newval.connect("notify", self.__playlist_notify)
@property
def accelerators(self) -> list[ActionEntry]:
"""Get a list of accelerators for the Tracklist."""
return [ActionEntry("focus-search-track", self._filter.grab_focus,
"<Control>slash"),
ActionEntry("clear-selected-tracks", self._unselect.activate,
"Escape", enabled=(self._unselect, "sensitive")),
ActionEntry("cycle-loop", self._loop.activate,
"<Control>l", enabled=(self._top_right,
"sensitive")),
ActionEntry("toggle-shuffle", self._shuffle.activate,
"<Control>s", enabled=(self._top_right,
"sensitive"))] + \
self._osd.accelerators