diff --git a/data/org.gnome.Showtime.gschema.xml.in b/data/org.gnome.Showtime.gschema.xml.in index 4b7bc4ba660091f1189fe2182e771868264b605d..abce18be38c4fddd8b7fe0431bf9b5ace3fb6d2d 100644 --- a/data/org.gnome.Showtime.gschema.xml.in +++ b/data/org.gnome.Showtime.gschema.xml.in @@ -2,9 +2,19 @@ + + + + + + "Duration" + What to show in the end timestamp position + E.g. total duration of the video or the amount of time remaining + false + diff --git a/showtime/gtk/style.css b/showtime/gtk/style.css index 07d7f141e5134857abd8a0eec8d107c907ab233d..0bbd0bf725b3a2fe246a7d1ad2fef5d917d58c1e 100644 --- a/showtime/gtk/style.css +++ b/showtime/gtk/style.css @@ -46,6 +46,10 @@ toast { -gtk-icon-size: 21px; } +#end-timestamp-button { + padding: 0 12px; +} + .no-knob { padding: 6px 9px 6px 15px; } diff --git a/showtime/gtk/window.blp b/showtime/gtk/window.blp index bf4b43c98264d47e300f5dbf72136a6611975a30..cc38e71248d15f5b9577f2204ad2bae113f9ec68 100644 --- a/showtime/gtk/window.blp +++ b/showtime/gtk/window.blp @@ -415,16 +415,19 @@ template $ShowtimeWindow: Adw.ApplicationWindow { } [end] - Label duration_label { + Button end_timestamp_button { + name: "end-timestamp-button"; label: "0∶00"; margin-start: 3; - margin-end: 12; + tooltip-text: _("Toggle Duration/Remaining"); + clicked => $_cycle_end_timestamp_type(); styles [ "caption", "numeric", "timestamp", - "dim-label" + "dim-label", + "flat" ] } } diff --git a/showtime/shared.py.in b/showtime/shared.py.in index 1e7918a94b97a37dc5e0524b29789cdc07547e6b..d057159fdb48a163ee3cd1bbb67311c6c767052e 100644 --- a/showtime/shared.py.in +++ b/showtime/shared.py.in @@ -39,6 +39,8 @@ state_schema = Gio.Settings.new(APP_ID + ".State") cache_path = Path(GLib.get_user_cache_dir()) / "showtime" log_files = [] +end_timestamp_type = state_schema.get_enum("end-timestamp-type") + # For large enough monitors, occupy 40% of the screen area when opening a window with a video DEFAULT_OCCUPY_SCREEN = 0.4 diff --git a/showtime/shared.pyi b/showtime/shared.pyi index 2ce30c97ee23b058cfe3b99216cb8a5096bd2a7e..d4340406b9da6f57027540cfedd7f4bdd9fd4753 100644 --- a/showtime/shared.pyi +++ b/showtime/shared.pyi @@ -39,6 +39,8 @@ state_schema: Gio.Settings cache_path: Path log_files: list[Path] +end_timestamp_type: int + DEFAULT_OCCUPY_SCREEN: float SMALL_SCREEN_AREA: int SMALL_OCCUPY_SCREEN: float diff --git a/showtime/window.py b/showtime/window.py index 7acd3a28f28a40120d93ccacfc55daceb48835d4..ded97f7b5337e77e974c49639936ad7e02e7a039 100644 --- a/showtime/window.py +++ b/showtime/window.py @@ -81,7 +81,7 @@ class ShowtimeWindow(Adw.ApplicationWindow): play_button: Gtk.Button = Gtk.Template.Child() position_label: Gtk.Label = Gtk.Template.Child() seek_scale: Gtk.Scale = Gtk.Template.Child() - duration_label: Gtk.Label = Gtk.Template.Child() + end_timestamp_button: Gtk.Button = Gtk.Template.Child() volume_menu_button: Gtk.MenuButton = Gtk.Template.Child() volume_button: Gtk.Button = Gtk.Template.Child() @@ -293,6 +293,10 @@ class ShowtimeWindow(Adw.ApplicationWindow): ), ) + shared.state_schema.connect( + "changed::end-timestamp-type", self.__on_end_timestamp_type_changed + ) + self.volume_scale.connect( "change-value", lambda _obj, _scroll, val: self.play.set_volume(max(val, 0)), @@ -368,24 +372,20 @@ class ShowtimeWindow(Adw.ApplicationWindow): self.paused = False case GstPlay.PlayMessage.DURATION_CHANGED: - self.duration_label.set_label( - nanoseconds_to_timestamp( - GstPlay.PlayMessage.parse_duration_updated(msg) - ) - ) + pos = self.play.get_position() + dur = GstPlay.PlayMessage.parse_duration_updated(msg) + + self.__set_end_timestamp_label(pos, dur) case GstPlay.PlayMessage.POSITION_UPDATED: - self.seek_scale.set_value( - GstPlay.PlayMessage.parse_position_updated(msg) - / self.play.get_duration() - ) + pos = GstPlay.PlayMessage.parse_position_updated(msg) + dur = self.play.get_duration() + + self.seek_scale.set_value(pos / dur) # TODO: This can probably be done only every second instead - self.position_label.set_label( - nanoseconds_to_timestamp( - GstPlay.PlayMessage.parse_position_updated(msg) - ) - ) + self.position_label.set_label(nanoseconds_to_timestamp(pos)) + self.__set_end_timestamp_label(pos, dur) case GstPlay.PlayMessage.SEEK_DONE: pos = self.play.get_position() @@ -393,6 +393,7 @@ class ShowtimeWindow(Adw.ApplicationWindow): self.seek_scale.set_value(pos / dur) self.position_label.set_label(nanoseconds_to_timestamp(pos)) + self.__set_end_timestamp_label(pos, dur) logging.debug("Seeked to %i.", pos) case GstPlay.PlayMessage.MEDIA_INFO_UPDATED: @@ -911,6 +912,33 @@ class ShowtimeWindow(Adw.ApplicationWindow): (anim.skip if initial else anim.play)() logging.debug("Resized window to %i×%i.", int(nat_width), int(nat_height)) + def __on_end_timestamp_type_changed(self, *_args: Any) -> None: + shared.end_timestamp_type = shared.state_schema.get_enum("end-timestamp-type") + self.__set_end_timestamp_label( + self.play.get_position(), self.play.get_duration() + ) + + def __set_end_timestamp_label(self, pos: int, dur: int) -> None: + match shared.end_timestamp_type: + case 0: # Duration + self.end_timestamp_button.set_label(nanoseconds_to_timestamp(dur)) + case 1: # Remaining + self.end_timestamp_button.set_label( + "-" + nanoseconds_to_timestamp(dur - pos) + ) + + @Gtk.Template.Callback() + def _cycle_end_timestamp_type(self, *_args: Any) -> None: + match shared.end_timestamp_type: + case 0: + shared.state_schema.set_enum("end-timestamp-type", 1) + case 1: + shared.state_schema.set_enum("end-timestamp-type", 0) + + self.__set_end_timestamp_label( + self.play.get_position(), self.play.get_duration() + ) + @Gtk.Template.Callback() def _resume(self, *_args: Any) -> None: self.unpause()