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:
parent
f0ce0eb653
commit
3a9a2f4033
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -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"
|
||||||
|
|||||||
@ -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"]
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
|
|||||||
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user