From fb5455ae891c30cdf55fca393b70eb8dd907dd16 Mon Sep 17 00:00:00 2001 From: Namilskyy Date: Sat, 6 Dec 2025 23:13:31 +0300 Subject: Implemented bookmarks and some fixes --- src/bookmarks.rs | 163 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 60 +++++++++++++++++++- 2 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 src/bookmarks.rs diff --git a/src/bookmarks.rs b/src/bookmarks.rs new file mode 100644 index 0000000..82edd33 --- /dev/null +++ b/src/bookmarks.rs @@ -0,0 +1,163 @@ +use gtk4::prelude::*; +use gtk4::{Box, Button, Orientation}; +use serde::{Deserialize, Serialize}; +use std::fs; +use std::path::PathBuf; +use webkit6::prelude::*; +use webkit6::WebView; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Bookmark { + pub name: String, + pub url: String, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct Bookmarks { + pub items: Vec, +} + +impl Bookmarks { + fn default_config_path() -> PathBuf { + let home = std::env::var("HOME").unwrap_or_else(|_| String::from(".")); + let mut path = PathBuf::from(home); + path.push(".config"); + path.push("i2p-browser"); + let _ = fs::create_dir_all(&path); + path.push("bookmarks.toml"); + path + } + + pub fn load_from_file() -> Bookmarks { + let path = Self::default_config_path(); + if let Ok(content) = fs::read_to_string(path) { + toml::from_str(&content).unwrap_or_else(|_| Bookmarks { + items: vec![ + Bookmark { + name: "Reg.i2p".to_string(), + url: "http://reg.i2p".to_string(), + }, + Bookmark { + name: "Kislitsa forum".to_string(), + url: "http://kislitsa.i2p/".to_string(), + }, + Bookmark { + name: "Soundcloack".to_string(), + url: "http://maidzones5taqt3g2boaepkxdi6f2etznqbfebludsl4i2ydrcjq.b32.i2p/".to_string(), + }, + ], + }) + } else { + Bookmarks { + items: vec![ + Bookmark { + name: "Reg.i2p".to_string(), + url: "http://reg.i2p".to_string(), + }, + Bookmark { + name: "Kislitsa forum".to_string(), + url: "http://kislitsa.i2p/".to_string(), + }, + Bookmark { + name: "Soundcloack".to_string(), + url: "http://maidzones5taqt3g2boaepkxdi6f2etznqbfebludsl4i2ydrcjq.b32.i2p/".to_string(), + }, + ], + } + } + } + + pub fn save_to_file(&self) -> Result<(), toml::ser::Error> { + let path = Self::default_config_path(); + let toml: String = toml::to_string(self)?; + fs::write(path, toml) + .expect("Failed to write bookmarks file"); + Ok(()) + } + + pub fn add_bookmark(&mut self, name: String, url: String) { + self.items.push(Bookmark { name, url }); + let _ = self.save_to_file(); + } + + pub fn remove_bookmark(&mut self, index: usize) { + if index < self.items.len() { + self.items.remove(index); + let _ = self.save_to_file(); + } + } +} + +pub struct BookmarksBar { + pub container: Box, + bookmarks: Bookmarks, + webview: Option, +} + +impl BookmarksBar { + pub fn new() -> Self { + let container = Box::builder() + .orientation(Orientation::Horizontal) + .spacing(5) + .margin_start(10) + .margin_end(10) + .margin_top(5) + .margin_bottom(5) + .build(); + + let mut bookmarks_bar = BookmarksBar { + container, + bookmarks: Bookmarks::load_from_file(), + webview: None, + }; + + bookmarks_bar.refresh_bookmarks(); + bookmarks_bar + } + + pub fn set_webview(&mut self, webview: &WebView) { + self.webview = Some(webview.clone()); + self.refresh_bookmarks(); + } + + fn refresh_bookmarks(&mut self) { + while let Some(child) = self.container.first_child() { + self.container.remove(&child); + } + + for bookmark in self.bookmarks.items.iter() { + let button = Button::with_label(&bookmark.name); + let url = bookmark.url.clone(); + + if let Some(ref webview) = self.webview { + let webview_clone = webview.clone(); + button.connect_clicked(move |_| { + webview_clone.load_uri(&url); + }); + } + + self.container.append(&button); + } + + let add_button = Button::with_label("+"); + let webview_ref = self.webview.clone(); + add_button.connect_clicked(move |_| { + // TODO: Show dialog to add bookmark + if let Some(ref webview) = webview_ref { + if let Some(uri) = webview.uri() { + println!("Add bookmark for current URL: {}", uri); + } + } + }); + self.container.append(&add_button); + } + + pub fn add_current_page(&mut self, name: String, url: String) { + self.bookmarks.add_bookmark(name, url); + self.refresh_bookmarks(); + } + + pub fn get_container(&self) -> &Box { + &self.container + } +} diff --git a/src/main.rs b/src/main.rs index 287ec42..48fb653 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,10 @@ mod redirecting_i2p; mod settings_window; +mod bookmarks; use gtk4::prelude::*; use gtk4::{ - Application, ApplicationWindow, Box, Button, CheckButton, Entry, HeaderBar, Image, Orientation, + Application, ApplicationWindow, Box, Button, CheckButton, Entry, HeaderBar, Orientation, ScrolledWindow, }; use webkit6::prelude::*; @@ -50,6 +51,7 @@ fn build_ui(app: &Application) { let settings_button = Button::with_label("Settings"); let i2p_proxy = CheckButton::with_label("Enable i2p proxy"); let i2p_panel = Button::with_label("Panel"); + let bookmark_button = Button::with_label("Bookmark"); header_bar.pack_start(&button_back); header_bar.pack_start(&button_forward); @@ -58,10 +60,19 @@ fn build_ui(app: &Application) { header_bar.pack_end(&settings_button); header_bar.pack_end(&i2p_proxy); header_bar.pack_end(&i2p_panel); + header_bar.pack_end(&bookmark_button); let main_box = Box::new(Orientation::Vertical, 0); window.set_child(Some(&main_box)); + // Create bookmarks bar + let bookmarks_bar = bookmarks::BookmarksBar::new(); + + // Use Rc for shared mutable access + use std::rc::Rc; + use std::cell::RefCell; + let bookmarks_bar_shared = Rc::new(RefCell::new(bookmarks_bar)); + if let Some(enabled) = settings_window::load_i2p_proxy_enabled() { i2p_proxy.set_active(enabled); configure_i2p_proxy(enabled); @@ -80,7 +91,11 @@ fn build_ui(app: &Application) { web_view.set_vexpand(true); web_view.set_hexpand(true); + // Set webview for bookmarks bar + bookmarks_bar_shared.borrow_mut().set_webview(&web_view); + main_box.append(&url_entry); + main_box.append(bookmarks_bar_shared.borrow().get_container()); if let Some(enable_js) = settings_window::load_js_enabled() && let Some(web_settings) = webkit6::prelude::WebViewExt::settings(&web_view) { @@ -162,6 +177,49 @@ fn build_ui(app: &Application) { let i2p_panel_url = format!("http://127.0.0.1:{}/", panel_port); web_view_for_panel.load_uri(&i2p_panel_url); }); + + let web_view_for_bookmark = web_view.clone(); + let bookmarks_bar_for_bookmark = bookmarks_bar_shared.clone(); + bookmark_button.connect_clicked(move |_| { + if let Some(uri) = web_view_for_bookmark.uri() { + let dialog = gtk4::Dialog::builder() + .title("Add Bookmark") + .modal(true) + .default_width(300) + .build(); + + let content_area = dialog.content_area(); + let entry_box = gtk4::Box::new(Orientation::Vertical, 5); + entry_box.set_margin_top(10); + entry_box.set_margin_bottom(10); + entry_box.set_margin_start(10); + entry_box.set_margin_end(10); + + let label = gtk4::Label::new(Some("Bookmark name:")); + let name_entry = gtk4::Entry::new(); + name_entry.set_text(&uri); + + entry_box.append(&label); + entry_box.append(&name_entry); + content_area.append(&entry_box); + + dialog.add_button("Cancel", gtk4::ResponseType::Cancel); + dialog.add_button("Add", gtk4::ResponseType::Accept); + + let name_entry_for_response = name_entry.clone(); + let uri_for_response = uri.clone(); + let bookmarks_bar_clone = bookmarks_bar_for_bookmark.clone(); + dialog.connect_response(move |dialog, response| { + if response == gtk4::ResponseType::Accept { + let name = name_entry_for_response.text().to_string(); + bookmarks_bar_clone.borrow_mut().add_current_page(name, uri_for_response.to_string()); + } + dialog.close(); + }); + + dialog.present(); + } + }); web_view.load_uri("http://legwork.i2p/"); window.present(); } -- cgit v1.2.3