h-gtk: Refactor part of the ShowStack.

This commit is contained in:
Jordan Petridis 2018-04-21 07:40:42 +03:00
parent 173d2d3a3a
commit 6406c3af13
No known key found for this signature in database
GPG Key ID: CEABAD9F5683B9A6
8 changed files with 163 additions and 197 deletions

View File

@ -24,8 +24,6 @@ pub enum Action {
RefreshEpisodesView, RefreshEpisodesView,
RefreshEpisodesViewBGR, RefreshEpisodesViewBGR,
RefreshShowsView, RefreshShowsView,
RefreshWidget,
RefreshWidgetIfVis,
ReplaceWidget(Arc<Podcast>), ReplaceWidget(Arc<Podcast>),
RefreshWidgetIfSame(i32), RefreshWidgetIfSame(i32),
ShowWidgetAnimated, ShowWidgetAnimated,
@ -36,7 +34,6 @@ pub enum Action {
HeaderBarHideUpdateIndicator, HeaderBarHideUpdateIndicator,
MarkAllPlayerNotification(Arc<Podcast>), MarkAllPlayerNotification(Arc<Podcast>),
RemoveShow(Arc<Podcast>), RemoveShow(Arc<Podcast>),
SetShowWidgetAlignment(Arc<Podcast>),
} }
#[derive(Debug)] #[derive(Debug)]
@ -170,21 +167,26 @@ impl App {
match receiver.try_recv() { match receiver.try_recv() {
Ok(Action::RefreshAllViews) => content.update(), Ok(Action::RefreshAllViews) => content.update(),
Ok(Action::RefreshShowsView) => content.update_shows_view(), 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::RefreshWidgetIfSame(id)) => content.update_widget_if_same(id),
Ok(Action::RefreshEpisodesView) => content.update_episode_view(), Ok(Action::RefreshEpisodesView) => content.update_episode_view(),
Ok(Action::RefreshEpisodesViewBGR) => content.update_episode_view_if_baground(), Ok(Action::RefreshEpisodesViewBGR) => content.update_episode_view_if_baground(),
Ok(Action::ReplaceWidget(pd)) => { Ok(Action::ReplaceWidget(pd)) => {
content let mut shows = content.get_shows();
.get_shows() shows
.borrow_mut()
.replace_widget(pd.clone()) .replace_widget(pd.clone())
.map_err(|err| error!("Failed to update ShowWidget: {}", err)) .map_err(|err| error!("Failed to update ShowWidget: {}", err))
.map_err(|_| error!("Failed ot update ShowWidget {}", pd.title())) .map_err(|_| error!("Failed ot update ShowWidget {}", pd.title()))
.ok(); .ok();
} }
Ok(Action::ShowWidgetAnimated) => content.get_shows().switch_widget_animated(), Ok(Action::ShowWidgetAnimated) => {
Ok(Action::ShowShowsAnimated) => content.get_shows().switch_podcasts_animated(), 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::HeaderBarShowTile(title)) => headerbar.switch_to_back(&title),
Ok(Action::HeaderBarNormal) => headerbar.switch_to_normal(), Ok(Action::HeaderBarNormal) => headerbar.switch_to_normal(),
Ok(Action::HeaderBarShowUpdateIndicator) => headerbar.show_update_notification(), Ok(Action::HeaderBarShowUpdateIndicator) => headerbar.show_update_notification(),
@ -197,13 +199,6 @@ impl App {
let notif = remove_show_notif(pd, sender.clone()); let notif = remove_show_notif(pd, sender.clone());
notif.show(&overlay); 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(_) => (), Err(_) => (),
} }

View File

@ -67,18 +67,18 @@ macro_rules! clone {
// They do not need to be public // They do not need to be public
// But it helps when looking at the generated docs. // But it helps when looking at the generated docs.
pub mod stacks; mod stacks;
pub mod views; mod views;
pub mod widgets; mod widgets;
pub mod app; mod app;
pub mod headerbar; mod headerbar;
pub mod appnotif; mod appnotif;
pub mod manager; mod manager;
pub mod settings; mod settings;
pub mod static_resource; mod static_resource;
pub mod utils; mod utils;
use app::App; use app::App;

View File

@ -14,7 +14,7 @@ use std::sync::mpsc::Sender;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Content { pub struct Content {
stack: gtk::Stack, stack: gtk::Stack,
shows: Rc<ShowStack>, shows: Rc<RefCell<ShowStack>>,
episodes: Rc<RefCell<EpisodeStack>>, episodes: Rc<RefCell<EpisodeStack>>,
sender: Sender<Action>, sender: Sender<Action>,
} }
@ -23,10 +23,10 @@ impl Content {
pub fn new(sender: Sender<Action>) -> Result<Content, Error> { pub fn new(sender: Sender<Action>) -> Result<Content, Error> {
let stack = gtk::Stack::new(); let stack = gtk::Stack::new();
let episodes = Rc::new(RefCell::new(EpisodeStack::new(sender.clone())?)); 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(&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 { Ok(Content {
stack, stack,
@ -59,6 +59,7 @@ impl Content {
pub fn update_shows_view(&self) { pub fn update_shows_view(&self) {
self.shows self.shows
.borrow_mut()
.update_podcasts() .update_podcasts()
.map_err(|err| error!("Failed to update ShowsView: {}", err)) .map_err(|err| error!("Failed to update ShowsView: {}", err))
.ok(); .ok();
@ -66,6 +67,7 @@ impl Content {
pub fn update_widget(&self) { pub fn update_widget(&self) {
self.shows self.shows
.borrow_mut()
.update_widget() .update_widget()
.map_err(|err| error!("Failed to update ShowsWidget: {}", err)) .map_err(|err| error!("Failed to update ShowsWidget: {}", err))
.ok(); .ok();
@ -73,24 +75,17 @@ impl Content {
pub fn update_widget_if_same(&self, pid: i32) { pub fn update_widget_if_same(&self, pid: i32) {
self.shows self.shows
.borrow_mut()
.update_widget_if_same(pid) .update_widget_if_same(pid)
.map_err(|err| error!("Failed to update ShowsWidget: {}", err)) .map_err(|err| error!("Failed to update ShowsWidget: {}", err))
.ok(); .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 { pub fn get_stack(&self) -> gtk::Stack {
self.stack.clone() self.stack.clone()
} }
pub fn get_shows(&self) -> Rc<ShowStack> { pub fn get_shows(&self) -> Rc<RefCell<ShowStack>> {
self.shows.clone() self.shows.clone()
} }
} }

View File

@ -51,11 +51,11 @@ impl EpisodeStack {
fn replace_view(&mut self) -> Result<(), Error> { fn replace_view(&mut self) -> Result<(), Error> {
// Get the container of the view // 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())?; let eps = EpisodesView::new(self.sender.clone())?;
// Remove the old widget and add the new one // Remove the old widget and add the new one
self.stack.remove(&old); self.stack.remove(old);
self.stack.add_named(&eps.container, "episodes"); self.stack.add_named(&eps.container, "episodes");
// replace view in the struct too // replace view in the struct too

View File

@ -4,4 +4,4 @@ mod show;
pub use self::content::Content; pub use self::content::Content;
pub use self::episode::EpisodeStack; pub use self::episode::EpisodeStack;
pub use self::show::{ShowStack, SHOW_WIDGET_VALIGNMENT}; pub use self::show::ShowStack;

View File

@ -3,7 +3,6 @@ use gtk::prelude::*;
use gtk::Cast; use gtk::Cast;
use failure::Error; use failure::Error;
use send_cell::SendCell;
use hammond_data::dbqueries; use hammond_data::dbqueries;
use hammond_data::errors::DataError; use hammond_data::errors::DataError;
@ -14,17 +13,16 @@ use views::{EmptyView, ShowsPopulated};
use app::Action; use app::Action;
use widgets::ShowWidget; use widgets::ShowWidget;
use std::rc::Rc;
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use std::sync::{Arc, Mutex}; use std::sync::Arc;
lazy_static! {
pub static ref SHOW_WIDGET_VALIGNMENT: Mutex<Option<(i32, SendCell<gtk::Adjustment>)>> =
Mutex::new(None);
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ShowStack { pub struct ShowStack {
stack: gtk::Stack, stack: gtk::Stack,
podcasts: ShowsPopulated,
show: Rc<ShowWidget>,
empty: EmptyView,
sender: Sender<Action>, sender: Sender<Action>,
} }
@ -32,19 +30,22 @@ impl ShowStack {
pub fn new(sender: Sender<Action>) -> Result<ShowStack, Error> { pub fn new(sender: Sender<Action>) -> Result<ShowStack, Error> {
let stack = gtk::Stack::new(); let stack = gtk::Stack::new();
let show = ShowStack { let podcasts = ShowsPopulated::new(sender.clone())?;
stack, let show = Rc::new(ShowWidget::default());
sender: sender.clone(),
};
let pop = ShowsPopulated::new(sender.clone())?;
let widget = ShowWidget::default();
let empty = EmptyView::new(); let empty = EmptyView::new();
show.stack.add_named(&pop.container, "podcasts"); stack.add_named(&podcasts.container, "podcasts");
show.stack.add_named(&widget.container, "widget"); stack.add_named(&show.container, "widget");
show.stack.add_named(&empty.container, "empty"); stack.add_named(&empty.container, "empty");
set_stack_visible(&show.get_stack())?; set_stack_visible(&stack)?;
let show = ShowStack {
stack,
podcasts,
show,
empty,
sender,
};
Ok(show) Ok(show)
} }
@ -87,19 +88,15 @@ impl ShowStack {
Ok(()) Ok(())
} }
pub fn replace_widget(&self, pd: Arc<Podcast>) -> Result<(), Error> { pub fn replace_widget(&mut self, pd: Arc<Podcast>) -> Result<(), Error> {
let old = self.stack let old = self.show.container.clone();
.get_child_by_name("widget")
.ok_or_else(|| format_err!("Faild to get \"widget\" child from the stack."))?
.downcast::<gtk::Box>()
.map_err(|_| format_err!("Failed to downcast stack child to a Box."))?;
let oldname = WidgetExt::get_name(&old); let oldname = WidgetExt::get_name(&old);
debug!("Name: {:?}", oldname); debug!("Name: {:?}", oldname);
oldname oldname
.clone() .clone()
.and_then(|id| id.parse().ok()) .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()); let new = ShowWidget::new(pd, self.sender.clone());
// Each composite ShowWidget is a gtkBox with the Podcast.id encoded in the // Each composite ShowWidget is a gtkBox with the Podcast.id encoded in the
@ -111,19 +108,19 @@ impl ShowStack {
WidgetExt::get_name(&new.container) WidgetExt::get_name(&new.container)
); );
let root = new.container.clone();
self.show = new;
self.stack.remove(&old); self.stack.remove(&old);
self.stack.add_named(&new.container, "widget"); self.stack.add_named(&root, "widget");
Ok(()) Ok(())
} }
pub fn update_widget(&self) -> Result<(), Error> { pub fn update_widget(&mut self) -> Result<(), Error> {
let vis = self.stack let vis = self.stack
.get_visible_child_name() .get_visible_child_name()
.ok_or_else(|| format_err!("Failed to 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); let id = WidgetExt::get_name(&old);
if id == Some("GtkBox".to_string()) || id.is_none() { if id == Some("GtkBox".to_string()) || id.is_none() {
return Ok(()); return Ok(());
@ -138,12 +135,10 @@ impl ShowStack {
} }
// Only update widget if it's podcast_id is equal to pid. // Only update widget if it's podcast_id is equal to pid.
pub fn update_widget_if_same(&self, pid: i32) -> Result<(), Error> { pub fn update_widget_if_same(&mut self, pid: i32) -> Result<(), Error> {
let old = self.stack let old = &self.show.container.clone();
.get_child_by_name("widget")
.ok_or_else(|| format_err!("Faild to get \"widget\" child from the stack."))?;
let id = WidgetExt::get_name(&old); let id = WidgetExt::get_name(old);
if id != Some(pid.to_string()) || id.is_none() { if id != Some(pid.to_string()) || id.is_none() {
debug!("Different widget. Early return"); debug!("Different widget. Early return");
return Ok(()); return Ok(());
@ -151,43 +146,6 @@ impl ShowStack {
self.update_widget() self.update_widget()
} }
pub fn set_widget_scroll_alignment(&self, pd: Arc<Podcast>) -> 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::<gtk::Box>()
.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::<gtk::ScrolledWindow>()
.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) { pub fn switch_podcasts_animated(&self) {
self.stack self.stack
.set_visible_child_full("podcasts", gtk::StackTransitionType::SlideRight); .set_visible_child_full("podcasts", gtk::StackTransitionType::SlideRight);
@ -213,24 +171,3 @@ fn set_stack_visible(stack: &gtk::Stack) -> Result<(), DataError> {
Ok(()) Ok(())
} }
// ATTENTION: EXPECTS THE SHOW WIDGET CONTAINER
fn save_alignment(oldid: i32, widget: &gtk::Box) -> Result<(), Error> {
let scrolled_window = widget
.get_children()
.first()
.ok_or_else(|| format_err!("Box container has no childs."))?
.clone()
.downcast::<gtk::ScrolledWindow>()
.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(())
}

View File

@ -67,12 +67,6 @@ impl ShowsPopulated {
Ok(()) Ok(())
} }
#[inline]
/// Set scrolled window vertical adjustment.
pub fn set_vadjustment(&self, vadjustment: &gtk::Adjustment) {
self.scrolled_window.set_vadjustment(vadjustment)
}
} }
#[inline] #[inline]

View File

@ -6,6 +6,7 @@ use failure::Error;
use html2pango::markup_from_raw; use html2pango::markup_from_raw;
use open; use open;
use rayon; use rayon;
use send_cell::SendCell;
use hammond_data::dbqueries; use hammond_data::dbqueries;
use hammond_data::utils::delete_show; use hammond_data::utils::delete_show;
@ -16,8 +17,14 @@ use appnotif::InAppNotification;
use utils::{self, lazy_load}; use utils::{self, lazy_load};
use widgets::EpisodeWidget; use widgets::EpisodeWidget;
use std::rc::Rc;
use std::sync::mpsc::{SendError, Sender}; use std::sync::mpsc::{SendError, Sender};
use std::sync::Arc; use std::sync::{Arc, Mutex};
lazy_static! {
static ref SHOW_WIDGET_VALIGNMENT: Mutex<Option<(i32, SendCell<gtk::Adjustment>)>> =
Mutex::new(None);
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ShowWidget { pub struct ShowWidget {
@ -60,9 +67,12 @@ impl Default for ShowWidget {
impl ShowWidget { impl ShowWidget {
#[inline] #[inline]
pub fn new(pd: Arc<Podcast>, sender: Sender<Action>) -> ShowWidget { pub fn new(pd: Arc<Podcast>, sender: Sender<Action>) -> Rc<ShowWidget> {
let pdw = ShowWidget::default(); let pdw = Rc::new(ShowWidget::default());
pdw.init(pd, sender); pdw.init(pd.clone(), sender.clone());
populate_listbox(&pdw, pd, sender)
.map_err(|err| error!("Failed to populate the listbox: {}", err))
.ok();
pdw pdw
} }
@ -80,10 +90,6 @@ impl ShowWidget {
self.set_description(pd.description()); 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()) self.set_cover(pd.clone())
.map_err(|err| error!("Failed to set a cover: {}", err)) .map_err(|err| error!("Failed to set a cover: {}", err))
.ok(); .ok();
@ -125,59 +131,98 @@ impl ShowWidget {
} }
#[inline] #[inline]
/// Set scrolled window vertical adjustment. /// Save the scrollabar vajustment to the cache.
pub fn set_vadjustment(&self, vadjustment: &gtk::Adjustment) { pub fn save_vadjustment(&self, oldid: i32) -> Result<(), Error> {
self.scrolled_window.set_vadjustment(vadjustment) if let Ok(mut guard) = SHOW_WIDGET_VALIGNMENT.lock() {
} let adj = self.scrolled_window
.get_vadjustment()
#[inline] .ok_or_else(|| format_err!("Could not get the adjustment"))?;
/// Populate the listbox with the shows episodes. *guard = Some((oldid, SendCell::new(adj)));
fn populate_listbox(&self, pd: Arc<Podcast>, sender: Sender<Action>) -> Result<(), Error> { debug!("Widget Alignment was saved with ID: {}.", oldid);
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(());
} }
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(()) Ok(())
} }
#[inline]
/// Set scrolled window vertical adjustment.
fn set_vadjustment(&self, pd: Arc<Podcast>) -> 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<ShowWidget>,
pd: Arc<Podcast>,
sender: Sender<Action>,
) -> 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] #[inline]