From 9cb2782ef93db67dd12e69cb2ab3ccd3aa28d577 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Thu, 5 Apr 2018 20:41:31 +0300 Subject: [PATCH 01/11] ShowWidget: Initial Lazier evaluation of the widgets. --- hammond-data/src/dbqueries.rs | 10 ++++++++ hammond-gtk/src/widgets/episode.rs | 41 +++++++++++++++++++++++++----- hammond-gtk/src/widgets/show.rs | 4 +-- 3 files changed, 46 insertions(+), 9 deletions(-) diff --git a/hammond-data/src/dbqueries.rs b/hammond-data/src/dbqueries.rs index 4923310..3bf982d 100644 --- a/hammond-data/src/dbqueries.rs +++ b/hammond-data/src/dbqueries.rs @@ -175,6 +175,16 @@ pub fn get_pd_episodes(parent: &Podcast) -> Result, DataError> { .map_err(From::from) } +pub fn get_pd_episodes_count(parent: &Podcast) -> Result { + let db = connection(); + let con = db.get()?; + + Episode::belonging_to(parent) + .count() + .get_result(&con) + .map_err(From::from) +} + pub fn get_pd_episodeswidgets(parent: &Podcast) -> Result, DataError> { use schema::episode::dsl::*; let db = connection(); diff --git a/hammond-gtk/src/widgets/episode.rs b/hammond-gtk/src/widgets/episode.rs index 57ea477..f22359d 100644 --- a/hammond-gtk/src/widgets/episode.rs +++ b/hammond-gtk/src/widgets/episode.rs @@ -5,6 +5,7 @@ use gtk::prelude::*; use failure::Error; use humansize::FileSize; use open; +use rayon; use take_mut; use hammond_data::{EpisodeWidgetQuery, Podcast}; @@ -20,7 +21,7 @@ use std::ops::DerefMut; use std::path::Path; use std::rc::Rc; use std::sync::{Arc, Mutex}; -use std::sync::mpsc::Sender; +use std::sync::mpsc::{channel, Sender}; #[derive(Debug)] pub struct EpisodeWidget { @@ -350,23 +351,49 @@ fn total_size_helper( // delete_local_content(&mut ep).map_err(From::from).map(|_| ()) // } -pub fn episodes_listbox(pd: &Podcast, sender: Sender) -> Result { - let episodes = dbqueries::get_pd_episodeswidgets(pd)?; +pub fn episodes_listbox(pd: Arc, sender: Sender) -> Result { + let count = dbqueries::get_pd_episodes_count(&pd)?; + + let (sender_, receiver) = channel(); + rayon::spawn(move || { + let episodes = dbqueries::get_pd_episodeswidgets(&pd).unwrap(); + sender_ + .send(episodes) + .expect("Something terrible happened to the channnel"); + }); let list = gtk::ListBox::new(); list.set_visible(true); list.set_selection_mode(gtk::SelectionMode::None); - if episodes.is_empty() { + if count == 0 { let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/empty_show.ui"); let container: gtk::Box = builder.get_object("empty_show").unwrap(); list.add(&container); return Ok(list); } - episodes.into_iter().for_each(|ep| { - let widget = EpisodeWidget::new(ep, sender.clone()); - list.add(&widget.container); + let widgets: Vec<_> = (0..count) + .into_iter() + .map(|_| { + let widget = EpisodeWidget::default(); + list.add(&widget.container); + widget + }) + .collect(); + + let (s3, r3) = channel(); + s3.send(widgets).unwrap(); + gtk::idle_add(move || { + let episodes = receiver.recv().unwrap(); + let widgets = r3.recv().unwrap(); + episodes + .into_iter() + .zip(widgets) + .for_each(|(ep, mut widget)| widget.init(ep, sender.clone())); + + glib::Continue(false) }); + Ok(list) } diff --git a/hammond-gtk/src/widgets/show.rs b/hammond-gtk/src/widgets/show.rs index bf90607..c02e652 100644 --- a/hammond-gtk/src/widgets/show.rs +++ b/hammond-gtk/src/widgets/show.rs @@ -73,7 +73,7 @@ impl ShowWidget { } })); - self.setup_listbox(&pd, sender.clone()); + self.setup_listbox(pd.clone(), sender.clone()); self.set_description(pd.description()); if let Err(err) = self.set_cover(pd.clone()) { @@ -105,7 +105,7 @@ impl ShowWidget { } /// Populate the listbox with the shows episodes. - fn setup_listbox(&self, pd: &Podcast, sender: Sender) { + fn setup_listbox(&self, pd: Arc, sender: Sender) { let listbox = episodes_listbox(pd, sender.clone()); listbox.ok().map(|l| self.episodes.add(&l)); } From 701b759ba2afe01f6135c92e7ae9a498b3adb710 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Fri, 6 Apr 2018 01:19:45 +0300 Subject: [PATCH 02/11] EpisodesListBox: Add each widget lazyly. --- Cargo.lock | 75 ++++++++++++++++++++++++++++++ hammond-gtk/Cargo.toml | 1 + hammond-gtk/src/main.rs | 1 + hammond-gtk/src/widgets/episode.rs | 42 ++++++++--------- 4 files changed, 98 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index edaa3e4..e940ff0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -260,6 +260,16 @@ dependencies = [ "thread-scoped 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "crossbeam-channel" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-epoch 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "crossbeam-deque" version = "0.2.0" @@ -278,6 +288,19 @@ dependencies = [ "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "crossbeam-epoch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "memoffset 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "crossbeam-epoch" version = "0.3.1" @@ -696,6 +719,7 @@ name = "hammond-gtk" version = "0.1.0" dependencies = [ "chrono 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-channel 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "gdk 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -969,6 +993,11 @@ dependencies = [ "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "memoffset" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "memoffset" version = "0.2.1" @@ -1145,6 +1174,14 @@ dependencies = [ "vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "owning_ref" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "pango" version = "0.4.0" @@ -1170,6 +1207,26 @@ dependencies = [ "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "parking_lot" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parking_lot_core" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "percent-encoding" version = "1.0.1" @@ -1563,6 +1620,16 @@ name = "smallvec" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "smallvec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "stable_deref_trait" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "string_cache" version = "0.7.1" @@ -2051,8 +2118,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum criterion 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4556caa5b5e69626a0b69c4892baa6ab518cf2e802384a5eeef15dd4416d4771" "checksum criterion-plot 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "029914bacd6bb0a521429ff8df0c6c79be1d99f5c4bb85475caabb78f5e06da6" "checksum criterion-stats 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a57a868c589ef2208c0f441e816810e16bfd9cf6a6ea6548f53938b8a530d362" +"checksum crossbeam-channel 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9d7b07a3084d8718d95338443d5a46aab38ce16d5f991d4027a0906b369f70a3" "checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3" "checksum crossbeam-deque 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c1bdc73742c36f7f35ebcda81dbb33a7e0d33757d03a06d9ddca762712ec5ea2" +"checksum crossbeam-epoch 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9898f21d6d647793e163c804944941fb19aecd1f4a1a4c254bbb0bee15ccdea5" "checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150" "checksum crossbeam-epoch 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9b4e2817eb773f770dcb294127c011e22771899c21d18fce7dd739c0b9832e81" "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" @@ -2118,6 +2187,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum markup5ever 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfedc97d5a503e96816d10fedcd5b42f760b2e525ce2f7ec71f6a41780548475" "checksum matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "100aabe6b8ff4e4a7e32c1c13523379802df0772b82466207ac25b013f193376" "checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d" +"checksum memoffset 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e163e5baece1a039e71e75b074de17a9b4114982aa109921fc20253bdf91a53c" "checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" "checksum migrations_internals 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd916de6df9ac7e811e7e1ac28e0abfebe5205f3b29a7bda9ec8a41ee980a4eb" "checksum migrations_macros 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5a550cfd76f6cfdf15a7b541893d7c79b68277b0b309f12179211a373a56e617" @@ -2137,8 +2207,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum open 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c281318d992e4432cfa799969467003d05921582a7489a8325e37f8a450d5113" "checksum openssl 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)" = "a3605c298474a3aa69de92d21139fb5e2a81688d308262359d85cdd0d12a7985" "checksum openssl-sys 0.9.27 (registry+https://github.com/rust-lang/crates.io-index)" = "d6fdc5c4a02e69ce65046f1763a0181107038e02176233acb0b3351d7cc588f9" +"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" "checksum pango 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "45374801e224373c3c0393cd48073c81093494c8735721e81d1dbaa4096b2767" "checksum pango-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94039b3921a4af4058a3e4335e5d15099101f298a92f5afc40bab3a3027594a1" +"checksum parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "149d8f5b97f3c1133e3cfcd8886449959e856b557ff281e292b733d7c69e005e" +"checksum parking_lot_core 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "538ef00b7317875071d5e00f603f24d16f0b474c1a5fc0ccb8b454ca72eafa79" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum pest 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0fce5d8b5cc33983fc74f78ad552b5522ab41442c4ca91606e4236eb4b5ceefc" "checksum pest_derive 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "ab94faafeb93f4c5e3ce81ca0e5a779529a602ad5d09ae6d21996bfb8b6a52bf" @@ -2187,6 +2260,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23" "checksum slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fdeff4cd9ecff59ec7e3744cbca73dfe5ac35c2aedb2cfba8a1c715a18912e9d" "checksum smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c8cbcd6df1e117c2210e13ab5109635ad68a929fcbb8964dc965b76cb5ee013" +"checksum smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44db0ecb22921ef790d17ae13a3f6d15784183ff5f2a01aa32098c7498d2b4b9" +"checksum stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15132e0e364248108c5e2c02e3ab539be8d6f5d52a01ca9bbf27ed657316f02b" "checksum string_cache 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39cb4173bcbd1319da31faa5468a7e3870683d7a237150b0b0aaafd546f6ad12" "checksum string_cache_codegen 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "479cde50c3539481f33906a387f2bd17c8e87cb848c35b6021d41fb81ff9b4d7" "checksum string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1884d1bc09741d466d9b14e6d37ac89d6909cbcac41dd9ae982d4d063bbedfc" diff --git a/hammond-gtk/Cargo.toml b/hammond-gtk/Cargo.toml index 93d8a40..f0ff5c6 100644 --- a/hammond-gtk/Cargo.toml +++ b/hammond-gtk/Cargo.toml @@ -7,6 +7,7 @@ workspace = "../" [dependencies] chrono = "0.4.1" +crossbeam-channel = "0.1.2" gdk = "0.8.0" gdk-pixbuf = "0.4.0" glib = "0.5.0" diff --git a/hammond-gtk/src/main.rs b/hammond-gtk/src/main.rs index 276ae35..f9c3c6c 100644 --- a/hammond-gtk/src/main.rs +++ b/hammond-gtk/src/main.rs @@ -24,6 +24,7 @@ extern crate log; extern crate pretty_assertions; extern crate chrono; +extern crate crossbeam_channel; extern crate hammond_data; extern crate hammond_downloader; extern crate html2pango; diff --git a/hammond-gtk/src/widgets/episode.rs b/hammond-gtk/src/widgets/episode.rs index f22359d..f0ce02e 100644 --- a/hammond-gtk/src/widgets/episode.rs +++ b/hammond-gtk/src/widgets/episode.rs @@ -21,7 +21,7 @@ use std::ops::DerefMut; use std::path::Path; use std::rc::Rc; use std::sync::{Arc, Mutex}; -use std::sync::mpsc::{channel, Sender}; +use std::sync::mpsc::Sender; #[derive(Debug)] pub struct EpisodeWidget { @@ -352,9 +352,12 @@ fn total_size_helper( // } pub fn episodes_listbox(pd: Arc, sender: Sender) -> Result { + // use crossbeam_channel::TryRecvError::*; + use crossbeam_channel::bounded; + let count = dbqueries::get_pd_episodes_count(&pd)?; - let (sender_, receiver) = channel(); + let (sender_, receiver) = bounded(1); rayon::spawn(move || { let episodes = dbqueries::get_pd_episodeswidgets(&pd).unwrap(); sender_ @@ -373,27 +376,24 @@ pub fn episodes_listbox(pd: Arc, sender: Sender) -> Result = (0..count) - .into_iter() - .map(|_| { - let widget = EpisodeWidget::default(); + let episodes = receiver.recv().unwrap(); + // Ok(e) => e, + // Err(Empty) => return glib::Continue(true), + // Err(Disconnected) => return glib::Continue(false), + // }; + + let mut idx = 0; + gtk::idle_add(clone!(list => move || { + if idx >= episodes.len() { return glib::Continue(false) } + + episodes.get(idx).cloned().map(|ep| { + let widget = EpisodeWidget::new(ep, sender.clone()); list.add(&widget.container); - widget - }) - .collect(); + }); - let (s3, r3) = channel(); - s3.send(widgets).unwrap(); - gtk::idle_add(move || { - let episodes = receiver.recv().unwrap(); - let widgets = r3.recv().unwrap(); - episodes - .into_iter() - .zip(widgets) - .for_each(|(ep, mut widget)| widget.init(ep, sender.clone())); - - glib::Continue(false) - }); + idx += 1; + glib::Continue(true) + })); Ok(list) } From cc84a4637d5b4c0afb0891b607baaf2a9fb1f1d0 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Fri, 6 Apr 2018 01:36:43 +0300 Subject: [PATCH 03/11] EpisodesListBox: Do not block while fetching episode backlog. --- hammond-gtk/src/widgets/episode.rs | 34 +++++++++++++++++------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/hammond-gtk/src/widgets/episode.rs b/hammond-gtk/src/widgets/episode.rs index f0ce02e..19694bb 100644 --- a/hammond-gtk/src/widgets/episode.rs +++ b/hammond-gtk/src/widgets/episode.rs @@ -352,7 +352,7 @@ fn total_size_helper( // } pub fn episodes_listbox(pd: Arc, sender: Sender) -> Result { - // use crossbeam_channel::TryRecvError::*; + use crossbeam_channel::TryRecvError::*; use crossbeam_channel::bounded; let count = dbqueries::get_pd_episodes_count(&pd)?; @@ -376,23 +376,27 @@ pub fn episodes_listbox(pd: Arc, sender: Sender) -> Result e, - // Err(Empty) => return glib::Continue(true), - // Err(Disconnected) => return glib::Continue(false), - // }; - - let mut idx = 0; gtk::idle_add(clone!(list => move || { - if idx >= episodes.len() { return glib::Continue(false) } + let episodes = match receiver.try_recv() { + Ok(e) => e, + Err(Empty) => return glib::Continue(true), + Err(Disconnected) => return glib::Continue(false), + }; - episodes.get(idx).cloned().map(|ep| { - let widget = EpisodeWidget::new(ep, sender.clone()); - list.add(&widget.container); - }); + let mut idx = 0; + gtk::idle_add(clone!(list, sender => move || { + if idx >= episodes.len() { return glib::Continue(false) } - idx += 1; - glib::Continue(true) + episodes.get(idx).cloned().map(|ep| { + let widget = EpisodeWidget::new(ep, sender.clone()); + list.add(&widget.container); + }); + + idx += 1; + glib::Continue(true) + })); + + glib::Continue(false) })); Ok(list) From ed806057550badb33df6c0440943320fd6366564 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Fri, 6 Apr 2018 21:30:03 +0300 Subject: [PATCH 04/11] Move the lazy_load logic to a Generic function. --- hammond-gtk/src/widgets/episode.rs | 39 +++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/hammond-gtk/src/widgets/episode.rs b/hammond-gtk/src/widgets/episode.rs index 19694bb..034b2f6 100644 --- a/hammond-gtk/src/widgets/episode.rs +++ b/hammond-gtk/src/widgets/episode.rs @@ -383,17 +383,9 @@ pub fn episodes_listbox(pd: Arc, sender: Sender) -> Result return glib::Continue(false), }; - let mut idx = 0; - gtk::idle_add(clone!(list, sender => move || { - if idx >= episodes.len() { return glib::Continue(false) } - - episodes.get(idx).cloned().map(|ep| { - let widget = EpisodeWidget::new(ep, sender.clone()); - list.add(&widget.container); - }); - - idx += 1; - glib::Continue(true) + lazy_load(episodes, list.clone(), clone!(sender => move |ep| { + let w = EpisodeWidget::new(ep, sender.clone()); + w.container.clone() })); glib::Continue(false) @@ -401,3 +393,28 @@ pub fn episodes_listbox(pd: Arc, sender: Sender) -> Result(data: Vec, container: Z, mut predicate: P) +where + T: Clone + 'static, + Z: ContainerExt + 'static, + P: FnMut(T) -> U + 'static, + U: IsA, +{ + let mut idx = 0; + gtk::idle_add(move || { + if idx >= data.len() { + return glib::Continue(false); + } + + data.get(idx).cloned().map(|x| { + let widget = predicate(x); + container.add(&widget); + }); + + idx += 1; + glib::Continue(true) + }); +} From 43bf8b3f1508c4cec7d50be55e01ee15d5efad37 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Fri, 6 Apr 2018 21:36:02 +0300 Subject: [PATCH 05/11] Lazy_load: Avoid manually indexing. make the data: Vec mutable, then reverse the vector so it can be used as a stack, and then use the ::pop() method to retrieve the item. This also avoid the constrain for Clone on T. --- hammond-gtk/src/widgets/episode.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hammond-gtk/src/widgets/episode.rs b/hammond-gtk/src/widgets/episode.rs index 034b2f6..a464856 100644 --- a/hammond-gtk/src/widgets/episode.rs +++ b/hammond-gtk/src/widgets/episode.rs @@ -396,25 +396,25 @@ pub fn episodes_listbox(pd: Arc, sender: Sender) -> Result(data: Vec, container: Z, mut predicate: P) +fn lazy_load(mut data: Vec, container: Z, mut predicate: P) where - T: Clone + 'static, + T: 'static, Z: ContainerExt + 'static, P: FnMut(T) -> U + 'static, U: IsA, { - let mut idx = 0; + // to use it as a stack + data.reverse(); gtk::idle_add(move || { - if idx >= data.len() { + if data.is_empty() { return glib::Continue(false); } - data.get(idx).cloned().map(|x| { + data.pop().map(|x| { let widget = predicate(x); container.add(&widget); }); - idx += 1; glib::Continue(true) }); } From 9d5fa04d492da0bcea6338a026afa61abd77bcc8 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Fri, 6 Apr 2018 22:26:35 +0300 Subject: [PATCH 06/11] Lazy_load: accept an iterator instead a Vec<_> over T. --- 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 a464856..182845f 100644 --- a/hammond-gtk/src/widgets/episode.rs +++ b/hammond-gtk/src/widgets/episode.rs @@ -383,7 +383,7 @@ pub fn episodes_listbox(pd: Arc, sender: Sender) -> Result return glib::Continue(false), }; - lazy_load(episodes, list.clone(), clone!(sender => move |ep| { + lazy_load(episodes.into_iter(), list.clone(), clone!(sender => move |ep| { let w = EpisodeWidget::new(ep, sender.clone()); w.container.clone() })); @@ -396,25 +396,21 @@ pub fn episodes_listbox(pd: Arc, sender: Sender) -> Result(mut data: Vec, container: Z, mut predicate: P) +fn lazy_load(mut data: T, container: Z, mut predicate: P) where - T: 'static, + T: Iterator + 'static, + T::Item: 'static, Z: ContainerExt + 'static, - P: FnMut(T) -> U + 'static, + P: FnMut(T::Item) -> U + 'static, U: IsA, { - // to use it as a stack - data.reverse(); gtk::idle_add(move || { - if data.is_empty() { - return glib::Continue(false); - } - - data.pop().map(|x| { - let widget = predicate(x); - container.add(&widget); - }); - - glib::Continue(true) + data.next() + .and_then(|x| { + container.add(&predicate(x)); + Some(glib::Continue(true)) + }) + .or(Some(glib::Continue(false))) + .unwrap() }); } From 28d737377992b0c26fec91ead9ad4554257ea2bd Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Fri, 6 Apr 2018 22:56:44 +0300 Subject: [PATCH 07/11] Lazy_load: Use IntoIterator for T, instead of Iterator. --- hammond-gtk/src/widgets/episode.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/hammond-gtk/src/widgets/episode.rs b/hammond-gtk/src/widgets/episode.rs index 182845f..439ee2c 100644 --- a/hammond-gtk/src/widgets/episode.rs +++ b/hammond-gtk/src/widgets/episode.rs @@ -383,7 +383,7 @@ pub fn episodes_listbox(pd: Arc, sender: Sender) -> Result return glib::Continue(false), }; - lazy_load(episodes.into_iter(), list.clone(), clone!(sender => move |ep| { + lazy_load(episodes, list.clone(), clone!(sender => move |ep| { let w = EpisodeWidget::new(ep, sender.clone()); w.container.clone() })); @@ -396,21 +396,19 @@ pub fn episodes_listbox(pd: Arc, sender: Sender) -> Result(mut data: T, container: Z, mut predicate: P) +fn lazy_load(data: T, container: Z, mut predicate: P) where - T: Iterator + 'static, + T: IntoIterator + 'static, T::Item: 'static, Z: ContainerExt + 'static, P: FnMut(T::Item) -> U + 'static, U: IsA, { + let mut data = data.into_iter(); gtk::idle_add(move || { data.next() - .and_then(|x| { - container.add(&predicate(x)); - Some(glib::Continue(true)) - }) - .or(Some(glib::Continue(false))) - .unwrap() + .map(|x| container.add(&predicate(x))) + .map(|_| glib::Continue(true)) + .unwrap_or(glib::Continue(false)) }); } From 5069430a3a7dcebae507be4bdbc640e4c04bc437 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Fri, 6 Apr 2018 23:12:32 +0300 Subject: [PATCH 08/11] Lazy_load: remove unnecessary clone of an Rc pointer. --- hammond-gtk/src/widgets/episode.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hammond-gtk/src/widgets/episode.rs b/hammond-gtk/src/widgets/episode.rs index 439ee2c..22399e5 100644 --- a/hammond-gtk/src/widgets/episode.rs +++ b/hammond-gtk/src/widgets/episode.rs @@ -384,8 +384,7 @@ pub fn episodes_listbox(pd: Arc, sender: Sender) -> Result move |ep| { - let w = EpisodeWidget::new(ep, sender.clone()); - w.container.clone() + EpisodeWidget::new(ep, sender.clone()).container })); glib::Continue(false) From 4b4f5c39d4b7e515ee51cae44f519be5c261ab1f Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Sat, 7 Apr 2018 05:50:34 +0300 Subject: [PATCH 09/11] Lazy_load: improve the naming scheme. --- hammond-gtk/src/widgets/episode.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hammond-gtk/src/widgets/episode.rs b/hammond-gtk/src/widgets/episode.rs index 22399e5..83a47fb 100644 --- a/hammond-gtk/src/widgets/episode.rs +++ b/hammond-gtk/src/widgets/episode.rs @@ -395,18 +395,18 @@ pub fn episodes_listbox(pd: Arc, sender: Sender) -> Result(data: T, container: Z, mut predicate: P) +fn lazy_load(data: T, container: C, mut contructor: F) where T: IntoIterator + 'static, T::Item: 'static, - Z: ContainerExt + 'static, - P: FnMut(T::Item) -> U + 'static, - U: IsA, + C: ContainerExt + 'static, + F: FnMut(T::Item) -> W + 'static, + W: IsA, { let mut data = data.into_iter(); gtk::idle_add(move || { data.next() - .map(|x| container.add(&predicate(x))) + .map(|x| container.add(&contructor(x))) .map(|_| glib::Continue(true)) .unwrap_or(glib::Continue(false)) }); From 29cf5940f5b2970a977e170aa9b38f0344dbfff5 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Sat, 7 Apr 2018 05:53:13 +0300 Subject: [PATCH 10/11] Lazy_load: move to utils module and make it public. --- hammond-gtk/src/utils.rs | 18 ++++++++++++++++++ hammond-gtk/src/widgets/episode.rs | 20 +------------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/hammond-gtk/src/utils.rs b/hammond-gtk/src/utils.rs index 81a9bad..928026e 100644 --- a/hammond-gtk/src/utils.rs +++ b/hammond-gtk/src/utils.rs @@ -4,6 +4,7 @@ use gdk_pixbuf::Pixbuf; use gio::{Settings, SettingsExt}; use glib; use gtk; +use gtk::{IsA, Widget}; use gtk::prelude::*; use failure::Error; @@ -30,6 +31,23 @@ use app::Action; use chrono::Duration; use chrono::prelude::*; +pub fn lazy_load(data: T, container: C, mut contructor: F) +where + T: IntoIterator + 'static, + T::Item: 'static, + C: ContainerExt + 'static, + F: FnMut(T::Item) -> W + 'static, + W: IsA, +{ + let mut data = data.into_iter(); + gtk::idle_add(move || { + data.next() + .map(|x| container.add(&contructor(x))) + .map(|_| glib::Continue(true)) + .unwrap_or(glib::Continue(false)) + }); +} + lazy_static! { static ref IGNORESHOWS: Arc>> = Arc::new(Mutex::new(HashSet::new())); } diff --git a/hammond-gtk/src/widgets/episode.rs b/hammond-gtk/src/widgets/episode.rs index 83a47fb..ec4a595 100644 --- a/hammond-gtk/src/widgets/episode.rs +++ b/hammond-gtk/src/widgets/episode.rs @@ -14,6 +14,7 @@ use hammond_data::utils::get_download_folder; use app::Action; use manager; +use utils::lazy_load; use widgets::episode_states::*; use std::cell::RefCell; @@ -392,22 +393,3 @@ pub fn episodes_listbox(pd: Arc, sender: Sender) -> Result(data: T, container: C, mut contructor: F) -where - T: IntoIterator + 'static, - T::Item: 'static, - C: ContainerExt + 'static, - F: FnMut(T::Item) -> W + 'static, - W: IsA, -{ - let mut data = data.into_iter(); - gtk::idle_add(move || { - data.next() - .map(|x| container.add(&contructor(x))) - .map(|_| glib::Continue(true)) - .unwrap_or(glib::Continue(false)) - }); -} From 572ab86bc4c8fefe13a16649490e57547f57645d Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Sat, 7 Apr 2018 06:47:40 +0300 Subject: [PATCH 11/11] Document utils::lazy_load. --- hammond-gtk/src/utils.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/hammond-gtk/src/utils.rs b/hammond-gtk/src/utils.rs index 928026e..998e49e 100644 --- a/hammond-gtk/src/utils.rs +++ b/hammond-gtk/src/utils.rs @@ -31,6 +31,38 @@ use app::Action; use chrono::Duration; use chrono::prelude::*; +/// Lazy evaluates and loads widgets to the parent `container` widget. +/// +/// Accepts an IntoIterator, `T`, as the source from which each widget +/// will be constructed. An `FnMut` function that returns the desired +/// widget should be passed as the widget `constructor`. +/// +/// ```no_run +/// # struct Message; +/// # struct MessageWidget(gtk::Label); +/// +/// # impl MessageWidget { +/// # fn new(_: Message) -> Self { +/// # MessageWidget(gtk::Label::new("A message")) +/// # } +/// # } +/// +/// let messages: Vec = Vec::new(); +/// let list = gtk::ListBox::new(); +/// let constructor = |m| { MessageWidget::new(m).0}; +/// lazy_load(messages, list, constructor); +/// ``` +/// +/// If you have already constructed the widgets and only want to +/// load them to the parent you can pass a closure that returns it's +/// own argument to the constructor. +/// +/// ```no_run +/// # use std::collections::binary_heap::BinaryHeap; +/// let widgets: BinaryHeap = BinaryHeap::new(); +/// let list = gtk::ListBox::new(); +/// lazy_load(widgets, list, |w| w); +/// ``` pub fn lazy_load(data: T, container: C, mut contructor: F) where T: IntoIterator + 'static,