mirror of
https://github.com/YGGverse/Yoda.git
synced 2026-03-31 08:35:28 +00:00
implement copy link text, selected text, add link to the bookmarks (context menu) items; group menu items
This commit is contained in:
parent
c95cb6e756
commit
bb08b7cb9a
7 changed files with 295 additions and 77 deletions
|
|
@ -21,7 +21,7 @@ impl Text {
|
|||
.info
|
||||
.borrow_mut()
|
||||
.set_mime(Some("text/gemini".to_string()));
|
||||
page.content.to_text_gemini(uri, data)
|
||||
page.content.to_text_gemini(&page.profile, uri, data)
|
||||
}),
|
||||
Self::Markdown(uri, data) => (uri, {
|
||||
page.navigation
|
||||
|
|
@ -29,7 +29,7 @@ impl Text {
|
|||
.info
|
||||
.borrow_mut()
|
||||
.set_mime(Some("text/markdown".to_string()));
|
||||
page.content.to_text_markdown(uri, data)
|
||||
page.content.to_text_markdown(&page.profile, uri, data)
|
||||
}),
|
||||
Self::Plain(uri, data) => (uri, page.content.to_text_plain(data)),
|
||||
Self::Source(uri, data) => (uri, page.content.to_text_source(data)),
|
||||
|
|
|
|||
|
|
@ -357,8 +357,8 @@ fn handle(
|
|||
page.content.to_text_source(data)
|
||||
} else {
|
||||
match m.as_str() {
|
||||
"text/gemini" => page.content.to_text_gemini(&uri, data),
|
||||
"text/markdown" => page.content.to_text_markdown(&uri, data),
|
||||
"text/gemini" => page.content.to_text_gemini(&page.profile, &uri, data),
|
||||
"text/markdown" => page.content.to_text_markdown(&page.profile, &uri, data),
|
||||
"text/plain" => page.content.to_text_plain(data),
|
||||
_ => panic!() // unexpected
|
||||
}
|
||||
|
|
|
|||
|
|
@ -299,7 +299,7 @@ fn render(
|
|||
} else if q.ends_with("/") {
|
||||
p.content.to_text_nex(&u, d)
|
||||
} else if q.ends_with(".gmi") || q.ends_with(".gemini") {
|
||||
p.content.to_text_gemini(&u, d)
|
||||
p.content.to_text_gemini(&p.profile, &u, d)
|
||||
} else {
|
||||
p.content.to_text_plain(d)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ use directory::Directory;
|
|||
use image::Image;
|
||||
use text::Text;
|
||||
|
||||
use crate::profile::Profile;
|
||||
|
||||
use super::{ItemAction, TabAction, WindowAction};
|
||||
use adw::StatusPage;
|
||||
use gtk::{
|
||||
|
|
@ -126,9 +128,14 @@ impl Content {
|
|||
}
|
||||
|
||||
/// `text/gemini`
|
||||
pub fn to_text_gemini(&self, base: &Uri, data: &str) -> Text {
|
||||
pub fn to_text_gemini(&self, profile: &Rc<Profile>, base: &Uri, data: &str) -> Text {
|
||||
self.clean();
|
||||
match Text::gemini((&self.window_action, &self.item_action), base, data) {
|
||||
match Text::gemini(
|
||||
(&self.window_action, &self.item_action),
|
||||
profile,
|
||||
base,
|
||||
data,
|
||||
) {
|
||||
Ok(text) => {
|
||||
self.g_box.append(&text.scrolled_window);
|
||||
text
|
||||
|
|
@ -155,9 +162,14 @@ impl Content {
|
|||
}
|
||||
|
||||
/// `text/markdown`
|
||||
pub fn to_text_markdown(&self, base: &Uri, data: &str) -> Text {
|
||||
pub fn to_text_markdown(&self, profile: &Rc<Profile>, base: &Uri, data: &str) -> Text {
|
||||
self.clean();
|
||||
let m = Text::markdown((&self.window_action, &self.item_action), base, data);
|
||||
let m = Text::markdown(
|
||||
(&self.window_action, &self.item_action),
|
||||
profile,
|
||||
base,
|
||||
data,
|
||||
);
|
||||
self.g_box.append(&m.scrolled_window);
|
||||
m
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ mod nex;
|
|||
mod plain;
|
||||
mod source;
|
||||
|
||||
use crate::profile::Profile;
|
||||
|
||||
use super::{ItemAction, WindowAction};
|
||||
use adw::ClampScrollable;
|
||||
use gemini::Gemini;
|
||||
|
|
@ -27,10 +29,11 @@ pub struct Text {
|
|||
impl Text {
|
||||
pub fn gemini(
|
||||
actions: (&Rc<WindowAction>, &Rc<ItemAction>),
|
||||
profile: &Rc<Profile>,
|
||||
base: &Uri,
|
||||
gemtext: &str,
|
||||
) -> Result<Self, (String, Option<Self>)> {
|
||||
match Gemini::build(actions, base, gemtext) {
|
||||
match Gemini::build(actions, profile, base, gemtext) {
|
||||
Ok(widget) => Ok(Self {
|
||||
scrolled_window: reader(&widget.text_view),
|
||||
text_view: widget.text_view,
|
||||
|
|
@ -55,10 +58,11 @@ impl Text {
|
|||
|
||||
pub fn markdown(
|
||||
actions: (&Rc<WindowAction>, &Rc<ItemAction>),
|
||||
profile: &Rc<Profile>,
|
||||
base: &Uri,
|
||||
gemtext: &str,
|
||||
) -> Self {
|
||||
let markdown = Markdown::build(actions, base, gemtext);
|
||||
let markdown = Markdown::build(actions, profile, base, gemtext);
|
||||
Self {
|
||||
scrolled_window: reader(&markdown.text_view),
|
||||
text_view: markdown.text_view,
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ mod syntax;
|
|||
mod tag;
|
||||
|
||||
use super::{ItemAction, WindowAction};
|
||||
use crate::app::browser::window::action::Position;
|
||||
use crate::{app::browser::window::action::Position, profile::Profile};
|
||||
pub use error::Error;
|
||||
use gtk::{
|
||||
EventControllerMotion, GestureClick, TextBuffer, TextTag, TextView, TextWindowType,
|
||||
UriLauncher, Window, WrapMode,
|
||||
gdk::{BUTTON_MIDDLE, BUTTON_PRIMARY, BUTTON_SECONDARY, RGBA},
|
||||
gio::{Cancellable, SimpleAction, SimpleActionGroup},
|
||||
gdk::{BUTTON_MIDDLE, BUTTON_PRIMARY, BUTTON_SECONDARY, Display, RGBA},
|
||||
gio::{Cancellable, Menu, SimpleAction, SimpleActionGroup},
|
||||
glib::{Uri, uuid_string_random},
|
||||
prelude::{PopoverExt, TextBufferExt, TextBufferExtManual, TextTagExt, TextViewExt, WidgetExt},
|
||||
};
|
||||
|
|
@ -36,6 +36,7 @@ impl Gemini {
|
|||
/// Build new `Self`
|
||||
pub fn build(
|
||||
(window_action, item_action): (&Rc<WindowAction>, &Rc<ItemAction>),
|
||||
profile: &Rc<Profile>,
|
||||
base: &Uri,
|
||||
gemtext: &str,
|
||||
) -> Result<Self, Error> {
|
||||
|
|
@ -220,7 +221,7 @@ impl Gemini {
|
|||
let mut alt = Vec::with_capacity(2);
|
||||
|
||||
if uri.scheme() != base.scheme() {
|
||||
alt.push("⇖".to_string());
|
||||
alt.push(LINK_EXTERNAL_INDICATOR.to_string());
|
||||
}
|
||||
|
||||
alt.push(match link.alt {
|
||||
|
|
@ -235,9 +236,7 @@ impl Gemini {
|
|||
.wrap_mode(WrapMode::Word)
|
||||
.build();
|
||||
|
||||
if !tag.text_tag_table.add(&a) {
|
||||
panic!()
|
||||
}
|
||||
assert!(tag.text_tag_table.add(&a));
|
||||
|
||||
buffer.insert_with_tags(&mut buffer.end_iter(), &alt.join(" "), &[&a]);
|
||||
buffer.insert(&mut buffer.end_iter(), NEW_LINE);
|
||||
|
|
@ -296,14 +295,39 @@ impl Gemini {
|
|||
)
|
||||
}
|
||||
});
|
||||
let action_link_copy =
|
||||
let action_link_copy_url =
|
||||
SimpleAction::new_stateful(&uuid_string_random(), None, &String::new().to_variant());
|
||||
action_link_copy.connect_activate(|this, _| {
|
||||
gtk::gdk::Display::default()
|
||||
action_link_copy_url.connect_activate(|this, _| {
|
||||
Display::default()
|
||||
.unwrap()
|
||||
.clipboard()
|
||||
.set_text(&this.state().unwrap().get::<String>().unwrap())
|
||||
});
|
||||
let action_link_copy_text =
|
||||
SimpleAction::new_stateful(&uuid_string_random(), None, &String::new().to_variant());
|
||||
action_link_copy_text.connect_activate(|this, _| {
|
||||
Display::default()
|
||||
.unwrap()
|
||||
.clipboard()
|
||||
.set_text(&this.state().unwrap().get::<String>().unwrap())
|
||||
});
|
||||
let action_link_copy_text_selected =
|
||||
SimpleAction::new_stateful(&uuid_string_random(), None, &String::new().to_variant());
|
||||
action_link_copy_text_selected.connect_activate(|this, _| {
|
||||
Display::default()
|
||||
.unwrap()
|
||||
.clipboard()
|
||||
.set_text(&this.state().unwrap().get::<String>().unwrap())
|
||||
});
|
||||
let action_link_bookmark =
|
||||
SimpleAction::new_stateful(&uuid_string_random(), None, &String::new().to_variant());
|
||||
action_link_bookmark.connect_activate({
|
||||
let p = profile.clone();
|
||||
move |this, _| {
|
||||
let state = this.state().unwrap().get::<String>().unwrap();
|
||||
p.bookmark.toggle(&state, None).unwrap();
|
||||
}
|
||||
});
|
||||
let action_link_download =
|
||||
SimpleAction::new_stateful(&uuid_string_random(), None, &String::new().to_variant());
|
||||
action_link_download.connect_activate({
|
||||
|
|
@ -338,14 +362,17 @@ impl Gemini {
|
|||
Some(&{
|
||||
let g = SimpleActionGroup::new();
|
||||
g.add_action(&action_link_tab);
|
||||
g.add_action(&action_link_copy);
|
||||
g.add_action(&action_link_copy_url);
|
||||
g.add_action(&action_link_copy_text);
|
||||
g.add_action(&action_link_copy_text_selected);
|
||||
g.add_action(&action_link_bookmark);
|
||||
g.add_action(&action_link_download);
|
||||
g.add_action(&action_link_source);
|
||||
g
|
||||
}),
|
||||
);
|
||||
let link_context = gtk::PopoverMenu::from_model(Some(&{
|
||||
let m = gtk::gio::Menu::new();
|
||||
let m = Menu::new();
|
||||
m.append(
|
||||
Some("Open Link in New Tab"),
|
||||
Some(&format!(
|
||||
|
|
@ -353,27 +380,56 @@ impl Gemini {
|
|||
action_link_tab.name()
|
||||
)),
|
||||
);
|
||||
m.append(
|
||||
Some("Copy Link"),
|
||||
Some(&format!(
|
||||
"{link_context_group_id}.{}",
|
||||
action_link_copy.name()
|
||||
)),
|
||||
);
|
||||
m.append(
|
||||
Some("Download Link"),
|
||||
Some(&format!(
|
||||
"{link_context_group_id}.{}",
|
||||
action_link_download.name()
|
||||
)),
|
||||
);
|
||||
m.append(
|
||||
Some("View Link as Source"),
|
||||
Some(&format!(
|
||||
"{link_context_group_id}.{}",
|
||||
action_link_source.name()
|
||||
)),
|
||||
);
|
||||
m.append_section(None, &{
|
||||
let m_copy = Menu::new();
|
||||
m_copy.append(
|
||||
Some("Copy Link URL"),
|
||||
Some(&format!(
|
||||
"{link_context_group_id}.{}",
|
||||
action_link_copy_url.name()
|
||||
)),
|
||||
);
|
||||
m_copy.append(
|
||||
Some("Copy Link Text"),
|
||||
Some(&format!(
|
||||
"{link_context_group_id}.{}",
|
||||
action_link_copy_text.name()
|
||||
)),
|
||||
);
|
||||
m_copy.append(
|
||||
Some("Copy Link Text Selected"),
|
||||
Some(&format!(
|
||||
"{link_context_group_id}.{}",
|
||||
action_link_copy_text_selected.name()
|
||||
)),
|
||||
);
|
||||
m_copy
|
||||
});
|
||||
m.append_section(None, &{
|
||||
let m_other = Menu::new();
|
||||
m_other.append(
|
||||
Some("Bookmark Link"), // @TODO highlight state
|
||||
Some(&format!(
|
||||
"{link_context_group_id}.{}",
|
||||
action_link_bookmark.name()
|
||||
)),
|
||||
);
|
||||
m_other.append(
|
||||
Some("Download Link"),
|
||||
Some(&format!(
|
||||
"{link_context_group_id}.{}",
|
||||
action_link_download.name()
|
||||
)),
|
||||
);
|
||||
m_other.append(
|
||||
Some("View Link as Source"),
|
||||
Some(&format!(
|
||||
"{link_context_group_id}.{}",
|
||||
action_link_source.name()
|
||||
)),
|
||||
);
|
||||
m_other
|
||||
});
|
||||
m
|
||||
}));
|
||||
link_context.set_parent(&text_view);
|
||||
|
|
@ -435,18 +491,61 @@ impl Gemini {
|
|||
let request_str = uri.to_str();
|
||||
let request_var = request_str.to_variant();
|
||||
|
||||
// Open in the new tab
|
||||
action_link_tab.set_state(&request_var);
|
||||
action_link_copy.set_state(&request_var);
|
||||
action_link_copy_text.set_enabled(!request_str.is_empty());
|
||||
|
||||
action_link_copy_url.set_state(&request_var);
|
||||
action_link_copy_text.set_enabled(!request_str.is_empty());
|
||||
|
||||
{
|
||||
// Copy link text
|
||||
let mut start_iter = iter;
|
||||
let mut end_iter = iter;
|
||||
if !start_iter.starts_tag(Some(&tag)) {
|
||||
start_iter.backward_to_tag_toggle(Some(&tag));
|
||||
}
|
||||
if !end_iter.ends_tag(Some(&tag)) {
|
||||
end_iter.forward_to_tag_toggle(Some(&tag));
|
||||
}
|
||||
let tagged_text = text_view
|
||||
.buffer()
|
||||
.text(&start_iter, &end_iter, false)
|
||||
.replace(LINK_EXTERNAL_INDICATOR, "")
|
||||
.trim()
|
||||
.to_string();
|
||||
|
||||
action_link_copy_text.set_state(&tagged_text.to_variant());
|
||||
action_link_copy_text.set_enabled(!tagged_text.is_empty());
|
||||
}
|
||||
|
||||
// Copy link text (if) selected
|
||||
if let Some((sel_start, sel_end)) = buffer.selection_bounds() {
|
||||
let selected_tag_text = buffer.text(&sel_start, &sel_end, false);
|
||||
action_link_copy_text_selected
|
||||
.set_state(&selected_tag_text.to_variant());
|
||||
action_link_copy_text_selected
|
||||
.set_enabled(!selected_tag_text.is_empty());
|
||||
} else {
|
||||
action_link_copy_text_selected.set_enabled(false);
|
||||
}
|
||||
|
||||
// Bookmark
|
||||
action_link_bookmark.set_state(&request_var);
|
||||
action_link_bookmark.set_enabled(is_prefixable_link(&request_str));
|
||||
|
||||
// Download (new tab)
|
||||
action_link_download.set_state(&request_var);
|
||||
action_link_download.set_enabled(is_prefixable_link(&request_str));
|
||||
|
||||
// View as Source (new tab)
|
||||
action_link_source.set_state(&request_var);
|
||||
action_link_source.set_enabled(is_prefixable_link(&request_str));
|
||||
|
||||
// Toggle
|
||||
link_context
|
||||
.set_pointing_to(Some(>k::gdk::Rectangle::new(x, y, 1, 1)));
|
||||
link_context.popup();
|
||||
link_context.popup()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -580,5 +679,6 @@ fn link_prefix(request: String, prefix: &str) -> String {
|
|||
format!("{prefix}{}", request.trim_start_matches(prefix))
|
||||
}
|
||||
|
||||
const LINK_EXTERNAL_INDICATOR: &str = "⇖";
|
||||
const LINK_PREFIX_DOWNLOAD: &str = "download:";
|
||||
const LINK_PREFIX_SOURCE: &str = "source:";
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@ mod gutter;
|
|||
mod tags;
|
||||
|
||||
use super::{ItemAction, WindowAction};
|
||||
use crate::app::browser::window::action::Position;
|
||||
use crate::{app::browser::window::action::Position, profile::Profile};
|
||||
use gtk::{
|
||||
EventControllerMotion, GestureClick, TextBuffer, TextSearchFlags, TextTag, TextTagTable,
|
||||
TextView, TextWindowType, UriLauncher, Window, WrapMode,
|
||||
gdk::{BUTTON_MIDDLE, BUTTON_PRIMARY, BUTTON_SECONDARY, RGBA},
|
||||
gio::{Cancellable, SimpleAction, SimpleActionGroup},
|
||||
gdk::{BUTTON_MIDDLE, BUTTON_PRIMARY, BUTTON_SECONDARY, Display, RGBA},
|
||||
gio::{Cancellable, Menu, SimpleAction, SimpleActionGroup},
|
||||
glib::{ControlFlow, GString, Uri, idle_add_local, uri_unescape_string, uuid_string_random},
|
||||
prelude::{PopoverExt, TextBufferExt, TextTagExt, TextViewExt, WidgetExt},
|
||||
};
|
||||
|
|
@ -27,6 +27,7 @@ impl Markdown {
|
|||
/// Build new `Self`
|
||||
pub fn build(
|
||||
(window_action, item_action): (&Rc<WindowAction>, &Rc<ItemAction>),
|
||||
profile: &Rc<Profile>,
|
||||
base: &Uri,
|
||||
markdown: &str,
|
||||
) -> Self {
|
||||
|
|
@ -88,14 +89,39 @@ impl Markdown {
|
|||
)
|
||||
}
|
||||
});
|
||||
let action_link_copy =
|
||||
let action_link_copy_url =
|
||||
SimpleAction::new_stateful(&uuid_string_random(), None, &String::new().to_variant());
|
||||
action_link_copy.connect_activate(|this, _| {
|
||||
gtk::gdk::Display::default()
|
||||
action_link_copy_url.connect_activate(|this, _| {
|
||||
Display::default()
|
||||
.unwrap()
|
||||
.clipboard()
|
||||
.set_text(&this.state().unwrap().get::<String>().unwrap())
|
||||
});
|
||||
let action_link_copy_text =
|
||||
SimpleAction::new_stateful(&uuid_string_random(), None, &String::new().to_variant());
|
||||
action_link_copy_text.connect_activate(|this, _| {
|
||||
Display::default()
|
||||
.unwrap()
|
||||
.clipboard()
|
||||
.set_text(&this.state().unwrap().get::<String>().unwrap())
|
||||
});
|
||||
let action_link_copy_text_selected =
|
||||
SimpleAction::new_stateful(&uuid_string_random(), None, &String::new().to_variant());
|
||||
action_link_copy_text_selected.connect_activate(|this, _| {
|
||||
Display::default()
|
||||
.unwrap()
|
||||
.clipboard()
|
||||
.set_text(&this.state().unwrap().get::<String>().unwrap())
|
||||
});
|
||||
let action_link_bookmark =
|
||||
SimpleAction::new_stateful(&uuid_string_random(), None, &String::new().to_variant());
|
||||
action_link_bookmark.connect_activate({
|
||||
let p = profile.clone();
|
||||
move |this, _| {
|
||||
let state = this.state().unwrap().get::<String>().unwrap();
|
||||
p.bookmark.toggle(&state, None).unwrap();
|
||||
}
|
||||
});
|
||||
let action_link_download =
|
||||
SimpleAction::new_stateful(&uuid_string_random(), None, &String::new().to_variant());
|
||||
action_link_download.connect_activate({
|
||||
|
|
@ -130,14 +156,17 @@ impl Markdown {
|
|||
Some(&{
|
||||
let g = SimpleActionGroup::new();
|
||||
g.add_action(&action_link_tab);
|
||||
g.add_action(&action_link_copy);
|
||||
g.add_action(&action_link_copy_url);
|
||||
g.add_action(&action_link_copy_text);
|
||||
g.add_action(&action_link_copy_text_selected);
|
||||
g.add_action(&action_link_bookmark);
|
||||
g.add_action(&action_link_download);
|
||||
g.add_action(&action_link_source);
|
||||
g
|
||||
}),
|
||||
);
|
||||
let link_context = gtk::PopoverMenu::from_model(Some(&{
|
||||
let m = gtk::gio::Menu::new();
|
||||
let m = Menu::new();
|
||||
m.append(
|
||||
Some("Open Link in New Tab"),
|
||||
Some(&format!(
|
||||
|
|
@ -145,27 +174,56 @@ impl Markdown {
|
|||
action_link_tab.name()
|
||||
)),
|
||||
);
|
||||
m.append(
|
||||
Some("Copy Link"),
|
||||
Some(&format!(
|
||||
"{link_context_group_id}.{}",
|
||||
action_link_copy.name()
|
||||
)),
|
||||
);
|
||||
m.append(
|
||||
Some("Download Link"),
|
||||
Some(&format!(
|
||||
"{link_context_group_id}.{}",
|
||||
action_link_download.name()
|
||||
)),
|
||||
);
|
||||
m.append(
|
||||
Some("View Link as Source"),
|
||||
Some(&format!(
|
||||
"{link_context_group_id}.{}",
|
||||
action_link_source.name()
|
||||
)),
|
||||
);
|
||||
m.append_section(None, &{
|
||||
let m_copy = Menu::new();
|
||||
m_copy.append(
|
||||
Some("Copy Link URL"),
|
||||
Some(&format!(
|
||||
"{link_context_group_id}.{}",
|
||||
action_link_copy_url.name()
|
||||
)),
|
||||
);
|
||||
m_copy.append(
|
||||
Some("Copy Link Text"),
|
||||
Some(&format!(
|
||||
"{link_context_group_id}.{}",
|
||||
action_link_copy_text.name()
|
||||
)),
|
||||
);
|
||||
m_copy.append(
|
||||
Some("Copy Link Text Selected"),
|
||||
Some(&format!(
|
||||
"{link_context_group_id}.{}",
|
||||
action_link_copy_text_selected.name()
|
||||
)),
|
||||
);
|
||||
m_copy
|
||||
});
|
||||
m.append_section(None, &{
|
||||
let m_other = Menu::new();
|
||||
m_other.append(
|
||||
Some("Bookmark Link"), // @TODO highlight state
|
||||
Some(&format!(
|
||||
"{link_context_group_id}.{}",
|
||||
action_link_bookmark.name()
|
||||
)),
|
||||
);
|
||||
m_other.append(
|
||||
Some("Download Link"),
|
||||
Some(&format!(
|
||||
"{link_context_group_id}.{}",
|
||||
action_link_download.name()
|
||||
)),
|
||||
);
|
||||
m_other.append(
|
||||
Some("View Link as Source"),
|
||||
Some(&format!(
|
||||
"{link_context_group_id}.{}",
|
||||
action_link_source.name()
|
||||
)),
|
||||
);
|
||||
m_other
|
||||
});
|
||||
m
|
||||
}));
|
||||
link_context.set_parent(&text_view);
|
||||
|
|
@ -231,18 +289,61 @@ impl Markdown {
|
|||
let request_str = uri.to_str();
|
||||
let request_var = request_str.to_variant();
|
||||
|
||||
// Open in the new tab
|
||||
action_link_tab.set_state(&request_var);
|
||||
action_link_copy.set_state(&request_var);
|
||||
action_link_copy_text.set_enabled(!request_str.is_empty());
|
||||
|
||||
action_link_copy_url.set_state(&request_var);
|
||||
action_link_copy_text.set_enabled(!request_str.is_empty());
|
||||
|
||||
{
|
||||
// Copy link text
|
||||
let mut start_iter = iter;
|
||||
let mut end_iter = iter;
|
||||
if !start_iter.starts_tag(Some(&tag)) {
|
||||
start_iter.backward_to_tag_toggle(Some(&tag));
|
||||
}
|
||||
if !end_iter.ends_tag(Some(&tag)) {
|
||||
end_iter.forward_to_tag_toggle(Some(&tag));
|
||||
}
|
||||
let tagged_text = text_view
|
||||
.buffer()
|
||||
.text(&start_iter, &end_iter, false)
|
||||
.replace(LINK_EXTERNAL_INDICATOR, "")
|
||||
.trim()
|
||||
.to_string();
|
||||
|
||||
action_link_copy_text.set_state(&tagged_text.to_variant());
|
||||
action_link_copy_text.set_enabled(!tagged_text.is_empty());
|
||||
}
|
||||
|
||||
// Copy link text (if) selected
|
||||
if let Some((sel_start, sel_end)) = buffer.selection_bounds() {
|
||||
let selected_tag_text = buffer.text(&sel_start, &sel_end, false);
|
||||
action_link_copy_text_selected
|
||||
.set_state(&selected_tag_text.to_variant());
|
||||
action_link_copy_text_selected
|
||||
.set_enabled(!selected_tag_text.is_empty());
|
||||
} else {
|
||||
action_link_copy_text_selected.set_enabled(false);
|
||||
}
|
||||
|
||||
// Bookmark
|
||||
action_link_bookmark.set_state(&request_var);
|
||||
action_link_bookmark.set_enabled(is_prefixable_link(&request_str));
|
||||
|
||||
// Download (new tab)
|
||||
action_link_download.set_state(&request_var);
|
||||
action_link_download.set_enabled(is_prefixable_link(&request_str));
|
||||
|
||||
// View as Source (new tab)
|
||||
action_link_source.set_state(&request_var);
|
||||
action_link_source.set_enabled(is_prefixable_link(&request_str));
|
||||
|
||||
// Toggle
|
||||
link_context
|
||||
.set_pointing_to(Some(>k::gdk::Rectangle::new(x, y, 1, 1)));
|
||||
link_context.popup();
|
||||
link_context.popup()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -405,5 +506,6 @@ fn link_prefix(request: String, prefix: &str) -> String {
|
|||
format!("{prefix}{}", request.trim_start_matches(prefix))
|
||||
}
|
||||
|
||||
const LINK_EXTERNAL_INDICATOR: &str = "⇖";
|
||||
const LINK_PREFIX_DOWNLOAD: &str = "download:";
|
||||
const LINK_PREFIX_SOURCE: &str = "source:";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue