emmental/emmental/tracklist/buttons.py

165 lines
6.4 KiB
Python

# Copyright 2022 (c) Anna Schumaker.
"""Extra buttons for the TrackList."""
from gi.repository import GObject
from gi.repository import Gio
from gi.repository import Gtk
from . import sorter
from .. import buttons
from .. import factory
class VisibleSwitch(factory.ListRow):
"""A list row containing a Gtk.Switch."""
def __init__(self, listitem: Gtk.ListItem):
"""Initialize a VisibleSwitch ListRow."""
super().__init__(listitem=listitem, child=Gtk.Switch())
def do_bind(self) -> None:
"""Bind the visible property to the switch active property."""
self.bind_and_set_property("visible", "active", bidirectional=True)
class VisibleColumns(buttons.PopoverButton):
"""Shows a Popover Menu to select which columns are visible."""
columns = GObject.Property(type=Gio.ListModel)
def __init__(self, columns: Gio.ListModel, **kwargs):
"""Initialize the VisibleColumns button."""
super().__init__(columns=columns, icon_name="columns-symbolic",
has_frame=False, **kwargs)
self._selection = Gtk.NoSelection(model=self.columns)
self.popover_child = Gtk.ColumnView(model=self._selection,
show_row_separators=True)
self.__append_column(factory.InscriptionFactory("title"),
"Column", width=125)
self.__append_column(factory.Factory(row_type=VisibleSwitch),
"Visible")
self.popover_child.add_css_class("data-table")
def __append_column(self, factory: factory.Factory,
title: str, *, width: int = -1) -> None:
column = Gtk.ColumnViewColumn(factory=factory, title=title,
fixed_width=width)
self.popover_child.append_column(column)
class LoopButton(buttons.ImageToggle):
"""A button for setting Loop state of a Playlist."""
can_disable = GObject.Property(type=bool, default=True)
def __init__(self, **kwargs):
"""Initialize a Loop Button."""
super().__init__(active_icon_name="media-playlist-repeat-song",
inactive_icon_name="media-playlist-repeat",
icon_size=Gtk.IconSize.NORMAL, state="None",
has_frame=False, **kwargs)
def do_clicked(self):
"""Cycle though Loop states."""
match (self.state, self.can_disable):
case ("None", _): self.state = "Playlist"
case ("Playlist", _): self.state = "Track"
case ("Track", True): self.state = "None"
case ("Track", False): self.state = "Playlist"
@GObject.Property(type=str)
def state(self) -> str:
"""Get the current state of the button."""
match (self.active, self.icon_opacity):
case (True, 1.0): return "Track"
case (False, 1.0): return "Playlist"
case (_, _): return "None"
@state.setter
def state(self, newval: str) -> None:
match (newval, self.can_disable):
case ("None", True):
self.active = False
self.icon_opacity = 0.5
case ("Playlist", _):
self.active = False
self.icon_opacity = 1.0
case ("Track", _):
self.active = True
self.icon_opacity = 1.0
class ShuffleButton(buttons.ImageToggle):
"""A button for setting Shuffle state of a Playlist."""
def __init__(self, **kwargs):
"""Initialize a Shuffle Button."""
super().__init__(active_icon_name="media-playlist-shuffle",
inactive_icon_name="media-playlist-consecutive",
icon_size=Gtk.IconSize.NORMAL, icon_opacity=0.5,
has_frame=False, **kwargs)
def do_toggled(self):
"""Adjust opacity when active state toggles."""
self.icon_opacity = 1.0 if self.active else 0.5
class SortFieldWidget(Gtk.Box):
"""A Widget to display in the Sort Order button popover."""
sort_field = GObject.Property(type=sorter.SortField)
def __init__(self) -> None:
"""Initialize a SortField Widget."""
super().__init__(spacing=6)
self._enabled = Gtk.Switch(valign=Gtk.Align.CENTER)
self._name = Gtk.Label(hexpand=True, sensitive=False)
self._reverse = buttons.ImageToggle("arrow1-up", "arrow1-down",
icon_size=Gtk.IconSize.NORMAL,
sensitive=False)
self._box = Gtk.Box(sensitive=False)
self._move_up = Gtk.Button(icon_name="go-up-symbolic")
self._move_down = Gtk.Button(icon_name="go-down-symbolic")
self._enabled.bind_property("active", self._name, "sensitive")
self._enabled.bind_property("active", self._reverse, "sensitive")
self._enabled.bind_property("active", self._box, "sensitive")
self._enabled.connect("notify::active", self.__notify_enabled)
self._reverse.connect("clicked", self.__reverse)
self._move_up.connect("clicked", self.__move_item_up)
self._move_down.connect("clicked", self.__move_item_down)
self.append(self._enabled)
self.append(self._name)
self.append(self._reverse)
self.append(self._box)
self._box.append(self._move_up)
self._box.append(self._move_down)
self._box.add_css_class("linked")
def __move_item_down(self, button: Gtk.Button) -> None:
if self.sort_field is not None:
self.sort_field.move_down()
def __move_item_up(self, button: Gtk.Button) -> None:
if self.sort_field is not None:
self.sort_field.move_up()
def __notify_enabled(self, switch: Gtk.Switch, param) -> None:
if self.sort_field is not None:
if switch.get_active():
self.sort_field.enable()
else:
self.sort_field.disable()
def __reverse(self, button: buttons.ImageToggle) -> None:
if self.sort_field is not None:
self.sort_field.reverse()
def set_sort_field(self, field: sorter.SortField | None) -> None:
"""Set the Sort Field displayed by this Widget."""
self.sort_field = field
self._name.set_text(field.name if field is not None else "")
self._enabled.set_active(field is not None and field.enabled)
self._reverse.active = field is not None and field.reversed