From 2504f4b91ddcfe5056c571ffc652551407b577ca Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Wed, 30 Aug 2023 13:22:28 -0400 Subject: [PATCH] sidebar: Add a timeout when selecting playlists on load I've found that during startup, we sometimes try to select the current playlist before the Gtk sidebar widgets are completely loaded. This results showing the right section, but not actually selecting a playlist. We can fix this by selecting the playlist after a short timeout to give everything a chance to load. Signed-off-by: Anna Schumaker --- emmental/sidebar/__init__.py | 43 +++++++++++------- tests/sidebar/test_sidebar.py | 83 +++++++++++++++++++---------------- 2 files changed, 72 insertions(+), 54 deletions(-) diff --git a/emmental/sidebar/__init__.py b/emmental/sidebar/__init__.py index f460fb1..b9a4b89 100644 --- a/emmental/sidebar/__init__.py +++ b/emmental/sidebar/__init__.py @@ -1,6 +1,7 @@ # Copyright 2022 (c) Anna Schumaker. """A card for displaying the list of playlists.""" from gi.repository import GObject +from gi.repository import GLib from gi.repository import Gtk from . import artist from . import decade @@ -66,27 +67,37 @@ class Card(Gtk.Box): if self.get_sensitive() is False: if False not in {tbl.loaded for tbl in sql.playlist_tables()}: self.set_sensitive(True) - self.select_playlist(sql.active_playlist) + self.select_playlist(sql.active_playlist, 150) if len(sql.libraries) == 0: self._libraries.extra_widget.emit("clicked") - def select_playlist(self, playlist: db.playlist.Playlist) -> None: - """Set the current active playlist.""" + def __select_playlist(self, playlist: db.playlist.Playlist) -> bool: if playlist is not None: - match playlist.table: - case self.sql.playlists: - section = self._playlists - case self.sql.artists | self.sql.albums | self.sql.media: - section = self._artists - case self.sql.genres: - section = self._genres - case self.sql.decades | self.sql.years: - section = self._decades - case self.sql.libraries: - section = self._libraries - - section.active = True + section = self.table_section(playlist.table) + if not section.active: + section.active = True + return GLib.SOURCE_CONTINUE section.select_playlist(playlist) + return GLib.SOURCE_REMOVE + + def select_playlist(self, playlist: db.playlist.Playlist, + timeout: int = 0) -> None: + """Set the current active playlist.""" + GLib.timeout_add(timeout, self.__select_playlist, playlist) + + def table_section(self, table: db.playlist.Table) -> section.Section: + """Get the Section associated with a specific Playlist Table.""" + match table: + case self.sql.playlists: + return self._playlists + case self.sql.artists | self.sql.albums | self.sql.media: + return self._artists + case self.sql.genres: + return self._genres + case self.sql.decades | self.sql.years: + return self._decades + case self.sql.libraries: + return self._libraries @property def accelerators(self) -> list[ActionEntry]: diff --git a/tests/sidebar/test_sidebar.py b/tests/sidebar/test_sidebar.py index 4ba75fd..7aa6979 100644 --- a/tests/sidebar/test_sidebar.py +++ b/tests/sidebar/test_sidebar.py @@ -3,6 +3,7 @@ import emmental.sidebar import tests.util import unittest.mock +from gi.repository import GLib from gi.repository import Gtk @@ -73,10 +74,13 @@ class TestSidebar(tests.util.TestCase): self.assertFalse(self.sidebar.get_sensitive()) self.sidebar.select_playlist.assert_not_called() self.sidebar._libraries.extra_widget.emit.assert_not_called() - self.sql.emit("table-loaded", table) - self.assertTrue(self.sidebar.get_sensitive()) - self.sidebar.select_playlist.assert_called() + table.load(now=True) + self.assertEqual(self.sidebar.get_sensitive(), + table == tables[-1]) + + playlist = self.sql.playlists.collection + self.sidebar.select_playlist.assert_called_with(playlist, 150) self.sidebar._libraries.extra_widget.emit.assert_called_with("clicked") self.sidebar.select_playlist.reset_mock() @@ -147,45 +151,48 @@ class TestSidebar(tests.util.TestCase): def test_select_playlist(self): """Test setting the active playlist.""" + self.assertEqual(self.sidebar._Card__select_playlist(None), + GLib.SOURCE_REMOVE) + playlist = self.sql.playlists.create("Test Playlist") - self.sidebar.select_playlist(playlist) - self.assertTrue(self.sidebar._playlists.active) - self.assertEqual(self.sidebar.selected_playlist, playlist) + with unittest.mock.patch.object(self.sidebar._playlists, + "select_playlist") as mock_select: + self.assertEqual(self.sidebar._Card__select_playlist(playlist), + GLib.SOURCE_CONTINUE) + self.assertTrue(self.sidebar._playlists.active) + mock_select.assert_not_called() - artist = self.sql.artists.create("Test Artist") - album = self.sql.albums.create("Test Album", "Test Artist", "2023") - medium = self.sql.media.create(album, "Test Medium", number=1) + self.assertEqual(self.sidebar._Card__select_playlist(playlist), + GLib.SOURCE_REMOVE) + mock_select.assert_called_with(playlist) - self.sidebar._artists.select_playlist = unittest.mock.Mock() - for plist in [artist, album, medium]: - self.sidebar._artists.select_playlist.reset_mock() - self.sidebar._artists.active = False + with unittest.mock.patch.object(GLib, "timeout_add") as mock_to: + self.sidebar.select_playlist(playlist) + mock_to.assert_called_with(0, self.sidebar._Card__select_playlist, + playlist) + self.sidebar.select_playlist(playlist, 42) + mock_to.assert_called_with(42, self.sidebar._Card__select_playlist, + playlist) - self.sidebar.select_playlist(plist) - self.assertTrue(self.sidebar._artists.active) - self.sidebar._artists.select_playlist.assert_called_with(plist) - - genre = self.sql.genres.create("Test Genre") - self.sidebar.select_playlist(genre) - self.assertTrue(self.sidebar._genres.active) - self.assertEqual(self.sidebar.selected_playlist, genre) - - decade = self.sql.decades.create(1990) - year = self.sql.years.create(1990) - - self.sidebar._decades.select_playlist = unittest.mock.Mock() - for plist in [decade, year]: - self.sidebar._decades.select_playlist.reset_mock() - self.sidebar._decades.active = False - - self.sidebar.select_playlist(plist) - self.assertTrue(self.sidebar._decades.active) - self.sidebar._decades.select_playlist.assert_called_with(plist) - - library = self.sql.libraries.create("/a/b/c") - self.sidebar.select_playlist(library) - self.assertTrue(self.sidebar._libraries.active) - self.assertEqual(self.sidebar.selected_playlist, library) + def test_table_section(self): + """Test converting a Playlist database table into a Section.""" + self.assertEqual(self.sidebar.table_section(self.sql.playlists), + self.sidebar._playlists) + self.assertEqual(self.sidebar.table_section(self.sql.artists), + self.sidebar._artists) + self.assertEqual(self.sidebar.table_section(self.sql.albums), + self.sidebar._artists) + self.assertEqual(self.sidebar.table_section(self.sql.media), + self.sidebar._artists) + self.assertEqual(self.sidebar.table_section(self.sql.genres), + self.sidebar._genres) + self.assertEqual(self.sidebar.table_section(self.sql.decades), + self.sidebar._decades) + self.assertEqual(self.sidebar.table_section(self.sql.years), + self.sidebar._decades) + self.assertEqual(self.sidebar.table_section(self.sql.libraries), + self.sidebar._libraries) + self.assertIsNone(self.sidebar.table_section(None)) def test_accelerators(self): """Check that the accelerators list is set up properly."""