mirror of
https://github.com/YGGverse/Yoda.git
synced 2025-01-15 09:10:08 +00:00
implement view-source feature
This commit is contained in:
parent
9784a8a1a1
commit
614aa1d71a
@ -95,8 +95,9 @@ GTK 4 / Libadwaita client written in Rust
|
|||||||
* [ ] [NPS](https://nightfall.city/nps/info/specification.txt)
|
* [ ] [NPS](https://nightfall.city/nps/info/specification.txt)
|
||||||
* [ ] Localhost
|
* [ ] Localhost
|
||||||
* [ ] `file://` - localhost browser
|
* [ ] `file://` - localhost browser
|
||||||
* [ ] `config://` - low-level key/value settings editor
|
* [ ] System
|
||||||
* [ ] `view-source://` - page source viewer (where supported)
|
* [ ] `config:` - low-level key/value settings editor
|
||||||
|
* [x] `view-source:` - page source viewer (where supported)
|
||||||
|
|
||||||
### Media types
|
### Media types
|
||||||
|
|
||||||
|
@ -197,6 +197,12 @@ impl Page {
|
|||||||
self.navigation.request.widget.entry.text()
|
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
|
// Update
|
||||||
self.meta.set_status(Status::Reload).set_title("Loading..");
|
self.meta.set_status(Status::Reload).set_title("Loading..");
|
||||||
self.browser_action.update.activate(Some(&id));
|
self.browser_action.update.activate(Some(&id));
|
||||||
@ -207,7 +213,7 @@ impl Page {
|
|||||||
// Route by scheme
|
// Route by scheme
|
||||||
match uri.scheme().as_str() {
|
match uri.scheme().as_str() {
|
||||||
"file" => todo!(),
|
"file" => todo!(),
|
||||||
"gemini" => self.load_gemini(uri, is_history), // @TODO
|
"gemini" => self.load_gemini(uri, is_history, is_view_source), // @TODO
|
||||||
scheme => {
|
scheme => {
|
||||||
// Define common data
|
// Define common data
|
||||||
let status = Status::Failure;
|
let status = Status::Failure;
|
||||||
@ -379,7 +385,7 @@ impl Page {
|
|||||||
// Private helpers
|
// Private helpers
|
||||||
|
|
||||||
// @TODO move outside
|
// @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
|
// Init shared clones
|
||||||
let cancellable = self.client.cancellable();
|
let cancellable = self.client.cancellable();
|
||||||
let update = self.browser_action.update.clone();
|
let update = self.browser_action.update.clone();
|
||||||
@ -489,20 +495,26 @@ impl Page {
|
|||||||
move |result|{
|
move |result|{
|
||||||
match result {
|
match result {
|
||||||
Ok(buffer) => {
|
Ok(buffer) => {
|
||||||
// Set children component
|
// Set children component,
|
||||||
let text_gemini = content.to_text_gemini(
|
// extract title from meta parsed
|
||||||
&uri,
|
let title = if is_view_source {
|
||||||
|
content.to_text_source(
|
||||||
&buffer.data
|
&buffer.data
|
||||||
);
|
);
|
||||||
|
uri_to_title(&uri)
|
||||||
let title = match text_gemini.meta.title {
|
} else {
|
||||||
Some(ref title) => title,
|
match content.to_text_gemini(
|
||||||
None => &uri_to_title(&uri)
|
&uri,
|
||||||
|
&buffer.data
|
||||||
|
).meta.title {
|
||||||
|
Some(meta_title) => meta_title,
|
||||||
|
None => uri_to_title(&uri)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Update page meta
|
// Update page meta
|
||||||
meta.set_status(Status::Success)
|
meta.set_status(Status::Success)
|
||||||
.set_title(title);
|
.set_title(&title);
|
||||||
|
|
||||||
// Update window components
|
// Update window components
|
||||||
update.activate(Some(&id));
|
update.activate(Some(&id));
|
||||||
|
@ -92,7 +92,7 @@ impl Content {
|
|||||||
/// * could be useful to extract document title parsed from Gemtext
|
/// * could be useful to extract document title parsed from Gemtext
|
||||||
pub fn to_text_gemini(&self, base: &Uri, data: &str) -> Text {
|
pub fn to_text_gemini(&self, base: &Uri, data: &str) -> Text {
|
||||||
self.clean();
|
self.clean();
|
||||||
let text = Text::gemini(
|
let text = Text::new_gemini(
|
||||||
data,
|
data,
|
||||||
base,
|
base,
|
||||||
(self.window_action.clone(), self.tab_action.clone()),
|
(self.window_action.clone(), self.tab_action.clone()),
|
||||||
@ -101,6 +101,13 @@ impl Content {
|
|||||||
text
|
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`
|
/// Remove all children components from `Self`
|
||||||
pub fn clean(&self) {
|
pub fn clean(&self) {
|
||||||
while let Some(child) = self.gobject.last_child() {
|
while let Some(child) = self.gobject.last_child() {
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
mod gemini;
|
mod gemini;
|
||||||
|
mod source;
|
||||||
|
|
||||||
use gemini::Gemini;
|
use gemini::Gemini;
|
||||||
|
use source::Source;
|
||||||
|
|
||||||
use crate::app::browser::window::{tab::item::Action as TabAction, Action as WindowAction};
|
use crate::app::browser::window::{tab::item::Action as TabAction, Action as WindowAction};
|
||||||
use gtk::{glib::Uri, ScrolledWindow};
|
use gtk::{glib::Uri, ScrolledWindow};
|
||||||
@ -16,24 +18,38 @@ pub struct Text {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Text {
|
impl Text {
|
||||||
// Construct
|
// Constructors
|
||||||
pub fn gemini(gemtext: &str, base: &Uri, actions: (Rc<WindowAction>, Rc<TabAction>)) -> Self {
|
|
||||||
// Init components
|
pub fn new_gemini(
|
||||||
|
gemtext: &str,
|
||||||
|
base: &Uri,
|
||||||
|
actions: (Rc<WindowAction>, Rc<TabAction>),
|
||||||
|
) -> Self {
|
||||||
|
// Init widget driver
|
||||||
let gemini = Gemini::new(gemtext, base, actions);
|
let gemini = Gemini::new(gemtext, base, actions);
|
||||||
|
|
||||||
// Init meta
|
// Init scrolled container
|
||||||
let meta = Meta {
|
|
||||||
title: gemini.reader.title.clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Init scrolled_window
|
|
||||||
let scrolled_window = ScrolledWindow::builder().build();
|
let scrolled_window = ScrolledWindow::builder().build();
|
||||||
|
|
||||||
scrolled_window.set_child(Some(&gemini.widget.clamp_scrollable));
|
scrolled_window.set_child(Some(&gemini.widget.clamp_scrollable));
|
||||||
|
|
||||||
// Result
|
// Result
|
||||||
Self {
|
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,
|
scrolled_window,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
25
src/app/browser/window/tab/item/page/content/text/source.rs
Normal file
25
src/app/browser/window/tab/item/page/content/text/source.rs
Normal 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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user