From 9c4788f12d9ece12504fe36e7985fef666e94659 Mon Sep 17 00:00:00 2001 From: rdbende Date: Sun, 14 Jul 2024 17:36:21 +0200 Subject: [PATCH] Add a progress ring instead of the bar This still looks broken, but I'm going to leave it as-is for now, and figure out the layout later --- com.github.geigi.cozy.json | 2 +- cozy/ui/book_detail_view.py | 83 ++++++++--- cozy/view_model/book_detail_view_model.py | 16 +-- data/ui/book_detail.blp | 165 ++++++++++------------ 4 files changed, 138 insertions(+), 128 deletions(-) diff --git a/com.github.geigi.cozy.json b/com.github.geigi.cozy.json index 71689c45..9cd251a5 100644 --- a/com.github.geigi.cozy.json +++ b/com.github.geigi.cozy.json @@ -1,7 +1,7 @@ { "app-id": "com.github.geigi.cozy", "runtime": "org.gnome.Platform", - "runtime-version": "46", + "runtime-version": "master", "sdk": "org.gnome.Sdk", "command": "com.github.geigi.cozy", "finish-args": [ diff --git a/cozy/ui/book_detail_view.py b/cozy/ui/book_detail_view.py index 79dedb07..f59dd71f 100644 --- a/cozy/ui/book_detail_view.py +++ b/cozy/ui/book_detail_view.py @@ -2,9 +2,10 @@ import time from threading import Event, Thread from typing import Callable, Final +from math import pi as PI -from gi.repository import Adw, GLib, Gtk, GObject - +from gi.repository import Adw, GLib, Gtk, GObject, Gdk, Graphene, Gsk +import cairo from cozy.control.artwork_cache import ArtworkCache from cozy.ext import inject from cozy.model.book import Book @@ -13,9 +14,10 @@ from cozy.ui.chapter_element import ChapterElement from cozy.view_model.book_detail_view_model import BookDetailViewModel -log = logging.getLogger("BookDetailView") +log = logging.getLogger(__name__) ALBUM_ART_SIZE: Final[int] = 256 +PROGRESS_RING_LINE_WIDTH: Final[int] = 5 def call_in_main_thread(*args) -> None: @@ -23,6 +25,50 @@ def call_in_main_thread(*args) -> None: GLib.MainContext.default().invoke_full(GLib.PRIORITY_DEFAULT_IDLE, *args) +class ProgressRing(Gtk.Widget): + __gtype_name__ = "ProgressRing" + + progress = GObject.Property(type=float, default=0.0) + + def __init__(self) -> None: + super().__init__() + + self._style_manager = Adw.StyleManager() + self._style_manager.connect("notify::accent-color", self.redraw) + self.connect("notify::progress", self.redraw) + + def redraw(self, *_) -> None: + self.queue_draw() + + def do_measure(self, *_) -> tuple[int, int, int, int]: + return (40, 40, -1, -1) + + def do_snapshot(self, snapshot: Gtk.Snapshot) -> None: + size = self.get_allocated_height() + radius = (size - 8) / 2.0 + + context = snapshot.append_cairo(Graphene.Rect().init(0, 0, size, size)) + + context.arc(size / 2, size / 2, radius, 0, 2 * PI) + context.set_source_rgba(*self.get_dim_color()) + context.set_line_width(PROGRESS_RING_LINE_WIDTH) + context.stroke() + + context.arc(size / 2, size / 2, radius, -0.5 * PI, self.progress * 2 * PI - (0.5 * PI)) + context.set_source_rgb(*self.get_accent_color()) + context.set_line_width(PROGRESS_RING_LINE_WIDTH) + context.set_line_cap(cairo.LineCap.ROUND) + context.stroke() + + def get_dim_color(self) -> tuple[int, int, int, int]: + color = self.get_color() + return color.red, color.green, color.blue, 0.15 + + def get_accent_color(self) -> tuple[int, int, int]: + color = self._style_manager.get_accent_color_rgba() + return color.red, color.green, color.blue + + class ChaptersListBox(Adw.PreferencesGroup): def __init__(self, title: str): super().__init__() @@ -44,11 +90,10 @@ class BookDetailView(Adw.NavigationPage): book_label: Gtk.Label = Gtk.Template.Child() author_label: Gtk.Label = Gtk.Template.Child() - last_played_label: Gtk.Label = Gtk.Template.Child() total_label: Gtk.Label = Gtk.Template.Child() - remaining_label: Gtk.Label = Gtk.Template.Child() - book_progress_bar: Gtk.ProgressBar = Gtk.Template.Child() + + book_progress_ring: ProgressRing = Gtk.Template.Child() download_box: Gtk.Box = Gtk.Template.Child() download_label: Gtk.Label = Gtk.Template.Child() @@ -89,11 +134,9 @@ def _connect_view_model(self): self._view_model.bind_to("is_book_available", self._on_book_available_changed) self._view_model.bind_to("downloaded", self._set_book_download_status) self._view_model.bind_to("current_chapter", self._on_current_chapter_changed) - self._view_model.bind_to("last_played_text", self._on_last_played_text_changed) - self._view_model.bind_to("remaining_text", self._on_times_changed) - self._view_model.bind_to("progress_percent", self._on_times_changed) - self._view_model.bind_to("total_text", self._on_times_changed) - self._view_model.bind_to("playback_speed", self._on_times_changed) + self._view_model.bind_to("length", self._on_length_changed) + self._view_model.bind_to("progress", self._on_progress_changed) + self._view_model.bind_to("playback_speed", self._on_progress_changed) self._view_model.bind_to("lock_ui", self._on_lock_ui_changed) def _connect_widgets(self): @@ -122,10 +165,8 @@ def _on_book_changed(self): self.book_title = book.name self.author_label.set_text(book.author) - self.last_played_label.set_text(self._view_model.last_played_text) - self._set_cover_image(book) - self._set_progress() + self._on_progress_changed() self._display_external_section() def _on_play_changed(self): @@ -167,12 +208,12 @@ def _on_current_chapter_changed(self): child.set_playing(self._view_model.playing) break - def _on_last_played_text_changed(self): - self.last_played_label.set_text(self._view_model.last_played_text) - - def _on_times_changed(self): + def _on_length_changed(self): self.total_label.set_text(self._view_model.total_text) - self._set_progress() + + def _on_progress_changed(self): + self.remaining_label.set_text(self._view_model.remaining_text) + self.book_progress_ring.progress = self._view_model.progress_percent def _on_lock_ui_changed(self): self.download_switch.set_sensitive(not self._view_model.lock_ui) @@ -261,10 +302,6 @@ def _display_external_section(self): self.download_switch.set_active(self._view_model.book.offline) self.download_switch.handler_unblock_by_func(self._download_switch_changed) - def _set_progress(self): - self.remaining_label.set_text(self._view_model.remaining_text) - self.book_progress_bar.set_fraction(self._view_model.progress_percent) - def _set_cover_image(self, book: Book): self.album_art_container.set_visible(False) diff --git a/cozy/view_model/book_detail_view_model.py b/cozy/view_model/book_detail_view_model.py index f2effe00..35b846eb 100644 --- a/cozy/view_model/book_detail_view_model.py +++ b/cozy/view_model/book_detail_view_model.py @@ -162,8 +162,7 @@ def _on_player_event(self, event, message): if event in {"play", "pause"}: self._notify("playing") elif event in {"position", "book-finished"}: - self._notify("progress_percent") - self._notify("remaining_text") + self._notify("progress") def _on_fs_monitor_event(self, event, _): if not self._book: @@ -179,18 +178,15 @@ def _on_book_last_played_changed(self): self._notify("last_played_text") def _on_book_progress_changed(self): - self._notify("remaining_text") - self._notify("progress_percent") + self._notify("progress") def _on_book_duration_changed(self): - self._notify("progress_percent") - self._notify("remaining_text") - self._notify("total_text") + self._notify("progress") + self._notify("length") def _on_playback_speed_changed(self): - self._notify("progress_percent") - self._notify("remaining_text") - self._notify("total_text") + self._notify("progress") + self._notify("length") def _on_offline_cache_event(self, event, message) -> None: if self._book and isinstance(message, Book) and self._book.id == message.id and event in {"book-offline-removed", "book-offline"}: diff --git a/data/ui/book_detail.blp b/data/ui/book_detail.blp index 6c4d5b21..661bcacd 100644 --- a/data/ui/book_detail.blp +++ b/data/ui/book_detail.blp @@ -104,14 +104,81 @@ template $BookDetail: Adw.NavigationPage { ] } - Button play_button { - focusable: true; - receives-default: true; + Box { + valign: end; margin-top: 18; + spacing: 6; + + $ProgressRing book_progress_ring { + valign: center; + } + + Grid { + hexpand: true; + row-spacing: 3; + column-spacing: 20; + + Label { + halign: start; + hexpand: true; + label: _("Remaining"); + + styles [ + "dim-label", + ] + + layout { + column: 0; + row: 0; + } + } + + Label remaining_label { + hexpand: true; + xalign: 0; + + layout { + column: 1; + row: 0; + } + } + + Label { + halign: start; + hexpand: true; + label: _("Total"); + + styles [ + "dim-label", + ] + + layout { + column: 0; + row: 1; + } + } + + Label total_label{ + hexpand: true; + xalign: 0; + + layout { + column: 1; + row: 1; + } + } + } + } + + Button play_button { halign: start; valign: end; vexpand: true; + margin-top: 12; + focusable: true; + receives-default: true; tooltip-text: _("Start/Stop playback"); + accessibility { label: _("Start or pause the playback"); } @@ -133,6 +200,7 @@ template $BookDetail: Adw.NavigationPage { valign: center; margin-bottom: 12; spacing: 5; + visible: false; Box download_box { halign: end; @@ -155,97 +223,6 @@ template $BookDetail: Adw.NavigationPage { valign: center; } } - - ProgressBar book_progress_bar { - width-request: 250; - halign: center; - valign: start; - show-text: true; - margin-bottom: 18; - } - - Grid { - margin-bottom: 18; - hexpand: true; - row-spacing: 4; - column-spacing: 20; - - Label remaining_text { - halign: start; - hexpand: true; - label: _("Remaining"); - - styles [ - "dim-label", - ] - - layout { - column: '0'; - row: '3'; - } - } - - Label remaining_label { - hexpand: true; - label: 'label'; - xalign: 0; - - layout { - column: '1'; - row: '3'; - } - } - - Label total_label { - hexpand: true; - label: 'label'; - xalign: 0; - - layout { - column: '1'; - row: '2'; - } - } - - Label last_played_label { - hexpand: true; - label: 'label'; - xalign: 0; - - layout { - column: '1'; - row: '1'; - } - } - - Label { - halign: start; - label: _("Total"); - - styles [ - "dim-label", - ] - - layout { - column: '0'; - row: '2'; - } - } - - Label { - halign: start; - label: _("Last played"); - - styles [ - "dim-label", - ] - - layout { - column: '0'; - row: '1'; - } - } - } } }