From 5f8cee471c8d9fb659674d0f1607c1d835e603db Mon Sep 17 00:00:00 2001 From: rdbende Date: Sun, 7 Jan 2024 16:51:02 +0100 Subject: [PATCH 1/8] Fix seek bar jumping when changing playback speed rapidly To solve this, we no longer set the upper limit of the scale to the length of the song, but leave it at 100 and compute the relative position of the playback to set the scale's position. --- cozy/ui/media_controller.py | 4 +-- cozy/ui/widgets/seek_bar.py | 29 +++++++------------ .../view_model/playback_control_view_model.py | 17 +++++++++++ 3 files changed, 29 insertions(+), 21 deletions(-) diff --git a/cozy/ui/media_controller.py b/cozy/ui/media_controller.py index b5c9a506..265924f7 100644 --- a/cozy/ui/media_controller.py +++ b/cozy/ui/media_controller.py @@ -116,7 +116,7 @@ def _on_play_changed(self): self.play_button.set_icon_name("media-playback-start-symbolic") def _on_position_changed(self): - position = self._playback_control_view_model.position + position = self._playback_control_view_model.relative_position if position is not None: self.seek_bar.position = position @@ -154,4 +154,4 @@ def _on_volume_button_changed(self, _, volume): self._playback_control_view_model.volume = volume def _on_seek_bar_position_changed(self, _, position): - self._playback_control_view_model.position = position + self._playback_control_view_model.relative_position = position diff --git a/cozy/ui/widgets/seek_bar.py b/cozy/ui/widgets/seek_bar.py index 31c9e5c6..58c66207 100644 --- a/cozy/ui/widgets/seek_bar.py +++ b/cozy/ui/widgets/seek_bar.py @@ -15,6 +15,8 @@ class SeekBar(Gtk.Box): def __init__(self, **kwargs): super().__init__(**kwargs) + self.length = 0 + self._progress_scale_pressed = False self.progress_scale.connect("value-changed", self._on_progress_scale_changed) @@ -25,8 +27,7 @@ def __init__(self, **kwargs): self.progress_scale.add_controller(self._progress_scale_gesture) self._progress_scale_key = Gtk.EventControllerKey() - self._progress_scale_key.connect("key-pressed", self._on_progress_scale_press) - self._progress_scale_key.connect("key-released", self._on_progress_scale_release) + self._progress_scale_key.connect("key-pressed", self._on_progress_key_pressed) self.progress_scale.add_controller(self._progress_scale_key) @property @@ -38,15 +39,6 @@ def position(self, new_value: float): if not self._progress_scale_pressed: self.progress_scale.set_value(new_value) - @property - def length(self) -> float: - return self.progress_scale.get_adjustment().get_upper() - - @length.setter - def length(self, new_value: float): - self.progress_scale.set_range(0, new_value) - self._on_progress_scale_changed(None) - @property def sensitive(self) -> bool: return self.progress_scale.get_sensitive() @@ -67,7 +59,7 @@ def visible(self, value: bool): def _on_progress_scale_changed(self, _): position = int(self.progress_scale.get_value()) - total = self.progress_scale.get_adjustment().get_upper() + total = self.length remaining_secs: int = int(total - position) current_text = seconds_to_str(position, total) @@ -80,14 +72,13 @@ def _on_progress_scale_release(self, *_): value = self.progress_scale.get_value() self.emit("position-changed", value) - def _on_progress_key_pressed(self, _, event): - if event.keyval == Gdk.KEY_Up or event.keyval == Gdk.KEY_Left: + def _on_progress_key_pressed(self, _, event, *__): + if event in {Gdk.KEY_Up, Gdk.KEY_Left}: self.position = max(self.position - 30, 0) - self.emit("position-changed", self.position) - elif event.keyval == Gdk.KEY_Down or event.keyval == Gdk.KEY_Right: - max_value = self.progress_scale.get_adjustment().get_upper() - self.position = min(self.position + 30, max_value) - self.emit("position-changed", self.position) + elif event in {Gdk.KEY_Down, Gdk.KEY_Right}: + self.position = min(self.position + 30, 100) + + self.emit("position-changed", self.position) def _on_progress_scale_press(self, *_): self._progress_scale_pressed = True diff --git a/cozy/view_model/playback_control_view_model.py b/cozy/view_model/playback_control_view_model.py index 3214b9bf..56713d64 100644 --- a/cozy/view_model/playback_control_view_model.py +++ b/cozy/view_model/playback_control_view_model.py @@ -68,6 +68,23 @@ def length(self) -> Optional[float]: return self._player.loaded_book.current_chapter.length / self._book.playback_speed + @property + def relative_position(self) -> float | None: + if not self._player.loaded_book or not self._book: + return None + + position = self._book.current_chapter.position - self._book.current_chapter.start_position + length = self._player.loaded_book.current_chapter.length + return position / NS_TO_SEC / length * 100 + + @relative_position.setter + def relative_position(self, new_value: float) -> None: + if not self._book: + return + + length = self._player.loaded_book.current_chapter.length + self._player.position = new_value / 100 * length * self._book.playback_speed + @property def lock_ui(self) -> bool: return not self._book From 2b316fea7c3187e69dd9fc04adbdc21cf8ba1b97 Mon Sep 17 00:00:00 2001 From: rdbende Date: Sun, 7 Jan 2024 17:16:29 +0100 Subject: [PATCH 2/8] Connect the view model elsewhere --- cozy/ui/widgets/playback_speed_popover.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/cozy/ui/widgets/playback_speed_popover.py b/cozy/ui/widgets/playback_speed_popover.py index 2dd59847..18c9266b 100644 --- a/cozy/ui/widgets/playback_speed_popover.py +++ b/cozy/ui/widgets/playback_speed_popover.py @@ -20,12 +20,10 @@ def __init__(self, **kwargs): self.playback_speed_scale.set_increments(0.02, 0.05) self.playback_speed_scale.connect("value-changed", self._on_playback_speed_scale_changed) - self._connect_view_model() - self._on_playback_speed_changed() - - def _connect_view_model(self): self._view_model.bind_to("playback_speed", self._on_playback_speed_changed) + self._on_playback_speed_changed() + def _on_playback_speed_scale_changed(self, _): speed = round(self.playback_speed_scale.get_value(), 2) self._view_model.playback_speed = speed From 6757fae5dc6b3e66ccccfe45eca93a289fc090e6 Mon Sep 17 00:00:00 2001 From: rdbende Date: Sun, 7 Jan 2024 17:16:56 +0100 Subject: [PATCH 3/8] Looks like this shouldn't be bound here --- cozy/view_model/playback_control_view_model.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/cozy/view_model/playback_control_view_model.py b/cozy/view_model/playback_control_view_model.py index 56713d64..f2e916b8 100644 --- a/cozy/view_model/playback_control_view_model.py +++ b/cozy/view_model/playback_control_view_model.py @@ -30,13 +30,7 @@ def book(self) -> Optional[Book]: @book.setter def book(self, value: Optional[Book]): - if self._book: - self._book.remove_bind("playback_speed", self._on_playback_speed_changed) - self._book = value - if value: - self._book.bind_to("playback_speed", self._on_playback_speed_changed) - self._notify("lock_ui") @property From 96228b5a25352b3357e4224df8333d9cf5b8fe93 Mon Sep 17 00:00:00 2001 From: rdbende Date: Sun, 7 Jan 2024 17:18:02 +0100 Subject: [PATCH 4/8] Let's fix the type annotations while we're here --- cozy/ui/widgets/seek_bar.py | 5 +++-- cozy/view_model/playback_control_view_model.py | 12 +++++------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/cozy/ui/widgets/seek_bar.py b/cozy/ui/widgets/seek_bar.py index 58c66207..ea060bc3 100644 --- a/cozy/ui/widgets/seek_bar.py +++ b/cozy/ui/widgets/seek_bar.py @@ -12,11 +12,12 @@ class SeekBar(Gtk.Box): remaining_label: Gtk.Label = Gtk.Template.Child() remaining_event_box: Gtk.Box = Gtk.Template.Child() + length: float + def __init__(self, **kwargs): super().__init__(**kwargs) - self.length = 0 - + self.length: float = 0.0 self._progress_scale_pressed = False self.progress_scale.connect("value-changed", self._on_progress_scale_changed) diff --git a/cozy/view_model/playback_control_view_model.py b/cozy/view_model/playback_control_view_model.py index f2e916b8..51221f6a 100644 --- a/cozy/view_model/playback_control_view_model.py +++ b/cozy/view_model/playback_control_view_model.py @@ -1,5 +1,3 @@ -from typing import Optional - from cozy.architecture.event_sender import EventSender from cozy.architecture.observable import Observable from cozy.ext import inject @@ -17,7 +15,7 @@ def __init__(self): super().__init__() super(Observable, self).__init__() - self._book: Optional[Book] = None + self._book: Book | None = None self._player.add_listener(self._on_player_event) @@ -25,11 +23,11 @@ def __init__(self): self.book = self._player.loaded_book @property - def book(self) -> Optional[Book]: + def book(self) -> Book | None: return self._book @book.setter - def book(self, value: Optional[Book]): + def book(self, value: Book | None): self._book = value self._notify("lock_ui") @@ -41,7 +39,7 @@ def playing(self) -> bool: return self._player.playing @property - def position(self) -> Optional[float]: + def position(self) -> float | None: if not self._book: return None @@ -56,7 +54,7 @@ def position(self, new_value: int): self._player.position = new_value * self._book.playback_speed @property - def length(self) -> Optional[float]: + def length(self) -> float | None: if not self._player.loaded_book or not self._book: return None From b168a02d563e19df5c354e8a6281e46559db638e Mon Sep 17 00:00:00 2001 From: rdbende Date: Sun, 7 Jan 2024 17:29:41 +0100 Subject: [PATCH 5/8] Fix keyboard navigation to move the amount of time set in the settings --- cozy/media/player.py | 1 - cozy/ui/media_controller.py | 12 +++++++----- cozy/ui/widgets/seek_bar.py | 9 +++++---- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/cozy/media/player.py b/cozy/media/player.py index e018a4b0..1c2742ef 100644 --- a/cozy/media/player.py +++ b/cozy/media/player.py @@ -24,7 +24,6 @@ US_TO_SEC = 10 ** 6 NS_TO_SEC = 10 ** 9 -REWIND_SECONDS = 30 class Player(EventSender): diff --git a/cozy/ui/media_controller.py b/cozy/ui/media_controller.py index 265924f7..9cd319d7 100644 --- a/cozy/ui/media_controller.py +++ b/cozy/ui/media_controller.py @@ -71,15 +71,17 @@ def _connect_view_model(self): def _connect_widgets(self): self.play_button.connect("clicked", self._play_clicked) - self.prev_button.connect("clicked", self._rewind_clicked) - self.next_button.connect("clicked", self._forward_clicked) self.volume_button.connect("value-changed", self._on_volume_button_changed) self.seek_bar.connect("position-changed", self._on_seek_bar_position_changed) - self._cover_img_gesture = Gtk.GestureClick() - self._cover_img_gesture.connect("pressed", self._cover_clicked) - self.cover_img.add_controller(self._cover_img_gesture) + self.prev_button.connect("clicked", self._rewind_clicked) + self.next_button.connect("clicked", self._forward_clicked) + self.seek_bar.connect("rewind", self._rewind_clicked) + self.seek_bar.connect("forward", self._forward_clicked) + cover_click_gesture = Gtk.GestureClick() + cover_click_gesture.connect("pressed", self._cover_clicked) + self.cover_img.add_controller(cover_click_gesture) self.cover_img.set_cursor(Gdk.Cursor.new_from_name("pointer")) def _set_cover_image(self, book: Book): diff --git a/cozy/ui/widgets/seek_bar.py b/cozy/ui/widgets/seek_bar.py index ea060bc3..c723ab6c 100644 --- a/cozy/ui/widgets/seek_bar.py +++ b/cozy/ui/widgets/seek_bar.py @@ -75,11 +75,9 @@ def _on_progress_scale_release(self, *_): def _on_progress_key_pressed(self, _, event, *__): if event in {Gdk.KEY_Up, Gdk.KEY_Left}: - self.position = max(self.position - 30, 0) + self.emit("rewind") elif event in {Gdk.KEY_Down, Gdk.KEY_Right}: - self.position = min(self.position + 30, 100) - - self.emit("position-changed", self.position) + self.emit("forward") def _on_progress_scale_press(self, *_): self._progress_scale_pressed = True @@ -88,3 +86,6 @@ def _on_progress_scale_press(self, *_): GObject.signal_new('position-changed', SeekBar, GObject.SIGNAL_RUN_LAST, GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,)) + +GObject.signal_new('rewind', SeekBar, GObject.SIGNAL_RUN_LAST, GObject.TYPE_PYOBJECT, ()) +GObject.signal_new('forward', SeekBar, GObject.SIGNAL_RUN_LAST, GObject.TYPE_PYOBJECT, ()) From bf83e148a0992a22c42d86e3ec24848dc9ff4d2b Mon Sep 17 00:00:00 2001 From: rdbende Date: Sun, 7 Jan 2024 17:39:48 +0100 Subject: [PATCH 6/8] Fix computation of playback position labels --- cozy/ui/widgets/seek_bar.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cozy/ui/widgets/seek_bar.py b/cozy/ui/widgets/seek_bar.py index c723ab6c..a658925b 100644 --- a/cozy/ui/widgets/seek_bar.py +++ b/cozy/ui/widgets/seek_bar.py @@ -59,10 +59,10 @@ def visible(self, value: bool): self.remaining_event_box.set_visible(value) def _on_progress_scale_changed(self, _): - position = int(self.progress_scale.get_value()) total = self.length + position = int(total * self.progress_scale.get_value() / 100) + remaining_secs = int(total - position) - remaining_secs: int = int(total - position) current_text = seconds_to_str(position, total) remaining_text = seconds_to_str(remaining_secs, total) self.current_label.set_markup("" + current_text + "") From 84dee61d4c604a69e1adca01e9d27829c8fc54c2 Mon Sep 17 00:00:00 2001 From: rdbende Date: Sun, 7 Jan 2024 17:43:42 +0100 Subject: [PATCH 7/8] Use .numeric style class instead of Pango markup --- cozy/ui/widgets/seek_bar.py | 6 ++---- data/ui/seek_bar.ui | 12 ++++++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/cozy/ui/widgets/seek_bar.py b/cozy/ui/widgets/seek_bar.py index a658925b..1c210b4f 100644 --- a/cozy/ui/widgets/seek_bar.py +++ b/cozy/ui/widgets/seek_bar.py @@ -63,10 +63,8 @@ def _on_progress_scale_changed(self, _): position = int(total * self.progress_scale.get_value() / 100) remaining_secs = int(total - position) - current_text = seconds_to_str(position, total) - remaining_text = seconds_to_str(remaining_secs, total) - self.current_label.set_markup("" + current_text + "") - self.remaining_label.set_markup("-" + remaining_text + "") + self.current_label.set_text(seconds_to_str(position, total)) + self.remaining_label.set_text(seconds_to_str(remaining_secs, total)) def _on_progress_scale_release(self, *_): self._progress_scale_pressed = False diff --git a/data/ui/seek_bar.ui b/data/ui/seek_bar.ui index 6873bf27..e05a66dd 100644 --- a/data/ui/seek_bar.ui +++ b/data/ui/seek_bar.ui @@ -15,12 +15,14 @@ Elapsed time end center - <span font_features='tnum'>--:--</span> - true + --:-- true Elapsed time of current part + @@ -46,12 +48,14 @@ Remaining time start - <span font_features='tnum'>--:--</span> - true + --:-- true Remaining time of current part + From d931834dc18f2782f88d051b611a9620276dc89e Mon Sep 17 00:00:00 2001 From: rdbende Date: Sun, 7 Jan 2024 18:49:22 +0100 Subject: [PATCH 8/8] Whoops, we don't need to multiply --- cozy/view_model/playback_control_view_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cozy/view_model/playback_control_view_model.py b/cozy/view_model/playback_control_view_model.py index 51221f6a..b02a95e5 100644 --- a/cozy/view_model/playback_control_view_model.py +++ b/cozy/view_model/playback_control_view_model.py @@ -75,7 +75,7 @@ def relative_position(self, new_value: float) -> None: return length = self._player.loaded_book.current_chapter.length - self._player.position = new_value / 100 * length * self._book.playback_speed + self._player.position = new_value / 100 * length @property def lock_ui(self) -> bool: