h-gtk: Restructure the stacks structure.

This commit reimplements support for the empty view in the ShowStack.
The current structure is the following:
* A Content stack which holds the HomeStack and the ShowStack.
  It's what is used in the headerbar StackSwitcher.

* The HomeStack is composed of the HomeView and an EmptyView.

* The ShowStack is composed of the PopulatedStack and an EmptyView.

* The PopulatedStack is composed of the ShowsView and the ShowWidget
  currently. An AboutEpisode widget is planned to be added here also.
This commit is contained in:
Jordan Petridis 2018-04-25 19:23:02 +03:00
parent 734f85a517
commit 3d160fc35c
No known key found for this signature in database
GPG Key ID: CEABAD9F5683B9A6
5 changed files with 228 additions and 127 deletions

View File

@ -10,7 +10,7 @@ use hammond_data::Podcast;
use headerbar::Header; use headerbar::Header;
use settings::{self, WindowGeometry}; use settings::{self, WindowGeometry};
use stacks::{Content, ShowState}; use stacks::{Content, PopulatedState};
use utils; use utils;
use widgets::{mark_all_notif, remove_show_notif}; use widgets::{mark_all_notif, remove_show_notif};
@ -171,21 +171,23 @@ impl App {
Ok(Action::RefreshEpisodesView) => content.update_home(), Ok(Action::RefreshEpisodesView) => content.update_home(),
Ok(Action::RefreshEpisodesViewBGR) => content.update_home_if_background(), Ok(Action::RefreshEpisodesViewBGR) => content.update_home_if_background(),
Ok(Action::ReplaceWidget(pd)) => { Ok(Action::ReplaceWidget(pd)) => {
let mut shows = content.get_shows(); let shows = content.get_shows();
shows let mut pop = shows.borrow().populated();
.borrow_mut() pop.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) => { Ok(Action::ShowWidgetAnimated) => {
let mut shows = content.get_shows(); let shows = content.get_shows();
shows.borrow_mut().switch_visible(ShowState::ShowWidget); let mut pop = shows.borrow().populated();
pop.borrow_mut().switch_visible(PopulatedState::ShowWidget);
} }
Ok(Action::ShowShowsAnimated) => { Ok(Action::ShowShowsAnimated) => {
let mut shows = content.get_shows(); let shows = content.get_shows();
shows.borrow_mut().switch_visible(ShowState::ShowsView); let mut pop = shows.borrow().populated();
pop.borrow_mut().switch_visible(PopulatedState::ShowsView);
} }
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(),

View File

@ -37,15 +37,14 @@ impl Content {
pub fn update(&self) { pub fn update(&self) {
self.update_home(); self.update_home();
self.update_shows_view(); self.update_shows();
self.update_widget()
} }
pub fn update_home(&self) { pub fn update_home(&self) {
self.home self.home
.borrow_mut() .borrow_mut()
.update() .update()
.map_err(|err| error!("Failed to update EpisodeView: {}", err)) .map_err(|err| error!("Failed to update HomeView: {}", err))
.ok(); .ok();
} }
@ -55,25 +54,25 @@ impl Content {
} }
} }
pub fn update_shows_view(&self) { fn update_shows(&self) {
self.shows self.shows
.borrow_mut() .borrow_mut()
.update()
.map_err(|err| error!("Failed to update ShowsView: {}", err))
.ok();
}
pub fn update_shows_view(&self) {
let pop = self.shows.borrow().populated();
pop.borrow_mut()
.update_shows() .update_shows()
.map_err(|err| error!("Failed to update ShowsView: {}", err)) .map_err(|err| error!("Failed to update ShowsView: {}", err))
.ok(); .ok();
} }
pub fn update_widget(&self) {
self.shows
.borrow_mut()
.update_widget()
.map_err(|err| error!("Failed to update ShowsWidget: {}", err))
.ok();
}
pub fn update_widget_if_same(&self, pid: i32) { pub fn update_widget_if_same(&self, pid: i32) {
self.shows let pop = self.shows.borrow().populated();
.borrow_mut() pop.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();

View File

@ -1,7 +1,9 @@
mod content; mod content;
mod home; mod home;
mod populated;
mod show; mod show;
pub use self::content::Content; pub use self::content::Content;
pub use self::home::HomeStack; pub use self::home::HomeStack;
pub use self::populated::{PopulatedStack, PopulatedState};
pub use self::show::{ShowStack, ShowState}; pub use self::show::{ShowStack, ShowState};

View File

@ -0,0 +1,154 @@
use gtk;
use gtk::prelude::*;
use failure::Error;
use hammond_data::dbqueries;
use hammond_data::Podcast;
use app::Action;
use widgets::{ShowWidget, ShowsPopulated};
use std::rc::Rc;
use std::sync::mpsc::Sender;
use std::sync::Arc;
#[derive(Debug, Clone)]
pub enum PopulatedState {
ShowsView,
ShowWidget,
}
#[derive(Debug, Clone)]
pub struct PopulatedStack {
container: gtk::Box,
populated: Rc<ShowsPopulated>,
show: Rc<ShowWidget>,
stack: gtk::Stack,
state: PopulatedState,
sender: Sender<Action>,
}
impl PopulatedStack {
pub fn new(sender: Sender<Action>) -> Result<PopulatedStack, Error> {
let stack = gtk::Stack::new();
let state = PopulatedState::ShowsView;
let populated = ShowsPopulated::new(sender.clone())?;
let show = Rc::new(ShowWidget::default());
let container = gtk::Box::new(gtk::Orientation::Horizontal, 0);
stack.add_named(&populated.container, "shows");
stack.add_named(&show.container, "widget");
container.add(&stack);
container.show_all();
let show = PopulatedStack {
container,
stack,
populated,
show,
state,
sender,
};
Ok(show)
}
pub fn update(&mut self) {
self.update_widget().map_err(|err| format!("{}", err)).ok();
self.update_shows().map_err(|err| format!("{}", err)).ok();
}
pub fn update_shows(&mut self) -> Result<(), Error> {
let old = &self.populated.container.clone();
debug!("Name: {:?}", WidgetExt::get_name(old));
let pop = ShowsPopulated::new(self.sender.clone())?;
self.populated = pop;
self.stack.remove(old);
self.stack.add_named(&self.populated.container, "shows");
// The current visible child might change depending on
// removal and insertion in the gtk::Stack, so we have
// to make sure it will stay the same.
let s = self.state.clone();
self.switch_visible(s);
old.destroy();
Ok(())
}
pub fn replace_widget(&mut self, pd: Arc<Podcast>) -> Result<(), Error> {
let old = self.show.container.clone();
// save the ShowWidget vertical scrollabar alignment
self.show
.podcast_id()
.map(|id| self.show.save_vadjustment(id));
let new = ShowWidget::new(pd, self.sender.clone());
self.show = new;
self.stack.remove(&old);
self.stack.add_named(&self.show.container, "widget");
// The current visible child might change depending on
// removal and insertion in the gtk::Stack, so we have
// to make sure it will stay the same.
let s = self.state.clone();
self.switch_visible(s);
Ok(())
}
pub fn update_widget(&mut self) -> Result<(), Error> {
let old = self.show.container.clone();
let id = self.show.podcast_id();
if id.is_none() {
return Ok(());
}
let pd = dbqueries::get_podcast_from_id(id.unwrap_or_default())?;
self.replace_widget(Arc::new(pd))?;
// The current visible child might change depending on
// removal and insertion in the gtk::Stack, so we have
// to make sure it will stay the same.
let s = self.state.clone();
self.switch_visible(s);
old.destroy();
Ok(())
}
// Only update widget if its podcast_id is equal to pid.
pub fn update_widget_if_same(&mut self, pid: i32) -> Result<(), Error> {
if self.show.podcast_id() != Some(pid) {
debug!("Different widget. Early return");
return Ok(());
}
self.update_widget()
}
pub fn container(&self) -> gtk::Box {
self.container.clone()
}
#[inline]
pub fn switch_visible(&mut self, state: PopulatedState) {
use self::PopulatedState::*;
match state {
ShowsView => {
self.stack
.set_visible_child_full("shows", gtk::StackTransitionType::SlideRight);
self.state = ShowsView;
}
ShowWidget => {
self.stack
.set_visible_child_full("widget", gtk::StackTransitionType::SlideLeft);
self.state = ShowWidget;
}
}
}
}

View File

@ -2,148 +2,92 @@ use gtk;
use gtk::prelude::*; use gtk::prelude::*;
use failure::Error; use failure::Error;
use hammond_data::dbqueries::is_podcasts_populated;
use hammond_data::dbqueries;
use hammond_data::Podcast;
use app::Action; use app::Action;
use widgets::{ShowWidget, ShowsPopulated}; use stacks::PopulatedStack;
use widgets::EmptyView;
use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use std::sync::Arc;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum ShowState { pub enum ShowState {
ShowsView, Populated,
ShowWidget, Empty,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ShowStack { pub struct ShowStack {
populated: Rc<ShowsPopulated>, empty: EmptyView,
show: Rc<ShowWidget>, populated: Rc<RefCell<PopulatedStack>>,
stack: gtk::Stack, stack: gtk::Stack,
state: ShowState, state: ShowState,
sender: Sender<Action>, sender: Sender<Action>,
} }
impl ShowStack { impl ShowStack {
pub fn new(sender: Sender<Action>) -> Result<ShowStack, Error> { pub fn new(sender: Sender<Action>) -> Result<Self, Error> {
let populated = Rc::new(RefCell::new(PopulatedStack::new(sender.clone())?));
let empty = EmptyView::new();
let stack = gtk::Stack::new(); let stack = gtk::Stack::new();
let state = ShowState::ShowsView; let state = ShowState::Empty;
let populated = ShowsPopulated::new(sender.clone())?;
let show = Rc::new(ShowWidget::default());
stack.add_named(&populated.container, "shows"); stack.add_named(&populated.borrow().container(), "populated");
stack.add_named(&show.container, "widget"); stack.add_named(&empty.container, "empty");
let show = ShowStack { let mut show = ShowStack {
stack, empty,
populated, populated,
show, stack,
state, state,
sender, sender,
}; };
show.determine_state()?;
Ok(show) Ok(show)
} }
// pub fn update(&self) {
// self.update_widget();
// self.update_podcasts();
// }
pub fn update_shows(&mut self) -> Result<(), Error> {
let old = &self.populated.container.clone();
debug!("Name: {:?}", WidgetExt::get_name(old));
let pop = ShowsPopulated::new(self.sender.clone())?;
self.populated = pop;
self.stack.remove(old);
self.stack.add_named(&self.populated.container, "shows");
// The current visible child might change depending on
// removal and insertion in the gtk::Stack, so we have
// to make sure it will stay the same.
let s = self.state.clone();
self.switch_visible(s);
old.destroy();
Ok(())
}
pub fn replace_widget(&mut self, pd: Arc<Podcast>) -> Result<(), Error> {
let old = self.show.container.clone();
// save the ShowWidget vertical scrollabar alignment
self.show
.podcast_id()
.map(|id| self.show.save_vadjustment(id));
let new = ShowWidget::new(pd, self.sender.clone());
self.show = new;
self.stack.remove(&old);
self.stack.add_named(&self.show.container, "widget");
// The current visible child might change depending on
// removal and insertion in the gtk::Stack, so we have
// to make sure it will stay the same.
let s = self.state.clone();
self.switch_visible(s);
Ok(())
}
pub fn update_widget(&mut self) -> Result<(), Error> {
let old = self.show.container.clone();
let id = self.show.podcast_id();
if id.is_none() {
return Ok(());
}
let pd = dbqueries::get_podcast_from_id(id.unwrap_or_default())?;
self.replace_widget(Arc::new(pd))?;
// The current visible child might change depending on
// removal and insertion in the gtk::Stack, so we have
// to make sure it will stay the same.
let s = self.state.clone();
self.switch_visible(s);
old.destroy();
Ok(())
}
// Only update widget if its podcast_id is equal to pid.
pub fn update_widget_if_same(&mut self, pid: i32) -> Result<(), Error> {
if self.show.podcast_id() != Some(pid) {
debug!("Different widget. Early return");
return Ok(());
}
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 populated(&self) -> Rc<RefCell<PopulatedStack>> {
self.populated.clone()
}
pub fn update(&mut self) -> Result<(), Error> {
self.populated.borrow_mut().update();
self.determine_state()
}
#[inline] #[inline]
pub fn switch_visible(&mut self, state: ShowState) { fn switch_visible(&mut self, s: ShowState) {
use self::ShowState::*; use self::ShowState::*;
match state { match s {
ShowsView => { Populated => {
self.stack self.stack.set_visible_child_name("populated");
.set_visible_child_full("shows", gtk::StackTransitionType::SlideRight); self.state = Populated;
self.state = ShowsView;
} }
ShowWidget => { Empty => {
self.stack self.stack.set_visible_child_name("empty");
.set_visible_child_full("widget", gtk::StackTransitionType::SlideLeft); self.state = Empty;
self.state = ShowWidget;
} }
};
} }
#[inline]
fn determine_state(&mut self) -> Result<(), Error> {
use self::ShowState::*;
if is_podcasts_populated()? {
self.switch_visible(Populated);
} else {
self.switch_visible(Empty);
};
Ok(())
} }
} }