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(
&window.widget.g_box,
&[
// Connect action groups (to apply accels)
// action groups
(
// Browser
// browser
&action.id,
action.simple_action_group.clone(),
),
(
// Window
// window
&window.action.id,
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 error;
mod item;
mod menu;
mod widget;
use action::Action;
use error::Error;
pub use item::Item;
use menu::Menu;
@ -27,6 +29,7 @@ pub struct Tab {
window_action: Rc<WindowAction>,
profile: Rc<Profile>,
index: Rc<RefCell<HashMap<Rc<GString>, Rc<Item>>>>,
pub action: Rc<Action>,
pub widget: Rc<Widget>,
}
@ -38,6 +41,8 @@ impl Tab {
profile: &Rc<Profile>,
(browser_action, window_action): (&Rc<BrowserAction>, &Rc<WindowAction>),
) -> Self {
let action = Rc::new(Action::new());
// Init empty HashMap index
let index: Rc<RefCell<HashMap<Rc<GString>, Rc<Item>>>> =
Rc::new(RefCell::new(HashMap::new()));
@ -142,6 +147,7 @@ impl Tab {
window_action: window_action.clone(),
index,
widget,
action,
}
}
@ -160,7 +166,7 @@ impl Tab {
&self.widget.tab_view,
&self.profile,
// Actions
(&self.browser_action, &self.window_action),
(&self.browser_action, &self.window_action, &self.action),
// Options
(
position,
@ -260,19 +266,13 @@ impl Tab {
pub fn page_history_back(&self, page_position: Option<i32>) {
if let Some(item) = self.item(page_position) {
if let Some(back) = item.page.navigation.history.back(true) {
item.page.navigation.request.widget.entry.set_text(&back);
item.client.handle(&back, false);
}
item.action.history.back(true);
}
}
pub fn page_history_forward(&self, page_position: Option<i32>) {
if let Some(item) = self.item(page_position) {
if let Some(forward) = item.page.navigation.history.forward(true) {
item.page.navigation.request.widget.entry.set_text(&forward);
item.client.handle(&forward, false);
}
item.action.history.forward(true);
}
}
@ -338,7 +338,7 @@ impl Tab {
transaction,
record.id,
&self.profile,
(&self.browser_action, &self.window_action),
(&self.browser_action, &self.window_action, &self.action),
) {
Ok(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 gtk::{
glib::{uuid_string_random, GString},
prelude::{Cast, EditableExt},
prelude::{ActionExt, ActionMapExt, Cast, EditableExt},
};
use page::Page;
use sqlite::Transaction;
@ -31,6 +31,7 @@ pub struct Item {
// Components
pub page: Rc<Page>,
pub widget: Rc<Widget>,
pub action: Rc<Action>,
}
impl Item {
@ -40,7 +41,11 @@ impl Item {
pub fn build(
tab_view: &TabView,
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,
Option<String>,
@ -57,10 +62,22 @@ impl Item {
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(
&id,
profile,
(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(
@ -128,6 +145,7 @@ impl Item {
client,
page,
widget,
action,
}
}
@ -169,7 +187,11 @@ impl Item {
app_browser_window_tab_id: i64,
profile: &Rc<Profile>,
// 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> {
let mut items = Vec::new();
@ -181,7 +203,7 @@ impl Item {
tab_view,
profile,
// Actions
(browser_action, window_action),
(browser_action, window_action, item_action),
// Options tuple
(
Position::End,

View File

@ -1,6 +1,8 @@
mod history;
mod ident;
mod load;
use history::History;
use ident::Ident;
use load::Load;
@ -8,6 +10,7 @@ use std::rc::Rc;
/// [SimpleActionGroup](https://docs.gtk.org/gio/class.SimpleActionGroup.html) wrapper for `Browser` actions
pub struct Action {
pub history: Rc<History>,
pub ident: Rc<Ident>,
pub load: Rc<Load>,
}
@ -23,9 +26,18 @@ impl Action {
/// Create 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 {
ident: Rc::new(Ident::new()),
load: Rc::new(Load::new()),
history,
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
Err(uri) => subject
.page
.tab_action
.item_action
.load
.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
if match subject.page.navigation.history.current() {
Some(current) => current != request, // apply additional filters
None => true,
} {
subject.page.navigation.history.add(request, true)
}
subject.page.item_action.history.add(request, true)
}

View File

@ -185,14 +185,14 @@ fn handle(
};
if matches!(response.meta.status, Status::SensitiveInput) {
subject.page.input.set_new_sensitive(
subject.page.tab_action.clone(),
subject.page.item_action.clone(),
uri,
Some(&title),
Some(1024),
);
} else {
subject.page.input.set_new_response(
subject.page.tab_action.clone(),
subject.page.item_action.clone(),
uri,
Some(&title),
Some(1024),
@ -369,7 +369,7 @@ fn handle(
mime => {
let status = subject.page
.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")));
subject.page.navigation.request.widget.entry.set_progress_fraction(0.0);
subject.tab_page.set_loading(false);
@ -443,7 +443,7 @@ fn handle(
.set_text(&uri.to_string());
}
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) => {

View File

@ -13,7 +13,7 @@ use navigation::Navigation;
use search::Search;
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 sqlite::Transaction;
@ -24,7 +24,7 @@ pub struct Page {
pub profile: Rc<Profile>,
// Actions
pub browser_action: Rc<BrowserAction>,
pub tab_action: Rc<TabAction>,
pub item_action: Rc<ItemAction>,
pub window_action: Rc<WindowAction>,
// Components
pub content: Rc<Content>,
@ -40,20 +40,22 @@ impl Page {
pub fn build(
id: &Rc<GString>,
profile: &Rc<Profile>,
(browser_action, window_action, tab_action): (
(browser_action, window_action, item_action): (
&Rc<BrowserAction>,
&Rc<WindowAction>,
&Rc<TabAction>,
&Rc<ItemAction>,
),
(back_action_name, forward_action_name): (&str, &str),
) -> Self {
// 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 navigation = Rc::new(Navigation::build(
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());
@ -72,7 +74,7 @@ impl Page {
profile: profile.clone(),
// Actions
browser_action: browser_action.clone(),
tab_action: tab_action.clone(),
item_action: item_action.clone(),
window_action: window_action.clone(),
// Components
content,

View File

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

View File

@ -4,4 +4,4 @@ pub mod identity;
pub mod loading;
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 gtk::{glib::Uri, prelude::ButtonExt, Align, Button};
use std::rc::Rc;
/// Create new default `GObject` preset for mime issue
/// [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()
.description(format!("Content type `{mime}` not supported!"))
.icon_name("dialog-question-symbolic")

View File

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

View File

@ -4,7 +4,7 @@ mod widget;
use reader::Reader;
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 std::rc::Rc;
@ -18,11 +18,11 @@ impl Gemini {
pub fn new(
gemtext: &str,
base: &Uri,
(window_action, tab_action): (&Rc<WindowAction>, &Rc<TabAction>),
(window_action, item_action): (&Rc<WindowAction>, &Rc<ItemAction>),
) -> Self {
// Init components
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
let widget = Rc::new(Widget::new(&reader.widget.text_view));

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,107 +1,26 @@
mod back;
mod forward;
mod widget;
pub mod back;
pub mod forward;
use back::Back;
use forward::Forward;
use widget::Widget;
pub use back::Back;
pub use forward::Forward;
use super::WindowAction;
use gtk::{glib::GString, Button};
use std::{cell::RefCell, rc::Rc};
use gtk::{prelude::BoxExt, Box, Button, Orientation};
struct Memory {
request: GString,
// time: SystemTime,
pub trait History {
fn history(back_action_name: &str, forward_action_name: &str) -> Self;
}
pub struct History {
// Extras
memory: RefCell<Vec<Memory>>,
index: RefCell<Option<usize>>,
// GTK
pub widget: Rc<Widget>,
}
impl History for Box {
fn history(back_action_name: &str, forward_action_name: &str) -> Self {
let g_box = Box::builder()
.orientation(Orientation::Horizontal)
.css_classes([
"linked", // merge childs
])
.build();
impl History {
// Constructors
/// 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
g_box.append(&Button::back(back_action_name));
g_box.append(&Button::forward(forward_action_name));
g_box
}
}

View File

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

View File

@ -1,19 +1,13 @@
use super::WindowAction;
use gtk::{prelude::ActionExt, Button};
use std::rc::Rc;
use gtk::Button;
pub trait Forward {
fn forward(action: &Rc<WindowAction>) -> Self;
fn forward(action_name: &str) -> Self;
}
impl Forward for Button {
fn forward(action: &Rc<WindowAction>) -> Self {
fn forward(action_name: &str) -> Self {
Button::builder()
.action_name(format!(
"{}.{}",
action.id,
action.history_back.simple_action.name()
)) // @TODO
.action_name(action_name)
.icon_name("go-next-symbolic")
.tooltip_text("Forward")
.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 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::{
glib::{gformat, GString, Uri, UriFlags},
prelude::EditableExt,
@ -21,9 +21,9 @@ impl Request {
// Constructors
/// 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 {
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 super::{BrowserAction, TabAction};
use super::{BrowserAction, ItemAction};
use gtk::{
glib::{timeout_add_local, ControlFlow, SourceId},
prelude::{EditableExt, EntryExt, WidgetExt},
@ -36,7 +36,7 @@ impl Widget {
// Constructors
/// 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
let progress = Rc::new(Progress {
fraction: RefCell::new(0.0),
@ -52,10 +52,10 @@ impl Widget {
// Connect events
entry.connect_icon_release({
let tab_action = tab_action.clone();
let item_action = item_action.clone();
move |this, position| match position {
EntryIconPosition::Primary => tab_action.ident.activate(), // @TODO PrimaryIcon impl
EntryIconPosition::Secondary => tab_action.load.activate(Some(&this.text()), true),
EntryIconPosition::Primary => item_action.ident.activate(), // @TODO PrimaryIcon impl
EntryIconPosition::Secondary => item_action.load.activate(Some(&this.text()), true),
_ => todo!(), // unexpected
}
});
@ -77,9 +77,9 @@ impl Widget {
});
entry.connect_activate({
let tab_action = tab_action.clone();
let item_action = item_action.clone();
move |entry| {
tab_action.load.activate(Some(&entry.text()), true);
item_action.load.activate(Some(&entry.text()), true);
}
});