tracklist: Add a SortButton

This is a Popover Button containing a Gtk.ListView to display the Sort
Order Model for the currently visible Playlist.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
Anna Schumaker 2022-11-16 16:46:30 -05:00
parent ed4a484a31
commit ee1152bcc4
4 changed files with 129 additions and 0 deletions

View File

@ -34,12 +34,14 @@ class Card(Gtk.Box):
has_frame=False, sensitive=False)
self._loop = buttons.LoopButton()
self._shuffle = buttons.ShuffleButton()
self._sort = buttons.SortButton()
self._top_left.append(self._visible_cols)
self._top_left.append(self._unselect)
self._top_right.append(self._loop)
self._top_right.append(self._shuffle)
self._top_right.append(self._sort)
self._top_box.set_start_widget(self._top_left)
self._top_box.set_center_widget(self._filter)
@ -55,6 +57,7 @@ class Card(Gtk.Box):
self._unselect.connect("clicked", self.__clear_selection)
self._loop.connect("notify::state", self.__update_loop_state)
self._shuffle.connect("notify::active", self.__update_shuffle_state)
self._sort.connect("notify::sort-order", self.__update_sort_order)
self.add_css_class("card")
@ -69,12 +72,15 @@ class Card(Gtk.Box):
self.__set_button_state()
case "shuffle":
self._shuffle.active = playlist.shuffle
case "sort-order":
self._sort.sort_order = playlist.sort_order
def __set_button_state(self) -> None:
can_disable = self.playlist.playlist != self.sql.playlists.collection
self._loop.can_disable = can_disable
self._loop.state = self.playlist.loop
self._shuffle.active = self.playlist.shuffle
self._sort.set_sort_order(self.playlist.sort_order)
def __update_loop_state(self, loop: buttons.LoopButton, param) -> None:
if self.playlist.loop != loop.state:
@ -85,6 +91,10 @@ class Card(Gtk.Box):
if self.playlist.shuffle != shuffle.active:
self.playlist.shuffle = shuffle.active
def __update_sort_order(self, sort: buttons.SortButton, param) -> None:
if self.playlist.sort_order != sort.sort_order:
self.playlist.sort_order = sort.sort_order
def __search_changed(self, filter: entry.Filter) -> None:
self.sql.tracks.filter(filter.get_query())

View File

@ -162,3 +162,41 @@ class SortFieldWidget(Gtk.Box):
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)

View File

@ -275,3 +275,63 @@ class TestSortFieldWidget(unittest.TestCase):
self.assertFalse(self.model[0].reversed)
self.sort._reverse.emit("clicked")
self.assertTrue(self.model[0].reversed)
class TestSortButton(unittest.TestCase):
"""Test the Sort button."""
def setUp(self):
"""Set up common variables."""
self.sort = emmental.tracklist.buttons.SortButton()
def test_init(self):
"""Test that the Sort button is configured correctly."""
self.assertIsInstance(self.sort, emmental.buttons.PopoverButton)
self.assertEqual(self.sort.get_icon_name(),
"view-list-ordered-symbolic")
self.assertFalse(self.sort.get_has_frame())
def test_popover_child(self):
"""Test that the popover_child is configured correctly."""
self.assertIsInstance(self.sort.popover_child, Gtk.ListView)
self.assertIsInstance(self.sort.model,
emmental.tracklist.sorter.SortOrderModel)
self.assertIsInstance(self.sort._selection, Gtk.NoSelection)
self.assertIsInstance(self.sort._factory, emmental.factory.Factory)
self.assertTrue(self.sort.popover_child.get_show_separators())
self.assertEqual(self.sort.popover_child.get_model(),
self.sort._selection)
self.assertEqual(self.sort._selection.get_model(), self.sort.model)
self.assertEqual(self.sort.popover_child.get_factory(),
self.sort._factory)
self.assertEqual(self.sort._factory.row_type,
emmental.tracklist.buttons.SortRow)
def test_sort_row(self):
"""Test the Sort Row object."""
(field := self.sort.model[0]).enable()
listitem = Gtk.ListItem()
listitem.get_item = lambda: field
self.sort.model[1].enable()
row = emmental.tracklist.buttons.SortRow(listitem)
self.assertIsInstance(row, emmental.factory.ListRow)
self.assertIsInstance(row.child,
emmental.tracklist.buttons.SortFieldWidget)
row.bind()
self.assertEqual(row.child.sort_field, field)
row.unbind()
self.assertIsNone(row.child.sort_field)
def test_sort_order(self):
"""Test the sort-order property."""
self.assertEqual(self.sort.sort_order, "")
self.sort.model[0].enable()
self.assertEqual(self.sort.sort_order, self.sort.model.sort_order)
self.sort.set_sort_order("artist")
self.assertEqual(self.sort.model.sort_order, "artist")

View File

@ -136,6 +136,27 @@ class TestTracklist(tests.util.TestCase):
self.tracklist._shuffle.active = False
self.assertFalse(self.playlist.shuffle)
def test_sort_button(self):
"""Test the sort button."""
self.assertIsInstance(self.tracklist._sort,
emmental.tracklist.buttons.SortButton)
self.assertEqual(self.tracklist._shuffle.get_next_sibling(),
self.tracklist._sort)
self.tracklist.playlist = self.playlist
self.assertEqual(self.tracklist._sort.sort_order,
self.playlist.sort_order)
self.playlist.playlist = self.sql.playlists.collection
self.assertEqual(self.tracklist._sort.sort_order,
self.sql.playlists.collection.sort_order)
self.playlist.sort_order = "album"
self.assertEqual(self.tracklist._sort.sort_order, "album")
self.tracklist._sort.sort_order = "artist"
self.assertEqual(self.playlist.sort_order, "artist")
def test_trackview(self):
"""Test the Trackview widget."""
self.assertIsInstance(self.tracklist._trackview,