2022-10-25 11:00:32 -04:00
|
|
|
# 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
|
2022-11-16 16:46:30 -05:00
|
|
|
from . import sorter
|
2022-10-25 11:00:32 -04:00
|
|
|
from .. import buttons
|
2024-01-20 14:03:44 -05:00
|
|
|
from .. import gsetup
|
2022-10-25 11:00:32 -04:00
|
|
|
|
|
|
|
|
2023-08-10 18:10:13 -04:00
|
|
|
class VisibleRow(Gtk.ListBoxRow):
|
|
|
|
"""A ListBoxRow containing a Gtk.Switch and a title Label."""
|
2022-10-25 11:00:32 -04:00
|
|
|
|
2023-08-10 18:10:13 -04:00
|
|
|
active = GObject.Property(type=bool, default=True)
|
|
|
|
title = GObject.Property(type=str)
|
2022-10-25 11:00:32 -04:00
|
|
|
|
2023-08-10 18:10:13 -04:00
|
|
|
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)
|
2022-10-25 11:00:32 -04:00
|
|
|
|
|
|
|
|
|
|
|
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",
|
2023-08-22 13:28:38 -04:00
|
|
|
tooltip_text="configure visible columns",
|
2022-10-25 11:00:32 -04:00
|
|
|
has_frame=False, **kwargs)
|
2023-08-10 18:10:13 -04:00
|
|
|
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
|
2022-10-26 10:34:01 -04:00
|
|
|
|
|
|
|
|
|
|
|
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",
|
2023-08-22 13:28:38 -04:00
|
|
|
active_tooltip_text="loop: track",
|
2022-10-26 10:34:01 -04:00
|
|
|
inactive_icon_name="media-playlist-repeat",
|
2023-08-22 13:28:38 -04:00
|
|
|
inactive_tooltip_text="loop: disabled",
|
2023-06-10 21:25:54 -04:00
|
|
|
large_icon=False, state="None",
|
2022-10-26 10:34:01 -04:00
|
|
|
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
|
2023-08-22 13:28:38 -04:00
|
|
|
self.inactive_tooltip_text = "loop: disabled"
|
2022-10-26 10:34:01 -04:00
|
|
|
case ("Playlist", _):
|
|
|
|
self.active = False
|
|
|
|
self.icon_opacity = 1.0
|
2023-08-22 13:28:38 -04:00
|
|
|
self.inactive_tooltip_text = "loop: playlist"
|
2022-10-26 10:34:01 -04:00
|
|
|
case ("Track", _):
|
|
|
|
self.active = True
|
|
|
|
self.icon_opacity = 1.0
|
2022-10-26 11:05:30 -04:00
|
|
|
|
|
|
|
|
|
|
|
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",
|
2023-08-22 13:28:38 -04:00
|
|
|
active_tooltip_text="shuffle: enabled",
|
2024-01-20 14:03:44 -05:00
|
|
|
inactive_icon_name=self.get_inactive_icon(),
|
2023-08-22 13:28:38 -04:00
|
|
|
inactive_tooltip_text="shuffle: disabled",
|
2023-06-10 21:25:54 -04:00
|
|
|
large_icon=False, icon_opacity=0.5,
|
2022-10-26 11:05:30 -04:00
|
|
|
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
|
2024-01-20 14:03:44 -05:00
|
|
|
self.inactive_icon_name = self.get_inactive_icon()
|
|
|
|
|
|
|
|
def get_inactive_icon(self) -> str:
|
|
|
|
"""Return the inactive icon name."""
|
|
|
|
if gsetup.has_icon("media-playlist-normal"):
|
|
|
|
return "media-playlist-normal"
|
|
|
|
return "media-playlist-consecutive"
|
2022-11-16 16:46:30 -05:00
|
|
|
|
|
|
|
|
2023-08-11 22:43:28 -04:00
|
|
|
class SortRow(Gtk.ListBoxRow):
|
|
|
|
"""A ListBoxRow for managing Sort Order."""
|
2022-11-16 16:46:30 -05:00
|
|
|
|
2023-08-11 22:43:28 -04:00
|
|
|
active = GObject.Property(type=bool, default=False)
|
2022-11-16 16:46:30 -05:00
|
|
|
sort_field = GObject.Property(type=sorter.SortField)
|
|
|
|
|
2023-08-11 22:43:28 -04:00
|
|
|
def __init__(self, sort_field: sorter.SortField):
|
|
|
|
"""Initialize a Sort Row."""
|
|
|
|
super().__init__(sort_field=sort_field, active=sort_field.enabled,
|
|
|
|
child=Gtk.Box(margin_start=6, margin_end=6,
|
|
|
|
margin_top=6, margin_bottom=6,
|
|
|
|
spacing=6))
|
|
|
|
self._switch = Gtk.Switch(active=self.active, valign=Gtk.Align.CENTER)
|
|
|
|
self._label = Gtk.Label(label=sort_field.name, hexpand=True,
|
|
|
|
sensitive=self.active, xalign=0.0)
|
2022-11-16 16:46:30 -05:00
|
|
|
self._reverse = buttons.ImageToggle("arrow1-up", "arrow1-down",
|
2023-08-11 22:43:28 -04:00
|
|
|
active=sort_field.reversed,
|
|
|
|
sensitive=self.active,
|
|
|
|
has_frame=False)
|
|
|
|
self._move_box = Gtk.Box(sensitive=self.active)
|
2022-11-16 16:46:30 -05:00
|
|
|
self._move_up = Gtk.Button(icon_name="go-up-symbolic")
|
|
|
|
self._move_down = Gtk.Button(icon_name="go-down-symbolic")
|
|
|
|
|
2023-08-11 22:43:28 -04:00
|
|
|
self._switch.connect("notify::active", self.__toggle_enabled)
|
|
|
|
self._reverse.connect("toggled", self.__reverse)
|
|
|
|
self._move_up.connect("clicked", self.__move_up)
|
|
|
|
self._move_down.connect("clicked", self.__move_down)
|
2022-11-16 16:46:30 -05:00
|
|
|
|
2023-08-11 22:43:28 -04:00
|
|
|
self.props.child.append(self._switch)
|
|
|
|
self.props.child.append(self._label)
|
|
|
|
self.props.child.append(self._reverse)
|
|
|
|
self.props.child.append(self._move_box)
|
2022-11-16 16:46:30 -05:00
|
|
|
|
2023-08-11 22:43:28 -04:00
|
|
|
self._move_box.append(self._move_up)
|
|
|
|
self._move_box.append(self._move_down)
|
|
|
|
self._move_box.add_css_class("linked")
|
2022-11-16 16:46:30 -05:00
|
|
|
|
2023-08-11 22:43:28 -04:00
|
|
|
def __toggle_enabled(self, switch: Gtk.Switch, param) -> None:
|
|
|
|
if switch.props.active:
|
|
|
|
self.sort_field.enable()
|
|
|
|
else:
|
|
|
|
self.sort_field.disable()
|
2022-11-16 16:46:30 -05:00
|
|
|
|
|
|
|
def __reverse(self, button: buttons.ImageToggle) -> None:
|
2023-08-11 22:43:28 -04:00
|
|
|
self.sort_field.reverse()
|
2022-11-16 16:46:30 -05:00
|
|
|
|
2023-08-11 22:43:28 -04:00
|
|
|
def __move_up(self, button: Gtk.Button) -> None:
|
|
|
|
self.sort_field.move_up()
|
2022-11-16 16:46:30 -05:00
|
|
|
|
2023-08-11 22:43:28 -04:00
|
|
|
def __move_down(self, button: Gtk.Button) -> None:
|
|
|
|
self.sort_field.move_down()
|
2022-11-16 16:46:30 -05:00
|
|
|
|
|
|
|
|
|
|
|
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(),
|
2023-08-22 13:28:38 -04:00
|
|
|
tooltip_text="configure playlist sort order",
|
2024-01-20 11:33:36 -05:00
|
|
|
icon_name="list-compact-symbolic", **kwargs)
|
2023-08-11 22:43:28 -04:00
|
|
|
self.popover_child = Gtk.ListBox(selection_mode=Gtk.SelectionMode.NONE)
|
|
|
|
self.popover_child.bind_model(self.model, self.__create_func)
|
|
|
|
self.popover_child.connect("row-activated", self.__row_activated)
|
|
|
|
self.popover_child.add_css_class("boxed-list")
|
|
|
|
|
2022-11-16 16:46:30 -05:00
|
|
|
self.model.bind_property("sort-order", self, "sort-order")
|
|
|
|
|
2023-08-11 22:43:28 -04:00
|
|
|
def __create_func(self, sort_field: sorter.SortField) -> SortRow:
|
|
|
|
return SortRow(sort_field)
|
|
|
|
|
|
|
|
def __row_activated(self, box: Gtk.ListBox, row: SortRow) -> None:
|
|
|
|
if row.active:
|
|
|
|
row._reverse.active = not row.sort_field.reversed
|
|
|
|
else:
|
|
|
|
row.sort_field.enable()
|
|
|
|
|
2022-11-16 16:46:30 -05:00
|
|
|
def set_sort_order(self, newval: str) -> None:
|
|
|
|
"""Directly set the sort order."""
|
|
|
|
self.model.set_sort_order(newval)
|