mirror of
https://github.com/YGGverse/Yoda.git
synced 2026-04-01 09:05:27 +00:00
use single click for selection and double click for activation, hide suggestions on escape, replace request trait with struct
This commit is contained in:
parent
3e4423eca7
commit
4a2996d3b7
6 changed files with 235 additions and 245 deletions
|
|
@ -84,7 +84,8 @@ impl Page {
|
||||||
|
|
||||||
/// Request `Escape` action for all page components
|
/// Request `Escape` action for all page components
|
||||||
pub fn escape(&self) {
|
pub fn escape(&self) {
|
||||||
self.search.hide()
|
self.search.hide();
|
||||||
|
self.navigation.escape();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Toggle `Find` widget
|
/// Toggle `Find` widget
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ use bookmark::Bookmark;
|
||||||
use gtk::{
|
use gtk::{
|
||||||
glib::{GString, Uri},
|
glib::{GString, Uri},
|
||||||
prelude::{BoxExt, EditableExt, EntryExt, WidgetExt},
|
prelude::{BoxExt, EditableExt, EntryExt, WidgetExt},
|
||||||
Box, Button, Entry, Orientation,
|
Box, Button, Orientation,
|
||||||
};
|
};
|
||||||
use history::History;
|
use history::History;
|
||||||
use home::Home;
|
use home::Home;
|
||||||
|
|
@ -24,8 +24,7 @@ const MARGIN: i32 = 6;
|
||||||
const SPACING: i32 = 6;
|
const SPACING: i32 = 6;
|
||||||
|
|
||||||
pub struct Navigation {
|
pub struct Navigation {
|
||||||
profile: Rc<Profile>,
|
request: Rc<Request>,
|
||||||
request: Entry,
|
|
||||||
pub g_box: Box,
|
pub g_box: Box,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -40,10 +39,10 @@ impl Navigation {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
// 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 = Entry::request(item_action, profile);
|
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, profile, &request);
|
let bookmark = Button::bookmark(window_action, profile, &request.entry);
|
||||||
|
|
||||||
// Init main widget
|
// Init main widget
|
||||||
let g_box = Box::builder()
|
let g_box = Box::builder()
|
||||||
|
|
@ -57,18 +56,18 @@ impl Navigation {
|
||||||
g_box.append(&home);
|
g_box.append(&home);
|
||||||
g_box.append(&history);
|
g_box.append(&history);
|
||||||
g_box.append(&reload);
|
g_box.append(&reload);
|
||||||
g_box.append(&request);
|
g_box.append(&request.entry);
|
||||||
g_box.append(&bookmark);
|
g_box.append(&bookmark);
|
||||||
|
|
||||||
Self {
|
Self { request, g_box }
|
||||||
profile: profile.clone(),
|
|
||||||
request,
|
|
||||||
g_box,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
|
|
||||||
|
pub fn escape(&self) {
|
||||||
|
self.request.escape();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn clean(
|
pub fn clean(
|
||||||
&self,
|
&self,
|
||||||
transaction: &Transaction,
|
transaction: &Transaction,
|
||||||
|
|
@ -106,21 +105,21 @@ impl Navigation {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn grab_focus(&self) -> bool {
|
pub fn grab_focus(&self) -> bool {
|
||||||
self.request.grab_focus()
|
self.request.entry.grab_focus()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show_identity_dialog(&self) {
|
pub fn show_identity_dialog(&self) {
|
||||||
self.request.show_identity_dialog(&self.profile)
|
self.request.show_identity_dialog()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setters
|
// Setters
|
||||||
|
|
||||||
pub fn set_request(&self, value: &str) {
|
pub fn set_request(&self, value: &str) {
|
||||||
self.request.set_text(value);
|
self.request.entry.set_text(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_progress_fraction(&self, value: f64) {
|
pub fn set_progress_fraction(&self, value: f64) {
|
||||||
self.request.set_progress_fraction(value);
|
self.request.entry.set_progress_fraction(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_download(&self) {
|
pub fn to_download(&self) {
|
||||||
|
|
@ -134,7 +133,7 @@ impl Navigation {
|
||||||
// Getters
|
// Getters
|
||||||
|
|
||||||
pub fn request(&self) -> GString {
|
pub fn request(&self) -> GString {
|
||||||
self.request.text()
|
self.request.entry.text()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn home(&self) -> Option<Uri> {
|
pub fn home(&self) -> Option<Uri> {
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,15 @@ use crate::app::browser::window::action::Position;
|
||||||
use gtk::{
|
use gtk::{
|
||||||
gdk::BUTTON_MIDDLE,
|
gdk::BUTTON_MIDDLE,
|
||||||
prelude::{ActionExt, WidgetExt},
|
prelude::{ActionExt, WidgetExt},
|
||||||
Button, Entry, GestureClick,
|
Button, GestureClick,
|
||||||
};
|
};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub trait Home {
|
pub trait Home {
|
||||||
fn home(action: (&Rc<WindowAction>, &Rc<TabAction>, &Rc<ItemAction>), request: &Entry) -> Self;
|
fn home(
|
||||||
|
action: (&Rc<WindowAction>, &Rc<TabAction>, &Rc<ItemAction>),
|
||||||
|
request: &Rc<Request>,
|
||||||
|
) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Home for Button {
|
impl Home for Button {
|
||||||
|
|
@ -18,7 +21,7 @@ impl Home for Button {
|
||||||
&Rc<TabAction>,
|
&Rc<TabAction>,
|
||||||
&Rc<ItemAction>,
|
&Rc<ItemAction>,
|
||||||
),
|
),
|
||||||
request: &Entry,
|
request: &Rc<Request>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let button = Button::builder()
|
let button = Button::builder()
|
||||||
.action_name(format!("{}.{}", tab_action.id, item_action.home.name()))
|
.action_name(format!("{}.{}", tab_action.id, item_action.home.name()))
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,14 @@ use crate::app::browser::window::action::Position;
|
||||||
use gtk::{
|
use gtk::{
|
||||||
gdk::BUTTON_MIDDLE,
|
gdk::BUTTON_MIDDLE,
|
||||||
prelude::{ActionExt, WidgetExt},
|
prelude::{ActionExt, WidgetExt},
|
||||||
Button, Entry, GestureClick,
|
Button, GestureClick,
|
||||||
};
|
};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub trait Reload {
|
pub trait Reload {
|
||||||
fn reload(
|
fn reload(
|
||||||
action: (&Rc<WindowAction>, &Rc<TabAction>, &Rc<ItemAction>),
|
action: (&Rc<WindowAction>, &Rc<TabAction>, &Rc<ItemAction>),
|
||||||
request: &Entry,
|
request: &Rc<Request>,
|
||||||
) -> Self;
|
) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -21,7 +21,7 @@ impl Reload for Button {
|
||||||
&Rc<TabAction>,
|
&Rc<TabAction>,
|
||||||
&Rc<ItemAction>,
|
&Rc<ItemAction>,
|
||||||
),
|
),
|
||||||
request: &Entry,
|
request: &Rc<Request>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let button = Button::builder()
|
let button = Button::builder()
|
||||||
.action_name(format!("{}.{}", tab_action.id, item_action.reload.name()))
|
.action_name(format!("{}.{}", tab_action.id, item_action.reload.name()))
|
||||||
|
|
|
||||||
|
|
@ -20,57 +20,17 @@ use suggestion::Suggestion;
|
||||||
const PREFIX_DOWNLOAD: &str = "download:";
|
const PREFIX_DOWNLOAD: &str = "download:";
|
||||||
const PREFIX_SOURCE: &str = "source:";
|
const PREFIX_SOURCE: &str = "source:";
|
||||||
|
|
||||||
pub trait Request {
|
pub struct Request {
|
||||||
// Constructors
|
pub entry: Entry,
|
||||||
|
suggestion: Rc<Suggestion>,
|
||||||
fn request(item_action: &Rc<ItemAction>, profile: &Rc<Profile>) -> Self;
|
profile: Rc<Profile>,
|
||||||
|
|
||||||
// Actions
|
|
||||||
|
|
||||||
fn clean(
|
|
||||||
&self,
|
|
||||||
transaction: &Transaction,
|
|
||||||
app_browser_window_tab_item_page_navigation_id: &i64,
|
|
||||||
) -> Result<()>;
|
|
||||||
|
|
||||||
fn restore(
|
|
||||||
&self,
|
|
||||||
transaction: &Transaction,
|
|
||||||
app_browser_window_tab_item_page_navigation_id: &i64,
|
|
||||||
) -> Result<()>;
|
|
||||||
|
|
||||||
fn save(
|
|
||||||
&self,
|
|
||||||
transaction: &Transaction,
|
|
||||||
app_browser_window_tab_item_page_navigation_id: &i64,
|
|
||||||
) -> Result<()>;
|
|
||||||
|
|
||||||
fn update_primary_icon(&self, profile: &Profile);
|
|
||||||
fn update_secondary_icon(&self);
|
|
||||||
|
|
||||||
fn show_identity_dialog(&self, profile: &Rc<Profile>);
|
|
||||||
fn show_search_dialog(&self, profile: &Rc<Profile>);
|
|
||||||
|
|
||||||
// Setters
|
|
||||||
|
|
||||||
fn to_download(&self);
|
|
||||||
fn to_source(&self);
|
|
||||||
|
|
||||||
// Getters
|
|
||||||
|
|
||||||
fn prefix_less(&self) -> GString;
|
|
||||||
fn download(&self) -> GString;
|
|
||||||
fn source(&self) -> GString;
|
|
||||||
fn uri(&self) -> Option<Uri>;
|
|
||||||
fn home(&self) -> Option<Uri>;
|
|
||||||
fn is_file(&self) -> bool;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Request for Entry {
|
impl Request {
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
/// Build new `Self`
|
/// Build new `Self`
|
||||||
fn request(item_action: &Rc<ItemAction>, profile: &Rc<Profile>) -> Self {
|
pub fn build(item_action: &Rc<ItemAction>, profile: &Rc<Profile>) -> Self {
|
||||||
// Init main widget
|
// Init main widget
|
||||||
let entry = Entry::builder()
|
let entry = Entry::builder()
|
||||||
.placeholder_text("URL or search term...")
|
.placeholder_text("URL or search term...")
|
||||||
|
|
@ -79,7 +39,7 @@ impl Request for Entry {
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// Detect primary icon on construct
|
// Detect primary icon on construct
|
||||||
entry.update_primary_icon(profile);
|
update_primary_icon(&entry, profile);
|
||||||
|
|
||||||
// Init additional features
|
// Init additional features
|
||||||
let suggestion = Rc::new(Suggestion::build(profile, &entry));
|
let suggestion = Rc::new(Suggestion::build(profile, &entry));
|
||||||
|
|
@ -90,9 +50,9 @@ impl Request for Entry {
|
||||||
move |this, position| match position {
|
move |this, position| match position {
|
||||||
EntryIconPosition::Primary => {
|
EntryIconPosition::Primary => {
|
||||||
if matches!(primary_icon::from(&this.text()), PrimaryIcon::Search { .. }) {
|
if matches!(primary_icon::from(&this.text()), PrimaryIcon::Search { .. }) {
|
||||||
this.show_search_dialog(&profile)
|
show_search_dialog(this, &profile)
|
||||||
} else {
|
} else {
|
||||||
this.show_identity_dialog(&profile)
|
show_identity_dialog(this, &profile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EntryIconPosition::Secondary => this.emit_activate(),
|
EntryIconPosition::Secondary => this.emit_activate(),
|
||||||
|
|
@ -100,7 +60,7 @@ impl Request for Entry {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
entry.connect_has_focus_notify(|this| this.update_secondary_icon());
|
entry.connect_has_focus_notify(update_secondary_icon);
|
||||||
|
|
||||||
suggestion
|
suggestion
|
||||||
.signal_handler_id
|
.signal_handler_id
|
||||||
|
|
@ -112,11 +72,11 @@ impl Request for Entry {
|
||||||
move |this| {
|
move |this| {
|
||||||
// Update actions
|
// Update actions
|
||||||
item_action.reload.set_enabled(!this.text().is_empty());
|
item_action.reload.set_enabled(!this.text().is_empty());
|
||||||
item_action.home.set_enabled(this.home().is_some());
|
item_action.home.set_enabled(home(this).is_some());
|
||||||
|
|
||||||
// Update icons
|
// Update icons
|
||||||
this.update_primary_icon(&profile);
|
update_primary_icon(this, &profile);
|
||||||
this.update_secondary_icon();
|
update_secondary_icon(this);
|
||||||
|
|
||||||
// Show search suggestions
|
// Show search suggestions
|
||||||
if this.focus_child().is_some() {
|
if this.focus_child().is_some() {
|
||||||
|
|
@ -156,11 +116,35 @@ impl Request for Entry {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
entry
|
Self {
|
||||||
|
entry,
|
||||||
|
suggestion,
|
||||||
|
profile: profile.clone(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
fn clean(
|
pub fn escape(&self) {
|
||||||
|
self.suggestion.hide()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Try build home [Uri](https://docs.gtk.org/glib/struct.Uri.html) for `Self`
|
||||||
|
/// * return `None` if current request already match home or Uri not parsable
|
||||||
|
pub fn home(&self) -> Option<Uri> {
|
||||||
|
home(&self.entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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(&self.entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show_identity_dialog(&self) {
|
||||||
|
show_identity_dialog(&self.entry, &self.profile)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clean(
|
||||||
&self,
|
&self,
|
||||||
transaction: &Transaction,
|
transaction: &Transaction,
|
||||||
app_browser_window_tab_item_page_navigation_id: &i64,
|
app_browser_window_tab_item_page_navigation_id: &i64,
|
||||||
|
|
@ -174,7 +158,7 @@ impl Request for Entry {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn restore(
|
pub fn restore(
|
||||||
&self,
|
&self,
|
||||||
transaction: &Transaction,
|
transaction: &Transaction,
|
||||||
app_browser_window_tab_item_page_navigation_id: &i64,
|
app_browser_window_tab_item_page_navigation_id: &i64,
|
||||||
|
|
@ -182,7 +166,7 @@ impl Request for Entry {
|
||||||
for record in database::select(transaction, app_browser_window_tab_item_page_navigation_id)?
|
for record in database::select(transaction, app_browser_window_tab_item_page_navigation_id)?
|
||||||
{
|
{
|
||||||
if let Some(text) = record.text {
|
if let Some(text) = record.text {
|
||||||
self.set_text(&text);
|
self.entry.set_text(&text);
|
||||||
}
|
}
|
||||||
// Delegate restore action to the item childs
|
// Delegate restore action to the item childs
|
||||||
// nothing yet..
|
// nothing yet..
|
||||||
|
|
@ -190,13 +174,13 @@ impl Request for Entry {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn save(
|
pub fn save(
|
||||||
&self,
|
&self,
|
||||||
transaction: &Transaction,
|
transaction: &Transaction,
|
||||||
app_browser_window_tab_item_page_navigation_id: &i64,
|
app_browser_window_tab_item_page_navigation_id: &i64,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// Keep value in memory until operation complete
|
// Keep value in memory until operation complete
|
||||||
let text = self.text();
|
let text = self.entry.text();
|
||||||
let _id = database::insert(
|
let _id = database::insert(
|
||||||
transaction,
|
transaction,
|
||||||
app_browser_window_tab_item_page_navigation_id,
|
app_browser_window_tab_item_page_navigation_id,
|
||||||
|
|
@ -210,154 +194,32 @@ impl Request for Entry {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_primary_icon(&self, profile: &Profile) {
|
|
||||||
self.first_child().unwrap().remove_css_class("success"); // @TODO handle
|
|
||||||
|
|
||||||
match primary_icon::from(&self.text()) {
|
|
||||||
PrimaryIcon::Download { name, tooltip } | PrimaryIcon::File { name, tooltip } => {
|
|
||||||
self.set_primary_icon_activatable(false);
|
|
||||||
self.set_primary_icon_sensitive(false);
|
|
||||||
self.set_primary_icon_name(Some(name));
|
|
||||||
self.set_primary_icon_tooltip_text(Some(tooltip));
|
|
||||||
}
|
|
||||||
PrimaryIcon::Gemini { name, tooltip } | PrimaryIcon::Titan { name, tooltip } => {
|
|
||||||
self.set_primary_icon_activatable(true);
|
|
||||||
self.set_primary_icon_sensitive(true);
|
|
||||||
self.set_primary_icon_name(Some(name));
|
|
||||||
if profile.identity.get(&self.prefix_less()).is_some() {
|
|
||||||
self.first_child().unwrap().add_css_class("success"); // @TODO handle
|
|
||||||
self.set_primary_icon_tooltip_text(Some(tooltip.1));
|
|
||||||
} else {
|
|
||||||
self.set_primary_icon_tooltip_text(Some(tooltip.0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PrimaryIcon::Search { name, tooltip } => {
|
|
||||||
self.set_primary_icon_activatable(true);
|
|
||||||
self.set_primary_icon_sensitive(true);
|
|
||||||
self.set_primary_icon_name(Some(name));
|
|
||||||
self.set_primary_icon_tooltip_text(Some(tooltip));
|
|
||||||
}
|
|
||||||
PrimaryIcon::Source { name, tooltip } => {
|
|
||||||
self.set_primary_icon_activatable(false);
|
|
||||||
self.set_primary_icon_sensitive(false);
|
|
||||||
self.set_primary_icon_name(Some(name));
|
|
||||||
self.set_primary_icon_tooltip_text(Some(tooltip));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_secondary_icon(&self) {
|
|
||||||
if !self.text().is_empty() && self.focus_child().is_some_and(|text| text.has_focus()) {
|
|
||||||
self.set_secondary_icon_name(Some("pan-end-symbolic"));
|
|
||||||
} else {
|
|
||||||
self.set_secondary_icon_name(None);
|
|
||||||
self.select_region(0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Present Identity [AlertDialog](https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.AlertDialog.html) for `Self`
|
|
||||||
fn show_identity_dialog(&self, profile: &Rc<Profile>) {
|
|
||||||
// connect identity traits
|
|
||||||
use identity::{Common, Unsupported};
|
|
||||||
if let Some(uri) = self.uri() {
|
|
||||||
if ["gemini", "titan"].contains(&uri.scheme().as_str()) {
|
|
||||||
return AlertDialog::common(
|
|
||||||
profile,
|
|
||||||
&uri,
|
|
||||||
&Rc::new({
|
|
||||||
let profile = profile.clone();
|
|
||||||
let this = self.clone();
|
|
||||||
move |is_reload| {
|
|
||||||
this.update_primary_icon(&profile);
|
|
||||||
if is_reload {
|
|
||||||
this.emit_activate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.present(Some(self));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AlertDialog::unsupported().present(Some(self));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Present Search providers [AlertDialog](https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.AlertDialog.html) for `Self`
|
|
||||||
fn show_search_dialog(&self, profile: &Rc<Profile>) {
|
|
||||||
use search::Search;
|
|
||||||
AlertDialog::search(profile).present(Some(self))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setters
|
// Setters
|
||||||
|
|
||||||
fn to_download(&self) {
|
pub fn to_download(&self) {
|
||||||
self.set_text(&self.download());
|
self.entry.set_text(&self.download());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_source(&self) {
|
pub fn to_source(&self) {
|
||||||
self.set_text(&self.source());
|
self.entry.set_text(&self.source());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getters
|
// Getters
|
||||||
|
|
||||||
/// Get current request value without system prefix
|
pub fn is_file(&self) -> bool {
|
||||||
/// * the `prefix` is not `scheme`
|
self.entry.text().starts_with("file://")
|
||||||
fn prefix_less(&self) -> GString {
|
|
||||||
let mut request = self.text();
|
|
||||||
|
|
||||||
if let Some(postfix) = request.strip_prefix(PREFIX_SOURCE) {
|
|
||||||
request = postfix.into()
|
|
||||||
}
|
|
||||||
if let Some(postfix) = request.strip_prefix(PREFIX_DOWNLOAD) {
|
|
||||||
request = postfix.into()
|
|
||||||
}
|
|
||||||
request
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tools
|
||||||
|
|
||||||
/// Get request value with formatted `download` prefix
|
/// Get request value with formatted `download` prefix
|
||||||
fn download(&self) -> GString {
|
fn download(&self) -> GString {
|
||||||
gformat!("{PREFIX_DOWNLOAD}{}", self.prefix_less())
|
gformat!("{PREFIX_DOWNLOAD}{}", prefix_less(&self.entry))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get request value with formatted `source` prefix
|
/// Get request value with formatted `source` prefix
|
||||||
fn source(&self) -> GString {
|
fn source(&self) -> GString {
|
||||||
gformat!("{PREFIX_SOURCE}{}", self.prefix_less())
|
gformat!("{PREFIX_SOURCE}{}", prefix_less(&self.entry))
|
||||||
}
|
|
||||||
|
|
||||||
/// Try get current request value as [Uri](https://docs.gtk.org/glib/struct.Uri.html)
|
|
||||||
/// * `strip_prefix` on parse
|
|
||||||
fn uri(&self) -> Option<Uri> {
|
|
||||||
match Uri::parse(&self.prefix_less(), UriFlags::NONE) {
|
|
||||||
Ok(uri) => Some(uri),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Try build home [Uri](https://docs.gtk.org/glib/struct.Uri.html) for `Self`
|
|
||||||
/// * return `None` if current request already match home or Uri not parsable
|
|
||||||
fn home(&self) -> Option<Uri> {
|
|
||||||
let uri = self.uri()?;
|
|
||||||
if uri.path().len() > 1 || uri.query().is_some() || uri.fragment().is_some() {
|
|
||||||
Some(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,
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_file(&self) -> bool {
|
|
||||||
self.text().starts_with("file://")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -373,3 +235,127 @@ pub fn migrate(tx: &Transaction) -> Result<()> {
|
||||||
// Success
|
// Success
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_primary_icon(entry: &Entry, profile: &Profile) {
|
||||||
|
entry.first_child().unwrap().remove_css_class("success"); // @TODO handle
|
||||||
|
|
||||||
|
match primary_icon::from(&entry.text()) {
|
||||||
|
PrimaryIcon::Download { name, tooltip } | PrimaryIcon::File { name, tooltip } => {
|
||||||
|
entry.set_primary_icon_activatable(false);
|
||||||
|
entry.set_primary_icon_sensitive(false);
|
||||||
|
entry.set_primary_icon_name(Some(name));
|
||||||
|
entry.set_primary_icon_tooltip_text(Some(tooltip));
|
||||||
|
}
|
||||||
|
PrimaryIcon::Gemini { name, tooltip } | PrimaryIcon::Titan { name, tooltip } => {
|
||||||
|
entry.set_primary_icon_activatable(true);
|
||||||
|
entry.set_primary_icon_sensitive(true);
|
||||||
|
entry.set_primary_icon_name(Some(name));
|
||||||
|
if profile.identity.get(&prefix_less(entry)).is_some() {
|
||||||
|
entry.first_child().unwrap().add_css_class("success"); // @TODO handle
|
||||||
|
entry.set_primary_icon_tooltip_text(Some(tooltip.1));
|
||||||
|
} else {
|
||||||
|
entry.set_primary_icon_tooltip_text(Some(tooltip.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PrimaryIcon::Search { name, tooltip } => {
|
||||||
|
entry.set_primary_icon_activatable(true);
|
||||||
|
entry.set_primary_icon_sensitive(true);
|
||||||
|
entry.set_primary_icon_name(Some(name));
|
||||||
|
entry.set_primary_icon_tooltip_text(Some(tooltip));
|
||||||
|
}
|
||||||
|
PrimaryIcon::Source { name, tooltip } => {
|
||||||
|
entry.set_primary_icon_activatable(false);
|
||||||
|
entry.set_primary_icon_sensitive(false);
|
||||||
|
entry.set_primary_icon_name(Some(name));
|
||||||
|
entry.set_primary_icon_tooltip_text(Some(tooltip));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_secondary_icon(entry: &Entry) {
|
||||||
|
if !entry.text().is_empty() && entry.focus_child().is_some_and(|text| text.has_focus()) {
|
||||||
|
entry.set_secondary_icon_name(Some("pan-end-symbolic"));
|
||||||
|
} else {
|
||||||
|
entry.set_secondary_icon_name(None);
|
||||||
|
entry.select_region(0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Present Identity [AlertDialog](https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.AlertDialog.html) for `Self`
|
||||||
|
fn show_identity_dialog(entry: &Entry, profile: &Rc<Profile>) {
|
||||||
|
// connect identity traits
|
||||||
|
use identity::{Common, Unsupported};
|
||||||
|
if let Some(uri) = uri(entry) {
|
||||||
|
if ["gemini", "titan"].contains(&uri.scheme().as_str()) {
|
||||||
|
return AlertDialog::common(
|
||||||
|
profile,
|
||||||
|
&uri,
|
||||||
|
&Rc::new({
|
||||||
|
let profile = profile.clone();
|
||||||
|
let entry = entry.clone();
|
||||||
|
move |is_reload| {
|
||||||
|
update_primary_icon(&entry, &profile);
|
||||||
|
if is_reload {
|
||||||
|
entry.emit_activate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.present(Some(entry));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AlertDialog::unsupported().present(Some(entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Present Search providers [AlertDialog](https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.AlertDialog.html) for `Self`
|
||||||
|
fn show_search_dialog(entry: &Entry, profile: &Rc<Profile>) {
|
||||||
|
use search::Search;
|
||||||
|
AlertDialog::search(profile).present(Some(entry))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get current request value without system prefix
|
||||||
|
/// * the `prefix` is not `scheme`
|
||||||
|
fn prefix_less(entry: &Entry) -> GString {
|
||||||
|
let mut request = entry.text();
|
||||||
|
|
||||||
|
if let Some(postfix) = request.strip_prefix(PREFIX_SOURCE) {
|
||||||
|
request = postfix.into()
|
||||||
|
}
|
||||||
|
if let Some(postfix) = request.strip_prefix(PREFIX_DOWNLOAD) {
|
||||||
|
request = postfix.into()
|
||||||
|
}
|
||||||
|
request
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Try get current request value as [Uri](https://docs.gtk.org/glib/struct.Uri.html)
|
||||||
|
/// * `strip_prefix` on parse
|
||||||
|
fn uri(entry: &Entry) -> Option<Uri> {
|
||||||
|
match Uri::parse(&prefix_less(entry), UriFlags::NONE) {
|
||||||
|
Ok(uri) => Some(uri),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Try build home [Uri](https://docs.gtk.org/glib/struct.Uri.html) for `Self`
|
||||||
|
/// * return `None` if current request already match home or Uri not parsable
|
||||||
|
fn home(entry: &Entry) -> Option<Uri> {
|
||||||
|
let uri = uri(entry)?;
|
||||||
|
if uri.path().len() > 1 || uri.query().is_some() || uri.fragment().is_some() {
|
||||||
|
Some(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,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ use gtk::{
|
||||||
Entry, ListItem, ListView, Popover, SignalListItemFactory, SingleSelection,
|
Entry, ListItem, ListView, Popover, SignalListItemFactory, SingleSelection,
|
||||||
};
|
};
|
||||||
pub use item::Item;
|
pub use item::Item;
|
||||||
use sourceview::prelude::ListModelExt;
|
|
||||||
use std::{cell::RefCell, rc::Rc};
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
pub struct Suggestion {
|
pub struct Suggestion {
|
||||||
|
|
@ -47,13 +46,39 @@ impl Suggestion {
|
||||||
.child(&{
|
.child(&{
|
||||||
let list_view = ListView::builder()
|
let list_view = ListView::builder()
|
||||||
.show_separators(true)
|
.show_separators(true)
|
||||||
.single_click_activate(true)
|
.model(&{
|
||||||
.model(
|
let s = SingleSelection::builder()
|
||||||
&SingleSelection::builder()
|
|
||||||
.model(&list_store)
|
.model(&list_store)
|
||||||
.autoselect(false)
|
.autoselect(false)
|
||||||
.build(),
|
.build();
|
||||||
)
|
s.connect_selected_notify({
|
||||||
|
let request = request.clone();
|
||||||
|
let signal_handler_id = signal_handler_id.clone();
|
||||||
|
move |this| {
|
||||||
|
use gtk::prelude::ObjectExt;
|
||||||
|
if let Some(signal_handler_id) =
|
||||||
|
signal_handler_id.borrow().as_ref()
|
||||||
|
{
|
||||||
|
request.block_signal(signal_handler_id);
|
||||||
|
}
|
||||||
|
request.set_text(
|
||||||
|
&this
|
||||||
|
.selected_item()
|
||||||
|
.unwrap()
|
||||||
|
.downcast_ref::<Item>()
|
||||||
|
.unwrap()
|
||||||
|
.request(),
|
||||||
|
);
|
||||||
|
request.select_region(0, -1);
|
||||||
|
if let Some(signal_handler_id) =
|
||||||
|
signal_handler_id.borrow().as_ref()
|
||||||
|
{
|
||||||
|
request.unblock_signal(signal_handler_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
s
|
||||||
|
})
|
||||||
.factory(&{
|
.factory(&{
|
||||||
let f = SignalListItemFactory::new();
|
let f = SignalListItemFactory::new();
|
||||||
f.connect_setup(|_, this| {
|
f.connect_setup(|_, this| {
|
||||||
|
|
@ -78,31 +103,7 @@ impl Suggestion {
|
||||||
.build();
|
.build();
|
||||||
list_view.connect_activate({
|
list_view.connect_activate({
|
||||||
let request = request.clone();
|
let request = request.clone();
|
||||||
let signal_handler_id = signal_handler_id.clone();
|
move |_, _| request.emit_activate()
|
||||||
move |this, i| {
|
|
||||||
use gtk::prelude::ObjectExt;
|
|
||||||
if let Some(signal_handler_id) =
|
|
||||||
signal_handler_id.borrow().as_ref()
|
|
||||||
{
|
|
||||||
request.block_signal(signal_handler_id);
|
|
||||||
}
|
|
||||||
request.set_text(
|
|
||||||
&this
|
|
||||||
.model()
|
|
||||||
.unwrap()
|
|
||||||
.item(i)
|
|
||||||
.unwrap()
|
|
||||||
.downcast_ref::<Item>()
|
|
||||||
.unwrap()
|
|
||||||
.request(),
|
|
||||||
);
|
|
||||||
request.select_region(0, -1);
|
|
||||||
if let Some(signal_handler_id) =
|
|
||||||
signal_handler_id.borrow().as_ref()
|
|
||||||
{
|
|
||||||
request.unblock_signal(signal_handler_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
list_view
|
list_view
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue