mirror of
https://github.com/YGGverse/Yoda.git
synced 2025-01-15 09:10:08 +00:00
implement new tab position enum
This commit is contained in:
parent
57cdc4cee9
commit
23e4e83e45
@ -23,6 +23,8 @@ use gtk::{
|
|||||||
};
|
};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub use append::Position; // public enum
|
||||||
|
|
||||||
/// [SimpleActionGroup](https://docs.gtk.org/gio/class.SimpleActionGroup.html) wrapper for `Browser` actions
|
/// [SimpleActionGroup](https://docs.gtk.org/gio/class.SimpleActionGroup.html) wrapper for `Browser` actions
|
||||||
pub struct Action {
|
pub struct Action {
|
||||||
// Actions
|
// Actions
|
||||||
|
@ -1,11 +1,34 @@
|
|||||||
use gtk::{
|
use gtk::{
|
||||||
gio::SimpleAction,
|
gio::SimpleAction,
|
||||||
glib::{uuid_string_random, GString},
|
glib::{uuid_string_random, GString, Variant},
|
||||||
prelude::{ActionExt, ToVariant},
|
prelude::{ActionExt, ToVariant},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// C-compatible variant type defaults
|
/// Append options
|
||||||
const DEFAULT_POSITION: i32 = -1;
|
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_REQUEST: String = String::new();
|
||||||
const DEFAULT_IS_PINNED: bool = false;
|
const DEFAULT_IS_PINNED: bool = false;
|
||||||
const DEFAULT_IS_SELECTED: bool = true;
|
const DEFAULT_IS_SELECTED: bool = true;
|
||||||
@ -50,7 +73,7 @@ impl Append {
|
|||||||
|
|
||||||
// Set default state
|
// Set default state
|
||||||
self.change_state(
|
self.change_state(
|
||||||
Some(DEFAULT_POSITION),
|
DEFAULT_POSITION,
|
||||||
Some(DEFAULT_REQUEST),
|
Some(DEFAULT_REQUEST),
|
||||||
DEFAULT_IS_PINNED,
|
DEFAULT_IS_PINNED,
|
||||||
DEFAULT_IS_SELECTED,
|
DEFAULT_IS_SELECTED,
|
||||||
@ -76,7 +99,7 @@ impl Append {
|
|||||||
/// * this action reset previous state for action after activation
|
/// * this action reset previous state for action after activation
|
||||||
pub fn activate_stateful_once(
|
pub fn activate_stateful_once(
|
||||||
&self,
|
&self,
|
||||||
position: Option<i32>,
|
position: Position,
|
||||||
request: Option<String>,
|
request: Option<String>,
|
||||||
is_pinned: bool,
|
is_pinned: bool,
|
||||||
is_selected: bool,
|
is_selected: bool,
|
||||||
@ -114,7 +137,7 @@ impl Append {
|
|||||||
/// Emit state change for action
|
/// Emit state change for action
|
||||||
pub fn change_state(
|
pub fn change_state(
|
||||||
&self,
|
&self,
|
||||||
position: Option<i32>,
|
position: Position,
|
||||||
request: Option<String>,
|
request: Option<String>,
|
||||||
is_pinned: bool,
|
is_pinned: bool,
|
||||||
is_selected: bool,
|
is_selected: bool,
|
||||||
@ -124,11 +147,7 @@ impl Append {
|
|||||||
self.gobject.change_state(
|
self.gobject.change_state(
|
||||||
&(
|
&(
|
||||||
// Convert Option to C-based variant value
|
// Convert Option to C-based variant value
|
||||||
if let Some(position) = position {
|
position,
|
||||||
position
|
|
||||||
} else {
|
|
||||||
DEFAULT_POSITION
|
|
||||||
},
|
|
||||||
if let Some(request) = request {
|
if let Some(request) = request {
|
||||||
request
|
request
|
||||||
} else {
|
} else {
|
||||||
@ -150,7 +169,7 @@ impl Append {
|
|||||||
/// * return `position`,`request`,`is_pinned`,`is_selected`,`is_attention`,`is_load` state as tuple
|
/// * return `position`,`request`,`is_pinned`,`is_selected`,`is_attention`,`is_load` state as tuple
|
||||||
pub fn connect_activate(
|
pub fn connect_activate(
|
||||||
&self,
|
&self,
|
||||||
callback: impl Fn(Option<i32>, Option<String>, bool, bool, bool, bool) + 'static,
|
callback: impl Fn(Position, Option<String>, bool, bool, bool, bool) + 'static,
|
||||||
) {
|
) {
|
||||||
self.gobject.connect_activate(move |this, _| {
|
self.gobject.connect_activate(move |this, _| {
|
||||||
let (position, request, is_pinned, is_selected, is_attention, is_load) = state(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
|
/// Shared helper to get C-based action state in Optional format
|
||||||
pub fn state(this: &SimpleAction) -> (Option<i32>, Option<String>, bool, bool, bool, bool) {
|
pub fn state(this: &SimpleAction) -> (Position, Option<String>, bool, bool, bool, bool) {
|
||||||
let (position, request, is_pinned, is_selected, is_attention, is_load) = this
|
let (position, request, is_pinned, is_selected, is_attention, is_load) = this
|
||||||
.state()
|
.state()
|
||||||
.expect("Expected (`position`,`request`,`is_pinned`,`is_selected`,`is_attention`,`is_load`) state")
|
.expect("Expected (`position`,`request`,`is_pinned`,`is_selected`,`is_attention`,`is_load`) state")
|
||||||
.get::<(i32, String, bool, bool, bool, bool)>()
|
.get::<(i32, String, bool, bool, bool, bool)>()
|
||||||
.expect("Parameter type does not match (`i32`,`String`,`bool`,`bool`,`bool`,`bool`) tuple");
|
.expect("Parameter type does not match (`i32`,`String`,`bool`,`bool`,`bool`,`bool`) tuple");
|
||||||
(
|
(
|
||||||
// Convert from C-based variant value to Option
|
// Convert from C-based variant value to Position enum
|
||||||
if position == DEFAULT_POSITION {
|
match position {
|
||||||
None
|
POSITION_AFTER => Position::After,
|
||||||
} else {
|
POSITION_END => Position::End,
|
||||||
Some(position)
|
value => Position::Number(value),
|
||||||
},
|
},
|
||||||
if request.is_empty() {
|
if request.is_empty() {
|
||||||
None
|
None
|
||||||
|
@ -8,8 +8,10 @@ use item::Item;
|
|||||||
use menu::Menu;
|
use menu::Menu;
|
||||||
use widget::Widget;
|
use widget::Widget;
|
||||||
|
|
||||||
use crate::app::browser::action::Action as BrowserAction;
|
use crate::app::browser::{
|
||||||
use crate::app::browser::window::action::Action as WindowAction;
|
window::action::{Action as WindowAction, Position},
|
||||||
|
Action as BrowserAction,
|
||||||
|
};
|
||||||
use gtk::{
|
use gtk::{
|
||||||
glib::{GString, Propagation},
|
glib::{GString, Propagation},
|
||||||
prelude::WidgetExt,
|
prelude::WidgetExt,
|
||||||
@ -103,7 +105,7 @@ impl Tab {
|
|||||||
// Actions
|
// Actions
|
||||||
pub fn append(
|
pub fn append(
|
||||||
&self,
|
&self,
|
||||||
position: Option<i32>,
|
position: Position,
|
||||||
request: Option<String>,
|
request: Option<String>,
|
||||||
is_pinned: bool,
|
is_pinned: bool,
|
||||||
is_selected: bool,
|
is_selected: bool,
|
||||||
@ -321,7 +323,7 @@ impl Tab {
|
|||||||
pub fn init(&self) {
|
pub fn init(&self) {
|
||||||
// Append just one blank page if no tabs available after last session restore
|
// Append just one blank page if no tabs available after last session restore
|
||||||
if self.index.borrow().is_empty() {
|
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..
|
// @TODO other/child features..
|
||||||
|
@ -8,7 +8,10 @@ use database::Database;
|
|||||||
use page::Page;
|
use page::Page;
|
||||||
use widget::Widget;
|
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 adw::TabView;
|
||||||
use gtk::{
|
use gtk::{
|
||||||
glib::{uuid_string_random, GString},
|
glib::{uuid_string_random, GString},
|
||||||
@ -34,7 +37,7 @@ impl Item {
|
|||||||
browser_action: Rc<BrowserAction>,
|
browser_action: Rc<BrowserAction>,
|
||||||
window_action: Rc<WindowAction>,
|
window_action: Rc<WindowAction>,
|
||||||
// Options tuple @TODO struct?
|
// Options tuple @TODO struct?
|
||||||
options: (Option<i32>, Option<String>, bool, bool, bool, bool),
|
options: (Position, Option<String>, bool, bool, bool, bool),
|
||||||
) -> Self {
|
) -> Self {
|
||||||
// Get item options from tuple
|
// Get item options from tuple
|
||||||
let (position, request, is_pinned, is_selected, is_attention, is_load) = options;
|
let (position, request, is_pinned, is_selected, is_attention, is_load) = options;
|
||||||
@ -150,7 +153,7 @@ impl Item {
|
|||||||
window_action.clone(),
|
window_action.clone(),
|
||||||
// Options tuple
|
// Options tuple
|
||||||
(
|
(
|
||||||
None,
|
Position::End,
|
||||||
None,
|
None,
|
||||||
record.is_pinned,
|
record.is_pinned,
|
||||||
record.is_selected,
|
record.is_selected,
|
||||||
|
@ -4,7 +4,9 @@ mod widget;
|
|||||||
use tag::Tag;
|
use tag::Tag;
|
||||||
use widget::Widget;
|
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 adw::StyleManager;
|
||||||
use gemtext::line::{
|
use gemtext::line::{
|
||||||
code::Code,
|
code::Code,
|
||||||
@ -283,7 +285,7 @@ impl Reader {
|
|||||||
"gemini" => {
|
"gemini" => {
|
||||||
// Open new page in browser
|
// Open new page in browser
|
||||||
actions.0.append().activate_stateful_once(
|
actions.0.append().activate_stateful_once(
|
||||||
None,
|
Position::After,
|
||||||
Some(uri.to_string()),
|
Some(uri.to_string()),
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
@ -2,6 +2,7 @@ mod database;
|
|||||||
|
|
||||||
use database::Database;
|
use database::Database;
|
||||||
|
|
||||||
|
use crate::app::browser::window::action::Position;
|
||||||
use adw::{TabPage, TabView};
|
use adw::{TabPage, TabView};
|
||||||
use gtk::prelude::IsA;
|
use gtk::prelude::IsA;
|
||||||
use sqlite::Transaction;
|
use sqlite::Transaction;
|
||||||
@ -13,34 +14,32 @@ pub struct Widget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Widget {
|
impl Widget {
|
||||||
// Construct
|
// Constructors
|
||||||
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
keyword: &str, // ID
|
keyword: &str, // ID
|
||||||
tab_view: &TabView,
|
tab_view: &TabView,
|
||||||
child: &impl IsA<gtk::Widget>,
|
child: &impl IsA<gtk::Widget>,
|
||||||
title: Option<&str>,
|
title: Option<&str>,
|
||||||
position: Option<i32>,
|
position: Position,
|
||||||
state: (bool, bool, bool),
|
state: (bool, bool, bool),
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let gobject = match position {
|
// Define state variables
|
||||||
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),
|
|
||||||
};
|
|
||||||
|
|
||||||
let (is_pinned, is_selected, is_attention) = state;
|
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_needs_attention(is_attention);
|
||||||
gobject.set_keyword(keyword);
|
gobject.set_keyword(keyword);
|
||||||
|
|
||||||
gobject.set_title(match title {
|
gobject.set_title(match title {
|
||||||
Some(value) => value,
|
Some(value) => value,
|
||||||
None => DEFAULT_TITLE,
|
None => DEFAULT_TITLE,
|
||||||
@ -52,10 +51,12 @@ impl Widget {
|
|||||||
tab_view.set_selected_page(&gobject);
|
tab_view.set_selected_page(&gobject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Done
|
||||||
Self { gobject }
|
Self { gobject }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
|
|
||||||
pub fn clean(
|
pub fn clean(
|
||||||
&self,
|
&self,
|
||||||
transaction: &Transaction,
|
transaction: &Transaction,
|
||||||
@ -131,12 +132,14 @@ impl Widget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Getters
|
// Getters
|
||||||
|
|
||||||
pub fn gobject(&self) -> &TabPage {
|
pub fn gobject(&self) -> &TabPage {
|
||||||
&self.gobject
|
&self.gobject
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tools
|
// Tools
|
||||||
|
|
||||||
pub fn migrate(tx: &Transaction) -> Result<(), String> {
|
pub fn migrate(tx: &Transaction) -> Result<(), String> {
|
||||||
// Migrate self components
|
// Migrate self components
|
||||||
if let Err(e) = Database::init(tx) {
|
if let Err(e) = Database::init(tx) {
|
||||||
@ -149,3 +152,17 @@ pub fn migrate(tx: &Transaction) -> Result<(), String> {
|
|||||||
// Success
|
// Success
|
||||||
Ok(())
|
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<gtk::Widget>, position: i32) -> TabPage {
|
||||||
|
if position > tab_view.n_pinned_pages() {
|
||||||
|
tab_view.insert(child, position)
|
||||||
|
} else {
|
||||||
|
tab_view.prepend(child)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user