Yoda/src/app/browser/window/tab/item/page/content/text.rs
2026-03-11 15:50:57 +02:00

131 lines
3.5 KiB
Rust

mod gemini;
mod markdown;
mod nex;
mod plain;
mod source;
use crate::{app::browser::window::tab::item::page::Page, profile::Profile};
use super::{ItemAction, WindowAction};
use adw::ClampScrollable;
use gemini::Gemini;
use gtk::{ScrolledWindow, TextView, glib::Uri};
use markdown::Markdown;
use nex::Nex;
use plain::Plain;
use source::Source;
use std::rc::Rc;
pub struct Meta {
pub title: Option<String>,
} // @TODO move to separated mod
pub struct Text {
pub meta: Meta,
pub scrolled_window: ScrolledWindow,
pub text_view: TextView,
}
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, profile, base, gemtext) {
Ok(widget) => Ok(Self {
scrolled_window: reader(&widget.text_view),
text_view: widget.text_view,
meta: Meta {
title: widget.title,
},
}),
Err(e) => match e {
gemini::Error::Markup(message, widget) => Err((
message,
Some(Self {
scrolled_window: reader(&widget.text_view),
text_view: widget.text_view,
meta: Meta {
title: widget.title,
},
}),
)),
},
}
}
pub fn markdown(
actions: (&Rc<WindowAction>, &Rc<ItemAction>),
page: &Rc<Page>,
base: &Uri,
gemtext: &str,
) -> Self {
let markdown = Markdown::build(actions, page, base, gemtext);
Self {
scrolled_window: reader(&markdown.text_view),
text_view: markdown.text_view,
meta: Meta {
title: markdown.title,
},
}
}
pub fn plain(data: &str) -> Self {
let text_view = TextView::plain(data);
Self {
scrolled_window: reader(&text_view),
text_view,
meta: Meta { title: None },
}
}
pub fn nex(actions: (&Rc<WindowAction>, &Rc<ItemAction>), base: &Uri, data: &str) -> Self {
let mut title = None;
let text_view = TextView::nex(actions, base, data, &mut title);
Self {
scrolled_window: reader(&text_view),
text_view,
meta: Meta { title },
}
}
pub fn source(data: &str) -> Self {
let source = sourceview::View::source(data);
Self {
scrolled_window: ScrolledWindow::builder().child(&source).build(),
text_view: source.into_text_view(),
meta: Meta { title: None },
}
}
}
// Tools
// Grab focus into the `TextView` on click empty `ClampScrollable` area
fn grab_focus_patch(clamp_scrollable: &ClampScrollable, text_view: &TextView) {
use gtk::{GestureClick, prelude::WidgetExt};
let controller = GestureClick::new();
controller.connect_pressed({
let text_view = text_view.clone();
move |_, _, _, _| {
text_view.grab_focus();
}
});
clamp_scrollable.add_controller(controller);
}
fn reader(text_view: &TextView) -> ScrolledWindow {
let clamp_scrollable = ClampScrollable::builder()
.child(text_view)
.css_classes(["view"])
.maximum_size(800)
.build();
grab_focus_patch(&clamp_scrollable, text_view);
ScrolledWindow::builder().child(&clamp_scrollable).build()
}