emmental/emmental/tracklist/buttons.py

211 lines
8.0 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 VisibleRow(Gtk.ListBoxRow):
"""A ListBoxRow containing a Gtk.Switch and a title Label."""
active = GObject.Property(type=bool, default=True)
title = GObject.Property(type=str)
def __init__(self, title: str, active: bool):
"""Initialize a VisibleRow ListBoxRow."""
super().__init__(title=title, active=active,
child=Gtk.Box(margin_start=6, margin_end=6,
margin_top=6, margin_bottom=6,
spacing=6))
self._switch = Gtk.Switch(active=active)
self._label = Gtk.Label.new(title)
self.bind_property("active", self._switch, "active",
GObject.BindingFlags.BIDIRECTIONAL)
self.props.child.append(self._switch)
self.props.child.append(self._label)
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.popover_child = Gtk.ListBox(selection_mode=Gtk.SelectionMode.NONE)
self.popover_child.bind_model(columns, self.__create_func)
self.popover_child.connect("row-activated", self.__row_activated)
self.popover_child.add_css_class("boxed-list")
def __create_func(self, column: Gtk.ColumnViewColumn) -> VisibleRow:
row = VisibleRow(column.get_title(), column.get_visible())
row.bind_property("active", column, "visible",
GObject.BindingFlags.BIDIRECTIONAL)
return row
def __row_activated(self, box: Gtk.ListBox, row: Gtk.ListBoxRow) -> None:
row.active = not row.active
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",
large_icon=False, 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",
large_icon=False, 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",
large_icon=False, 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
class SortRow(factory.ListRow):
"""A row for managing Sort Order."""
def __init__(self, listitem: Gtk.ListItem):
"""Initialize a Sort Row."""
super().__init__(listitem=listitem, child=SortFieldWidget())
def do_bind(self) -> None:
"""Bind Sort Field properties to the Widget."""
self.child.set_sort_field(self.item)
def do_unbind(self) -> None:
"""Unbind properties from the widget."""
self.child.set_sort_field(None)
class SortButton(buttons.PopoverButton):
"""Shows a Popover Menu to sort playlists."""
model = GObject.Property(type=sorter.SortOrderModel)
sort_order = GObject.Property(type=str)
def __init__(self, **kwargs):
"""Initialize the Sort button."""
super().__init__(has_frame=False, model=sorter.SortOrderModel(),
icon_name="view-list-ordered-symbolic", **kwargs)
self._selection = Gtk.NoSelection(model=self.model)
self._factory = factory.Factory(row_type=SortRow)
self.popover_child = Gtk.ListView(model=self._selection,
factory=self._factory,
show_separators=True)
self.model.bind_property("sort-order", self, "sort-order")
def set_sort_order(self, newval: str) -> None:
"""Directly set the sort order."""
self.model.set_sort_order(newval)