diff --git a/src/main.rs b/src/main.rs index c839b84..188624c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,6 +17,8 @@ use gtk::{gio, glib}; pub mod mpv; +pub mod util; + fn init_tracing() { #[cfg(debug_assertions)] { diff --git a/src/ui/window.rs b/src/ui/window.rs index fa9b6f5..8056956 100644 --- a/src/ui/window.rs +++ b/src/ui/window.rs @@ -1,11 +1,12 @@ use crate::model::Song; +use crate::util::buffering::BufferingPulseController; use crate::{mpris, mpv}; use adw::prelude::*; use adw::subclass::prelude::*; use glib::subclass::InitializingObject; use glib::JoinHandle; use gtk::{gdk, gio, glib}; -use std::cell::{Cell, RefCell}; +use std::cell::{Cell, OnceCell, RefCell}; use std::rc::Rc; use tracing::{event, span, Level}; use zbus::object_server::InterfaceRef; @@ -78,7 +79,8 @@ mod imp { loading_cover_handle: RefCell>>, - buffering_timeout: Cell>, + playbar_buffering: OnceCell, + time_pos_notify_timeout: Cell>, mpris_player: RefCell>>, @@ -140,7 +142,8 @@ mod imp { loading_cover_handle: Default::default(), - buffering_timeout: Default::default(), + playbar_buffering: Default::default(), + time_pos_notify_timeout: Default::default(), mpris_player: Default::default(), @@ -539,33 +542,9 @@ mod imp { ) } - fn buffering_start(&self) { - let started_buffering = std::time::Instant::now(); - let window = self.obj().downgrade(); - if let Some(source) = self.buffering_timeout.replace(Some(glib::timeout_add_local( - std::time::Duration::from_millis(100), - move || { - window - .upgrade() - .map_or(glib::ControlFlow::Break, 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 - }) - }, - ))) { - source.remove() - } - } - - fn buffering_end(&self) { - if let Some(source) = self.buffering_timeout.take() { - source.remove(); - } - self.playbar.set_show_pulse_bar(false); + fn playbar_buffering(&self) -> &BufferingPulseController { + self.playbar_buffering + .get_or_init(|| BufferingPulseController::new(&self.playbar.pulse_bar())) } fn on_property_change(&self, event: crate::mpv::event::PropertyEvent) { @@ -734,7 +713,7 @@ mod imp { event!(target: "audrey::playback", Level::DEBUG, "StartFile"); self.obj().notify("song"); - self.buffering_start(); + self.playbar_buffering().start(); let song = self.obj().song().unwrap(); @@ -879,7 +858,7 @@ mod imp { self.state.set(State::Seeking); self.obj().notify("time-pos"); - self.buffering_start(); + self.playbar_buffering().stop(); let time_pos = self.obj().time_pos(); if let Some(iface_ref) = self.mpris_player() { @@ -910,7 +889,7 @@ mod imp { self.state.set(State::Active); event!(target: "audrey::playback", Level::DEBUG, "PlaybackRestart"); - self.buffering_end(); + self.playbar_buffering().stop(); if let Some(queued_seek) = self.queued_seek.take() { // a seek was tried before and failed, try again now @@ -943,7 +922,7 @@ mod imp { self.obj().notify("time-pos"); self.obj().notify("song"); - self.buffering_end(); + self.playbar_buffering().stop(); if let Err(err) = event.reason { event!(Level::ERROR, "end file error: {err}"); @@ -964,9 +943,6 @@ mod imp { if let Some(source) = self.time_pos_notify_timeout.take() { source.remove(); } - if let Some(source) = self.buffering_timeout.take() { - source.remove(); - } } } } diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..597c4f9 --- /dev/null +++ b/src/util.rs @@ -0,0 +1 @@ +pub mod buffering; diff --git a/src/util/buffering.rs b/src/util/buffering.rs new file mode 100644 index 0000000..cbb19f6 --- /dev/null +++ b/src/util/buffering.rs @@ -0,0 +1,56 @@ +use adw::{glib, prelude::*}; +use std::cell::Cell; +use std::time::Duration; + +// gnome HIG: "Progress needs to be indicated whenever an operation takes more than around three +// seconds." +const PULSE_DELAY: Duration = Duration::from_secs(3); +const PULSE_PERIOD: Duration = Duration::from_millis(100); + +pub struct BufferingPulseController { + progress_bar: gtk::ProgressBar, + handle: Cell>>, +} + +impl BufferingPulseController { + pub fn new(progress_bar: >k::ProgressBar) -> Self { + Self { + progress_bar: progress_bar.clone(), + handle: Cell::new(None), + } + } + + pub fn start(&self) { + let progress_bar = self.progress_bar.clone(); + + let prev_handle = self + .handle + .replace(Some(glib::spawn_future_local(async move { + glib::timeout_future(PULSE_DELAY).await; + progress_bar.set_visible(true); + loop { + progress_bar.pulse(); + glib::timeout_future(PULSE_PERIOD).await; + } + }))); + + if let Some(handle) = prev_handle { + handle.abort(); + } + } + + pub fn stop(&self) { + if let Some(handle) = self.handle.take() { + handle.abort(); + } + self.progress_bar.set_visible(false); + } +} + +impl Drop for BufferingPulseController { + fn drop(&mut self) { + if let Some(handle) = self.handle.take() { + handle.abort(); + } + } +}