diff --git a/resources/playbar.blp b/resources/playbar.blp index c3f2008..323791f 100644 --- a/resources/playbar.blp +++ b/resources/playbar.blp @@ -2,174 +2,186 @@ using Gtk 4.0; using Adw 1; template $AudreyUiPlaybar: Adw.Bin { - child: CenterBox { - hexpand: true; + child: Overlay { + [overlay] + ProgressBar pulse_bar { + styles [ + "osd" + ] + visible: bind template.show-pulse-bar; + valign: start; + } - styles [ - "toolbar", - ] + child: CenterBox { + hexpand: true; - [start] - Box { - AspectFrame { - visible: false; // FIXME annoying annoying annoying annoying - // visible: bind template.show_cover_art; - vexpand: true; - ratio: 1.0; - obey-child: false; + styles [ + "toolbar", + ] - child: Picture { - content-fit: scale_down; - paintable: bind template.playing_cover_art; - }; + [start] + Box { + AspectFrame { + visible: false; // FIXME annoying annoying annoying annoying + // visible: bind template.show_cover_art; + vexpand: true; + ratio: 1.0; + obey-child: false; + + child: Picture { + content-fit: scale_down; + paintable: bind template.playing_cover_art; + }; + } + + Box { + margin-start: 6; + orientation: vertical; + valign: center; + + Label { + styles [ + "heading" + ] + + xalign: 0; + halign: start; + label: bind template.song as <$AudreyPlaybinSong>.title; + ellipsize: end; + } + + Label { + styles [ + "caption" + ] + + xalign: 0; + label: bind template.song as <$AudreyPlaybinSong>.artist; + ellipsize: end; + } + + Label { + styles [ + "caption" + ] + + xalign: 0; + label: bind template.song as <$AudreyPlaybinSong>.album; + ellipsize: end; + } + } } + [center] Box { - margin-start: 6; orientation: vertical; valign: center; - Label { - styles [ - "heading" - ] + CenterBox { + [start] + Label play_position_label { + styles [ + "caption", + "numeric", + ] - xalign: 0; - halign: start; - label: bind template.song as <$AudreyPlaybinSong>.title; - ellipsize: end; + label: bind $format_timestamp(template.position) as ; + } + + [center] + Scale play_position { + name: "seek-scale"; + orientation: horizontal; + width-request: 400; + sensitive: bind template.idle-active inverted; + + adjustment: Adjustment { + lower: 0; + value: bind template.position; + upper: bind template.duration; + }; + + change-value => $on_play_position_seek() swapped; + } + + [end] + Label play_duration { + styles [ + "caption", + "numeric", + ] + + label: bind $format_timestamp(template.duration) as ; + } } - Label { - styles [ - "caption" - ] + Box { + halign: center; + orientation: horizontal; - xalign: 0; - label: bind template.song as <$AudreyPlaybinSong>.artist; - ellipsize: end; - } + Button { + icon-name: "media-skip-backward"; + valign: center; + sensitive: bind template.idle-active inverted; + clicked => $on_skip_backward_clicked() swapped; + } - Label { - styles [ - "caption" - ] + Button { + icon-name: "media-seek-backward"; + valign: center; + sensitive: bind template.idle-active inverted; + clicked => $seek_backward() swapped; + } - xalign: 0; - label: bind template.song as <$AudreyPlaybinSong>.album; - ellipsize: end; + Button { + icon-name: bind $play_pause_icon_name(template.idle-active, template.pause) as ; + valign: center; + sensitive: bind $play_pause_sensitive(template.playlist-count) as ; + clicked => $on_play_pause_clicked() swapped; + } + + Button { + icon-name: "media-seek-forward"; + valign: center; + sensitive: bind template.idle-active inverted; + clicked => $seek_forward() swapped; + } + + Button { + icon-name: "media-skip-forward"; + valign: center; + sensitive: bind template.idle-active inverted; + clicked => $on_skip_forward_clicked() swapped; + } } } - } - [center] - Box { - orientation: vertical; - valign: center; - - CenterBox { - [start] - Label play_position_label { - styles [ - "caption", - "numeric", - ] - - label: bind $format_timestamp(template.position) as ; + [end] + Box { + Button { + icon-name: "non-starred"; + valign: center; + sensitive: bind template.idle-active inverted; } - [center] - Scale play_position { - name: "seek-scale"; + Button { + icon-name: bind $mute_button_icon_name(template.mute) as ; + valign: center; + clicked => $on_mute_toggle() swapped; + } + + Scale { + name: "volume-scale"; orientation: horizontal; - width-request: 400; - sensitive: bind template.idle-active inverted; + width-request: 130; + adjustment: Adjustment { lower: 0; - value: bind template.position; - upper: bind template.duration; + value: bind template.volume bidirectional; + upper: 100; }; - - change-value => $on_play_position_seek() swapped; - } - - [end] - Label play_duration { - styles [ - "caption", - "numeric", - ] - - label: bind $format_timestamp(template.duration) as ; } } - - Box { - halign: center; - orientation: horizontal; - - Button { - icon-name: "media-skip-backward"; - valign: center; - sensitive: bind template.idle-active inverted; - clicked => $on_skip_backward_clicked() swapped; - } - - Button { - icon-name: "media-seek-backward"; - valign: center; - sensitive: bind template.idle-active inverted; - clicked => $seek_backward() swapped; - } - - Button { - icon-name: bind $play_pause_icon_name(template.idle-active, template.pause) as ; - valign: center; - sensitive: bind $play_pause_sensitive(template.playlist-count) as ; - clicked => $on_play_pause_clicked() swapped; - } - - Button { - icon-name: "media-seek-forward"; - valign: center; - sensitive: bind template.idle-active inverted; - clicked => $seek_forward() swapped; - } - - Button { - icon-name: "media-skip-forward"; - valign: center; - sensitive: bind template.idle-active inverted; - clicked => $on_skip_forward_clicked() swapped; - } - } - } - - [end] - Box { - Button { - icon-name: "non-starred"; - valign: center; - sensitive: bind template.idle-active inverted; - } - - Button { - icon-name: bind $mute_button_icon_name(template.mute) as ; - valign: center; - clicked => $on_mute_toggle() swapped; - } - - Scale { - name: "volume-scale"; - orientation: horizontal; - width-request: 130; - - adjustment: Adjustment { - lower: 0; - value: bind template.volume bidirectional; - upper: 100; - }; - } - } + }; }; } diff --git a/src/ui/playbar.rs b/src/ui/playbar.rs index 05f10c7..92ffa81 100644 --- a/src/ui/playbar.rs +++ b/src/ui/playbar.rs @@ -11,6 +11,10 @@ mod imp { #[properties(wrapper_type = super::Playbar)] #[template(resource = "/eu/callcc/audrey/playbar.ui")] pub struct Playbar { + #[template_child] + #[property(get)] + pulse_bar: TemplateChild, + #[property(get, set)] song: RefCell>, #[property(get, set)] @@ -34,6 +38,9 @@ mod imp { position: Cell, #[property(get, set)] duration: Cell, + + #[property(get, set)] + _show_pulse_bar: Cell, } #[glib::object_subclass] diff --git a/src/ui/window.rs b/src/ui/window.rs index 6323456..bb714c4 100644 --- a/src/ui/window.rs +++ b/src/ui/window.rs @@ -54,6 +54,8 @@ mod imp { #[property(type = u32, get = Self::playlist_count)] _playlist_count: (), + tick_callback: Cell>, + pub(super) queued_seek: Cell>, pub(crate) initial_setup_handle: RefCell>>, @@ -103,7 +105,9 @@ mod imp { _idle_active: (), _playlist_count: (), - queued_seek: Cell::new(None), + tick_callback: Default::default(), + + queued_seek: Default::default(), initial_setup_handle: Default::default(), mpv_event_loop_handle: Default::default(), @@ -202,7 +206,9 @@ mod imp { }, Event::StartFile(_) => { + event!(Level::INFO, "start file event"); window.notify("song"); + window.imp().buffering_start(); // TODO: load cover art } @@ -258,12 +264,12 @@ mod imp { Event::Seek => { event!(Level::INFO, "seek event"); - // TODO: if doing "buffering" progress bar, show it after a timeout here + window.imp().buffering_start(); } Event::PlaybackRestart => { event!(Level::INFO, "playback restart event"); - // TODO: if doing "buffering" progress bar, hide it here + window.imp().buffering_end(); if let Some(queued_seek) = window.imp().queued_seek.take() { // a seek was tried before and failed, try again now @@ -478,6 +484,25 @@ mod imp { Some(song) } } + + fn buffering_start(&self) { + let started_buffering = std::time::Instant::now(); + self.tick_callback + .replace(Some(self.obj().add_tick_callback(move |window, _| { + // 3 second period from gnome hig + if started_buffering.elapsed() > std::time::Duration::from_secs(3) { + window.imp().playbar.set_show_pulse_bar(true); + window.imp().playbar.pulse_bar().pulse(); + } + glib::ControlFlow::Continue + }))) + .map(gtk::TickCallbackId::remove); + } + + fn buffering_end(&self) { + self.tick_callback.take().map(gtk::TickCallbackId::remove); + self.playbar.set_show_pulse_bar(false); + } } impl Drop for Window {