diff --git a/src/app.rs b/src/app.rs index c4991d37..9833231a 100644 --- a/src/app.rs +++ b/src/app.rs @@ -52,7 +52,7 @@ impl App { let action_page_home = Action::new("win", false, None); let action_page_history_back = Action::new("win", false, None); let action_page_history_forward = Action::new("win", false, None); - let action_page_reload = Action::new("win", true, None); + let action_page_reload = Action::new_stateful("win", true, None, &0.to_variant()); // @TODO let action_page_pin = Action::new("win", true, None); // Init GTK diff --git a/src/app/action.rs b/src/app/action.rs index 1f44d456..1c1a7108 100644 --- a/src/app/action.rs +++ b/src/app/action.rs @@ -4,7 +4,7 @@ use gtk::{ gio::SimpleAction, - glib::{gformat, uuid_string_random, GString, VariantTy}, + glib::{gformat, uuid_string_random, GString, Variant, VariantTy}, prelude::ActionExt, }; @@ -27,6 +27,23 @@ impl Action { Self { group, simple } } + pub fn new_stateful( + group: &str, + is_enabled: bool, + parameter_type: Option<&VariantTy>, + state: &Variant, + ) -> Self { + // Create random action name as no static values should be in use + let simple = SimpleAction::new_stateful(&uuid_string_random(), parameter_type, state); + simple.set_enabled(is_enabled); + + // Assign action to the group + let group = GString::from(group); + + // Return new Action + Self { group, simple } + } + // Getters pub fn detailed_name(&self) -> GString { gformat!("{}.{}", self.group, self.simple.name()) // @TODO find the way to ident parent group diff --git a/src/app/browser.rs b/src/app/browser.rs index c78594d9..86bb2a1f 100644 --- a/src/app/browser.rs +++ b/src/app/browser.rs @@ -11,7 +11,7 @@ use window::Window; use adw::ApplicationWindow; use gtk::{ gio::{Cancellable, File, SimpleAction}, - prelude::GtkWindowExt, + prelude::{ActionExt, GtkWindowExt}, FileLauncher, }; use sqlite::Transaction; @@ -174,8 +174,14 @@ impl Browser { action_page_reload.connect_activate({ let window = window.clone(); - move |_, _| { - window.tab_page_navigation_reload(); + move |this, _| { + let page_position = this + .state() + .expect("Page position required for reload action") + .get::() + .expect("Parameter does not match `i32`"); + + window.tab_page_navigation_reload(page_position); } }); diff --git a/src/app/browser/window.rs b/src/app/browser/window.rs index 8e6012d0..51dbffa1 100644 --- a/src/app/browser/window.rs +++ b/src/app/browser/window.rs @@ -92,8 +92,8 @@ impl Window { self.tab.page_navigation_history_forward(); } - pub fn tab_page_navigation_reload(&self) { - self.tab.page_navigation_reload(); + pub fn tab_page_navigation_reload(&self, page_position: i32) { + self.tab.page_navigation_reload(page_position); } pub fn tab_close(&self) { diff --git a/src/app/browser/window/tab.rs b/src/app/browser/window/tab.rs index 6dcee98b..a2e97742 100644 --- a/src/app/browser/window/tab.rs +++ b/src/app/browser/window/tab.rs @@ -8,9 +8,9 @@ use widget::Widget; use adw::TabView; use gtk::{ - gio::SimpleAction, - glib::{uuid_string_random, GString, Propagation}, - prelude::StaticVariantType, + gio::{Menu, SimpleAction}, + glib::{gformat, uuid_string_random, GString, Propagation}, + prelude::{ActionExt, StaticVariantType, ToVariant}, }; use sqlite::Transaction; use std::{cell::RefCell, collections::HashMap, sync::Arc}; @@ -48,10 +48,51 @@ impl Tab { // Init empty HashMap index as no tabs appended yet let index = Arc::new(RefCell::new(HashMap::new())); + // @TODO move to mod + let menu = Menu::new(); + + menu.append( + Some("Reload"), + Some(&gformat!("win.{}", action_page_reload.name())), + ); // @TODO resolve namespace outside + // Init widget - let widget = Arc::new(Widget::new(action_tab_open.clone())); + let widget = Arc::new(Widget::new(&menu)); // Init events + + // Setup actions for context menu + widget.gobject().connect_setup_menu({ + let action_page_reload = action_page_reload.clone(); + move |tab_view, tab_page| { + // Enable actions by default + action_page_reload.set_enabled(true); + + match tab_page { + // Menu opened: + // setup actions to operate with page selected only + Some(this) => { + let position = tab_view.page_position(this).to_variant(); + + action_page_reload.change_state(&position); + } + // Menu closed: + // return actions to default values + None => match tab_view.selected_page() { + Some(selected) => { + // Get position of page selected + let position = tab_view.page_position(&selected).to_variant(); + + // Update related actions + action_page_reload.change_state(&position); + } + // No selected page found, disable related actions + None => action_page_reload.set_enabled(false), + }, + } + } + }); + widget.gobject().connect_close_page({ let index = index.clone(); move |_, item| { @@ -201,8 +242,8 @@ impl Tab { } } - pub fn page_navigation_reload(&self) { - if let Some(id) = self.widget.current_page_keyword() { + pub fn page_navigation_reload(&self, page_position: i32) { + if let Some(id) = self.widget.gobject().nth_page(page_position).keyword() { if let Some(item) = self.index.borrow().get(&id) { item.page_navigation_reload(); } diff --git a/src/app/browser/window/tab/widget.rs b/src/app/browser/window/tab/widget.rs index 36b962a6..657f031f 100644 --- a/src/app/browser/window/tab/widget.rs +++ b/src/app/browser/window/tab/widget.rs @@ -1,36 +1,37 @@ use adw::TabView; use gtk::{ - gio::{Icon, SimpleAction, SimpleActionGroup}, - glib::{uuid_string_random, GString}, - prelude::{ActionMapExt, WidgetExt}, + gio::{Icon, MenuModel}, + glib::GString, + prelude::IsA, }; +/// Currently used as the indicator for pinned tabs +const DEFAULT_TAB_ICON: &str = "view-pin-symbolic"; + +/// Wrapper for [TabView](https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.TabView.html) GObject pub struct Widget { gobject: TabView, } impl Widget { // Construct - pub fn new(action_page_new: SimpleAction) -> Self { - // Init additional action group - let action_group = SimpleActionGroup::new(); - action_group.add_action(&action_page_new); - + pub fn new(menu_model: &impl IsA) -> Self { // Init gobject - let gobject = TabView::new(); + let gobject = TabView::builder().menu_model(menu_model).build(); - // Change default icon visible for tabs pinned - if let Ok(default_icon) = Icon::for_string("view-pin-symbolic") { + // Change default icon (if available in the system icon set) + // * visible for pinned tabs only + // * @TODO not default GTK behavior, make this feature optional + if let Ok(default_icon) = Icon::for_string(DEFAULT_TAB_ICON) { gobject.set_default_icon(&default_icon); } - // Create new group for actions - gobject.insert_action_group(&uuid_string_random(), Some(&action_group)); - + // Done Self { gobject } } // Actions + pub fn close(&self) { if let Some(selected_page) = self.gobject.selected_page() { self.gobject.close_page(&selected_page); @@ -46,6 +47,7 @@ impl Widget { } // Getters + pub fn current_page_keyword(&self) -> Option { let page = self.gobject.selected_page()?; let id = page.keyword()?;