Implemented the recommended clone macro from gtk-rs docs.

This commit is contained in:
Jordan Petridis 2017-10-23 07:09:23 +03:00
parent 91c6a98e1e
commit 539a5eae2f
No known key found for this signature in database
GPG Key ID: CEABAD9F5683B9A6
6 changed files with 123 additions and 66 deletions

View File

@ -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<Mutex<SqliteConnection>>, stack: &gtk::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<Mutex<SqliteConnection>>, stack: &gtk::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
}

View File

@ -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;

View File

@ -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<Option<(Arc<Mutex<SqliteConnection>>,
@ -28,15 +46,14 @@ pub fn refresh_db(db: &Arc<Mutex<SqliteConnection>>, stack: &gtk::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<Mutex<SqliteConnection>>, stack: &gtk::Stack) {
// http://gtk-rs.org/docs/glib/source/fn.idle_add.html
glib::idle_add(refresh_podcasts_view);
});
}));
}
pub fn refresh_feed(db: &Arc<Mutex<SqliteConnection>>, stack: &gtk::Stack, source: &mut Source) {
@ -55,10 +72,9 @@ pub fn refresh_feed(db: &Arc<Mutex<SqliteConnection>>, stack: &gtk::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<Mutex<SqliteConnection>>, stack: &gtk::Stack, sourc
sender.send(true).expect("Couldn't send data to channel");;
glib::idle_add(refresh_podcasts_view);
};
});
}));
}
fn refresh_podcasts_view() -> glib::Continue {

View File

@ -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) {

View File

@ -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<Option<((gtk::Button,
gtk::Button,
@ -26,7 +46,7 @@ thread_local!(
// TODO: REFACTOR AND MODULATE ME.
fn epidose_widget(
connection: &Arc<Mutex<SqliteConnection>>,
db: &Arc<Mutex<SqliteConnection>>,
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<Mutex<SqliteConnection>>, episode_id: i32) {

View File

@ -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<Mutex<SqliteConnection>>, 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<Mutex<SqliteConnection>>, 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<Pixbuf> {
let img_path = downloader::cache_image(pd_title, img_path);