From 23e4e83e45eeabf2608aeb16936faaa33f672aac Mon Sep 17 00:00:00 2001 From: yggverse Date: Mon, 11 Nov 2024 19:28:39 +0200 Subject: [PATCH] implement new tab position enum --- src/app/browser/window/action.rs | 2 + src/app/browser/window/action/append.rs | 55 +++++++++++++------ src/app/browser/window/tab.rs | 10 ++-- src/app/browser/window/tab/item.rs | 9 ++- .../item/page/content/text/gemini/reader.rs | 6 +- src/app/browser/window/tab/item/widget.rs | 51 +++++++++++------ 6 files changed, 89 insertions(+), 44 deletions(-) diff --git a/src/app/browser/window/action.rs b/src/app/browser/window/action.rs index bbc66cd6..8c52fff4 100644 --- a/src/app/browser/window/action.rs +++ b/src/app/browser/window/action.rs @@ -23,6 +23,8 @@ use gtk::{ }; use std::rc::Rc; +pub use append::Position; // public enum + /// [SimpleActionGroup](https://docs.gtk.org/gio/class.SimpleActionGroup.html) wrapper for `Browser` actions pub struct Action { // Actions diff --git a/src/app/browser/window/action/append.rs b/src/app/browser/window/action/append.rs index ce211f4f..9f75348b 100644 --- a/src/app/browser/window/action/append.rs +++ b/src/app/browser/window/action/append.rs @@ -1,11 +1,34 @@ use gtk::{ gio::SimpleAction, - glib::{uuid_string_random, GString}, + glib::{uuid_string_random, GString, Variant}, prelude::{ActionExt, ToVariant}, }; -/// C-compatible variant type defaults -const DEFAULT_POSITION: i32 = -1; +/// Append options +pub enum Position { + After, + End, + Number(i32), +} + +// C-compatible Position values +const POSITION_AFTER: i32 = -1; +const POSITION_END: i32 = -2; + +// Implement conversion trait +impl ToVariant for Position { + fn to_variant(&self) -> Variant { + match self { + Position::After => &POSITION_AFTER, + Position::End => &POSITION_END, + Position::Number(value) => value, + } + .to_variant() + } +} + +// Action state defaults +const DEFAULT_POSITION: Position = Position::End; const DEFAULT_REQUEST: String = String::new(); const DEFAULT_IS_PINNED: bool = false; const DEFAULT_IS_SELECTED: bool = true; @@ -50,7 +73,7 @@ impl Append { // Set default state self.change_state( - Some(DEFAULT_POSITION), + DEFAULT_POSITION, Some(DEFAULT_REQUEST), DEFAULT_IS_PINNED, DEFAULT_IS_SELECTED, @@ -76,7 +99,7 @@ impl Append { /// * this action reset previous state for action after activation pub fn activate_stateful_once( &self, - position: Option, + position: Position, request: Option, is_pinned: bool, is_selected: bool, @@ -114,7 +137,7 @@ impl Append { /// Emit state change for action pub fn change_state( &self, - position: Option, + position: Position, request: Option, is_pinned: bool, is_selected: bool, @@ -124,11 +147,7 @@ impl Append { self.gobject.change_state( &( // Convert Option to C-based variant value - if let Some(position) = position { - position - } else { - DEFAULT_POSITION - }, + position, if let Some(request) = request { request } else { @@ -150,7 +169,7 @@ impl Append { /// * return `position`,`request`,`is_pinned`,`is_selected`,`is_attention`,`is_load` state as tuple pub fn connect_activate( &self, - callback: impl Fn(Option, Option, bool, bool, bool, bool) + 'static, + callback: impl Fn(Position, Option, bool, bool, bool, bool) + 'static, ) { self.gobject.connect_activate(move |this, _| { let (position, request, is_pinned, is_selected, is_attention, is_load) = state(this); @@ -179,18 +198,18 @@ impl Append { } /// Shared helper to get C-based action state in Optional format -pub fn state(this: &SimpleAction) -> (Option, Option, bool, bool, bool, bool) { +pub fn state(this: &SimpleAction) -> (Position, Option, bool, bool, bool, bool) { let (position, request, is_pinned, is_selected, is_attention, is_load) = this .state() .expect("Expected (`position`,`request`,`is_pinned`,`is_selected`,`is_attention`,`is_load`) state") .get::<(i32, String, bool, bool, bool, bool)>() .expect("Parameter type does not match (`i32`,`String`,`bool`,`bool`,`bool`,`bool`) tuple"); ( - // Convert from C-based variant value to Option - if position == DEFAULT_POSITION { - None - } else { - Some(position) + // Convert from C-based variant value to Position enum + match position { + POSITION_AFTER => Position::After, + POSITION_END => Position::End, + value => Position::Number(value), }, if request.is_empty() { None diff --git a/src/app/browser/window/tab.rs b/src/app/browser/window/tab.rs index 40f1715f..63ee6821 100644 --- a/src/app/browser/window/tab.rs +++ b/src/app/browser/window/tab.rs @@ -8,8 +8,10 @@ use item::Item; use menu::Menu; use widget::Widget; -use crate::app::browser::action::Action as BrowserAction; -use crate::app::browser::window::action::Action as WindowAction; +use crate::app::browser::{ + window::action::{Action as WindowAction, Position}, + Action as BrowserAction, +}; use gtk::{ glib::{GString, Propagation}, prelude::WidgetExt, @@ -103,7 +105,7 @@ impl Tab { // Actions pub fn append( &self, - position: Option, + position: Position, request: Option, is_pinned: bool, is_selected: bool, @@ -321,7 +323,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, None, false, true, false, false); + self.append(Position::End, None, false, true, false, false); } // @TODO other/child features.. diff --git a/src/app/browser/window/tab/item.rs b/src/app/browser/window/tab/item.rs index 2e707224..7fb9f747 100644 --- a/src/app/browser/window/tab/item.rs +++ b/src/app/browser/window/tab/item.rs @@ -8,7 +8,10 @@ use database::Database; use page::Page; use widget::Widget; -use crate::app::browser::{window::Action as WindowAction, Action as BrowserAction}; +use crate::app::browser::{ + window::action::{Action as WindowAction, Position}, + Action as BrowserAction, +}; use adw::TabView; use gtk::{ glib::{uuid_string_random, GString}, @@ -34,7 +37,7 @@ impl Item { browser_action: Rc, window_action: Rc, // Options tuple @TODO struct? - options: (Option, Option, bool, bool, bool, bool), + options: (Position, Option, bool, bool, bool, bool), ) -> Self { // Get item options from tuple let (position, request, is_pinned, is_selected, is_attention, is_load) = options; @@ -150,7 +153,7 @@ impl Item { window_action.clone(), // Options tuple ( - None, + Position::End, None, record.is_pinned, record.is_selected, 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 3323e6ba..6d3e94c8 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 @@ -4,7 +4,9 @@ mod widget; use tag::Tag; use widget::Widget; -use crate::app::browser::window::{tab::item::Action as TabAction, Action as WindowAction}; +use crate::app::browser::window::{ + action::Position, tab::item::Action as TabAction, Action as WindowAction, +}; use adw::StyleManager; use gemtext::line::{ code::Code, @@ -283,7 +285,7 @@ impl Reader { "gemini" => { // Open new page in browser actions.0.append().activate_stateful_once( - None, + Position::After, Some(uri.to_string()), false, false, diff --git a/src/app/browser/window/tab/item/widget.rs b/src/app/browser/window/tab/item/widget.rs index 72489358..0ead6a91 100644 --- a/src/app/browser/window/tab/item/widget.rs +++ b/src/app/browser/window/tab/item/widget.rs @@ -2,6 +2,7 @@ mod database; use database::Database; +use crate::app::browser::window::action::Position; use adw::{TabPage, TabView}; use gtk::prelude::IsA; use sqlite::Transaction; @@ -13,34 +14,32 @@ pub struct Widget { } impl Widget { - // Construct + // Constructors + pub fn new( keyword: &str, // ID tab_view: &TabView, child: &impl IsA, title: Option<&str>, - position: Option, + position: Position, state: (bool, bool, bool), ) -> Self { - let gobject = match position { - Some(value) => { - // If given `position` match pinned tab, GTK will panic with notice: - // adw_tab_view_insert: assertion 'position >= self->n_pinned_pages' - // as the solution, prepend new page after pinned tabs on this case - if value > tab_view.n_pinned_pages() { - tab_view.insert(child, value) - } else { - tab_view.prepend(child) - } - } - None => tab_view.append(child), - }; - + // Define state variables let (is_pinned, is_selected, is_attention) = state; + // Create new `TabPage` GObject in given `TabView` + let gobject = match position { + Position::After => match tab_view.selected_page() { + Some(page) => add(tab_view, child, tab_view.page_position(&page) + 1), + None => tab_view.append(child), + }, + Position::End => tab_view.append(child), + Position::Number(value) => add(tab_view, child, value), + }; + + // Setup `GObject` gobject.set_needs_attention(is_attention); gobject.set_keyword(keyword); - gobject.set_title(match title { Some(value) => value, None => DEFAULT_TITLE, @@ -52,10 +51,12 @@ impl Widget { tab_view.set_selected_page(&gobject); } + // Done Self { gobject } } // Actions + pub fn clean( &self, transaction: &Transaction, @@ -131,12 +132,14 @@ impl Widget { } // Getters + pub fn gobject(&self) -> &TabPage { &self.gobject } } // Tools + pub fn migrate(tx: &Transaction) -> Result<(), String> { // Migrate self components if let Err(e) = Database::init(tx) { @@ -149,3 +152,17 @@ pub fn migrate(tx: &Transaction) -> Result<(), String> { // Success Ok(()) } + +/// Create new [TabPage](https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.TabPage.html) +/// in [TabView](https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.TabView.html) at given position +/// +/// * if given `position` match pinned tab, GTK will panic with notice: +/// adw_tab_view_insert: assertion 'position >= self->n_pinned_pages'\ +/// as the solution, prepend new page after pinned tabs in this case +fn add(tab_view: &TabView, child: &impl IsA, position: i32) -> TabPage { + if position > tab_view.n_pinned_pages() { + tab_view.insert(child, position) + } else { + tab_view.prepend(child) + } +}