diff --git a/hammond-gtk/src/app.rs b/hammond-gtk/src/app.rs index dd2ea41..8871313 100644 --- a/hammond-gtk/src/app.rs +++ b/hammond-gtk/src/app.rs @@ -24,8 +24,6 @@ pub enum Action { RefreshEpisodesView, RefreshEpisodesViewBGR, RefreshShowsView, - RefreshWidget, - RefreshWidgetIfVis, ReplaceWidget(Arc), RefreshWidgetIfSame(i32), ShowWidgetAnimated, @@ -36,7 +34,6 @@ pub enum Action { HeaderBarHideUpdateIndicator, MarkAllPlayerNotification(Arc), RemoveShow(Arc), - SetShowWidgetAlignment(Arc), } #[derive(Debug)] @@ -170,21 +167,26 @@ impl App { match receiver.try_recv() { Ok(Action::RefreshAllViews) => content.update(), Ok(Action::RefreshShowsView) => content.update_shows_view(), - Ok(Action::RefreshWidget) => content.update_widget(), - Ok(Action::RefreshWidgetIfVis) => content.update_widget_if_visible(), Ok(Action::RefreshWidgetIfSame(id)) => content.update_widget_if_same(id), Ok(Action::RefreshEpisodesView) => content.update_episode_view(), Ok(Action::RefreshEpisodesViewBGR) => content.update_episode_view_if_baground(), Ok(Action::ReplaceWidget(pd)) => { - content - .get_shows() + let mut shows = content.get_shows(); + shows + .borrow_mut() .replace_widget(pd.clone()) .map_err(|err| error!("Failed to update ShowWidget: {}", err)) .map_err(|_| error!("Failed ot update ShowWidget {}", pd.title())) .ok(); } - Ok(Action::ShowWidgetAnimated) => content.get_shows().switch_widget_animated(), - Ok(Action::ShowShowsAnimated) => content.get_shows().switch_podcasts_animated(), + Ok(Action::ShowWidgetAnimated) => { + let mut shows = content.get_shows(); + shows.borrow().switch_widget_animated(); + } + Ok(Action::ShowShowsAnimated) => { + let mut shows = content.get_shows(); + shows.borrow().switch_podcasts_animated(); + } Ok(Action::HeaderBarShowTile(title)) => headerbar.switch_to_back(&title), Ok(Action::HeaderBarNormal) => headerbar.switch_to_normal(), Ok(Action::HeaderBarShowUpdateIndicator) => headerbar.show_update_notification(), @@ -197,13 +199,6 @@ impl App { let notif = remove_show_notif(pd, sender.clone()); notif.show(&overlay); } - Ok(Action::SetShowWidgetAlignment(pd)) => { - content - .get_shows() - .set_widget_scroll_alignment(pd) - .map_err(|err| error!("Failed to set ShowWidget alignment: {}", err)) - .ok(); - } Err(_) => (), } diff --git a/hammond-gtk/src/main.rs b/hammond-gtk/src/main.rs index aa343e9..9ecefb5 100644 --- a/hammond-gtk/src/main.rs +++ b/hammond-gtk/src/main.rs @@ -67,18 +67,18 @@ macro_rules! clone { // They do not need to be public // But it helps when looking at the generated docs. -pub mod stacks; -pub mod views; -pub mod widgets; +mod stacks; +mod views; +mod widgets; -pub mod app; -pub mod headerbar; +mod app; +mod headerbar; -pub mod appnotif; -pub mod manager; -pub mod settings; -pub mod static_resource; -pub mod utils; +mod appnotif; +mod manager; +mod settings; +mod static_resource; +mod utils; use app::App; diff --git a/hammond-gtk/src/stacks/content.rs b/hammond-gtk/src/stacks/content.rs index 9d3bd4a..7f77772 100644 --- a/hammond-gtk/src/stacks/content.rs +++ b/hammond-gtk/src/stacks/content.rs @@ -14,7 +14,7 @@ use std::sync::mpsc::Sender; #[derive(Debug, Clone)] pub struct Content { stack: gtk::Stack, - shows: Rc, + shows: Rc>, episodes: Rc>, sender: Sender, } @@ -23,10 +23,10 @@ impl Content { pub fn new(sender: Sender) -> Result { let stack = gtk::Stack::new(); let episodes = Rc::new(RefCell::new(EpisodeStack::new(sender.clone())?)); - let shows = Rc::new(ShowStack::new(sender.clone())?); + let shows = Rc::new(RefCell::new(ShowStack::new(sender.clone())?)); stack.add_titled(&episodes.borrow().get_stack(), "episodes", "Episodes"); - stack.add_titled(&shows.get_stack(), "shows", "Shows"); + stack.add_titled(&shows.borrow().get_stack(), "shows", "Shows"); Ok(Content { stack, @@ -59,6 +59,7 @@ impl Content { pub fn update_shows_view(&self) { self.shows + .borrow_mut() .update_podcasts() .map_err(|err| error!("Failed to update ShowsView: {}", err)) .ok(); @@ -66,6 +67,7 @@ impl Content { pub fn update_widget(&self) { self.shows + .borrow_mut() .update_widget() .map_err(|err| error!("Failed to update ShowsWidget: {}", err)) .ok(); @@ -73,24 +75,17 @@ impl Content { pub fn update_widget_if_same(&self, pid: i32) { self.shows + .borrow_mut() .update_widget_if_same(pid) .map_err(|err| error!("Failed to update ShowsWidget: {}", err)) .ok(); } - pub fn update_widget_if_visible(&self) { - if self.stack.get_visible_child_name() == Some("shows".to_string()) - && self.shows.get_stack().get_visible_child_name() == Some("widget".to_string()) - { - self.update_widget(); - } - } - pub fn get_stack(&self) -> gtk::Stack { self.stack.clone() } - pub fn get_shows(&self) -> Rc { + pub fn get_shows(&self) -> Rc> { self.shows.clone() } } diff --git a/hammond-gtk/src/stacks/episode.rs b/hammond-gtk/src/stacks/episode.rs index a25e086..ad014a9 100644 --- a/hammond-gtk/src/stacks/episode.rs +++ b/hammond-gtk/src/stacks/episode.rs @@ -51,11 +51,11 @@ impl EpisodeStack { fn replace_view(&mut self) -> Result<(), Error> { // Get the container of the view - let old = self.episodes.container.clone(); + let old = &self.episodes.container.clone(); let eps = EpisodesView::new(self.sender.clone())?; // Remove the old widget and add the new one - self.stack.remove(&old); + self.stack.remove(old); self.stack.add_named(&eps.container, "episodes"); // replace view in the struct too diff --git a/hammond-gtk/src/stacks/mod.rs b/hammond-gtk/src/stacks/mod.rs index 8c8a843..e038f99 100644 --- a/hammond-gtk/src/stacks/mod.rs +++ b/hammond-gtk/src/stacks/mod.rs @@ -4,4 +4,4 @@ mod show; pub use self::content::Content; pub use self::episode::EpisodeStack; -pub use self::show::{ShowStack, SHOW_WIDGET_VALIGNMENT}; +pub use self::show::ShowStack; diff --git a/hammond-gtk/src/stacks/show.rs b/hammond-gtk/src/stacks/show.rs index c4a35a2..5ba26d2 100644 --- a/hammond-gtk/src/stacks/show.rs +++ b/hammond-gtk/src/stacks/show.rs @@ -3,7 +3,6 @@ use gtk::prelude::*; use gtk::Cast; use failure::Error; -use send_cell::SendCell; use hammond_data::dbqueries; use hammond_data::errors::DataError; @@ -14,17 +13,16 @@ use views::{EmptyView, ShowsPopulated}; use app::Action; use widgets::ShowWidget; +use std::rc::Rc; use std::sync::mpsc::Sender; -use std::sync::{Arc, Mutex}; - -lazy_static! { - pub static ref SHOW_WIDGET_VALIGNMENT: Mutex)>> = - Mutex::new(None); -} +use std::sync::Arc; #[derive(Debug, Clone)] pub struct ShowStack { stack: gtk::Stack, + podcasts: ShowsPopulated, + show: Rc, + empty: EmptyView, sender: Sender, } @@ -32,19 +30,22 @@ impl ShowStack { pub fn new(sender: Sender) -> Result { let stack = gtk::Stack::new(); - let show = ShowStack { - stack, - sender: sender.clone(), - }; - - let pop = ShowsPopulated::new(sender.clone())?; - let widget = ShowWidget::default(); + let podcasts = ShowsPopulated::new(sender.clone())?; + let show = Rc::new(ShowWidget::default()); let empty = EmptyView::new(); - show.stack.add_named(&pop.container, "podcasts"); - show.stack.add_named(&widget.container, "widget"); - show.stack.add_named(&empty.container, "empty"); - set_stack_visible(&show.get_stack())?; + stack.add_named(&podcasts.container, "podcasts"); + stack.add_named(&show.container, "widget"); + stack.add_named(&empty.container, "empty"); + set_stack_visible(&stack)?; + + let show = ShowStack { + stack, + podcasts, + show, + empty, + sender, + }; Ok(show) } @@ -87,19 +88,15 @@ impl ShowStack { Ok(()) } - pub fn replace_widget(&self, pd: Arc) -> Result<(), Error> { - let old = self.stack - .get_child_by_name("widget") - .ok_or_else(|| format_err!("Faild to get \"widget\" child from the stack."))? - .downcast::() - .map_err(|_| format_err!("Failed to downcast stack child to a Box."))?; + pub fn replace_widget(&mut self, pd: Arc) -> Result<(), Error> { + let old = self.show.container.clone(); let oldname = WidgetExt::get_name(&old); debug!("Name: {:?}", oldname); oldname .clone() .and_then(|id| id.parse().ok()) - .map(|id| save_alignment(id, &old)); + .map(|id| self.show.save_vadjustment(id)); let new = ShowWidget::new(pd, self.sender.clone()); // Each composite ShowWidget is a gtkBox with the Podcast.id encoded in the @@ -111,19 +108,19 @@ impl ShowStack { WidgetExt::get_name(&new.container) ); + let root = new.container.clone(); + self.show = new; self.stack.remove(&old); - self.stack.add_named(&new.container, "widget"); + self.stack.add_named(&root, "widget"); Ok(()) } - pub fn update_widget(&self) -> Result<(), Error> { + pub fn update_widget(&mut self) -> Result<(), Error> { let vis = self.stack .get_visible_child_name() .ok_or_else(|| format_err!("Failed to get visible child name."))?; - let old = self.stack - .get_child_by_name("widget") - .ok_or_else(|| format_err!("Faild to get \"widget\" child from the stack."))?; + let old = self.show.container.clone(); let id = WidgetExt::get_name(&old); if id == Some("GtkBox".to_string()) || id.is_none() { return Ok(()); @@ -138,12 +135,10 @@ impl ShowStack { } // Only update widget if it's podcast_id is equal to pid. - pub fn update_widget_if_same(&self, pid: i32) -> Result<(), Error> { - let old = self.stack - .get_child_by_name("widget") - .ok_or_else(|| format_err!("Faild to get \"widget\" child from the stack."))?; + pub fn update_widget_if_same(&mut self, pid: i32) -> Result<(), Error> { + let old = &self.show.container.clone(); - let id = WidgetExt::get_name(&old); + let id = WidgetExt::get_name(old); if id != Some(pid.to_string()) || id.is_none() { debug!("Different widget. Early return"); return Ok(()); @@ -151,43 +146,6 @@ impl ShowStack { self.update_widget() } - pub fn set_widget_scroll_alignment(&self, pd: Arc) -> Result<(), Error> { - let guard = SHOW_WIDGET_VALIGNMENT - .lock() - .map_err(|err| format_err!("Failed to lock widget align mutex: {}", err))?; - - if let Some((oldid, ref sendcell)) = *guard { - // Only copy the old scrollbar if both widget's represent the same podcast. - debug!("PID: {}", pd.id()); - debug!("OLDID: {}", oldid); - if pd.id() != oldid { - debug!("Early return"); - return Ok(()); - }; - - let widget = self.stack - .get_child_by_name("widget") - .ok_or_else(|| format_err!("Faild to get \"widget\" child from the stack."))? - .downcast::() - .map_err(|_| format_err!("Failed to downcast stack child to a Box."))?; - - let scrolled_window = widget - .get_children() - .first() - .ok_or_else(|| format_err!("Box container has no childs."))? - .clone() - .downcast::() - .map_err(|_| format_err!("Failed to downcast stack child to a ScrolledWindow."))?; - - // Copy the vertical scrollbar adjustment from the old view into the new one. - sendcell - .try_get() - .map(|x| scrolled_window.set_vadjustment(&x)); - } - - Ok(()) - } - pub fn switch_podcasts_animated(&self) { self.stack .set_visible_child_full("podcasts", gtk::StackTransitionType::SlideRight); @@ -213,24 +171,3 @@ fn set_stack_visible(stack: >k::Stack) -> Result<(), DataError> { Ok(()) } - -// ATTENTION: EXPECTS THE SHOW WIDGET CONTAINER -fn save_alignment(oldid: i32, widget: >k::Box) -> Result<(), Error> { - let scrolled_window = widget - .get_children() - .first() - .ok_or_else(|| format_err!("Box container has no childs."))? - .clone() - .downcast::() - .map_err(|_| format_err!("Failed to downcast stack child to a ScrolledWindow."))?; - - if let Ok(mut guard) = SHOW_WIDGET_VALIGNMENT.lock() { - let adj = scrolled_window - .get_vadjustment() - .ok_or_else(|| format_err!("Could not get the adjustment"))?; - *guard = Some((oldid, SendCell::new(adj))); - debug!("Widget Alignment was saved with ID: {}.", oldid); - } - - Ok(()) -} diff --git a/hammond-gtk/src/views/shows.rs b/hammond-gtk/src/views/shows.rs index d8b6f32..3467551 100644 --- a/hammond-gtk/src/views/shows.rs +++ b/hammond-gtk/src/views/shows.rs @@ -67,12 +67,6 @@ impl ShowsPopulated { Ok(()) } - - #[inline] - /// Set scrolled window vertical adjustment. - pub fn set_vadjustment(&self, vadjustment: >k::Adjustment) { - self.scrolled_window.set_vadjustment(vadjustment) - } } #[inline] diff --git a/hammond-gtk/src/widgets/show.rs b/hammond-gtk/src/widgets/show.rs index ed91936..4a22c9a 100644 --- a/hammond-gtk/src/widgets/show.rs +++ b/hammond-gtk/src/widgets/show.rs @@ -6,6 +6,7 @@ use failure::Error; use html2pango::markup_from_raw; use open; use rayon; +use send_cell::SendCell; use hammond_data::dbqueries; use hammond_data::utils::delete_show; @@ -16,8 +17,14 @@ use appnotif::InAppNotification; use utils::{self, lazy_load}; use widgets::EpisodeWidget; +use std::rc::Rc; use std::sync::mpsc::{SendError, Sender}; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; + +lazy_static! { + static ref SHOW_WIDGET_VALIGNMENT: Mutex)>> = + Mutex::new(None); +} #[derive(Debug, Clone)] pub struct ShowWidget { @@ -60,9 +67,12 @@ impl Default for ShowWidget { impl ShowWidget { #[inline] - pub fn new(pd: Arc, sender: Sender) -> ShowWidget { - let pdw = ShowWidget::default(); - pdw.init(pd, sender); + pub fn new(pd: Arc, sender: Sender) -> Rc { + let pdw = Rc::new(ShowWidget::default()); + pdw.init(pd.clone(), sender.clone()); + populate_listbox(&pdw, pd, sender) + .map_err(|err| error!("Failed to populate the listbox: {}", err)) + .ok(); pdw } @@ -80,10 +90,6 @@ impl ShowWidget { self.set_description(pd.description()); - self.populate_listbox(pd.clone(), sender.clone()) - .map_err(|err| error!("Failed to populate the listbox: {}", err)) - .ok(); - self.set_cover(pd.clone()) .map_err(|err| error!("Failed to set a cover: {}", err)) .ok(); @@ -125,59 +131,98 @@ impl ShowWidget { } #[inline] - /// Set scrolled window vertical adjustment. - pub fn set_vadjustment(&self, vadjustment: >k::Adjustment) { - self.scrolled_window.set_vadjustment(vadjustment) - } - - #[inline] - /// Populate the listbox with the shows episodes. - fn populate_listbox(&self, pd: Arc, sender: Sender) -> Result<(), Error> { - use crossbeam_channel::bounded; - use crossbeam_channel::TryRecvError::*; - - let count = dbqueries::get_pd_episodes_count(&pd)?; - - let (sender_, receiver) = bounded(1); - rayon::spawn(clone!(pd => move || { - let episodes = dbqueries::get_pd_episodeswidgets(&pd).unwrap(); - // The receiver can be dropped if there's an early return - // like on show without episodes for example. - sender_.send(episodes).ok(); - })); - - 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(); - self.episodes.add(&container); - return Ok(()); + /// Save the scrollabar vajustment to the cache. + pub fn save_vadjustment(&self, oldid: i32) -> Result<(), Error> { + if let Ok(mut guard) = SHOW_WIDGET_VALIGNMENT.lock() { + let adj = self.scrolled_window + .get_vadjustment() + .ok_or_else(|| format_err!("Could not get the adjustment"))?; + *guard = Some((oldid, SendCell::new(adj))); + debug!("Widget Alignment was saved with ID: {}.", oldid); } - let list = self.episodes.clone(); - gtk::idle_add(move || { - let episodes = match receiver.try_recv() { - Ok(e) => e, - Err(Empty) => return glib::Continue(true), - Err(Disconnected) => return glib::Continue(false), - }; - - let constructor = clone!(sender => move |ep| { - EpisodeWidget::new(ep, sender.clone()).container - }); - - let callback = clone!(pd, sender => move || { - sender.send(Action::SetShowWidgetAlignment(pd.clone())) - .map_err(|err| error!("Action Sender: {}", err)) - .ok(); - }); - - lazy_load(episodes, list.clone(), constructor, callback); - - glib::Continue(false) - }); - Ok(()) } + + #[inline] + /// Set scrolled window vertical adjustment. + fn set_vadjustment(&self, pd: Arc) -> Result<(), Error> { + let guard = SHOW_WIDGET_VALIGNMENT + .lock() + .map_err(|err| format_err!("Failed to lock widget align mutex: {}", err))?; + + if let Some((oldid, ref sendcell)) = *guard { + // Only copy the old scrollbar if both widget's represent the same podcast. + debug!("PID: {}", pd.id()); + debug!("OLDID: {}", oldid); + if pd.id() != oldid { + debug!("Early return"); + return Ok(()); + }; + + // Copy the vertical scrollbar adjustment from the old view into the new one. + sendcell + .try_get() + .map(|x| self.scrolled_window.set_vadjustment(&x)); + } + + Ok(()) + } +} + +#[inline] +/// Populate the listbox with the shows episodes. +fn populate_listbox( + show: &Rc, + pd: Arc, + sender: Sender, +) -> Result<(), Error> { + use crossbeam_channel::bounded; + use crossbeam_channel::TryRecvError::*; + + let count = dbqueries::get_pd_episodes_count(&pd)?; + + let (sender_, receiver) = bounded(1); + rayon::spawn(clone!(pd => move || { + let episodes = dbqueries::get_pd_episodeswidgets(&pd).unwrap(); + // The receiver can be dropped if there's an early return + // like on show without episodes for example. + sender_.send(episodes).ok(); + })); + + 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(); + show.episodes.add(&container); + return Ok(()); + } + + let show_ = show.clone(); + gtk::idle_add(move || { + let episodes = match receiver.try_recv() { + Ok(e) => e, + Err(Empty) => return glib::Continue(true), + Err(Disconnected) => return glib::Continue(false), + }; + + let list = show_.episodes.clone(); + + let constructor = clone!(sender => move |ep| { + EpisodeWidget::new(ep, sender.clone()).container + }); + + let callback = clone!(pd, show_ => move || { + show_.set_vadjustment(pd.clone()) + .map_err(|err| error!("Failed to set ShowWidget Alignment: {}", err)) + .ok(); + }); + + lazy_load(episodes, list.clone(), constructor, callback); + + glib::Continue(false) + }); + + Ok(()) } #[inline]