From 91aae6a9f55c9b2080aed3cdea523b4086fccfb3 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Wed, 27 Jun 2018 20:15:42 +0300 Subject: [PATCH] Headerbar: Factor out the AddPopover widget. --- hammond-gtk/resources/gtk/headerbar.ui | 6 +- hammond-gtk/src/headerbar.rs | 216 ++++++++++++++----------- 2 files changed, 122 insertions(+), 100 deletions(-) diff --git a/hammond-gtk/resources/gtk/headerbar.ui b/hammond-gtk/resources/gtk/headerbar.ui index 4bdb777..dbaf339 100644 --- a/hammond-gtk/resources/gtk/headerbar.ui +++ b/hammond-gtk/resources/gtk/headerbar.ui @@ -151,9 +151,10 @@ Tobias Bernard True True - False + True Add a new feed center + add_popover True @@ -262,7 +263,6 @@ Tobias Bernard True False center - True True @@ -281,4 +281,4 @@ Tobias Bernard - \ No newline at end of file + diff --git a/hammond-gtk/src/headerbar.rs b/hammond-gtk/src/headerbar.rs index 69e8159..ce8b550 100644 --- a/hammond-gtk/src/headerbar.rs +++ b/hammond-gtk/src/headerbar.rs @@ -1,13 +1,12 @@ use gio::MenuModel; -use glib; use gtk; use gtk::prelude::*; +use crossbeam_channel::Sender; use failure::Error; -use failure::ResultExt; +use rayon; use url::Url; -use crossbeam_channel::Sender; use hammond_data::{dbqueries, Source}; use app::Action; @@ -20,13 +19,13 @@ use std::rc::Rc; // TODO: split this into smaller pub struct Header { container: gtk::HeaderBar, - add_toggle: gtk::MenuButton, switch: gtk::StackSwitcher, back: gtk::Button, show_title: gtk::Label, menu_button: gtk::MenuButton, app_menu: MenuModel, updater: UpdateIndicator, + add: AddPopover, } #[derive(Debug, Clone)] @@ -52,21 +51,100 @@ impl UpdateIndicator { } } +#[derive(Debug, Clone)] +struct AddPopover { + container: gtk::Popover, + result: gtk::Label, + entry: gtk::Entry, + add: gtk::Button, + toggle: gtk::MenuButton, +} + +impl AddPopover { + // FIXME: THIS ALSO SUCKS! + fn on_add_clicked(&self, sender: &Sender) -> Result<(), Error> { + let url = self + .entry + .get_text() + .ok_or_else(|| format_err!("GtkEntry blew up somehow."))?; + + debug!("Url: {}", url); + let url = if url.contains("itunes.com") || url.contains("apple.com") { + info!("Detected itunes url."); + let foo = itunes_to_rss(&url)?; + info!("Resolved to {}", foo); + foo + } else { + url.to_owned() + }; + + self.entry.set_text(""); + + rayon::spawn(clone!(sender => move || { + if let Ok(source) = Source::from_url(&url) { + refresh(Some(vec![source]), sender.clone()); + } else { + error!("Failed to convert, url: {}, to a source entry", url); + } + })); + + self.container.hide(); + Ok(()) + } + + // FIXME: THIS SUCKS! REFACTOR ME. + fn on_entry_changed(&self) -> Result<(), Error> { + let uri = self + .entry + .get_text() + .ok_or_else(|| format_err!("GtkEntry blew up somehow."))?; + debug!("Url: {}", uri); + + let url = Url::parse(&uri); + // TODO: refactor to avoid duplication + match url { + Ok(u) => { + if !dbqueries::source_exists(u.as_str())? { + self.add.set_sensitive(true); + self.result.hide(); + self.result.set_label(""); + } else { + self.add.set_sensitive(false); + self.result.set_label("Show already exists."); + self.result.show(); + } + Ok(()) + } + Err(err) => { + self.add.set_sensitive(false); + if !uri.is_empty() { + self.result.set_label("Invalid url."); + self.result.show(); + error!("Error: {}", err); + } else { + self.result.hide(); + } + Ok(()) + } + } + } +} + impl Default for Header { fn default() -> Header { let builder = gtk::Builder::new_from_resource("/org/gnome/Hammond/gtk/headerbar.ui"); let menus = gtk::Builder::new_from_resource("/org/gnome/Hammond/gtk/menus.ui"); let header = builder.get_object("headerbar").unwrap(); - let add_toggle = builder.get_object("add_toggle").unwrap(); let switch = builder.get_object("switch").unwrap(); let back = builder.get_object("back").unwrap(); let show_title = builder.get_object("show_title").unwrap(); + let menu_button = builder.get_object("menu_button").unwrap(); + let app_menu = menus.get_object("menu").unwrap(); + let update_box = builder.get_object("update_notification").unwrap(); let update_label = builder.get_object("update_label").unwrap(); let update_spinner = builder.get_object("update_spinner").unwrap(); - let menu_button = builder.get_object("menu_button").unwrap(); - let app_menu = menus.get_object("menu").unwrap(); let updater = UpdateIndicator { container: update_box, @@ -74,15 +152,28 @@ impl Default for Header { spinner: update_spinner, }; + let add_toggle = builder.get_object("add_toggle").unwrap(); + let add_popover = builder.get_object("add_popover").unwrap(); + let new_url = builder.get_object("new_url").unwrap(); + let add_button = builder.get_object("add_button").unwrap(); + let result = builder.get_object("result_label").unwrap(); + let add = AddPopover { + container: add_popover, + entry: new_url, + toggle: add_toggle, + add: add_button, + result, + }; + Header { container: header, - add_toggle, switch, back, show_title, menu_button, app_menu, updater, + add, } } } @@ -95,46 +186,39 @@ impl Header { sender: &Sender, ) -> Rc { let h = Rc::new(Header::default()); - h.init(content, window, &sender); + Self::init(&h, content, window, &sender); h } pub fn init( - &self, + s: &Rc, content: &Content, window: >k::ApplicationWindow, sender: &Sender, ) { - let builder = gtk::Builder::new_from_resource("/org/gnome/Hammond/gtk/headerbar.ui"); + let weak = Rc::downgrade(s); - let add_popover: gtk::Popover = builder.get_object("add_popover").unwrap(); - let new_url: gtk::Entry = builder.get_object("new_url").unwrap(); - let add_button: gtk::Button = builder.get_object("add_button").unwrap(); - let result_label: gtk::Label = builder.get_object("result_label").unwrap(); - self.switch.set_stack(&content.get_stack()); + s.switch.set_stack(&content.get_stack()); - new_url.connect_changed(clone!(add_button => move |url| { - on_url_change(url, &result_label, &add_button) - .map_err(|err| error!("Error: {}", err)) - .ok(); + s.add.entry.connect_changed(clone!(weak => move |_| { + weak.upgrade().map(|h| { + h.add.on_entry_changed() + .map_err(|err| error!("Error: {}", err)) + .ok(); + }); })); - add_button.connect_clicked(clone!(add_popover, new_url, sender => move |_| { - on_add_bttn_clicked(&new_url, sender.clone()) - .map_err(|err| error!("Error: {}", err)) - .ok(); - add_popover.hide(); + s.add.add.connect_clicked(clone!(weak, sender => move |_| { + weak.upgrade().map(|h| h.add.on_add_clicked(&sender)); })); - self.add_toggle.set_popover(&add_popover); - // Add the Headerbar to the window. - window.set_titlebar(&self.container); + window.set_titlebar(&s.container); - let switch = &self.switch; - let add_toggle = &self.add_toggle; - let show_title = &self.show_title; - self.back.connect_clicked( + let switch = &s.switch; + let add_toggle = &s.add.toggle; + let show_title = &s.show_title; + s.back.connect_clicked( clone!(switch, add_toggle, show_title, sender => move |back| { switch.show(); add_toggle.show(); @@ -144,12 +228,12 @@ impl Header { }), ); - self.menu_button.set_menu_model(Some(&self.app_menu)); + s.menu_button.set_menu_model(Some(&s.app_menu)); } pub fn switch_to_back(&self, title: &str) { self.switch.hide(); - self.add_toggle.hide(); + self.add.toggle.hide(); self.back.show(); self.set_show_title(title); self.show_title.show(); @@ -157,7 +241,7 @@ impl Header { pub fn switch_to_normal(&self) { self.switch.show(); - self.add_toggle.show(); + self.add.toggle.show(); self.back.hide(); self.show_title.hide(); } @@ -178,65 +262,3 @@ impl Header { self.menu_button.clicked(); } } - -// FIXME: THIS ALSO SUCKS! -fn on_add_bttn_clicked(entry: >k::Entry, sender: Sender) -> Result<(), Error> { - let url = entry.get_text().unwrap_or_default(); - let url = if url.contains("itunes.com") || url.contains("apple.com") { - info!("Detected itunes url."); - let foo = itunes_to_rss(&url)?; - info!("Resolved to {}", foo); - foo - } else { - url.to_owned() - }; - - let source = Source::from_url(&url).context("Failed to convert url to a Source entry.")?; - entry.set_text(""); - - gtk::idle_add(move || { - refresh(Some(vec![source.clone()]), sender.clone()); - glib::Continue(false) - }); - Ok(()) -} - -// FIXME: THIS SUCKS! -fn on_url_change( - entry: >k::Entry, - result: >k::Label, - add_button: >k::Button, -) -> Result<(), Error> { - let uri = entry - .get_text() - .ok_or_else(|| format_err!("GtkEntry blew up somehow."))?; - debug!("Url: {}", uri); - - let url = Url::parse(&uri); - // TODO: refactor to avoid duplication - match url { - Ok(u) => { - if !dbqueries::source_exists(u.as_str())? { - add_button.set_sensitive(true); - result.hide(); - result.set_label(""); - } else { - add_button.set_sensitive(false); - result.set_label("Show already exists."); - result.show(); - } - Ok(()) - } - Err(err) => { - add_button.set_sensitive(false); - if !uri.is_empty() { - result.set_label("Invalid url."); - result.show(); - error!("Error: {}", err); - } else { - result.hide(); - } - Ok(()) - } - } -}