diff --git a/src/app.rs b/src/app.rs index f82f6fdf..b47f4a67 100644 --- a/src/app.rs +++ b/src/app.rs @@ -189,6 +189,14 @@ impl App { ), &["r"], ), + ( + format!( + "{}.{}", + browser.window.action.id, + browser.window.action.save_as.gobject.name() + ), + &["s"], + ), ( format!( "{}.{}", diff --git a/src/app/browser/window.rs b/src/app/browser/window.rs index 13f70ff7..579b11f0 100644 --- a/src/app/browser/window.rs +++ b/src/app/browser/window.rs @@ -81,6 +81,11 @@ impl Window { move |_| tab.close_all() }); + action.save_as.connect_activate({ + let tab = tab.clone(); + move |position| tab.save_as(position) + }); + action.history_back.connect_activate({ let tab = tab.clone(); move |position| { diff --git a/src/app/browser/window/action.rs b/src/app/browser/window/action.rs index 47e5ae4e..a99847b0 100644 --- a/src/app/browser/window/action.rs +++ b/src/app/browser/window/action.rs @@ -7,6 +7,7 @@ mod history_forward; mod home; mod pin; mod reload; +mod save_as; use append::Append; use bookmark::Bookmark; @@ -17,6 +18,7 @@ use history_forward::HistoryForward; use home::Home; use pin::Pin; use reload::Reload; +use save_as::SaveAs; use gtk::{ gio::SimpleActionGroup, @@ -39,6 +41,7 @@ pub struct Action { pub home: Rc, pub pin: Rc, pub reload: Rc, + pub save_as: Rc, // Group pub id: GString, pub gobject: SimpleActionGroup, @@ -59,6 +62,7 @@ impl Action { let home = Rc::new(Home::new()); let pin = Rc::new(Pin::new()); let reload = Rc::new(Reload::new()); + let save_as = Rc::new(SaveAs::new()); // Generate unique group ID let id = uuid_string_random(); @@ -76,6 +80,7 @@ impl Action { gobject.add_action(&home.gobject); gobject.add_action(&pin.gobject); gobject.add_action(&reload.gobject); + gobject.add_action(&save_as.gobject); // Done Self { @@ -88,6 +93,7 @@ impl Action { home, pin, reload, + save_as, id, gobject, } diff --git a/src/app/browser/window/action/save_as.rs b/src/app/browser/window/action/save_as.rs new file mode 100644 index 00000000..9be15e1d --- /dev/null +++ b/src/app/browser/window/action/save_as.rs @@ -0,0 +1,64 @@ +use gtk::{ + gio::SimpleAction, + glib::uuid_string_random, + prelude::{ActionExt, ToVariant}, +}; + +// Defaults + +/// C-compatible variant type +const DEFAULT_STATE: i32 = -1; + +/// [SimpleAction](https://docs.gtk.org/gio/class.SimpleAction.html) wrapper for `Save As` action of `Window` group +pub struct SaveAs { + pub gobject: SimpleAction, +} + +impl SaveAs { + // Constructors + + /// Create new `Self` + pub fn new() -> Self { + Self { + gobject: SimpleAction::new_stateful( + &uuid_string_random(), + None, + &DEFAULT_STATE.to_variant(), + ), + } + } + + // Actions + + /// Change action [state](https://docs.gtk.org/gio/method.SimpleAction.set_state.html) + /// * set `DEFAULT_STATE` on `None` + pub fn change_state(&self, state: Option) { + self.gobject.change_state( + &match state { + Some(value) => value, + None => DEFAULT_STATE, + } + .to_variant(), + ) + } + + // Events + + /// Define callback function for + /// [SimpleAction::activate](https://docs.gtk.org/gio/signal.SimpleAction.activate.html) signal + pub fn connect_activate(&self, callback: impl Fn(Option) + 'static) { + self.gobject.connect_activate(move |this, _| { + let state = this + .state() + .expect("State value required") + .get::() + .expect("Parameter type does not match `i32`"); + + callback(if state == DEFAULT_STATE { + None + } else { + Some(state) + }) + }); + } +} diff --git a/src/app/browser/window/header/bar/menu.rs b/src/app/browser/window/header/bar/menu.rs index 16b2ccaf..99f4886d 100644 --- a/src/app/browser/window/header/bar/menu.rs +++ b/src/app/browser/window/header/bar/menu.rs @@ -36,6 +36,12 @@ impl Menu { window_action.reload.gobject.name() ))); + main_page.append(Some("Save as.."), Some(&format!( + "{}.{}", + window_action.id, + window_action.save_as.gobject.name() + ))); + // Main > Page > Mark let main_page_mark = gio::Menu::new(); diff --git a/src/app/browser/window/tab.rs b/src/app/browser/window/tab.rs index f8c25e48..b6dd5ba2 100644 --- a/src/app/browser/window/tab.rs +++ b/src/app/browser/window/tab.rs @@ -59,6 +59,7 @@ impl Tab { action.home.change_state(state); action.pin.change_state(state); action.reload.change_state(state); + action.save_as.change_state(state); } }); @@ -152,18 +153,22 @@ impl Tab { self.widget.close_all(); } + // Save page at given `position`, `None` to save selected page (if available) + pub fn save_as(&self, page_position: Option) { + if let Some(item) = self.item(page_position) { + item.page.navigation.request.to_download(); + item.page.load(true); + } + } + /// Toggle `Bookmark` in current `Profile` for `Page` at given `position` (current page on `None`) /// * return `true` on bookmark created, `false` on deleted; `Error` otherwise. pub fn bookmark(&self, page_position: Option) -> Result { - if let Some(page) = self.widget.page(page_position) { - if let Some(id) = page.keyword() { - if let Some(item) = self.index.borrow().get(&id) { - return match item.page.bookmark() { - Ok(result) => Ok(result), - Err(_) => Err(Error::Bookmark), - }; - } - } + if let Some(item) = self.item(page_position) { + return match item.page.bookmark() { + Ok(result) => Ok(result), + Err(_) => Err(Error::Bookmark), + }; } Err(Error::PageNotFound) } @@ -174,43 +179,27 @@ impl Tab { } pub fn page_home(&self, page_position: Option) { - if let Some(page) = self.widget.page(page_position) { - if let Some(id) = page.keyword() { - if let Some(item) = self.index.borrow().get(&id) { - item.page.home(); - } - } + if let Some(item) = self.item(page_position) { + item.page.home(); } } pub fn page_history_back(&self, page_position: Option) { - if let Some(page) = self.widget.page(page_position) { - if let Some(id) = page.keyword() { - if let Some(item) = self.index.borrow().get(&id) { - item.page.history_back(); - } - } + if let Some(item) = self.item(page_position) { + item.page.history_back(); } } pub fn page_history_forward(&self, page_position: Option) { - if let Some(page) = self.widget.page(page_position) { - if let Some(id) = page.keyword() { - if let Some(item) = self.index.borrow().get(&id) { - item.page.history_forward(); - } - } + if let Some(item) = self.item(page_position) { + item.page.history_forward(); } } /// Reload page at `i32` position or selected page on `None` given pub fn page_reload(&self, page_position: Option) { - if let Some(page) = self.widget.page(page_position) { - if let Some(id) = page.keyword() { - if let Some(item) = self.index.borrow().get(&id) { - item.page.reload(); - } - } + if let Some(item) = self.item(page_position) { + item.page.reload(); } } @@ -343,6 +332,17 @@ impl Tab { // @TODO other/child features.. } + + fn item(&self, position: Option) -> Option> { + if let Some(page) = self.widget.page(position) { + if let Some(id) = page.keyword() { + if let Some(item) = self.index.borrow().get(&id) { + return Some(item.clone()); + } + } + } + None + } } // Tools diff --git a/src/app/browser/window/tab/item/page/navigation/request.rs b/src/app/browser/window/tab/item/page/navigation/request.rs index 49643bc8..35ac13a7 100644 --- a/src/app/browser/window/tab/item/page/navigation/request.rs +++ b/src/app/browser/window/tab/item/page/navigation/request.rs @@ -5,7 +5,7 @@ use widget::Widget; use crate::app::browser::{window::tab::item::Action as TabAction, Action as BrowserAction}; use gtk::{ - glib::{GString, Uri, UriFlags}, + glib::{gformat, GString, Uri, UriFlags}, prelude::EditableExt, }; use sqlite::Transaction; @@ -88,6 +88,12 @@ impl Request { Ok(()) } + // Setters + + pub fn to_download(&self) { + self.widget.entry.set_text(&self.download()); + } + // Getters pub fn uri(&self) -> Option { @@ -110,6 +116,10 @@ impl Request { text } + + pub fn download(&self) -> GString { + gformat!("download:{}", self.strip_prefix()) + } } // Tools diff --git a/src/app/browser/window/tab/menu.rs b/src/app/browser/window/tab/menu.rs index 4f9267ab..a533da1a 100644 --- a/src/app/browser/window/tab/menu.rs +++ b/src/app/browser/window/tab/menu.rs @@ -25,6 +25,15 @@ impl Menu { )), ); + main.append( + Some("Save as.."), + Some(&format!( + "{}.{}", + window_action.id, + window_action.save_as.gobject.name() + )), + ); + let main_mark = gtk::gio::Menu::new(); main_mark.append(