From ddfd37130b18087dd9da06c9e332cbd1d45c417a Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Thu, 10 Aug 2023 16:51:35 -0400 Subject: [PATCH] tracklist: Move the OSD out of the TrackView This simplifies the code a lot by letting the TrackList directly call OSD functions without going through the TrackView. I can also simplify the TrackView to just contain our columnview. Signed-off-by: Anna Schumaker --- emmental/emmental.css | 8 ++++++ emmental/tracklist/__init__.py | 22 ++++++++++------ emmental/tracklist/trackview.py | 23 +++++----------- tests/tracklist/test_tracklist.py | 44 +++++++++++++++++++++++-------- tests/tracklist/test_trackview.py | 39 +++------------------------ 5 files changed, 66 insertions(+), 70 deletions(-) diff --git a/emmental/emmental.css b/emmental/emmental.css index b82e3f5..54865e1 100644 --- a/emmental/emmental.css +++ b/emmental/emmental.css @@ -70,6 +70,14 @@ button.emmental-stop>image { color: @red_3; } +columnview.emmental-track-list > header { + background-color: @card_bg_color; +} + +columnview.emmental-track-list > listview { + background-color: @card_bg_color; +} + columnview.emmental-track-list > listview > row > cell { padding: 0px 2px; min-height: 40px; diff --git a/emmental/tracklist/__init__.py b/emmental/tracklist/__init__.py index c84f717..92f267a 100644 --- a/emmental/tracklist/__init__.py +++ b/emmental/tracklist/__init__.py @@ -10,6 +10,7 @@ from .. import db from .. import entry from . import buttons from . import footer +from . import selection from . import trackview @@ -27,8 +28,9 @@ class Card(Gtk.Box): self._top_box = Gtk.CenterBox(margin_start=6, margin_end=6) self._filter = entry.Filter("tracks", hexpand=True, margin_start=100, margin_end=100) - self._trackview = trackview.TrackView(sql, margin_start=6, - margin_end=6) + self._trackview = trackview.TrackView(sql) + self._osd = selection.OSD(sql, self._trackview.selection_model, + child=self._trackview) self._visible_cols = buttons.VisibleColumns(self._trackview.columns) self._unselect = Gtk.Button(icon_name="edit-select-none-symbolic", has_frame=False, sensitive=False) @@ -50,9 +52,14 @@ class Card(Gtk.Box): self._top_box.set_end_widget(self._top_right) self.append(self._top_box) - self.append(self._trackview) + self.append(Gtk.Separator()) + self.append(self._osd) + self.append(Gtk.Separator()) self.append(self._footer) + self._osd.bind_property("have-selected", self._trackview, + "have-selected") + self._osd.bind_property("n-selected", self._trackview, "n-selected") self._trackview.bind_property("n-tracks", self._footer, "count") self._trackview.bind_property("n-selected", self._footer, "selected") self._trackview.bind_property("runtime", self._footer, "runtime") @@ -61,7 +68,7 @@ class Card(Gtk.Box): "sensitive") self._filter.connect("search-changed", self.__search_changed) - self._unselect.connect("clicked", self.__clear_selection) + self._unselect.connect("clicked", self._osd.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) @@ -69,9 +76,6 @@ class Card(Gtk.Box): self._top_box.add_css_class("toolbar") self.add_css_class("card") - def __clear_selection(self, unselect: Gtk.Button) -> None: - self._trackview.clear_selected_tracks() - def __playlist_notify(self, playlist: Playlist, param) -> None: match param.name: case "loop": @@ -89,7 +93,7 @@ class Card(Gtk.Box): self._loop.state = self.playlist.loop self._shuffle.active = self.playlist.shuffle self._sort.set_sort_order(self.playlist.sort_order) - self._trackview.reset_osd() + self._osd.reset() def __update_loop_state(self, loop: buttons.LoopButton, param) -> None: if self.playlist.loop != loop.state: @@ -132,6 +136,8 @@ class Card(Gtk.Box): self._trackview.playlist.disconnect_by_func(self.__playlist_notify) self._trackview.playlist = newval + self._osd.playlist = newval + if newval is not None: self._top_right.set_sensitive(not isinstance(newval, Previous)) self.__set_button_state() diff --git a/emmental/tracklist/trackview.py b/emmental/tracklist/trackview.py index 85b19a3..81aa37e 100644 --- a/emmental/tracklist/trackview.py +++ b/emmental/tracklist/trackview.py @@ -7,10 +7,9 @@ from .. import db from .. import factory from .. import playlist from . import row -from . import selection -class TrackView(Gtk.Frame): +class TrackView(Gtk.ScrolledWindow): """A Gtk.ColumnView that has been configured to show Tracks.""" playlist = GObject.Property(type=playlist.playlist.Playlist) @@ -30,8 +29,6 @@ class TrackView(Gtk.Frame): show_row_separators=True, enable_rubberband=True, model=self._selection) - self._scrollwin = Gtk.ScrolledWindow(child=self._columnview) - self._osd = selection.OSD(sql, self._selection, child=self._scrollwin) self.__append_column("Art", "cover", row.AlbumCover, resizable=False) self.__append_column("Fav", "favorite", row.FavoriteButton, @@ -57,16 +54,13 @@ class TrackView(Gtk.Frame): self.__append_column("Filepath", "path", row.PathString, visible=False) self.bind_property("playlist", self._filtermodel, "model") - self.bind_property("playlist", self._osd, "playlist") - self._osd.bind_property("have-selected", self, "have-selected") - self._osd.bind_property("n-selected", self, "n-selected") self._selection.bind_property("n-items", self, "n-tracks") self._selection.connect("items-changed", self.__runtime_changed) self._columnview.connect("activate", self.__track_activated) self._columnview.add_css_class("emmental-track-list") - self.set_child(self._osd) + self.set_child(self._columnview) def __append_column(self, title: str, property: str, row_type: type, *, width: int = -1, visible: bool = True, @@ -95,15 +89,12 @@ class TrackView(Gtk.Frame): pos = max(i - 3, 0) * adjustment.get_upper() adjustment.set_value(pos / self._selection.get_n_items()) - def clear_selected_tracks(self) -> None: - """Clear the currently selected tracks.""" - self._osd.clear_selection() - - def reset_osd(self) -> None: - """Reset the OSD.""" - self._osd.reset() - @GObject.Property(type=Gio.ListModel) def columns(self) -> Gio.ListModel: """Get the ListModel for the columns.""" return self._columnview.get_columns() + + @GObject.Property(type=Gio.ListModel) + def selection_model(self) -> Gio.ListModel: + """Get the SelectionModel for the ColumnView.""" + return self._columnview.get_model() diff --git a/tests/tracklist/test_tracklist.py b/tests/tracklist/test_tracklist.py index 3b64a6f..e89928c 100644 --- a/tests/tracklist/test_tracklist.py +++ b/tests/tracklist/test_tracklist.py @@ -66,8 +66,8 @@ class TestTracklist(tests.util.TestCase): self.tracklist._trackview.have_selected = True self.assertTrue(self.tracklist._unselect.get_sensitive()) - with unittest.mock.patch.object(self.tracklist._trackview, - "clear_selected_tracks") as mock_clear: + with unittest.mock.patch.object(self.tracklist._osd.selection, + "unselect_all") as mock_clear: self.tracklist._unselect.emit("clicked") mock_clear.assert_called() @@ -160,14 +160,32 @@ class TestTracklist(tests.util.TestCase): """Test the Trackview widget.""" self.assertIsInstance(self.tracklist._trackview, emmental.tracklist.trackview.TrackView) - self.assertEqual(self.tracklist._top_box.get_next_sibling(), - self.tracklist._trackview) - - self.assertEqual(self.tracklist._trackview.get_margin_start(), 6) - self.assertEqual(self.tracklist._trackview.get_margin_end(), 6) self.assertEqual(self.tracklist.columns, self.tracklist._trackview.columns) + sep = self.tracklist._top_box.get_next_sibling() + self.assertIsInstance(sep, Gtk.Separator) + self.assertEqual(sep.get_orientation(), Gtk.Orientation.HORIZONTAL) + self.assertEqual(sep.get_next_sibling(), self.tracklist._osd) + self.assertEqual(self.tracklist._osd.get_child(), + self.tracklist._trackview) + + def test_osd(self): + """Test the OSD widget.""" + self.assertIsInstance(self.tracklist._osd, + emmental.tracklist.selection.OSD) + self.assertEqual(self.tracklist._osd.sql, self.sql) + self.assertEqual(self.tracklist._osd.selection, + self.tracklist._trackview.selection_model) + + self.assertFalse(self.tracklist._trackview.have_selected) + self.tracklist._osd.have_selected = True + self.assertTrue(self.tracklist._trackview.have_selected) + + self.assertEqual(self.tracklist._trackview.n_selected, 0) + self.tracklist._osd.n_selected = 4 + self.assertEqual(self.tracklist._trackview.n_selected, 4) + def test_footer(self): """Test that the footer is wired up properly.""" self.assertIsInstance(self.tracklist._footer, @@ -176,8 +194,11 @@ class TestTracklist(tests.util.TestCase): self.assertEqual(self.tracklist._footer.get_margin_end(), 6) self.assertEqual(self.tracklist._footer.get_margin_top(), 6) self.assertEqual(self.tracklist._footer.get_margin_bottom(), 6) - self.assertEqual(self.tracklist._trackview.get_next_sibling(), - self.tracklist._footer) + + sep = self.tracklist._osd.get_next_sibling() + self.assertIsInstance(sep, Gtk.Separator) + self.assertEqual(sep.get_orientation(), Gtk.Orientation.HORIZONTAL) + self.assertEqual(sep.get_next_sibling(), self.tracklist._footer) self.tracklist._trackview.n_tracks = 5 self.tracklist._trackview.n_selected = 3 @@ -192,11 +213,12 @@ class TestTracklist(tests.util.TestCase): self.assertIsNone(self.tracklist.playlist) self.assertFalse(self.tracklist._top_right.get_sensitive()) - with unittest.mock.patch.object(self.tracklist._trackview, - "reset_osd") as mock_reset_osd: + with unittest.mock.patch.object(self.tracklist._osd, + "reset") as mock_reset_osd: self.tracklist.playlist = self.playlist self.assertEqual(self.tracklist.playlist, self.playlist) self.assertEqual(self.tracklist._trackview.playlist, self.playlist) + self.assertEqual(self.tracklist._osd.playlist, self.playlist) self.assertTrue(self.tracklist._top_right.get_sensitive()) mock_reset_osd.assert_called() diff --git a/tests/tracklist/test_trackview.py b/tests/tracklist/test_trackview.py index 2c027b7..eaea0b3 100644 --- a/tests/tracklist/test_trackview.py +++ b/tests/tracklist/test_trackview.py @@ -28,15 +28,8 @@ class TestTrackView(tests.util.TestCase): def test_init(self): """Test that the TrackView is initialized properly.""" - self.assertIsInstance(self.trackview, Gtk.Frame) - self.assertIsInstance(self.trackview._scrollwin, Gtk.ScrolledWindow) - self.assertIsInstance(self.trackview._osd, - emmental.tracklist.selection.OSD) - - self.assertEqual(self.trackview.get_child(), self.trackview._osd) - self.assertEqual(self.trackview._osd.get_child(), - self.trackview._scrollwin) - self.assertEqual(self.trackview._scrollwin.get_child(), + self.assertIsInstance(self.trackview, Gtk.ScrolledWindow) + self.assertEqual(self.trackview.get_child(), self.trackview._columnview) def test_list_models(self): @@ -50,6 +43,8 @@ class TestTrackView(tests.util.TestCase): self.assertEqual(self.trackview._selection.get_model(), self.trackview._filtermodel) + self.assertEqual(self.trackview.selection_model, + self.trackview._selection) def test_columnview(self): """Test the columnview.""" @@ -80,27 +75,12 @@ class TestTrackView(tests.util.TestCase): requested.assert_called_with(self.playlist, self.track) mock_unselect.assert_called() - def test_clear_selected_tracks(self): - """Test the clear_selected_tracks() function.""" - with unittest.mock.patch.object(self.trackview._osd, - "clear_selection") as mock_clear: - self.trackview.clear_selected_tracks() - mock_clear.assert_called() - - def test_reset_osd(self): - """Test the reset_osd() function.""" - with unittest.mock.patch.object(self.trackview._osd, - "reset") as mock_reset: - self.trackview.reset_osd() - mock_reset.assert_called() - def test_playlist(self): """Test the playlist property.""" self.assertIsNone(self.trackview.playlist) self.trackview.playlist = self.playlist self.assertEqual(self.trackview._filtermodel.get_model(), self.playlist) - self.assertEqual(self.trackview._osd.playlist, self.playlist) def test_n_tracks(self): """Test the n-tracks property.""" @@ -116,17 +96,6 @@ class TestTrackView(tests.util.TestCase): self.db_plist.add_track(self.track) self.assertEqual(self.trackview.runtime, 10.0) - def test_n_selected(self): - """Test the n-selected and have-selected properties.""" - self.assertEqual(self.trackview.n_selected, 0) - self.assertFalse(self.trackview.have_selected) - - self.trackview._osd.n_selected = 4 - self.trackview._osd.have_selected = True - - self.assertEqual(self.trackview.n_selected, 4) - self.assertTrue(self.trackview.have_selected) - class TestTrackViewColumns(tests.util.TestCase): """Test the TrackView Columns."""