implement view-source feature

This commit is contained in:
yggverse 2024-12-08 13:59:57 +02:00
parent 9784a8a1a1
commit 614aa1d71a
5 changed files with 86 additions and 25 deletions

View File

@ -95,8 +95,9 @@ GTK 4 / Libadwaita client written in Rust
* [ ] [NPS](https://nightfall.city/nps/info/specification.txt)
* [ ] Localhost
* [ ] `file://` - localhost browser
* [ ] `config://` - low-level key/value settings editor
* [ ] `view-source://` - page source viewer (where supported)
* [ ] System
* [ ] `config:` - low-level key/value settings editor
* [x] `view-source:` - page source viewer (where supported)
### Media types

View File

@ -197,6 +197,12 @@ impl Page {
self.navigation.request.widget.entry.text()
};
// Detect source view mode, return `request` string prepared for route
let (request, is_view_source) = match request.strip_prefix("view-source:") {
Some(postfix) => (gformat!("{}", postfix.to_string()), true),
None => (request, false),
};
// Update
self.meta.set_status(Status::Reload).set_title("Loading..");
self.browser_action.update.activate(Some(&id));
@ -207,7 +213,7 @@ impl Page {
// Route by scheme
match uri.scheme().as_str() {
"file" => todo!(),
"gemini" => self.load_gemini(uri, is_history), // @TODO
"gemini" => self.load_gemini(uri, is_history, is_view_source), // @TODO
scheme => {
// Define common data
let status = Status::Failure;
@ -379,7 +385,7 @@ impl Page {
// Private helpers
// @TODO move outside
fn load_gemini(&self, uri: Uri, is_history: bool) {
fn load_gemini(&self, uri: Uri, is_history: bool, is_view_source: bool) {
// Init shared clones
let cancellable = self.client.cancellable();
let update = self.browser_action.update.clone();
@ -489,20 +495,26 @@ impl Page {
move |result|{
match result {
Ok(buffer) => {
// Set children component
let text_gemini = content.to_text_gemini(
&uri,
// Set children component,
// extract title from meta parsed
let title = if is_view_source {
content.to_text_source(
&buffer.data
);
let title = match text_gemini.meta.title {
Some(ref title) => title,
None => &uri_to_title(&uri)
uri_to_title(&uri)
} else {
match content.to_text_gemini(
&uri,
&buffer.data
).meta.title {
Some(meta_title) => meta_title,
None => uri_to_title(&uri)
}
};
// Update page meta
meta.set_status(Status::Success)
.set_title(title);
.set_title(&title);
// Update window components
update.activate(Some(&id));

View File

@ -92,7 +92,7 @@ impl Content {
/// * could be useful to extract document title parsed from Gemtext
pub fn to_text_gemini(&self, base: &Uri, data: &str) -> Text {
self.clean();
let text = Text::gemini(
let text = Text::new_gemini(
data,
base,
(self.window_action.clone(), self.tab_action.clone()),
@ -101,6 +101,13 @@ impl Content {
text
}
pub fn to_text_source(&self, data: &str) -> Text {
self.clean();
let text = Text::new_source(data);
self.gobject.append(&text.scrolled_window);
text
}
/// Remove all children components from `Self`
pub fn clean(&self) {
while let Some(child) = self.gobject.last_child() {

View File

@ -1,6 +1,8 @@
mod gemini;
mod source;
use gemini::Gemini;
use source::Source;
use crate::app::browser::window::{tab::item::Action as TabAction, Action as WindowAction};
use gtk::{glib::Uri, ScrolledWindow};
@ -16,24 +18,38 @@ pub struct Text {
}
impl Text {
// Construct
pub fn gemini(gemtext: &str, base: &Uri, actions: (Rc<WindowAction>, Rc<TabAction>)) -> Self {
// Init components
// Constructors
pub fn new_gemini(
gemtext: &str,
base: &Uri,
actions: (Rc<WindowAction>, Rc<TabAction>),
) -> Self {
// Init widget driver
let gemini = Gemini::new(gemtext, base, actions);
// Init meta
let meta = Meta {
title: gemini.reader.title.clone(),
};
// Init scrolled_window
// Init scrolled container
let scrolled_window = ScrolledWindow::builder().build();
scrolled_window.set_child(Some(&gemini.widget.clamp_scrollable));
// Result
Self {
meta,
meta: Meta {
title: gemini.reader.title.clone(),
},
scrolled_window,
}
}
pub fn new_source(data: &str) -> Self {
// Init scrolled container
let scrolled_window = ScrolledWindow::builder().build();
scrolled_window.set_child(Some(&Source::new(data).text_view));
// Result
Self {
meta: Meta { title: None },
scrolled_window,
}
}

View File

@ -0,0 +1,25 @@
use gtk::{TextBuffer, TextView};
const MARGIN: i32 = 8;
pub struct Source {
pub text_view: TextView,
}
impl Source {
pub fn new(data: &str) -> Self {
Self {
text_view: TextView::builder()
.bottom_margin(MARGIN)
.buffer(&TextBuffer::builder().text(data).build())
.cursor_visible(false)
.editable(false)
.left_margin(MARGIN)
.monospace(true)
.right_margin(MARGIN)
.top_margin(MARGIN)
.vexpand(true)
.build(),
}
}
}