149 lines
5.9 KiB
Python
149 lines
5.9 KiB
Python
# Copyright 2022 (c) Anna Schumaker.
|
|
"""A sidebar Header attached to a hidden ListView for selecting playlists."""
|
|
import typing
|
|
from gi.repository import GObject
|
|
from gi.repository import Gtk
|
|
from .. import db
|
|
from .. import factory
|
|
from . import header
|
|
from . import row
|
|
|
|
|
|
class Section(header.Header):
|
|
"""A widget for displaying a group of playlists with a header."""
|
|
|
|
table = GObject.Property(type=db.playlist.Table)
|
|
|
|
def __init__(self, table: db.playlist.Table,
|
|
row_type: typing.Type[row.TreeRow], **kwargs):
|
|
"""Set up our sidebar Section."""
|
|
super().__init__(table=table, pending=table.queue.running, **kwargs)
|
|
self._selection = Gtk.SingleSelection(model=self.table.treemodel,
|
|
autoselect=False)
|
|
self._factory = factory.Factory(row_type=row_type)
|
|
self._listview = Gtk.ListView.new(self._selection, self._factory)
|
|
|
|
self.reveal_widget = Gtk.ScrolledWindow(child=self._listview)
|
|
self.reveal_widget.set_policy(Gtk.PolicyType.NEVER,
|
|
Gtk.PolicyType.AUTOMATIC)
|
|
|
|
table.queue.bind_property("running", self, "pending")
|
|
table.queue.bind_property("progress", self, "progress")
|
|
|
|
table.treemodel.connect("items-changed", self.__model_items_changed)
|
|
self._selection.connect("selection-changed", self.__selection_changed)
|
|
self._listview.connect("activate", self.__activated)
|
|
|
|
self._listview.add_css_class("navigation-sidebar")
|
|
self.add_css_class("emmental-sidebar-section")
|
|
|
|
def __activated(self, view: Gtk.ListView, position: int) -> None:
|
|
if playlist := self.__get_playlist(position):
|
|
self.emit("playlist-activated", playlist)
|
|
|
|
def __get_playlist(self, index: int) -> db.playlist.Playlist | None:
|
|
if item := self._selection.get_item(index):
|
|
return item.get_item()
|
|
|
|
def __model_items_changed(self, model: Gtk.TreeModel, position: int,
|
|
removed: int, added: int) -> None:
|
|
self.subtitle = self.do_get_subtitle(len(model))
|
|
|
|
def __selection_changed(self, model: Gtk.SingleSelection,
|
|
position: int, n_items: int) -> None:
|
|
if (index := model.get_selected()) != Gtk.INVALID_LIST_POSITION:
|
|
self.emit("playlist-selected", self.__get_playlist(index))
|
|
|
|
def do_get_subtitle(self, n_items: int) -> str:
|
|
"""Return a new subtitle for the section."""
|
|
raise NotImplementedError
|
|
|
|
def clear_selection(self) -> None:
|
|
"""Clear the selected playlist."""
|
|
self._selection.set_selected(Gtk.INVALID_LIST_POSITION)
|
|
|
|
def playlist_index(self, playlist: db.playlist.Playlist) -> int | None:
|
|
"""Find the index of a specific playlist in the tree."""
|
|
if playlist.parent is None:
|
|
index = -1
|
|
depth = 0
|
|
else:
|
|
index = self.playlist_index(playlist.parent)
|
|
parent = self._selection.get_item(index)
|
|
parent.set_expanded(True)
|
|
depth = parent.get_depth() + 1
|
|
|
|
for index in range(index + 1, len(self._selection)):
|
|
item = self._selection.get_item(index)
|
|
if item.get_depth() > depth:
|
|
continue
|
|
if item.get_depth() < depth:
|
|
break
|
|
if item.get_item() == playlist:
|
|
return index
|
|
|
|
def select_playlist(self, playlist: db.playlist.Playlist) -> None:
|
|
"""Select the requested playlist."""
|
|
if (index := self.playlist_index(playlist)) is not None:
|
|
self._listview.scroll_to(index, Gtk.ListScrollFlags.SELECT)
|
|
|
|
@GObject.Signal(arg_types=(db.playlist.Playlist,))
|
|
def playlist_activated(self, playlist: db.playlist.Playlist):
|
|
"""Signal that a playlist has been activated."""
|
|
|
|
@GObject.Signal(arg_types=(db.playlist.Playlist,))
|
|
def playlist_selected(self, playlist: db.playlist.Playlist):
|
|
"""Signal that the selected playlist has changed."""
|
|
|
|
|
|
class View(Gtk.Box):
|
|
"""A widget for displaying a group of sections."""
|
|
|
|
sql = GObject.Property(type=db.Connection)
|
|
current = GObject.Property(type=Section)
|
|
selected_section = GObject.Property(type=Section)
|
|
selected_playlist = GObject.Property(type=db.playlist.Playlist)
|
|
|
|
def __init__(self, sql: db.Connection):
|
|
"""Initialize a Section View."""
|
|
super().__init__(sql=sql, orientation=Gtk.Orientation.VERTICAL)
|
|
self._sections = []
|
|
|
|
def __on_active(self, section: Section, param: GObject.ParamSpec) -> None:
|
|
if section.active:
|
|
new = self._sections.index(section)
|
|
prev = len(self._sections)
|
|
|
|
if self.current and self.current is not section:
|
|
prev = self._sections.index(self.current)
|
|
self.current.active = False
|
|
|
|
self.current = section
|
|
if new < prev:
|
|
section.animation = Gtk.RevealerTransitionType.SLIDE_DOWN
|
|
else:
|
|
section.animation = Gtk.RevealerTransitionType.SLIDE_UP
|
|
|
|
if section is self.selected_section:
|
|
section.select_playlist(self.selected_playlist)
|
|
|
|
def __playlist_activated(self, section: Section,
|
|
playlist: db.playlist.Playlist) -> None:
|
|
self.sql.set_active_playlist(playlist)
|
|
|
|
def __playlist_selected(self, section: Section,
|
|
playlist: db.playlist.Playlist) -> None:
|
|
if self.selected_section and self.selected_section is not section:
|
|
self.selected_section.clear_selection()
|
|
|
|
self.selected_section = section
|
|
self.selected_playlist = playlist
|
|
|
|
def add(self, section: Section) -> None:
|
|
"""Add a section to the group."""
|
|
self._sections.append(section)
|
|
self.append(section)
|
|
section.connect("notify::active", self.__on_active)
|
|
section.connect("playlist-activated", self.__playlist_activated)
|
|
section.connect("playlist-selected", self.__playlist_selected)
|