diff --git a/emmental/sidebar/icon.py b/emmental/sidebar/icon.py new file mode 100644 index 0000000..fd3c2c2 --- /dev/null +++ b/emmental/sidebar/icon.py @@ -0,0 +1,34 @@ +# Copyright 2022 (c) Anna Schumaker. +"""Custom icon widgets for playlist rows.""" +from gi.repository import GObject +from gi.repository import Gdk +from gi.repository import Adw + + +class Icon(Adw.Bin): + """A custom Adw.Avatar that can load images from a file path.""" + + text = GObject.Property(type=str) + filepath = GObject.Property(type=GObject.TYPE_PYOBJECT) + icon_name = GObject.Property(type=str) + show_initials = GObject.Property(type=bool, default=False) + + def __init__(self, **kwargs): + """Initialize our Playlist icon.""" + super().__init__(**kwargs) + self._icon = Adw.Avatar(size=40, show_initials=self.show_initials) + + self.bind_property("text", self._icon, "text") + self.bind_property("icon-name", self._icon, "icon-name") + self.bind_property("show-initials", self._icon, "show-initials") + + self.connect("notify::filepath", self.__notify_filepath) + + self.set_child(self._icon) + + def __notify_filepath(self, icon: Adw.Bin, param) -> None: + if self.filepath is None: + texture = None + else: + texture = Gdk.Texture.new_from_filename(str(self.filepath)) + self._icon.set_custom_image(texture) diff --git a/tests/sidebar/test_icon.py b/tests/sidebar/test_icon.py new file mode 100644 index 0000000..f219e4c --- /dev/null +++ b/tests/sidebar/test_icon.py @@ -0,0 +1,62 @@ +# Copyright 2022 (c) Anna Schumaker. +"""Tests our custom icon widgets.""" +import unittest +import emmental.sidebar.icon +import tests.util +from gi.repository import Gdk +from gi.repository import Adw + + +class TestIcon(unittest.TestCase): + """Test our icon that can also be set from filepath.""" + + def setUp(self): + """Set up common variables.""" + self.icon = emmental.sidebar.icon.Icon() + + def test_init(self): + """Test that the icon is set up properly.""" + self.assertIsInstance(self.icon, Adw.Bin) + self.assertIsInstance(self.icon._icon, Adw.Avatar) + + self.assertEqual(self.icon._icon.get_size(), 40) + self.assertEqual(self.icon.get_child(), self.icon._icon) + + def test_icon_name(self): + """Test setting an icon name to the inner icon widget.""" + self.assertEqual(self.icon.icon_name, "") + self.icon.icon_name = "audio-x-generic" + self.assertEqual(self.icon._icon.get_icon_name(), "audio-x-generic") + + def test_text(self): + """Test the text property.""" + self.assertEqual(self.icon.text, "") + self.icon.text = "Test Playlist" + self.assertEqual(self.icon._icon.get_text(), "Test Playlist") + + def test_show_initials(self): + """Test the show initials property.""" + self.assertFalse(self.icon.show_initials) + + self.icon.show_initials = True + self.assertTrue(self.icon._icon.get_show_initials()) + + icon2 = emmental.sidebar.icon.Icon(show_initials=True) + self.assertTrue(icon2._icon.get_show_initials()) + + def test_filepath(self): + """Test the filepath property.""" + self.assertIsNone(self.icon.filepath) + + with unittest.mock.patch("gi.repository.Gdk.Texture.new_from_filename", + wraps=Gdk.Texture.new_from_filename) \ + as mock_new: + self.icon.filepath = tests.util.COVER_JPG + mock_new.assert_called_with(str(tests.util.COVER_JPG)) + self.assertIsInstance(self.icon._icon.get_custom_image(), + Gdk.Texture) + + mock_new.reset_mock() + self.icon.filepath = None + self.assertIsNone(self.icon._icon.get_custom_image()) + mock_new.assert_not_called()