isolate request entry, fix proxy resolver change on entry update (by using sync lookup impl)

This commit is contained in:
yggverse 2025-07-29 16:57:17 +03:00
parent 65c5ffebb0
commit 6e4eec54f0
8 changed files with 88 additions and 102 deletions

View File

@ -76,8 +76,8 @@ impl Tab {
if let Some(item) = index.borrow_mut().remove(tab_page) {
// keep removed `Item` reference in the memory (to reopen from the main menu)
// * skip item with blank request
if !item.page.navigation.request().is_empty() {
profile.history.close(&item.page.navigation.request());
if !item.page.navigation.request.is_empty() {
profile.history.close(&item.page.navigation.request.text());
}
}
// reassign global actions to active tab
@ -160,7 +160,7 @@ impl Tab {
// Expect user input on tab appended has empty request entry
// * 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()) {
item.page.navigation.grab_focus();
item.page.navigation.request.grab_focus();
}
// Relate with GTK `TabPage` with app `Item`
@ -221,7 +221,7 @@ impl Tab {
// Save page at given `position`, `None` to save selected page (if available)
pub fn save_as(&self, page_position: Option<i32>) {
if let Some(item) = self.item(page_position) {
item.page.navigation.to_download();
item.page.navigation.request.to_download();
self.window_action.reload.activate();
}
}
@ -229,7 +229,7 @@ impl Tab {
// View source for page at given `position`, `None` to use selected page (if available)
pub fn source(&self, page_position: Option<i32>) {
if let Some(item) = self.item(page_position) {
item.page.navigation.to_source();
item.page.navigation.request.to_source();
self.window_action.reload.activate();
}
}
@ -273,7 +273,7 @@ impl Tab {
pub fn reload(&self, page_position: Option<i32>) {
if let Some(item) = self.item(page_position) {
item.client
.handle(&item.page.navigation.request(), true, false);
.handle(&item.page.navigation.request.text(), true, false);
}
}
@ -414,7 +414,7 @@ fn update_actions(
window_action
.save_as
.simple_action
.set_enabled(!item.page.navigation.is_file());
.set_enabled(!item.page.navigation.request.is_file());
window_action.change_state(Some(tab_view.page_position(tab_page)));
return;

View File

@ -83,9 +83,9 @@ impl Item {
let page = page.clone();
move |this, _| {
this.set_enabled(false);
if let Some(uri) = page.navigation.home() {
if let Some(uri) = page.navigation.request.home() {
let request = uri.to_string();
page.navigation.set_request(&request);
page.navigation.request.set_text(&request);
client.handle(&request, true, false);
}
}
@ -96,7 +96,7 @@ impl Item {
let client = client.clone();
move |request, is_snap_history, is_redirect| {
if let Some(request) = request {
page.navigation.set_request(&request);
page.navigation.request.set_text(&request);
client.handle(&request, is_snap_history, is_redirect);
}
}
@ -110,7 +110,7 @@ impl Item {
action.reload.connect_activate({
let page = page.clone();
let client = client.clone();
move |_, _| client.handle(&page.navigation.request(), true, false)
move |_, _| client.handle(&page.navigation.request.text(), true, false)
});
action.reload.connect_enabled_notify({
@ -145,7 +145,7 @@ impl Item {
// Handle immediately on request
if let Some(request) = request {
page.navigation.set_request(request);
page.navigation.request.set_text(request);
if is_load {
client.handle(request, true, false)
}

View File

@ -569,7 +569,7 @@ fn handle(
} else {
let t = target.to_string();
if matches!(redirect, Redirect::Permanent { .. }) {
page.navigation.set_request(&t);
page.navigation.request.set_text(&t);
}
redirects.replace(total);
{

View File

@ -59,7 +59,7 @@ impl Nex {
.request
.info
.replace(i.into_permanent_redirect());
self.page.navigation.set_request(&r);
self.page.navigation.request.set_text(&r);
self.page.item_action.load.activate(Some(&r), false, true);
return; // prevents operation cancelled message on redirect
}

View File

@ -98,10 +98,10 @@ impl Page {
pub fn snap_history(&self) {
self.item_action
.history
.add(self.navigation.request(), true);
.add(self.navigation.request.text(), true);
self.profile
.history
.open(self.navigation.request(), Some(self.title()))
.open(self.navigation.request.text(), Some(self.title()))
}
/// Cleanup session for `Self`
@ -136,7 +136,7 @@ impl Page {
// Make initial page history snap
self.profile
.history
.open(self.navigation.request(), Some(self.title()));
.open(self.navigation.request.text(), Some(self.title()));
}
Ok(())
}
@ -176,7 +176,9 @@ impl Page {
}
pub fn set_progress(&self, progress_fraction: f64) {
self.navigation.set_progress_fraction(progress_fraction);
self.navigation
.request
.set_progress_fraction(progress_fraction);
self.tab_page.set_loading(progress_fraction > 0.0)
}

View File

@ -8,11 +8,7 @@ mod request;
use super::{ItemAction, Profile, TabAction, WindowAction};
use anyhow::Result;
use bookmark::Bookmark;
use gtk::{
Box, Button, Orientation,
glib::{GString, Uri},
prelude::{BoxExt, EditableExt, EntryExt, WidgetExt},
};
use gtk::{Box, Button, Orientation, prelude::BoxExt};
use history::History;
use home::Home;
use reload::Reload;
@ -43,7 +39,7 @@ impl Navigation {
let request = Rc::new(Request::build(item_action, profile));
let reload = Button::reload((window_action, tab_action, item_action), &request);
let home = Button::home((window_action, tab_action, item_action), &request);
let bookmark = Rc::new(Bookmark::build(window_action, profile, &request.entry));
let bookmark = Rc::new(Bookmark::build(window_action, profile, &request));
// Init main widget
let g_box = Box::builder()
@ -57,7 +53,7 @@ impl Navigation {
g_box.append(&home);
g_box.append(&history);
g_box.append(&reload);
g_box.append(&request.entry);
request.append_to(&g_box); // private member
g_box.append(&bookmark.button);
Self {
@ -114,45 +110,9 @@ impl Navigation {
Ok(())
}
pub fn grab_focus(&self) -> bool {
self.request.entry.grab_focus()
}
pub fn show_identity_dialog(&self) {
self.request.show_identity_dialog()
}
// Setters
pub fn set_request(&self, value: &str) {
self.request.entry.set_text(value);
}
pub fn set_progress_fraction(&self, value: f64) {
self.request.entry.set_progress_fraction(value);
}
pub fn to_download(&self) {
self.request.to_download();
}
pub fn to_source(&self) {
self.request.to_source();
}
// Getters
pub fn request(&self) -> GString {
self.request.entry.text()
}
pub fn home(&self) -> Option<Uri> {
self.request.home()
}
pub fn is_file(&self) -> bool {
self.request.is_file()
}
}
// Tools

View File

@ -1,7 +1,7 @@
use super::{Profile, WindowAction};
use super::{Profile, Request, WindowAction};
use gtk::{
Button, Entry,
prelude::{ActionExt, ButtonExt, EditableExt, WidgetExt},
Button,
prelude::{ActionExt, ButtonExt, WidgetExt},
};
use std::rc::Rc;
@ -10,12 +10,12 @@ const TOOLTIP_TEXT: (&str, &str) = ("Add Bookmark", "Remove Bookmark");
pub struct Bookmark {
profile: Rc<Profile>,
request: Entry,
request: Rc<Request>,
pub button: Button,
}
impl Bookmark {
pub fn build(action: &Rc<WindowAction>, profile: &Rc<Profile>, request: &Entry) -> Self {
pub fn build(action: &Rc<WindowAction>, profile: &Rc<Profile>, request: &Rc<Request>) -> Self {
let button = Button::builder()
.action_name(format!(
"{}.{}",
@ -23,11 +23,12 @@ impl Bookmark {
action.bookmark.simple_action.name()
))
.build();
update(profile, &button, request.text());
request.connect_changed({
let profile = profile.clone();
let button = button.clone();
move |entry| update(&profile, &button, entry.text())
update(profile, &button, &request.text());
request.on_change({
let b = button.clone();
let p = profile.clone();
let r = request.clone();
move || update(&p, &b, &r.text())
});
Self {
profile: profile.clone(),
@ -65,14 +66,10 @@ fn icon_name(has_bookmark: bool) -> &'static str {
}
}
fn update(profile: &Rc<Profile>, button: &Button, request: gtk::glib::GString) {
let profile = profile.clone();
let button = button.clone();
gtk::glib::spawn_future_local(async move {
fn update(profile: &Profile, button: &Button, request: &str) {
button.set_sensitive(false); // lock
let has_bookmark = profile.bookmark.is_match_request(&request);
let has_bookmark = profile.bookmark.is_match_request(request);
button.set_icon_name(icon_name(has_bookmark));
button.set_tooltip_text(Some(tooltip_text(has_bookmark)));
button.set_sensitive(true);
}); // may take a while
}

View File

@ -27,7 +27,8 @@ const PREFIX_DOWNLOAD: &str = "download:";
const PREFIX_SOURCE: &str = "source:";
pub struct Request {
pub entry: Entry,
/// * keep it private to properly update some local dependencies on change (e.g. proxy resolver)
entry: Entry,
pub info: Rc<RefCell<Info>>,
profile: Rc<Profile>,
proxy_resolver: Rc<RefCell<Option<ProxyResolver>>>,
@ -290,6 +291,27 @@ impl Request {
self.entry.text().starts_with("file://")
}
pub fn is_empty(&self) -> bool {
self.entry.text_length() > 0
}
pub fn grab_focus(&self) -> bool {
self.entry.grab_focus()
}
pub fn set_progress_fraction(&self, value: f64) {
self.entry.set_progress_fraction(value);
}
pub fn set_text(&self, value: &str) {
self.entry.set_text(value);
self.refresh()
}
pub fn text(&self) -> GString {
self.entry.text()
}
/// Get [ProxyResolver](https://docs.gtk.org/gio/iface.ProxyResolver.html)
/// which is constructed for every `Request` entry change
/// * useful on build new [SocketClient](https://docs.gtk.org/gio/class.SocketClient.html)
@ -298,6 +320,15 @@ impl Request {
self.proxy_resolver.borrow().clone()
}
pub fn append_to(&self, parent: &gtk::Box) {
use gtk::prelude::BoxExt;
parent.append(&self.entry)
}
pub fn on_change(&self, callback: impl Fn() + 'static) {
self.entry.connect_changed(move |_| callback());
}
// Tools
/// Get request value with formatted `download` prefix
@ -474,34 +505,30 @@ fn update_blocked(
}
/// Indicate proxy connections @TODO cancel previous operation on update
/// * note: it's important to use sync lookup method by the current impl
fn refresh_proxy_resolver(
entry: &Entry,
profile: &Rc<Profile>,
resolver: &Rc<RefCell<Option<ProxyResolver>>>,
profile: &Profile,
resolver: &RefCell<Option<ProxyResolver>>,
) {
const NONE: &[&str] = &[];
let t = entry.text(); // allocate once
match profile.proxy.matches(&t) {
Some(m) => m.clone().lookup_async(&t, Cancellable::NONE, {
let e = entry.clone();
let p = profile.clone();
let r = resolver.clone();
move |l| {
let (css_classes, tooltip_text) = match l {
Some(m) => {
let (css_classes, tooltip_text) = match m.lookup(&t, Cancellable::NONE) {
Ok(h) => (&["accent"], format!("Proxy over {}", h.join(","))),
Err(i) => (&["error"], i.to_string()),
};
e.set_css_classes(if p.proxy.misc.is_highlight_request_entry() {
entry.set_css_classes(if profile.proxy.misc.is_highlight_request_entry() {
css_classes
} else {
NONE
});
e.set_tooltip_text(Some(&tooltip_text));
r.replace(Some(m));
entry.set_tooltip_text(Some(&tooltip_text));
resolver.replace(Some(m));
}
}),
None => {
entry.set_css_classes(NONE);
entry.set_tooltip_text(None);