mirror of
https://github.com/YGGverse/Yoda.git
synced 2026-04-01 09:05:27 +00:00
begin request entry refactory
This commit is contained in:
parent
5255708be3
commit
e7bd5bbdc6
24 changed files with 351 additions and 849 deletions
14
src/app.rs
14
src/app.rs
|
|
@ -32,12 +32,10 @@ impl App {
|
||||||
// Init components
|
// Init components
|
||||||
let browser = Rc::new(Browser::build(profile));
|
let browser = Rc::new(Browser::build(profile));
|
||||||
|
|
||||||
// Init events
|
// Prevent startup warning @TODO
|
||||||
application.connect_activate({
|
application.connect_activate(|_| {});
|
||||||
let browser = browser.clone();
|
|
||||||
move |_| browser.update()
|
|
||||||
});
|
|
||||||
|
|
||||||
|
// Init events
|
||||||
application.connect_startup({
|
application.connect_startup({
|
||||||
let browser = browser.clone();
|
let browser = browser.clone();
|
||||||
let profile = profile.clone();
|
let profile = profile.clone();
|
||||||
|
|
@ -150,11 +148,7 @@ impl App {
|
||||||
&["<Primary>i"],
|
&["<Primary>i"],
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
format!(
|
format!("{}.{}", browser.action.id, browser.action.escape.name()),
|
||||||
"{}.{}",
|
|
||||||
browser.action.id,
|
|
||||||
browser.action.escape.simple_action.name()
|
|
||||||
),
|
|
||||||
&["Escape"],
|
&["Escape"],
|
||||||
),
|
),
|
||||||
// Tab actions
|
// Tab actions
|
||||||
|
|
|
||||||
|
|
@ -77,8 +77,8 @@ impl Browser {
|
||||||
action.escape.connect_activate({
|
action.escape.connect_activate({
|
||||||
let widget = widget.clone();
|
let widget = widget.clone();
|
||||||
let window = window.clone();
|
let window = window.clone();
|
||||||
move |tab_item_id| {
|
move |_, _| {
|
||||||
window.escape(tab_item_id);
|
window.escape();
|
||||||
widget.application_window.set_focus(gtk::Window::NONE);
|
widget.application_window.set_focus(gtk::Window::NONE);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -98,11 +98,6 @@ impl Browser {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
action.update.connect_activate({
|
|
||||||
let window = window.clone();
|
|
||||||
move |tab_item_id| window.update(tab_item_id)
|
|
||||||
});
|
|
||||||
|
|
||||||
// Return new activated `Self`
|
// Return new activated `Self`
|
||||||
Self {
|
Self {
|
||||||
action,
|
action,
|
||||||
|
|
@ -185,10 +180,6 @@ impl Browser {
|
||||||
self.widget.application_window.present();
|
self.widget.application_window.present();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&self) {
|
|
||||||
self.window.update(None);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tools
|
// Tools
|
||||||
|
|
|
||||||
|
|
@ -3,17 +3,15 @@ mod close;
|
||||||
mod debug;
|
mod debug;
|
||||||
mod escape;
|
mod escape;
|
||||||
mod profile;
|
mod profile;
|
||||||
mod update;
|
|
||||||
|
|
||||||
use about::About;
|
use about::About;
|
||||||
use close::Close;
|
use close::Close;
|
||||||
use debug::Debug;
|
use debug::Debug;
|
||||||
use escape::Escape;
|
use escape::Escape;
|
||||||
use profile::Profile;
|
use profile::Profile;
|
||||||
use update::Update;
|
|
||||||
|
|
||||||
use gtk::{
|
use gtk::{
|
||||||
gio::SimpleActionGroup,
|
gio::{SimpleAction, SimpleActionGroup},
|
||||||
glib::{uuid_string_random, GString},
|
glib::{uuid_string_random, GString},
|
||||||
prelude::ActionMapExt,
|
prelude::ActionMapExt,
|
||||||
};
|
};
|
||||||
|
|
@ -25,9 +23,8 @@ pub struct Action {
|
||||||
pub about: Rc<About>,
|
pub about: Rc<About>,
|
||||||
pub close: Rc<Close>,
|
pub close: Rc<Close>,
|
||||||
pub debug: Rc<Debug>,
|
pub debug: Rc<Debug>,
|
||||||
pub escape: Rc<Escape>,
|
pub escape: SimpleAction,
|
||||||
pub profile: Rc<Profile>,
|
pub profile: Rc<Profile>,
|
||||||
pub update: Rc<Update>,
|
|
||||||
// Group
|
// Group
|
||||||
pub id: GString,
|
pub id: GString,
|
||||||
pub simple_action_group: SimpleActionGroup,
|
pub simple_action_group: SimpleActionGroup,
|
||||||
|
|
@ -48,9 +45,8 @@ impl Action {
|
||||||
let about = Rc::new(About::new());
|
let about = Rc::new(About::new());
|
||||||
let close = Rc::new(Close::new());
|
let close = Rc::new(Close::new());
|
||||||
let debug = Rc::new(Debug::new());
|
let debug = Rc::new(Debug::new());
|
||||||
let escape = Rc::new(Escape::new());
|
let escape = SimpleAction::escape();
|
||||||
let profile = Rc::new(Profile::new());
|
let profile = Rc::new(Profile::new());
|
||||||
let update = Rc::new(Update::new());
|
|
||||||
|
|
||||||
// Generate unique group ID
|
// Generate unique group ID
|
||||||
let id = uuid_string_random();
|
let id = uuid_string_random();
|
||||||
|
|
@ -62,9 +58,8 @@ impl Action {
|
||||||
simple_action_group.add_action(&about.simple_action);
|
simple_action_group.add_action(&about.simple_action);
|
||||||
simple_action_group.add_action(&close.simple_action);
|
simple_action_group.add_action(&close.simple_action);
|
||||||
simple_action_group.add_action(&debug.simple_action);
|
simple_action_group.add_action(&debug.simple_action);
|
||||||
simple_action_group.add_action(&escape.simple_action);
|
simple_action_group.add_action(&escape);
|
||||||
simple_action_group.add_action(&profile.simple_action);
|
simple_action_group.add_action(&profile.simple_action);
|
||||||
simple_action_group.add_action(&update.simple_action);
|
|
||||||
|
|
||||||
// Done
|
// Done
|
||||||
Self {
|
Self {
|
||||||
|
|
@ -73,7 +68,6 @@ impl Action {
|
||||||
debug,
|
debug,
|
||||||
escape,
|
escape,
|
||||||
profile,
|
profile,
|
||||||
update,
|
|
||||||
id,
|
id,
|
||||||
simple_action_group,
|
simple_action_group,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,85 +1,12 @@
|
||||||
use gtk::{
|
use gtk::{gio::SimpleAction, glib::uuid_string_random};
|
||||||
gio::SimpleAction,
|
|
||||||
glib::{uuid_string_random, GString},
|
|
||||||
prelude::{ActionExt, ToVariant},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// [SimpleAction](https://docs.gtk.org/gio/class.SimpleAction.html) wrapper for `Escape` action of `Browser` group
|
/// [SimpleAction](https://docs.gtk.org/gio/class.SimpleAction.html) wrapper for `Escape` action of `Browser` group
|
||||||
pub struct Escape {
|
pub trait Escape {
|
||||||
pub simple_action: SimpleAction,
|
fn escape() -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Escape {
|
impl Escape for SimpleAction {
|
||||||
fn default() -> Self {
|
fn escape() -> Self {
|
||||||
Self::new()
|
SimpleAction::new(&uuid_string_random(), None)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Escape {
|
|
||||||
// Constructors
|
|
||||||
|
|
||||||
/// Create new `Self`
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
simple_action: SimpleAction::new_stateful(
|
|
||||||
&uuid_string_random(),
|
|
||||||
None,
|
|
||||||
&String::new().to_variant(),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Actions
|
|
||||||
|
|
||||||
/// 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, tab_item_id: Option<GString>) {
|
|
||||||
// Save current state in memory
|
|
||||||
let _tab_item_id = state(&self.simple_action);
|
|
||||||
|
|
||||||
// Apply requested state
|
|
||||||
self.change_state(tab_item_id);
|
|
||||||
|
|
||||||
// Activate action
|
|
||||||
self.simple_action.activate(None);
|
|
||||||
|
|
||||||
// Return previous state
|
|
||||||
self.change_state(_tab_item_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Change action [state](https://docs.gtk.org/gio/method.SimpleAction.set_state.html)
|
|
||||||
/// * set default state on `None`
|
|
||||||
pub fn change_state(&self, state: Option<GString>) {
|
|
||||||
self.simple_action.change_state(
|
|
||||||
&match state {
|
|
||||||
Some(value) => value.to_string(),
|
|
||||||
None => String::new(),
|
|
||||||
}
|
|
||||||
.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(Option<GString>) + 'static) {
|
|
||||||
self.simple_action
|
|
||||||
.connect_activate(move |this, _| callback(state(this)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Shared helper to get C-based action state in Optional format
|
|
||||||
fn state(this: &SimpleAction) -> Option<GString> {
|
|
||||||
let state = this
|
|
||||||
.state()
|
|
||||||
.expect("State value required")
|
|
||||||
.get::<String>()
|
|
||||||
.expect("Parameter type does not match `String`");
|
|
||||||
|
|
||||||
if state.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(state.into())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,62 +0,0 @@
|
||||||
use gtk::{
|
|
||||||
gio::SimpleAction,
|
|
||||||
glib::{uuid_string_random, GString},
|
|
||||||
prelude::{ActionExt, StaticVariantType, ToVariant},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// [SimpleAction](https://docs.gtk.org/gio/class.SimpleAction.html) wrapper for `Update` action of `Browser` group
|
|
||||||
pub struct Update {
|
|
||||||
pub simple_action: SimpleAction,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Update {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Update {
|
|
||||||
// Constructors
|
|
||||||
|
|
||||||
/// Create new `Self`
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
simple_action: SimpleAction::new(
|
|
||||||
&uuid_string_random(),
|
|
||||||
Some(&String::static_variant_type()),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Actions
|
|
||||||
|
|
||||||
/// Emit [activate](https://docs.gtk.org/gio/signal.SimpleAction.activate.html) signal
|
|
||||||
/// with formatted for this action [Variant](https://docs.gtk.org/glib/struct.Variant.html) value
|
|
||||||
pub fn activate(&self, tab_item_id: Option<&str>) {
|
|
||||||
self.simple_action.activate(Some(
|
|
||||||
&match tab_item_id {
|
|
||||||
Some(value) => String::from(value),
|
|
||||||
None => String::new(),
|
|
||||||
}
|
|
||||||
.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(Option<GString>) + 'static) {
|
|
||||||
self.simple_action.connect_activate(move |_, variant| {
|
|
||||||
let tab_item_id = variant
|
|
||||||
.expect("Variant required to call this action")
|
|
||||||
.get::<String>()
|
|
||||||
.expect("Parameter type does not match `String`");
|
|
||||||
|
|
||||||
callback(match tab_item_id.is_empty() {
|
|
||||||
true => None,
|
|
||||||
false => Some(tab_item_id.into()),
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -11,7 +11,7 @@ use tab::Tab;
|
||||||
|
|
||||||
use super::Action as BrowserAction;
|
use super::Action as BrowserAction;
|
||||||
use crate::Profile;
|
use crate::Profile;
|
||||||
use gtk::{glib::GString, prelude::BoxExt, Box, Orientation};
|
use gtk::{prelude::BoxExt, Box, Orientation};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct Window {
|
pub struct Window {
|
||||||
|
|
@ -131,12 +131,8 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
pub fn escape(&self, tab_item_id: Option<GString>) {
|
pub fn escape(&self) {
|
||||||
self.tab.escape(tab_item_id);
|
self.tab.escape();
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(&self, tab_item_id: Option<GString>) {
|
|
||||||
self.tab.update(tab_item_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clean(&self, transaction: &Transaction, app_browser_id: i64) -> Result<(), String> {
|
pub fn clean(&self, transaction: &Transaction, app_browser_id: i64) -> Result<(), String> {
|
||||||
|
|
|
||||||
|
|
@ -210,7 +210,7 @@ impl Menu {
|
||||||
// Recently closed history
|
// Recently closed history
|
||||||
main_history_tab.remove_all();
|
main_history_tab.remove_all();
|
||||||
for item in profile.history.memory.tab.recent() {
|
for item in profile.history.memory.tab.recent() {
|
||||||
let item_request = item.page.navigation.request.widget.entry.text(); // @TODO restore entire `Item`
|
let item_request = item.page.navigation.request.entry.text(); // @TODO restore entire `Item`
|
||||||
let menu_item = gio::MenuItem::new(Some(&ellipsize(&item_request, LABEL_MAX_LENGTH)), None);
|
let menu_item = gio::MenuItem::new(Some(&ellipsize(&item_request, LABEL_MAX_LENGTH)), None);
|
||||||
menu_item.set_action_and_target_value(Some(&format!(
|
menu_item.set_action_and_target_value(Some(&format!(
|
||||||
"{}.{}",
|
"{}.{}",
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ mod menu;
|
||||||
mod widget;
|
mod widget;
|
||||||
|
|
||||||
use action::Action;
|
use action::Action;
|
||||||
|
use adw::TabPage;
|
||||||
use error::Error;
|
use error::Error;
|
||||||
pub use item::Item;
|
pub use item::Item;
|
||||||
use menu::Menu;
|
use menu::Menu;
|
||||||
|
|
@ -14,7 +15,7 @@ use widget::Widget;
|
||||||
use super::{Action as WindowAction, BrowserAction, Position};
|
use super::{Action as WindowAction, BrowserAction, Position};
|
||||||
use crate::Profile;
|
use crate::Profile;
|
||||||
use gtk::{
|
use gtk::{
|
||||||
glib::{DateTime, GString, Propagation},
|
glib::{DateTime, Propagation},
|
||||||
prelude::{ActionExt, EditableExt, WidgetExt},
|
prelude::{ActionExt, EditableExt, WidgetExt},
|
||||||
};
|
};
|
||||||
use sqlite::Transaction;
|
use sqlite::Transaction;
|
||||||
|
|
@ -25,7 +26,7 @@ pub struct Tab {
|
||||||
browser_action: Rc<BrowserAction>,
|
browser_action: Rc<BrowserAction>,
|
||||||
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<TabPage, Rc<Item>>>>,
|
||||||
pub action: Rc<Action>,
|
pub action: Rc<Action>,
|
||||||
pub widget: Rc<Widget>,
|
pub widget: Rc<Widget>,
|
||||||
}
|
}
|
||||||
|
|
@ -41,8 +42,7 @@ impl Tab {
|
||||||
let action = Rc::new(Action::new());
|
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<TabPage, Rc<Item>>>> = Rc::new(RefCell::new(HashMap::new()));
|
||||||
Rc::new(RefCell::new(HashMap::new()));
|
|
||||||
|
|
||||||
// Init context menu
|
// Init context menu
|
||||||
let menu = Menu::new(window_action);
|
let menu = Menu::new(window_action);
|
||||||
|
|
@ -58,21 +58,18 @@ impl Tab {
|
||||||
move |tab_view, tab_page| {
|
move |tab_view, tab_page| {
|
||||||
let state = match tab_page {
|
let state = match tab_page {
|
||||||
// on menu open
|
// on menu open
|
||||||
Some(this) => {
|
Some(tab_page) => {
|
||||||
if let Some(id) = this.keyword() {
|
if let Some(item) = index.borrow().get(tab_page) {
|
||||||
if let Some(item) = index.borrow().get(&id) {
|
item.page.update(); // update window actions using page of tab activated
|
||||||
item.page.update(); // update window actions using page of tab activated
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Some(tab_view.page_position(this)) // activated tab
|
|
||||||
|
Some(tab_view.page_position(tab_page)) // activated tab
|
||||||
}
|
}
|
||||||
// on menu close
|
// on menu close
|
||||||
None => {
|
None => {
|
||||||
if let Some(page) = widget.page(None) {
|
if let Some(tab_page) = widget.page(None) {
|
||||||
if let Some(id) = page.keyword() {
|
if let Some(item) = index.borrow().get(&tab_page) {
|
||||||
if let Some(item) = index.borrow().get(&id) {
|
item.page.update(); // update window actions using page of current tab
|
||||||
item.page.update(); // update window actions using page of current tab
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None // current tab
|
None // current tab
|
||||||
|
|
@ -97,57 +94,39 @@ impl Tab {
|
||||||
widget.tab_view.connect_close_page({
|
widget.tab_view.connect_close_page({
|
||||||
let index = index.clone();
|
let index = index.clone();
|
||||||
let profile = profile.clone();
|
let profile = profile.clone();
|
||||||
move |_, item| {
|
move |_, tab_page| {
|
||||||
// Get index ID by keyword saved
|
// Cleanup HashMap index
|
||||||
match item.keyword() {
|
if let Some(item) = index.borrow_mut().remove(tab_page) {
|
||||||
Some(id) => {
|
// Add history record into profile memory pool
|
||||||
if id.is_empty() {
|
// * this action allows to recover recently closed tab (e.g. from the main menu)
|
||||||
panic!("Tab index can not be empty!")
|
profile
|
||||||
}
|
.history
|
||||||
// Cleanup HashMap index
|
.memory
|
||||||
if let Some(item) = index.borrow_mut().remove(&id) {
|
.tab
|
||||||
// Add history record into profile memory pool
|
.add(item, DateTime::now_local().unwrap().to_unix());
|
||||||
// * this action allows to recover recently closed tab (e.g. from the main menu)
|
|
||||||
profile
|
|
||||||
.history
|
|
||||||
.memory
|
|
||||||
.tab
|
|
||||||
.add(item, DateTime::now_local().unwrap().to_unix());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => panic!("Undefined tab index!"),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Propagation::Proceed
|
Propagation::Proceed
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
widget.tab_view.connect_page_attached({
|
||||||
|
let window_action = window_action.clone();
|
||||||
|
let index = index.clone();
|
||||||
|
move |_, tab_page, _| {
|
||||||
|
if tab_page.is_selected() {
|
||||||
|
update_actions(tab_page, &index, &window_action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
widget.tab_view.connect_selected_page_notify({
|
widget.tab_view.connect_selected_page_notify({
|
||||||
let window_action = window_action.clone();
|
let window_action = window_action.clone();
|
||||||
let index = index.clone();
|
let index = index.clone();
|
||||||
move |this| {
|
move |this| {
|
||||||
if let Some(page) = this.selected_page() {
|
if let Some(tab_page) = this.selected_page() {
|
||||||
if let Some(id) = page.keyword() {
|
tab_page.set_needs_attention(false);
|
||||||
if let Some(item) = index.borrow().get(&id) {
|
update_actions(&tab_page, &index, &window_action);
|
||||||
window_action
|
|
||||||
.home
|
|
||||||
.simple_action
|
|
||||||
.set_enabled(item.action.home.is_enabled());
|
|
||||||
window_action
|
|
||||||
.reload
|
|
||||||
.simple_action
|
|
||||||
.set_enabled(item.action.reload.is_enabled());
|
|
||||||
window_action
|
|
||||||
.history_back
|
|
||||||
.simple_action
|
|
||||||
.set_enabled(item.action.history.back.is_enabled());
|
|
||||||
window_action
|
|
||||||
.history_forward
|
|
||||||
.simple_action
|
|
||||||
.set_enabled(item.action.history.forward.is_enabled());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
page.set_needs_attention(false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -193,13 +172,13 @@ impl Tab {
|
||||||
// Expect user input on tab appended has empty request entry
|
// Expect user input on tab appended has empty request entry
|
||||||
// * this action initiated here because should be applied on tab appending event only
|
// * this action initiated here because should be applied on tab appending event only
|
||||||
if request.is_none() || request.is_some_and(|value| value.is_empty()) {
|
if request.is_none() || request.is_some_and(|value| value.is_empty()) {
|
||||||
item.page.navigation.request.widget.entry.grab_focus();
|
item.page.navigation.request.entry.grab_focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register dynamically created tab components in the HashMap index
|
// Register dynamically created tab components in the HashMap index
|
||||||
self.index
|
self.index
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.insert(item.id.clone(), item.clone());
|
.insert(item.widget.tab_page.clone(), item.clone());
|
||||||
|
|
||||||
item
|
item
|
||||||
}
|
}
|
||||||
|
|
@ -215,18 +194,9 @@ impl Tab {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Toggle escape action for specified or current item
|
// Toggle escape action for specified or current item
|
||||||
pub fn escape(&self, item_id: Option<GString>) {
|
pub fn escape(&self) {
|
||||||
match item_id {
|
if let Some(item) = self.item(None) {
|
||||||
Some(id) => {
|
item.page.escape();
|
||||||
if let Some(item) = self.index.borrow().get(&id) {
|
|
||||||
item.page.escape()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
if let Some(item) = self.item(None) {
|
|
||||||
item.page.escape();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -274,7 +244,7 @@ impl Tab {
|
||||||
if let Some(item) = self.item(page_position) {
|
if let Some(item) = self.item(page_position) {
|
||||||
if let Some(home) = item.page.navigation.request.home() {
|
if let Some(home) = item.page.navigation.request.home() {
|
||||||
let home = home.to_string();
|
let home = home.to_string();
|
||||||
item.page.navigation.request.widget.entry.set_text(&home);
|
item.page.navigation.request.entry.set_text(&home);
|
||||||
item.client.handle(&home, true);
|
item.client.handle(&home, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -296,23 +266,7 @@ impl Tab {
|
||||||
pub fn page_reload(&self, page_position: Option<i32>) {
|
pub fn page_reload(&self, page_position: Option<i32>) {
|
||||||
if let Some(item) = self.item(page_position) {
|
if let Some(item) = self.item(page_position) {
|
||||||
item.client
|
item.client
|
||||||
.handle(&item.page.navigation.request.widget.entry.text(), true);
|
.handle(&item.page.navigation.request.entry.text(), true);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(&self, item_id: Option<GString>) {
|
|
||||||
let key = item_id.unwrap_or_default();
|
|
||||||
|
|
||||||
match self.index.borrow().get(&key) {
|
|
||||||
Some(item) => {
|
|
||||||
item.update();
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
// update all tabs
|
|
||||||
for (_, item) in self.index.borrow().iter() {
|
|
||||||
item.update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -361,7 +315,7 @@ impl Tab {
|
||||||
// Register dynamically created tab item in the HashMap index
|
// Register dynamically created tab item in the HashMap index
|
||||||
self.index
|
self.index
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.insert(item.id.clone(), item.clone());
|
.insert(item.widget.tab_page.clone(), item.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => return Err(e.to_string()),
|
Err(e) => return Err(e.to_string()),
|
||||||
|
|
@ -412,11 +366,9 @@ impl Tab {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn item(&self, position: Option<i32>) -> Option<Rc<Item>> {
|
fn item(&self, position: Option<i32>) -> Option<Rc<Item>> {
|
||||||
if let Some(page) = self.widget.page(position) {
|
if let Some(tab_page) = self.widget.page(position) {
|
||||||
if let Some(id) = page.keyword() {
|
if let Some(item) = self.index.borrow().get(&tab_page) {
|
||||||
if let Some(item) = self.index.borrow().get(&id) {
|
return Some(item.clone());
|
||||||
return Some(item.clone());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
|
|
@ -424,6 +376,7 @@ impl Tab {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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) {
|
||||||
|
|
@ -436,3 +389,37 @@ pub fn migrate(tx: &Transaction) -> Result<(), String> {
|
||||||
// Success
|
// Success
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_actions(
|
||||||
|
tab_page: &TabPage,
|
||||||
|
index: &Rc<RefCell<HashMap<TabPage, Rc<Item>>>>,
|
||||||
|
window_action: &Rc<WindowAction>,
|
||||||
|
) {
|
||||||
|
if let Some(item) = index.borrow().get(tab_page) {
|
||||||
|
window_action
|
||||||
|
.home
|
||||||
|
.simple_action
|
||||||
|
.set_enabled(item.action.home.is_enabled());
|
||||||
|
window_action
|
||||||
|
.reload
|
||||||
|
.simple_action
|
||||||
|
.set_enabled(item.action.reload.is_enabled());
|
||||||
|
window_action
|
||||||
|
.history_back
|
||||||
|
.simple_action
|
||||||
|
.set_enabled(item.action.history.back.is_enabled());
|
||||||
|
window_action
|
||||||
|
.history_forward
|
||||||
|
.simple_action
|
||||||
|
.set_enabled(item.action.history.forward.is_enabled());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window_action.home.simple_action.set_enabled(false);
|
||||||
|
window_action.reload.simple_action.set_enabled(false);
|
||||||
|
window_action.history_back.simple_action.set_enabled(false);
|
||||||
|
window_action
|
||||||
|
.history_forward
|
||||||
|
.simple_action
|
||||||
|
.set_enabled(false);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -95,14 +95,13 @@ impl Item {
|
||||||
this.set_enabled(false);
|
this.set_enabled(false);
|
||||||
if let Some(uri) = page.navigation.request.home() {
|
if let Some(uri) = page.navigation.request.home() {
|
||||||
let request = uri.to_string();
|
let request = uri.to_string();
|
||||||
page.navigation.request.widget.entry.set_text(&request);
|
page.navigation.request.entry.set_text(&request);
|
||||||
client.handle(&request, true);
|
client.handle(&request, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
action.ident.connect_activate({
|
action.ident.connect_activate({
|
||||||
let browser_action = browser_action.clone();
|
|
||||||
let page = page.clone();
|
let page = page.clone();
|
||||||
let parent = tab_view.clone().upcast::<gtk::Widget>();
|
let parent = tab_view.clone().upcast::<gtk::Widget>();
|
||||||
let profile = profile.clone();
|
let profile = profile.clone();
|
||||||
|
|
@ -111,12 +110,8 @@ impl Item {
|
||||||
if let Some(uri) = page.navigation.request.uri() {
|
if let Some(uri) = page.navigation.request.uri() {
|
||||||
let scheme = uri.scheme();
|
let scheme = uri.scheme();
|
||||||
if scheme == "gemini" || scheme == "titan" {
|
if scheme == "gemini" || scheme == "titan" {
|
||||||
return identity::default(
|
return identity::default(&window_action, &profile, &uri)
|
||||||
(&browser_action, &window_action),
|
.present(Some(&parent));
|
||||||
&profile,
|
|
||||||
&uri,
|
|
||||||
)
|
|
||||||
.present(Some(&parent));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
identity::unsupported().present(Some(&parent));
|
identity::unsupported().present(Some(&parent));
|
||||||
|
|
@ -128,7 +123,7 @@ impl Item {
|
||||||
let client = client.clone();
|
let client = client.clone();
|
||||||
move |request, is_history| {
|
move |request, is_history| {
|
||||||
if let Some(text) = request {
|
if let Some(text) = request {
|
||||||
page.navigation.request.widget.entry.set_text(&text);
|
page.navigation.request.entry.set_text(&text);
|
||||||
client.handle(&text, is_history);
|
client.handle(&text, is_history);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -138,13 +133,13 @@ impl Item {
|
||||||
let page = page.clone();
|
let page = page.clone();
|
||||||
let client = client.clone();
|
let client = client.clone();
|
||||||
move |_, _| {
|
move |_, _| {
|
||||||
client.handle(&page.navigation.request.widget.entry.text(), false);
|
client.handle(&page.navigation.request.entry.text(), false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle immediately on request
|
// Handle immediately on request
|
||||||
if let Some(text) = request {
|
if let Some(text) = request {
|
||||||
page.navigation.request.widget.entry.set_text(text);
|
page.navigation.request.entry.set_text(text);
|
||||||
if is_load {
|
if is_load {
|
||||||
client.handle(text, true);
|
client.handle(text, true);
|
||||||
}
|
}
|
||||||
|
|
@ -162,15 +157,17 @@ impl Item {
|
||||||
// Actions
|
// Actions
|
||||||
pub fn update(&self) {
|
pub fn update(&self) {
|
||||||
// Update self actions
|
// Update self actions
|
||||||
self.action
|
self.action.home.set_enabled(
|
||||||
.home
|
self.page
|
||||||
.set_enabled(self.page.navigation.request.home().is_some_and(|home| {
|
.navigation
|
||||||
home.to_string() != self.page.navigation.request.widget.entry.text()
|
.request
|
||||||
}));
|
.home()
|
||||||
|
.is_some_and(|home| home.to_string() != self.page.navigation.request.entry.text()),
|
||||||
|
);
|
||||||
|
|
||||||
self.action
|
self.action
|
||||||
.reload
|
.reload
|
||||||
.set_enabled(!self.page.navigation.request.widget.entry.text().is_empty());
|
.set_enabled(!self.page.navigation.request.entry.text().is_empty());
|
||||||
|
|
||||||
// Update child components
|
// Update child components
|
||||||
self.page.update();
|
self.page.update();
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ use feature::Feature;
|
||||||
use gtk::{
|
use gtk::{
|
||||||
gio::Cancellable,
|
gio::Cancellable,
|
||||||
glib::{Uri, UriFlags},
|
glib::{Uri, UriFlags},
|
||||||
prelude::{CancellableExt, EditableExt, EntryExt},
|
prelude::{ActionExt, CancellableExt, EditableExt, EntryExt},
|
||||||
};
|
};
|
||||||
use std::{cell::Cell, rc::Rc};
|
use std::{cell::Cell, rc::Rc};
|
||||||
use subject::Subject;
|
use subject::Subject;
|
||||||
|
|
@ -43,12 +43,8 @@ impl Client {
|
||||||
/// Route tab item `request` to protocol driver
|
/// Route tab item `request` to protocol driver
|
||||||
/// * or `navigation` entry if the value not provided
|
/// * or `navigation` entry if the value not provided
|
||||||
pub fn handle(&self, request: &str, is_snap_history: bool) {
|
pub fn handle(&self, request: &str, is_snap_history: bool) {
|
||||||
// Move focus out from navigation entry
|
// Move focus out from navigation entry @TODO
|
||||||
self.subject
|
self.subject.page.browser_action.escape.activate(None);
|
||||||
.page
|
|
||||||
.browser_action
|
|
||||||
.escape
|
|
||||||
.activate_stateful_once(Some(self.subject.page.id.as_str().into()));
|
|
||||||
|
|
||||||
// Initially disable find action
|
// Initially disable find action
|
||||||
self.subject
|
self.subject
|
||||||
|
|
@ -66,7 +62,6 @@ impl Client {
|
||||||
.page
|
.page
|
||||||
.navigation
|
.navigation
|
||||||
.request
|
.request
|
||||||
.widget
|
|
||||||
.entry
|
.entry
|
||||||
.set_progress_fraction(0.1);
|
.set_progress_fraction(0.1);
|
||||||
|
|
||||||
|
|
@ -96,7 +91,6 @@ impl Client {
|
||||||
.page
|
.page
|
||||||
.navigation
|
.navigation
|
||||||
.request
|
.request
|
||||||
.widget
|
|
||||||
.entry
|
.entry
|
||||||
.set_progress_fraction(0.0);
|
.set_progress_fraction(0.0);
|
||||||
subject.tab_page.set_loading(false);
|
subject.tab_page.set_loading(false);
|
||||||
|
|
@ -207,7 +201,7 @@ fn search(query: &str) -> Uri {
|
||||||
/// Make new history record in related components
|
/// Make new history record in related components
|
||||||
/// * optional [Uri](https://docs.gtk.org/glib/struct.Uri.html) reference wanted only for performance reasons, to not parse it twice
|
/// * optional [Uri](https://docs.gtk.org/glib/struct.Uri.html) reference wanted only for performance reasons, to not parse it twice
|
||||||
fn snap_history(subject: &Rc<Subject>, uri: Option<&Uri>) {
|
fn snap_history(subject: &Rc<Subject>, uri: Option<&Uri>) {
|
||||||
let request = subject.page.navigation.request.widget.entry.text();
|
let request = subject.page.navigation.request.entry.text();
|
||||||
|
|
||||||
// Add new record into the global memory index (used in global menu)
|
// Add new record into the global memory index (used in global menu)
|
||||||
// * if the `Uri` is `None`, try parse it from `request`
|
// * if the `Uri` is `None`, try parse it from `request`
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,6 @@ impl Gemini {
|
||||||
.page
|
.page
|
||||||
.navigation
|
.navigation
|
||||||
.request
|
.request
|
||||||
.widget
|
|
||||||
.entry
|
.entry
|
||||||
.set_progress_fraction(progress_fraction);
|
.set_progress_fraction(progress_fraction);
|
||||||
}
|
}
|
||||||
|
|
@ -135,7 +134,6 @@ impl Gemini {
|
||||||
.page
|
.page
|
||||||
.navigation
|
.navigation
|
||||||
.request
|
.request
|
||||||
.widget
|
|
||||||
.entry
|
.entry
|
||||||
.set_progress_fraction(0.0);
|
.set_progress_fraction(0.0);
|
||||||
self.subject.tab_page.set_loading(false);
|
self.subject.tab_page.set_loading(false);
|
||||||
|
|
@ -205,7 +203,7 @@ fn handle(
|
||||||
Some(1024),
|
Some(1024),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
subject.page.navigation.request.widget.entry.set_progress_fraction(0.0);
|
subject.page.navigation.request.entry.set_progress_fraction(0.0);
|
||||||
subject.tab_page.set_loading(false);
|
subject.tab_page.set_loading(false);
|
||||||
subject.tab_page.set_title(&title);
|
subject.tab_page.set_title(&title);
|
||||||
redirects.replace(0); // reset
|
redirects.replace(0); // reset
|
||||||
|
|
@ -277,7 +275,7 @@ fn handle(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
subject.page.navigation.request.widget.entry.set_progress_fraction(0.0);
|
subject.page.navigation.request.entry.set_progress_fraction(0.0);
|
||||||
subject.tab_page.set_loading(false);
|
subject.tab_page.set_loading(false);
|
||||||
subject.tab_page.set_title(&status.title());
|
subject.tab_page.set_title(&status.title());
|
||||||
redirects.replace(0); // reset
|
redirects.replace(0); // reset
|
||||||
|
|
@ -300,7 +298,7 @@ fn handle(
|
||||||
Some(title) => title.into(), // @TODO
|
Some(title) => title.into(), // @TODO
|
||||||
None => uri_to_title(&uri),
|
None => uri_to_title(&uri),
|
||||||
});
|
});
|
||||||
subject.page.navigation.request.widget.entry.set_progress_fraction(0.0);
|
subject.page.navigation.request.entry.set_progress_fraction(0.0);
|
||||||
subject.tab_page.set_loading(false);
|
subject.tab_page.set_loading(false);
|
||||||
subject.page.window_action
|
subject.page.window_action
|
||||||
.find
|
.find
|
||||||
|
|
@ -311,7 +309,7 @@ fn handle(
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let status = subject.page.content.to_status_failure();
|
let status = subject.page.content.to_status_failure();
|
||||||
status.set_description(Some(&e.to_string()));
|
status.set_description(Some(&e.to_string()));
|
||||||
subject.page.navigation.request.widget.entry.set_progress_fraction(0.0);
|
subject.page.navigation.request.entry.set_progress_fraction(0.0);
|
||||||
subject.tab_page.set_loading(false);
|
subject.tab_page.set_loading(false);
|
||||||
subject.tab_page.set_title(&status.title());
|
subject.tab_page.set_title(&status.title());
|
||||||
redirects.replace(0); // reset
|
redirects.replace(0); // reset
|
||||||
|
|
@ -355,7 +353,7 @@ fn handle(
|
||||||
subject.tab_page.set_title(&status.title());
|
subject.tab_page.set_title(&status.title());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
subject.page.navigation.request.widget.entry.set_progress_fraction(0.0);
|
subject.page.navigation.request.entry.set_progress_fraction(0.0);
|
||||||
subject.tab_page.set_loading(false);
|
subject.tab_page.set_loading(false);
|
||||||
redirects.replace(0); // reset
|
redirects.replace(0); // reset
|
||||||
},
|
},
|
||||||
|
|
@ -364,7 +362,7 @@ fn handle(
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let status = subject.page.content.to_status_failure();
|
let status = subject.page.content.to_status_failure();
|
||||||
status.set_description(Some(&e.to_string()));
|
status.set_description(Some(&e.to_string()));
|
||||||
subject.page.navigation.request.widget.entry.set_progress_fraction(0.0);
|
subject.page.navigation.request.entry.set_progress_fraction(0.0);
|
||||||
subject.tab_page.set_loading(false);
|
subject.tab_page.set_loading(false);
|
||||||
subject.tab_page.set_title(&status.title());
|
subject.tab_page.set_title(&status.title());
|
||||||
redirects.replace(0); // reset
|
redirects.replace(0); // reset
|
||||||
|
|
@ -378,7 +376,7 @@ fn handle(
|
||||||
.content
|
.content
|
||||||
.to_status_mime(mime, Some((&subject.page.item_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.entry.set_progress_fraction(0.0);
|
||||||
subject.tab_page.set_loading(false);
|
subject.tab_page.set_loading(false);
|
||||||
subject.tab_page.set_title(&status.title());
|
subject.tab_page.set_title(&status.title());
|
||||||
redirects.replace(0); // reset
|
redirects.replace(0); // reset
|
||||||
|
|
@ -387,7 +385,7 @@ fn handle(
|
||||||
None => {
|
None => {
|
||||||
let status = subject.page.content.to_status_failure();
|
let status = subject.page.content.to_status_failure();
|
||||||
status.set_description(Some("MIME type not found"));
|
status.set_description(Some("MIME type not found"));
|
||||||
subject.page.navigation.request.widget.entry.set_progress_fraction(0.0);
|
subject.page.navigation.request.entry.set_progress_fraction(0.0);
|
||||||
subject.tab_page.set_loading(false);
|
subject.tab_page.set_loading(false);
|
||||||
subject.tab_page.set_title(&status.title());
|
subject.tab_page.set_title(&status.title());
|
||||||
redirects.replace(0); // reset
|
redirects.replace(0); // reset
|
||||||
|
|
@ -425,7 +423,7 @@ fn handle(
|
||||||
if total > 5 {
|
if total > 5 {
|
||||||
let status = subject.page.content.to_status_failure();
|
let status = subject.page.content.to_status_failure();
|
||||||
status.set_description(Some("Redirection limit reached"));
|
status.set_description(Some("Redirection limit reached"));
|
||||||
subject.page.navigation.request.widget.entry.set_progress_fraction(0.0);
|
subject.page.navigation.request.entry.set_progress_fraction(0.0);
|
||||||
subject.tab_page.set_loading(false);
|
subject.tab_page.set_loading(false);
|
||||||
subject.tab_page.set_title(&status.title());
|
subject.tab_page.set_title(&status.title());
|
||||||
redirects.replace(0); // reset
|
redirects.replace(0); // reset
|
||||||
|
|
@ -436,7 +434,7 @@ fn handle(
|
||||||
|| uri.host() != target.host() {
|
|| uri.host() != target.host() {
|
||||||
let status = subject.page.content.to_status_failure();
|
let status = subject.page.content.to_status_failure();
|
||||||
status.set_description(Some("External redirects not allowed by protocol specification"));
|
status.set_description(Some("External redirects not allowed by protocol specification"));
|
||||||
subject.page.navigation.request.widget.entry.set_progress_fraction(0.0);
|
subject.page.navigation.request.entry.set_progress_fraction(0.0);
|
||||||
subject.tab_page.set_loading(false);
|
subject.tab_page.set_loading(false);
|
||||||
subject.tab_page.set_title(&status.title());
|
subject.tab_page.set_title(&status.title());
|
||||||
redirects.replace(0); // reset
|
redirects.replace(0); // reset
|
||||||
|
|
@ -445,7 +443,6 @@ fn handle(
|
||||||
if matches!(response.meta.status, Status::PermanentRedirect) {
|
if matches!(response.meta.status, Status::PermanentRedirect) {
|
||||||
subject.page.navigation
|
subject.page.navigation
|
||||||
.request
|
.request
|
||||||
.widget
|
|
||||||
.entry
|
.entry
|
||||||
.set_text(&uri.to_string());
|
.set_text(&uri.to_string());
|
||||||
}
|
}
|
||||||
|
|
@ -456,7 +453,7 @@ fn handle(
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let status = subject.page.content.to_status_failure();
|
let status = subject.page.content.to_status_failure();
|
||||||
status.set_description(Some(&e.to_string()));
|
status.set_description(Some(&e.to_string()));
|
||||||
subject.page.navigation.request.widget.entry.set_progress_fraction(0.0);
|
subject.page.navigation.request.entry.set_progress_fraction(0.0);
|
||||||
subject.tab_page.set_loading(false);
|
subject.tab_page.set_loading(false);
|
||||||
subject.tab_page.set_title(&status.title());
|
subject.tab_page.set_title(&status.title());
|
||||||
redirects.replace(0); // reset
|
redirects.replace(0); // reset
|
||||||
|
|
@ -465,7 +462,7 @@ fn handle(
|
||||||
None => {
|
None => {
|
||||||
let status = subject.page.content.to_status_failure();
|
let status = subject.page.content.to_status_failure();
|
||||||
status.set_description(Some("Redirection target not found"));
|
status.set_description(Some("Redirection target not found"));
|
||||||
subject.page.navigation.request.widget.entry.set_progress_fraction(0.0);
|
subject.page.navigation.request.entry.set_progress_fraction(0.0);
|
||||||
subject.tab_page.set_loading(false);
|
subject.tab_page.set_loading(false);
|
||||||
subject.tab_page.set_title(&status.title());
|
subject.tab_page.set_title(&status.title());
|
||||||
redirects.replace(0); // reset
|
redirects.replace(0); // reset
|
||||||
|
|
@ -484,7 +481,7 @@ fn handle(
|
||||||
None => response.meta.status.to_string(),
|
None => response.meta.status.to_string(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
subject.page.navigation.request.widget.entry.set_progress_fraction(0.0);
|
subject.page.navigation.request.entry.set_progress_fraction(0.0);
|
||||||
subject.tab_page.set_loading(false);
|
subject.tab_page.set_loading(false);
|
||||||
subject.tab_page.set_title(&status.title());
|
subject.tab_page.set_title(&status.title());
|
||||||
redirects.replace(0); // reset
|
redirects.replace(0); // reset
|
||||||
|
|
@ -492,7 +489,7 @@ fn handle(
|
||||||
error => {
|
error => {
|
||||||
let status = subject.page.content.to_status_failure();
|
let status = subject.page.content.to_status_failure();
|
||||||
status.set_description(Some(&error.to_string()));
|
status.set_description(Some(&error.to_string()));
|
||||||
subject.page.navigation.request.widget.entry.set_progress_fraction(0.0);
|
subject.page.navigation.request.entry.set_progress_fraction(0.0);
|
||||||
subject.tab_page.set_loading(false);
|
subject.tab_page.set_loading(false);
|
||||||
subject.tab_page.set_title(&status.title());
|
subject.tab_page.set_title(&status.title());
|
||||||
redirects.replace(0); // reset
|
redirects.replace(0); // reset
|
||||||
|
|
@ -502,7 +499,7 @@ fn handle(
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let status = subject.page.content.to_status_failure();
|
let status = subject.page.content.to_status_failure();
|
||||||
status.set_description(Some(&e.to_string()));
|
status.set_description(Some(&e.to_string()));
|
||||||
subject.page.navigation.request.widget.entry.set_progress_fraction(0.0);
|
subject.page.navigation.request.entry.set_progress_fraction(0.0);
|
||||||
subject.tab_page.set_loading(false);
|
subject.tab_page.set_loading(false);
|
||||||
subject.tab_page.set_title(&status.title());
|
subject.tab_page.set_title(&status.title());
|
||||||
redirects.replace(0); // reset
|
redirects.replace(0); // reset
|
||||||
|
|
|
||||||
|
|
@ -4,17 +4,13 @@ mod unsupported;
|
||||||
use default::Default;
|
use default::Default;
|
||||||
use unsupported::Unsupported;
|
use unsupported::Unsupported;
|
||||||
|
|
||||||
use super::{BrowserAction, Profile, WindowAction};
|
use super::{Profile, WindowAction};
|
||||||
use gtk::glib::Uri;
|
use gtk::glib::Uri;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
/// Create new identity widget for Gemini protocol match given URI
|
/// Create new identity widget for Gemini protocol match given URI
|
||||||
pub fn default(
|
pub fn default(window_action: &Rc<WindowAction>, profile: &Rc<Profile>, request: &Uri) -> Default {
|
||||||
action: (&Rc<BrowserAction>, &Rc<WindowAction>),
|
Default::build(window_action, profile, request)
|
||||||
profile: &Rc<Profile>,
|
|
||||||
request: &Uri,
|
|
||||||
) -> Default {
|
|
||||||
Default::build(action, profile, request)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create new identity widget for unknown request
|
/// Create new identity widget for unknown request
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
mod widget;
|
mod widget;
|
||||||
use widget::{form::list::item::value::Value, Widget};
|
use widget::{form::list::item::value::Value, Widget};
|
||||||
|
|
||||||
use super::{BrowserAction, Profile, WindowAction};
|
use super::{Profile, WindowAction};
|
||||||
use gtk::{glib::Uri, prelude::IsA};
|
use gtk::{glib::Uri, prelude::IsA};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
|
@ -14,26 +14,12 @@ impl Default {
|
||||||
// Construct
|
// Construct
|
||||||
|
|
||||||
/// Create new `Self` for given `Profile`
|
/// Create new `Self` for given `Profile`
|
||||||
pub fn build(
|
pub fn build(window_action: &Rc<WindowAction>, profile: &Rc<Profile>, request: &Uri) -> Self {
|
||||||
(browser_action, window_action): (&Rc<BrowserAction>, &Rc<WindowAction>),
|
|
||||||
profile: &Rc<Profile>,
|
|
||||||
request: &Uri,
|
|
||||||
) -> Self {
|
|
||||||
// Init widget
|
// Init widget
|
||||||
let widget = Rc::new(Widget::build(
|
let widget = Rc::new(Widget::build(profile, request));
|
||||||
(browser_action, window_action),
|
|
||||||
profile,
|
|
||||||
request,
|
|
||||||
));
|
|
||||||
|
|
||||||
// Init events
|
// Init events
|
||||||
widget.on_cancel({
|
|
||||||
let browser_action = browser_action.clone();
|
|
||||||
move || browser_action.update.activate(None)
|
|
||||||
});
|
|
||||||
|
|
||||||
widget.on_apply({
|
widget.on_apply({
|
||||||
let browser_action = browser_action.clone();
|
|
||||||
let profile = profile.clone();
|
let profile = profile.clone();
|
||||||
let request = request.clone();
|
let request = request.clone();
|
||||||
let widget = widget.clone();
|
let widget = widget.clone();
|
||||||
|
|
@ -79,7 +65,6 @@ impl Default {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply changes
|
// Apply changes
|
||||||
browser_action.update.activate(None);
|
|
||||||
window_action.reload.activate();
|
window_action.reload.activate();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,7 @@ pub mod form;
|
||||||
use action::Action as WidgetAction;
|
use action::Action as WidgetAction;
|
||||||
use form::{list::item::value::Value, Form};
|
use form::{list::item::value::Value, Form};
|
||||||
|
|
||||||
use crate::{
|
use crate::Profile;
|
||||||
app::browser::{action::Action as BrowserAction, window::action::Action as WindowAction},
|
|
||||||
Profile,
|
|
||||||
};
|
|
||||||
use adw::{
|
use adw::{
|
||||||
prelude::{AdwDialogExt, AlertDialogExt, AlertDialogExtManual},
|
prelude::{AdwDialogExt, AlertDialogExt, AlertDialogExtManual},
|
||||||
AlertDialog, ResponseAppearance,
|
AlertDialog, ResponseAppearance,
|
||||||
|
|
@ -36,20 +33,12 @@ impl Widget {
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
/// Create new `Self`
|
/// Create new `Self`
|
||||||
pub fn build(
|
pub fn build(profile: &Rc<Profile>, request: &Uri) -> Self {
|
||||||
(browser_action, window_action): (&Rc<BrowserAction>, &Rc<WindowAction>),
|
|
||||||
profile: &Rc<Profile>,
|
|
||||||
request: &Uri,
|
|
||||||
) -> Self {
|
|
||||||
// Init actions
|
// Init actions
|
||||||
let widget_action = Rc::new(WidgetAction::new());
|
let action = Rc::new(WidgetAction::new());
|
||||||
|
|
||||||
// Init child container
|
// Init child container
|
||||||
let form = Rc::new(Form::build(
|
let form = Rc::new(Form::build(&action, profile, request));
|
||||||
(browser_action, window_action, &widget_action),
|
|
||||||
profile,
|
|
||||||
request,
|
|
||||||
));
|
|
||||||
|
|
||||||
// Init main widget
|
// Init main widget
|
||||||
let alert_dialog = AlertDialog::builder()
|
let alert_dialog = AlertDialog::builder()
|
||||||
|
|
@ -76,7 +65,7 @@ impl Widget {
|
||||||
alert_dialog.set_response_appearance(RESPONSE_CANCEL.0, ResponseAppearance::Destructive); */
|
alert_dialog.set_response_appearance(RESPONSE_CANCEL.0, ResponseAppearance::Destructive); */
|
||||||
|
|
||||||
// Init events
|
// Init events
|
||||||
widget_action.update.connect_activate({
|
action.update.connect_activate({
|
||||||
let form = form.clone();
|
let form = form.clone();
|
||||||
let alert_dialog = alert_dialog.clone();
|
let alert_dialog = alert_dialog.clone();
|
||||||
move || {
|
move || {
|
||||||
|
|
@ -89,7 +78,7 @@ impl Widget {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Make initial update
|
// Make initial update
|
||||||
widget_action.update.activate();
|
action.update.activate();
|
||||||
|
|
||||||
// Return new activated `Self`
|
// Return new activated `Self`
|
||||||
Self {
|
Self {
|
||||||
|
|
@ -116,20 +105,6 @@ impl Widget {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Callback wrapper to cancel
|
|
||||||
/// [response](https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/signal.AlertDialog.response.html)
|
|
||||||
/// * return require reload state
|
|
||||||
pub fn on_cancel(&self, callback: impl Fn() + 'static) {
|
|
||||||
self.alert_dialog
|
|
||||||
.connect_response(Some(RESPONSE_CANCEL.0), move |this, response| {
|
|
||||||
// Prevent double-click action
|
|
||||||
this.set_response_enabled(response, false);
|
|
||||||
|
|
||||||
// Result
|
|
||||||
callback()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Show dialog with new preset
|
/// Show dialog with new preset
|
||||||
pub fn present(&self, parent: Option<&impl IsA<gtk::Widget>>) {
|
pub fn present(&self, parent: Option<&impl IsA<gtk::Widget>>) {
|
||||||
self.alert_dialog.present(parent)
|
self.alert_dialog.present(parent)
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,7 @@ use name::Name;
|
||||||
use save::Save;
|
use save::Save;
|
||||||
|
|
||||||
use super::WidgetAction;
|
use super::WidgetAction;
|
||||||
use crate::{
|
use crate::Profile;
|
||||||
app::browser::{action::Action as BrowserAction, window::action::Action as WindowAction},
|
|
||||||
Profile,
|
|
||||||
};
|
|
||||||
use gtk::{glib::Uri, prelude::BoxExt, Box, Orientation};
|
use gtk::{glib::Uri, prelude::BoxExt, Box, Orientation};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
|
@ -37,27 +34,14 @@ impl Form {
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
/// Create new `Self`
|
/// Create new `Self`
|
||||||
pub fn build(
|
pub fn build(widget_action: &Rc<WidgetAction>, profile: &Rc<Profile>, request: &Uri) -> Self {
|
||||||
(browser_action, _window_action, widget_action): (
|
|
||||||
&Rc<BrowserAction>,
|
|
||||||
&Rc<WindowAction>,
|
|
||||||
&Rc<WidgetAction>,
|
|
||||||
),
|
|
||||||
profile: &Rc<Profile>,
|
|
||||||
request: &Uri,
|
|
||||||
) -> Self {
|
|
||||||
// Init components
|
// Init components
|
||||||
let list = Rc::new(List::build(widget_action, profile, request));
|
let list = Rc::new(List::build(widget_action, profile, request));
|
||||||
let file = Rc::new(File::build(widget_action));
|
let file = Rc::new(File::build(widget_action));
|
||||||
let name = Rc::new(Name::build(widget_action));
|
let name = Rc::new(Name::build(widget_action));
|
||||||
let save = Rc::new(Save::build(profile, &list));
|
let save = Rc::new(Save::build(profile, &list));
|
||||||
let drop = Rc::new(Drop::build(profile, &list));
|
let drop = Rc::new(Drop::build(profile, &list));
|
||||||
let exit = Rc::new(Exit::build(
|
let exit = Rc::new(Exit::build(widget_action, profile, &list, request));
|
||||||
(browser_action, widget_action),
|
|
||||||
profile,
|
|
||||||
&list,
|
|
||||||
request,
|
|
||||||
));
|
|
||||||
|
|
||||||
// Init main container
|
// Init main container
|
||||||
let g_box = Box::builder().orientation(Orientation::Vertical).build();
|
let g_box = Box::builder().orientation(Orientation::Vertical).build();
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use super::{
|
||||||
list::{item::Value, List},
|
list::{item::Value, List},
|
||||||
WidgetAction,
|
WidgetAction,
|
||||||
};
|
};
|
||||||
use crate::{app::browser::Action as BrowserAction, Profile};
|
use crate::Profile;
|
||||||
use adw::{
|
use adw::{
|
||||||
prelude::{AdwDialogExt, AlertDialogExt, AlertDialogExtManual},
|
prelude::{AdwDialogExt, AlertDialogExt, AlertDialogExtManual},
|
||||||
AlertDialog, ResponseAppearance,
|
AlertDialog, ResponseAppearance,
|
||||||
|
|
@ -34,7 +34,7 @@ impl Exit {
|
||||||
|
|
||||||
/// Create new `Self`
|
/// Create new `Self`
|
||||||
pub fn build(
|
pub fn build(
|
||||||
(browser_action, widget_action): (&Rc<BrowserAction>, &Rc<WidgetAction>),
|
widget_action: &Rc<WidgetAction>,
|
||||||
profile: &Rc<Profile>,
|
profile: &Rc<Profile>,
|
||||||
list: &Rc<List>,
|
list: &Rc<List>,
|
||||||
request: &Uri,
|
request: &Uri,
|
||||||
|
|
@ -49,7 +49,6 @@ impl Exit {
|
||||||
|
|
||||||
// Init events
|
// Init events
|
||||||
button.connect_clicked({
|
button.connect_clicked({
|
||||||
let browser_action = browser_action.clone();
|
|
||||||
let button = button.clone();
|
let button = button.clone();
|
||||||
let list = list.clone();
|
let list = list.clone();
|
||||||
let profile = profile.clone();
|
let profile = profile.clone();
|
||||||
|
|
@ -84,7 +83,6 @@ impl Exit {
|
||||||
|
|
||||||
// Connect confirmation event
|
// Connect confirmation event
|
||||||
alert_dialog.connect_response(Some(RESPONSE_CONFIRM.0), {
|
alert_dialog.connect_response(Some(RESPONSE_CONFIRM.0), {
|
||||||
let browser_action = browser_action.clone();
|
|
||||||
let button = button.clone();
|
let button = button.clone();
|
||||||
let list = list.clone();
|
let list = list.clone();
|
||||||
let profile = profile.clone();
|
let profile = profile.clone();
|
||||||
|
|
@ -111,7 +109,6 @@ impl Exit {
|
||||||
button.set_label(&e.to_string())
|
button.set_label(&e.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
browser_action.update.activate(None);
|
|
||||||
widget_action.update.activate();
|
widget_action.update.activate();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ impl Page {
|
||||||
|
|
||||||
let navigation = Rc::new(Navigation::build(
|
let navigation = Rc::new(Navigation::build(
|
||||||
profile,
|
profile,
|
||||||
(browser_action, window_action, tab_action, item_action),
|
(window_action, tab_action, item_action),
|
||||||
));
|
));
|
||||||
|
|
||||||
let input = Rc::new(Input::new());
|
let input = Rc::new(Input::new());
|
||||||
|
|
@ -92,7 +92,7 @@ impl Page {
|
||||||
let result = match self
|
let result = match self
|
||||||
.profile
|
.profile
|
||||||
.bookmark
|
.bookmark
|
||||||
.toggle(self.navigation.request.widget.entry.text().as_str())
|
.toggle(self.navigation.request.entry.text().as_str())
|
||||||
{
|
{
|
||||||
Ok(result) => Ok(result),
|
Ok(result) => Ok(result),
|
||||||
Err(_) => Err(Error::Bookmark), // @TODO
|
Err(_) => Err(Error::Bookmark), // @TODO
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ mod reload;
|
||||||
mod request;
|
mod request;
|
||||||
mod widget;
|
mod widget;
|
||||||
|
|
||||||
use super::{BrowserAction, ItemAction, Profile, TabAction, WindowAction};
|
use super::{ItemAction, Profile, TabAction, WindowAction};
|
||||||
use bookmark::Bookmark;
|
use bookmark::Bookmark;
|
||||||
use gtk::{Box, Button};
|
use gtk::{Box, Button};
|
||||||
use history::History;
|
use history::History;
|
||||||
|
|
@ -29,8 +29,7 @@ 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, item_action): (
|
(window_action, tab_action, item_action): (
|
||||||
&Rc<BrowserAction>,
|
|
||||||
&Rc<WindowAction>,
|
&Rc<WindowAction>,
|
||||||
&Rc<TabAction>,
|
&Rc<TabAction>,
|
||||||
&Rc<ItemAction>,
|
&Rc<ItemAction>,
|
||||||
|
|
@ -39,7 +38,7 @@ impl Navigation {
|
||||||
// init children components
|
// init children components
|
||||||
|
|
||||||
let history = Box::history((window_action, tab_action, item_action));
|
let history = Box::history((window_action, tab_action, item_action));
|
||||||
let request = Rc::new(Request::build((browser_action, item_action)));
|
let request = Rc::new(Request::build(item_action, profile));
|
||||||
let reload = Button::reload((window_action, tab_action, item_action), &request);
|
let reload = Button::reload((window_action, tab_action, item_action), &request);
|
||||||
let home = Button::home((window_action, tab_action, item_action), &request);
|
let home = Button::home((window_action, tab_action, item_action), &request);
|
||||||
let bookmark = Button::bookmark(window_action);
|
let bookmark = Button::bookmark(window_action);
|
||||||
|
|
@ -49,7 +48,7 @@ impl Navigation {
|
||||||
&home,
|
&home,
|
||||||
&history,
|
&history,
|
||||||
&reload,
|
&reload,
|
||||||
&request.widget.entry, // @TODO
|
&request.entry, // @TODO
|
||||||
&bookmark,
|
&bookmark,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
@ -73,8 +72,6 @@ impl Navigation {
|
||||||
// update children components
|
// update children components
|
||||||
self.bookmark
|
self.bookmark
|
||||||
.update(self.profile.bookmark.get(&request).is_ok());
|
.update(self.profile.bookmark.get(&request).is_ok());
|
||||||
self.request
|
|
||||||
.update(self.profile.identity.get(&request).is_some());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clean(
|
pub fn clean(
|
||||||
|
|
|
||||||
|
|
@ -1,38 +1,132 @@
|
||||||
mod database;
|
mod database;
|
||||||
mod test;
|
mod primary_icon;
|
||||||
mod widget;
|
|
||||||
|
|
||||||
use widget::Widget;
|
use primary_icon::PrimaryIcon;
|
||||||
|
|
||||||
use crate::app::browser::{window::tab::item::Action as ItemAction, Action as BrowserAction};
|
use super::{ItemAction, Profile};
|
||||||
use gtk::{
|
use gtk::{
|
||||||
glib::{gformat, GString, Uri, UriFlags},
|
glib::{gformat, GString, Uri, UriFlags},
|
||||||
prelude::EditableExt,
|
prelude::{EditableExt, EntryExt, WidgetExt},
|
||||||
|
Entry, EntryIconPosition, StateFlags,
|
||||||
};
|
};
|
||||||
use sqlite::Transaction;
|
use sqlite::Transaction;
|
||||||
use std::rc::Rc;
|
use std::{cell::Cell, rc::Rc};
|
||||||
|
|
||||||
|
const PLACEHOLDER_TEXT: &str = "URL or search term...";
|
||||||
|
|
||||||
// Main
|
|
||||||
pub struct Request {
|
pub struct Request {
|
||||||
pub widget: Rc<Widget>,
|
pub entry: Entry,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Request {
|
impl Request {
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
/// Build new `Self`
|
/// Build new `Self`
|
||||||
pub fn build((browser_action, item_action): (&Rc<BrowserAction>, &Rc<ItemAction>)) -> Self {
|
pub fn build(item_action: &Rc<ItemAction>, profile: &Rc<Profile>) -> Self {
|
||||||
Self {
|
// Init main widget
|
||||||
widget: Rc::new(Widget::build((browser_action, item_action))),
|
let entry = Entry::builder()
|
||||||
}
|
.placeholder_text(PLACEHOLDER_TEXT)
|
||||||
|
.secondary_icon_tooltip_text("Go to the location")
|
||||||
|
.hexpand(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// Connect events
|
||||||
|
entry.connect_icon_release({
|
||||||
|
let item_action = item_action.clone();
|
||||||
|
move |this, position| match position {
|
||||||
|
EntryIconPosition::Primary => item_action.ident.activate(), // @TODO PrimaryIcon impl
|
||||||
|
EntryIconPosition::Secondary => item_action.load.activate(Some(&this.text()), true),
|
||||||
|
_ => todo!(), // unexpected
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
entry.connect_has_focus_notify(|this| {
|
||||||
|
if this.focus_child().is_some_and(|text| text.has_focus()) {
|
||||||
|
this.set_secondary_icon_name(Some("pan-end-symbolic"));
|
||||||
|
} else {
|
||||||
|
this.set_secondary_icon_name(None);
|
||||||
|
this.select_region(0, 0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
entry.connect_changed({
|
||||||
|
let profile = profile.clone();
|
||||||
|
let item_action = item_action.clone();
|
||||||
|
move |this| {
|
||||||
|
// Update actions
|
||||||
|
item_action.reload.set_enabled(!this.text().is_empty());
|
||||||
|
item_action
|
||||||
|
.home
|
||||||
|
.set_enabled(home(uri(&this.text())).is_some());
|
||||||
|
|
||||||
|
// Update primary icon
|
||||||
|
this.first_child().unwrap().remove_css_class("success"); // @TODO handle
|
||||||
|
|
||||||
|
this.set_primary_icon_activatable(false);
|
||||||
|
this.set_primary_icon_sensitive(false);
|
||||||
|
|
||||||
|
match primary_icon::from(&this.text()) {
|
||||||
|
PrimaryIcon::Download { name, tooltip } => {
|
||||||
|
this.set_primary_icon_name(Some(name));
|
||||||
|
this.set_primary_icon_tooltip_text(Some(tooltip));
|
||||||
|
}
|
||||||
|
PrimaryIcon::Gemini { name, tooltip }
|
||||||
|
| PrimaryIcon::Titan { name, tooltip } => {
|
||||||
|
this.set_primary_icon_activatable(true);
|
||||||
|
this.set_primary_icon_sensitive(true);
|
||||||
|
this.set_primary_icon_name(Some(name));
|
||||||
|
if profile.identity.get(&strip_prefix(this.text())).is_some() {
|
||||||
|
this.first_child().unwrap().add_css_class("success"); // @TODO handle
|
||||||
|
this.set_primary_icon_tooltip_text(Some(tooltip.1));
|
||||||
|
} else {
|
||||||
|
this.set_primary_icon_tooltip_text(Some(tooltip.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PrimaryIcon::Search { name, tooltip } => {
|
||||||
|
this.set_primary_icon_name(Some(name));
|
||||||
|
this.set_primary_icon_tooltip_text(Some(tooltip));
|
||||||
|
}
|
||||||
|
PrimaryIcon::Source { name, tooltip } => {
|
||||||
|
this.set_primary_icon_name(Some(name));
|
||||||
|
this.set_primary_icon_tooltip_text(Some(tooltip));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
entry.connect_activate({
|
||||||
|
let item_action = item_action.clone();
|
||||||
|
move |entry| {
|
||||||
|
item_action.load.activate(Some(&entry.text()), true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
entry.connect_state_flags_changed({
|
||||||
|
// Define last focus state container
|
||||||
|
let has_focus = Cell::new(false);
|
||||||
|
move |entry, state| {
|
||||||
|
// Select entire text on first click (release)
|
||||||
|
// this behavior implemented in most web-browsers,
|
||||||
|
// to simply overwrite current request with new value
|
||||||
|
// Note:
|
||||||
|
// * Custom GestureClick is not an option here, as GTK Entry has default controller
|
||||||
|
// * This is experimental feature does not follow native GTK behavior @TODO make optional
|
||||||
|
if !has_focus.take()
|
||||||
|
&& state.contains(StateFlags::ACTIVE | StateFlags::FOCUS_WITHIN)
|
||||||
|
&& entry.selection_bounds().is_none()
|
||||||
|
{
|
||||||
|
entry.select_region(0, entry.text_length().into());
|
||||||
|
}
|
||||||
|
// Update last focus state
|
||||||
|
has_focus.replace(state.contains(StateFlags::FOCUS_WITHIN));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Return activated `Self`
|
||||||
|
Self { entry }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
|
|
||||||
pub fn update(&self, is_identity_active: bool) {
|
|
||||||
self.widget.update(is_identity_active);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clean(
|
pub fn clean(
|
||||||
&self,
|
&self,
|
||||||
transaction: &Transaction,
|
transaction: &Transaction,
|
||||||
|
|
@ -44,7 +138,7 @@ impl Request {
|
||||||
match database::delete(transaction, &record.id) {
|
match database::delete(transaction, &record.id) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
// Delegate clean action to the item childs
|
// Delegate clean action to the item childs
|
||||||
self.widget.clean(transaction, &record.id)?;
|
// nothing yet..
|
||||||
}
|
}
|
||||||
Err(e) => return Err(e.to_string()),
|
Err(e) => return Err(e.to_string()),
|
||||||
}
|
}
|
||||||
|
|
@ -64,8 +158,12 @@ impl Request {
|
||||||
match database::select(transaction, app_browser_window_tab_item_page_navigation_id) {
|
match database::select(transaction, app_browser_window_tab_item_page_navigation_id) {
|
||||||
Ok(records) => {
|
Ok(records) => {
|
||||||
for record in records {
|
for record in records {
|
||||||
|
if let Some(text) = record.text {
|
||||||
|
self.entry.set_text(&text);
|
||||||
|
}
|
||||||
|
|
||||||
// Delegate restore action to the item childs
|
// Delegate restore action to the item childs
|
||||||
self.widget.restore(transaction, &record.id)?;
|
// nothing yet..
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => return Err(e.to_string()),
|
Err(e) => return Err(e.to_string()),
|
||||||
|
|
@ -79,12 +177,22 @@ impl Request {
|
||||||
transaction: &Transaction,
|
transaction: &Transaction,
|
||||||
app_browser_window_tab_item_page_navigation_id: &i64,
|
app_browser_window_tab_item_page_navigation_id: &i64,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
match database::insert(transaction, app_browser_window_tab_item_page_navigation_id) {
|
// Keep value in memory until operation complete
|
||||||
|
let text = self.entry.text();
|
||||||
|
|
||||||
|
match database::insert(
|
||||||
|
transaction,
|
||||||
|
app_browser_window_tab_item_page_navigation_id,
|
||||||
|
match text.is_empty() {
|
||||||
|
true => None,
|
||||||
|
false => Some(text.as_str()),
|
||||||
|
},
|
||||||
|
) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
let id = database::last_insert_id(transaction);
|
// let id = database::last_insert_id(transaction);
|
||||||
|
|
||||||
// Delegate save action to childs
|
// Delegate save action to childs
|
||||||
self.widget.save(transaction, &id)?;
|
// nothing yet..
|
||||||
}
|
}
|
||||||
Err(e) => return Err(e.to_string()),
|
Err(e) => return Err(e.to_string()),
|
||||||
}
|
}
|
||||||
|
|
@ -95,48 +203,19 @@ impl Request {
|
||||||
// Setters
|
// Setters
|
||||||
|
|
||||||
pub fn to_download(&self) {
|
pub fn to_download(&self) {
|
||||||
self.widget.entry.set_text(&self.download());
|
self.entry.set_text(&self.download());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_source(&self) {
|
pub fn to_source(&self) {
|
||||||
self.widget.entry.set_text(&self.source());
|
self.entry.set_text(&self.source());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getters
|
// Getters
|
||||||
|
|
||||||
/// Try get current request value as [Uri](https://docs.gtk.org/glib/struct.Uri.html)
|
|
||||||
/// * `strip_prefix` on parse
|
|
||||||
pub fn uri(&self) -> Option<Uri> {
|
|
||||||
match Uri::parse(&strip_prefix(self.widget.entry.text()), UriFlags::NONE) {
|
|
||||||
Ok(uri) => Some(uri),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get current request value without system prefix
|
/// Get current request value without system prefix
|
||||||
/// * the `prefix` is not `scheme`
|
/// * the `prefix` is not `scheme`
|
||||||
pub fn strip_prefix(&self) -> GString {
|
pub fn strip_prefix(&self) -> GString {
|
||||||
strip_prefix(self.widget.entry.text())
|
strip_prefix(self.entry.text())
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse home [Uri](https://docs.gtk.org/glib/struct.Uri.html) of `Self`
|
|
||||||
pub fn home(&self) -> Option<Uri> {
|
|
||||||
self.uri().map(|uri| {
|
|
||||||
Uri::build(
|
|
||||||
UriFlags::NONE,
|
|
||||||
&if uri.scheme() == "titan" {
|
|
||||||
GString::from("gemini")
|
|
||||||
} else {
|
|
||||||
uri.scheme()
|
|
||||||
},
|
|
||||||
uri.userinfo().as_deref(),
|
|
||||||
uri.host().as_deref(),
|
|
||||||
uri.port(),
|
|
||||||
"/",
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get request value in `download:` format
|
/// Get request value in `download:` format
|
||||||
|
|
@ -148,13 +227,37 @@ impl Request {
|
||||||
pub fn source(&self) -> GString {
|
pub fn source(&self) -> GString {
|
||||||
gformat!("source:{}", self.strip_prefix())
|
gformat!("source:{}", self.strip_prefix())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Try get current request value as [Uri](https://docs.gtk.org/glib/struct.Uri.html)
|
||||||
|
/// * `strip_prefix` on parse
|
||||||
|
pub fn uri(&self) -> Option<Uri> {
|
||||||
|
uri(&strip_prefix(self.entry.text()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Try build home [Uri](https://docs.gtk.org/glib/struct.Uri.html) for `Self`
|
||||||
|
pub fn home(&self) -> Option<Uri> {
|
||||||
|
home(self.uri())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tools
|
// Tools
|
||||||
|
|
||||||
|
pub fn migrate(tx: &Transaction) -> Result<(), String> {
|
||||||
|
// Migrate self components
|
||||||
|
if let Err(e) = database::init(tx) {
|
||||||
|
return Err(e.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delegate migration to childs
|
||||||
|
// nothing yet..
|
||||||
|
|
||||||
|
// Success
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Strip system prefix from request string
|
/// Strip system prefix from request string
|
||||||
/// * the `prefix` is not `scheme`
|
/// * the `prefix` is not `scheme`
|
||||||
pub fn strip_prefix(mut request: GString) -> GString {
|
fn strip_prefix(mut request: GString) -> GString {
|
||||||
if let Some(postfix) = request.strip_prefix("source:") {
|
if let Some(postfix) = request.strip_prefix("source:") {
|
||||||
request = postfix.into()
|
request = postfix.into()
|
||||||
};
|
};
|
||||||
|
|
@ -166,15 +269,29 @@ pub fn strip_prefix(mut request: GString) -> GString {
|
||||||
request
|
request
|
||||||
} // @TODO move prefix features to page client
|
} // @TODO move prefix features to page client
|
||||||
|
|
||||||
pub fn migrate(tx: &Transaction) -> Result<(), String> {
|
fn uri(value: &str) -> Option<Uri> {
|
||||||
// Migrate self components
|
match Uri::parse(value, UriFlags::NONE) {
|
||||||
if let Err(e) = database::init(tx) {
|
Ok(uri) => Some(uri),
|
||||||
return Err(e.to_string());
|
_ => None,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Delegate migration to childs
|
|
||||||
widget::migrate(tx)?;
|
/// Parse home [Uri](https://docs.gtk.org/glib/struct.Uri.html) for `subject`
|
||||||
|
fn home(subject: Option<Uri>) -> Option<Uri> {
|
||||||
// Success
|
subject.map(|uri| {
|
||||||
Ok(())
|
Uri::build(
|
||||||
|
UriFlags::NONE,
|
||||||
|
&if uri.scheme() == "titan" {
|
||||||
|
GString::from("gemini")
|
||||||
|
} else {
|
||||||
|
uri.scheme()
|
||||||
|
},
|
||||||
|
uri.userinfo().as_deref(),
|
||||||
|
uri.host().as_deref(),
|
||||||
|
uri.port(),
|
||||||
|
"/",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ use sqlite::{Error, Transaction};
|
||||||
pub struct Table {
|
pub struct Table {
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
// pub app_browser_window_tab_item_page_navigation_id: i64, not in use
|
// pub app_browser_window_tab_item_page_navigation_id: i64, not in use
|
||||||
|
pub text: Option<String>, // can be stored as NULL
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(tx: &Transaction) -> Result<usize, Error> {
|
pub fn init(tx: &Transaction) -> Result<usize, Error> {
|
||||||
|
|
@ -11,6 +12,7 @@ pub fn init(tx: &Transaction) -> Result<usize, Error> {
|
||||||
(
|
(
|
||||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
`app_browser_window_tab_item_page_navigation_id` INTEGER NOT NULL,
|
`app_browser_window_tab_item_page_navigation_id` INTEGER NOT NULL,
|
||||||
|
`text` VARCHAR(1024),
|
||||||
|
|
||||||
FOREIGN KEY (`app_browser_window_tab_item_page_navigation_id`) REFERENCES `app_browser_window_tab_item_page_navigation`(`id`)
|
FOREIGN KEY (`app_browser_window_tab_item_page_navigation_id`) REFERENCES `app_browser_window_tab_item_page_navigation`(`id`)
|
||||||
)",
|
)",
|
||||||
|
|
@ -21,12 +23,14 @@ pub fn init(tx: &Transaction) -> Result<usize, Error> {
|
||||||
pub fn insert(
|
pub fn insert(
|
||||||
tx: &Transaction,
|
tx: &Transaction,
|
||||||
app_browser_window_tab_item_page_navigation_id: &i64,
|
app_browser_window_tab_item_page_navigation_id: &i64,
|
||||||
|
text: Option<&str>,
|
||||||
) -> Result<usize, Error> {
|
) -> Result<usize, Error> {
|
||||||
tx.execute(
|
tx.execute(
|
||||||
"INSERT INTO `app_browser_window_tab_item_page_navigation_request` (
|
"INSERT INTO `app_browser_window_tab_item_page_navigation_request` (
|
||||||
`app_browser_window_tab_item_page_navigation_id`
|
`app_browser_window_tab_item_page_navigation_id`,
|
||||||
) VALUES (?)",
|
`text`
|
||||||
[app_browser_window_tab_item_page_navigation_id],
|
) VALUES (?, ?)",
|
||||||
|
(app_browser_window_tab_item_page_navigation_id, text),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -36,7 +40,8 @@ pub fn select(
|
||||||
) -> Result<Vec<Table>, Error> {
|
) -> Result<Vec<Table>, Error> {
|
||||||
let mut stmt = tx.prepare(
|
let mut stmt = tx.prepare(
|
||||||
"SELECT `id`,
|
"SELECT `id`,
|
||||||
`app_browser_window_tab_item_page_navigation_id`
|
`app_browser_window_tab_item_page_navigation_id`,
|
||||||
|
`text`
|
||||||
FROM `app_browser_window_tab_item_page_navigation_request`
|
FROM `app_browser_window_tab_item_page_navigation_request`
|
||||||
WHERE `app_browser_window_tab_item_page_navigation_id` = ?",
|
WHERE `app_browser_window_tab_item_page_navigation_id` = ?",
|
||||||
)?;
|
)?;
|
||||||
|
|
@ -45,6 +50,7 @@ pub fn select(
|
||||||
Ok(Table {
|
Ok(Table {
|
||||||
id: row.get(0)?,
|
id: row.get(0)?,
|
||||||
// app_browser_window_tab_item_page_navigation_id: row.get(1)?, not in use
|
// app_browser_window_tab_item_page_navigation_id: row.get(1)?, not in use
|
||||||
|
text: row.get(2)?,
|
||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
|
@ -65,6 +71,7 @@ pub fn delete(tx: &Transaction, id: &i64) -> Result<usize, Error> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* not in use
|
||||||
pub fn last_insert_id(tx: &Transaction) -> i64 {
|
pub fn last_insert_id(tx: &Transaction) -> i64 {
|
||||||
tx.last_insert_rowid()
|
tx.last_insert_rowid()
|
||||||
}
|
} */
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
#[test]
|
|
||||||
fn strip_prefix() {
|
|
||||||
assert_eq!(super::strip_prefix("source:gemini".into()), "gemini");
|
|
||||||
assert_eq!(super::strip_prefix("download:gemini".into()), "gemini");
|
|
||||||
}
|
|
||||||
|
|
@ -1,286 +0,0 @@
|
||||||
mod database;
|
|
||||||
mod primary_icon;
|
|
||||||
|
|
||||||
use primary_icon::PrimaryIcon;
|
|
||||||
|
|
||||||
use super::{BrowserAction, ItemAction};
|
|
||||||
use gtk::{
|
|
||||||
glib::{timeout_add_local, ControlFlow, SourceId},
|
|
||||||
prelude::{EditableExt, EntryExt, WidgetExt},
|
|
||||||
Entry, EntryIconPosition, StateFlags,
|
|
||||||
};
|
|
||||||
use sqlite::Transaction;
|
|
||||||
use std::{
|
|
||||||
cell::{Cell, RefCell},
|
|
||||||
rc::Rc,
|
|
||||||
time::Duration,
|
|
||||||
};
|
|
||||||
|
|
||||||
const PLACEHOLDER_TEXT: &str = "URL or search term...";
|
|
||||||
|
|
||||||
// Progress bar animation setup
|
|
||||||
const PROGRESS_ANIMATION_STEP: f64 = 0.05;
|
|
||||||
const PROGRESS_ANIMATION_TIME: u64 = 20; //ms
|
|
||||||
|
|
||||||
struct Progress {
|
|
||||||
fraction: RefCell<f64>,
|
|
||||||
source_id: RefCell<Option<SourceId>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Widget {
|
|
||||||
pub entry: Entry,
|
|
||||||
progress: Rc<Progress>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Widget {
|
|
||||||
// Constructors
|
|
||||||
|
|
||||||
/// Build new `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),
|
|
||||||
source_id: RefCell::new(None),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Init main widget
|
|
||||||
let entry = Entry::builder()
|
|
||||||
.placeholder_text(PLACEHOLDER_TEXT)
|
|
||||||
.secondary_icon_tooltip_text("Go to the location")
|
|
||||||
.hexpand(true)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
// Connect events
|
|
||||||
entry.connect_icon_release({
|
|
||||||
let item_action = item_action.clone();
|
|
||||||
move |this, position| match position {
|
|
||||||
EntryIconPosition::Primary => item_action.ident.activate(), // @TODO PrimaryIcon impl
|
|
||||||
EntryIconPosition::Secondary => item_action.load.activate(Some(&this.text()), true),
|
|
||||||
_ => todo!(), // unexpected
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
entry.connect_has_focus_notify(|this| {
|
|
||||||
if this.focus_child().is_some_and(|text| text.has_focus()) {
|
|
||||||
this.set_secondary_icon_name(Some("pan-end-symbolic"));
|
|
||||||
} else {
|
|
||||||
this.set_secondary_icon_name(None);
|
|
||||||
this.select_region(0, 0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
entry.connect_changed({
|
|
||||||
let browser_action = browser_action.clone();
|
|
||||||
move |_| {
|
|
||||||
browser_action.update.activate(None);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
entry.connect_activate({
|
|
||||||
let item_action = item_action.clone();
|
|
||||||
move |entry| {
|
|
||||||
item_action.load.activate(Some(&entry.text()), true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
entry.connect_state_flags_changed({
|
|
||||||
// Define last focus state container
|
|
||||||
let has_focus = Cell::new(false);
|
|
||||||
move |entry, state| {
|
|
||||||
// Select entire text on first click (release)
|
|
||||||
// this behavior implemented in most web-browsers,
|
|
||||||
// to simply overwrite current request with new value
|
|
||||||
// Note:
|
|
||||||
// * Custom GestureClick is not an option here, as GTK Entry has default controller
|
|
||||||
// * This is experimental feature does not follow native GTK behavior @TODO make optional
|
|
||||||
if !has_focus.take()
|
|
||||||
&& state.contains(StateFlags::ACTIVE | StateFlags::FOCUS_WITHIN)
|
|
||||||
&& entry.selection_bounds().is_none()
|
|
||||||
{
|
|
||||||
entry.select_region(0, entry.text_length().into());
|
|
||||||
}
|
|
||||||
// Update last focus state
|
|
||||||
has_focus.replace(state.contains(StateFlags::FOCUS_WITHIN));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Return activated `Self`
|
|
||||||
Self { entry, progress }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Actions
|
|
||||||
pub fn clean(
|
|
||||||
&self,
|
|
||||||
transaction: &Transaction,
|
|
||||||
app_browser_window_tab_item_page_navigation_request_id: &i64,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
match database::select(
|
|
||||||
transaction,
|
|
||||||
app_browser_window_tab_item_page_navigation_request_id,
|
|
||||||
) {
|
|
||||||
Ok(records) => {
|
|
||||||
for record in records {
|
|
||||||
match database::delete(transaction, &record.id) {
|
|
||||||
Ok(_) => {
|
|
||||||
// Delegate clean action to the item childs
|
|
||||||
// nothing yet..
|
|
||||||
}
|
|
||||||
Err(e) => return Err(e.to_string()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => return Err(e.to_string()),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn restore(
|
|
||||||
&self,
|
|
||||||
transaction: &Transaction,
|
|
||||||
app_browser_window_tab_item_page_navigation_request_id: &i64,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
match database::select(
|
|
||||||
transaction,
|
|
||||||
app_browser_window_tab_item_page_navigation_request_id,
|
|
||||||
) {
|
|
||||||
Ok(records) => {
|
|
||||||
for record in records {
|
|
||||||
if let Some(text) = record.text {
|
|
||||||
self.entry.set_text(&text);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delegate restore action to the item childs
|
|
||||||
// nothing yet..
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => return Err(e.to_string()),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn save(
|
|
||||||
&self,
|
|
||||||
transaction: &Transaction,
|
|
||||||
app_browser_window_tab_item_page_navigation_request_id: &i64,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
// Keep value in memory until operation complete
|
|
||||||
let text = self.entry.text();
|
|
||||||
|
|
||||||
match database::insert(
|
|
||||||
transaction,
|
|
||||||
app_browser_window_tab_item_page_navigation_request_id,
|
|
||||||
match text.is_empty() {
|
|
||||||
true => None,
|
|
||||||
false => Some(text.as_str()),
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
Ok(_) => {
|
|
||||||
// let id = database::last_insert_id(transaction);
|
|
||||||
|
|
||||||
// Delegate save action to childs
|
|
||||||
// nothing yet..
|
|
||||||
}
|
|
||||||
Err(e) => return Err(e.to_string()),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(&self, is_identity_active: bool) {
|
|
||||||
// Update primary icon
|
|
||||||
self.entry
|
|
||||||
.first_child()
|
|
||||||
.unwrap()
|
|
||||||
.remove_css_class("success"); // @TODO handle
|
|
||||||
|
|
||||||
self.entry.set_primary_icon_activatable(false);
|
|
||||||
self.entry.set_primary_icon_sensitive(false);
|
|
||||||
|
|
||||||
match primary_icon::from(&self.entry.text()) {
|
|
||||||
PrimaryIcon::Download { name, tooltip } => {
|
|
||||||
self.entry.set_primary_icon_name(Some(name));
|
|
||||||
self.entry.set_primary_icon_tooltip_text(Some(tooltip));
|
|
||||||
}
|
|
||||||
PrimaryIcon::Gemini { name, tooltip } | PrimaryIcon::Titan { name, tooltip } => {
|
|
||||||
self.entry.set_primary_icon_activatable(true);
|
|
||||||
self.entry.set_primary_icon_sensitive(true);
|
|
||||||
self.entry.set_primary_icon_name(Some(name));
|
|
||||||
if is_identity_active {
|
|
||||||
self.entry.first_child().unwrap().add_css_class("success"); // @TODO handle
|
|
||||||
self.entry.set_primary_icon_tooltip_text(Some(tooltip.1));
|
|
||||||
} else {
|
|
||||||
self.entry.set_primary_icon_tooltip_text(Some(tooltip.0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PrimaryIcon::Search { name, tooltip } => {
|
|
||||||
self.entry.set_primary_icon_name(Some(name));
|
|
||||||
self.entry.set_primary_icon_tooltip_text(Some(tooltip));
|
|
||||||
}
|
|
||||||
PrimaryIcon::Source { name, tooltip } => {
|
|
||||||
self.entry.set_primary_icon_name(Some(name));
|
|
||||||
self.entry.set_primary_icon_tooltip_text(Some(tooltip));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update progress
|
|
||||||
// * @TODO skip update animation for None value
|
|
||||||
let value = self.entry.progress_fraction();
|
|
||||||
|
|
||||||
// Update shared fraction on new value was changed
|
|
||||||
if value != self.progress.fraction.replace(value) {
|
|
||||||
// Start new frame on previous process function completed (`source_id` changed to None)
|
|
||||||
// If previous process still active, we have just updated shared fraction value before, to use it inside the active process
|
|
||||||
if self.progress.source_id.borrow().is_none() {
|
|
||||||
// Start new animation frame iterator, update `source_id`
|
|
||||||
self.progress.source_id.replace(Some(timeout_add_local(
|
|
||||||
Duration::from_millis(PROGRESS_ANIMATION_TIME),
|
|
||||||
{
|
|
||||||
// Clone async pointers dependency
|
|
||||||
let entry = self.entry.clone();
|
|
||||||
let progress = self.progress.clone();
|
|
||||||
|
|
||||||
// Frame
|
|
||||||
move || {
|
|
||||||
// Animate
|
|
||||||
if *progress.fraction.borrow() > entry.progress_fraction() {
|
|
||||||
entry.set_progress_fraction(
|
|
||||||
// Currently, here is no outrange validation, seems that wrapper make this work @TODO
|
|
||||||
entry.progress_fraction() + PROGRESS_ANIMATION_STEP,
|
|
||||||
);
|
|
||||||
return ControlFlow::Continue;
|
|
||||||
}
|
|
||||||
// Deactivate
|
|
||||||
progress.source_id.replace(None);
|
|
||||||
|
|
||||||
// Reset on 100% (to hide progress bar)
|
|
||||||
// or, just await for new value request
|
|
||||||
if entry.progress_fraction() == 1.0 {
|
|
||||||
entry.set_progress_fraction(0.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop iteration
|
|
||||||
ControlFlow::Break
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tools
|
|
||||||
|
|
||||||
pub fn migrate(tx: &Transaction) -> Result<(), String> {
|
|
||||||
// Migrate self components
|
|
||||||
if let Err(e) = database::init(tx) {
|
|
||||||
return Err(e.to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delegate migration to childs
|
|
||||||
// nothing yet..
|
|
||||||
|
|
||||||
// Success
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
@ -1,80 +0,0 @@
|
||||||
use sqlite::{Error, Transaction};
|
|
||||||
|
|
||||||
pub struct Table {
|
|
||||||
pub id: i64,
|
|
||||||
// pub app_browser_window_tab_item_page_navigation_request_id: i64, not in use
|
|
||||||
pub text: Option<String>, // can be stored as NULL
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init(tx: &Transaction) -> Result<usize, Error> {
|
|
||||||
tx.execute(
|
|
||||||
"CREATE TABLE IF NOT EXISTS `app_browser_window_tab_item_page_navigation_request_widget`
|
|
||||||
(
|
|
||||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
||||||
`app_browser_window_tab_item_page_navigation_request_id` INTEGER NOT NULL,
|
|
||||||
`text` VARCHAR(1024),
|
|
||||||
|
|
||||||
FOREIGN KEY (`app_browser_window_tab_item_page_navigation_request_id`) REFERENCES `app_browser_window_tab_item_page_navigation_request`(`id`)
|
|
||||||
)",
|
|
||||||
[],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert(
|
|
||||||
tx: &Transaction,
|
|
||||||
app_browser_window_tab_item_page_navigation_request_id: &i64,
|
|
||||||
text: Option<&str>,
|
|
||||||
) -> Result<usize, Error> {
|
|
||||||
tx.execute(
|
|
||||||
"INSERT INTO `app_browser_window_tab_item_page_navigation_request_widget` (
|
|
||||||
`app_browser_window_tab_item_page_navigation_request_id`,
|
|
||||||
`text`
|
|
||||||
) VALUES (?, ?)",
|
|
||||||
(app_browser_window_tab_item_page_navigation_request_id, text),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn select(
|
|
||||||
tx: &Transaction,
|
|
||||||
app_browser_window_tab_item_page_navigation_request_id: &i64,
|
|
||||||
) -> Result<Vec<Table>, Error> {
|
|
||||||
let mut stmt = tx.prepare(
|
|
||||||
"SELECT `id`,
|
|
||||||
`app_browser_window_tab_item_page_navigation_request_id`,
|
|
||||||
`text`
|
|
||||||
FROM `app_browser_window_tab_item_page_navigation_request_widget`
|
|
||||||
WHERE `app_browser_window_tab_item_page_navigation_request_id` = ?",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let result = stmt.query_map(
|
|
||||||
[app_browser_window_tab_item_page_navigation_request_id],
|
|
||||||
|row| {
|
|
||||||
Ok(Table {
|
|
||||||
id: row.get(0)?,
|
|
||||||
// app_browser_window_tab_item_page_navigation_request_id: row.get(1)?, not in use
|
|
||||||
text: row.get(2)?,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let mut records = Vec::new();
|
|
||||||
|
|
||||||
for record in result {
|
|
||||||
let table = record?;
|
|
||||||
records.push(table);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(records)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn delete(tx: &Transaction, id: &i64) -> Result<usize, Error> {
|
|
||||||
tx.execute(
|
|
||||||
"DELETE FROM `app_browser_window_tab_item_page_navigation_request_widget` WHERE `id` = ?",
|
|
||||||
[id],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/* not in use
|
|
||||||
pub fn last_insert_id(tx: &Transaction) -> i64 {
|
|
||||||
tx.last_insert_rowid()
|
|
||||||
} */
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue