-- Leo's gemini proxy
-- Connecting to typed-hole.org:1965...
-- Connected
-- Sending request
-- Meta line: 20 text/gemini
commit 110fa1541eb3710ec5783f8dafca3e394e0f7e37
Author: Julien Blanchard <julien@sideburns.eu>
Date: Sun Dec 22 15:47:47 2019 +0100
Extract GUI stuff, use Arc, display current URL in bar
diff --git a/src/gui.rs b/src/gui.rs
new file mode 100644
index 0000000..bbda792
--- /dev/null
+++ b/src/gui.rs
@@ -0,0 +1,70 @@
+use gtk::prelude::*;
+use gtk::{ApplicationWindow, Button, Entry, TextView};
+
+
+pub struct Gui {
+ window: ApplicationWindow,
+ url_bar: Entry,
+ content_view: TextView,
+ back_button: Button,
+}
+
+impl Gui {
+ pub fn new() -> Gui {
+ // Initialize the UI from the Glade XML.
+ let glade_src = include_str!("castor.glade");
+ let builder = gtk::Builder::new_from_string(glade_src);
+
+ // Get handles for the various controls we need to use.
+ let window: ApplicationWindow = builder.get_object("window").expect("Couldn't get window");
+ let url_bar: Entry = builder.get_object("url_bar").expect("Couldn't get url_bar");
+ let content_view: TextView = builder.get_object("content_view").expect("Couldn't get content_view");
+ let back_button: Button = builder.get_object("back_button").expect("Couldn't get back_button");
+
+ Gui {
+ window,
+ url_bar,
+ content_view,
+ back_button,
+ }
+ }
+
+ // Set up naming for the window and show it to the user.
+ pub fn start(&self) {
+ glib::set_application_name("Castor");
+ self.window.set_wmclass("Castor", "Castor");
+ self.window.connect_delete_event(|_, _| { gtk::main_quit(); Inhibit(false) });
+ self.window.show_all();
+ }
+
+ // pub fn update_from(&self, state: &State) {
+ // if let Some(ref err) = state.error {
+ // self.error_label.set_text(
+ // &format!("The dice expression entered is not valid:\n{}", err)
+ // );
+ // self.popover.show_all();
+ // } else {
+ // // The popover will hide itself anyway when the user clicks
+ // // outside of it, but we shouldn't leave an error indicator in it.
+ // self.error_label.set_text("");
+ // }
+
+ // self.result.set_text(&format!("{}", state.value));
+ // }
+
+ pub fn window(&self) -> &ApplicationWindow {
+ &self.window
+ }
+
+ pub fn url_bar(&self) -> &Entry {
+ &self.url_bar
+ }
+
+ pub fn content_view(&self) -> &TextView {
+ &self.content_view
+ }
+
+ pub fn back_button(&self) -> &Button {
+ &self.back_button
+ }
+}
diff --git a/src/main.rs b/src/main.rs
index 27ffa46..c214307 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -6,22 +6,22 @@ extern crate gtk;
extern crate lazy_static;
-use gio::prelude::*;
+use std::sync::Arc;
+
use glib::clone;
use gtk::prelude::*;
-use gtk::{ApplicationWindow, Builder, Button, Entry, TextBuffer, TextBufferExt, TextView};
-
-use std::env::args;
+use gtk::{TextBuffer, TextBufferExt};
extern crate regex;
use regex::Regex;
-mod content;
+mod gui;
+use gui::Gui;
mod absolute;
+mod content;
mod history;
-mod tags;
mod link;
-use link::Link;
+mod tags;
const LINK_REGEX: &str = r"^=>\s*(\S*)\s*(.*)?$";
const H1_REGEX: &str = r"^#\s+(.*)$";
@@ -29,79 +29,87 @@ const H2_REGEX: &str = r"^##\s+(.*)$";
const H3_REGEX: &str = r"^###\s+(.*)$";
const UL_REGEX: &str = r"^\s*\*\s+(.*)$";
-fn build_ui(application: >k::Application) {
- let glade_src = include_str!("castor.glade");
- let builder = Builder::new_from_string(glade_src);
-
- let window: ApplicationWindow = builder.get_object("window").expect("Couldn't get window");
- window.set_application(Some(application));
- let url_bar: Entry = builder.get_object("url_bar").expect("Couldn't get url_bar");
- let content_view: TextView = builder.get_object("content_view").expect("Couldn't get content_view");
- let back_button: Button = builder.get_object("back_button").expect("Couldn't get back_button");
- tags::apply_tags(&content_view.get_buffer().unwrap());
-
- url_bar.connect_activate(clone!(@weak content_view => move |bar| {
- let url = bar.get_text().expect("get_text failed").to_string();
- let full_url = if url.starts_with("gemini://") {
- url
- } else {
- format!("gemini://{}", url)
- };
+fn main() {
+ // Start up the GTK3 subsystem.
+ gtk::init().expect("Unable to start GTK3. Error");
- let new_content = visit_url(full_url, &content_view);
- }));
+ // Create the main window.
+ let gui = Arc::new(Gui::new());
+ let content_view = gui.content_view();
- back_button.connect_clicked(clone!(@weak content_view => move |_| {
- go_back(&content_view);
- }));
+ // Bind back button
+ {
+ let button = gui.back_button();
+ let gui = gui.clone();
+ button.connect_clicked(clone!(@weak content_view => move |_| {
+ go_back(&gui);
+ }));
+ }
- window.show_all();
-}
+ // Bind URL bar
+ {
+ let gui2 = gui.clone();
+ let url_bar = gui.url_bar();
+ url_bar.connect_activate(clone!(@weak content_view => move |bar| {
+ let url = bar.get_text().expect("get_text failed").to_string();
+ let full_url = if url.starts_with("gemini://") {
+ url
+ } else {
+ format!("gemini://{}", url)
+ };
-fn main() {
- let application =
- gtk::Application::new(Some("org.typed-hole.castor"), Default::default())
- .expect("Initialization failed...");
+ let new_content = visit_url(full_url, &gui2);
+ }));
+ }
- application.connect_activate(|app| {
- build_ui(app);
- });
+ // Create Pango tags
+ tags::apply_tags(&content_view.get_buffer().unwrap());
- application.run(&args().collect::<Vec<_>>());
+ gui.start();
+ gtk::main();
}
-fn go_back(view: &TextView) {
+fn go_back(gui: &Arc<Gui>) {
let previous = history::get_previous_url();
if let Some(url) = previous {
- visit_url(url.to_string(), view)
+ visit_url(url.to_string(), gui)
}
}
-fn visit_url(url: String, view: &TextView) {
+fn update_url_field(url: &str, gui: &Arc<Gui>) -> () {
+ let url_bar = gui.url_bar();
+ url_bar.get_buffer().set_text(url);
+}
+
+fn visit_url(url: String, gui: &Arc<Gui>) {
{
- println!("{:?}", url);
+ let content_view = gui.content_view();
+
match absolute::make(url.as_str()) {
Ok(url) => match content::get_data(&url) {
Ok((_meta, new_content)) => {
history::append(url.as_str());
+ update_url_field(url.as_str(), &gui);
let content_str = String::from_utf8_lossy(&new_content).to_string();
- clear_buffer(&view);
- parse_gemini(content_str, &view);
- view.show_all();
+
+ clear_buffer(&content_view);
+
+ parse_gemini(content_str, &gui);
+ content_view.show_all();
}
Err(_) => {
- let buffer = view.get_buffer().unwrap();
+ let buffer = content_view.get_buffer().unwrap();
let mut end_iter = buffer.get_end_iter();
- clear_buffer(&view);
+ clear_buffer(&content_view);
buffer.insert_markup(
&mut end_iter,
"<span foreground=\"red\" size=\"x-large\">ERROR</span>\n",
);
}
- }
+ },
Err(_) => {
println!("Could not parse {}", url.as_str());
}
@@ -109,13 +117,14 @@ fn visit_url(url: String, view: &TextView) {
}
}
-fn parse_gemini(content: String, view: &TextView) -> TextBuffer {
+fn parse_gemini(content: String, gui: &Arc<Gui>) -> TextBuffer {
let link_regexp = Regex::new(LINK_REGEX).unwrap();
let h1_regexp = Regex::new(H1_REGEX).unwrap();
let h2_regexp = Regex::new(H2_REGEX).unwrap();
let h3_regexp = Regex::new(H3_REGEX).unwrap();
let ul_regexp = Regex::new(UL_REGEX).unwrap();
- let buffer = view.get_buffer().unwrap();
+ let content_view = gui.content_view();
+ let buffer = content_view.get_buffer().unwrap();
let mut i = 0;
for line in content.lines() {
@@ -132,14 +141,14 @@ fn parse_gemini(content: String, view: &TextView) -> TextBuffer {
let button = gtk::Button::new_with_label(&button_label);
- button.connect_clicked(clone!(@weak view => move |button| {
+ button.connect_clicked(clone!(@weak gui => move |button| {
let new_url = absolute::make(&dest.clone()).unwrap().to_string();
- visit_url(new_url, &view);
+ visit_url(new_url, &gui);
}));
let mut start_iter = buffer.get_iter_at_line(i);
let anchor = buffer.create_child_anchor(&mut start_iter).unwrap();
- view.add_child_at_anchor(&button, &anchor);
+ content_view.add_child_at_anchor(&button, &anchor);
let mut end_iter = buffer.get_end_iter();
buffer.insert(&mut end_iter, "\n");
} else if h1_regexp.is_match(line) {
---
Served by Pollux Gemini Server.
-- Response ended
-- Page fetched on Sun May 19 07:57:27 2024