tracklist: Create a TrackView

The TrackView sets up a scrollable Gtk.ColumnView inside a nice looking
frame. It also creates a FilterListModel for filtering tracks in a
separate layer from the Playlist so we don't affect choosing the next
track.

Finally, I add the TrackView to the TrackList Card.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
Anna Schumaker 2022-10-18 16:19:35 -04:00
parent 2c2462c3d6
commit 999a3eb523
4 changed files with 136 additions and 0 deletions

View File

@ -5,6 +5,7 @@ from gi.repository import Gtk
from .. import db
from .. import entry
from .. import playlist
from . import trackview
class Card(Gtk.Box):
@ -21,9 +22,14 @@ class Card(Gtk.Box):
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._top_box.set_center_widget(self._filter)
self.append(self._top_box)
self.append(self._trackview)
self.bind_property("playlist", self._trackview, "playlist")
self._filter.connect("search-changed", self.__search_changed)

View File

@ -0,0 +1,38 @@
# Copyright 2022 (c) Anna Schumaker.
"""A Gtk.ColumnView for displaying Tracks."""
from gi.repository import GObject
from gi.repository import Gtk
from .. import db
from .. import playlist
class TrackView(Gtk.Frame):
"""A Gtk.ColumnView that has been configured to show Tracks."""
playlist = GObject.Property(type=playlist.playlist.Playlist)
n_tracks = GObject.Property(type=int)
runtime = GObject.Property(type=float)
def __init__(self, sql: db.Connection, **kwargs):
"""Initialize a TrackView."""
super().__init__(**kwargs)
self._filtermodel = Gtk.FilterListModel(filter=sql.tracks.get_filter(),
incremental=True)
self._selection = Gtk.MultiSelection(model=self._filtermodel)
self._columnview = Gtk.ColumnView(hexpand=True, vexpand=True,
show_row_separators=True,
enable_rubberband=True,
model=self._selection)
self._scrollwin = Gtk.ScrolledWindow(child=self._columnview)
self.bind_property("playlist", self._filtermodel, "model")
self._selection.bind_property("n-items", self, "n-tracks")
self._selection.connect("items-changed", self.__runtime_changed)
self._columnview.add_css_class("emmental-track-list")
self.set_child(self._scrollwin)
def __runtime_changed(self, selection: Gtk.MultiSelection,
position: int, removed: int, added: int) -> None:
self.runtime = sum(t.length for t in self._selection)

View File

@ -56,9 +56,20 @@ class TestTracklist(tests.util.TestCase):
self.tracklist._filter.emit("search-changed")
mock_filter.assert_called_with("*test text*")
def test_trackview(self):
"""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)
def test_playlist(self):
"""Test the playlist property."""
self.assertIsNone(self.tracklist.playlist)
self.tracklist.playlist = self.playlist
self.assertEqual(self.tracklist.playlist, self.playlist)
self.assertEqual(self.tracklist._trackview.playlist, self.playlist)

View File

@ -0,0 +1,81 @@
# Copyright 2022 (c) Anna Schumaker.
"""Tests our Track ColumnView."""
import pathlib
import emmental.tracklist.trackview
import tests.util
from gi.repository import Gtk
class TestTrackView(tests.util.TestCase):
"""Tests the TrackView widget."""
def setUp(self):
"""Set up common variables."""
super().setUp()
self.db_plist = self.sql.playlists.create("Test Playlist")
self.playlist = emmental.playlist.playlist.Playlist(self.sql,
self.db_plist)
self.trackview = emmental.tracklist.trackview.TrackView(self.sql)
self.library = self.sql.libraries.create(pathlib.Path("/a/b/"))
self.album = self.sql.albums.create("Test Album", "Artist", "2023")
self.medium = self.sql.media.create(self.album, "", number=1)
self.year = self.sql.years.create(2023)
self.track = self.sql.tracks.create(self.library,
pathlib.Path("/a/b/1.ogg"),
self.medium, self.year, length=10)
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.assertEqual(self.trackview.get_child(), self.trackview._scrollwin)
self.assertEqual(self.trackview._scrollwin.get_child(),
self.trackview._columnview)
def test_list_models(self):
"""Test our filter and selection list models."""
self.assertIsInstance(self.trackview._filtermodel, Gtk.FilterListModel)
self.assertIsInstance(self.trackview._selection, Gtk.MultiSelection)
self.assertEqual(self.trackview._filtermodel.get_filter(),
self.sql.tracks.get_filter())
self.assertTrue(self.trackview._filtermodel.get_incremental())
self.assertEqual(self.trackview._selection.get_model(),
self.trackview._filtermodel)
def test_columnview(self):
"""Test the columnview."""
self.assertIsInstance(self.trackview._columnview, Gtk.ColumnView)
self.assertEqual(self.trackview._columnview.get_model(),
self.trackview._selection)
self.assertTrue(self.trackview._columnview.get_hexpand())
self.assertTrue(self.trackview._columnview.get_vexpand())
self.assertTrue(self.trackview._columnview.get_show_row_separators())
self.assertTrue(self.trackview._columnview.get_enable_rubberband())
self.assertTrue(self.trackview._columnview.has_css_class(
"emmental-track-list"))
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)
def test_n_tracks(self):
"""Test the n-tracks property."""
self.assertEqual(self.trackview.n_tracks, 0)
self.trackview._selection.set_model(self.playlist)
self.db_plist.add_track(self.track)
self.assertEqual(self.trackview.n_tracks, 1)
def test_runtime(self):
"""Test the runtime property."""
self.assertEqual(self.trackview.runtime, 0)
self.trackview._selection.set_model(self.playlist)
self.db_plist.add_track(self.track)
self.assertEqual(self.trackview.runtime, 10.0)