tracklist: Change the "shuffle disabled" icon based on icon theme

The Breeze "media-playlist-consecutive" icon looks terrible, and we want
to use "media-playlist-normal" to get the same look as with the Adwaita
icon theme. Unfortunately, Adwaita doesn't have the
"media-playlist-normal" icon. So I created a function to ask the icon
theme if it has a specific icon, and modify the shuffle button to change
the inactive icon when toggled to keep up with icon theme changes.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
Anna Schumaker 2024-01-20 14:03:44 -05:00
parent c37ae94a5d
commit 3c25dc2c7f
4 changed files with 60 additions and 3 deletions

View File

@ -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}"

View File

@ -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):

View File

@ -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(),

View File

@ -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