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 <Anna@NoWheyCreamery.com>
This commit is contained in:
Anna Schumaker 2023-08-10 16:51:35 -04:00
parent 5011db344e
commit ddfd37130b
5 changed files with 66 additions and 70 deletions

View File

@ -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;

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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."""