use gtk; use gtk::Cast; use gtk::prelude::*; use hammond_data::Podcast; use hammond_data::dbqueries; use views::shows::ShowsPopulated; use views::empty::EmptyView; use views::episodes::EpisodesView; use widgets::show::ShowWidget; use app::Action; use std::sync::Arc; use std::sync::mpsc::Sender; #[derive(Debug, Clone)] pub struct Content { stack: gtk::Stack, shows: Arc, episodes: Arc, sender: Sender, } impl Content { pub fn new(sender: Sender) -> Arc { let stack = gtk::Stack::new(); let episodes = Arc::new(EpisodeStack::new(sender.clone())); let shows = Arc::new(ShowStack::new(sender.clone())); stack.add_titled(&episodes.stack, "episodes", "Episodes"); stack.add_titled(&shows.stack, "shows", "Shows"); Arc::new(Content { stack, shows, episodes, sender, }) } pub fn update(&self) { self.update_episode_view(); self.update_shows_view(); self.update_widget() } pub fn update_episode_view(&self) { self.episodes.update(); } pub fn update_episode_view_if_baground(&self) { if self.stack.get_visible_child_name() != Some("episodes".into()) { self.episodes.update(); } } pub fn update_shows_view(&self) { self.shows.update_podcasts(); } pub fn update_widget(&self) { self.shows.update_widget(); } pub fn update_widget_if_same(&self, pid: i32) { self.shows.update_widget_if_same(pid); } 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.shows.update_widget(); } } pub fn get_stack(&self) -> gtk::Stack { self.stack.clone() } pub fn get_shows(&self) -> Arc { self.shows.clone() } } #[derive(Debug, Clone)] pub struct ShowStack { stack: gtk::Stack, sender: Sender, } impl ShowStack { fn new(sender: Sender) -> ShowStack { let stack = gtk::Stack::new(); let show = ShowStack { stack, sender: sender.clone(), }; let pop = ShowsPopulated::new(sender.clone()); let widget = 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"); if pop.is_empty() { show.stack.set_visible_child_name("empty") } else { show.stack.set_visible_child_name("podcasts") } show } // pub fn update(&self) { // self.update_widget(); // self.update_podcasts(); // } pub fn update_podcasts(&self) { let vis = self.stack.get_visible_child_name().unwrap(); let old = self.stack .get_child_by_name("podcasts") // This is guaranted to exists, based on `ShowStack::new()`. .unwrap() .downcast::() // This is guaranted to be a Box based on the `ShowsPopulated` impl. .unwrap(); debug!("Name: {:?}", WidgetExt::get_name(&old)); let scrolled_window = old.get_children() .first() // This is guaranted to exist based on the show_widget.ui file. .unwrap() .clone() .downcast::() // This is guaranted based on the show_widget.ui file. .unwrap(); debug!("Name: {:?}", WidgetExt::get_name(&scrolled_window)); let pop = ShowsPopulated::new(self.sender.clone()); // Copy the vertical scrollbar adjustment from the old view into the new one. scrolled_window .get_vadjustment() .map(|x| pop.set_vadjustment(&x)); self.stack.remove(&old); self.stack.add_named(&pop.container, "podcasts"); if pop.is_empty() { self.stack.set_visible_child_name("empty"); } else if vis != "empty" { self.stack.set_visible_child_name(&vis); } else { self.stack.set_visible_child_name("podcasts"); } old.destroy(); } pub fn replace_widget(&self, pd: &Podcast) { let old = self.stack .get_child_by_name("widget") // This is guaranted to exists, based on `ShowStack::new()`. .unwrap() .downcast::() // This is guaranted to be a Box based on the `ShowWidget` impl. .unwrap(); debug!("Name: {:?}", WidgetExt::get_name(&old)); let new = ShowWidget::new(pd, self.sender.clone()); // Each composite ShowWidget is a gtkBox with the Podcast.id encoded in the gtk::Widget // name. It's a hack since we can't yet subclass GObject easily. let oldid = WidgetExt::get_name(&old); let newid = WidgetExt::get_name(&new.container); debug!("Old widget Name: {:?}\nNew widget Name: {:?}", oldid, newid); // Only copy the old scrollbar if both widget's represent the same podcast. if newid == oldid { let scrolled_window = old.get_children() .first() // This is guaranted to exist based on the show_widget.ui file. .unwrap() .clone() .downcast::() // This is guaranted based on the show_widget.ui file. .unwrap(); debug!("Name: {:?}", WidgetExt::get_name(&scrolled_window)); // Copy the vertical scrollbar adjustment from the old view into the new one. scrolled_window .get_vadjustment() .map(|x| new.set_vadjustment(&x)); } self.stack.remove(&old); self.stack.add_named(&new.container, "widget"); } pub fn update_widget(&self) { let vis = self.stack.get_visible_child_name().unwrap(); let old = self.stack.get_child_by_name("widget").unwrap(); let id = WidgetExt::get_name(&old); if id == Some("GtkBox".to_string()) || id.is_none() { return; } let pd = dbqueries::get_podcast_from_id(id.unwrap().parse::().unwrap()); if let Ok(pd) = pd { self.replace_widget(&pd); self.stack.set_visible_child_name(&vis); old.destroy(); } } // Only update widget if it's podcast_id is equal to pid. pub fn update_widget_if_same(&self, pid: i32) { let old = self.stack.get_child_by_name("widget").unwrap(); let id = WidgetExt::get_name(&old); if id != Some(pid.to_string()) || id.is_none() { return; } self.update_widget(); } pub fn switch_podcasts_animated(&self) { self.stack .set_visible_child_full("podcasts", gtk::StackTransitionType::SlideRight); } pub fn switch_widget_animated(&self) { self.stack .set_visible_child_full("widget", gtk::StackTransitionType::SlideLeft) } pub fn get_stack(&self) -> gtk::Stack { self.stack.clone() } } #[derive(Debug, Clone)] pub struct EpisodeStack { stack: gtk::Stack, sender: Sender, } impl EpisodeStack { fn new(sender: Sender) -> EpisodeStack { let episodes = EpisodesView::new(sender.clone()); let empty = EmptyView::new(); let stack = gtk::Stack::new(); stack.add_named(&episodes.container, "episodes"); stack.add_named(&empty.container, "empty"); if episodes.is_empty() { stack.set_visible_child_name("empty"); } else { stack.set_visible_child_name("episodes"); } EpisodeStack { stack, sender } } pub fn update(&self) { let old = self.stack .get_child_by_name("episodes") // This is guaranted to exists, based on `EpisodeStack::new()`. .unwrap() .downcast::() // This is guaranted to be a Box based on the `EpisodesView` impl. .unwrap(); debug!("Name: {:?}", WidgetExt::get_name(&old)); let scrolled_window = old.get_children() .first() // This is guaranted to exist based on the episodes_view.ui file. .unwrap() .clone() .downcast::() // This is guaranted based on the episodes_view.ui file. .unwrap(); debug!("Name: {:?}", WidgetExt::get_name(&scrolled_window)); let eps = EpisodesView::new(self.sender.clone()); // Copy the vertical scrollbar adjustment from the old view into the new one. scrolled_window .get_vadjustment() .map(|x| eps.set_vadjustment(&x)); self.stack.remove(&old); self.stack.add_named(&eps.container, "episodes"); if eps.is_empty() { self.stack.set_visible_child_name("empty"); } else { self.stack.set_visible_child_name("episodes"); } old.destroy(); } }