From 8d72e1375f1d9c1b3b736cce0c419ad8f16e6eeb Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Thu, 8 Jun 2023 12:10:55 -0400 Subject: [PATCH] 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 --- emmental/__init__.py | 1 + emmental/nowplaying/__init__.py | 4 +- emmental/nowplaying/controls.py | 49 ++++++++++++++++++++++- tests/nowplaying/test_controls.py | 61 +++++++++++++++++++++++++++++ tests/nowplaying/test_nowplaying.py | 10 ++++- tests/test_emmental.py | 3 ++ 6 files changed, 124 insertions(+), 4 deletions(-) diff --git a/emmental/__init__.py b/emmental/__init__.py index 6bc3b10..72ce0f9 100644 --- a/emmental/__init__.py +++ b/emmental/__init__.py @@ -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"), diff --git a/emmental/nowplaying/__init__.py b/emmental/nowplaying/__init__.py index 02722c2..38fa906 100644 --- a/emmental/nowplaying/__init__.py +++ b/emmental/nowplaying/__init__.py @@ -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") diff --git a/emmental/nowplaying/controls.py b/emmental/nowplaying/controls.py index 65bf3a6..e1dbdf1 100644 --- a/emmental/nowplaying/controls.py +++ b/emmental/nowplaying/controls.py @@ -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.""" diff --git a/tests/nowplaying/test_controls.py b/tests/nowplaying/test_controls.py index 4715347..53546c4 100644 --- a/tests/nowplaying/test_controls.py +++ b/tests/nowplaying/test_controls.py @@ -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) diff --git a/tests/nowplaying/test_nowplaying.py b/tests/nowplaying/test_nowplaying.py index 8230ebe..ef00c0e 100644 --- a/tests/nowplaying/test_nowplaying.py +++ b/tests/nowplaying/test_nowplaying.py @@ -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)) diff --git a/tests/test_emmental.py b/tests/test_emmental.py index a856c1a..7627cec 100644 --- a/tests/test_emmental.py +++ b/tests/test_emmental.py @@ -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")