Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix seekbar jumping when changing playback speed #847

Merged
merged 9 commits into from
Feb 16, 2024
1 change: 0 additions & 1 deletion cozy/media/player.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@

US_TO_SEC = 10 ** 6
NS_TO_SEC = 10 ** 9
REWIND_SECONDS = 30


class Player(EventSender):
Expand Down
16 changes: 9 additions & 7 deletions cozy/ui/media_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -116,7 +118,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

Expand Down Expand Up @@ -148,4 +150,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
6 changes: 2 additions & 4 deletions cozy/ui/widgets/playback_speed_popover.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
40 changes: 16 additions & 24 deletions cozy/ui/widgets/seek_bar.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +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: float = 0.0
self._progress_scale_pressed = False

self.progress_scale.connect("value-changed", self._on_progress_scale_changed)
Expand Down Expand Up @@ -47,15 +50,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()
Expand All @@ -75,32 +69,30 @@ 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.progress_scale.get_adjustment().get_upper()
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("<span font_features='tnum'>" + current_text + "</span>")
self.remaining_label.set_markup("<span font_features='tnum'>-" + remaining_text + "</span>")
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
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:
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)
def _on_progress_key_pressed(self, _, event, *__):
if event in {Gdk.KEY_Up, Gdk.KEY_Left}:
self.emit("rewind")
elif event in {Gdk.KEY_Down, Gdk.KEY_Right}:
self.emit("forward")

def _on_progress_scale_press(self, *_):
self._progress_scale_pressed = True


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, ())
35 changes: 22 additions & 13 deletions cozy/view_model/playback_control_view_model.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -17,26 +15,20 @@ 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)

if self._player.loaded_book:
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]):
if self._book:
self._book.remove_bind("playback_speed", self._on_playback_speed_changed)

def book(self, value: Book | None):
self._book = value
if value:
self._book.bind_to("playback_speed", self._on_playback_speed_changed)

self._notify("lock_ui")

@property
Expand All @@ -47,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

Expand All @@ -62,12 +54,29 @@ 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

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

@property
def lock_ui(self) -> bool:
return not self._book
Expand Down
12 changes: 8 additions & 4 deletions data/ui/seek_bar.ui
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@
<property name="tooltip-text" translatable="true">Elapsed time</property>
<property name="halign">end</property>
<property name="valign">center</property>
<property name="label">&lt;span font_features=&apos;tnum&apos;&gt;--:--&lt;/span&gt;</property>
<property name="use-markup">true</property>
<property name="label">--:--</property>
<property name="single-line-mode">true</property>
<accessibility>
<property name="label" translatable="true">Elapsed time of current part</property>
</accessibility>
<style>
<class name="numeric"/>
</style>
</object>
</child>
<child>
Expand All @@ -45,12 +47,14 @@
<object class="GtkLabel" id="remaining_label">
<property name="tooltip-text" translatable="true">Remaining time</property>
<property name="halign">start</property>
<property name="label">&lt;span font_features=&apos;tnum&apos;&gt;--:--&lt;/span&gt;</property>
<property name="use-markup">true</property>
<property name="label">--:--</property>
<property name="single-line-mode">true</property>
<accessibility>
<property name="label" translatable="true">Remaining time of current part</property>
</accessibility>
<style>
<class name="numeric"/>
</style>
</object>
</child>
</object>
Expand Down
Loading