nowplaying: Give the Controls accelerator functions and properties

I create can-activate-* properties to indicate if a specific accelerator
can be activated. At the same time, I introduce functions intended to be
called by accelerators to activate each of our widgets.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
Anna Schumaker 2023-06-08 12:10:55 -04:00
parent 87b92ffc90
commit 8d72e1375f
6 changed files with 124 additions and 4 deletions

View File

@ -201,6 +201,7 @@ class Application(Adw.Application):
now_playing=self.build_now_playing(),
sidebar=self.build_sidebar(),
tracklist=self.build_tracklist())
win.bind_property("user-editing", win.now_playing, "editing")
for (setting, property) in [("window.width", "default-width"),
("window.height", "default-height"),

View File

@ -28,6 +28,7 @@ class Card(Gtk.Box):
have_previous = GObject.Property(type=bool, default=False)
have_track = GObject.Property(type=bool, default=False)
have_db_track = GObject.Property(type=bool, default=False)
editing = GObject.Property(type=bool, default=False)
def __init__(self):
"""Initialize a Now Playing Card."""
@ -55,7 +56,8 @@ class Card(Gtk.Box):
self.bind_property(prop, self._tags, prop)
self.bind_property("prefer-artist", self._tags, "prefer-artist",
GObject.BindingFlags.BIDIRECTIONAL)
for prop in ["playing", "have-next", "have-previous", "have-track"]:
for prop in ["playing", "editing", "have-next",
"have-previous", "have-track"]:
self.bind_property(prop, self._controls, prop)
self.bind_property("have-db-track", self._jump, "sensitive")
self.bind_property("have-db-track", self._favorite, "sensitive")

View File

@ -28,6 +28,11 @@ class Controls(Gtk.Box):
have_previous = GObject.Property(type=bool, default=False)
have_track = GObject.Property(type=bool, default=False)
editing = GObject.Property(type=bool, default=False)
can_activate_next = GObject.Property(type=bool, default=False)
can_activate_prev = GObject.Property(type=bool, default=False)
can_activate_play_pause = GObject.Property(type=bool, default=False)
def __init__(self):
"""Initialize the Controls."""
super().__init__(valign=Gtk.Align.START, homogeneous=True,
@ -65,19 +70,59 @@ class Controls(Gtk.Box):
self.bind_property("have-previous", self._prev, "sensitive")
self.bind_property("have-track", self._play, "sensitive")
self.bind_property("have-track", self._pause, "sensitive")
self.connect("notify::playing", self.__notify_playing)
self.connect("notify", self.__notify)
self.add_css_class("linked")
def __on_click(self, button: Gtk.Button, signal: str) -> None:
self.emit(signal)
def __notify_playing(self, controls, param) -> None:
def __notify_playing(self, controls: Gtk.Box, param) -> None:
if not self.playing and self.autopause != -1:
if win := self.get_ancestor(window.Window):
win.post_toast("Autopause Cancelled")
self.autopause = -1
def __notify(self, controls: Gtk.Box, param) -> None:
match param.name:
case "editing":
allowed = not self.editing
self.can_activate_next = self.have_next and allowed
self.can_activate_prev = self.have_previous and allowed
self.can_activate_play_pause = self.have_track and allowed
case "playing": self.__notify_playing(controls, param)
case "have-next":
self.can_activate_next = self.have_next and not self.editing
case "have-previous":
can_activate = self.have_previous and not self.editing
self.can_activate_prev = can_activate
case "have-track":
can_activate = self.have_track and not self.editing
self.can_activate_play_pause = can_activate
def activate_next(self) -> None:
"""Activate the Next button."""
self._next.activate()
def activate_previous(self) -> None:
"""Activate the Previous button."""
self._prev.activate()
def activate_play_pause(self) -> None:
"""Activate the Play or Pause button."""
if self.playing:
self._pause.activate()
else:
self._play.activate()
def decrease_autopause(self) -> None:
"""Decrease the autopause count."""
self._autopause.decrement()
def increase_autopause(self) -> None:
"""Increase the autopause count."""
self._autopause.increment()
@GObject.Signal
def previous(self) -> None:
"""Signals that the Previous button has been clicked."""

View File

@ -47,6 +47,8 @@ class TestControls(unittest.TestCase):
self.assertEqual(self.controls.get_margin_end(),
emmental.nowplaying.controls.MARGIN)
self.assertFalse(self.controls.editing)
def test_previous_button(self):
"""Test the previous button."""
self.assertIsInstance(self.controls._prev,
@ -60,6 +62,19 @@ class TestControls(unittest.TestCase):
self.controls._prev.emit("clicked")
self.clicked.assert_called_with(self.controls._prev)
def test_activate_previous(self):
"""Test can-activate-prev and the activate_previous() function."""
self.assertFalse(self.controls.can_activate_prev)
self.controls.have_previous = True
self.assertTrue(self.controls.can_activate_prev)
self.controls.editing = True
self.assertFalse(self.controls.can_activate_prev)
activate = unittest.mock.Mock()
self.controls._prev.connect("activate", activate)
self.controls.activate_previous()
activate.assert_called()
def test_play_button(self):
"""Test the play button."""
self.assertIsInstance(self.controls._play,
@ -101,6 +116,25 @@ class TestControls(unittest.TestCase):
self.controls._pause.emit("clicked")
self.clicked.assert_called_with(self.controls._pause)
def test_activate_play_pause(self):
"""Test can-activate-play-pause and the activate_play_pause() func."""
self.assertFalse(self.controls.can_activate_play_pause)
self.controls.have_track = True
self.assertTrue(self.controls.can_activate_play_pause)
self.controls.editing = True
self.assertFalse(self.controls.can_activate_play_pause)
play = unittest.mock.Mock()
self.controls._play.connect("activate", play)
self.controls.activate_play_pause()
play.assert_called()
self.controls.playing = True
pause = unittest.mock.Mock()
self.controls._pause.connect("activate-primary", pause)
self.controls.activate_play_pause()
pause.assert_called()
def test_autopause_button(self):
"""Test the autopause button."""
self.assertIsInstance(self.controls._autopause,
@ -127,6 +161,33 @@ class TestControls(unittest.TestCase):
self.controls._next.emit("clicked")
self.clicked.assert_called_with(self.controls._next)
def test_activate_next(self):
"""Test can-activate-next and the activate_next() function."""
self.assertFalse(self.controls.can_activate_next)
self.controls.have_next = True
self.assertTrue(self.controls.can_activate_next)
self.controls.editing = True
self.assertFalse(self.controls.can_activate_next)
activate = unittest.mock.Mock()
self.controls._next.connect("activate", activate)
self.controls.activate_next()
activate.assert_called()
def test_decrease_autopause(self):
"""Test the decrease_autopause() function."""
with unittest.mock.patch.object(self.controls._autopause,
"decrement") as mock_decrement:
self.controls.decrease_autopause()
mock_decrement.assert_called()
def test_increase_autopause(self):
"""Test the increase_autopause() function."""
with unittest.mock.patch.object(self.controls._autopause,
"increment") as mock_increment:
self.controls.increase_autopause()
mock_increment.assert_called()
def test_have_properties(self):
"""Test the have_{next, previous, track} properties."""
self.assertFalse(self.controls.have_next)

View File

@ -145,6 +145,14 @@ class TestNowPlaying(unittest.TestCase):
self.card._seeker.emit("change-value", Gtk.ScrollType.JUMP, 10)
handler.assert_called_with(self.card, 10)
def test_editing(self):
"""Test the 'editing' property."""
self.assertFalse(self.card.editing)
self.card.editing = True
self.assertTrue(self.card._controls.editing)
self.card.editing = False
self.assertFalse(self.card._controls.editing)
def test_playing(self):
"""Test the 'playing' property."""
self.assertFalse(self.card.playing)
@ -154,7 +162,7 @@ class TestNowPlaying(unittest.TestCase):
self.assertFalse(self.card._controls.playing)
def test_have_properties(self):
"""Test the 'have-{next, previous, track} properties."""
"""Test the 'have-{next, previous, track}' properties."""
for property in ["have-next", "have-previous", "have-track"]:
with self.subTest(property=property):
self.assertFalse(self.card.get_property(property))

View File

@ -163,6 +163,9 @@ class TestEmmental(unittest.TestCase):
self.application.factory.can_go_previous = True
self.assertTrue(win.now_playing.have_previous)
win.user_editing = True
self.assertTrue(win.now_playing.editing)
win.now_playing.emit("play")
play_func.assert_called()
win.now_playing.emit("pause")