diff --git a/src/app/browser/window.rs b/src/app/browser/window.rs index 3e7c6a50..be26c151 100644 --- a/src/app/browser/window.rs +++ b/src/app/browser/window.rs @@ -33,15 +33,13 @@ impl Window { // Init components let tab = Rc::new(Tab::new(browser_action.clone(), action.clone())); let header = Header::new(browser_action, action.clone(), tab.widget().gobject()); - - // GTK let widget = Rc::new(Widget::new(header.gobject(), tab.widget().gobject())); // Init events action.append().connect_activate({ let tab = tab.clone(); - move || { - tab.append(None); + move |position, is_pinned, is_selected| { + tab.append(position, is_pinned, is_selected); } }); diff --git a/src/app/browser/window/action/append.rs b/src/app/browser/window/action/append.rs index 0448145c..cd9c474a 100644 --- a/src/app/browser/window/action/append.rs +++ b/src/app/browser/window/action/append.rs @@ -1,9 +1,14 @@ use gtk::{ gio::SimpleAction, glib::{uuid_string_random, GString}, - prelude::ActionExt, + prelude::{ActionExt, ToVariant}, }; +/// C-compatible variant type +const DEFAULT_POSITION: i32 = -1; +const DEFAULT_IS_PINNED: bool = false; +const DEFAULT_IS_SELECTED: bool = true; + /// [SimpleAction](https://docs.gtk.org/gio/class.SimpleAction.html) wrapper for `Append` action of `Window` group pub struct Append { gobject: SimpleAction, @@ -12,26 +17,87 @@ pub struct Append { impl Append { // Constructors - /// Create new `Self` + /// Create new `Self` with default action state preset pub fn new() -> Self { Self { - gobject: SimpleAction::new(&uuid_string_random(), None), + gobject: SimpleAction::new_stateful( + &uuid_string_random(), + None, + &(DEFAULT_POSITION, DEFAULT_IS_PINNED, DEFAULT_IS_SELECTED).to_variant(), + ), } } // Actions - /// Emit [activate](https://docs.gtk.org/gio/signal.SimpleAction.activate.html) signal - pub fn activate(&self) { + /// Emit [activate](https://docs.gtk.org/gio/signal.SimpleAction.activate.html) signal with default state + /// * this action reset previous state for action after activation + pub fn activate_default_once(&self) { + // Save current state in memory + let (position, is_pinned, is_selected) = state(&self.gobject); + + // Set default state + self.change_state( + Some(DEFAULT_POSITION), + DEFAULT_IS_PINNED, + DEFAULT_IS_SELECTED, + ); + + // Activate action self.gobject.activate(None); + + // Return previous state + self.change_state(position, is_pinned, is_selected); + } + + /// Emit [activate](https://docs.gtk.org/gio/signal.SimpleAction.activate.html) signal + /// * this action reset previous state for action after activation + pub fn activate_stateful_once( + &self, + position: Option, + is_pinned: bool, + is_selected: bool, + ) { + // Save current state in memory + let (_position, _is_pinned, _is_selected) = state(&self.gobject); + + // Apply requested state + self.change_state(position, is_pinned, is_selected); + + // Activate action + self.gobject.activate(None); + + // Return previous state + self.change_state(_position, _is_pinned, _is_selected); + } + + /// Emit state change for action + pub fn change_state(&self, position: Option, is_pinned: bool, is_selected: bool) { + self.gobject.change_state( + &( + // Convert Option to C-based variant value + if let Some(position) = position { + position + } else { + DEFAULT_POSITION + }, + is_pinned, + is_selected, + ) + .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() + 'static) { - self.gobject.connect_activate(move |_, _| callback()); + /// * return `position`, `is_pinned`, `is_selected` state as tuple + pub fn connect_activate(&self, callback: impl Fn(Option, bool, bool) + 'static) { + self.gobject.connect_activate(move |this, _| { + let (position, is_pinned, is_selected) = state(this); + callback(position, is_pinned, is_selected) + }); } // Getters @@ -46,3 +112,22 @@ impl Append { self.gobject.name() } } + +/// Shared helper to get C-based action state in Optional format +pub fn state(this: &SimpleAction) -> (Option, bool, bool) { + let (position, is_pinned, is_selected) = this + .state() + .expect("Expected (`position`,`is_pinned`,`is_selected`) state") + .get::<(i32, bool, bool)>() + .expect("Parameter type does not match (`i32`,`bool`,`bool`) tuple"); + ( + // Convert from C-based variant value to Option + if position == DEFAULT_POSITION { + None + } else { + Some(position) + }, + is_pinned, + is_selected, + ) +} diff --git a/src/app/browser/window/header/bar/tab/append/widget.rs b/src/app/browser/window/header/bar/tab/append/widget.rs index 368675ce..886c5fcc 100644 --- a/src/app/browser/window/header/bar/tab/append/widget.rs +++ b/src/app/browser/window/header/bar/tab/append/widget.rs @@ -18,7 +18,7 @@ impl Widget { .build(); // Init events - gobject.connect_clicked(move |_| window_action.append().activate()); + gobject.connect_clicked(move |_| window_action.append().activate_default_once()); Self { gobject } } diff --git a/src/app/browser/window/tab.rs b/src/app/browser/window/tab.rs index c54a081c..8474454f 100644 --- a/src/app/browser/window/tab.rs +++ b/src/app/browser/window/tab.rs @@ -99,7 +99,7 @@ impl Tab { } // Actions - pub fn append(&self, position: Option) -> Rc { + pub fn append(&self, position: Option, is_pinned: bool, is_selected: bool) -> Rc { // Init new tab item let item = Rc::new(Item::new( self.widget.gobject(), @@ -107,8 +107,8 @@ impl Tab { self.window_action.clone(), // Options position, - false, - true, + is_pinned, + is_selected, )); // Register dynamically created tab components in the HashMap index @@ -305,7 +305,7 @@ impl Tab { pub fn init(&self) { // Append just one blank page if no tabs available after last session restore if self.index.borrow().is_empty() { - self.append(None); + self.append(None, false, true); } // @TODO other/child features.. diff --git a/src/app/browser/window/tab/item/page/content/text/gemini/reader.rs b/src/app/browser/window/tab/item/page/content/text/gemini/reader.rs index a33595f8..46a32fae 100644 --- a/src/app/browser/window/tab/item/page/content/text/gemini/reader.rs +++ b/src/app/browser/window/tab/item/page/content/text/gemini/reader.rs @@ -288,7 +288,9 @@ impl Reader { return match uri.scheme().as_str() { "gemini" => { // Open new page in browser - window_action.append().activate(); + window_action + .append() + .activate_stateful_once(None, false, false); } // Scheme not supported, delegate _ => UriLauncher::new(&uri.to_str()).launch(