emmental/emmental/sidebar/row.py

185 lines
7.1 KiB
Python

# Copyright 2022 (c) Anna Schumaker.
"""Widgets for sidebar rows."""
from gi.repository import GObject
from gi.repository import Gtk
from .title import PlaylistTitle
from .title import EditableTitle
from .icon import Icon
from .icon import Settable
from .. import db
from .. import factory
class BaseRow(Gtk.Box):
"""Base class for Row widgets."""
playlist = GObject.Property(type=db.playlist.Playlist)
name = GObject.Property(type=str)
count = GObject.Property(type=int)
class Row(BaseRow):
"""A basic row representing a playlist without extra actions."""
image = GObject.Property(type=GObject.TYPE_PYOBJECT)
def __init__(self, **kwargs):
"""Initialize a sidebar Row."""
super().__init__(spacing=12, **kwargs)
self._icon = Icon(text=self.name, show_initials=True)
self._title = PlaylistTitle(title=self.name, count=self.count)
self._year = None
self.bind_property("image", self._icon, "filepath")
self.bind_property("name", self._icon, "text")
self.bind_property("name", self._title, "title")
self.bind_property("count", self._title, "count")
self.append(self._icon)
self.append(self._title)
@GObject.Property(type=int)
def year(self) -> int | None:
"""Set the year displayed in the icon."""
return self._year
@year.setter
def year(self, year: int) -> None:
digits = str(year)
self._year = year
self._icon.text = f"{digits[-2]} {digits[-1]}"
class PlaylistRow(BaseRow):
"""An advanced playlist row with extra actions for users."""
icon_name = GObject.Property(type=str)
filepath = GObject.Property(type=GObject.TYPE_PYOBJECT)
modifiable = GObject.Property(type=bool, default=False)
def __init__(self, **kwargs):
"""Initialize a PlaylistRow."""
super().__init__(**kwargs)
self._icon = Settable()
self._title = EditableTitle(margin_start=12, margin_end=12)
self._delete = Gtk.Button(icon_name="big-x-symbolic",
tooltip_text="delete playlist",
valign=Gtk.Align.CENTER,
has_frame=False, visible=False)
self.bind_property("name", self._icon, "text")
self.bind_property("icon-name", self._icon, "icon-name")
self.bind_property("filepath", self._icon, "filepath",
GObject.BindingFlags.BIDIRECTIONAL)
self.bind_property("modifiable", self._icon, "settable")
self.bind_property("name", self._title, "title")
self.bind_property("count", self._title, "count")
self.bind_property("modifiable", self._title, "editable")
self.bind_property("modifiable", self._delete, "visible")
self._title.connect("request-rename", self.__on_rename)
self._delete.connect("clicked", self.__on_delete)
self._delete.add_css_class("emmental-delete")
self.append(self._icon)
self.append(self._title)
self.append(self._delete)
def __on_rename(self, title: EditableTitle, new_name: str) -> None:
if self.playlist is not None:
self.playlist.rename(new_name)
def __on_delete(self, button: Gtk.Button) -> None:
if self.playlist is not None:
self.playlist.delete()
class LibraryRow(BaseRow):
"""An advaced playlist row with extra actions for library management."""
enabled = GObject.Property(type=bool, default=True)
online = GObject.Property(type=bool, default=True)
scanning = GObject.Property(type=bool, default=False)
progress = GObject.Property(type=float)
def __init__(self, **kwargs):
"""Initialize a LibraryRow."""
super().__init__(**kwargs)
self._box = Gtk.Box()
self._overlay = Gtk.Overlay(child=self._box)
self._switch = Gtk.Switch(active=self.enabled, valign=Gtk.Align.CENTER,
tooltip_text="disable library path")
self._title = PlaylistTitle(margin_start=12, margin_end=12)
self._scan = Gtk.Button(icon_name="update", has_frame=False,
tooltip_text="update library path",
valign=Gtk.Align.CENTER)
self._stop = Gtk.Button(icon_name="stop-sign-large", has_frame=False,
tooltip_text="cancel update",
valign=Gtk.Align.CENTER, visible=False)
self._delete = Gtk.Button(icon_name="big-x-symbolic",
tooltip_text="delete library path",
valign=Gtk.Align.CENTER, has_frame=False)
self._progress = Gtk.ProgressBar(valign=Gtk.Align.END, visible=False)
self.bind_property("enabled", self._switch, "active",
GObject.BindingFlags.BIDIRECTIONAL)
self.bind_property("online", self._switch, "sensitive")
self.bind_property("name", self._title, "title")
self.bind_property("count", self._title, "count")
self.bind_property("online", self._title, "sensitive")
self.bind_property("enabled", self._title, "sensitive")
self.bind_property("scanning", self._stop, "visible")
self.bind_property("scanning", self._scan, "visible",
GObject.BindingFlags.INVERT_BOOLEAN)
self.bind_property("scanning", self._progress, "visible")
self.bind_property("progress", self._progress, "fraction")
self._delete.connect("clicked", self.__on_button_press, "delete")
self._scan.connect("clicked", self.__on_button_press, "scan")
self._stop.connect("clicked", self.__on_button_press, "stop")
self._switch.connect("notify::active", self.__on_switch_activated)
self._delete.add_css_class("emmental-delete")
self._stop.add_css_class("emmental-stop")
self._progress.add_css_class("osd")
self._box.append(self._switch)
self._box.append(self._title)
self._box.append(self._scan)
self._box.append(self._stop)
self._box.append(self._delete)
self._overlay.add_overlay(self._progress)
self.append(self._overlay)
def __on_button_press(self, button: Gtk.Button, action: str) -> None:
if self.playlist is not None:
match action:
case "delete": self.playlist.delete()
case "scan": self.playlist.scan()
case "stop": self.playlist.stop()
def __on_switch_activated(self, switch: Gtk.Switch, param) -> None:
state = "disable" if self.enabled else "enable"
self._switch.set_tooltip_text(f"{state} library path")
class TreeRow(factory.TreeRow):
"""A factory Row used for displaying individual playlists."""
def do_bind(self) -> None:
"""Bind a Playlist to this Row."""
self.child.playlist = self.item
self.bind_and_set_property("name", "name")
self.bind_and_set_property("n-tracks", "count")
self.bind_n_children(self.item.children)
self.bind_active("active")
def do_unbind(self) -> None:
"""Unbind a Playlist from this Row."""
self.child.playlist = None