diff --git a/emmental/gsetup.py b/emmental/gsetup.py index a12e9fb..e255248 100644 --- a/emmental/gsetup.py +++ b/emmental/gsetup.py @@ -43,6 +43,13 @@ def add_style(): CSS_PROVIDER, CSS_PRIORITY) +def has_icon(icon_name: str): + """Check if the icon theme has a specific icon.""" + display = gi.repository.Gdk.Display.get_default() + theme = gi.repository.Gtk.IconTheme.get_for_display(display) + return theme.has_icon(icon_name) + + def __version_string(subsystem, major, minor, micro): return f" ⋅ {subsystem} {major}.{minor}.{micro}" diff --git a/emmental/tracklist/buttons.py b/emmental/tracklist/buttons.py index 8e77115..6982173 100644 --- a/emmental/tracklist/buttons.py +++ b/emmental/tracklist/buttons.py @@ -5,6 +5,7 @@ from gi.repository import Gio from gi.repository import Gtk from . import sorter from .. import buttons +from .. import gsetup class VisibleRow(Gtk.ListBoxRow): @@ -107,7 +108,7 @@ class ShuffleButton(buttons.ImageToggle): """Initialize a Shuffle Button.""" super().__init__(active_icon_name="media-playlist-shuffle", active_tooltip_text="shuffle: enabled", - inactive_icon_name="media-playlist-consecutive", + inactive_icon_name=self.get_inactive_icon(), inactive_tooltip_text="shuffle: disabled", large_icon=False, icon_opacity=0.5, has_frame=False, **kwargs) @@ -115,6 +116,13 @@ class ShuffleButton(buttons.ImageToggle): def do_toggled(self): """Adjust opacity when active state toggles.""" self.icon_opacity = 1.0 if self.active else 0.5 + self.inactive_icon_name = self.get_inactive_icon() + + def get_inactive_icon(self) -> str: + """Return the inactive icon name.""" + if gsetup.has_icon("media-playlist-normal"): + return "media-playlist-normal" + return "media-playlist-consecutive" class SortRow(Gtk.ListBoxRow): diff --git a/tests/test_gsetup.py b/tests/test_gsetup.py index 5d4d2db..3f1195c 100644 --- a/tests/test_gsetup.py +++ b/tests/test_gsetup.py @@ -73,6 +73,11 @@ class TestGSetup(unittest.TestCase): data_path = xdg.BaseDirectory.save_data_path("emmental") self.assertEqual(emmental.gsetup.DATA_DIR, pathlib.Path(data_path)) + def test_has_icon(self): + """Check that has_icon() works as expected.""" + self.assertTrue(emmental.gsetup.has_icon("media-playback-start")) + self.assertFalse(emmental.gsetup.has_icon("no-such-icon")) + def test_env_string(self): """Check that the env_string() function works as expected.""" self.assertRegex(emmental.gsetup.env_string(), diff --git a/tests/tracklist/test_buttons.py b/tests/tracklist/test_buttons.py index f906fe8..c34e938 100644 --- a/tests/tracklist/test_buttons.py +++ b/tests/tracklist/test_buttons.py @@ -195,13 +195,50 @@ class TestShuffleButtons(unittest.TestCase): "media-playlist-shuffle") self.assertEqual(self.shuffle.active_tooltip_text, "shuffle: enabled") - self.assertEqual(self.shuffle.inactive_icon_name, - "media-playlist-consecutive") self.assertEqual(self.shuffle.inactive_tooltip_text, "shuffle: disabled") self.assertAlmostEqual(self.shuffle.icon_opacity, 0.5, delta=0.005) + @unittest.mock.patch("emmental.gsetup.has_icon") + def test_get_inactive_icon(self, mock_has_icon: unittest.mock.Mock): + """Test the get_inactive_icon() function.""" + mock_has_icon.return_value = True + self.assertEqual(self.shuffle.get_inactive_icon(), + "media-playlist-normal") + mock_has_icon.assert_called() + + mock_has_icon.return_value = False + self.assertEqual(self.shuffle.get_inactive_icon(), + "media-playlist-consecutive") + + @unittest.mock.patch("emmental.gsetup.has_icon") + def test_inactive_icon_name(self, mock_has_icon: unittest.mock.Mock): + """Test setting the inactive icon name.""" + mock_has_icon.return_value = True + button = emmental.tracklist.buttons.ShuffleButton() + mock_has_icon.assert_called_with("media-playlist-normal") + self.assertEqual(button.inactive_icon_name, "media-playlist-normal") + + mock_has_icon.return_value = False + button = emmental.tracklist.buttons.ShuffleButton() + self.assertEqual(button.inactive_icon_name, + "media-playlist-consecutive") + + @unittest.mock.patch("emmental.gsetup.has_icon") + def test_toggled(self, mock_has_icon: unittest.mock.Mock): + """Test changing the icon when toggled.""" + mock_has_icon.return_value = True + self.shuffle.active = True + self.assertEqual(self.shuffle.inactive_icon_name, + "media-playlist-normal") + mock_has_icon.assert_called() + + mock_has_icon.return_value = False + self.shuffle.active = False + self.assertEqual(self.shuffle.inactive_icon_name, + "media-playlist-consecutive") + def test_opacity(self): """Test adjusting the opacity based on active state.""" self.shuffle.active = True