diff --git a/hammond-gtk/src/headerbar.rs b/hammond-gtk/src/headerbar.rs index 45d5997..e6f93c6 100644 --- a/hammond-gtk/src/headerbar.rs +++ b/hammond-gtk/src/headerbar.rs @@ -9,6 +9,24 @@ use utils; use std::sync::{Arc, Mutex}; +// http://gtk-rs.org/tuto/closures +macro_rules! clone { + (@param _) => ( _ ); + (@param $x:ident) => ( $x ); + ($($n:ident),+ => move || $body:expr) => ( + { + $( let $n = $n.clone(); )+ + move || $body + } + ); + ($($n:ident),+ => move |$($p:tt),+| $body:expr) => ( + { + $( let $n = $n.clone(); )+ + move |$(clone!(@param $p),)+| $body + } + ); +} + pub fn get_headerbar(db: &Arc>, stack: >k::Stack) -> gtk::HeaderBar { let builder = include_str!("../gtk/headerbar.ui"); let builder = gtk::Builder::new_from_string(builder); @@ -27,32 +45,27 @@ pub fn get_headerbar(db: &Arc>, stack: >k::Stack) -> g println!("{:?}", url.get_text()); }); - let add_popover_clone = add_popover.clone(); - let db_clone = db.clone(); - let stack_clone = stack.clone(); - - add_button.connect_clicked(move |_| { + add_button.connect_clicked(clone!(db, stack, add_popover => move |_| { let url = new_url.get_text().unwrap_or_default(); - on_add_bttn_clicked(&db_clone, &stack_clone, &url); + on_add_bttn_clicked(&db, &stack, &url); // TODO: lock the button instead of hiding and add notification of feed added. // TODO: map the spinner - add_popover_clone.hide(); - }); + add_popover.hide(); + })); add_popover.hide(); add_toggle_button.set_popover(&add_popover); // TODO: make it a back arrow button, that will hide when appropriate, // and add a StackSwitcher when more views are added. - let stack_clone = stack.clone(); - home_button.connect_clicked(move |_| stack_clone.set_visible_child_name("pd_grid")); + home_button.connect_clicked(clone!(stack => move |_| stack.set_visible_child_name("pd_grid"))); - let stack_clone = stack.clone(); - let db_clone = db.clone(); + let stack = stack.clone(); + let db = db.clone(); // FIXME: There appears to be a memmory leak here. - refresh_button.connect_clicked(move |_| { - utils::refresh_db(&db_clone, &stack_clone); - }); + refresh_button.connect_clicked(clone!(stack, db => move |_| { + utils::refresh_db(&db, &stack); + })); header } diff --git a/hammond-gtk/src/main.rs b/hammond-gtk/src/main.rs index 85d5387..125c80c 100644 --- a/hammond-gtk/src/main.rs +++ b/hammond-gtk/src/main.rs @@ -23,10 +23,11 @@ use std::sync::{Arc, Mutex}; use gtk::prelude::*; use gio::ApplicationExt; -pub mod views; -pub mod widgets; -pub mod headerbar; -pub mod utils; +mod views; +mod widgets; +mod headerbar; + +mod utils; use views::podcasts_view; diff --git a/hammond-gtk/src/utils.rs b/hammond-gtk/src/utils.rs index 6e96e20..08fae34 100644 --- a/hammond-gtk/src/utils.rs +++ b/hammond-gtk/src/utils.rs @@ -17,6 +17,24 @@ use std::sync::mpsc::{channel, Receiver}; use views::podcasts_view; +// http://gtk-rs.org/tuto/closures +macro_rules! clone { + (@param _) => ( _ ); + (@param $x:ident) => ( $x ); + ($($n:ident),+ => move || $body:expr) => ( + { + $( let $n = $n.clone(); )+ + move || $body + } + ); + ($($n:ident),+ => move |$($p:tt),+| $body:expr) => ( + { + $( let $n = $n.clone(); )+ + move |$(clone!(@param $p),)+| $body + } + ); +} + // Create a thread local storage that will store the arguments to be transfered. thread_local!( static GLOBAL: RefCell>, @@ -28,15 +46,14 @@ pub fn refresh_db(db: &Arc>, stack: >k::Stack) { let (sender, receiver) = channel(); // Pass the desired arguments into the Local Thread Storage. - GLOBAL.with(move |global| { - *global.borrow_mut() = Some((db.clone(), stack.clone(), receiver)); - }); + GLOBAL.with(clone!(db, stack => move |global| { + *global.borrow_mut() = Some((db, stack, receiver)); + })); // The implementation of how this is done is probably terrible but it works!. // TODO: add timeout option and error reporting. - let db_clone = db.clone(); - thread::spawn(move || { - let t = hammond_data::index_feed::index_loop(&db_clone, false); + thread::spawn(clone!(db => move || { + let t = hammond_data::index_feed::index_loop(&db, false); if t.is_err() { error!("Error While trying to update the database."); error!("Error msg: {}", t.unwrap_err()); @@ -45,7 +62,7 @@ pub fn refresh_db(db: &Arc>, stack: >k::Stack) { // http://gtk-rs.org/docs/glib/source/fn.idle_add.html glib::idle_add(refresh_podcasts_view); - }); + })); } pub fn refresh_feed(db: &Arc>, stack: >k::Stack, source: &mut Source) { @@ -55,10 +72,9 @@ pub fn refresh_feed(db: &Arc>, stack: >k::Stack, sourc *global.borrow_mut() = Some((db.clone(), stack.clone(), receiver)); }); - let db = db.clone(); let mut source = source.clone(); // TODO: add timeout option and error reporting. - thread::spawn(move || { + thread::spawn(clone!(db => move || { let db_ = db.lock().unwrap(); let foo_ = hammond_data::index_feed::refresh_source(&db_, &mut source, false); drop(db_); @@ -74,7 +90,7 @@ pub fn refresh_feed(db: &Arc>, stack: >k::Stack, sourc sender.send(true).expect("Couldn't send data to channel");; glib::idle_add(refresh_podcasts_view); }; - }); + })); } fn refresh_podcasts_view() -> glib::Continue { diff --git a/hammond-gtk/src/views/podcasts_view.rs b/hammond-gtk/src/views/podcasts_view.rs index 0de97eb..f263f8e 100644 --- a/hammond-gtk/src/views/podcasts_view.rs +++ b/hammond-gtk/src/views/podcasts_view.rs @@ -10,6 +10,24 @@ use std::sync::{Arc, Mutex}; use widgets::podcast::*; +// http://gtk-rs.org/tuto/closures +macro_rules! clone { + (@param _) => ( _ ); + (@param $x:ident) => ( $x ); + ($($n:ident),+ => move || $body:expr) => ( + { + $( let $n = $n.clone(); )+ + move || $body + } + ); + ($($n:ident),+ => move |$($p:tt),+| $body:expr) => ( + { + $( let $n = $n.clone(); )+ + move |$(clone!(@param $p),)+| $body + } + ); +} + // NOT IN USE. // TRYING OUT STORELESS ATM. pub fn populate_podcasts_flowbox( @@ -47,24 +65,21 @@ pub fn populate_podcasts_flowbox( let pixbuf = get_pixbuf_from_path(image_uri.as_ref().map(|s| s.as_str()), &title); let f = create_flowbox_child(&title, pixbuf.clone()); - let stack_clone = stack.clone(); - let db_clone = db.clone(); - - f.connect_activate(move |_| { - let old = stack_clone.get_child_by_name("pdw").unwrap(); + f.connect_activate(clone!(stack, db => move |_| { + let old = stack.get_child_by_name("pdw").unwrap(); let pdw = podcast_widget( - &db_clone, + &db, Some(title.as_str()), description.as_ref().map(|x| x.as_str()), pixbuf.clone(), ); - stack_clone.remove(&old); - stack_clone.add_named(&pdw, "pdw"); - stack_clone.set_visible_child(&pdw); + stack.remove(&old); + stack.add_named(&pdw, "pdw"); + stack.set_visible_child(&pdw); old.destroy(); println!("Hello World!, child activated"); - }); + })); flowbox.add(&f); if !pd_model.iter_next(&iter) { diff --git a/hammond-gtk/src/widgets/episode.rs b/hammond-gtk/src/widgets/episode.rs index 476f7c5..7ad685b 100644 --- a/hammond-gtk/src/widgets/episode.rs +++ b/hammond-gtk/src/widgets/episode.rs @@ -19,6 +19,26 @@ use gtk; use gtk::prelude::*; use gtk::ContainerExt; +// http://gtk-rs.org/tuto/closures +// FIXME: Atm this macro is copied into every module. +// Figure out how to propely define once and export it instead. +macro_rules! clone { + (@param _) => ( _ ); + (@param $x:ident) => ( $x ); + ($($n:ident),+ => move || $body:expr) => ( + { + $( let $n = $n.clone(); )+ + move || $body + } + ); + ($($n:ident),+ => move |$($p:tt),+| $body:expr) => ( + { + $( let $n = $n.clone(); )+ + move |$(clone!(@param $p),)+| $body + } + ); +} + thread_local!( static GLOBAL: RefCell>, + db: &Arc>, episode: &mut Episode, pd_title: &str, ) -> gtk::Box { @@ -65,27 +85,20 @@ fn epidose_widget( play_button.show(); } - let ep_clone = episode.clone(); - let db = connection.clone(); - play_button.connect_clicked(move |_| { - on_play_bttn_clicked(&db, ep_clone.id()); - }); + play_button.connect_clicked(clone!(episode, db => move |_| { + on_play_bttn_clicked(&db, episode.id()); + })); - // TODO: figure out how to use the gtk-clone macro, - // to make it less tedious. - let pd_title_clone = pd_title.to_owned(); - let db = connection.clone(); - let ep_clone = episode.clone(); - let play_button_clone = play_button.clone(); - dl_button.connect_clicked(move |dl| { + let pd_title = pd_title.to_owned(); + dl_button.connect_clicked(clone!(db, play_button, episode => move |dl| { on_dl_clicked( &db, - &pd_title_clone, - &mut ep_clone.clone(), + &pd_title, + &mut episode.clone(), dl, - &play_button_clone, + &play_button, ); - }); + })); ep } @@ -102,14 +115,13 @@ fn on_dl_clicked( let (sender, receiver) = channel(); // Pass the desired arguments into the Local Thread Storage. - GLOBAL.with(move |global| { - *global.borrow_mut() = Some((dl_bttn.clone(), play_bttn.clone(), receiver)); - }); + GLOBAL.with(clone!(dl_bttn, play_bttn => move |global| { + *global.borrow_mut() = Some((dl_bttn, play_bttn, receiver)); + })); let pd_title = pd_title.to_owned(); let mut ep = ep.clone(); - let db = db.clone(); - thread::spawn(move || { + thread::spawn(clone!(db => move || { let dl_fold = downloader::get_dl_folder(&pd_title).unwrap(); let e = downloader::get_episode(&db, &mut ep, dl_fold.as_str()); if let Err(err) = e { @@ -118,7 +130,7 @@ fn on_dl_clicked( }; sender.send(true).expect("Couldn't send data to channel");; glib::idle_add(receive); - }); + })); } fn on_play_bttn_clicked(db: &Arc>, episode_id: i32) { diff --git a/hammond-gtk/src/widgets/podcast.rs b/hammond-gtk/src/widgets/podcast.rs index 07a31ff..97bd006 100644 --- a/hammond-gtk/src/widgets/podcast.rs +++ b/hammond-gtk/src/widgets/podcast.rs @@ -126,10 +126,10 @@ pub fn podcast_liststore(connection: &SqliteConnection) -> gtk::ListStore { // stack.set_visible_child_name(&vis); // } -pub fn pd_widget_from_diesel_model(db: &Arc>, pd: &Podcast) -> gtk::Box { - let img = get_pixbuf_from_path(pd.image_uri(), pd.title()); - podcast_widget(db, Some(pd.title()), Some(pd.description()), img) -} +// pub fn pd_widget_from_diesel_model(db: &Arc>, pd: &Podcast) -> gtk::Box { +// let img = get_pixbuf_from_path(pd.image_uri(), pd.title()); +// podcast_widget(db, Some(pd.title()), Some(pd.description()), img) +// } pub fn get_pixbuf_from_path(img_path: Option<&str>, pd_title: &str) -> Option { let img_path = downloader::cache_image(pd_title, img_path);