tracklist: Scroll to the requested Track

And wire this up to not only the Now Playing "jump" signal, but also the
next track pickers so we scroll when tracks are changed.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
Anna Schumaker 2023-04-28 13:57:09 -04:00
parent 481c4856c7
commit a485a3806b
4 changed files with 45 additions and 0 deletions

View File

@ -64,12 +64,17 @@ class Application(Adw.Application):
else:
track.start()
self.__set_replaygain(rg_auto=rg_auto)
self.__on_jump()
def __pick_next_track(self, *, user: bool, gapless: bool = False) -> None:
(track, rg_auto, restart) = self.factory.next_track(user=user)
self.__load_track(track, gapless=gapless,
rg_auto=rg_auto, restart=restart)
def __on_jump(self, nowplay: nowplaying.Card | None = None) -> None:
"""Handle a jump event."""
self.win.tracklist.scroll_to_track(self.db.tracks.current_track)
def __on_seek(self, nowplay: nowplaying.Card, newpos: float) -> None:
"""Handle a seek event."""
self.player.seek(newpos)
@ -114,6 +119,7 @@ class Application(Adw.Application):
self.player.file = track_table.current_track.path
self.player.pause()
track_table.current_track.start()
self.__on_jump()
def build_header(self) -> header.Header:
"""Build a new header instance."""
@ -147,6 +153,7 @@ class Application(Adw.Application):
self.db.settings.bind_setting("now-playing.prefer-artist",
playing, "prefer-artist")
playing.connect("jump", self.__on_jump)
playing.connect("play", self.player.play)
playing.connect("pause", self.player.pause)
playing.connect("seek", self.__on_seek)

View File

@ -1,6 +1,7 @@
# Copyright 2022 (c) Anna Schumaker.
"""A card for displaying a list of tracks."""
from gi.repository import GObject
from gi.repository import GLib
from gi.repository import Gio
from gi.repository import Gtk
from .. import db
@ -39,6 +40,15 @@ class Card(Gtk.Box):
def __search_changed(self, filter: entry.Filter) -> None:
self.sql.tracks.filter(filter.get_query())
def __scroll_idle(self, track: db.tracks.Track) -> bool:
self._trackview.scroll_to_track(track)
return GLib.SOURCE_REMOVE
def scroll_to_track(self, track: db.tracks.Track) -> None:
"""Scroll to the requested Track."""
if self.playlist is not None:
GLib.idle_add(self.__scroll_idle, track)
@GObject.Property(type=Gio.ListModel)
def columns(self) -> Gio.ListModel:
"""Get the columns displayed in the Tracklist."""

View File

@ -77,6 +77,16 @@ class TrackView(Gtk.Frame):
self.playlist.request_track(self._selection[position])
self._selection.unselect_all()
def scroll_to_track(self, track: db.tracks.Track) -> None:
"""Scroll to the requested Track."""
# This is a workaround until the ColumnView has better scrolling
# support, which seems to be targeted for Gtk 4.10.
adjustment = self._scrollwin.get_vadjustment()
for (i, t) in enumerate(self._selection):
if t == track:
pos = max(i - 3, 0) * adjustment.get_upper()
adjustment.set_value(pos / self._selection.get_n_items())
@GObject.Property(type=Gio.ListModel)
def columns(self) -> Gio.ListModel:
"""Get the ListModel for the columns."""

View File

@ -3,6 +3,7 @@
import unittest.mock
import emmental.tracklist
import tests.util
from gi.repository import GLib
from gi.repository import Gtk
@ -75,3 +76,20 @@ class TestTracklist(tests.util.TestCase):
self.tracklist.playlist = self.playlist
self.assertEqual(self.tracklist.playlist, self.playlist)
self.assertEqual(self.tracklist._trackview.playlist, self.playlist)
@unittest.mock.patch("gi.repository.GLib.idle_add")
def test_scroll_to_track(self, mock_idle_add: unittest.mock.Mock):
"""Test the scroll_to_track() function."""
self.tracklist.scroll_to_track(None)
mock_idle_add.assert_not_called()
self.tracklist.playlist = self.playlist
self.tracklist.scroll_to_track(None)
mock_idle_add.assert_called_with(self.tracklist._Card__scroll_idle,
None)
with unittest.mock.patch.object(self.tracklist._trackview,
"scroll_to_track") as mock_scroll:
self.assertEqual(self.tracklist._Card__scroll_idle(None),
GLib.SOURCE_REMOVE)
mock_scroll.assert_called_with(None)