mirror of
https://github.com/YGGverse/Yoda.git
synced 2025-01-28 20:14: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 gtk::{
|
||||
gdk::{BUTTON_MIDDLE, BUTTON_PRIMARY},
|
||||
gio::SimpleAction,
|
||||
glib::{GString, TimeZone, Uri},
|
||||
prelude::{TextBufferExt, TextBufferExtManual},
|
||||
TextBuffer, TextTag, TextTagTable, TextView, WrapMode,
|
||||
prelude::{ActionExt, TextBufferExt, TextBufferExtManual, TextViewExt, ToVariant},
|
||||
EventControllerMotion, GestureClick, TextBuffer, TextTag, TextView, TextWindowType, WrapMode,
|
||||
};
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
pub struct Reader {
|
||||
title: Option<GString>,
|
||||
// css: CssProvider,
|
||||
widget: Arc<Widget>,
|
||||
}
|
||||
|
||||
impl Reader {
|
||||
// Construct
|
||||
pub fn new_arc(gemtext: &str, base: &Uri, action_page_open: Arc<SimpleAction>) -> Arc<Self> {
|
||||
// Init title
|
||||
// Init default values
|
||||
let mut title = None;
|
||||
|
||||
// Init tag table
|
||||
let tags = TextTagTable::new();
|
||||
// Init HashMap storage for event controllers
|
||||
let mut links: HashMap<TextTag, Uri> = HashMap::new();
|
||||
|
||||
// Init header tags
|
||||
let h1 = TextTag::builder()
|
||||
.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));
|
||||
// Init new text buffer
|
||||
let buffer = TextBuffer::new(None);
|
||||
|
||||
// Parse gemtext lines
|
||||
for line in gemtext.lines() {
|
||||
// Is header
|
||||
if let Some(header) = Header::from(line) {
|
||||
// Detect level
|
||||
// Build tag from level parsed
|
||||
let tag = match header.level {
|
||||
parser::header::Level::H1 => "h1",
|
||||
parser::header::Level::H2 => "h2",
|
||||
parser::header::Level::H3 => "h3",
|
||||
parser::header::Level::H1 => TextTag::builder()
|
||||
.scale(1.6)
|
||||
.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
|
||||
buffer.insert_with_tags_by_name(
|
||||
&mut buffer.end_iter(),
|
||||
header.value.as_str(),
|
||||
&[tag],
|
||||
);
|
||||
// Register tag in buffer
|
||||
buffer.tag_table().add(&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");
|
||||
|
||||
// Set title if empty, on first document header match
|
||||
// this feature wanted to update parent elements like tab title
|
||||
// Update reader title using first gemtext header match
|
||||
if title == None {
|
||||
title = Some(header.value.clone());
|
||||
}
|
||||
|
||||
// Skip other actions for this line
|
||||
continue;
|
||||
}
|
||||
|
||||
// Is link
|
||||
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();
|
||||
|
||||
// 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 {
|
||||
Some(alt) => alt.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");
|
||||
|
||||
// Skip other actions for this line
|
||||
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(), "\n");
|
||||
}
|
||||
|
||||
// Init additional controllers
|
||||
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();
|
||||
|
||||
// Init widget
|
||||
let widget = Widget::new_arc(&buffer);
|
||||
let widget = Widget::new_arc(
|
||||
&buffer,
|
||||
primary_button_controller.clone(),
|
||||
middle_button_controller.clone(),
|
||||
motion_controller.clone(),
|
||||
);
|
||||
|
||||
// Connect actions
|
||||
/* @TODO
|
||||
widget.connect_activate_link(move |_, href| {
|
||||
// Detect requested protocol
|
||||
if let Ok(uri) = Uri::parse(&href, UriFlags::NONE) {
|
||||
return match uri.scheme().as_str() {
|
||||
"gemini" => {
|
||||
// Open new page
|
||||
action_page_open.activate(Some(&uri.to_str().to_variant()));
|
||||
|
||||
// Prevent link open in external application
|
||||
Propagation::Stop
|
||||
// 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" => {
|
||||
// Open new page
|
||||
action_page_open.activate(Some(&uri.to_str().to_variant()));
|
||||
}
|
||||
_ => (), // Protocol not supported, delegate to the external app @TODO
|
||||
};
|
||||
}
|
||||
}
|
||||
// Protocol not supported
|
||||
_ => Propagation::Proceed,
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Delegate unparsable
|
||||
Propagation::Proceed
|
||||
}); */
|
||||
// @TODO on middle-click, on hover events
|
||||
// primary_button_controller(|_, _, _, _| {});
|
||||
// motion_controller.connect_motion(|_, _, _| {});
|
||||
|
||||
// Result
|
||||
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;
|
||||
|
||||
pub struct Widget {
|
||||
@ -7,16 +9,25 @@ pub struct Widget {
|
||||
|
||||
impl Widget {
|
||||
// Construct
|
||||
pub fn new_arc(buffer: &TextBuffer) -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
gobject: TextView::builder()
|
||||
.editable(false)
|
||||
.cursor_visible(false)
|
||||
.wrap_mode(WrapMode::Word)
|
||||
.vexpand(true)
|
||||
.buffer(buffer)
|
||||
.build(),
|
||||
})
|
||||
pub fn new_arc(
|
||||
buffer: &TextBuffer,
|
||||
primary_button_controller: GestureClick,
|
||||
middle_button_controller: GestureClick,
|
||||
motion_controller: EventControllerMotion,
|
||||
) -> Arc<Self> {
|
||||
let gobject = TextView::builder()
|
||||
.editable(false)
|
||||
.cursor_visible(false)
|
||||
.wrap_mode(WrapMode::Word)
|
||||
.vexpand(true)
|
||||
.buffer(buffer)
|
||||
.build();
|
||||
|
||||
gobject.add_controller(primary_button_controller);
|
||||
gobject.add_controller(middle_button_controller);
|
||||
gobject.add_controller(motion_controller);
|
||||
|
||||
Arc::new(Self { gobject })
|
||||
}
|
||||
|
||||
// Getters
|
||||
|
Loading…
x
Reference in New Issue
Block a user