From 872726086ed05e8369ad7be78845e6567d2b4114 Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Wed, 18 Sep 2024 22:37:27 -0600 Subject: [PATCH 01/15] creating overlay --- examples/basic_browser.rs | 43 +++--- src/lib.rs | 54 +++++++- src/widgets/browser_view.rs | 7 +- src/widgets/mod.rs | 258 ++++++++++++++++++++++++++---------- src/widgets/overlay.rs | 32 +++++ 5 files changed, 305 insertions(+), 89 deletions(-) create mode 100644 src/widgets/overlay.rs diff --git a/examples/basic_browser.rs b/examples/basic_browser.rs index d27ad56..76d76a4 100644 --- a/examples/basic_browser.rs +++ b/examples/basic_browser.rs @@ -1,9 +1,12 @@ // Simple browser with familiar browser widget and the ultralight(webkit) webengine as a backend -use iced::{Element, Settings, Theme}; +use iced::Theme; +use iced::{Element, Settings, Subscription, Task}; use iced_aw::BOOTSTRAP_FONT_BYTES; +use smol_str::SmolStr; +use std::time::Duration; -use icy_browser::{widgets, BrowserWidget, Ultralight}; +use icy_browser::{widgets, BrowserWidget, KeyType, Shortcut, Ultralight}; fn main() -> iced::Result { // This imports `icons` for widgets @@ -14,6 +17,7 @@ fn main() -> iced::Result { }; iced::application("Basic Browser Example", Browser::update, Browser::view) + .subscription(Browser::subscription) .settings(settings) .theme(|_| Theme::Dark) .run() @@ -22,34 +26,41 @@ fn main() -> iced::Result { #[derive(Debug, Clone)] pub enum Message { BrowserWidget(widgets::Message), + Update, } struct Browser { widgets: BrowserWidget, } +impl Default for Browser { + fn default() -> Self { + let shortcuts = vec![Shortcut::ShowOverlay(vec![KeyType::Key( + iced::keyboard::Key::Character(SmolStr::from("e")), + )])]; + let widgets = BrowserWidget::new_with_ultralight() + .with_custom_shortcuts(shortcuts) + .with_tab_bar() + .with_nav_bar() + .build(); + + Self { widgets } + } +} + impl Browser { - fn update(&mut self, message: Message) { + fn update(&mut self, message: Message) -> Task { match message { - Message::BrowserWidget(msg) => { - self.widgets.update(msg); - } + Message::BrowserWidget(msg) => self.widgets.update(msg).map(Message::BrowserWidget), + Message::Update => self.widgets.force_update().map(Message::BrowserWidget), } } fn view(&self) -> Element { self.widgets.view().map(Message::BrowserWidget) } -} - -impl Default for Browser { - fn default() -> Self { - let widgets = BrowserWidget::new_with_ultralight() - .with_tab_bar() - .with_nav_bar() - .with_browsesr_view() - .build(); - Self { widgets } + fn subscription(&self) -> Subscription { + iced::time::every(Duration::from_millis(10)).map(move |_| Message::Update) } } diff --git a/src/lib.rs b/src/lib.rs index 42f6fcc..6d1604d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +use iced::keyboard::{Key, Modifiers}; use iced::widget::image::{Handle, Image}; use url::{ParseError, Url}; @@ -33,7 +34,7 @@ impl ImageInfo { const WIDTH: u32 = 800; const HEIGHT: u32 = 800; - fn new(pixels: Vec, format: PixelFormat, width: u32, height: u32) -> Self { + pub fn new(pixels: Vec, format: PixelFormat, width: u32, height: u32) -> Self { // R, G, B, A assert_eq!(pixels.len() % 4, 0); @@ -75,3 +76,54 @@ fn to_url(url: &str) -> Option { } } } + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum ShortcutModifier { + Shift, + Ctrl, + Alt, +} + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum KeyType { + Key(iced::keyboard::Key), + Modifier(ShortcutModifier), +} +/// Configures Widget Keyboard Shortcuts +pub type Shortcuts = Vec; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum Shortcut { + GoBackward(Vec), + GoForward(Vec), + Refresh(Vec), + GoHome(Vec), + CloseTab(Vec), + CreateTab(Vec), + ShowOverlay(Vec), + HideOverlay(Vec), +} + +fn check_shortcut(shortcut: &Vec, key: &Key, modifiers: &Modifiers) -> bool { + shortcut + .iter() + .map(|s| match s { + KeyType::Key(s_key) => { + if let iced::keyboard::Key::Character(s_char) = s_key { + if let iced::keyboard::Key::Character(key_char) = key { + key_char == s_char + } else { + false + } + } else { + false + } + } + KeyType::Modifier(s_mod) => match s_mod { + ShortcutModifier::Shift => modifiers.shift(), + ShortcutModifier::Ctrl => modifiers.control(), + ShortcutModifier::Alt => modifiers.alt(), + }, + }) + .all(|s| s == true) +} diff --git a/src/widgets/browser_view.rs b/src/widgets/browser_view.rs index 9271171..e50f421 100644 --- a/src/widgets/browser_view.rs +++ b/src/widgets/browser_view.rs @@ -111,18 +111,15 @@ where match event { Event::Keyboard(event) => { shell.publish((self.keyboard_event)(event)); - Status::Captured } Event::Mouse(event) => { if let Some(point) = cursor.position_in(layout.bounds()) { shell.publish((self.mouse_event)(point, event)); - Status::Captured - } else { - Status::Ignored } } - _ => Status::Ignored, + _ => (), } + Status::Ignored } } diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index 9df6c62..c6821fa 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -1,5 +1,6 @@ use iced::keyboard::{self, key}; -use iced::{event::Event, mouse, widget::column, Element, Point, Size}; +use iced::widget::{self, column}; +use iced::{event::Event, mouse, Element, Point, Size, Task}; use iced_on_focus_widget::hoverable; use url::Url; @@ -12,7 +13,10 @@ pub use nav_bar::nav_bar; mod tab_bar; pub use tab_bar::tab_bar; -use crate::{engines::BrowserEngine, to_url, ImageInfo}; +mod overlay; +pub use overlay::overlay; + +use crate::{check_shortcut, engines::BrowserEngine, to_url, ImageInfo, Shortcuts}; #[derive(Debug, Clone)] pub enum Message { @@ -23,6 +27,7 @@ pub enum Message { GoToUrl(String), ChangeTab(TabSelectionType), CloseTab(TabSelectionType), + CloseCurrentTab, CreateTab, UrlChanged(String), UpdateUrl, @@ -45,9 +50,10 @@ pub struct BrowserWidget { engine: Option, home: Url, url: String, - tab_bar: bool, - nav_bar: bool, - browser_view: bool, + with_tab_bar: bool, + with_nav_bar: bool, + show_overlay: bool, + shortcuts: Shortcuts, view_size: Size, } @@ -61,9 +67,10 @@ where engine: None, home, url: String::new(), - tab_bar: false, - nav_bar: false, - browser_view: false, + with_tab_bar: false, + with_nav_bar: false, + show_overlay: false, + shortcuts: Shortcuts::default(), view_size: Size::new(800, 800), } } @@ -101,17 +108,18 @@ where } pub fn with_tab_bar(mut self) -> Self { - self.tab_bar = true; + self.with_tab_bar = true; self } pub fn with_nav_bar(mut self) -> Self { - self.nav_bar = true; + self.with_nav_bar = true; self } - pub fn with_browsesr_view(mut self) -> Self { - self.browser_view = true; + pub fn with_custom_shortcuts(mut self, shortcuts: Shortcuts) -> Self { + self.shortcuts = shortcuts; + // TODO: Check that shortcuts dont have duplicates self } @@ -135,19 +143,73 @@ where .expect("Browser was created without a backend engine!") } - pub fn update(&mut self, message: Message) { + fn update_engine(&mut self) { self.engine().do_work(); + if self.engine().has_loaded() { + if self.engine().need_render() { + let (format, image_data) = self.engine_mut().pixel_buffer(); + let view = ImageInfo::new( + image_data, + format, + self.view_size.width, + self.view_size.height, + ); + self.engine_mut() + .get_tabs_mut() + .get_current_mut() + .set_view(view) + } + } else { + let view = ImageInfo { + width: self.view_size.width, + height: self.view_size.height, + ..Default::default() + }; + self.engine_mut() + .get_tabs_mut() + .get_current_mut() + .set_view(view) + } + } + + /// This is used to periodically update browserview + pub fn force_update(&mut self) -> Task { + self.engine().do_work(); + let (format, image_data) = self.engine_mut().pixel_buffer(); + let view = ImageInfo::new( + image_data, + format, + self.view_size.width, + self.view_size.height, + ); + self.engine_mut() + .get_tabs_mut() + .get_current_mut() + .set_view(view); + + Task::none() + } + + pub fn update(&mut self, message: Message) -> Task { + println!("message: {message:?}"); + let task = match message { + Message::Event(Event::Keyboard(key)) => { + println!("key: {:?}", key); + proccess_keys(key, &self.shortcuts, self.show_overlay) + } - match message { Message::UpdateViewSize(size) => { self.view_size = size; self.engine_mut().resize(size); + Task::none() } Message::SendKeyboardEvent(event) => { self.engine().handle_keyboard_event(event); + Task::none() } Message::SendMouseEvent(point, event) => { self.engine_mut().handle_mouse_event(point, event); + Task::none() } Message::ChangeTab(index_type) => { let id = match index_type { @@ -158,11 +220,15 @@ where }; self.engine_mut().get_tabs_mut().set_current_id(id); self.url = self.engine().get_tabs().get_current().url(); + Task::none() } + Message::CloseCurrentTab => Task::done(Message::CloseTab(TabSelectionType::Id( + self.engine().get_tabs().get_current_id(), + ))), Message::CloseTab(index_type) => { - // ensure there is still a tab + // ensure there is always at least one tab if self.engine().get_tabs().tabs().len() == 1 { - self.update(Message::CreateTab) + self.update(Message::CreateTab); } let id = match index_type { @@ -173,6 +239,7 @@ where }; self.engine_mut().get_tabs_mut().remove(id); self.url = self.engine().get_tabs().get_current().url(); + Task::none() } Message::CreateTab => { self.url = self.home.to_string(); @@ -187,91 +254,148 @@ where self.engine_mut().force_need_render(); self.engine_mut().resize(bounds); self.engine().goto_url(&home); + Task::none() } Message::GoBackward => { self.engine().go_back(); self.url = self.engine().get_tabs().get_current().url(); + Task::none() } Message::GoForward => { self.engine().go_forward(); self.url = self.engine().get_tabs().get_current().url(); + Task::none() + } + Message::Refresh => { + self.engine().refresh(); + Task::none() } - Message::Refresh => self.engine().refresh(), Message::GoHome => { self.engine().goto_url(&self.home); + Task::none() } Message::GoToUrl(url) => { self.engine().goto_url(&to_url(&url).unwrap()); + Task::none() } Message::UpdateUrl => { self.url = self.engine().get_tabs().get_current().url(); + Task::none() + } + Message::UrlChanged(url) => { + self.url = url; + Task::none() } - Message::UrlChanged(url) => self.url = url, Message::ShowOverlay => { - // self.show_modal = true; - // widget::focus_next() + self.show_overlay = true; + widget::focus_next() } Message::HideOverlay => { - // self.hide_modal(); + self.show_overlay = false; + Task::none() } + _ => Task::none(), + }; - Message::Event(event) => match event { - Event::Keyboard(keyboard::Event::KeyPressed { - key: keyboard::Key::Named(key::Named::Escape), - .. - }) => { - // self.hide_modal(); - } - _ => (), - }, - } + self.update_engine(); - if self.engine().has_loaded() { - if self.engine().need_render() { - let (format, image_data) = self.engine_mut().pixel_buffer(); - let view = ImageInfo::new( - image_data, - format, - self.view_size.width, - self.view_size.height, - ); - self.engine_mut() - .get_tabs_mut() - .get_current_mut() - .set_view(view) - } - } else { - let view = ImageInfo { - width: self.view_size.width, - height: self.view_size.height, - ..Default::default() - }; - self.engine_mut() - .get_tabs_mut() - .get_current_mut() - .set_view(view) - } + task } pub fn view(&self) -> Element { let mut column = column![]; - if self.tab_bar { + if self.with_tab_bar { column = column.push(tab_bar(self.engine().get_tabs())) } - if self.nav_bar { - column = column.push(hoverable(nav_bar(&self.url)).on_unfocus(Message::UpdateUrl)) + if self.with_nav_bar { + column = column.push(hoverable(nav_bar(&self.url)).on_focus_change(Message::UpdateUrl)) } - if self.browser_view { - column = column.push(browser_view( - self.view_size, - self.engine().get_tabs().get_current().get_view(), - Box::new(Message::UpdateViewSize), - Box::new(Message::SendKeyboardEvent), - Box::new(Message::SendMouseEvent), - )) + + let browser_view = browser_view( + self.view_size, + self.engine().get_tabs().get_current().get_view(), + Box::new(Message::UpdateViewSize), + Box::new(Message::SendKeyboardEvent), + Box::new(Message::SendMouseEvent), + ); + if self.show_overlay { + column = column.push(overlay(browser_view, Message::HideOverlay)) + } else { + column = column.push(browser_view); } column.into() } } + +fn proccess_keys( + key: iced::keyboard::Event, + shortcuts: &Shortcuts, + show_overlay: bool, +) -> Task { + println!("pressed key: {:?}", key); + if let iced::keyboard::Event::KeyPressed { + key, + modified_key: _, + physical_key: _, + location: _, + modifiers, + text: _, + } = key + { + println!("key: {:?}", key); + println!("mod: {:?}", modifiers); + let mut tasks = Vec::new(); + for shortcut in shortcuts.iter() { + match shortcut { + crate::Shortcut::GoBackward(keys) => { + if check_shortcut(keys, &key, &modifiers) { + tasks.push(Task::done(Message::GoBackward)) + } + } + crate::Shortcut::GoForward(keys) => { + if check_shortcut(keys, &key, &modifiers) { + tasks.push(Task::done(Message::GoForward)) + } + } + crate::Shortcut::Refresh(keys) => { + if check_shortcut(keys, &key, &modifiers) { + tasks.push(Task::done(Message::Refresh)) + } + } + crate::Shortcut::GoHome(keys) => { + if check_shortcut(keys, &key, &modifiers) { + tasks.push(Task::done(Message::GoHome)) + } + } + crate::Shortcut::CloseTab(keys) => { + if check_shortcut(keys, &key, &modifiers) { + tasks.push(Task::done(Message::CloseCurrentTab)) + } + } + crate::Shortcut::CreateTab(keys) => { + if check_shortcut(keys, &key, &modifiers) { + tasks.push(Task::done(Message::CreateTab)) + } + } + crate::Shortcut::ShowOverlay(keys) => { + if check_shortcut(keys, &key, &modifiers) { + tasks.push(Task::done(Message::ShowOverlay)) + } + } + crate::Shortcut::HideOverlay(keys) => { + if check_shortcut(keys, &key, &modifiers) { + tasks.push(Task::done(Message::HideOverlay)) + } + } + } + } + if key == keyboard::Key::Named(key::Named::Escape) && show_overlay { + tasks.push(Task::done(Message::HideOverlay)); + } + Task::batch(tasks) + } else { + Task::none() + } +} diff --git a/src/widgets/overlay.rs b/src/widgets/overlay.rs new file mode 100644 index 0000000..77ee7b4 --- /dev/null +++ b/src/widgets/overlay.rs @@ -0,0 +1,32 @@ +use iced::widget::{center, container, mouse_area, opaque, stack, text}; +use iced::{Color, Element}; + +pub fn overlay<'a, Message>( + base: impl Into>, + click_on_blur: Message, +) -> Element<'a, Message> +where + Message: Clone + 'a, +{ + let content = text("Overlay content here"); + + stack![ + base.into(), + opaque( + mouse_area(center(opaque(content)).style(|_theme| { + container::Style { + background: Some( + Color { + a: 0.8, + ..Color::BLACK + } + .into(), + ), + ..container::Style::default() + } + })) + .on_press(click_on_blur) + ) + ] + .into() +} From 0095efd106958051fe0f4504eb0986602ffa082d Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Fri, 20 Sep 2024 14:06:49 -0600 Subject: [PATCH 02/15] get shortcut builder working --- Cargo.lock | 40 +++++------ examples/basic_browser.rs | 10 +-- examples/keyboard_driven.rs | 83 ++++++++++++++++++++++ src/lib.rs | 85 +++++++++++++++++++--- src/widgets/browser_view.rs | 43 +++++++---- src/widgets/mod.rs | 137 +++++++++++++++--------------------- src/widgets/overlay.rs | 2 +- 7 files changed, 267 insertions(+), 133 deletions(-) create mode 100644 examples/keyboard_driven.rs diff --git a/Cargo.lock b/Cargo.lock index 7ebab81..ebaca28 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1736,9 +1736,9 @@ dependencies = [ [[package]] name = "iced" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44ccdb0d1a25ff7581e75991229857c353c87d79c199d375af37d264f6fbd06d" +checksum = "88acfabc84ec077eaf9ede3457ffa3a104626d79022a9bf7f296093b1d60c73f" dependencies = [ "iced_core", "iced_futures", @@ -1766,9 +1766,9 @@ dependencies = [ [[package]] name = "iced_core" -version = "0.13.0" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce264c157ad3968928d93f9b244ad8ad63465b5a5c31c86c13199b333629f16f" +checksum = "0013a238275494641bf8f1732a23a808196540dc67b22ff97099c044ae4c8a1c" dependencies = [ "bitflags 2.6.0", "bytes", @@ -1786,9 +1786,9 @@ dependencies = [ [[package]] name = "iced_futures" -version = "0.13.0" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b47bd5c48706c57004c8a2d4cb127cb4535600843edb13aed10b09c7cd55eda4" +checksum = "0c04a6745ba2e80f32cf01e034fd00d853aa4f4cd8b91888099cb7aaee0d5d7c" dependencies = [ "futures", "iced_core", @@ -1859,9 +1859,9 @@ dependencies = [ [[package]] name = "iced_runtime" -version = "0.13.0" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f72474ab379b1c53f4ec5e468c66f8e307f8db13c865c2714d2c4a4a5b38c9a1" +checksum = "348b5b2c61c934d88ca3b0ed1ed913291e923d086a66fa288ce9669da9ef62b5" dependencies = [ "bytes", "iced_core", @@ -1909,9 +1909,9 @@ dependencies = [ [[package]] name = "iced_widget" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e88dd57d414cc44427c523534b80e52a42b6828f0e27ad7b8478f839865ee3c" +checksum = "23eef161bcd216f3472916570919dc3542647285da9cc81085b8d5cb434dc870" dependencies = [ "iced_renderer", "iced_runtime", @@ -3153,9 +3153,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.36.1" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96a05e2e8efddfa51a84ca47cec303fac86c8541b686d37cac5efc0e094417bc" +checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe" dependencies = [ "memchr", ] @@ -4260,9 +4260,9 @@ checksum = "52ea75f83c0137a9b98608359a5f1af8144876eb67bcb1ce837368e906a9f524" [[package]] name = "unicode-script" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8d71f5726e5f285a935e9fe8edfd53f0491eb6e9a5774097fdabee7cd8c9cd" +checksum = "9fb421b350c9aff471779e262955939f565ec18b86c15364e6bdf0d662ca7c1f" [[package]] name = "unicode-segmentation" @@ -4272,15 +4272,15 @@ checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-width" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "unicode-xid" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "untrusted" @@ -4565,9 +4565,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.26.5" +version = "0.26.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bd24728e5af82c6c4ec1b66ac4844bdf8156257fccda846ec58b42cd0cdbe6a" +checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" dependencies = [ "rustls-pki-types", ] diff --git a/examples/basic_browser.rs b/examples/basic_browser.rs index 76d76a4..d1fa436 100644 --- a/examples/basic_browser.rs +++ b/examples/basic_browser.rs @@ -3,10 +3,9 @@ use iced::Theme; use iced::{Element, Settings, Subscription, Task}; use iced_aw::BOOTSTRAP_FONT_BYTES; -use smol_str::SmolStr; use std::time::Duration; -use icy_browser::{widgets, BrowserWidget, KeyType, Shortcut, Ultralight}; +use icy_browser::{widgets, BrowserWidget, Ultralight}; fn main() -> iced::Result { // This imports `icons` for widgets @@ -25,7 +24,7 @@ fn main() -> iced::Result { #[derive(Debug, Clone)] pub enum Message { - BrowserWidget(widgets::Message), + BrowserWidget(widgets::Message), // Passes messagees to Browser widgets Update, } @@ -35,11 +34,8 @@ struct Browser { impl Default for Browser { fn default() -> Self { - let shortcuts = vec![Shortcut::ShowOverlay(vec![KeyType::Key( - iced::keyboard::Key::Character(SmolStr::from("e")), - )])]; + // Customize the look and feel of the browser here let widgets = BrowserWidget::new_with_ultralight() - .with_custom_shortcuts(shortcuts) .with_tab_bar() .with_nav_bar() .build(); diff --git a/examples/keyboard_driven.rs b/examples/keyboard_driven.rs new file mode 100644 index 0000000..9059445 --- /dev/null +++ b/examples/keyboard_driven.rs @@ -0,0 +1,83 @@ +// Simple keybaord driven browser using the ultralight(webkit) webengine as a backend + +use iced::event::{self, Event}; +use iced::Theme; +use iced::{Element, Settings, Subscription, Task}; +use iced_aw::BOOTSTRAP_FONT_BYTES; +use std::time::Duration; + +use icy_browser::{ + widgets, BrowserWidget, KeyType, ShortcutBuilder, ShortcutModifier, ShortcutType, Ultralight, +}; + +fn main() -> iced::Result { + // This imports `icons` for widgets + let bootstrap_font = BOOTSTRAP_FONT_BYTES.into(); + let settings = Settings { + fonts: vec![bootstrap_font], + ..Default::default() + }; + + iced::application("Keyboard Driven Browser", Browser::update, Browser::view) + .subscription(Browser::subscription) + .settings(settings) + .theme(|_| Theme::Dark) + .run() +} + +#[derive(Debug, Clone)] +pub enum Message { + BrowserWidget(widgets::Message), // Passes messagees to Browser widgets + Update, + Event(Event), +} + +struct Browser { + widgets: BrowserWidget, +} + +impl Default for Browser { + fn default() -> Self { + let shortcuts = ShortcutBuilder::new() + .add_shortcut( + ShortcutType::ToggleOverlay, + vec![ + KeyType::Modifier(ShortcutModifier::Alt), + KeyType::Key(iced::keyboard::Key::Character("e".into())), + ], + ) + .build(); + let widgets = BrowserWidget::new_with_ultralight() + .with_custom_shortcuts(shortcuts) + .with_tab_bar() + .with_nav_bar() + .build(); + + Self { widgets } + } +} + +impl Browser { + fn update(&mut self, message: Message) -> Task { + match message { + Message::BrowserWidget(msg) => self.widgets.update(msg).map(Message::BrowserWidget), + Message::Update => self.widgets.force_update().map(Message::BrowserWidget), + Message::Event(event) => self + .widgets + .update(widgets::Message::Event(event)) + .map(Message::BrowserWidget), + } + } + + fn view(&self) -> Element { + self.widgets.view().map(Message::BrowserWidget) + } + + fn subscription(&self) -> Subscription { + Subscription::batch([ + iced::time::every(Duration::from_millis(10)).map(move |_| Message::Update), + // This is needed for child widgets such as overlay to detect Key events + event::listen().map(Message::Event), + ]) + } +} diff --git a/src/lib.rs b/src/lib.rs index 6d1604d..3090b76 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -77,6 +77,66 @@ fn to_url(url: &str) -> Option { } } +pub struct ShortcutBuilder(Shortcuts); +impl ShortcutBuilder { + pub fn new() -> Self { + ShortcutBuilder(Vec::new()) + } + + pub fn add_shortcut( + mut self, + shortcut_action: ShortcutType, + shortcut_keys: Vec, + ) -> Self { + if self.0.iter().filter(|sc| sc.0 == shortcut_action).count() != 0 { + panic!("Tried to add a duplicated shortcut"); + } + + // Must have 1 char key + if shortcut_keys + .iter() + .map(|item| { + if let KeyType::Key(_) = item { + return true; + } else if let KeyType::Modifier(_) = item { + return false; + } + unreachable!() + }) + .filter(|item| *item == true) + .count() + != 1 + { + panic!("Shortcuts MUST have ONLY one Charecter key") + } + + // Must have at least one modifier key + if shortcut_keys + .iter() + .map(|item| { + if let KeyType::Key(_) = item { + return false; + } else if let KeyType::Modifier(_) = item { + return true; + } + unreachable!() + }) + .filter(|item| *item == true) + .count() + < 1 + { + panic!("Shortcuts MUST have at least 1 Modifier key") + } + + self.0.push((shortcut_action, shortcut_keys)); + self + } + + pub fn build(self) -> Shortcuts { + self.0 + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum ShortcutModifier { Shift, @@ -89,23 +149,28 @@ pub enum KeyType { Key(iced::keyboard::Key), Modifier(ShortcutModifier), } +/// Configures Widget Keyboard Shortcut +pub type Shortcut = (ShortcutType, Vec); + /// Configures Widget Keyboard Shortcuts pub type Shortcuts = Vec; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub enum Shortcut { - GoBackward(Vec), - GoForward(Vec), - Refresh(Vec), - GoHome(Vec), - CloseTab(Vec), - CreateTab(Vec), - ShowOverlay(Vec), - HideOverlay(Vec), +pub enum ShortcutType { + GoBackward, + GoForward, + Refresh, + GoHome, + CloseCurrentTab, + CreateTab, + ToggleOverlay, + ShowOverlay, + HideOverlay, } -fn check_shortcut(shortcut: &Vec, key: &Key, modifiers: &Modifiers) -> bool { +fn check_shortcut(shortcut: &Shortcut, key: &Key, modifiers: &Modifiers) -> bool { shortcut + .1 .iter() .map(|s| match s { KeyType::Key(s_key) => { diff --git a/src/widgets/browser_view.rs b/src/widgets/browser_view.rs index e50f421..59a5ed0 100644 --- a/src/widgets/browser_view.rs +++ b/src/widgets/browser_view.rs @@ -18,8 +18,16 @@ pub fn browser_view( send_bounds: Box) -> Message>, keyboard_event: Box Message>, mouse_event: Box Message>, + can_type: bool, ) -> BrowserView { - BrowserView::new(bounds, image, send_bounds, keyboard_event, mouse_event) + BrowserView::new( + bounds, + image, + send_bounds, + keyboard_event, + mouse_event, + can_type, + ) } pub struct BrowserView { @@ -28,6 +36,7 @@ pub struct BrowserView { send_bounds: Box) -> Message>, keyboard_event: Box Message>, mouse_event: Box Message>, + can_interact: bool, // wheather or not to allow typing - useful when overlay enabled } impl BrowserView { @@ -37,6 +46,7 @@ impl BrowserView { send_bounds: Box) -> Message>, keyboard_event: Box Message>, mouse_event: Box Message>, + can_type: bool, ) -> Self { Self { bounds, @@ -44,6 +54,7 @@ impl BrowserView { send_bounds, keyboard_event, mouse_event, + can_interact: can_type, } } } @@ -101,23 +112,25 @@ where shell: &mut Shell<'_, Message>, _viewport: &Rectangle, ) -> event::Status { - // Send updates back if bounds change - // convert to u32 because Image takes u32 - let size = Size::new(layout.bounds().width as u32, layout.bounds().height as u32); - if self.bounds != size { - shell.publish((self.send_bounds)(size)); - } - - match event { - Event::Keyboard(event) => { - shell.publish((self.keyboard_event)(event)); + if self.can_interact { + // Send updates back if bounds change + // convert to u32 because Image takes u32 + let size = Size::new(layout.bounds().width as u32, layout.bounds().height as u32); + if self.bounds != size { + shell.publish((self.send_bounds)(size)); } - Event::Mouse(event) => { - if let Some(point) = cursor.position_in(layout.bounds()) { - shell.publish((self.mouse_event)(point, event)); + + match event { + Event::Keyboard(event) => { + shell.publish((self.keyboard_event)(event)); + } + Event::Mouse(event) => { + if let Some(point) = cursor.position_in(layout.bounds()) { + shell.publish((self.mouse_event)(point, event)); + } } + _ => (), } - _ => (), } Status::Ignored } diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index c6821fa..5d77d4f 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -119,7 +119,6 @@ where pub fn with_custom_shortcuts(mut self, shortcuts: Shortcuts) -> Self { self.shortcuts = shortcuts; - // TODO: Check that shortcuts dont have duplicates self } @@ -127,7 +126,7 @@ where assert!(self.engine.is_some()); let mut build = Self { ..self }; - build.update(Message::CreateTab); + let _ = build.update(Message::CreateTab); // disregaurd task::none() for update build } @@ -191,13 +190,7 @@ where } pub fn update(&mut self, message: Message) -> Task { - println!("message: {message:?}"); let task = match message { - Message::Event(Event::Keyboard(key)) => { - println!("key: {:?}", key); - proccess_keys(key, &self.shortcuts, self.show_overlay) - } - Message::UpdateViewSize(size) => { self.view_size = size; self.engine_mut().resize(size); @@ -228,7 +221,7 @@ where Message::CloseTab(index_type) => { // ensure there is always at least one tab if self.engine().get_tabs().tabs().len() == 1 { - self.update(Message::CreateTab); + let _ = self.update(Message::CreateTab); // ignore task } let id = match index_type { @@ -294,6 +287,60 @@ where self.show_overlay = false; Task::none() } + Message::Event(Event::Keyboard(key)) => { + if let iced::keyboard::Event::KeyPressed { + key, + modified_key: _, + physical_key: _, + location: _, + modifiers, + text: _, + } = key + { + // Default behaviors + if key == keyboard::Key::Named(key::Named::Escape) && self.show_overlay { + return Task::done(Message::HideOverlay); + } + + // Shortcut (Customizable) behaviors + for shortcut in self.shortcuts.iter() { + if check_shortcut(shortcut, &key, &modifiers) { + match shortcut.0 { + crate::ShortcutType::GoBackward => { + return Task::done(Message::GoBackward) + } + crate::ShortcutType::GoForward => { + return Task::done(Message::GoForward) + } + crate::ShortcutType::Refresh => { + return Task::done(Message::Refresh) + } + crate::ShortcutType::GoHome => return Task::done(Message::GoHome), + crate::ShortcutType::CloseCurrentTab => { + return Task::done(Message::CloseCurrentTab) + } + crate::ShortcutType::CreateTab => { + return Task::done(Message::CreateTab) + } + crate::ShortcutType::ToggleOverlay => { + if self.show_overlay { + return Task::done(Message::HideOverlay); + } else { + return Task::done(Message::ShowOverlay); + } + } + crate::ShortcutType::ShowOverlay => { + return Task::done(Message::ShowOverlay) + } + crate::ShortcutType::HideOverlay => { + return Task::done(Message::HideOverlay) + } + } + } + } + } + Task::none() + } _ => Task::none(), }; @@ -318,6 +365,7 @@ where Box::new(Message::UpdateViewSize), Box::new(Message::SendKeyboardEvent), Box::new(Message::SendMouseEvent), + !self.show_overlay, ); if self.show_overlay { column = column.push(overlay(browser_view, Message::HideOverlay)) @@ -328,74 +376,3 @@ where column.into() } } - -fn proccess_keys( - key: iced::keyboard::Event, - shortcuts: &Shortcuts, - show_overlay: bool, -) -> Task { - println!("pressed key: {:?}", key); - if let iced::keyboard::Event::KeyPressed { - key, - modified_key: _, - physical_key: _, - location: _, - modifiers, - text: _, - } = key - { - println!("key: {:?}", key); - println!("mod: {:?}", modifiers); - let mut tasks = Vec::new(); - for shortcut in shortcuts.iter() { - match shortcut { - crate::Shortcut::GoBackward(keys) => { - if check_shortcut(keys, &key, &modifiers) { - tasks.push(Task::done(Message::GoBackward)) - } - } - crate::Shortcut::GoForward(keys) => { - if check_shortcut(keys, &key, &modifiers) { - tasks.push(Task::done(Message::GoForward)) - } - } - crate::Shortcut::Refresh(keys) => { - if check_shortcut(keys, &key, &modifiers) { - tasks.push(Task::done(Message::Refresh)) - } - } - crate::Shortcut::GoHome(keys) => { - if check_shortcut(keys, &key, &modifiers) { - tasks.push(Task::done(Message::GoHome)) - } - } - crate::Shortcut::CloseTab(keys) => { - if check_shortcut(keys, &key, &modifiers) { - tasks.push(Task::done(Message::CloseCurrentTab)) - } - } - crate::Shortcut::CreateTab(keys) => { - if check_shortcut(keys, &key, &modifiers) { - tasks.push(Task::done(Message::CreateTab)) - } - } - crate::Shortcut::ShowOverlay(keys) => { - if check_shortcut(keys, &key, &modifiers) { - tasks.push(Task::done(Message::ShowOverlay)) - } - } - crate::Shortcut::HideOverlay(keys) => { - if check_shortcut(keys, &key, &modifiers) { - tasks.push(Task::done(Message::HideOverlay)) - } - } - } - } - if key == keyboard::Key::Named(key::Named::Escape) && show_overlay { - tasks.push(Task::done(Message::HideOverlay)); - } - Task::batch(tasks) - } else { - Task::none() - } -} diff --git a/src/widgets/overlay.rs b/src/widgets/overlay.rs index 77ee7b4..18d6e5c 100644 --- a/src/widgets/overlay.rs +++ b/src/widgets/overlay.rs @@ -8,7 +8,7 @@ pub fn overlay<'a, Message>( where Message: Clone + 'a, { - let content = text("Overlay content here"); + let content = text("Overlay content here").center(); stack![ base.into(), From 14adcb7fdd6138389827e63f4fbbed721a486fdf Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Fri, 20 Sep 2024 15:03:02 -0600 Subject: [PATCH 03/15] working on creating custom command window --- src/widgets/overlay.rs | 91 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) diff --git a/src/widgets/overlay.rs b/src/widgets/overlay.rs index 18d6e5c..2b7b026 100644 --- a/src/widgets/overlay.rs +++ b/src/widgets/overlay.rs @@ -8,7 +8,8 @@ pub fn overlay<'a, Message>( where Message: Clone + 'a, { - let content = text("Overlay content here").center(); + let content = text("fsdfsdf").center(); + // let content = command_window::CommandWindow::new(); stack![ base.into(), @@ -30,3 +31,91 @@ where ] .into() } + +pub mod command_window { + use iced::advanced::{layout, renderer::Style, widget::Tree, Layout, Widget}; + use iced::widget::canvas::{Cache, Path, Stroke}; + use iced::{mouse, Color, Element, Length, Point, Rectangle, Renderer, Size, Theme, Vector}; + + #[derive(Clone)] + enum Message {} + + pub struct CommandWindow { + cache: Cache, + } + + impl CommandWindow { + pub fn new() -> Self { + Self { + cache: Cache::new(), + } + } + } + + impl Widget for CommandWindow { + fn size(&self) -> Size { + Size { + width: Length::Fill, + height: Length::Fill, + } + } + + fn layout( + &self, + _tree: &mut Tree, + _renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + layout::Node::new(Size::new( + limits.max().width / 60., + limits.max().height / 60., + )) + } + + fn draw( + &self, + _tree: &Tree, + renderer: &mut Renderer, + _theme: &Theme, + _style: &Style, + layout: Layout<'_>, + _cursor: mouse::Cursor, + _viewport: &Rectangle, + ) { + self.cache.draw(renderer, layout.bounds().size(), |frame| { + frame.fill_rectangle( + Point::ORIGIN, + layout.bounds().size(), + Color::from_rgb(0.0, 0.2, 0.4), + ); + + frame.fill( + &Path::circle(frame.center(), frame.width().min(frame.height()) / 4.0), + Color::from_rgb(0.6, 0.8, 1.0), + ); + + frame.stroke( + &Path::line( + frame.center() + Vector::new(-250.0, 100.0), + frame.center() + Vector::new(250.0, -100.0), + ), + Stroke { + style: Color::WHITE.into(), + width: 50.0, + ..Default::default() + }, + ); + }); + } + } + + impl From + for Element<'_, Message, Theme, Renderer> + where + CommandWindow: Widget, + { + fn from(widget: CommandWindow) -> Self { + Self::new(widget) + } + } +} From 209e60b335bd1d747652578ba7c2c97b199cf144 Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Fri, 20 Sep 2024 15:31:51 -0600 Subject: [PATCH 04/15] overlay can overlay --- src/widgets/command_window.rs | 73 ++++++++++++++++++++ src/widgets/mod.rs | 6 +- src/widgets/overlay.rs | 121 ---------------------------------- 3 files changed, 76 insertions(+), 124 deletions(-) create mode 100644 src/widgets/command_window.rs delete mode 100644 src/widgets/overlay.rs diff --git a/src/widgets/command_window.rs b/src/widgets/command_window.rs new file mode 100644 index 0000000..85cb41c --- /dev/null +++ b/src/widgets/command_window.rs @@ -0,0 +1,73 @@ +use iced::widget::canvas::{Canvas, Frame, Geometry, Path, Program, Stroke}; +use iced::widget::{center, container, mouse_area, opaque, stack}; +use iced::{mouse, Color, Element, Length, Point, Rectangle, Renderer, Theme, Vector}; + +pub fn command_window<'a, Message>( + base: impl Into>, + click_on_blur: Message, +) -> Element<'a, Message> +where + Message: Clone + 'a, +{ + let content = Canvas::new(CommandWindow) + .width(Length::Fill) + .height(Length::Fill); + + stack![ + base.into(), + opaque( + mouse_area(center(opaque(content)).style(|_theme| { + container::Style { + background: Some( + Color { + a: 0.8, + ..Color::BLACK + } + .into(), + ), + ..container::Style::default() + } + })) + .on_press(click_on_blur) + ) + ] + .into() +} + +struct CommandWindow; + +impl Program for CommandWindow { + type State = (); + + fn draw( + &self, + _state: &Self::State, + renderer: &Renderer, + _theme: &Theme, + bounds: Rectangle, + _cursor: mouse::Cursor, + ) -> Vec { + let mut frame = Frame::new(renderer, bounds.size()); + + frame.fill_rectangle(Point::ORIGIN, bounds.size(), Color::from_rgb(0.0, 0.2, 0.4)); + + frame.fill( + &Path::circle(frame.center(), frame.width().min(frame.height()) / 4.0), + Color::from_rgb(0.6, 0.8, 1.0), + ); + + frame.stroke( + &Path::line( + frame.center() + Vector::new(-250.0, 100.0), + frame.center() + Vector::new(250.0, -100.0), + ), + Stroke { + style: Color::WHITE.into(), + width: 50.0, + ..Default::default() + }, + ); + + vec![frame.into_geometry()] + } +} diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index 5d77d4f..fd107a9 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -13,8 +13,8 @@ pub use nav_bar::nav_bar; mod tab_bar; pub use tab_bar::tab_bar; -mod overlay; -pub use overlay::overlay; +mod command_window; +pub use command_window::command_window; use crate::{check_shortcut, engines::BrowserEngine, to_url, ImageInfo, Shortcuts}; @@ -368,7 +368,7 @@ where !self.show_overlay, ); if self.show_overlay { - column = column.push(overlay(browser_view, Message::HideOverlay)) + column = column.push(command_window(browser_view, Message::HideOverlay)) } else { column = column.push(browser_view); } diff --git a/src/widgets/overlay.rs b/src/widgets/overlay.rs deleted file mode 100644 index 2b7b026..0000000 --- a/src/widgets/overlay.rs +++ /dev/null @@ -1,121 +0,0 @@ -use iced::widget::{center, container, mouse_area, opaque, stack, text}; -use iced::{Color, Element}; - -pub fn overlay<'a, Message>( - base: impl Into>, - click_on_blur: Message, -) -> Element<'a, Message> -where - Message: Clone + 'a, -{ - let content = text("fsdfsdf").center(); - // let content = command_window::CommandWindow::new(); - - stack![ - base.into(), - opaque( - mouse_area(center(opaque(content)).style(|_theme| { - container::Style { - background: Some( - Color { - a: 0.8, - ..Color::BLACK - } - .into(), - ), - ..container::Style::default() - } - })) - .on_press(click_on_blur) - ) - ] - .into() -} - -pub mod command_window { - use iced::advanced::{layout, renderer::Style, widget::Tree, Layout, Widget}; - use iced::widget::canvas::{Cache, Path, Stroke}; - use iced::{mouse, Color, Element, Length, Point, Rectangle, Renderer, Size, Theme, Vector}; - - #[derive(Clone)] - enum Message {} - - pub struct CommandWindow { - cache: Cache, - } - - impl CommandWindow { - pub fn new() -> Self { - Self { - cache: Cache::new(), - } - } - } - - impl Widget for CommandWindow { - fn size(&self) -> Size { - Size { - width: Length::Fill, - height: Length::Fill, - } - } - - fn layout( - &self, - _tree: &mut Tree, - _renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - layout::Node::new(Size::new( - limits.max().width / 60., - limits.max().height / 60., - )) - } - - fn draw( - &self, - _tree: &Tree, - renderer: &mut Renderer, - _theme: &Theme, - _style: &Style, - layout: Layout<'_>, - _cursor: mouse::Cursor, - _viewport: &Rectangle, - ) { - self.cache.draw(renderer, layout.bounds().size(), |frame| { - frame.fill_rectangle( - Point::ORIGIN, - layout.bounds().size(), - Color::from_rgb(0.0, 0.2, 0.4), - ); - - frame.fill( - &Path::circle(frame.center(), frame.width().min(frame.height()) / 4.0), - Color::from_rgb(0.6, 0.8, 1.0), - ); - - frame.stroke( - &Path::line( - frame.center() + Vector::new(-250.0, 100.0), - frame.center() + Vector::new(250.0, -100.0), - ), - Stroke { - style: Color::WHITE.into(), - width: 50.0, - ..Default::default() - }, - ); - }); - } - } - - impl From - for Element<'_, Message, Theme, Renderer> - where - CommandWindow: Widget, - { - fn from(widget: CommandWindow) -> Self { - Self::new(widget) - } - } -} From 0991dc94645ef37b9956f1a5f1431bd47c2a3229 Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Fri, 20 Sep 2024 18:01:56 -0600 Subject: [PATCH 05/15] updates to command window --- examples/keyboard_driven.rs | 2 +- src/widgets/command_window.rs | 71 ++++++--------------- src/widgets/mod.rs | 113 +++++++++++++++++++--------------- 3 files changed, 84 insertions(+), 102 deletions(-) diff --git a/examples/keyboard_driven.rs b/examples/keyboard_driven.rs index 9059445..58a490e 100644 --- a/examples/keyboard_driven.rs +++ b/examples/keyboard_driven.rs @@ -42,7 +42,7 @@ impl Default for Browser { .add_shortcut( ShortcutType::ToggleOverlay, vec![ - KeyType::Modifier(ShortcutModifier::Alt), + KeyType::Modifier(ShortcutModifier::Ctrl), KeyType::Key(iced::keyboard::Key::Character("e".into())), ], ) diff --git a/src/widgets/command_window.rs b/src/widgets/command_window.rs index 85cb41c..ccda3bb 100644 --- a/src/widgets/command_window.rs +++ b/src/widgets/command_window.rs @@ -1,22 +1,27 @@ -use iced::widget::canvas::{Canvas, Frame, Geometry, Path, Program, Stroke}; -use iced::widget::{center, container, mouse_area, opaque, stack}; -use iced::{mouse, Color, Element, Length, Point, Rectangle, Renderer, Theme, Vector}; +use iced::widget::{center, column, container, mouse_area, opaque, stack, text_input}; +use iced::{border, Color, Element, Theme}; -pub fn command_window<'a, Message>( +use super::Message; + +pub fn command_window<'a>( base: impl Into>, - click_on_blur: Message, -) -> Element<'a, Message> -where - Message: Clone + 'a, -{ - let content = Canvas::new(CommandWindow) - .width(Length::Fill) - .height(Length::Fill); + query: &str, +) -> Element<'a, Message> { + let window = container(column![ + text_input("Command Menu", query).on_input(Message::QueryChanged), + ]) + .padding(10) + .center(600) + .style(|theme: &Theme| container::Style { + background: Some(theme.palette().background.into()), + border: border::rounded(10), + ..container::Style::default() + }); stack![ base.into(), opaque( - mouse_area(center(opaque(content)).style(|_theme| { + mouse_area(center(opaque(window)).style(|_theme| { container::Style { background: Some( Color { @@ -28,46 +33,8 @@ where ..container::Style::default() } })) - .on_press(click_on_blur) + .on_press(Message::HideOverlay) ) ] .into() } - -struct CommandWindow; - -impl Program for CommandWindow { - type State = (); - - fn draw( - &self, - _state: &Self::State, - renderer: &Renderer, - _theme: &Theme, - bounds: Rectangle, - _cursor: mouse::Cursor, - ) -> Vec { - let mut frame = Frame::new(renderer, bounds.size()); - - frame.fill_rectangle(Point::ORIGIN, bounds.size(), Color::from_rgb(0.0, 0.2, 0.4)); - - frame.fill( - &Path::circle(frame.center(), frame.width().min(frame.height()) / 4.0), - Color::from_rgb(0.6, 0.8, 1.0), - ); - - frame.stroke( - &Path::line( - frame.center() + Vector::new(-250.0, 100.0), - frame.center() + Vector::new(250.0, -100.0), - ), - Stroke { - style: Color::WHITE.into(), - width: 50.0, - ..Default::default() - }, - ); - - vec![frame.into_geometry()] - } -} diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index fd107a9..30f867e 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -31,6 +31,7 @@ pub enum Message { CreateTab, UrlChanged(String), UpdateUrl, + QueryChanged(String), SendKeyboardEvent(keyboard::Event), SendMouseEvent(Point, mouse::Event), UpdateViewSize(Size), @@ -49,7 +50,8 @@ pub enum TabSelectionType { pub struct BrowserWidget { engine: Option, home: Url, - url: String, + url: String, // State of url bar + query: String, // State of Command window with_tab_bar: bool, with_nav_bar: bool, show_overlay: bool, @@ -67,6 +69,7 @@ where engine: None, home, url: String::new(), + query: String::new(), with_tab_bar: false, with_nav_bar: false, show_overlay: false, @@ -279,6 +282,10 @@ where self.url = url; Task::none() } + Message::QueryChanged(query) => { + self.query = query; + Task::none() + } Message::ShowOverlay => { self.show_overlay = true; widget::focus_next() @@ -287,61 +294,69 @@ where self.show_overlay = false; Task::none() } - Message::Event(Event::Keyboard(key)) => { - if let iced::keyboard::Event::KeyPressed { - key, - modified_key: _, - physical_key: _, - location: _, - modifiers, - text: _, - } = key - { - // Default behaviors - if key == keyboard::Key::Named(key::Named::Escape) && self.show_overlay { - return Task::done(Message::HideOverlay); - } + Message::Event(event) => { + match event { + Event::Keyboard(key) => { + if let iced::keyboard::Event::KeyPressed { + key, + modified_key: _, + physical_key: _, + location: _, + modifiers, + text: _, + } = key + { + // Default behaviors + if key == keyboard::Key::Named(key::Named::Escape) && self.show_overlay + { + return Task::done(Message::HideOverlay); + } - // Shortcut (Customizable) behaviors - for shortcut in self.shortcuts.iter() { - if check_shortcut(shortcut, &key, &modifiers) { - match shortcut.0 { - crate::ShortcutType::GoBackward => { - return Task::done(Message::GoBackward) - } - crate::ShortcutType::GoForward => { - return Task::done(Message::GoForward) - } - crate::ShortcutType::Refresh => { - return Task::done(Message::Refresh) - } - crate::ShortcutType::GoHome => return Task::done(Message::GoHome), - crate::ShortcutType::CloseCurrentTab => { - return Task::done(Message::CloseCurrentTab) - } - crate::ShortcutType::CreateTab => { - return Task::done(Message::CreateTab) - } - crate::ShortcutType::ToggleOverlay => { - if self.show_overlay { - return Task::done(Message::HideOverlay); - } else { - return Task::done(Message::ShowOverlay); + // Shortcut (Customizable) behaviors + for shortcut in self.shortcuts.iter() { + if check_shortcut(shortcut, &key, &modifiers) { + match shortcut.0 { + crate::ShortcutType::GoBackward => { + return Task::done(Message::GoBackward) + } + crate::ShortcutType::GoForward => { + return Task::done(Message::GoForward) + } + crate::ShortcutType::Refresh => { + return Task::done(Message::Refresh) + } + crate::ShortcutType::GoHome => { + return Task::done(Message::GoHome) + } + crate::ShortcutType::CloseCurrentTab => { + return Task::done(Message::CloseCurrentTab) + } + crate::ShortcutType::CreateTab => { + return Task::done(Message::CreateTab) + } + crate::ShortcutType::ToggleOverlay => { + if self.show_overlay { + return Task::done(Message::HideOverlay); + } else { + return Task::done(Message::ShowOverlay); + } + } + crate::ShortcutType::ShowOverlay => { + return Task::done(Message::ShowOverlay) + } + crate::ShortcutType::HideOverlay => { + return Task::done(Message::HideOverlay) + } } } - crate::ShortcutType::ShowOverlay => { - return Task::done(Message::ShowOverlay) - } - crate::ShortcutType::HideOverlay => { - return Task::done(Message::HideOverlay) - } } } + Task::none() } + // Other unwatched events + _ => Task::none(), } - Task::none() } - _ => Task::none(), }; self.update_engine(); @@ -368,7 +383,7 @@ where !self.show_overlay, ); if self.show_overlay { - column = column.push(command_window(browser_view, Message::HideOverlay)) + column = column.push(command_window(browser_view, &self.query)) } else { column = column.push(browser_view); } From 4d08594b17eec680f9825df4a89f1e92eb95158d Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Fri, 20 Sep 2024 18:16:16 -0600 Subject: [PATCH 06/15] updated browserview to remove uneeded params --- src/widgets/browser_view.rs | 53 ++++++++++--------------------------- src/widgets/mod.rs | 3 --- 2 files changed, 14 insertions(+), 42 deletions(-) diff --git a/src/widgets/browser_view.rs b/src/widgets/browser_view.rs index 59a5ed0..d885220 100644 --- a/src/widgets/browser_view.rs +++ b/src/widgets/browser_view.rs @@ -8,58 +8,32 @@ use iced::advanced::{ }; use iced::event::Status; use iced::widget::image::{Handle, Image}; -use iced::{theme::Theme, Element, Event, Length, Point, Rectangle, Size}; +use iced::{theme::Theme, Element, Event, Length, Rectangle, Size}; +use super::Message; use crate::ImageInfo; -pub fn browser_view( - bounds: Size, - image: &ImageInfo, - send_bounds: Box) -> Message>, - keyboard_event: Box Message>, - mouse_event: Box Message>, - can_type: bool, -) -> BrowserView { - BrowserView::new( - bounds, - image, - send_bounds, - keyboard_event, - mouse_event, - can_type, - ) +pub fn browser_view(bounds: Size, image: &ImageInfo, can_type: bool) -> BrowserView { + BrowserView::new(bounds, image, can_type) } -pub struct BrowserView { +pub struct BrowserView { bounds: Size, image: Image, - send_bounds: Box) -> Message>, - keyboard_event: Box Message>, - mouse_event: Box Message>, can_interact: bool, // wheather or not to allow typing - useful when overlay enabled } -impl BrowserView { - pub fn new( - bounds: Size, - image: &ImageInfo, - send_bounds: Box) -> Message>, - keyboard_event: Box Message>, - mouse_event: Box Message>, - can_type: bool, - ) -> Self { +impl BrowserView { + pub fn new(bounds: Size, image: &ImageInfo, can_type: bool) -> Self { Self { bounds, image: image.as_image(), - send_bounds, - keyboard_event, - mouse_event, can_interact: can_type, } } } -impl Widget for BrowserView +impl Widget for BrowserView where Renderer: iced::advanced::image::Renderer, { @@ -117,16 +91,16 @@ where // convert to u32 because Image takes u32 let size = Size::new(layout.bounds().width as u32, layout.bounds().height as u32); if self.bounds != size { - shell.publish((self.send_bounds)(size)); + shell.publish(Message::UpdateViewSize(size)); } match event { Event::Keyboard(event) => { - shell.publish((self.keyboard_event)(event)); + shell.publish(Message::SendKeyboardEvent(event)); } Event::Mouse(event) => { if let Some(point) = cursor.position_in(layout.bounds()) { - shell.publish((self.mouse_event)(point, event)); + shell.publish(Message::SendMouseEvent(point, event)); } } _ => (), @@ -136,11 +110,12 @@ where } } -impl<'a, Message: 'a, Renderer> From> for Element<'a, Message, Theme, Renderer> +impl<'a, Message: 'a, Renderer> From for Element<'a, Message, Theme, Renderer> where Renderer: advanced::Renderer + advanced::image::Renderer, + BrowserView: Widget, { - fn from(widget: BrowserView) -> Self { + fn from(widget: BrowserView) -> Self { Self::new(widget) } } diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index 30f867e..7968d09 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -377,9 +377,6 @@ where let browser_view = browser_view( self.view_size, self.engine().get_tabs().get_current().get_view(), - Box::new(Message::UpdateViewSize), - Box::new(Message::SendKeyboardEvent), - Box::new(Message::SendMouseEvent), !self.show_overlay, ); if self.show_overlay { From ca1f126dce3b8c79b8edcc4494fe243fc3d44e46 Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Fri, 20 Sep 2024 18:17:21 -0600 Subject: [PATCH 07/15] fix readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3481eca..ab88561 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ### Supported Platforms -| Platform | Support | +| Platform | Support | | Windows | | | Linux | | From 0bd6109ecaa539b8c539c188b9aab0778aeea3ef Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Fri, 20 Sep 2024 18:43:15 -0600 Subject: [PATCH 08/15] move shortcuts to independent file --- src/lib.rs | 120 ++------------------------------------------- src/shortcut.rs | 117 +++++++++++++++++++++++++++++++++++++++++++ src/widgets/mod.rs | 2 +- 3 files changed, 121 insertions(+), 118 deletions(-) create mode 100644 src/shortcut.rs diff --git a/src/lib.rs b/src/lib.rs index 3090b76..2393b2e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,3 @@ -use iced::keyboard::{Key, Modifiers}; use iced::widget::image::{Handle, Image}; use url::{ParseError, Url}; @@ -11,6 +10,9 @@ pub use engines::ultralight::Ultralight; pub mod widgets; pub use widgets::{nav_bar, tab_bar, BrowserWidget}; +mod shortcut; +pub use shortcut::{KeyType, Shortcut, ShortcutBuilder, ShortcutModifier, ShortcutType, Shortcuts}; + // Image details for passing the view around #[derive(Debug, Clone)] pub struct ImageInfo { @@ -76,119 +78,3 @@ fn to_url(url: &str) -> Option { } } } - -pub struct ShortcutBuilder(Shortcuts); -impl ShortcutBuilder { - pub fn new() -> Self { - ShortcutBuilder(Vec::new()) - } - - pub fn add_shortcut( - mut self, - shortcut_action: ShortcutType, - shortcut_keys: Vec, - ) -> Self { - if self.0.iter().filter(|sc| sc.0 == shortcut_action).count() != 0 { - panic!("Tried to add a duplicated shortcut"); - } - - // Must have 1 char key - if shortcut_keys - .iter() - .map(|item| { - if let KeyType::Key(_) = item { - return true; - } else if let KeyType::Modifier(_) = item { - return false; - } - unreachable!() - }) - .filter(|item| *item == true) - .count() - != 1 - { - panic!("Shortcuts MUST have ONLY one Charecter key") - } - - // Must have at least one modifier key - if shortcut_keys - .iter() - .map(|item| { - if let KeyType::Key(_) = item { - return false; - } else if let KeyType::Modifier(_) = item { - return true; - } - unreachable!() - }) - .filter(|item| *item == true) - .count() - < 1 - { - panic!("Shortcuts MUST have at least 1 Modifier key") - } - - self.0.push((shortcut_action, shortcut_keys)); - self - } - - pub fn build(self) -> Shortcuts { - self.0 - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub enum ShortcutModifier { - Shift, - Ctrl, - Alt, -} - -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub enum KeyType { - Key(iced::keyboard::Key), - Modifier(ShortcutModifier), -} -/// Configures Widget Keyboard Shortcut -pub type Shortcut = (ShortcutType, Vec); - -/// Configures Widget Keyboard Shortcuts -pub type Shortcuts = Vec; - -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub enum ShortcutType { - GoBackward, - GoForward, - Refresh, - GoHome, - CloseCurrentTab, - CreateTab, - ToggleOverlay, - ShowOverlay, - HideOverlay, -} - -fn check_shortcut(shortcut: &Shortcut, key: &Key, modifiers: &Modifiers) -> bool { - shortcut - .1 - .iter() - .map(|s| match s { - KeyType::Key(s_key) => { - if let iced::keyboard::Key::Character(s_char) = s_key { - if let iced::keyboard::Key::Character(key_char) = key { - key_char == s_char - } else { - false - } - } else { - false - } - } - KeyType::Modifier(s_mod) => match s_mod { - ShortcutModifier::Shift => modifiers.shift(), - ShortcutModifier::Ctrl => modifiers.control(), - ShortcutModifier::Alt => modifiers.alt(), - }, - }) - .all(|s| s == true) -} diff --git a/src/shortcut.rs b/src/shortcut.rs new file mode 100644 index 0000000..c7d43d4 --- /dev/null +++ b/src/shortcut.rs @@ -0,0 +1,117 @@ +use iced::keyboard::{Key, Modifiers}; + +pub struct ShortcutBuilder(Shortcuts); +impl ShortcutBuilder { + pub fn new() -> Self { + ShortcutBuilder(Vec::new()) + } + + pub fn add_shortcut( + mut self, + shortcut_action: ShortcutType, + shortcut_keys: Vec, + ) -> Self { + if self.0.iter().filter(|sc| sc.0 == shortcut_action).count() != 0 { + panic!("Tried to add a duplicated shortcut"); + } + + // Must have 1 char key + if shortcut_keys + .iter() + .map(|item| { + if let KeyType::Key(_) = item { + return true; + } else if let KeyType::Modifier(_) = item { + return false; + } + unreachable!() + }) + .filter(|item| *item == true) + .count() + != 1 + { + panic!("Shortcuts MUST have ONLY one Charecter key") + } + + // Must have at least one modifier key + if shortcut_keys + .iter() + .map(|item| { + if let KeyType::Key(_) = item { + return false; + } else if let KeyType::Modifier(_) = item { + return true; + } + unreachable!() + }) + .filter(|item| *item == true) + .count() + < 1 + { + panic!("Shortcuts MUST have at least 1 Modifier key") + } + + self.0.push((shortcut_action, shortcut_keys)); + self + } + + pub fn build(self) -> Shortcuts { + self.0 + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum ShortcutModifier { + Shift, + Ctrl, + Alt, +} + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum KeyType { + Key(iced::keyboard::Key), + Modifier(ShortcutModifier), +} +/// Configures Widget Keyboard Shortcut +pub type Shortcut = (ShortcutType, Vec); + +/// Configures Widget Keyboard Shortcuts +pub type Shortcuts = Vec; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum ShortcutType { + GoBackward, + GoForward, + Refresh, + GoHome, + CloseCurrentTab, + CreateTab, + ToggleOverlay, + ShowOverlay, + HideOverlay, +} + +pub fn check_shortcut(shortcut: &Shortcut, key: &Key, modifiers: &Modifiers) -> bool { + shortcut + .1 + .iter() + .map(|s| match s { + KeyType::Key(s_key) => { + if let iced::keyboard::Key::Character(s_char) = s_key { + if let iced::keyboard::Key::Character(key_char) = key { + key_char == s_char + } else { + false + } + } else { + false + } + } + KeyType::Modifier(s_mod) => match s_mod { + ShortcutModifier::Shift => modifiers.shift(), + ShortcutModifier::Ctrl => modifiers.control(), + ShortcutModifier::Alt => modifiers.alt(), + }, + }) + .all(|s| s == true) +} diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index 7968d09..ef450e9 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -16,7 +16,7 @@ pub use tab_bar::tab_bar; mod command_window; pub use command_window::command_window; -use crate::{check_shortcut, engines::BrowserEngine, to_url, ImageInfo, Shortcuts}; +use crate::{engines::BrowserEngine, shortcut::check_shortcut, to_url, ImageInfo, Shortcuts}; #[derive(Debug, Clone)] pub enum Message { From 9c1964ab5087f00c114dd104bcef3c891b30d510 Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Sat, 21 Sep 2024 13:49:16 -0600 Subject: [PATCH 09/15] make all actions unified --- examples/keyboard_driven.rs | 5 ++-- src/lib.rs | 4 ++-- src/shortcut.rs | 23 ++++-------------- src/widgets/mod.rs | 48 ++++++++++--------------------------- 4 files changed, 22 insertions(+), 58 deletions(-) diff --git a/examples/keyboard_driven.rs b/examples/keyboard_driven.rs index 58a490e..6cbabd5 100644 --- a/examples/keyboard_driven.rs +++ b/examples/keyboard_driven.rs @@ -7,7 +7,8 @@ use iced_aw::BOOTSTRAP_FONT_BYTES; use std::time::Duration; use icy_browser::{ - widgets, BrowserWidget, KeyType, ShortcutBuilder, ShortcutModifier, ShortcutType, Ultralight, + widgets, BrowserWidget, KeyType, Message as WidgetMessage, ShortcutBuilder, ShortcutModifier, + Ultralight, }; fn main() -> iced::Result { @@ -40,7 +41,7 @@ impl Default for Browser { fn default() -> Self { let shortcuts = ShortcutBuilder::new() .add_shortcut( - ShortcutType::ToggleOverlay, + WidgetMessage::ToggleOverlay, vec![ KeyType::Modifier(ShortcutModifier::Ctrl), KeyType::Key(iced::keyboard::Key::Character("e".into())), diff --git a/src/lib.rs b/src/lib.rs index 2393b2e..6c47b35 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,10 +8,10 @@ pub use engines::{BrowserEngine, PixelFormat, Tab, TabInfo, Tabs}; pub use engines::ultralight::Ultralight; pub mod widgets; -pub use widgets::{nav_bar, tab_bar, BrowserWidget}; +pub use widgets::{nav_bar, tab_bar, BrowserWidget, Message}; mod shortcut; -pub use shortcut::{KeyType, Shortcut, ShortcutBuilder, ShortcutModifier, ShortcutType, Shortcuts}; +pub use shortcut::{KeyType, Shortcut, ShortcutBuilder, ShortcutModifier, Shortcuts}; // Image details for passing the view around #[derive(Debug, Clone)] diff --git a/src/shortcut.rs b/src/shortcut.rs index c7d43d4..fa4b688 100644 --- a/src/shortcut.rs +++ b/src/shortcut.rs @@ -1,16 +1,14 @@ use iced::keyboard::{Key, Modifiers}; +use super::widgets::Message; + pub struct ShortcutBuilder(Shortcuts); impl ShortcutBuilder { pub fn new() -> Self { ShortcutBuilder(Vec::new()) } - pub fn add_shortcut( - mut self, - shortcut_action: ShortcutType, - shortcut_keys: Vec, - ) -> Self { + pub fn add_shortcut(mut self, shortcut_action: Message, shortcut_keys: Vec) -> Self { if self.0.iter().filter(|sc| sc.0 == shortcut_action).count() != 0 { panic!("Tried to add a duplicated shortcut"); } @@ -73,24 +71,11 @@ pub enum KeyType { Modifier(ShortcutModifier), } /// Configures Widget Keyboard Shortcut -pub type Shortcut = (ShortcutType, Vec); +pub type Shortcut = (Message, Vec); /// Configures Widget Keyboard Shortcuts pub type Shortcuts = Vec; -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub enum ShortcutType { - GoBackward, - GoForward, - Refresh, - GoHome, - CloseCurrentTab, - CreateTab, - ToggleOverlay, - ShowOverlay, - HideOverlay, -} - pub fn check_shortcut(shortcut: &Shortcut, key: &Key, modifiers: &Modifiers) -> bool { shortcut .1 diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index ef450e9..70d1cae 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -18,7 +18,7 @@ pub use command_window::command_window; use crate::{engines::BrowserEngine, shortcut::check_shortcut, to_url, ImageInfo, Shortcuts}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum Message { GoBackward, GoForward, @@ -36,12 +36,13 @@ pub enum Message { SendMouseEvent(Point, mouse::Event), UpdateViewSize(Size), Event(Event), + ToggleOverlay, ShowOverlay, HideOverlay, } /// Allows different widgets to interact in their native way -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum TabSelectionType { Id(u32), Index(usize), @@ -286,6 +287,15 @@ where self.query = query; Task::none() } + Message::ToggleOverlay => { + if self.show_overlay { + self.show_overlay = false; + widget::focus_next() + } else { + self.show_overlay = true; + widget::focus_next() + } + } Message::ShowOverlay => { self.show_overlay = true; widget::focus_next() @@ -315,39 +325,7 @@ where // Shortcut (Customizable) behaviors for shortcut in self.shortcuts.iter() { if check_shortcut(shortcut, &key, &modifiers) { - match shortcut.0 { - crate::ShortcutType::GoBackward => { - return Task::done(Message::GoBackward) - } - crate::ShortcutType::GoForward => { - return Task::done(Message::GoForward) - } - crate::ShortcutType::Refresh => { - return Task::done(Message::Refresh) - } - crate::ShortcutType::GoHome => { - return Task::done(Message::GoHome) - } - crate::ShortcutType::CloseCurrentTab => { - return Task::done(Message::CloseCurrentTab) - } - crate::ShortcutType::CreateTab => { - return Task::done(Message::CreateTab) - } - crate::ShortcutType::ToggleOverlay => { - if self.show_overlay { - return Task::done(Message::HideOverlay); - } else { - return Task::done(Message::ShowOverlay); - } - } - crate::ShortcutType::ShowOverlay => { - return Task::done(Message::ShowOverlay) - } - crate::ShortcutType::HideOverlay => { - return Task::done(Message::HideOverlay) - } - } + return Task::done(shortcut.0.clone()); } } } From 57b403054bc41bd1ebec8b7ca2fec3efac9ef70a Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Sat, 21 Sep 2024 17:21:59 -0600 Subject: [PATCH 10/15] command palatte has results --- Cargo.lock | 46 ++++++++++++++++-- Cargo.toml | 8 +++- examples/keyboard_driven.rs | 2 +- src/widgets/browser_view.rs | 4 +- src/widgets/command_window.rs | 34 +++++++++++-- src/widgets/mod.rs | 89 +++++++++++++++++++++++------------ src/widgets/nav_bar.rs | 14 ++++-- 7 files changed, 153 insertions(+), 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ebaca28..01634ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1574,6 +1574,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.3.9" @@ -1909,9 +1915,9 @@ dependencies = [ [[package]] name = "iced_widget" -version = "0.13.1" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23eef161bcd216f3472916570919dc3542647285da9cc81085b8d5cb434dc870" +checksum = "81429e1b950b0e4bca65be4c4278fea6678ea782030a411778f26fa9f8983e1d" dependencies = [ "iced_renderer", "iced_runtime", @@ -1956,6 +1962,8 @@ dependencies = [ "serde", "serde_json", "smol_str", + "strum", + "strum_macros", "tempfile", "ul-next", "url", @@ -2858,7 +2866,7 @@ version = "0.18.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39b0deead1528fd0e5947a8546a9642a9777c25f6e1e26f34c97b204bbb465bd" dependencies = [ - "heck", + "heck 0.4.1", "itertools 0.12.1", "proc-macro2", "proc-macro2-diagnostics", @@ -3438,6 +3446,12 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + [[package]] name = "rustybuzz" version = "0.14.1" @@ -3519,9 +3533,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.11.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" dependencies = [ "core-foundation-sys", "libc", @@ -3805,6 +3819,28 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.77", +] + [[package]] name = "subtle" version = "2.6.1" diff --git a/Cargo.toml b/Cargo.toml index dfa39c1..a50e88c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,13 +33,19 @@ ultralight = ["ul-next"] [dependencies] env_home = "0.1.0" iced = { version = "0.13", features = ["advanced", "image", "tokio", "lazy"] } -iced_aw = { version = "0.10", features = ["tab_bar", "icons"] } +iced_aw = { version = "0.10", features = [ + "tab_bar", + "icons", + "selection_list", +] } iced_on_focus_widget = "0.1.1" rand = "0.8.5" reqwest = "0.12.5" serde = "1.0.207" serde_json = "1.0.124" smol_str = "0.2.2" +strum = { version = "0.26.3", features = ["derive"] } +strum_macros = "0.26.4" tempfile = "3.12.0" ul-next = { version = "0.4", optional = true } url = "2.5.2" diff --git a/examples/keyboard_driven.rs b/examples/keyboard_driven.rs index 6cbabd5..1fd4b63 100644 --- a/examples/keyboard_driven.rs +++ b/examples/keyboard_driven.rs @@ -65,7 +65,7 @@ impl Browser { Message::Update => self.widgets.force_update().map(Message::BrowserWidget), Message::Event(event) => self .widgets - .update(widgets::Message::Event(event)) + .update(widgets::Message::Event(Some(event))) .map(Message::BrowserWidget), } } diff --git a/src/widgets/browser_view.rs b/src/widgets/browser_view.rs index d885220..ac11234 100644 --- a/src/widgets/browser_view.rs +++ b/src/widgets/browser_view.rs @@ -96,11 +96,11 @@ where match event { Event::Keyboard(event) => { - shell.publish(Message::SendKeyboardEvent(event)); + shell.publish(Message::SendKeyboardEvent(Some(event))); } Event::Mouse(event) => { if let Some(point) = cursor.position_in(layout.bounds()) { - shell.publish(Message::SendMouseEvent(point, event)); + shell.publish(Message::SendMouseEvent(point, Some(event))); } } _ => (), diff --git a/src/widgets/command_window.rs b/src/widgets/command_window.rs index ccda3bb..7ac4f8d 100644 --- a/src/widgets/command_window.rs +++ b/src/widgets/command_window.rs @@ -1,14 +1,42 @@ use iced::widget::{center, column, container, mouse_area, opaque, stack, text_input}; -use iced::{border, Color, Element, Theme}; +use iced::{border, Color, Element, Font, Length, Theme}; +use iced_aw::SelectionList; +use strum::IntoEnumIterator; use super::Message; +pub struct CommandWindowState { + pub query: String, + actions: Vec, + pub selected_action: String, + pub selected_index: usize, +} + +impl CommandWindowState { + pub fn new() -> Self { + Self { + query: String::new(), + actions: Message::iter().map(|e| e.clone().to_string()).collect(), + selected_action: String::new(), + selected_index: 0, + } + } +} + pub fn command_window<'a>( base: impl Into>, - query: &str, + state: &'a CommandWindowState, ) -> Element<'a, Message> { let window = container(column![ - text_input("Command Menu", query).on_input(Message::QueryChanged), + text_input("Command Menu", &state.query).on_input(Message::QueryChanged), + SelectionList::new(&state.actions, Message::CommandSelectionChanged) + .width(Length::Fill) + .height(Length::Fill) + .style(|theme: &Theme, _| iced_aw::style::selection_list::Style { + text_color: theme.palette().text.into(), + background: theme.palette().background.into(), + ..Default::default() + }), ]) .padding(10) .center(600) diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index 70d1cae..993fd37 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -1,7 +1,11 @@ +use command_window::CommandWindowState; use iced::keyboard::{self, key}; use iced::widget::{self, column}; use iced::{event::Event, mouse, Element, Point, Size, Task}; use iced_on_focus_widget::hoverable; +use nav_bar::NavBarState; +use std::string::ToString; +use strum_macros::{Display, EnumIter}; use url::Url; mod browser_view; @@ -18,27 +22,43 @@ pub use command_window::command_window; use crate::{engines::BrowserEngine, shortcut::check_shortcut, to_url, ImageInfo, Shortcuts}; -#[derive(Debug, Clone, PartialEq)] +// Options exist only to have defaults for EnumIter +#[derive(Debug, Clone, PartialEq, Display, EnumIter)] pub enum Message { + // Commands + #[strum(to_string = "Go Backward")] GoBackward, + #[strum(to_string = "Go Forward")] GoForward, Refresh, + #[strum(to_string = "Go Home")] GoHome, + #[strum(to_string = "Go To Url")] GoToUrl(String), + #[strum(to_string = "Change Tab")] ChangeTab(TabSelectionType), + #[strum(to_string = "Close Tab")] CloseTab(TabSelectionType), + #[strum(to_string = "Close Tab")] CloseCurrentTab, + #[strum(to_string = "New Tab")] CreateTab, + #[strum(to_string = "Toggle Command Palatte")] + ToggleOverlay, + #[strum(to_string = "Show Command Palatte")] + ShowOverlay, + #[strum(to_string = "Hide Command Palatte")] + HideOverlay, + + // Internal only - for widgets UrlChanged(String), UpdateUrl, QueryChanged(String), - SendKeyboardEvent(keyboard::Event), - SendMouseEvent(Point, mouse::Event), + CommandSelectionChanged(usize, String), + SendKeyboardEvent(Option), + SendMouseEvent(Point, Option), UpdateViewSize(Size), - Event(Event), - ToggleOverlay, - ShowOverlay, - HideOverlay, + Event(Option), } /// Allows different widgets to interact in their native way @@ -47,12 +67,17 @@ pub enum TabSelectionType { Id(u32), Index(usize), } +impl Default for TabSelectionType { + fn default() -> Self { + TabSelectionType::Index(0) + } +} pub struct BrowserWidget { engine: Option, home: Url, - url: String, // State of url bar - query: String, // State of Command window + nav_bar_state: NavBarState, + command_window_state: CommandWindowState, with_tab_bar: bool, with_nav_bar: bool, show_overlay: bool, @@ -69,8 +94,8 @@ where Self { engine: None, home, - url: String::new(), - query: String::new(), + nav_bar_state: NavBarState::new(), + command_window_state: CommandWindowState::new(), with_tab_bar: false, with_nav_bar: false, show_overlay: false, @@ -201,11 +226,13 @@ where Task::none() } Message::SendKeyboardEvent(event) => { - self.engine().handle_keyboard_event(event); + self.engine() + .handle_keyboard_event(event.expect("Value cannot be none")); Task::none() } Message::SendMouseEvent(point, event) => { - self.engine_mut().handle_mouse_event(point, event); + self.engine_mut() + .handle_mouse_event(point, event.expect("Value cannot be none")); Task::none() } Message::ChangeTab(index_type) => { @@ -216,7 +243,7 @@ where } }; self.engine_mut().get_tabs_mut().set_current_id(id); - self.url = self.engine().get_tabs().get_current().url(); + self.nav_bar_state.0 = self.engine().get_tabs().get_current().url(); Task::none() } Message::CloseCurrentTab => Task::done(Message::CloseTab(TabSelectionType::Id( @@ -235,11 +262,11 @@ where } }; self.engine_mut().get_tabs_mut().remove(id); - self.url = self.engine().get_tabs().get_current().url(); + self.nav_bar_state.0 = self.engine().get_tabs().get_current().url(); Task::none() } Message::CreateTab => { - self.url = self.home.to_string(); + self.nav_bar_state.0 = self.home.to_string(); let home = self.home.clone(); let bounds = self.view_size; let tab = self.engine_mut().new_tab( @@ -255,12 +282,12 @@ where } Message::GoBackward => { self.engine().go_back(); - self.url = self.engine().get_tabs().get_current().url(); + self.nav_bar_state.0 = self.engine().get_tabs().get_current().url(); Task::none() } Message::GoForward => { self.engine().go_forward(); - self.url = self.engine().get_tabs().get_current().url(); + self.nav_bar_state.0 = self.engine().get_tabs().get_current().url(); Task::none() } Message::Refresh => { @@ -276,24 +303,27 @@ where Task::none() } Message::UpdateUrl => { - self.url = self.engine().get_tabs().get_current().url(); + self.nav_bar_state.0 = self.engine().get_tabs().get_current().url(); Task::none() } Message::UrlChanged(url) => { - self.url = url; + self.nav_bar_state.0 = url; Task::none() } Message::QueryChanged(query) => { - self.query = query; + self.command_window_state.query = query; + Task::none() + } + Message::CommandSelectionChanged(index, name) => { + self.command_window_state.selected_index = index; + self.command_window_state.selected_action = name; Task::none() } Message::ToggleOverlay => { if self.show_overlay { - self.show_overlay = false; - widget::focus_next() + Task::done(Message::HideOverlay) } else { - self.show_overlay = true; - widget::focus_next() + Task::done(Message::ShowOverlay) } } Message::ShowOverlay => { @@ -302,11 +332,11 @@ where } Message::HideOverlay => { self.show_overlay = false; - Task::none() + widget::focus_next() } Message::Event(event) => { match event { - Event::Keyboard(key) => { + Some(Event::Keyboard(key)) => { if let iced::keyboard::Event::KeyPressed { key, modified_key: _, @@ -349,7 +379,8 @@ where column = column.push(tab_bar(self.engine().get_tabs())) } if self.with_nav_bar { - column = column.push(hoverable(nav_bar(&self.url)).on_focus_change(Message::UpdateUrl)) + column = column + .push(hoverable(nav_bar(&self.nav_bar_state)).on_focus_change(Message::UpdateUrl)) } let browser_view = browser_view( @@ -358,7 +389,7 @@ where !self.show_overlay, ); if self.show_overlay { - column = column.push(command_window(browser_view, &self.query)) + column = column.push(command_window(browser_view, &self.command_window_state)) } else { column = column.push(browser_view); } diff --git a/src/widgets/nav_bar.rs b/src/widgets/nav_bar.rs index 17974fc..7af8258 100644 --- a/src/widgets/nav_bar.rs +++ b/src/widgets/nav_bar.rs @@ -4,7 +4,15 @@ use iced_aw::core::icons::bootstrap::{icon_to_text, Bootstrap}; use super::Message; -pub fn nav_bar(url: &str) -> Element { +/// Holds the state of infomation in nav_bar +pub struct NavBarState(pub String); +impl NavBarState { + pub fn new() -> Self { + NavBarState(String::new()) + } +} + +pub fn nav_bar(state: &NavBarState) -> Element { let back = tooltip_helper( Button::new(icon_to_text(Bootstrap::ChevronBarLeft)) .on_press(Message::GoBackward) @@ -31,10 +39,10 @@ pub fn nav_bar(url: &str) -> Element { ); let space_left = Space::new(Length::Fill, Length::Shrink); let space_right = Space::new(Length::Fill, Length::Shrink); - let search = text_input("https://site.com", url) + let search = text_input("https://site.com", &state.0) .on_input(Message::UrlChanged) .on_paste(Message::GoToUrl) - .on_submit(Message::GoToUrl(url.to_string())) + .on_submit(Message::GoToUrl(state.0.to_string())) .line_height(LineHeight::Relative(2.0)); row!( From 3d2434d830fab2b830116e1d768ed47e003000b2 Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Sat, 21 Sep 2024 17:40:02 -0600 Subject: [PATCH 11/15] working on build.rs --- build.rs | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 build.rs diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..d550e21 --- /dev/null +++ b/build.rs @@ -0,0 +1,9 @@ +fn main() { + // ensure runtime resources exist + #[cfg(feature = "ultralight")] + { + let path = env!("CARGO_MANIFEST_DIR"); + + let possible_directories = Vec::new(); + } +} From 61826c7876fa34af3c6d33eb9db9f1c6264f35cc Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Sun, 22 Sep 2024 14:55:43 -0400 Subject: [PATCH 12/15] can now copy resource directory to project dir --- build.rs | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 89 insertions(+), 2 deletions(-) diff --git a/build.rs b/build.rs index d550e21..3c6ff48 100644 --- a/build.rs +++ b/build.rs @@ -1,9 +1,96 @@ +use std::fs::{self, DirEntry}; +use std::path::Path; + +const PATH: &str = env!("CARGO_MANIFEST_DIR"); + fn main() { // ensure runtime resources exist #[cfg(feature = "ultralight")] { - let path = env!("CARGO_MANIFEST_DIR"); + let mut possible_directories = Vec::new(); + + let target = Path::new(PATH).join("target"); + let debug_path = target.clone().join("debug"); + let release_path = target.clone().join("release"); + + if let Ok(debug) = fs::exists(debug_path.clone()) { + if debug { + get_paths( + &mut possible_directories, + debug_path.join("build").to_str().unwrap().to_string(), + ) + } + } else if let Ok(release) = fs::exists(release_path.clone()) { + if release { + get_paths( + &mut possible_directories, + release_path.join("build").to_str().unwrap().to_string(), + ) + } + } else { + panic!("Could not find either debug or release dirs") + } + + assert!(possible_directories.len() >= 1); + + let local_resources = Path::new(PATH).join("resources"); + + for path in possible_directories { + if let Ok(resources) = fs::exists(path.path().join("out/ul-sdk/resources")) { + if resources { + if let Ok(local_resources_exist) = fs::exists(local_resources.clone()) { + if local_resources_exist { + fs::remove_dir_all(local_resources.clone()) + .expect("Failed to delete resources dir") + } + } - let possible_directories = Vec::new(); + fs::create_dir(local_resources.clone()) + .expect("Failed to create resources dir"); + + copy_file( + path.path().join("out/ul-sdk/resources").as_path(), + local_resources.clone().join("").as_path(), + "cacert.pem", + ) + .expect("Failed to copy cacert.pem"); + copy_file( + path.path().join("out/ul-sdk/resources").as_path(), + local_resources.clone().join("").as_path(), + "icudt67l.dat", + ) + .expect("Failed to copy icudt67l.dat"); + + break; + } + } else { + panic!("The resouce dir entered has not resources") + } + } } + + println!("cargo:rerun-if-changed=resources"); + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=Cargo.lock"); +} + +fn copy_file(from: &Path, to: &Path, file_name: &str) -> Result { + fs::copy(&from.join(file_name), to.join(file_name)) +} + +fn get_paths(possible_paths: &mut Vec, path_str: String) { + let mut paths: Vec = fs::read_dir(path_str) + .expect("Could not read dir") + .map(|f| f.unwrap()) + .filter(|file| file.path().to_string_lossy().contains("ul-next-sys")) + .collect(); + // TODO: check if sort working + paths.sort_by(|a, b| { + a.metadata() + .unwrap() + .modified() + .unwrap() + .cmp(&b.metadata().unwrap().modified().unwrap()) + }); + possible_paths.append(&mut paths); } From 1b26a892d7bb2f18ea23c0d75aeb3e9d3c219d05 Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Sun, 22 Sep 2024 19:53:47 -0600 Subject: [PATCH 13/15] cargo update --- Cargo.lock | 8 ++++---- src/widgets/command_window.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 01634ce..54489cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3949,18 +3949,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", diff --git a/src/widgets/command_window.rs b/src/widgets/command_window.rs index 7ac4f8d..39f2cc1 100644 --- a/src/widgets/command_window.rs +++ b/src/widgets/command_window.rs @@ -1,5 +1,5 @@ use iced::widget::{center, column, container, mouse_area, opaque, stack, text_input}; -use iced::{border, Color, Element, Font, Length, Theme}; +use iced::{border, Color, Element, Length, Theme}; use iced_aw::SelectionList; use strum::IntoEnumIterator; From 783bc8c4fc52145726ac7493bf50567bba3f340b Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Sun, 22 Sep 2024 19:57:52 -0600 Subject: [PATCH 14/15] cargo fix --- Cargo.toml | 2 +- build.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a50e88c..de977db 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "icy_browser" version = "0.1.0" edition = "2021" -rust-version = "1.79.0" +rust-version = "1.81.0" description = "iced browser widgets" repository = "https://github.com/LegitCamper/rust-browser" diff --git a/build.rs b/build.rs index 3c6ff48..0e640c9 100644 --- a/build.rs +++ b/build.rs @@ -75,7 +75,7 @@ fn main() { } fn copy_file(from: &Path, to: &Path, file_name: &str) -> Result { - fs::copy(&from.join(file_name), to.join(file_name)) + fs::copy(from.join(file_name), to.join(file_name)) } fn get_paths(possible_paths: &mut Vec, path_str: String) { From a89bef3911e6440f432693bcae1126bd287f3e9c Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Sun, 22 Sep 2024 20:05:33 -0600 Subject: [PATCH 15/15] clippy --- build.rs | 2 +- src/shortcut.rs | 12 +++++++++--- src/widgets/command_window.rs | 8 +++++++- src/widgets/nav_bar.rs | 7 +++++++ 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/build.rs b/build.rs index 0e640c9..bd3bd7f 100644 --- a/build.rs +++ b/build.rs @@ -31,7 +31,7 @@ fn main() { panic!("Could not find either debug or release dirs") } - assert!(possible_directories.len() >= 1); + assert!(!possible_directories.is_empty()); let local_resources = Path::new(PATH).join("resources"); diff --git a/src/shortcut.rs b/src/shortcut.rs index fa4b688..83ee3ef 100644 --- a/src/shortcut.rs +++ b/src/shortcut.rs @@ -24,7 +24,7 @@ impl ShortcutBuilder { } unreachable!() }) - .filter(|item| *item == true) + .filter(|item| *item) // if item == true .count() != 1 { @@ -42,7 +42,7 @@ impl ShortcutBuilder { } unreachable!() }) - .filter(|item| *item == true) + .filter(|item| *item) // if itme == true .count() < 1 { @@ -58,6 +58,12 @@ impl ShortcutBuilder { } } +impl Default for ShortcutBuilder { + fn default() -> Self { + Self::new() + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum ShortcutModifier { Shift, @@ -98,5 +104,5 @@ pub fn check_shortcut(shortcut: &Shortcut, key: &Key, modifiers: &Modifiers) -> ShortcutModifier::Alt => modifiers.alt(), }, }) - .all(|s| s == true) + .all(|s| s) // if s == true } diff --git a/src/widgets/command_window.rs b/src/widgets/command_window.rs index 39f2cc1..99668be 100644 --- a/src/widgets/command_window.rs +++ b/src/widgets/command_window.rs @@ -23,6 +23,12 @@ impl CommandWindowState { } } +impl Default for CommandWindowState { + fn default() -> Self { + Self::new() + } +} + pub fn command_window<'a>( base: impl Into>, state: &'a CommandWindowState, @@ -33,7 +39,7 @@ pub fn command_window<'a>( .width(Length::Fill) .height(Length::Fill) .style(|theme: &Theme, _| iced_aw::style::selection_list::Style { - text_color: theme.palette().text.into(), + text_color: theme.palette().text, background: theme.palette().background.into(), ..Default::default() }), diff --git a/src/widgets/nav_bar.rs b/src/widgets/nav_bar.rs index 7af8258..e082e0d 100644 --- a/src/widgets/nav_bar.rs +++ b/src/widgets/nav_bar.rs @@ -6,12 +6,19 @@ use super::Message; /// Holds the state of infomation in nav_bar pub struct NavBarState(pub String); + impl NavBarState { pub fn new() -> Self { NavBarState(String::new()) } } +impl Default for NavBarState { + fn default() -> Self { + Self::new() + } +} + pub fn nav_bar(state: &NavBarState) -> Element { let back = tooltip_helper( Button::new(icon_to_text(Bootstrap::ChevronBarLeft))