nowplaying: Add an Album Art class
This displays the current track's album art with a fancy frame drawn around it. Clicking the image opens a popover showing the artwork at full size. Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
parent
ee8db58fb2
commit
b3d04805d7
|
@ -0,0 +1,49 @@
|
|||
# Copyright 2022 (c) Anna Schumaker.
|
||||
"""Our custom Album Art widget."""
|
||||
import pathlib
|
||||
from gi.repository import GObject
|
||||
from gi.repository import Gtk
|
||||
from .. import gsetup
|
||||
|
||||
FALLBACK_RESOURCE = f"{gsetup.RESOURCE_ICONS}/emmental.svg"
|
||||
|
||||
|
||||
class Artwork(Gtk.Frame):
|
||||
"""Our custom Album Art widget that draws a border around a picture."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize the Album Art widget."""
|
||||
super().__init__(margin_top=6, margin_bottom=6, margin_start=6,
|
||||
margin_end=6, halign=Gtk.Align.CENTER,
|
||||
valign=Gtk.Align.CENTER)
|
||||
self._picture = Gtk.Picture(content_fit=Gtk.ContentFit.CONTAIN)
|
||||
self._fullsize = Gtk.Picture(content_fit=Gtk.ContentFit.FILL)
|
||||
self._popover = Gtk.Popover(child=self._fullsize)
|
||||
self._clicked = Gtk.GestureClick()
|
||||
|
||||
self._clicked.connect("released", self.clicked)
|
||||
self.add_controller(self._clicked)
|
||||
|
||||
self._popover.set_parent(self)
|
||||
self.set_child(self._picture)
|
||||
self.filepath = None
|
||||
|
||||
@GObject.Property(type=GObject.TYPE_PYOBJECT)
|
||||
def filepath(self) -> pathlib.Path:
|
||||
"""Get the current artwork path."""
|
||||
name = self._picture.get_file().get_parse_name()
|
||||
return None if name.startswith("resource:") else pathlib.Path(name)
|
||||
|
||||
@filepath.setter
|
||||
def filepath(self, path: pathlib.Path) -> None:
|
||||
if path is not None:
|
||||
self._picture.set_filename(str(path))
|
||||
self._fullsize.set_filename(str(path))
|
||||
else:
|
||||
self._picture.set_resource(FALLBACK_RESOURCE)
|
||||
self._fullsize.set_resource(FALLBACK_RESOURCE)
|
||||
|
||||
def clicked(self, gesture: Gtk.GestureClick, n_press: int,
|
||||
x: float, y: float) -> None:
|
||||
"""Handle a click event."""
|
||||
self._popover.popup()
|
|
@ -0,0 +1,90 @@
|
|||
# Copyright 2022 (c) Anna Schumaker.
|
||||
"""Tests our Album Art widget."""
|
||||
import unittest
|
||||
import emmental.nowplaying.artwork
|
||||
import tests.util
|
||||
from gi.repository import Gtk
|
||||
|
||||
|
||||
class TestArtwork(unittest.TestCase):
|
||||
"""Test case for our Album Art widget."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up common variables."""
|
||||
self.fallback = emmental.nowplaying.artwork.FALLBACK_RESOURCE
|
||||
self.artwork = emmental.nowplaying.artwork.Artwork()
|
||||
|
||||
def test_constants(self):
|
||||
"""Test constant values."""
|
||||
self.assertEqual(self.fallback,
|
||||
f"{emmental.gsetup.RESOURCE_ICONS}/emmental.svg")
|
||||
|
||||
def test_init(self):
|
||||
"""Test that the artwork widget is configured correctly."""
|
||||
self.assertIsInstance(self.artwork, Gtk.Frame)
|
||||
|
||||
self.assertEqual(self.artwork.get_halign(), Gtk.Align.CENTER)
|
||||
self.assertEqual(self.artwork.get_valign(), Gtk.Align.CENTER)
|
||||
|
||||
self.assertEqual(self.artwork.get_margin_top(), 6)
|
||||
self.assertEqual(self.artwork.get_margin_bottom(), 6)
|
||||
self.assertEqual(self.artwork.get_margin_start(), 6)
|
||||
self.assertEqual(self.artwork.get_margin_end(), 6)
|
||||
|
||||
def test_picture(self):
|
||||
"""Test that the artwork picture is configured correctly."""
|
||||
self.assertIsInstance(self.artwork._picture, Gtk.Picture)
|
||||
self.assertEqual(self.artwork._picture.get_content_fit(),
|
||||
Gtk.ContentFit.CONTAIN)
|
||||
self.assertEqual(self.artwork.get_child(), self.artwork._picture)
|
||||
|
||||
self.assertIsNone(self.artwork.filepath)
|
||||
self.assertIsNotNone(self.artwork._picture.get_paintable())
|
||||
self.assertEqual(self.artwork._picture.get_file().get_parse_name(),
|
||||
f"resource://{self.fallback}")
|
||||
|
||||
self.artwork.filepath = tests.util.COVER_JPG
|
||||
self.assertIsNotNone(self.artwork._picture.get_paintable())
|
||||
self.assertEqual(self.artwork._picture.get_file().get_parse_name(),
|
||||
str(tests.util.COVER_JPG))
|
||||
self.assertEqual(self.artwork.filepath, tests.util.COVER_JPG)
|
||||
|
||||
self.artwork.filepath = None
|
||||
self.assertIsNotNone(self.artwork._picture.get_paintable())
|
||||
self.assertEqual(self.artwork._picture.get_file().get_parse_name(),
|
||||
f"resource://{self.fallback}")
|
||||
|
||||
def test_fullsize(self):
|
||||
"""Test that the fullsize picture is configured correctly."""
|
||||
self.assertIsInstance(self.artwork._fullsize, Gtk.Picture)
|
||||
self.assertEqual(self.artwork._fullsize.get_content_fit(),
|
||||
Gtk.ContentFit.FILL)
|
||||
self.assertEqual(self.artwork._popover.get_child(),
|
||||
self.artwork._fullsize)
|
||||
|
||||
self.assertIsNotNone(self.artwork._fullsize.get_paintable())
|
||||
self.assertEqual(self.artwork._fullsize.get_file().get_parse_name(),
|
||||
f"resource://{self.fallback}")
|
||||
|
||||
self.artwork.filepath = tests.util.COVER_JPG
|
||||
self.assertIsNotNone(self.artwork._fullsize.get_paintable())
|
||||
self.assertEqual(self.artwork._fullsize.get_file().get_parse_name(),
|
||||
str(tests.util.COVER_JPG))
|
||||
|
||||
self.artwork.filepath = None
|
||||
self.assertIsNotNone(self.artwork._fullsize.get_paintable())
|
||||
self.assertEqual(self.artwork._fullsize.get_file().get_parse_name(),
|
||||
f"resource://{self.fallback}")
|
||||
|
||||
def test_popover(self):
|
||||
"""Test that the popover picture is configured correctly."""
|
||||
self.assertIsInstance(self.artwork._popover, Gtk.Popover)
|
||||
self.assertIsInstance(self.artwork._clicked, Gtk.GestureClick)
|
||||
|
||||
self.assertEqual(self.artwork._popover.get_parent(), self.artwork)
|
||||
self.assertEqual(self.artwork._clicked.get_widget(), self.artwork)
|
||||
|
||||
with unittest.mock.patch.object(self.artwork._popover,
|
||||
"popup") as mock_popup:
|
||||
self.artwork._clicked.emit("released", 1, 1, 1)
|
||||
mock_popup.assert_called()
|
Loading…
Reference in New Issue