From 9f42e910880a9630ce9464e750725fc368390af6 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Sun, 19 Aug 2018 13:44:11 +0300 Subject: [PATCH 1/3] Refactor content state with Application actions Instead of each view/widget determening if its populated on its own, make add Application Actions and apply the state globally. --- podcasts-gtk/src/app.rs | 4 ++++ podcasts-gtk/src/stacks/content.rs | 24 +++++++++++++++++++++ podcasts-gtk/src/stacks/home.rs | 34 +++++------------------------- podcasts-gtk/src/stacks/mod.rs | 2 +- podcasts-gtk/src/stacks/show.rs | 21 ++++++------------ 5 files changed, 41 insertions(+), 44 deletions(-) diff --git a/podcasts-gtk/src/app.rs b/podcasts-gtk/src/app.rs index d27ca80..0c6623e 100644 --- a/podcasts-gtk/src/app.rs +++ b/podcasts-gtk/src/app.rs @@ -64,6 +64,8 @@ pub(crate) enum Action { ErrorNotification(String), InitEpisode(i32), InitShowMenu(Fragile), + EmptyState, + PopulatedState, } #[derive(Debug, Clone)] @@ -347,6 +349,8 @@ impl App { let menu = &s.get().container; self.headerbar.set_secondary_menu(menu); } + Action::EmptyState => self.content.switch_to_empty_views(), + Action::PopulatedState => self.content.switch_to_populated(), } } diff --git a/podcasts-gtk/src/stacks/content.rs b/podcasts-gtk/src/stacks/content.rs index 506fd32..e83c512 100644 --- a/podcasts-gtk/src/stacks/content.rs +++ b/podcasts-gtk/src/stacks/content.rs @@ -12,6 +12,12 @@ use std::rc::Rc; use i18n::i18n; +#[derive(Debug, Clone, Copy)] +pub(crate) enum State { + Populated, + Empty, +} + #[derive(Debug, Clone)] pub(crate) struct Content { stack: gtk::Stack, @@ -88,4 +94,22 @@ impl Content { pub(crate) fn get_shows(&self) -> Rc> { self.shows.clone() } + + pub(crate) fn switch_to_empty_views(&self) { + use gtk::StackTransitionType::*; + + self.home + .borrow_mut() + .switch_visible(State::Empty, Crossfade); + self.shows.borrow_mut().switch_visible(State::Empty); + } + + pub(crate) fn switch_to_populated(&self) { + use gtk::StackTransitionType::*; + + self.home + .borrow_mut() + .switch_visible(State::Populated, Crossfade); + self.shows.borrow_mut().switch_visible(State::Populated); + } } diff --git a/podcasts-gtk/src/stacks/home.rs b/podcasts-gtk/src/stacks/home.rs index cccad87..c1b7e7f 100644 --- a/podcasts-gtk/src/stacks/home.rs +++ b/podcasts-gtk/src/stacks/home.rs @@ -4,21 +4,14 @@ use gtk::StackTransitionType; use crossbeam_channel::Sender; use failure::Error; -use podcasts_data::dbqueries::is_episodes_populated; -use podcasts_data::errors::DataError; use app::Action; +use stacks::State; use widgets::{EmptyView, HomeView}; use std::ops::Deref; use std::rc::Rc; -#[derive(Debug, Clone, Copy)] -enum State { - Home, - Empty, -} - #[derive(Debug, Clone)] pub(crate) struct HomeStack { empty: EmptyView, @@ -38,7 +31,7 @@ impl HomeStack { stack.add_named(episodes.view.container(), "home"); stack.add_named(empty.deref(), "empty"); - let mut home = HomeStack { + let home = HomeStack { empty, episodes, stack, @@ -46,7 +39,6 @@ impl HomeStack { sender, }; - home.determine_state()?; Ok(home) } @@ -55,12 +47,6 @@ impl HomeStack { } pub(crate) fn update(&mut self) -> Result<(), Error> { - self.replace_view()?; - // Determine the actual state. - self.determine_state().map_err(From::from) - } - - fn replace_view(&mut self) -> Result<(), Error> { // Get the container of the view let old = &self.episodes.view.container().clone(); @@ -88,13 +74,13 @@ impl HomeStack { Ok(()) } - fn switch_visible(&mut self, s: State, animation: StackTransitionType) { + pub(crate) fn switch_visible(&mut self, s: State, animation: StackTransitionType) { use self::State::*; match s { - Home => { + Populated => { self.stack.set_visible_child_full("home", animation); - self.state = Home; + self.state = Populated; } Empty => { self.stack.set_visible_child_full("empty", animation); @@ -102,14 +88,4 @@ impl HomeStack { } } } - - fn determine_state(&mut self) -> Result<(), DataError> { - if is_episodes_populated()? { - self.switch_visible(State::Home, StackTransitionType::Crossfade); - } else { - self.switch_visible(State::Empty, StackTransitionType::Crossfade); - }; - - Ok(()) - } } diff --git a/podcasts-gtk/src/stacks/mod.rs b/podcasts-gtk/src/stacks/mod.rs index 2855610..31adf96 100644 --- a/podcasts-gtk/src/stacks/mod.rs +++ b/podcasts-gtk/src/stacks/mod.rs @@ -3,7 +3,7 @@ mod home; mod populated; mod show; -pub(crate) use self::content::Content; +pub(crate) use self::content::{Content, State}; pub(crate) use self::home::HomeStack; pub(crate) use self::populated::{PopulatedStack, PopulatedState}; pub(crate) use self::show::ShowStack; diff --git a/podcasts-gtk/src/stacks/show.rs b/podcasts-gtk/src/stacks/show.rs index 73244a7..f57ad49 100644 --- a/podcasts-gtk/src/stacks/show.rs +++ b/podcasts-gtk/src/stacks/show.rs @@ -6,6 +6,7 @@ use failure::Error; use podcasts_data::dbqueries::is_podcasts_populated; use app::Action; +use stacks::content::State; use stacks::PopulatedStack; use utils::get_ignored_shows; use widgets::EmptyView; @@ -14,18 +15,12 @@ use std::cell::RefCell; use std::ops::Deref; use std::rc::Rc; -#[derive(Debug, Clone, Copy)] -pub(crate) enum ShowState { - Populated, - Empty, -} - #[derive(Debug, Clone)] pub(crate) struct ShowStack { empty: EmptyView, populated: Rc>, stack: gtk::Stack, - state: ShowState, + state: State, sender: Sender, } @@ -34,7 +29,7 @@ impl ShowStack { let populated = Rc::new(RefCell::new(PopulatedStack::new(sender.clone()))); let empty = EmptyView::default(); let stack = gtk::Stack::new(); - let state = ShowState::Empty; + let state = State::Empty; stack.add_named(&populated.borrow().container(), "populated"); stack.add_named(empty.deref(), "empty"); @@ -65,8 +60,8 @@ impl ShowStack { self.determine_state() } - fn switch_visible(&mut self, s: ShowState) { - use self::ShowState::*; + pub(crate) fn switch_visible(&mut self, s: State) { + use self::State::*; match s { Populated => { @@ -81,14 +76,12 @@ impl ShowStack { } fn determine_state(&mut self) -> Result<(), Error> { - use self::ShowState::*; - let ign = get_ignored_shows()?; debug!("IGNORED SHOWS {:?}", ign); if is_podcasts_populated(&ign)? { - self.switch_visible(Populated); + self.sender.send(Action::PopulatedState); } else { - self.switch_visible(Empty); + self.sender.send(Action::EmptyState); }; Ok(()) From 14d4818867393037b9b1081b27c87f5abe2424a0 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Sun, 19 Aug 2018 14:25:41 +0300 Subject: [PATCH 2/3] App: Disable refresh action while in empty state Close #71 --- podcasts-gtk/src/app.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/podcasts-gtk/src/app.rs b/podcasts-gtk/src/app.rs index 0c6623e..4806a39 100644 --- a/podcasts-gtk/src/app.rs +++ b/podcasts-gtk/src/app.rs @@ -349,8 +349,24 @@ impl App { let menu = &s.get().container; self.headerbar.set_secondary_menu(menu); } - Action::EmptyState => self.content.switch_to_empty_views(), - Action::PopulatedState => self.content.switch_to_populated(), + Action::EmptyState => { + self.window + .lookup_action("refresh") + .and_then(|action| action.downcast::().ok()) + // Disable refresh action + .map(|action| action.set_enabled(false)); + + self.content.switch_to_empty_views(); + } + Action::PopulatedState => { + self.window + .lookup_action("refresh") + .and_then(|action| action.downcast::().ok()) + // Enable refresh action + .map(|action| action.set_enabled(true)); + + self.content.switch_to_populated(); + } } } From 04161284a79bd58f9062ad4eb7714f8f55cfe68e Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Sun, 19 Aug 2018 14:31:27 +0300 Subject: [PATCH 3/3] Headerbar: Make the switcher insensitive if empty If there are no shows/episodes to display, there isn't any point to being able to hit the switcher. --- podcasts-gtk/src/app.rs | 2 ++ podcasts-gtk/src/headerbar.rs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/podcasts-gtk/src/app.rs b/podcasts-gtk/src/app.rs index 4806a39..23247ed 100644 --- a/podcasts-gtk/src/app.rs +++ b/podcasts-gtk/src/app.rs @@ -356,6 +356,7 @@ impl App { // Disable refresh action .map(|action| action.set_enabled(false)); + self.headerbar.switch.set_sensitive(false); self.content.switch_to_empty_views(); } Action::PopulatedState => { @@ -365,6 +366,7 @@ impl App { // Enable refresh action .map(|action| action.set_enabled(true)); + self.headerbar.switch.set_sensitive(true); self.content.switch_to_populated(); } } diff --git a/podcasts-gtk/src/headerbar.rs b/podcasts-gtk/src/headerbar.rs index 6deb7c4..b68d5bc 100644 --- a/podcasts-gtk/src/headerbar.rs +++ b/podcasts-gtk/src/headerbar.rs @@ -21,7 +21,7 @@ use i18n::i18n; // TODO: Make a proper state machine for the headerbar states pub(crate) struct Header { pub(crate) container: gtk::HeaderBar, - switch: gtk::StackSwitcher, + pub(crate) switch: gtk::StackSwitcher, back: gtk::Button, show_title: gtk::Label, hamburger: gtk::MenuButton,