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<T> 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<Mutex<Machine> could work with
callbacks and embed Nested state machies without copying.
This commit is contained in:
Jordan Petridis 2018-02-10 03:15:12 +02:00
parent f0ce0eb653
commit 3a9a2f4033
No known key found for this signature in database
GPG Key ID: CEABAD9F5683B9A6
5 changed files with 41 additions and 16 deletions

7
Cargo.lock generated
View File

@ -678,6 +678,7 @@ dependencies = [
"open 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "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)", "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)", "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -1610,6 +1611,11 @@ name = "take"
version = "0.1.0" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" 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]] [[package]]
name = "tempdir" name = "tempdir"
version = "0.3.6" 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 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 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 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 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 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" "checksum term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1"

View File

@ -22,6 +22,7 @@ send-cell = "0.1.2"
url = "1.6.0" url = "1.6.0"
failure = "0.1.1" failure = "0.1.1"
failure_derive = "0.1.1" failure_derive = "0.1.1"
take_mut = "0.2.0"
[dependencies.gtk] [dependencies.gtk]
features = ["v3_22"] features = ["v3_22"]

View File

@ -24,6 +24,7 @@ extern crate humansize;
extern crate loggerv; extern crate loggerv;
extern crate open; extern crate open;
extern crate send_cell; extern crate send_cell;
extern crate take_mut;
extern crate url; extern crate url;
// extern crate rayon; // extern crate rayon;

View File

@ -7,6 +7,7 @@ use gtk::prelude::*;
use failure::Error; use failure::Error;
use humansize::{file_size_opts as size_opts, FileSize}; use humansize::{file_size_opts as size_opts, FileSize};
use open; use open;
use take_mut;
use hammond_data::{EpisodeWidgetQuery, Podcast}; use hammond_data::{EpisodeWidgetQuery, Podcast};
use hammond_data::dbqueries; use hammond_data::dbqueries;
@ -16,6 +17,7 @@ use app::Action;
use manager; use manager;
use widgets::episode_states::*; use widgets::episode_states::*;
use std::ops::DerefMut;
use std::path::Path; use std::path::Path;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
@ -46,7 +48,7 @@ pub struct EpisodeWidget {
play: gtk::Button, play: gtk::Button,
download: gtk::Button, download: gtk::Button,
cancel: gtk::Button, cancel: gtk::Button,
title: TitleMachine, title: Arc<Mutex<TitleMachine>>,
date: gtk::Label, date: gtk::Label,
duration: DurationMachine, duration: DurationMachine,
progress: gtk::ProgressBar, progress: gtk::ProgressBar,
@ -77,7 +79,7 @@ impl Default for EpisodeWidget {
let separator2: gtk::Label = builder.get_object("separator2").unwrap(); let separator2: gtk::Label = builder.get_object("separator2").unwrap();
let prog_separator: gtk::Label = builder.get_object("prog_separator").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); let duration_machine = DurationMachine::new(duration, separator1, None);
EpisodeWidget { EpisodeWidget {
@ -106,15 +108,17 @@ impl EpisodeWidget {
fn init(mut self, episode: EpisodeWidgetQuery, sender: Sender<Action>) -> Self { fn init(mut self, episode: EpisodeWidgetQuery, sender: Sender<Action>) -> Self {
WidgetExt::set_name(&self.container, &episode.rowid().to_string()); WidgetExt::set_name(&self.container, &episode.rowid().to_string());
// Set the title label state.
self = self.set_title(&episode);
// Set the duaration label. // Set the duaration label.
self = self.set_duration(episode.duration()); self = self.set_duration(episode.duration());
// Set the date label. // Set the date label.
self.set_date(episode.epoch()); 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. // Show or hide the play/delete/download buttons upon widget initialization.
if let Err(err) = self.show_buttons(episode.local_uri()) { if let Err(err) = self.show_buttons(episode.local_uri()) {
error!("Failed to determine play/download button state."); error!("Failed to determine play/download button state.");
@ -135,10 +139,11 @@ impl EpisodeWidget {
let episode = Arc::new(Mutex::new(episode)); let episode = Arc::new(Mutex::new(episode));
let title = self.title.clone();
self.play self.play
.connect_clicked(clone!(episode, sender => move |_| { .connect_clicked(clone!(episode, sender => move |_| {
if let Ok(mut ep) = episode.lock() { 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); error!("Error: {}", err);
}; };
} }
@ -171,10 +176,13 @@ impl EpisodeWidget {
} }
/// Determine the title state. /// Determine the title state.
fn set_title(mut self, episode: &EpisodeWidgetQuery) -> Self { fn set_title(&mut self, episode: &EpisodeWidgetQuery) -> Result<(), Error> {
self.title.set_title(episode.title()); let mut lock = self.title.lock().map_err(|err| format_err!("{}", err))?;
self.title = self.title.determine_state(episode.played().is_some()); lock.set_title(episode.title());
self take_mut::take(lock.deref_mut(), |title| {
title.determine_state(episode.played().is_some())
});
Ok(())
} }
/// Set the date label depending on the current time. /// Set the date label depending on the current time.
@ -274,13 +282,18 @@ fn on_download_clicked(ep: &EpisodeWidgetQuery, sender: Sender<Action>) -> Resul
fn on_play_bttn_clicked( fn on_play_bttn_clicked(
episode: &mut EpisodeWidgetQuery, episode: &mut EpisodeWidgetQuery,
title: Arc<Mutex<TitleMachine>>,
sender: Sender<Action>, sender: Sender<Action>,
) -> Result<(), Error> { ) -> Result<(), Error> {
open_uri(episode.rowid())?; open_uri(episode.rowid())?;
episode.set_played_now()?; episode.set_played_now()?;
sender.send(Action::RefreshWidgetIfVis)?; let mut lock = title.lock().map_err(|err| format_err!("{}", err))?;
sender.send(Action::RefreshEpisodesView)?; take_mut::take(lock.deref_mut(), |title| {
title.determine_state(episode.played().is_some())
});
sender.send(Action::RefreshEpisodesViewBGR)?;
Ok(()) Ok(())
} }

View File

@ -14,7 +14,10 @@ pub struct Title<S> {
} }
impl<S> Title<S> { impl<S> Title<S> {
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); 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 { match *self {
TitleMachine::Normal(ref val) => val.set_title(s), TitleMachine::Normal(ref mut val) => val.set_title(s),
TitleMachine::GreyedOut(ref val) => val.set_title(s), TitleMachine::GreyedOut(ref mut val) => val.set_title(s),
} }
} }
} }