tracklist: Create a basic Tracklist widget

For now it only has the Gtk.CenterBox child with an entry.Filter widget,
but this will be expanded on in future patches. I also take the chance
to bind the factory:visible-playlist property to the playlist displayed
in the tracklist.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
Anna Schumaker 2022-09-09 09:48:42 -04:00
parent 0d27a09233
commit 2c2462c3d6
4 changed files with 123 additions and 1 deletions

View File

@ -11,6 +11,7 @@ from . import nowplaying
from . import options
from . import playlist
from . import sidebar
from . import tracklist
from . import window
from gi.repository import GObject
from gi.repository import GLib
@ -156,12 +157,19 @@ class Application(Adw.Application):
"show-all-artists")
return side_bar
def build_tracklist(self) -> tracklist.Card:
"""Build a new tracklist card."""
track_list = tracklist.Card(sql=self.db)
self.factory.bind_property("visible-playlist", track_list, "playlist")
return track_list
def build_window(self) -> window.Window:
"""Build a new window instance."""
win = window.Window(VERSION_STRING,
header=self.build_header(),
now_playing=self.build_now_playing(),
sidebar=self.build_sidebar())
sidebar=self.build_sidebar(),
tracklist=self.build_tracklist())
for (setting, property) in [("window.width", "default-width"),
("window.height", "default-height"),

View File

@ -0,0 +1,33 @@
# Copyright 2022 (c) Anna Schumaker.
"""A card for displaying a list of tracks."""
from gi.repository import GObject
from gi.repository import Gtk
from .. import db
from .. import entry
from .. import playlist
class Card(Gtk.Box):
"""Our Tracklist."""
sql = GObject.Property(type=db.Connection)
playlist = GObject.Property(type=playlist.playlist.Playlist)
def __init__(self, sql: db.Connection, **kwargs):
"""Set up the Tracklist widget."""
super().__init__(sql=sql, orientation=Gtk.Orientation.VERTICAL,
spacing=6, **kwargs)
self._top_box = Gtk.CenterBox(margin_top=6, margin_start=6,
margin_end=6)
self._filter = entry.Filter("tracks", hexpand=True,
margin_start=100, margin_end=100)
self._top_box.set_center_widget(self._filter)
self.append(self._top_box)
self._filter.connect("search-changed", self.__search_changed)
self.add_css_class("card")
def __search_changed(self, filter: entry.Filter) -> None:
self.sql.tracks.filter(filter.get_query())

View File

@ -109,6 +109,7 @@ class TestEmmental(unittest.TestCase):
self.assertIsInstance(win.header, emmental.header.Header)
self.assertIsInstance(win.now_playing, emmental.nowplaying.Card)
self.assertIsInstance(win.sidebar, emmental.sidebar.Card)
self.assertIsInstance(win.tracklist, emmental.tracklist.Card)
self.assertEqual(win.header.title, emmental.VERSION_STRING)
@ -168,6 +169,22 @@ class TestEmmental(unittest.TestCase):
self.assertEqual(win.sidebar.sql, self.application.db)
@unittest.mock.patch("sys.stdout", new_callable=io.StringIO)
def test_tracklist(self, new_callable: io.StringIO):
"""Check that the tracklist widget is wired up properly."""
self.application.db = emmental.db.Connection()
self.application.factory = emmental.playlist.Factory(
self.application.db)
self.application.player = emmental.audio.Player()
win = self.application.build_window()
self.assertEqual(win.tracklist.sql, self.application.db)
playlist = self.application.db.playlists.create("Test Playlist")
self.application.factory.db_visible = playlist
self.assertEqual(win.tracklist.playlist,
self.application.factory.visible_playlist)
@unittest.mock.patch("sys.stdout", new_callable=io.StringIO)
def test_playlist_factory(self, mock_stdout: io.StringIO):
"""Test that the Playlist Factory is wired up properly."""

View File

@ -0,0 +1,64 @@
# Copyright 2022 (c) Anna Schumaker.
"""Tests our Tracklist card."""
import unittest.mock
import emmental.tracklist
import tests.util
from gi.repository import Gtk
class TestTracklist(tests.util.TestCase):
"""Tests the Tracklist card."""
def setUp(self):
"""Set up common variables."""
super().setUp()
self.tracklist = emmental.tracklist.Card(self.sql)
self.db_plist = self.sql.playlists.create("Test Playlist")
self.playlist = emmental.playlist.playlist.Playlist(self.sql,
self.db_plist)
def test_init(self):
"""Test that the Tracklist has been set up correctly."""
self.assertIsInstance(self.tracklist, Gtk.Box)
self.assertIsInstance(self.tracklist._top_box, Gtk.CenterBox)
self.assertEqual(self.tracklist.sql, self.sql)
self.assertEqual(self.tracklist.get_spacing(), 6)
self.assertEqual(self.tracklist.get_orientation(),
Gtk.Orientation.VERTICAL)
self.assertEqual(self.tracklist._top_box.get_margin_top(), 6)
self.assertEqual(self.tracklist._top_box.get_margin_start(), 6)
self.assertEqual(self.tracklist._top_box.get_margin_end(), 6)
self.assertEqual(self.tracklist._top_box.get_orientation(),
Gtk.Orientation.HORIZONTAL)
self.assertEqual(self.tracklist.get_first_child(),
self.tracklist._top_box)
self.assertTrue(self.tracklist.has_css_class("card"))
def test_filter(self):
"""Test the Tracklist Filter entry."""
self.assertIsInstance(self.tracklist._filter, emmental.entry.Filter)
self.assertEqual(self.tracklist._top_box.get_center_widget(),
self.tracklist._filter)
self.assertEqual(self.tracklist._filter.get_margin_start(), 100)
self.assertEqual(self.tracklist._filter.get_margin_end(), 100)
self.assertEqual(self.tracklist._filter.get_placeholder_text(),
"type to filter tracks")
self.assertTrue(self.tracklist._filter.get_hexpand())
with unittest.mock.patch.object(self.sql.tracks,
"filter") as mock_filter:
self.tracklist._filter.set_text("test text")
self.tracklist._filter.emit("search-changed")
mock_filter.assert_called_with("*test text*")
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)