From 04e7f4f8a7206b5b1049463c1e9d1a19d531ec64 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Sat, 2 Jun 2018 20:41:07 +0300 Subject: [PATCH] EpisodeWidget: Wire the download_checker callback again. If an episode is being downloaded we setup a callback that will supervise the widget and update it's state once the download action is completed and the episode rowid is removed from `manager::ACTIVEDOWNLOADS`. --- hammond-gtk/src/main.rs | 1 - hammond-gtk/src/widgets/episode.rs | 118 +++----- hammond-gtk/src/widgets/episode_states.rs | 353 ---------------------- hammond-gtk/src/widgets/mod.rs | 1 - 4 files changed, 41 insertions(+), 432 deletions(-) delete mode 100644 hammond-gtk/src/widgets/episode_states.rs diff --git a/hammond-gtk/src/main.rs b/hammond-gtk/src/main.rs index 5e11002..083f19c 100644 --- a/hammond-gtk/src/main.rs +++ b/hammond-gtk/src/main.rs @@ -38,7 +38,6 @@ extern crate regex; extern crate reqwest; extern crate send_cell; extern crate serde_json; -extern crate take_mut; extern crate url; use log::Level; diff --git a/hammond-gtk/src/widgets/episode.rs b/hammond-gtk/src/widgets/episode.rs index 5188bc8..b13cc1e 100644 --- a/hammond-gtk/src/widgets/episode.rs +++ b/hammond-gtk/src/widgets/episode.rs @@ -1,5 +1,3 @@ -#![allow(warnings)] - use glib; use gtk; use gtk::prelude::*; @@ -10,7 +8,6 @@ use crossbeam_channel::Sender; use failure::Error; use humansize::{file_size_opts as size_opts, FileSize}; use open; -use take_mut; use hammond_data::dbqueries; use hammond_data::utils::get_download_folder; @@ -18,14 +15,29 @@ use hammond_data::EpisodeWidgetQuery; use app::Action; use manager; -use widgets::episode_states::*; -use std::cell::RefCell; -use std::ops::DerefMut; use std::path::Path; use std::rc::Rc; use std::sync::{Arc, Mutex}; +lazy_static! { + static ref SIZE_OPTS: Arc = { + // Declare a custom humansize option struct + // See: https://docs.rs/humansize/1.0.2/humansize/file_size_opts/struct.FileSizeOpts.html + Arc::new(size_opts::FileSizeOpts { + divider: size_opts::Kilo::Binary, + units: size_opts::Kilo::Decimal, + decimal_places: 0, + decimal_zeroes: 0, + fixed_at: size_opts::FixedAt::No, + long_units: false, + space: true, + suffix: "", + allow_negative: false, + }) + }; +} + #[derive(Clone, Debug)] pub struct EpisodeWidget { pub container: gtk::Box, @@ -35,7 +47,7 @@ pub struct EpisodeWidget { } #[derive(Clone, Debug)] -pub struct InfoLabels { +struct InfoLabels { container: gtk::Box, title: gtk::Label, date: gtk::Label, @@ -48,7 +60,7 @@ pub struct InfoLabels { } #[derive(Clone, Debug)] -pub struct Buttons { +struct Buttons { container: gtk::ButtonBox, play: gtk::Button, download: gtk::Button, @@ -124,24 +136,6 @@ impl InfoLabels { // Set the size label of the episode widget. fn set_size(&self, bytes: Option) { - lazy_static! { - static ref SIZE_OPTS: Arc = { - // Declare a custom humansize option struct - // See: https://docs.rs/humansize/1.0.2/humansize/file_size_opts/struct.FileSizeOpts.html - Arc::new(size_opts::FileSizeOpts { - divider: size_opts::Kilo::Binary, - units: size_opts::Kilo::Decimal, - decimal_places: 0, - decimal_zeroes: 0, - fixed_at: size_opts::FixedAt::No, - long_units: false, - space: true, - suffix: "", - allow_negative: false, - }) - }; - } - // Convert the bytes to a String label let size = || -> Option { let s = bytes?; @@ -215,7 +209,9 @@ impl EpisodeWidget { pub fn new(episode: EpisodeWidgetQuery, sender: &Sender) -> Rc { let widget = Rc::new(Self::default()); widget.info.init(&episode); - Self::determine_buttons_state(&widget, &episode, sender); + Self::determine_buttons_state(&widget, &episode, sender) + .map_err(|err| error!("Error: {}", err)) + .ok(); widget } @@ -298,7 +294,23 @@ impl EpisodeWidget { }; if let Some(prog) = active_dl()? { - // FIXME: Add again the callback ugly hack that makes things work somehow + // set a callback that will update the state when the download finishes + let callback = clone!(widget, sender => move || { + if let Ok(guard) = manager::ACTIVE_DOWNLOADS.read() { + if !guard.contains_key(&id) { + if let Ok(ep) = dbqueries::get_episode_widget_from_rowid(id) { + Self::determine_buttons_state(&widget, &ep, &sender) + .map_err(|err| error!("Error: {}", err)) + .ok(); + + return glib::Continue(false) + } + } + } + + glib::Continue(true) + }); + gtk::timeout_add(250, callback); // Wire the cancel button widget @@ -343,7 +355,7 @@ impl EpisodeWidget { return Ok(()); } - if let Some(path) = episode.local_uri() { + if episode.local_uri().is_some() { // Change the widget layout/state widget.state_playable(); @@ -390,54 +402,6 @@ impl EpisodeWidget { } } -fn determine_media_state( - media_machine: &Rc>, - episode: &EpisodeWidgetQuery, -) -> Result<(), Error> { - let id = episode.rowid(); - let active_dl = || -> Result, Error> { - let m = manager::ACTIVE_DOWNLOADS - .read() - .map_err(|_| format_err!("Failed to get a lock on the mutex."))?; - - Ok(m.get(&id).cloned()) - }()?; - - let mut lock = media_machine.try_borrow_mut()?; - take_mut::take(lock.deref_mut(), |media| { - media.determine_state( - episode.length(), - active_dl.is_some(), - episode.local_uri().is_some(), - ) - }); - - // Show or hide the play/delete/download buttons upon widget initialization. - if let Some(prog) = active_dl { - // set a callback that will update the state when the download finishes - let id = episode.rowid(); - let callback = clone!(media_machine => move || { - if let Ok(guard) = manager::ACTIVE_DOWNLOADS.read() { - if !guard.contains_key(&id) { - if let Ok(ep) = dbqueries::get_episode_widget_from_rowid(id) { - determine_media_state(&media_machine, &ep) - .map_err(|err| error!("Error: {}", err)) - .map_err(|_| error!("Could not determine Media State")) - .ok(); - - return glib::Continue(false) - } - } - } - - glib::Continue(true) - }); - gtk::timeout_add(250, callback); - } - - Ok(()) -} - fn on_download_clicked(ep: &EpisodeWidgetQuery, sender: &Sender) -> Result<(), Error> { let pd = dbqueries::get_podcast_from_id(ep.podcast_id())?; let download_fold = get_download_folder(&pd.title())?; diff --git a/hammond-gtk/src/widgets/episode_states.rs b/hammond-gtk/src/widgets/episode_states.rs deleted file mode 100644 index 7089689..0000000 --- a/hammond-gtk/src/widgets/episode_states.rs +++ /dev/null @@ -1,353 +0,0 @@ -// TODO: Things that should be done. -// -// * Wherever there's a function that take 2 or more arguments of the same type, -// eg: fn new(total_size: gtk::Label, local_size: gtk::Label ..) -// Wrap the types into Struct-tuples and imple deref so it won't be possible to pass -// the wrong argument to the wrong position. - -use gtk; - -use gtk::prelude::*; -use humansize::{file_size_opts as size_opts, FileSize}; - -use std::sync::Arc; - -lazy_static! { - pub static ref SIZE_OPTS: Arc = { - // Declare a custom humansize option struct - // See: https://docs.rs/humansize/1.0.2/humansize/file_size_opts/struct.FileSizeOpts.html - Arc::new(size_opts::FileSizeOpts { - divider: size_opts::Kilo::Binary, - units: size_opts::Kilo::Decimal, - decimal_places: 0, - decimal_zeroes: 0, - fixed_at: size_opts::FixedAt::No, - long_units: false, - space: true, - suffix: "", - allow_negative: false, - }) - }; -} - -#[derive(Debug, Clone)] -pub struct Shown; -#[derive(Debug, Clone)] -pub struct Hidden; - -pub trait Visibility {} - -impl Visibility for Shown {} -impl Visibility for Hidden {} - -#[derive(Debug, Clone)] -pub struct Size { - size: gtk::Label, - separator: gtk::Label, - state: S, -} - -impl Size { - fn set_size(self, s: &str) -> Size { - self.size.set_text(s); - self.size.show(); - self.separator.show(); - Size { - size: self.size, - separator: self.separator, - state: Shown {}, - } - } - - // https://play.rust-lang.org/?gist=1acffaf62743eeb85be1ae6ecf474784&version=stable - // It might be possible to make a generic definition with Specialization. - // https://github.com/rust-lang/rust/issues/31844 - fn into_shown(self) -> Size { - self.size.show(); - self.separator.show(); - - Size { - size: self.size, - separator: self.separator, - state: Shown {}, - } - } - - fn into_hidden(self) -> Size { - self.size.hide(); - self.separator.hide(); - - Size { - size: self.size, - separator: self.separator, - state: Hidden {}, - } - } -} - -// pub trait Playable {} - -// impl Playable for Download {} -// impl Playable for Play {} - -#[derive(Debug, Clone)] -pub struct Download; - -#[derive(Debug, Clone)] -pub struct Play; - -#[derive(Debug, Clone)] -// FIXME: Needs better name. -// Should each button also has it's own type and machine? -pub struct DownloadPlay { - play: gtk::Button, - download: gtk::Button, - state: S, -} - -impl DownloadPlay { - // https://play.rust-lang.org/?gist=1acffaf62743eeb85be1ae6ecf474784&version=stable // It might be possible to make a generic definition with Specialization. - // https://github.com/rust-lang/rust/issues/31844 - fn into_playable(self) -> DownloadPlay { - self.play.show(); - self.download.hide(); - - DownloadPlay { - play: self.play, - download: self.download, - state: Play {}, - } - } - - fn into_fetchable(self) -> DownloadPlay { - self.play.hide(); - self.download.show(); - - DownloadPlay { - play: self.play, - download: self.download, - state: Download {}, - } - } - - fn into_hidden(self) -> DownloadPlay { - self.play.hide(); - self.download.hide(); - - DownloadPlay { - play: self.play, - download: self.download, - state: Hidden {}, - } - } -} - -#[derive(Debug, Clone)] -pub struct Progress { - bar: gtk::ProgressBar, - cancel: gtk::Button, - local_size: gtk::Label, - prog_separator: gtk::Label, - state: S, -} - -impl Progress { - fn into_shown(self) -> Progress { - self.bar.show(); - self.cancel.show(); - self.local_size.show(); - self.prog_separator.show(); - - Progress { - bar: self.bar, - cancel: self.cancel, - local_size: self.local_size, - prog_separator: self.prog_separator, - state: Shown {}, - } - } - - fn into_hidden(self) -> Progress { - self.bar.hide(); - self.cancel.hide(); - self.local_size.hide(); - self.prog_separator.hide(); - - Progress { - bar: self.bar, - cancel: self.cancel, - local_size: self.local_size, - prog_separator: self.prog_separator, - state: Hidden {}, - } - } -} - -#[derive(Debug, Clone)] -pub struct Media { - dl: DownloadPlay, - size: Size, - progress: Progress, -} - -type New = Media