add new tab item action group, delegate history handle to action implementation

This commit is contained in:
yggverse 2025-01-25 17:28:05 +02:00
parent 5145a53bfa
commit 913030a955
29 changed files with 409 additions and 232 deletions

View File

@ -37,17 +37,22 @@ impl Browser {
let widget = Rc::new(Widget::new( let widget = Rc::new(Widget::new(
&window.widget.g_box, &window.widget.g_box,
&[ &[
// Connect action groups (to apply accels) // action groups
( (
// Browser // browser
&action.id, &action.id,
action.simple_action_group.clone(), action.simple_action_group.clone(),
), ),
( (
// Window // window
&window.action.id, &window.action.id,
window.action.simple_action_group.clone(), window.action.simple_action_group.clone(),
), ),
(
// tab
&window.tab.action.id,
window.tab.action.simple_action_group.clone(),
),
], ],
)); ));

View File

@ -1,9 +1,11 @@
mod action;
mod database; mod database;
mod error; mod error;
mod item; mod item;
mod menu; mod menu;
mod widget; mod widget;
use action::Action;
use error::Error; use error::Error;
pub use item::Item; pub use item::Item;
use menu::Menu; use menu::Menu;
@ -27,6 +29,7 @@ pub struct Tab {
window_action: Rc<WindowAction>, window_action: Rc<WindowAction>,
profile: Rc<Profile>, profile: Rc<Profile>,
index: Rc<RefCell<HashMap<Rc<GString>, Rc<Item>>>>, index: Rc<RefCell<HashMap<Rc<GString>, Rc<Item>>>>,
pub action: Rc<Action>,
pub widget: Rc<Widget>, pub widget: Rc<Widget>,
} }
@ -38,6 +41,8 @@ impl Tab {
profile: &Rc<Profile>, profile: &Rc<Profile>,
(browser_action, window_action): (&Rc<BrowserAction>, &Rc<WindowAction>), (browser_action, window_action): (&Rc<BrowserAction>, &Rc<WindowAction>),
) -> Self { ) -> Self {
let action = Rc::new(Action::new());
// Init empty HashMap index // Init empty HashMap index
let index: Rc<RefCell<HashMap<Rc<GString>, Rc<Item>>>> = let index: Rc<RefCell<HashMap<Rc<GString>, Rc<Item>>>> =
Rc::new(RefCell::new(HashMap::new())); Rc::new(RefCell::new(HashMap::new()));
@ -142,6 +147,7 @@ impl Tab {
window_action: window_action.clone(), window_action: window_action.clone(),
index, index,
widget, widget,
action,
} }
} }
@ -160,7 +166,7 @@ impl Tab {
&self.widget.tab_view, &self.widget.tab_view,
&self.profile, &self.profile,
// Actions // Actions
(&self.browser_action, &self.window_action), (&self.browser_action, &self.window_action, &self.action),
// Options // Options
( (
position, position,
@ -260,19 +266,13 @@ impl Tab {
pub fn page_history_back(&self, page_position: Option<i32>) { pub fn page_history_back(&self, page_position: Option<i32>) {
if let Some(item) = self.item(page_position) { if let Some(item) = self.item(page_position) {
if let Some(back) = item.page.navigation.history.back(true) { item.action.history.back(true);
item.page.navigation.request.widget.entry.set_text(&back);
item.client.handle(&back, false);
}
} }
} }
pub fn page_history_forward(&self, page_position: Option<i32>) { pub fn page_history_forward(&self, page_position: Option<i32>) {
if let Some(item) = self.item(page_position) { if let Some(item) = self.item(page_position) {
if let Some(forward) = item.page.navigation.history.forward(true) { item.action.history.forward(true);
item.page.navigation.request.widget.entry.set_text(&forward);
item.client.handle(&forward, false);
}
} }
} }
@ -338,7 +338,7 @@ impl Tab {
transaction, transaction,
record.id, record.id,
&self.profile, &self.profile,
(&self.browser_action, &self.window_action), (&self.browser_action, &self.window_action, &self.action),
) { ) {
Ok(items) => { Ok(items) => {
for item in items { for item in items {

View File

@ -0,0 +1,25 @@
use gtk::{
gio::SimpleActionGroup,
glib::{uuid_string_random, GString},
};
/// [SimpleActionGroup](https://docs.gtk.org/gio/class.SimpleActionGroup.html) wrapper for `Tab` actions
pub struct Action {
pub id: GString,
pub simple_action_group: SimpleActionGroup,
}
impl Default for Action {
fn default() -> Self {
Self::new()
}
}
impl Action {
pub fn new() -> Self {
Self {
id: uuid_string_random(),
simple_action_group: SimpleActionGroup::new(),
}
}
}

View File

@ -15,7 +15,7 @@ use adw::TabView;
use client::Client; use client::Client;
use gtk::{ use gtk::{
glib::{uuid_string_random, GString}, glib::{uuid_string_random, GString},
prelude::{Cast, EditableExt}, prelude::{ActionExt, ActionMapExt, Cast, EditableExt},
}; };
use page::Page; use page::Page;
use sqlite::Transaction; use sqlite::Transaction;
@ -31,6 +31,7 @@ pub struct Item {
// Components // Components
pub page: Rc<Page>, pub page: Rc<Page>,
pub widget: Rc<Widget>, pub widget: Rc<Widget>,
pub action: Rc<Action>,
} }
impl Item { impl Item {
@ -40,7 +41,11 @@ impl Item {
pub fn build( pub fn build(
tab_view: &TabView, tab_view: &TabView,
profile: &Rc<Profile>, profile: &Rc<Profile>,
(browser_action, window_action): (&Rc<BrowserAction>, &Rc<WindowAction>), (browser_action, window_action, tab_action): (
&Rc<BrowserAction>,
&Rc<WindowAction>,
&Rc<super::Action>,
),
(position, request, is_pinned, is_selected, is_attention, is_load): ( (position, request, is_pinned, is_selected, is_attention, is_load): (
Position, Position,
Option<String>, Option<String>,
@ -57,10 +62,22 @@ impl Item {
let action = Rc::new(Action::new()); let action = Rc::new(Action::new());
tab_action
.simple_action_group
.add_action(&action.history.back);
tab_action
.simple_action_group
.add_action(&action.history.forward);
let page = Rc::new(Page::build( let page = Rc::new(Page::build(
&id, &id,
profile, profile,
(browser_action, window_action, &action), (browser_action, window_action, &action),
(
&format!("{}.{}", &tab_action.id, action.history.back.name()),
&format!("{}.{}", &tab_action.id, action.history.forward.name()),
),
)); ));
let widget = Rc::new(Widget::build( let widget = Rc::new(Widget::build(
@ -128,6 +145,7 @@ impl Item {
client, client,
page, page,
widget, widget,
action,
} }
} }
@ -169,7 +187,11 @@ impl Item {
app_browser_window_tab_id: i64, app_browser_window_tab_id: i64,
profile: &Rc<Profile>, profile: &Rc<Profile>,
// Actions // Actions
(browser_action, window_action): (&Rc<BrowserAction>, &Rc<WindowAction>), (browser_action, window_action, item_action): (
&Rc<BrowserAction>,
&Rc<WindowAction>,
&Rc<super::Action>,
),
) -> Result<Vec<Rc<Item>>, String> { ) -> Result<Vec<Rc<Item>>, String> {
let mut items = Vec::new(); let mut items = Vec::new();
@ -181,7 +203,7 @@ impl Item {
tab_view, tab_view,
profile, profile,
// Actions // Actions
(browser_action, window_action), (browser_action, window_action, item_action),
// Options tuple // Options tuple
( (
Position::End, Position::End,

View File

@ -1,6 +1,8 @@
mod history;
mod ident; mod ident;
mod load; mod load;
use history::History;
use ident::Ident; use ident::Ident;
use load::Load; use load::Load;
@ -8,6 +10,7 @@ use std::rc::Rc;
/// [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 {
pub history: Rc<History>,
pub ident: Rc<Ident>, pub ident: Rc<Ident>,
pub load: Rc<Load>, pub load: Rc<Load>,
} }
@ -23,9 +26,18 @@ impl Action {
/// Create new `Self` /// Create new `Self`
pub fn new() -> Self { pub fn new() -> Self {
let ident = Rc::new(Ident::new());
let load = Rc::new(Load::new());
let history = Rc::new(History::build({
let load = load.clone();
move |request| load.activate(Some(&request), false)
}));
Self { Self {
ident: Rc::new(Ident::new()), history,
load: Rc::new(Load::new()), ident,
load,
} }
} }
} }

View File

@ -0,0 +1,79 @@
mod back;
mod forward;
mod memory;
use back::Back;
use forward::Forward;
use gtk::{gio::SimpleAction, glib::GString};
use memory::Memory;
use std::rc::Rc;
pub struct History {
memory: Rc<Memory>,
pub back: SimpleAction,
pub forward: SimpleAction,
}
impl History {
// Constructors
/// Build new activated `Self`
pub fn build(callback: impl Fn(GString) + 'static) -> Self {
// Init childs
let memory = Rc::new(Memory::new());
let back = SimpleAction::back();
let forward = SimpleAction::forward();
// Init events
let callback = Rc::new(callback);
back.connect_activate({
let callback = callback.clone();
let forward = forward.clone();
let memory = memory.clone();
move |this, _| {
if let Some(request) = memory.back(true) {
callback(request)
}
forward.set_enabled(memory.next(false).is_some());
this.set_enabled(memory.back(false).is_some());
}
});
forward.connect_activate({
let back = back.clone();
let callback = callback.clone();
let memory = memory.clone();
move |this, _| {
if let Some(request) = memory.next(true) {
callback(request)
}
back.set_enabled(memory.back(false).is_some());
this.set_enabled(memory.next(false).is_some());
}
});
// Done
Self {
memory,
back,
forward,
}
}
// Actions
pub fn add(&self, request: GString, follow_to_index: bool) {
self.memory.add(request, follow_to_index);
self.back.set_enabled(self.back(false).is_some());
self.forward.set_enabled(self.forward(false).is_some());
}
pub fn back(&self, follow_to_index: bool) -> Option<GString> {
self.memory.back(follow_to_index)
}
pub fn forward(&self, follow_to_index: bool) -> Option<GString> {
self.memory.next(follow_to_index)
}
}

View File

@ -0,0 +1,13 @@
use gtk::{gio::SimpleAction, glib::uuid_string_random};
pub trait Back {
fn back() -> Self;
}
impl Back for SimpleAction {
fn back() -> Self {
let back = SimpleAction::new(&uuid_string_random(), None);
back.set_enabled(false);
back
}
}

View File

@ -0,0 +1,13 @@
use gtk::{gio::SimpleAction, glib::uuid_string_random};
pub trait Forward {
fn forward() -> Self;
}
impl Forward for SimpleAction {
fn forward() -> Self {
let forward = SimpleAction::new(&uuid_string_random(), None);
forward.set_enabled(false);
forward
}
}

View File

@ -0,0 +1,68 @@
mod cursor;
use cursor::Cursor;
use gtk::glib::GString;
use std::cell::RefCell;
pub struct Memory {
cursor: RefCell<Cursor>,
index: RefCell<Vec<GString>>,
}
impl Memory {
// Constructors
pub fn new() -> Self {
Self {
cursor: RefCell::new(Cursor::new()),
index: RefCell::new(Vec::new()),
}
}
// Actions
pub fn add(&self, value: GString, follow_to_index: bool) {
let mut index = self.index.borrow_mut();
match index.last() {
Some(last) => {
if *last != value {
index.push(value);
}
}
None => index.push(value),
}
if follow_to_index {
self.cursor.borrow_mut().go_last(index.len());
}
}
pub fn back(&self, follow_to_index: bool) -> Option<GString> {
let index = self.index.borrow();
let len = index.len();
match if follow_to_index {
self.cursor.borrow_mut().go_back(len)
} else {
self.cursor.borrow().back(len)
} {
Some(i) => index.get(i).cloned(),
None => None,
}
}
pub fn next(&self, follow_to_index: bool) -> Option<GString> {
let index = self.index.borrow();
let len = index.len();
match if follow_to_index {
self.cursor.borrow_mut().go_next(len)
} else {
self.cursor.borrow().next(len)
} {
Some(i) => index.get(i).cloned(),
None => None,
}
}
}

View File

@ -0,0 +1,66 @@
pub struct Cursor(Option<usize>);
impl Default for Cursor {
fn default() -> Self {
Self::new()
}
}
impl Cursor {
// Constructors
pub fn new() -> Self {
Self(None)
}
// Actions
pub fn go_last(&mut self, len: usize) -> Option<usize> {
self.0 = len2i(len);
self.0
}
pub fn go_next(&mut self, len: usize) -> Option<usize> {
self.0 = self.next(len);
self.0
}
pub fn go_back(&mut self, len: usize) -> Option<usize> {
self.0 = self.back(len);
self.0
}
// Getters
pub fn next(&self, len: usize) -> Option<usize> {
let i = len2i(len)?;
let n = self.0.unwrap_or_default();
if n < i {
Some(n + 1)
} else {
None
}
}
pub fn back(&self, len: usize) -> Option<usize> {
len2i(len)?;
let n = self.0.unwrap_or_default();
if n > 0 {
Some(n - 1)
} else {
None
}
}
}
// Tools
fn len2i(len: usize) -> Option<usize> {
if len > 0 {
Some(len - 1)
} else {
None
}
}

View File

@ -105,7 +105,7 @@ impl Client {
// begin redirection to new address suggested // begin redirection to new address suggested
Err(uri) => subject Err(uri) => subject
.page .page
.tab_action .item_action
.load .load
.activate(Some(&uri.to_string()), false), .activate(Some(&uri.to_string()), false),
} }
@ -215,10 +215,5 @@ fn snap_history(subject: &Rc<Subject>, uri: Option<&Uri>) {
} }
// Add new record into the page navigation history // Add new record into the page navigation history
if match subject.page.navigation.history.current() { subject.page.item_action.history.add(request, true)
Some(current) => current != request, // apply additional filters
None => true,
} {
subject.page.navigation.history.add(request, true)
}
} }

View File

@ -185,14 +185,14 @@ fn handle(
}; };
if matches!(response.meta.status, Status::SensitiveInput) { if matches!(response.meta.status, Status::SensitiveInput) {
subject.page.input.set_new_sensitive( subject.page.input.set_new_sensitive(
subject.page.tab_action.clone(), subject.page.item_action.clone(),
uri, uri,
Some(&title), Some(&title),
Some(1024), Some(1024),
); );
} else { } else {
subject.page.input.set_new_response( subject.page.input.set_new_response(
subject.page.tab_action.clone(), subject.page.item_action.clone(),
uri, uri,
Some(&title), Some(&title),
Some(1024), Some(1024),
@ -369,7 +369,7 @@ fn handle(
mime => { mime => {
let status = subject.page let status = subject.page
.content .content
.to_status_mime(mime, Some((&subject.page.tab_action, &uri))); .to_status_mime(mime, Some((&subject.page.item_action, &uri)));
status.set_description(Some(&format!("Content type `{mime}` yet not supported"))); status.set_description(Some(&format!("Content type `{mime}` yet not supported")));
subject.page.navigation.request.widget.entry.set_progress_fraction(0.0); subject.page.navigation.request.widget.entry.set_progress_fraction(0.0);
subject.tab_page.set_loading(false); subject.tab_page.set_loading(false);
@ -443,7 +443,7 @@ fn handle(
.set_text(&uri.to_string()); .set_text(&uri.to_string());
} }
redirects.replace(total); redirects.replace(total);
subject.page.tab_action.load.activate(Some(&target.to_string()), false); subject.page.item_action.load.activate(Some(&target.to_string()), false);
} }
} }
Err(e) => { Err(e) => {

View File

@ -13,7 +13,7 @@ use navigation::Navigation;
use search::Search; use search::Search;
use widget::Widget; use widget::Widget;
use super::{Action as TabAction, BrowserAction, Profile, WindowAction}; use super::{Action as ItemAction, BrowserAction, Profile, WindowAction};
use gtk::{glib::GString, prelude::EditableExt}; use gtk::{glib::GString, prelude::EditableExt};
use sqlite::Transaction; use sqlite::Transaction;
@ -24,7 +24,7 @@ pub struct Page {
pub profile: Rc<Profile>, pub profile: Rc<Profile>,
// Actions // Actions
pub browser_action: Rc<BrowserAction>, pub browser_action: Rc<BrowserAction>,
pub tab_action: Rc<TabAction>, pub item_action: Rc<ItemAction>,
pub window_action: Rc<WindowAction>, pub window_action: Rc<WindowAction>,
// Components // Components
pub content: Rc<Content>, pub content: Rc<Content>,
@ -40,20 +40,22 @@ impl Page {
pub fn build( pub fn build(
id: &Rc<GString>, id: &Rc<GString>,
profile: &Rc<Profile>, profile: &Rc<Profile>,
(browser_action, window_action, tab_action): ( (browser_action, window_action, item_action): (
&Rc<BrowserAction>, &Rc<BrowserAction>,
&Rc<WindowAction>, &Rc<WindowAction>,
&Rc<TabAction>, &Rc<ItemAction>,
), ),
(back_action_name, forward_action_name): (&str, &str),
) -> Self { ) -> Self {
// Init components // Init components
let content = Rc::new(Content::build((window_action, tab_action))); let content = Rc::new(Content::build((window_action, item_action)));
let search = Rc::new(Search::new()); let search = Rc::new(Search::new());
let navigation = Rc::new(Navigation::build( let navigation = Rc::new(Navigation::build(
profile, profile,
(browser_action, window_action, tab_action), (browser_action, window_action, item_action),
(back_action_name, forward_action_name),
)); ));
let input = Rc::new(Input::new()); let input = Rc::new(Input::new());
@ -72,7 +74,7 @@ impl Page {
profile: profile.clone(), profile: profile.clone(),
// Actions // Actions
browser_action: browser_action.clone(), browser_action: browser_action.clone(),
tab_action: tab_action.clone(), item_action: item_action.clone(),
window_action: window_action.clone(), window_action: window_action.clone(),
// Components // Components
content, content,

View File

@ -5,7 +5,7 @@ mod text;
use image::Image; use image::Image;
use text::Text; use text::Text;
use super::{TabAction, WindowAction}; use super::{ItemAction, WindowAction};
use adw::StatusPage; use adw::StatusPage;
use gtk::{ use gtk::{
gdk::Paintable, gdk::Paintable,
@ -18,7 +18,7 @@ use std::{rc::Rc, time::Duration};
pub struct Content { pub struct Content {
window_action: Rc<WindowAction>, window_action: Rc<WindowAction>,
tab_action: Rc<TabAction>, item_action: Rc<ItemAction>,
pub g_box: Box, pub g_box: Box,
} }
@ -26,11 +26,11 @@ impl Content {
// Construct // Construct
/// Create new container for different components /// Create new container for different components
pub fn build((window_action, tab_action): (&Rc<WindowAction>, &Rc<TabAction>)) -> Self { pub fn build((window_action, item_action): (&Rc<WindowAction>, &Rc<ItemAction>)) -> Self {
Self { Self {
g_box: Box::builder().orientation(Orientation::Vertical).build(), g_box: Box::builder().orientation(Orientation::Vertical).build(),
window_action: window_action.clone(), window_action: window_action.clone(),
tab_action: tab_action.clone(), item_action: item_action.clone(),
} }
} }
@ -77,7 +77,7 @@ impl Content {
pub fn to_status_mime( pub fn to_status_mime(
&self, &self,
mime: &str, mime: &str,
download: Option<(&Rc<TabAction>, &Uri)>, download: Option<(&Rc<ItemAction>, &Uri)>,
) -> StatusPage { ) -> StatusPage {
self.clean(); self.clean();
let status = status::mime::build(mime, download); let status = status::mime::build(mime, download);
@ -90,7 +90,7 @@ impl Content {
/// * action removes previous children component from `Self` /// * action removes previous children component from `Self`
pub fn to_status_identity(&self) -> StatusPage { pub fn to_status_identity(&self) -> StatusPage {
self.clean(); self.clean();
let status = status::identity::build(self.tab_action.clone()); let status = status::identity::build(self.item_action.clone());
self.g_box.append(&status); self.g_box.append(&status);
status status
} }
@ -122,7 +122,7 @@ impl Content {
/// * could be useful to extract document title parsed from Gemtext /// * could be useful to extract document title parsed from Gemtext
pub fn to_text_gemini(&self, base: &Uri, data: &str) -> Text { pub fn to_text_gemini(&self, base: &Uri, data: &str) -> Text {
self.clean(); self.clean();
let text = Text::new_gemini(data, base, (&self.window_action, &self.tab_action)); let text = Text::new_gemini(data, base, (&self.window_action, &self.item_action));
self.g_box.append(&text.g_box); self.g_box.append(&text.g_box);
text text
} }

View File

@ -4,4 +4,4 @@ pub mod identity;
pub mod loading; pub mod loading;
pub mod mime; pub mod mime;
use super::TabAction; use super::ItemAction;

View File

@ -1,11 +1,11 @@
use super::TabAction; use super::ItemAction;
use adw::StatusPage; use adw::StatusPage;
use gtk::{glib::Uri, prelude::ButtonExt, Align, Button}; use gtk::{glib::Uri, prelude::ButtonExt, Align, Button};
use std::rc::Rc; use std::rc::Rc;
/// Create new default `GObject` preset for mime issue /// Create new default `GObject` preset for mime issue
/// [StatusPage](https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.StatusPage.html) /// [StatusPage](https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.StatusPage.html)
pub fn build(mime: &str, download: Option<(&Rc<TabAction>, &Uri)>) -> StatusPage { pub fn build(mime: &str, download: Option<(&Rc<ItemAction>, &Uri)>) -> StatusPage {
let status_page = StatusPage::builder() let status_page = StatusPage::builder()
.description(format!("Content type `{mime}` not supported!")) .description(format!("Content type `{mime}` not supported!"))
.icon_name("dialog-question-symbolic") .icon_name("dialog-question-symbolic")

View File

@ -4,7 +4,7 @@ mod source;
use gemini::Gemini; use gemini::Gemini;
use source::Source; use source::Source;
use super::{TabAction, WindowAction}; use super::{ItemAction, WindowAction};
use gtk::{ use gtk::{
glib::Uri, glib::Uri,
prelude::{BoxExt, Cast}, prelude::{BoxExt, Cast},
@ -28,10 +28,10 @@ impl Text {
pub fn new_gemini( pub fn new_gemini(
gemtext: &str, gemtext: &str,
base: &Uri, base: &Uri,
(window_action, tab_action): (&Rc<WindowAction>, &Rc<TabAction>), (window_action, item_action): (&Rc<WindowAction>, &Rc<ItemAction>),
) -> Self { ) -> Self {
// Init components // Init components
let gemini = Gemini::new(gemtext, base, (window_action, tab_action)); let gemini = Gemini::new(gemtext, base, (window_action, item_action));
// Init main widget // Init main widget
let g_box = Box::builder().orientation(Orientation::Vertical).build(); let g_box = Box::builder().orientation(Orientation::Vertical).build();

View File

@ -4,7 +4,7 @@ mod widget;
use reader::Reader; use reader::Reader;
use widget::Widget; use widget::Widget;
use crate::app::browser::window::{tab::item::Action as TabAction, Action as WindowAction}; use crate::app::browser::window::{tab::item::Action as ItemAction, Action as WindowAction};
use gtk::glib::Uri; use gtk::glib::Uri;
use std::rc::Rc; use std::rc::Rc;
@ -18,11 +18,11 @@ impl Gemini {
pub fn new( pub fn new(
gemtext: &str, gemtext: &str,
base: &Uri, base: &Uri,
(window_action, tab_action): (&Rc<WindowAction>, &Rc<TabAction>), (window_action, item_action): (&Rc<WindowAction>, &Rc<ItemAction>),
) -> Self { ) -> Self {
// Init components // Init components
let reader = Rc::new( let reader = Rc::new(
Reader::new(gemtext, base, (window_action.clone(), tab_action.clone())).unwrap(), Reader::new(gemtext, base, (window_action.clone(), item_action.clone())).unwrap(),
); // @TODO handle errors ); // @TODO handle errors
let widget = Rc::new(Widget::new(&reader.widget.text_view)); let widget = Rc::new(Widget::new(&reader.widget.text_view));

View File

@ -9,7 +9,7 @@ use syntax::Syntax;
use tag::Tag; use tag::Tag;
use widget::Widget; use widget::Widget;
use super::{TabAction, WindowAction}; use super::{ItemAction, WindowAction};
use crate::app::browser::window::action::Position; use crate::app::browser::window::action::Position;
use ggemtext::line::{ use ggemtext::line::{
code::{Inline, Multiline}, code::{Inline, Multiline},
@ -43,7 +43,7 @@ impl Reader {
pub fn new( pub fn new(
gemtext: &str, gemtext: &str,
base: &Uri, base: &Uri,
(window_action, tab_action): (Rc<WindowAction>, Rc<TabAction>), (window_action, item_action): (Rc<WindowAction>, Rc<ItemAction>),
) -> Result<Self, Error> { ) -> Result<Self, Error> {
// Init default values // Init default values
let mut title = None; let mut title = None;
@ -332,7 +332,7 @@ impl Reader {
return match uri.scheme().as_str() { return match uri.scheme().as_str() {
"gemini" | "titan" => { "gemini" | "titan" => {
// Open new page in browser // Open new page in browser
tab_action.load.activate(Some(&uri.to_str()), true); item_action.load.activate(Some(&uri.to_str()), true);
} }
// Scheme not supported, delegate // Scheme not supported, delegate
_ => UriLauncher::new(&uri.to_str()).launch( _ => UriLauncher::new(&uri.to_str()).launch(

View File

@ -2,7 +2,7 @@ mod response;
mod sensitive; mod sensitive;
mod titan; mod titan;
use super::TabAction; use super::ItemAction;
use adw::Clamp; use adw::Clamp;
use gtk::{glib::Uri, prelude::WidgetExt, Box, Label}; use gtk::{glib::Uri, prelude::WidgetExt, Box, Label};
use response::Response; use response::Response;
@ -52,7 +52,7 @@ impl Input {
// Setters // Setters
pub fn set_new_response( pub fn set_new_response(
&self, &self,
action: Rc<TabAction>, action: Rc<ItemAction>,
base: Uri, base: Uri,
title: Option<&str>, title: Option<&str>,
size_limit: Option<usize>, size_limit: Option<usize>,
@ -64,7 +64,7 @@ impl Input {
pub fn set_new_sensitive( pub fn set_new_sensitive(
&self, &self,
action: Rc<TabAction>, action: Rc<ItemAction>,
base: Uri, base: Uri,
title: Option<&str>, title: Option<&str>,
max_length: Option<i32>, max_length: Option<i32>,

View File

@ -6,7 +6,7 @@ use control::Control;
use form::Form; use form::Form;
use title::Title; use title::Title;
use super::TabAction; use super::ItemAction;
use gtk::{ use gtk::{
gio::SimpleAction, gio::SimpleAction,
glib::{uuid_string_random, Uri, UriHideFlags}, glib::{uuid_string_random, Uri, UriHideFlags},
@ -28,7 +28,7 @@ impl Response {
/// Build new `Self` /// Build new `Self`
pub fn build( pub fn build(
tab_action: Rc<TabAction>, item_action: Rc<ItemAction>,
base: Uri, base: Uri,
title: Option<&str>, title: Option<&str>,
size_limit: Option<usize>, size_limit: Option<usize>,
@ -77,7 +77,7 @@ impl Response {
action_send.connect_activate({ action_send.connect_activate({
let form = form.clone(); let form = form.clone();
move |_, _| { move |_, _| {
tab_action.load.activate( item_action.load.activate(
Some(&format!( Some(&format!(
"{}?{}", "{}?{}",
base.to_string_partial(UriHideFlags::QUERY), base.to_string_partial(UriHideFlags::QUERY),

View File

@ -1,6 +1,6 @@
mod form; mod form;
use super::TabAction; use super::ItemAction;
use form::Form; use form::Form;
use gtk::{ use gtk::{
gio::SimpleAction, gio::SimpleAction,
@ -22,7 +22,7 @@ impl Sensitive {
/// Build new `Self` /// Build new `Self`
pub fn build( pub fn build(
tab_action: Rc<TabAction>, item_action: Rc<ItemAction>,
base: Uri, base: Uri,
title: Option<&str>, title: Option<&str>,
max_length: Option<i32>, max_length: Option<i32>,
@ -54,7 +54,7 @@ impl Sensitive {
action_send.connect_activate({ action_send.connect_activate({
let form = form.clone(); let form = form.clone();
move |_, _| { move |_, _| {
tab_action.load.activate( item_action.load.activate(
Some(&format!( Some(&format!(
"{}?{}", "{}?{}",
base.to_string_partial(UriHideFlags::QUERY), base.to_string_partial(UriHideFlags::QUERY),

View File

@ -6,20 +6,18 @@ mod reload;
mod request; mod request;
mod widget; mod widget;
use super::{BrowserAction, ItemAction, Profile, WindowAction};
use bookmark::Bookmark; use bookmark::Bookmark;
use gtk::Button; use gtk::{Box, Button};
use history::History; use history::History;
use home::Home; use home::Home;
use reload::Reload; use reload::Reload;
use request::Request; use request::Request;
use widget::Widget;
use super::{BrowserAction, Profile, TabAction, WindowAction};
use sqlite::Transaction; use sqlite::Transaction;
use std::rc::Rc; use std::rc::Rc;
use widget::Widget;
pub struct Navigation { pub struct Navigation {
pub history: Rc<History>,
pub profile: Rc<Profile>, pub profile: Rc<Profile>,
pub request: Rc<Request>, pub request: Rc<Request>,
pub widget: Rc<Widget>, pub widget: Rc<Widget>,
@ -28,21 +26,21 @@ pub struct Navigation {
impl Navigation { impl Navigation {
pub fn build( pub fn build(
profile: &Rc<Profile>, profile: &Rc<Profile>,
(browser_action, window_action, tab_action): ( (browser_action, window_action, item_action): (
&Rc<BrowserAction>, &Rc<BrowserAction>,
&Rc<WindowAction>, &Rc<WindowAction>,
&Rc<TabAction>, &Rc<ItemAction>,
), ),
(back_action_name, forward_action_name): (&str, &str),
) -> Self { ) -> Self {
// init children components // init children components
let history = Rc::new(History::build(window_action)); let request = Rc::new(Request::build((browser_action, item_action)));
let request = Rc::new(Request::build((browser_action, tab_action)));
// init main widget // init main widget
let widget = Rc::new(Widget::build( let widget = Rc::new(Widget::build(
&Button::home(window_action), &Button::home(window_action),
&history.widget.g_box, // @TODO &Box::history(back_action_name, forward_action_name),
&Button::reload(window_action), &Button::reload(window_action),
&request.widget.entry, // @TODO &request.widget.entry, // @TODO
&Button::bookmark(window_action), &Button::bookmark(window_action),
@ -50,7 +48,6 @@ impl Navigation {
// done // done
Self { Self {
history,
profile: profile.clone(), profile: profile.clone(),
request, request,
widget, widget,

View File

@ -1,107 +1,26 @@
mod back; pub mod back;
mod forward; pub mod forward;
mod widget;
use back::Back; pub use back::Back;
use forward::Forward; pub use forward::Forward;
use widget::Widget;
use super::WindowAction; use gtk::{prelude::BoxExt, Box, Button, Orientation};
use gtk::{glib::GString, Button};
use std::{cell::RefCell, rc::Rc};
struct Memory { pub trait History {
request: GString, fn history(back_action_name: &str, forward_action_name: &str) -> Self;
// time: SystemTime,
} }
pub struct History { impl History for Box {
// Extras fn history(back_action_name: &str, forward_action_name: &str) -> Self {
memory: RefCell<Vec<Memory>>, let g_box = Box::builder()
index: RefCell<Option<usize>>, .orientation(Orientation::Horizontal)
// GTK .css_classes([
pub widget: Rc<Widget>, "linked", // merge childs
} ])
.build();
impl History { g_box.append(&Button::back(back_action_name));
// Constructors g_box.append(&Button::forward(forward_action_name));
g_box
/// Build new `Self`
pub fn build(action: &Rc<WindowAction>) -> Self {
// Init widget
let widget = Rc::new(Widget::build(
&Button::back(action),
&Button::forward(action),
));
// Init memory
let memory = RefCell::new(Vec::new());
// Init index
let index = RefCell::new(None);
Self {
memory,
index,
widget,
}
}
// Actions
pub fn add(&self, request: GString, follow_to_index: bool) {
// Append new Memory record
self.memory.borrow_mut().push(Memory {
request: request.clone(),
//time: SystemTime::now(),
});
if follow_to_index {
// Even push action make positive len value, make sure twice
if !self.memory.borrow().is_empty() {
// Navigate to the last record appended
self.index.replace(Some(self.memory.borrow().len() - 1));
} else {
self.index.replace(None);
}
}
}
pub fn back(&self, follow_to_index: bool) -> Option<GString> {
let index = *self.index.borrow();
if let Some(usize) = index {
// Make sure value positive to prevent panic
if usize > 0 {
if let Some(memory) = self.memory.borrow().get(usize - 1) {
if follow_to_index {
self.index.replace(Some(usize - 1));
}
return Some(memory.request.clone());
}
}
}
None
}
pub fn current(&self) -> Option<GString> {
let index = *self.index.borrow();
if let Some(usize) = index {
if let Some(memory) = self.memory.borrow().get(usize) {
return Some(memory.request.clone());
}
}
None
}
pub fn forward(&self, follow_to_index: bool) -> Option<GString> {
let index = *self.index.borrow();
if let Some(usize) = index {
if let Some(memory) = self.memory.borrow().get(usize + 1) {
if follow_to_index {
self.index.replace(Some(usize + 1));
}
return Some(memory.request.clone());
}
}
None
} }
} }

View File

@ -1,19 +1,13 @@
use super::WindowAction; use gtk::Button;
use gtk::{prelude::ActionExt, Button};
use std::rc::Rc;
pub trait Back { pub trait Back {
fn back(action: &Rc<WindowAction>) -> Self; fn back(action_name: &str) -> Self;
} }
impl Back for Button { impl Back for Button {
fn back(action: &Rc<WindowAction>) -> Self { fn back(action_name: &str) -> Self {
Button::builder() Button::builder()
.action_name(format!( .action_name(action_name)
"{}.{}",
action.id,
action.history_back.simple_action.name()
)) // @TODO
.icon_name("go-previous-symbolic") .icon_name("go-previous-symbolic")
.tooltip_text("Back") .tooltip_text("Back")
.build() .build()

View File

@ -1,19 +1,13 @@
use super::WindowAction; use gtk::Button;
use gtk::{prelude::ActionExt, Button};
use std::rc::Rc;
pub trait Forward { pub trait Forward {
fn forward(action: &Rc<WindowAction>) -> Self; fn forward(action_name: &str) -> Self;
} }
impl Forward for Button { impl Forward for Button {
fn forward(action: &Rc<WindowAction>) -> Self { fn forward(action_name: &str) -> Self {
Button::builder() Button::builder()
.action_name(format!( .action_name(action_name)
"{}.{}",
action.id,
action.history_back.simple_action.name()
)) // @TODO
.icon_name("go-next-symbolic") .icon_name("go-next-symbolic")
.tooltip_text("Forward") .tooltip_text("Forward")
.build() .build()

View File

@ -1,27 +0,0 @@
use gtk::{
prelude::{BoxExt, IsA},
Box, Orientation,
};
pub struct Widget {
pub g_box: Box,
}
impl Widget {
// Constructors
/// Build new `Self`
pub fn build(back: &impl IsA<gtk::Widget>, forward: &impl IsA<gtk::Widget>) -> Self {
let g_box = Box::builder()
.orientation(Orientation::Horizontal)
.css_classes([
"linked", // merge childs
])
.build();
g_box.append(back);
g_box.append(forward);
Self { g_box }
}
}

View File

@ -4,7 +4,7 @@ mod widget;
use widget::Widget; use widget::Widget;
use crate::app::browser::{window::tab::item::Action as TabAction, Action as BrowserAction}; use crate::app::browser::{window::tab::item::Action as ItemAction, Action as BrowserAction};
use gtk::{ use gtk::{
glib::{gformat, GString, Uri, UriFlags}, glib::{gformat, GString, Uri, UriFlags},
prelude::EditableExt, prelude::EditableExt,
@ -21,9 +21,9 @@ impl Request {
// Constructors // Constructors
/// Build new `Self` /// Build new `Self`
pub fn build((browser_action, tab_action): (&Rc<BrowserAction>, &Rc<TabAction>)) -> Self { pub fn build((browser_action, item_action): (&Rc<BrowserAction>, &Rc<ItemAction>)) -> Self {
Self { Self {
widget: Rc::new(Widget::build((browser_action, tab_action))), widget: Rc::new(Widget::build((browser_action, item_action))),
} }
} }

View File

@ -3,7 +3,7 @@ mod primary_icon;
use primary_icon::PrimaryIcon; use primary_icon::PrimaryIcon;
use super::{BrowserAction, TabAction}; use super::{BrowserAction, ItemAction};
use gtk::{ use gtk::{
glib::{timeout_add_local, ControlFlow, SourceId}, glib::{timeout_add_local, ControlFlow, SourceId},
prelude::{EditableExt, EntryExt, WidgetExt}, prelude::{EditableExt, EntryExt, WidgetExt},
@ -36,7 +36,7 @@ impl Widget {
// Constructors // Constructors
/// Build new `Self` /// Build new `Self`
pub fn build((browser_action, tab_action): (&Rc<BrowserAction>, &Rc<TabAction>)) -> Self { pub fn build((browser_action, item_action): (&Rc<BrowserAction>, &Rc<ItemAction>)) -> Self {
// Init animated progress bar state // Init animated progress bar state
let progress = Rc::new(Progress { let progress = Rc::new(Progress {
fraction: RefCell::new(0.0), fraction: RefCell::new(0.0),
@ -52,10 +52,10 @@ impl Widget {
// Connect events // Connect events
entry.connect_icon_release({ entry.connect_icon_release({
let tab_action = tab_action.clone(); let item_action = item_action.clone();
move |this, position| match position { move |this, position| match position {
EntryIconPosition::Primary => tab_action.ident.activate(), // @TODO PrimaryIcon impl EntryIconPosition::Primary => item_action.ident.activate(), // @TODO PrimaryIcon impl
EntryIconPosition::Secondary => tab_action.load.activate(Some(&this.text()), true), EntryIconPosition::Secondary => item_action.load.activate(Some(&this.text()), true),
_ => todo!(), // unexpected _ => todo!(), // unexpected
} }
}); });
@ -77,9 +77,9 @@ impl Widget {
}); });
entry.connect_activate({ entry.connect_activate({
let tab_action = tab_action.clone(); let item_action = item_action.clone();
move |entry| { move |entry| {
tab_action.load.activate(Some(&entry.text()), true); item_action.load.activate(Some(&entry.text()), true);
} }
}); });