From a96f4c57c9b10fca98efb53ee914ca37fa114930 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Thu, 8 Feb 2018 01:50:48 +0200 Subject: [PATCH 01/33] Probably the worst state machine implementation that was ever written. --- hammond-gtk/src/widgets/episode.rs | 135 +++++++++++++++++++++++------ 1 file changed, 110 insertions(+), 25 deletions(-) diff --git a/hammond-gtk/src/widgets/episode.rs b/hammond-gtk/src/widgets/episode.rs index 491f113..9773b05 100644 --- a/hammond-gtk/src/widgets/episode.rs +++ b/hammond-gtk/src/widgets/episode.rs @@ -40,13 +40,100 @@ lazy_static! { static ref NOW: DateTime = Utc::now(); } +#[derive(Debug, Clone)] +struct Normal; +#[derive(Debug, Clone)] +struct GreyedOut; + +#[derive(Debug, Clone)] +struct Title { + title: gtk::Label, + state: S, +} + +impl Title { + fn new(title: gtk::Label) -> Self { + Title { + title, + state: Normal {}, + } + } + + fn set_title(&self, s: &str) { + self.title.set_text(s); + } +} + +impl Title { + fn set_title(&self, s: &str) { + self.title.set_text(s); + } +} + +impl From> for Title { + fn from(machine: Title) -> Self { + machine + .title + .get_style_context() + .map(|c| c.add_class("dim-label")); + + Title { + title: machine.title, + state: GreyedOut {}, + } + } +} + +impl From> for Title { + fn from(machine: Title) -> Self { + machine + .title + .get_style_context() + .map(|c| c.remove_class("dim-label")); + + Title { + title: machine.title, + state: Normal {}, + } + } +} + +#[derive(Debug, Clone)] +enum TitleMachine { + Normal(Title), + GreyedOut(Title), +} + +impl TitleMachine { + fn new(label: gtk::Label, is_played: bool) -> Self { + let m = TitleMachine::Normal(Title::::new(label)); + m.determine_state(is_played) + } + + fn determine_state(self, is_played: bool) -> Self { + match (self, is_played) { + (title @ TitleMachine::Normal(_), false) => title, + (title @ TitleMachine::GreyedOut(_), true) => title, + (TitleMachine::Normal(val), true) => TitleMachine::GreyedOut(val.into()), + (TitleMachine::GreyedOut(val), false) => TitleMachine::Normal(val.into()), + } + } + + fn set_title(&self, s: &str) { + match *self { + TitleMachine::Normal(ref val) => val.set_title(s), + TitleMachine::GreyedOut(ref val) => val.set_title(s), + } + } +} + #[derive(Debug, Clone)] pub struct EpisodeWidget { pub container: gtk::Box, play: gtk::Button, download: gtk::Button, cancel: gtk::Button, - title: gtk::Label, + title: TitleMachine, date: gtk::Label, duration: gtk::Label, progress: gtk::ProgressBar, @@ -78,13 +165,15 @@ impl Default for EpisodeWidget { let separator2: gtk::Label = builder.get_object("separator2").unwrap(); let prog_separator: gtk::Label = builder.get_object("prog_separator").unwrap(); + let title_machine = TitleMachine::new(title, false); + EpisodeWidget { container, progress, download, play, cancel, - title, + title: title_machine, duration, date, total_size, @@ -98,16 +187,15 @@ impl Default for EpisodeWidget { impl EpisodeWidget { pub fn new(episode: EpisodeWidgetQuery, sender: Sender) -> EpisodeWidget { - let widget = EpisodeWidget::default(); - widget.init(episode, sender); - widget + let mut widget = EpisodeWidget::default(); + widget.init(episode, sender) } - fn init(&self, episode: EpisodeWidgetQuery, sender: Sender) { + fn init(mut self, episode: EpisodeWidgetQuery, sender: Sender) -> Self { WidgetExt::set_name(&self.container, &episode.rowid().to_string()); // Set the title label state. - self.set_title(&episode); + self = self.set_title(&episode); // Set the duaration label. self.set_duration(episode.duration()); @@ -135,15 +223,15 @@ impl EpisodeWidget { let episode = Arc::new(Mutex::new(episode)); - let title = self.title.clone(); - self.play - .connect_clicked(clone!(episode, sender => move |_| { - if let Ok(mut ep) = episode.lock() { - if let Err(err) = on_play_bttn_clicked(&mut ep, &title, sender.clone()){ - error!("Error: {}", err); - }; - } - })); + // let title = self.title.clone(); + // self.play + // .connect_clicked(clone!(episode, sender => move |_| { + // if let Ok(mut ep) = episode.lock() { + // if let Err(err) = on_play_bttn_clicked(&mut ep, &title, sender.clone()){ + // error!("Error: {}", err); + // }; + // } + // })); self.download .connect_clicked(clone!(episode, sender => move |dl| { @@ -157,6 +245,8 @@ impl EpisodeWidget { } } })); + + self } /// Show or hide the play/delete/download buttons upon widget initialization. @@ -170,15 +260,10 @@ impl EpisodeWidget { } /// Determine the title state. - fn set_title(&self, episode: &EpisodeWidgetQuery) { - self.title.set_text(episode.title()); - - // Grey out the title if the episode is played. - if episode.played().is_some() { - self.title - .get_style_context() - .map(|c| c.add_class("dim-label")); - } + fn set_title(mut self, episode: &EpisodeWidgetQuery) -> Self { + self.title.set_title(episode.title()); + self.title = self.title.determine_state(episode.played().is_some()); + self } /// Set the date label depending on the current time. From 7690cb135694c7e737979c20f9fb9a71ed13f2ee Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Thu, 8 Feb 2018 19:53:31 +0200 Subject: [PATCH 02/33] Remove code duplication using generics. --- hammond-gtk/src/widgets/episode.rs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/hammond-gtk/src/widgets/episode.rs b/hammond-gtk/src/widgets/episode.rs index 9773b05..4ec1162 100644 --- a/hammond-gtk/src/widgets/episode.rs +++ b/hammond-gtk/src/widgets/episode.rs @@ -51,6 +51,12 @@ struct Title { state: S, } +impl Title { + fn set_title(&self, s: &str) { + self.title.set_text(s); + } +} + impl Title { fn new(title: gtk::Label) -> Self { Title { @@ -58,16 +64,6 @@ impl Title { state: Normal {}, } } - - fn set_title(&self, s: &str) { - self.title.set_text(s); - } -} - -impl Title { - fn set_title(&self, s: &str) { - self.title.set_text(s); - } } impl From> for Title { From e22a78fac6963675023bef8319b7c3fbdb699645 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Fri, 9 Feb 2018 08:59:50 +0200 Subject: [PATCH 03/33] EpisodeWidget: Re-enable on_play_bttn_clicked callback. Before we were avoiding reloading the widget in view by directly dimming the title label. Now instead we reload the whole widget since I can't figure out a way to have multiple Owneded refferences of the same state machine. --- hammond-gtk/src/widgets/episode.rs | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/hammond-gtk/src/widgets/episode.rs b/hammond-gtk/src/widgets/episode.rs index 4ec1162..d4c4a1f 100644 --- a/hammond-gtk/src/widgets/episode.rs +++ b/hammond-gtk/src/widgets/episode.rs @@ -183,7 +183,7 @@ impl Default for EpisodeWidget { impl EpisodeWidget { pub fn new(episode: EpisodeWidgetQuery, sender: Sender) -> EpisodeWidget { - let mut widget = EpisodeWidget::default(); + let widget = EpisodeWidget::default(); widget.init(episode, sender) } @@ -219,15 +219,14 @@ impl EpisodeWidget { let episode = Arc::new(Mutex::new(episode)); - // let title = self.title.clone(); - // self.play - // .connect_clicked(clone!(episode, sender => move |_| { - // if let Ok(mut ep) = episode.lock() { - // if let Err(err) = on_play_bttn_clicked(&mut ep, &title, sender.clone()){ - // error!("Error: {}", err); - // }; - // } - // })); + self.play + .connect_clicked(clone!(episode, sender => move |_| { + if let Ok(mut ep) = episode.lock() { + if let Err(err) = on_play_bttn_clicked(&mut ep, sender.clone()){ + error!("Error: {}", err); + }; + } + })); self.download .connect_clicked(clone!(episode, sender => move |dl| { @@ -366,16 +365,13 @@ fn on_download_clicked(ep: &EpisodeWidgetQuery, sender: Sender) -> Resul fn on_play_bttn_clicked( episode: &mut EpisodeWidgetQuery, - title: >k::Label, sender: Sender, ) -> Result<(), Error> { open_uri(episode.rowid())?; + episode.set_played_now()?; - if episode.set_played_now().is_ok() { - title.get_style_context().map(|c| c.add_class("dim-label")); - sender.send(Action::RefreshEpisodesViewBGR)?; - }; - + sender.send(Action::RefreshWidgetIfVis)?; + sender.send(Action::RefreshEpisodesView)?; Ok(()) } From 23979b8f221f8318ad6020fc409a640f88630fea Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Fri, 9 Feb 2018 09:13:41 +0200 Subject: [PATCH 04/33] EpisodeWidget: Move state machine implementations into a separate module. --- hammond-gtk/src/widgets/episode.rs | 84 +--------------------- hammond-gtk/src/widgets/episode_states.rs | 85 +++++++++++++++++++++++ hammond-gtk/src/widgets/mod.rs | 1 + 3 files changed, 87 insertions(+), 83 deletions(-) create mode 100644 hammond-gtk/src/widgets/episode_states.rs diff --git a/hammond-gtk/src/widgets/episode.rs b/hammond-gtk/src/widgets/episode.rs index d4c4a1f..bfd16c8 100644 --- a/hammond-gtk/src/widgets/episode.rs +++ b/hammond-gtk/src/widgets/episode.rs @@ -15,6 +15,7 @@ use hammond_data::utils::get_download_folder; use app::Action; use manager; +use widgets::episode_states::*; use std::path::Path; use std::sync::{Arc, Mutex}; @@ -40,89 +41,6 @@ lazy_static! { static ref NOW: DateTime = Utc::now(); } -#[derive(Debug, Clone)] -struct Normal; -#[derive(Debug, Clone)] -struct GreyedOut; - -#[derive(Debug, Clone)] -struct Title { - title: gtk::Label, - state: S, -} - -impl Title { - fn set_title(&self, s: &str) { - self.title.set_text(s); - } -} - -impl Title { - fn new(title: gtk::Label) -> Self { - Title { - title, - state: Normal {}, - } - } -} - -impl From> for Title { - fn from(machine: Title) -> Self { - machine - .title - .get_style_context() - .map(|c| c.add_class("dim-label")); - - Title { - title: machine.title, - state: GreyedOut {}, - } - } -} - -impl From> for Title { - fn from(machine: Title) -> Self { - machine - .title - .get_style_context() - .map(|c| c.remove_class("dim-label")); - - Title { - title: machine.title, - state: Normal {}, - } - } -} - -#[derive(Debug, Clone)] -enum TitleMachine { - Normal(Title), - GreyedOut(Title), -} - -impl TitleMachine { - fn new(label: gtk::Label, is_played: bool) -> Self { - let m = TitleMachine::Normal(Title::::new(label)); - m.determine_state(is_played) - } - - fn determine_state(self, is_played: bool) -> Self { - match (self, is_played) { - (title @ TitleMachine::Normal(_), false) => title, - (title @ TitleMachine::GreyedOut(_), true) => title, - (TitleMachine::Normal(val), true) => TitleMachine::GreyedOut(val.into()), - (TitleMachine::GreyedOut(val), false) => TitleMachine::Normal(val.into()), - } - } - - fn set_title(&self, s: &str) { - match *self { - TitleMachine::Normal(ref val) => val.set_title(s), - TitleMachine::GreyedOut(ref val) => val.set_title(s), - } - } -} - #[derive(Debug, Clone)] pub struct EpisodeWidget { pub container: gtk::Box, diff --git a/hammond-gtk/src/widgets/episode_states.rs b/hammond-gtk/src/widgets/episode_states.rs new file mode 100644 index 0000000..c4a85ee --- /dev/null +++ b/hammond-gtk/src/widgets/episode_states.rs @@ -0,0 +1,85 @@ +use gtk; +use gtk::prelude::*; + +#[derive(Debug, Clone)] +pub struct Normal; +#[derive(Debug, Clone)] +pub struct GreyedOut; + +#[derive(Debug, Clone)] +pub struct Title { + title: gtk::Label, + state: S, +} + +impl Title { + fn set_title(&self, s: &str) { + self.title.set_text(s); + } +} + +impl Title { + fn new(title: gtk::Label) -> Self { + Title { + title, + state: Normal {}, + } + } +} + +impl From> for Title { + fn from(machine: Title) -> Self { + machine + .title + .get_style_context() + .map(|c| c.add_class("dim-label")); + + Title { + title: machine.title, + state: GreyedOut {}, + } + } +} + +impl From> for Title { + fn from(machine: Title) -> Self { + machine + .title + .get_style_context() + .map(|c| c.remove_class("dim-label")); + + Title { + title: machine.title, + state: Normal {}, + } + } +} + +#[derive(Debug, Clone)] +pub enum TitleMachine { + Normal(Title), + GreyedOut(Title), +} + +impl TitleMachine { + pub fn new(label: gtk::Label, is_played: bool) -> Self { + let m = TitleMachine::Normal(Title::::new(label)); + m.determine_state(is_played) + } + + pub fn determine_state(self, is_played: bool) -> Self { + match (self, is_played) { + (title @ TitleMachine::Normal(_), false) => title, + (title @ TitleMachine::GreyedOut(_), true) => title, + (TitleMachine::Normal(val), true) => TitleMachine::GreyedOut(val.into()), + (TitleMachine::GreyedOut(val), false) => TitleMachine::Normal(val.into()), + } + } + + pub fn set_title(&self, s: &str) { + match *self { + TitleMachine::Normal(ref val) => val.set_title(s), + TitleMachine::GreyedOut(ref val) => val.set_title(s), + } + } +} diff --git a/hammond-gtk/src/widgets/mod.rs b/hammond-gtk/src/widgets/mod.rs index 3b348fa..476deef 100644 --- a/hammond-gtk/src/widgets/mod.rs +++ b/hammond-gtk/src/widgets/mod.rs @@ -1,5 +1,6 @@ mod show; mod episode; +mod episode_states; pub use self::episode::EpisodeWidget; pub use self::show::ShowWidget; From f0ce0eb653958a1bea2a05eb75b0fe39ba23f7c2 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Fri, 9 Feb 2018 10:12:37 +0200 Subject: [PATCH 05/33] EpisodeWidget: Implement a state machine for duration label. --- hammond-gtk/src/widgets/episode.rs | 23 ++---- hammond-gtk/src/widgets/episode_states.rs | 98 +++++++++++++++++++++++ 2 files changed, 105 insertions(+), 16 deletions(-) diff --git a/hammond-gtk/src/widgets/episode.rs b/hammond-gtk/src/widgets/episode.rs index bfd16c8..9e7d593 100644 --- a/hammond-gtk/src/widgets/episode.rs +++ b/hammond-gtk/src/widgets/episode.rs @@ -4,7 +4,6 @@ use gtk; use chrono::prelude::*; use gtk::prelude::*; -use chrono::Duration; use failure::Error; use humansize::{file_size_opts as size_opts, FileSize}; use open; @@ -49,11 +48,10 @@ pub struct EpisodeWidget { cancel: gtk::Button, title: TitleMachine, date: gtk::Label, - duration: gtk::Label, + duration: DurationMachine, progress: gtk::ProgressBar, total_size: gtk::Label, local_size: gtk::Label, - separator1: gtk::Label, separator2: gtk::Label, prog_separator: gtk::Label, } @@ -80,6 +78,7 @@ impl Default for EpisodeWidget { let prog_separator: gtk::Label = builder.get_object("prog_separator").unwrap(); let title_machine = TitleMachine::new(title, false); + let duration_machine = DurationMachine::new(duration, separator1, None); EpisodeWidget { container, @@ -88,11 +87,10 @@ impl Default for EpisodeWidget { play, cancel, title: title_machine, - duration, + duration: duration_machine, date, total_size, local_size, - separator1, separator2, prog_separator, } @@ -112,7 +110,7 @@ impl EpisodeWidget { self = self.set_title(&episode); // Set the duaration label. - self.set_duration(episode.duration()); + self = self.set_duration(episode.duration()); // Set the date label. self.set_date(episode.epoch()); @@ -191,16 +189,9 @@ impl EpisodeWidget { } /// Set the duration label. - fn set_duration(&self, seconds: Option) -> Option<()> { - let minutes = Duration::seconds(seconds?.into()).num_minutes(); - if minutes == 0 { - return None; - } - - self.duration.set_text(&format!("{} min", minutes)); - self.duration.show(); - self.separator1.show(); - Some(()) + fn set_duration(mut self, seconds: Option) -> Self { + self.duration = self.duration.determine_state(seconds); + self } /// Set the Episode label dependings on its size diff --git a/hammond-gtk/src/widgets/episode_states.rs b/hammond-gtk/src/widgets/episode_states.rs index c4a85ee..b208fcb 100644 --- a/hammond-gtk/src/widgets/episode_states.rs +++ b/hammond-gtk/src/widgets/episode_states.rs @@ -1,3 +1,4 @@ +use chrono; use gtk; use gtk::prelude::*; @@ -83,3 +84,100 @@ impl TitleMachine { } } } + +#[derive(Debug, Clone)] +pub struct Shown; +#[derive(Debug, Clone)] +pub struct Hidden; + +#[derive(Debug, Clone)] +pub struct Duration { + // TODO: make duration and separator diff types + duration: gtk::Label, + separator: gtk::Label, + state: S, +} + +impl Duration { + // This needs a better name. + fn set_duration(&self, minutes: i64) { + self.duration.set_text(&format!("{} min", minutes)); + } +} + +impl Duration { + fn new(duration: gtk::Label, separator: gtk::Label) -> Self { + duration.hide(); + separator.hide(); + + Duration { + duration, + separator, + state: Hidden {}, + } + } +} + +impl From> for Duration { + fn from(d: Duration) -> Self { + d.duration.show(); + d.separator.show(); + + Duration { + duration: d.duration, + separator: d.separator, + state: Shown {}, + } + } +} + +impl From> for Duration { + fn from(d: Duration) -> Self { + d.duration.hide(); + d.separator.hide(); + + Duration { + duration: d.duration, + separator: d.separator, + state: Hidden {}, + } + } +} + +#[derive(Debug, Clone)] +pub enum DurationMachine { + Hidden(Duration), + Shown(Duration), +} + +impl DurationMachine { + pub fn new(duration: gtk::Label, separator: gtk::Label, seconds: Option) -> Self { + let m = DurationMachine::Hidden(Duration::::new(duration, separator)); + m.determine_state(seconds) + } + + pub fn determine_state(self, seconds: Option) -> Self { + match (self, seconds) { + (DurationMachine::Hidden(val), None) => DurationMachine::Hidden(val.into()), + (DurationMachine::Shown(val), None) => DurationMachine::Hidden(val.into()), + (DurationMachine::Hidden(val), Some(s)) => { + let minutes = chrono::Duration::seconds(s.into()).num_minutes(); + if minutes == 0 { + DurationMachine::Hidden(val.into()) + } else { + val.set_duration(minutes); + DurationMachine::Shown(val.into()) + } + } + (DurationMachine::Shown(val), Some(s)) => { + let minutes = chrono::Duration::seconds(s.into()).num_minutes(); + if minutes == 0 { + DurationMachine::Hidden(val.into()) + } else { + val.set_duration(minutes); + DurationMachine::Shown(val.into()) + } + } + } + } +} From 3a9a2f40331bf0b54c9cf6f3ef47786db1ae44b8 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Sat, 10 Feb 2018 03:15:12 +0200 Subject: [PATCH 06/33] EpisdoeWidget: Use take_mut crate to allow for a better api. Currently it's required that you take mut self in order to manipulate the internal state machines. This would not allow passing an Arc/Rc to a callback since A/Rc only derefs to &T and not T. The take_mut crate allows the retrieval of ownership if you have a &mut refference and as long you return T again. So Arc could work with callbacks and embed Nested state machies without copying. --- Cargo.lock | 7 +++++ hammond-gtk/Cargo.toml | 1 + hammond-gtk/src/main.rs | 1 + hammond-gtk/src/widgets/episode.rs | 37 +++++++++++++++-------- hammond-gtk/src/widgets/episode_states.rs | 11 ++++--- 5 files changed, 41 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ff8f174..1ac4536 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -678,6 +678,7 @@ dependencies = [ "open 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "send-cell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "take_mut 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1610,6 +1611,11 @@ name = "take" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "take_mut" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "tempdir" version = "0.3.6" @@ -2059,6 +2065,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" "checksum synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a761d12e6d8dcb4dcf952a7a89b475e3a9d69e4a69307e01a470977642914bd" "checksum take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b157868d8ac1f56b64604539990685fa7611d8fa9e5476cf0c02cf34d32917c5" +"checksum take_mut 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50b910a1174df4aeb5738e8a0e7253883cf7801de40d094175a5a557e487f4c5" "checksum tempdir 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f73eebdb68c14bcb24aef74ea96079830e7fa7b31a6106e42ea7ee887c1e134e" "checksum tendril 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9de21546595a0873061940d994bbbc5c35f024ae4fd61ec5c5b159115684f508" "checksum term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1" diff --git a/hammond-gtk/Cargo.toml b/hammond-gtk/Cargo.toml index d8b2ab2..2d28f1d 100644 --- a/hammond-gtk/Cargo.toml +++ b/hammond-gtk/Cargo.toml @@ -22,6 +22,7 @@ send-cell = "0.1.2" url = "1.6.0" failure = "0.1.1" failure_derive = "0.1.1" +take_mut = "0.2.0" [dependencies.gtk] features = ["v3_22"] diff --git a/hammond-gtk/src/main.rs b/hammond-gtk/src/main.rs index b19d6c8..d6c9603 100644 --- a/hammond-gtk/src/main.rs +++ b/hammond-gtk/src/main.rs @@ -24,6 +24,7 @@ extern crate humansize; extern crate loggerv; extern crate open; extern crate send_cell; +extern crate take_mut; extern crate url; // extern crate rayon; diff --git a/hammond-gtk/src/widgets/episode.rs b/hammond-gtk/src/widgets/episode.rs index 9e7d593..b5d19dd 100644 --- a/hammond-gtk/src/widgets/episode.rs +++ b/hammond-gtk/src/widgets/episode.rs @@ -7,6 +7,7 @@ use gtk::prelude::*; use failure::Error; use humansize::{file_size_opts as size_opts, FileSize}; use open; +use take_mut; use hammond_data::{EpisodeWidgetQuery, Podcast}; use hammond_data::dbqueries; @@ -16,6 +17,7 @@ use app::Action; use manager; use widgets::episode_states::*; +use std::ops::DerefMut; use std::path::Path; use std::sync::{Arc, Mutex}; use std::sync::mpsc::Sender; @@ -46,7 +48,7 @@ pub struct EpisodeWidget { play: gtk::Button, download: gtk::Button, cancel: gtk::Button, - title: TitleMachine, + title: Arc>, date: gtk::Label, duration: DurationMachine, progress: gtk::ProgressBar, @@ -77,7 +79,7 @@ impl Default for EpisodeWidget { let separator2: gtk::Label = builder.get_object("separator2").unwrap(); let prog_separator: gtk::Label = builder.get_object("prog_separator").unwrap(); - let title_machine = TitleMachine::new(title, false); + let title_machine = Arc::new(Mutex::new(TitleMachine::new(title, false))); let duration_machine = DurationMachine::new(duration, separator1, None); EpisodeWidget { @@ -106,15 +108,17 @@ impl EpisodeWidget { fn init(mut self, episode: EpisodeWidgetQuery, sender: Sender) -> Self { WidgetExt::set_name(&self.container, &episode.rowid().to_string()); - // Set the title label state. - self = self.set_title(&episode); - // Set the duaration label. self = self.set_duration(episode.duration()); // Set the date label. self.set_date(episode.epoch()); + // Set the title label state. + if let Err(err) = self.set_title(&episode) { + error!("Failed to set title state: {}", err); + } + // Show or hide the play/delete/download buttons upon widget initialization. if let Err(err) = self.show_buttons(episode.local_uri()) { error!("Failed to determine play/download button state."); @@ -135,10 +139,11 @@ impl EpisodeWidget { let episode = Arc::new(Mutex::new(episode)); + let title = self.title.clone(); self.play .connect_clicked(clone!(episode, sender => move |_| { if let Ok(mut ep) = episode.lock() { - if let Err(err) = on_play_bttn_clicked(&mut ep, sender.clone()){ + if let Err(err) = on_play_bttn_clicked(&mut ep, title.clone(), sender.clone()){ error!("Error: {}", err); }; } @@ -171,10 +176,13 @@ impl EpisodeWidget { } /// Determine the title state. - fn set_title(mut self, episode: &EpisodeWidgetQuery) -> Self { - self.title.set_title(episode.title()); - self.title = self.title.determine_state(episode.played().is_some()); - self + fn set_title(&mut self, episode: &EpisodeWidgetQuery) -> Result<(), Error> { + let mut lock = self.title.lock().map_err(|err| format_err!("{}", err))?; + lock.set_title(episode.title()); + take_mut::take(lock.deref_mut(), |title| { + title.determine_state(episode.played().is_some()) + }); + Ok(()) } /// Set the date label depending on the current time. @@ -274,13 +282,18 @@ fn on_download_clicked(ep: &EpisodeWidgetQuery, sender: Sender) -> Resul fn on_play_bttn_clicked( episode: &mut EpisodeWidgetQuery, + title: Arc>, sender: Sender, ) -> Result<(), Error> { open_uri(episode.rowid())?; episode.set_played_now()?; - sender.send(Action::RefreshWidgetIfVis)?; - sender.send(Action::RefreshEpisodesView)?; + let mut lock = title.lock().map_err(|err| format_err!("{}", err))?; + take_mut::take(lock.deref_mut(), |title| { + title.determine_state(episode.played().is_some()) + }); + + sender.send(Action::RefreshEpisodesViewBGR)?; Ok(()) } diff --git a/hammond-gtk/src/widgets/episode_states.rs b/hammond-gtk/src/widgets/episode_states.rs index b208fcb..d1ea1f9 100644 --- a/hammond-gtk/src/widgets/episode_states.rs +++ b/hammond-gtk/src/widgets/episode_states.rs @@ -14,7 +14,10 @@ pub struct Title { } impl Title { - fn set_title(&self, s: &str) { + #[allow(unused_must_use)] + // This does not need to be &mut since gtk-rs does not model ownership + // But I think it wouldn't heart if we treat it as a Rust api. + fn set_title(&mut self, s: &str) { self.title.set_text(s); } } @@ -77,10 +80,10 @@ impl TitleMachine { } } - pub fn set_title(&self, s: &str) { + pub fn set_title(&mut self, s: &str) { match *self { - TitleMachine::Normal(ref val) => val.set_title(s), - TitleMachine::GreyedOut(ref val) => val.set_title(s), + TitleMachine::Normal(ref mut val) => val.set_title(s), + TitleMachine::GreyedOut(ref mut val) => val.set_title(s), } } } From fc48ce9c47c7914046020eaa7c87f3fb854c12a7 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Sat, 10 Feb 2018 03:33:39 +0200 Subject: [PATCH 07/33] EpisodeWidget: Migrate Duration Machine to use take mut too, and revert the api to require just &mut self. --- hammond-gtk/src/widgets/episode.rs | 31 +++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/hammond-gtk/src/widgets/episode.rs b/hammond-gtk/src/widgets/episode.rs index b5d19dd..3aedca2 100644 --- a/hammond-gtk/src/widgets/episode.rs +++ b/hammond-gtk/src/widgets/episode.rs @@ -50,7 +50,7 @@ pub struct EpisodeWidget { cancel: gtk::Button, title: Arc>, date: gtk::Label, - duration: DurationMachine, + duration: Arc>, progress: gtk::ProgressBar, total_size: gtk::Label, local_size: gtk::Label, @@ -80,7 +80,8 @@ impl Default for EpisodeWidget { let prog_separator: gtk::Label = builder.get_object("prog_separator").unwrap(); let title_machine = Arc::new(Mutex::new(TitleMachine::new(title, false))); - let duration_machine = DurationMachine::new(duration, separator1, None); + let dur = DurationMachine::new(duration, separator1, None); + let duration_machine = Arc::new(Mutex::new(dur)); EpisodeWidget { container, @@ -101,16 +102,14 @@ impl Default for EpisodeWidget { impl EpisodeWidget { pub fn new(episode: EpisodeWidgetQuery, sender: Sender) -> EpisodeWidget { - let widget = EpisodeWidget::default(); - widget.init(episode, sender) + let mut widget = EpisodeWidget::default(); + widget.init(episode, sender); + widget } - fn init(mut self, episode: EpisodeWidgetQuery, sender: Sender) -> Self { + fn init(&mut self, episode: EpisodeWidgetQuery, sender: Sender) { WidgetExt::set_name(&self.container, &episode.rowid().to_string()); - // Set the duaration label. - self = self.set_duration(episode.duration()); - // Set the date label. self.set_date(episode.epoch()); @@ -119,6 +118,11 @@ impl EpisodeWidget { error!("Failed to set title state: {}", err); } + // Set the duaration label. + if let Err(err) = self.set_duration(episode.duration()) { + error!("Failed to set duration state: {}", err); + } + // Show or hide the play/delete/download buttons upon widget initialization. if let Err(err) = self.show_buttons(episode.local_uri()) { error!("Failed to determine play/download button state."); @@ -161,8 +165,6 @@ impl EpisodeWidget { } } })); - - self } /// Show or hide the play/delete/download buttons upon widget initialization. @@ -197,9 +199,12 @@ impl EpisodeWidget { } /// Set the duration label. - fn set_duration(mut self, seconds: Option) -> Self { - self.duration = self.duration.determine_state(seconds); - self + fn set_duration(&mut self, seconds: Option) -> Result<(), Error> { + let mut lock = self.duration.lock().map_err(|err| format_err!("{}", err))?; + take_mut::take(lock.deref_mut(), |duration| { + duration.determine_state(seconds) + }); + Ok(()) } /// Set the Episode label dependings on its size From 6d9dfe6fe16dd95801810d73cbcce80ce3020129 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Sat, 10 Feb 2018 05:41:25 +0200 Subject: [PATCH 08/33] EpisodeWidget: Add a StateMachine for the size labels. --- hammond-gtk/src/widgets/episode_states.rs | 138 ++++++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/hammond-gtk/src/widgets/episode_states.rs b/hammond-gtk/src/widgets/episode_states.rs index d1ea1f9..7f470f7 100644 --- a/hammond-gtk/src/widgets/episode_states.rs +++ b/hammond-gtk/src/widgets/episode_states.rs @@ -184,3 +184,141 @@ impl DurationMachine { } } } + +#[derive(Debug, Clone)] +pub struct LocalShown; + +#[derive(Debug, Clone)] +pub struct TotalShown; + +#[derive(Debug, Clone)] +pub struct Unkown; + +#[derive(Debug, Clone)] +pub struct InProgress; + +#[derive(Debug, Clone)] +pub struct Size { + local_size: gtk::Label, + total_size: gtk::Label, + separator: gtk::Label, + prog_separator: gtk::Label, + state: S, +} + +impl Size { + fn new( + local_size: gtk::Label, + total_size: gtk::Label, + separator: gtk::Label, + prog_separator: gtk::Label, + ) -> Self { + local_size.hide(); + total_size.hide(); + separator.hide(); + prog_separator.hide(); + + Size { + local_size, + total_size, + separator, + prog_separator, + state: Unkown {}, + } + } +} + +impl From> for Size { + fn from(f: Size) -> Self { + f.prog_separator.hide(); + f.total_size.hide(); + f.local_size.show(); + f.separator.show(); + + Size { + local_size: f.local_size, + total_size: f.total_size, + separator: f.separator, + prog_separator: f.prog_separator, + state: LocalShown {}, + } + } +} + +impl From> for Size { + fn from(f: Size) -> Self { + f.prog_separator.show(); + f.total_size.show(); + f.local_size.show(); + f.separator.show(); + + Size { + local_size: f.local_size, + total_size: f.total_size, + separator: f.separator, + prog_separator: f.prog_separator, + state: InProgress {}, + } + } +} + +impl From> for Size { + fn from(f: Size) -> Self { + f.prog_separator.show(); + f.total_size.show(); + f.local_size.show(); + f.separator.show(); + + Size { + local_size: f.local_size, + total_size: f.total_size, + separator: f.separator, + prog_separator: f.prog_separator, + state: InProgress {}, + } + } +} + +impl From> for Size { + fn from(f: Size) -> Self { + f.prog_separator.hide(); + f.total_size.hide(); + f.local_size.show(); + f.separator.show(); + + Size { + local_size: f.local_size, + total_size: f.total_size, + separator: f.separator, + prog_separator: f.prog_separator, + state: LocalShown {}, + } + } +} + +pub enum SizeMachine { + LocalShown(Size), + TotallShown(Size), + Unkown(Size), + InProgress(Size), +} + +impl SizeMachine { + pub fn new( + local_size: gtk::Label, + total_size: gtk::Label, + separator: gtk::Label, + prog_separator: gtk::Label, + ) -> Self { + SizeMachine::Unkown(Size::::new( + local_size, + total_size, + separator, + prog_separator, + )) + } + + pub fn determine_state(self) -> Self { + unimplemented!() + } +} From 46bd23cf66d504503d416f3d091ad68d1bbf72bc Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Sat, 10 Feb 2018 08:00:12 +0200 Subject: [PATCH 09/33] EpisodeWidget: Add a StateMachine that manages Play and Download Buttons. --- hammond-gtk/src/widgets/episode_states.rs | 162 +++++++++++++++++++++- 1 file changed, 156 insertions(+), 6 deletions(-) diff --git a/hammond-gtk/src/widgets/episode_states.rs b/hammond-gtk/src/widgets/episode_states.rs index 7f470f7..070b45c 100644 --- a/hammond-gtk/src/widgets/episode_states.rs +++ b/hammond-gtk/src/widgets/episode_states.rs @@ -1,7 +1,19 @@ +// 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 chrono; use gtk; use gtk::prelude::*; +#[derive(Debug, Clone)] +pub struct Shown; +#[derive(Debug, Clone)] +pub struct Hidden; + #[derive(Debug, Clone)] pub struct Normal; #[derive(Debug, Clone)] @@ -87,12 +99,6 @@ impl TitleMachine { } } } - -#[derive(Debug, Clone)] -pub struct Shown; -#[derive(Debug, Clone)] -pub struct Hidden; - #[derive(Debug, Clone)] pub struct Duration { // TODO: make duration and separator diff types @@ -296,6 +302,7 @@ impl From> for Size { } } +#[derive(Debug, Clone)] pub enum SizeMachine { LocalShown(Size), TotallShown(Size), @@ -322,3 +329,146 @@ impl SizeMachine { unimplemented!() } } +#[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 { + fn new(play: gtk::Button, download: gtk::Button) -> Self { + play.hide(); + download.show(); + + DownloadPlay { + play, + download, + state: Download {}, + } + } +} + +impl From> for DownloadPlay { + fn from(f: DownloadPlay) -> Self { + f.play.hide(); + f.download.show(); + + DownloadPlay { + play: f.play, + download: f.download, + state: Download {}, + } + } +} + +impl From> for DownloadPlay { + fn from(f: DownloadPlay) -> Self { + f.play.show(); + f.download.hide(); + + DownloadPlay { + play: f.play, + download: f.download, + state: Play {}, + } + } +} + +impl From> for DownloadPlay { + fn from(f: DownloadPlay) -> Self { + f.play.hide(); + f.download.hide(); + + DownloadPlay { + play: f.play, + download: f.download, + state: Hidden {}, + } + } +} + +impl From> for DownloadPlay { + fn from(f: DownloadPlay) -> Self { + f.play.hide(); + f.download.hide(); + + DownloadPlay { + play: f.play, + download: f.download, + state: Hidden {}, + } + } +} + +impl From> for DownloadPlay { + fn from(f: DownloadPlay) -> Self { + f.play.hide(); + f.download.show(); + + DownloadPlay { + play: f.play, + download: f.download, + state: Download {}, + } + } +} + +impl From> for DownloadPlay { + fn from(f: DownloadPlay) -> Self { + f.play.show(); + f.download.show(); + + DownloadPlay { + play: f.play, + download: f.download, + state: Play {}, + } + } +} + +pub enum DownloadPlayMachine { + Play(DownloadPlay), + Download(DownloadPlay), + Hidden(DownloadPlay), +} + +impl DownloadPlayMachine { + pub fn new(play: gtk::Button, download: gtk::Button) -> Self { + DownloadPlayMachine::Download(DownloadPlay::::new(play, download)) + } + + pub fn determine_state(self, downloaded: bool, should_hide: bool) -> Self { + match (self, downloaded, should_hide) { + (DownloadPlayMachine::Play(val), true, false) => DownloadPlayMachine::Play(val.into()), + (DownloadPlayMachine::Play(val), false, false) => { + DownloadPlayMachine::Download(val.into()) + } + (DownloadPlayMachine::Download(val), true, false) => { + DownloadPlayMachine::Play(val.into()) + } + (DownloadPlayMachine::Download(val), false, false) => { + DownloadPlayMachine::Download(val.into()) + } + (DownloadPlayMachine::Hidden(val), true, false) => { + DownloadPlayMachine::Play(val.into()) + } + (DownloadPlayMachine::Hidden(val), false, false) => { + DownloadPlayMachine::Download(val.into()) + } + (DownloadPlayMachine::Play(val), _, true) => DownloadPlayMachine::Hidden(val.into()), + (DownloadPlayMachine::Download(val), _, true) => { + DownloadPlayMachine::Hidden(val.into()) + } + (DownloadPlayMachine::Hidden(val), _, true) => DownloadPlayMachine::Hidden(val.into()), + } + } +} From f7b5b3537408d260b534a818a1f8364ceea28a0f Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Sat, 10 Feb 2018 08:13:07 +0200 Subject: [PATCH 10/33] EpisodeWidget: change DownloadPlayMachine default constructor to a hidden state. --- hammond-gtk/src/widgets/episode_states.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hammond-gtk/src/widgets/episode_states.rs b/hammond-gtk/src/widgets/episode_states.rs index 070b45c..c8078f9 100644 --- a/hammond-gtk/src/widgets/episode_states.rs +++ b/hammond-gtk/src/widgets/episode_states.rs @@ -344,15 +344,15 @@ pub struct DownloadPlay { state: S, } -impl DownloadPlay { +impl DownloadPlay { fn new(play: gtk::Button, download: gtk::Button) -> Self { play.hide(); - download.show(); + download.hide(); DownloadPlay { play, download, - state: Download {}, + state: Hidden {}, } } } @@ -443,7 +443,7 @@ pub enum DownloadPlayMachine { impl DownloadPlayMachine { pub fn new(play: gtk::Button, download: gtk::Button) -> Self { - DownloadPlayMachine::Download(DownloadPlay::::new(play, download)) + DownloadPlayMachine::Hidden(DownloadPlay::::new(play, download)) } pub fn determine_state(self, downloaded: bool, should_hide: bool) -> Self { From 2fbc833ebe83661fb02aa9c82a3492a38fe590c5 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Sat, 10 Feb 2018 09:11:31 +0200 Subject: [PATCH 11/33] EpisodeWidget: Add a state machine that will manager progress_bar and cancel bttn. --- hammond-gtk/src/widgets/episode_states.rs | 89 ++++++++++++++++++++--- 1 file changed, 79 insertions(+), 10 deletions(-) diff --git a/hammond-gtk/src/widgets/episode_states.rs b/hammond-gtk/src/widgets/episode_states.rs index c8078f9..b7cdef9 100644 --- a/hammond-gtk/src/widgets/episode_states.rs +++ b/hammond-gtk/src/widgets/episode_states.rs @@ -99,6 +99,7 @@ impl TitleMachine { } } } + #[derive(Debug, Clone)] pub struct Duration { // TODO: make duration and separator diff types @@ -128,26 +129,26 @@ impl Duration { } impl From> for Duration { - fn from(d: Duration) -> Self { - d.duration.show(); - d.separator.show(); + fn from(f: Duration) -> Self { + f.duration.show(); + f.separator.show(); Duration { - duration: d.duration, - separator: d.separator, + duration: f.duration, + separator: f.separator, state: Shown {}, } } } impl From> for Duration { - fn from(d: Duration) -> Self { - d.duration.hide(); - d.separator.hide(); + fn from(f: Duration) -> Self { + f.duration.hide(); + f.separator.hide(); Duration { - duration: d.duration, - separator: d.separator, + duration: f.duration, + separator: f.separator, state: Hidden {}, } } @@ -329,6 +330,7 @@ impl SizeMachine { unimplemented!() } } + #[derive(Debug, Clone)] pub struct Download; @@ -472,3 +474,70 @@ impl DownloadPlayMachine { } } } + +#[derive(Debug, Clone)] +pub struct Progress { + bar: gtk::ProgressBar, + cancel: gtk::Button, + state: S, +} + +impl Progress { + fn new(bar: gtk::ProgressBar, cancel: gtk::Button) -> Self { + bar.hide(); + cancel.hide(); + + Progress { + bar, + cancel, + state: Hidden {}, + } + } +} + +impl From> for Progress { + fn from(f: Progress) -> Self { + f.bar.show(); + f.cancel.show(); + + Progress { + bar: f.bar, + cancel: f.cancel, + state: Shown {}, + } + } +} + +impl From> for Progress { + fn from(f: Progress) -> Self { + f.bar.hide(); + f.cancel.hide(); + + Progress { + bar: f.bar, + cancel: f.cancel, + state: Hidden {}, + } + } +} + +#[derive(Debug, Clone)] +pub enum ProgressMachine { + Hidden(Progress), + Shown(Progress), +} + +impl ProgressMachine { + pub fn new(bar: gtk::ProgressBar, cancel: gtk::Button) -> Self { + ProgressMachine::Hidden(Progress::::new(bar, cancel)) + } + + pub fn determine_state(self, is_active: bool) -> Self { + match (self, is_active) { + (ProgressMachine::Hidden(val), false) => ProgressMachine::Hidden(val.into()), + (ProgressMachine::Hidden(val), true) => ProgressMachine::Shown(val.into()), + (ProgressMachine::Shown(val), false) => ProgressMachine::Hidden(val.into()), + (ProgressMachine::Shown(val), true) => ProgressMachine::Shown(val.into()), + } + } +} From bdf8901dd83535e326fbd6719baca03cfe5a2fa1 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Tue, 13 Feb 2018 02:23:32 +0200 Subject: [PATCH 12/33] This compiles. Instead of having a Wrapper of StateMachinesWrappers, use only the desired possible states in A new struct with only 1 Wrapper that covers all 3 of the embeded state machines. I don't even know if the comment makes any sense. Sorry. --- hammond-gtk/src/widgets/episode_states.rs | 175 ++++++++-------------- 1 file changed, 60 insertions(+), 115 deletions(-) diff --git a/hammond-gtk/src/widgets/episode_states.rs b/hammond-gtk/src/widgets/episode_states.rs index b7cdef9..edd6d19 100644 --- a/hammond-gtk/src/widgets/episode_states.rs +++ b/hammond-gtk/src/widgets/episode_states.rs @@ -192,9 +192,6 @@ impl DurationMachine { } } -#[derive(Debug, Clone)] -pub struct LocalShown; - #[derive(Debug, Clone)] pub struct TotalShown; @@ -213,6 +210,12 @@ pub struct Size { state: S, } +impl Size { + fn set_total_size(&self, text: &str) { + self.total_size.set_text(text) + } +} + impl Size { fn new( local_size: gtk::Label, @@ -235,23 +238,6 @@ impl Size { } } -impl From> for Size { - fn from(f: Size) -> Self { - f.prog_separator.hide(); - f.total_size.hide(); - f.local_size.show(); - f.separator.show(); - - Size { - local_size: f.local_size, - total_size: f.total_size, - separator: f.separator, - prog_separator: f.prog_separator, - state: LocalShown {}, - } - } -} - impl From> for Size { fn from(f: Size) -> Self { f.prog_separator.show(); @@ -286,51 +272,6 @@ impl From> for Size { } } -impl From> for Size { - fn from(f: Size) -> Self { - f.prog_separator.hide(); - f.total_size.hide(); - f.local_size.show(); - f.separator.show(); - - Size { - local_size: f.local_size, - total_size: f.total_size, - separator: f.separator, - prog_separator: f.prog_separator, - state: LocalShown {}, - } - } -} - -#[derive(Debug, Clone)] -pub enum SizeMachine { - LocalShown(Size), - TotallShown(Size), - Unkown(Size), - InProgress(Size), -} - -impl SizeMachine { - pub fn new( - local_size: gtk::Label, - total_size: gtk::Label, - separator: gtk::Label, - prog_separator: gtk::Label, - ) -> Self { - SizeMachine::Unkown(Size::::new( - local_size, - total_size, - separator, - prog_separator, - )) - } - - pub fn determine_state(self) -> Self { - unimplemented!() - } -} - #[derive(Debug, Clone)] pub struct Download; @@ -437,44 +378,6 @@ impl From> for DownloadPlay { } } -pub enum DownloadPlayMachine { - Play(DownloadPlay), - Download(DownloadPlay), - Hidden(DownloadPlay), -} - -impl DownloadPlayMachine { - pub fn new(play: gtk::Button, download: gtk::Button) -> Self { - DownloadPlayMachine::Hidden(DownloadPlay::::new(play, download)) - } - - pub fn determine_state(self, downloaded: bool, should_hide: bool) -> Self { - match (self, downloaded, should_hide) { - (DownloadPlayMachine::Play(val), true, false) => DownloadPlayMachine::Play(val.into()), - (DownloadPlayMachine::Play(val), false, false) => { - DownloadPlayMachine::Download(val.into()) - } - (DownloadPlayMachine::Download(val), true, false) => { - DownloadPlayMachine::Play(val.into()) - } - (DownloadPlayMachine::Download(val), false, false) => { - DownloadPlayMachine::Download(val.into()) - } - (DownloadPlayMachine::Hidden(val), true, false) => { - DownloadPlayMachine::Play(val.into()) - } - (DownloadPlayMachine::Hidden(val), false, false) => { - DownloadPlayMachine::Download(val.into()) - } - (DownloadPlayMachine::Play(val), _, true) => DownloadPlayMachine::Hidden(val.into()), - (DownloadPlayMachine::Download(val), _, true) => { - DownloadPlayMachine::Hidden(val.into()) - } - (DownloadPlayMachine::Hidden(val), _, true) => DownloadPlayMachine::Hidden(val.into()), - } - } -} - #[derive(Debug, Clone)] pub struct Progress { bar: gtk::ProgressBar, @@ -522,22 +425,64 @@ impl From> for Progress { } #[derive(Debug, Clone)] -pub enum ProgressMachine { - Hidden(Progress), - Shown(Progress), +pub struct Media { + dl: DownloadPlay, + progress: Progress

, + size: Size, } -impl ProgressMachine { - pub fn new(bar: gtk::ProgressBar, cancel: gtk::Button) -> Self { - ProgressMachine::Hidden(Progress::::new(bar, cancel)) +#[derive(Debug, Clone)] +pub enum MediaMachine { + NewWithSize(Media), + NewWithoutSize(Media), + // TODO: Since you've download it you probably know it's size + // Adjust accordignly + PlayableWithSize(Media), + PlayableWithoutSize(Media), + InProgress(Media), +} + +impl MediaMachine { + pub fn new( + play: gtk::Button, + download: gtk::Button, + bar: gtk::ProgressBar, + cancel: gtk::Button, + local_size: gtk::Label, + total_size: gtk::Label, + separator: gtk::Label, + prog_separator: gtk::Label, + ) -> Self { + let dl = DownloadPlay::::from(DownloadPlay::::new(play, download)); + let progress = Progress::::new(bar, cancel); + let size = Size::::new(local_size, total_size, separator, prog_separator); + + MediaMachine::NewWithoutSize(Media { dl, progress, size }) } - pub fn determine_state(self, is_active: bool) -> Self { - match (self, is_active) { - (ProgressMachine::Hidden(val), false) => ProgressMachine::Hidden(val.into()), - (ProgressMachine::Hidden(val), true) => ProgressMachine::Shown(val.into()), - (ProgressMachine::Shown(val), false) => ProgressMachine::Hidden(val.into()), - (ProgressMachine::Shown(val), true) => ProgressMachine::Shown(val.into()), + pub fn determine_state(self, is_downloaded: bool, is_active: bool, size: Option<&str>) -> Self { + match (self, is_downloaded, is_active, size) { + (MediaMachine::NewWithSize(val), _, true, Some(s)) => { + let Media { dl, progress, size } = val; + size.set_total_size(s); + + MediaMachine::InProgress(Media { + dl: dl.into(), + progress: progress.into(), + size: size.into(), + }) + } + (MediaMachine::NewWithSize(val), _, true, None) => { + let Media { dl, progress, size } = val; + size.set_total_size("Unkown"); + + MediaMachine::InProgress(Media { + dl: dl.into(), + progress: progress.into(), + size: size.into(), + }) + } + _ => unimplemented!(), } } } From 02de2059db6c8a213eb3672f141d1982d765cd30 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Tue, 13 Feb 2018 05:03:16 +0200 Subject: [PATCH 13/33] EpisodeWidget: Shrink the Size state Machine. --- hammond-gtk/src/widgets/episode_states.rs | 119 ++++++++-------------- 1 file changed, 45 insertions(+), 74 deletions(-) diff --git a/hammond-gtk/src/widgets/episode_states.rs b/hammond-gtk/src/widgets/episode_states.rs index edd6d19..012ed3f 100644 --- a/hammond-gtk/src/widgets/episode_states.rs +++ b/hammond-gtk/src/widgets/episode_states.rs @@ -110,6 +110,7 @@ pub struct Duration { impl Duration { // This needs a better name. + // TODO: make me mut fn set_duration(&self, minutes: i64) { self.duration.set_text(&format!("{} min", minutes)); } @@ -192,82 +193,55 @@ impl DurationMachine { } } -#[derive(Debug, Clone)] -pub struct TotalShown; - -#[derive(Debug, Clone)] -pub struct Unkown; - -#[derive(Debug, Clone)] -pub struct InProgress; - #[derive(Debug, Clone)] pub struct Size { local_size: gtk::Label, - total_size: gtk::Label, separator: gtk::Label, prog_separator: gtk::Label, state: S, } -impl Size { - fn set_total_size(&self, text: &str) { - self.total_size.set_text(text) - } -} - -impl Size { - fn new( - local_size: gtk::Label, - total_size: gtk::Label, - separator: gtk::Label, - prog_separator: gtk::Label, - ) -> Self { +impl Size { + fn new(local_size: gtk::Label, separator: gtk::Label, prog_separator: gtk::Label) -> Self { local_size.hide(); - total_size.hide(); separator.hide(); prog_separator.hide(); Size { local_size, - total_size, separator, prog_separator, - state: Unkown {}, + state: Hidden {}, } } } -impl From> for Size { - fn from(f: Size) -> Self { +impl From> for Size { + fn from(f: Size) -> Self { f.prog_separator.show(); - f.total_size.show(); f.local_size.show(); f.separator.show(); Size { local_size: f.local_size, - total_size: f.total_size, separator: f.separator, prog_separator: f.prog_separator, - state: InProgress {}, + state: Shown {}, } } } -impl From> for Size { - fn from(f: Size) -> Self { - f.prog_separator.show(); - f.total_size.show(); - f.local_size.show(); - f.separator.show(); +impl From> for Size { + fn from(f: Size) -> Self { + f.prog_separator.hide(); + f.local_size.hide(); + f.separator.hide(); Size { local_size: f.local_size, - total_size: f.total_size, separator: f.separator, prog_separator: f.prog_separator, - state: InProgress {}, + state: Hidden {}, } } } @@ -425,21 +399,17 @@ impl From> for Progress { } #[derive(Debug, Clone)] -pub struct Media { +pub struct Media { dl: DownloadPlay, - progress: Progress

, + progress: Progress, size: Size, } #[derive(Debug, Clone)] pub enum MediaMachine { - NewWithSize(Media), - NewWithoutSize(Media), - // TODO: Since you've download it you probably know it's size - // Adjust accordignly - PlayableWithSize(Media), - PlayableWithoutSize(Media), - InProgress(Media), + New(Media