From 614aa1d71a7c4b46b720e5d43ac825b255396d19 Mon Sep 17 00:00:00 2001 From: yggverse Date: Sun, 8 Dec 2024 13:59:57 +0200 Subject: [PATCH] implement view-source feature --- README.md | 5 +-- src/app/browser/window/tab/item/page.rs | 36 ++++++++++++------- .../browser/window/tab/item/page/content.rs | 9 ++++- .../window/tab/item/page/content/text.rs | 36 +++++++++++++------ .../tab/item/page/content/text/source.rs | 25 +++++++++++++ 5 files changed, 86 insertions(+), 25 deletions(-) create mode 100644 src/app/browser/window/tab/item/page/content/text/source.rs diff --git a/README.md b/README.md index 2012ec11..a2e003e9 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/src/app/browser/window/tab/item/page.rs b/src/app/browser/window/tab/item/page.rs index 499023a0..2f495667 100644 --- a/src/app/browser/window/tab/item/page.rs +++ b/src/app/browser/window/tab/item/page.rs @@ -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, - &buffer.data - ); - - let title = match text_gemini.meta.title { - Some(ref title) => title, - None => &uri_to_title(&uri) + // Set children component, + // extract title from meta parsed + let title = if is_view_source { + content.to_text_source( + &buffer.data + ); + 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)); diff --git a/src/app/browser/window/tab/item/page/content.rs b/src/app/browser/window/tab/item/page/content.rs index 6c3ae4b5..ea1df705 100644 --- a/src/app/browser/window/tab/item/page/content.rs +++ b/src/app/browser/window/tab/item/page/content.rs @@ -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() { diff --git a/src/app/browser/window/tab/item/page/content/text.rs b/src/app/browser/window/tab/item/page/content/text.rs index 7de7ca24..2fc1b450 100644 --- a/src/app/browser/window/tab/item/page/content/text.rs +++ b/src/app/browser/window/tab/item/page/content/text.rs @@ -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, Rc)) -> Self { - // Init components + // Constructors + + pub fn new_gemini( + gemtext: &str, + base: &Uri, + actions: (Rc, Rc), + ) -> 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, } } diff --git a/src/app/browser/window/tab/item/page/content/text/source.rs b/src/app/browser/window/tab/item/page/content/text/source.rs new file mode 100644 index 00000000..44e0694d --- /dev/null +++ b/src/app/browser/window/tab/item/page/content/text/source.rs @@ -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(), + } + } +}