mirror of
https://github.com/YGGverse/Yoda.git
synced 2025-01-30 13:04:13 +00:00
implement gesture actions for link tags
This commit is contained in:
parent
1a410860e8
commit
fb44222e4e
@ -6,99 +6,83 @@ use parser::link::Link;
|
|||||||
use widget::Widget;
|
use widget::Widget;
|
||||||
|
|
||||||
use gtk::{
|
use gtk::{
|
||||||
|
gdk::{BUTTON_MIDDLE, BUTTON_PRIMARY},
|
||||||
gio::SimpleAction,
|
gio::SimpleAction,
|
||||||
glib::{GString, TimeZone, Uri},
|
glib::{GString, TimeZone, Uri},
|
||||||
prelude::{TextBufferExt, TextBufferExtManual},
|
prelude::{ActionExt, TextBufferExt, TextBufferExtManual, TextViewExt, ToVariant},
|
||||||
TextBuffer, TextTag, TextTagTable, TextView, WrapMode,
|
EventControllerMotion, GestureClick, TextBuffer, TextTag, TextView, TextWindowType, WrapMode,
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::{collections::HashMap, sync::Arc};
|
||||||
|
|
||||||
pub struct Reader {
|
pub struct Reader {
|
||||||
title: Option<GString>,
|
title: Option<GString>,
|
||||||
// css: CssProvider,
|
|
||||||
widget: Arc<Widget>,
|
widget: Arc<Widget>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Reader {
|
impl Reader {
|
||||||
// Construct
|
// Construct
|
||||||
pub fn new_arc(gemtext: &str, base: &Uri, action_page_open: Arc<SimpleAction>) -> Arc<Self> {
|
pub fn new_arc(gemtext: &str, base: &Uri, action_page_open: Arc<SimpleAction>) -> Arc<Self> {
|
||||||
// Init title
|
// Init default values
|
||||||
let mut title = None;
|
let mut title = None;
|
||||||
|
|
||||||
// Init tag table
|
// Init HashMap storage for event controllers
|
||||||
let tags = TextTagTable::new();
|
let mut links: HashMap<TextTag, Uri> = HashMap::new();
|
||||||
|
|
||||||
// Init header tags
|
// Init new text buffer
|
||||||
let h1 = TextTag::builder()
|
let buffer = TextBuffer::new(None);
|
||||||
.name("h1")
|
|
||||||
.scale(1.6)
|
|
||||||
.weight(500)
|
|
||||||
.wrap_mode(gtk::WrapMode::Word)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
tags.add(&h1);
|
|
||||||
|
|
||||||
let h2 = TextTag::builder()
|
|
||||||
.name("h2")
|
|
||||||
.scale(1.4)
|
|
||||||
.weight(400)
|
|
||||||
.wrap_mode(gtk::WrapMode::Word)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
tags.add(&h2);
|
|
||||||
|
|
||||||
let h3 = TextTag::builder()
|
|
||||||
.name("h3")
|
|
||||||
.scale(1.2)
|
|
||||||
.weight(400)
|
|
||||||
.wrap_mode(WrapMode::Word)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
tags.add(&h3);
|
|
||||||
|
|
||||||
// Init link tag
|
|
||||||
let link = TextTag::builder()
|
|
||||||
.name("link")
|
|
||||||
.wrap_mode(WrapMode::Word)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
tags.add(&link);
|
|
||||||
|
|
||||||
// Parse lines
|
|
||||||
let buffer = TextBuffer::new(Some(&tags));
|
|
||||||
|
|
||||||
|
// Parse gemtext lines
|
||||||
for line in gemtext.lines() {
|
for line in gemtext.lines() {
|
||||||
// Is header
|
// Is header
|
||||||
if let Some(header) = Header::from(line) {
|
if let Some(header) = Header::from(line) {
|
||||||
// Detect level
|
// Build tag from level parsed
|
||||||
let tag = match header.level {
|
let tag = match header.level {
|
||||||
parser::header::Level::H1 => "h1",
|
parser::header::Level::H1 => TextTag::builder()
|
||||||
parser::header::Level::H2 => "h2",
|
.scale(1.6)
|
||||||
parser::header::Level::H3 => "h3",
|
.weight(500)
|
||||||
|
.wrap_mode(gtk::WrapMode::Word)
|
||||||
|
.build(),
|
||||||
|
parser::header::Level::H2 => TextTag::builder()
|
||||||
|
.scale(1.4)
|
||||||
|
.weight(400)
|
||||||
|
.wrap_mode(gtk::WrapMode::Word)
|
||||||
|
.build(),
|
||||||
|
parser::header::Level::H3 => TextTag::builder()
|
||||||
|
.scale(1.2)
|
||||||
|
.weight(400)
|
||||||
|
.wrap_mode(WrapMode::Word)
|
||||||
|
.build(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Insert tag line
|
// Register tag in buffer
|
||||||
buffer.insert_with_tags_by_name(
|
buffer.tag_table().add(&tag);
|
||||||
&mut buffer.end_iter(),
|
|
||||||
header.value.as_str(),
|
|
||||||
&[tag],
|
|
||||||
);
|
|
||||||
|
|
||||||
|
// Append value to buffer
|
||||||
|
buffer.insert_with_tags(&mut buffer.end_iter(), header.value.as_str(), &[&tag]);
|
||||||
buffer.insert(&mut buffer.end_iter(), "\n");
|
buffer.insert(&mut buffer.end_iter(), "\n");
|
||||||
|
|
||||||
// Set title if empty, on first document header match
|
// Update reader title using first gemtext header match
|
||||||
// this feature wanted to update parent elements like tab title
|
|
||||||
if title == None {
|
if title == None {
|
||||||
title = Some(header.value.clone());
|
title = Some(header.value.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Skip other actions for this line
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is link
|
// Is link
|
||||||
if let Some(link) = Link::from(line, Some(base), Some(&TimeZone::local())) {
|
if let Some(link) = Link::from(line, Some(base), Some(&TimeZone::local())) {
|
||||||
// Build link alt from optional values
|
// Init new tag for link
|
||||||
|
let tag = TextTag::builder().wrap_mode(WrapMode::Word).build();
|
||||||
|
|
||||||
|
// Append tag to buffer
|
||||||
|
buffer.tag_table().add(&tag);
|
||||||
|
|
||||||
|
// Append tag to HashMap storage
|
||||||
|
links.insert(tag.clone(), link.uri.clone());
|
||||||
|
|
||||||
|
// Create vector for alt values
|
||||||
let mut alt = Vec::new();
|
let mut alt = Vec::new();
|
||||||
|
|
||||||
// Append external indicator on exist
|
// Append external indicator on exist
|
||||||
@ -116,47 +100,63 @@ impl Reader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append alt on exist or use URL
|
// Append alt value on exist or use URL
|
||||||
alt.push(match link.alt {
|
alt.push(match link.alt {
|
||||||
Some(alt) => alt.to_string(),
|
Some(alt) => alt.to_string(),
|
||||||
None => link.uri.to_string(),
|
None => link.uri.to_string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
buffer.insert_with_tags_by_name(&mut buffer.end_iter(), &alt.join(" "), &["link"]);
|
// Append alt vector values to buffer
|
||||||
|
buffer.insert_with_tags(&mut buffer.end_iter(), &alt.join(" "), &[&tag]);
|
||||||
buffer.insert(&mut buffer.end_iter(), "\n");
|
buffer.insert(&mut buffer.end_iter(), "\n");
|
||||||
|
|
||||||
|
// Skip other actions for this line
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nothing match, use plain text @TODO
|
// Nothing match tags, use plain text @TODO
|
||||||
buffer.insert(&mut buffer.end_iter(), line);
|
buffer.insert(&mut buffer.end_iter(), line);
|
||||||
buffer.insert(&mut buffer.end_iter(), "\n");
|
buffer.insert(&mut buffer.end_iter(), "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init widget
|
// Init additional controllers
|
||||||
let widget = Widget::new_arc(&buffer);
|
let primary_button_controller = GestureClick::builder().button(BUTTON_PRIMARY).build();
|
||||||
|
let middle_button_controller = GestureClick::builder().button(BUTTON_MIDDLE).build();
|
||||||
|
let motion_controller = EventControllerMotion::new();
|
||||||
|
|
||||||
// Connect actions
|
// Init widget
|
||||||
/* @TODO
|
let widget = Widget::new_arc(
|
||||||
widget.connect_activate_link(move |_, href| {
|
&buffer,
|
||||||
// Detect requested protocol
|
primary_button_controller.clone(),
|
||||||
if let Ok(uri) = Uri::parse(&href, UriFlags::NONE) {
|
middle_button_controller.clone(),
|
||||||
return match uri.scheme().as_str() {
|
motion_controller.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Init events
|
||||||
|
primary_button_controller.connect_released({
|
||||||
|
let action_page_open = action_page_open.clone();
|
||||||
|
let gobject = widget.gobject().clone();
|
||||||
|
move |_, _, x, y| {
|
||||||
|
gobject.window_to_buffer_coords(TextWindowType::Widget, x as i32, y as i32);
|
||||||
|
if let Some(iter) = gobject.iter_at_location(x as i32, y as i32) {
|
||||||
|
for tag in iter.tags() {
|
||||||
|
if let Some(uri) = links.get(&tag) {
|
||||||
|
match uri.scheme().as_str() {
|
||||||
"gemini" => {
|
"gemini" => {
|
||||||
// Open new page
|
// Open new page
|
||||||
action_page_open.activate(Some(&uri.to_str().to_variant()));
|
action_page_open.activate(Some(&uri.to_str().to_variant()));
|
||||||
|
|
||||||
// Prevent link open in external application
|
|
||||||
Propagation::Stop
|
|
||||||
}
|
}
|
||||||
// Protocol not supported
|
_ => (), // Protocol not supported, delegate to the external app @TODO
|
||||||
_ => Propagation::Proceed,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Delegate unparsable
|
// @TODO on middle-click, on hover events
|
||||||
Propagation::Proceed
|
// primary_button_controller(|_, _, _, _| {});
|
||||||
}); */
|
// motion_controller.connect_motion(|_, _, _| {});
|
||||||
|
|
||||||
// Result
|
// Result
|
||||||
Arc::new(Self { title, widget })
|
Arc::new(Self { title, widget })
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
use gtk::{TextBuffer, TextView, WrapMode};
|
use gtk::{
|
||||||
|
prelude::WidgetExt, EventControllerMotion, GestureClick, TextBuffer, TextView, WrapMode,
|
||||||
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub struct Widget {
|
pub struct Widget {
|
||||||
@ -7,16 +9,25 @@ pub struct Widget {
|
|||||||
|
|
||||||
impl Widget {
|
impl Widget {
|
||||||
// Construct
|
// Construct
|
||||||
pub fn new_arc(buffer: &TextBuffer) -> Arc<Self> {
|
pub fn new_arc(
|
||||||
Arc::new(Self {
|
buffer: &TextBuffer,
|
||||||
gobject: TextView::builder()
|
primary_button_controller: GestureClick,
|
||||||
|
middle_button_controller: GestureClick,
|
||||||
|
motion_controller: EventControllerMotion,
|
||||||
|
) -> Arc<Self> {
|
||||||
|
let gobject = TextView::builder()
|
||||||
.editable(false)
|
.editable(false)
|
||||||
.cursor_visible(false)
|
.cursor_visible(false)
|
||||||
.wrap_mode(WrapMode::Word)
|
.wrap_mode(WrapMode::Word)
|
||||||
.vexpand(true)
|
.vexpand(true)
|
||||||
.buffer(buffer)
|
.buffer(buffer)
|
||||||
.build(),
|
.build();
|
||||||
})
|
|
||||||
|
gobject.add_controller(primary_button_controller);
|
||||||
|
gobject.add_controller(middle_button_controller);
|
||||||
|
gobject.add_controller(motion_controller);
|
||||||
|
|
||||||
|
Arc::new(Self { gobject })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getters
|
// Getters
|
||||||
|
Loading…
x
Reference in New Issue
Block a user