From 2a26e59cf30257be4ec3834eb3ce2fdbe91317dd Mon Sep 17 00:00:00 2001 From: Sawyer Bristol Date: Sun, 22 Sep 2024 20:22:44 -0600 Subject: [PATCH 01/21] fix README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ab88561..e679d93 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ## Iced library to create custom browsers -[![Build](https://github.com/LegitCamper/rust-browser/actions/workflows/ci.yml/badge.svg)](https://github.com/LegitCamper/rust-browser/actions/workflows/ci.yml) +[![Build](https://github.com/LegitCamper/icy_browser/actions/workflows/ci.yml/badge.svg)](https://github.com/LegitCamper/icy_browser/actions/workflows/ci.yml) ### Supported Platforms From 2b337a9864bbfdd72f83d3370295ff44dab98db9 Mon Sep 17 00:00:00 2001 From: Sawyer Bristol Date: Sun, 22 Sep 2024 20:23:19 -0600 Subject: [PATCH 02/21] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e679d93..8bd577a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ ## Iced library to create custom browsers [![Build](https://github.com/LegitCamper/icy_browser/actions/workflows/ci.yml/badge.svg)](https://github.com/LegitCamper/icy_browser/actions/workflows/ci.yml) - + ### Supported Platforms | Platform | Support | From 421a8a0d602884b6ef30a035293d5f4eb765d109 Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Mon, 23 Sep 2024 13:12:35 -0600 Subject: [PATCH 03/21] update iced_aw, add iced_fonts, and create font helper --- Cargo.lock | 10 ++++++++++ Cargo.toml | 7 ++----- examples/basic_browser.rs | 7 ++----- examples/keyboard_driven.rs | 9 +++------ src/lib.rs | 7 +++++++ 5 files changed, 24 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 54489cd..0eb24a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1790,6 +1790,15 @@ dependencies = [ "web-time", ] +[[package]] +name = "iced_fonts" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df7deb0800a850ee25c8a42559f72c0f249e577feb3aad37b9b65dc1e517e52a" +dependencies = [ + "iced_core", +] + [[package]] name = "iced_futures" version = "0.13.2" @@ -1956,6 +1965,7 @@ dependencies = [ "env_home", "iced", "iced_aw", + "iced_fonts", "iced_on_focus_widget", "rand", "reqwest", diff --git a/Cargo.toml b/Cargo.toml index de977db..f53c4be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,11 +33,8 @@ 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", - "selection_list", -] } +iced_aw = { version = "0.10", features = ["tab_bar", "selection_list"] } +iced_fonts = { version = "0.1.1", features = ["bootstrap"] } iced_on_focus_widget = "0.1.1" rand = "0.8.5" reqwest = "0.12.5" diff --git a/examples/basic_browser.rs b/examples/basic_browser.rs index d1fa436..66433e3 100644 --- a/examples/basic_browser.rs +++ b/examples/basic_browser.rs @@ -2,16 +2,13 @@ use iced::Theme; use iced::{Element, Settings, Subscription, Task}; -use iced_aw::BOOTSTRAP_FONT_BYTES; use std::time::Duration; -use icy_browser::{widgets, BrowserWidget, Ultralight}; +use icy_browser::{get_fonts, widgets, BrowserWidget, Ultralight}; fn main() -> iced::Result { - // This imports `icons` for widgets - let bootstrap_font = BOOTSTRAP_FONT_BYTES.into(); let settings = Settings { - fonts: vec![bootstrap_font], + fonts: get_fonts(), ..Default::default() }; diff --git a/examples/keyboard_driven.rs b/examples/keyboard_driven.rs index 1fd4b63..1ffe445 100644 --- a/examples/keyboard_driven.rs +++ b/examples/keyboard_driven.rs @@ -3,19 +3,16 @@ 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, Message as WidgetMessage, ShortcutBuilder, ShortcutModifier, - Ultralight, + get_fonts, widgets, BrowserWidget, KeyType, Message as WidgetMessage, ShortcutBuilder, + ShortcutModifier, Ultralight, }; fn main() -> iced::Result { - // This imports `icons` for widgets - let bootstrap_font = BOOTSTRAP_FONT_BYTES.into(); let settings = Settings { - fonts: vec![bootstrap_font], + fonts: get_fonts(), ..Default::default() }; diff --git a/src/lib.rs b/src/lib.rs index 6c47b35..f4b68e7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,6 @@ use iced::widget::image::{Handle, Image}; +pub use iced_fonts::BOOTSTRAP_FONT_BYTES; +use std::borrow::Cow; use url::{ParseError, Url}; mod engines; @@ -13,6 +15,11 @@ pub use widgets::{nav_bar, tab_bar, BrowserWidget, Message}; mod shortcut; pub use shortcut::{KeyType, Shortcut, ShortcutBuilder, ShortcutModifier, Shortcuts}; +// Helper function to ensure required icons are imported +pub fn get_fonts() -> Vec> { + vec![BOOTSTRAP_FONT_BYTES.into()] +} + // Image details for passing the view around #[derive(Debug, Clone)] pub struct ImageInfo { From fff5ec42079f50e1b6331636001f12c5787ce974 Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Mon, 23 Sep 2024 13:51:17 -0600 Subject: [PATCH 04/21] allow build.rs to be ran in other projects --- build.rs | 54 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/build.rs b/build.rs index bd3bd7f..5bec7fe 100644 --- a/build.rs +++ b/build.rs @@ -1,15 +1,30 @@ +use std::env::var; use std::fs::{self, DirEntry}; use std::path::Path; -const PATH: &str = env!("CARGO_MANIFEST_DIR"); - fn main() { + let out = var("OUT_DIR").unwrap(); + // This allows it to work in this project but also other projects too + let path = Path::new(&out) + .parent() + .unwrap() + .parent() + .unwrap() + .parent() + .unwrap() + .parent() + .unwrap() + .parent() + .unwrap(); + + println!("cargo:warning={:?}", path.as_os_str()); + // ensure runtime resources exist #[cfg(feature = "ultralight")] { let mut possible_directories = Vec::new(); - let target = Path::new(PATH).join("target"); + let target = Path::new(path).join("target"); let debug_path = target.clone().join("debug"); let release_path = target.clone().join("release"); @@ -33,33 +48,24 @@ fn main() { assert!(!possible_directories.is_empty()); - let local_resources = Path::new(PATH).join("resources"); + 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")) { + let resources_dir = path.path().join("out/ul-sdk/resources"); + if let Ok(resources) = fs::exists(resources_dir.clone()) { if resources { if let Ok(local_resources_exist) = fs::exists(local_resources.clone()) { if local_resources_exist { - fs::remove_dir_all(local_resources.clone()) + fs::remove_file(local_resources.clone()) .expect("Failed to delete resources dir") } } - 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"); + #[cfg(unix)] + { + std::os::unix::fs::symlink(resources_dir, local_resources) + .expect("Failed to sym link resource dir") + } break; } @@ -69,15 +75,11 @@ fn main() { } } - println!("cargo:rerun-if-changed=resources"); + println!("cargo:rerun-if-changed=targets"); 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") From d53907cdec98560227d49d1fe294504d3a902b79 Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Mon, 23 Sep 2024 15:31:23 -0600 Subject: [PATCH 05/21] simplify basic_browser example simplify --- Cargo.toml | 3 -- README.md | 58 ++++++++++--------------------------- build.rs | 2 -- examples/basic_browser.rs | 60 +++++++++------------------------------ src/lib.rs | 3 ++ 5 files changed, 32 insertions(+), 94 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f53c4be..a7b3067 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,9 +22,6 @@ incremental = true opt-level = "s" lto = "thin" -[build] -rustflags = ["-Z", "threads=8"] - [features] default = ["webkit"] webkit = ["ultralight"] diff --git a/README.md b/README.md index 8bd577a..61bbb2b 100644 --- a/README.md +++ b/README.md @@ -26,57 +26,29 @@ ``` Rust -use iced::{Element, Settings, Theme}; -use iced_aw::BOOTSTRAP_FONT_BYTES; +use iced::{Settings, Task, Theme}; +use icy_browser::{get_fonts, Browser, Message}; -use icy_browser::{widgets, BrowserWidget, Ultralight}; +fn run() -> (Browser, Task) { + ( + Browser::new_with_ultralight() + .with_tab_bar() + .with_nav_bar() + .build(), + Task::none(), + ) +} fn main() -> iced::Result { - // This imports `icons` for widgets - let bootstrap_font = BOOTSTRAP_FONT_BYTES.into(); let settings = Settings { - fonts: vec![bootstrap_font], + fonts: get_fonts(), ..Default::default() }; - iced::application("Basic Browser Example", Browser::update, Browser::view) + iced::application("Basic Browser", Browser::update, Browser::view) + .subscription(Browser::subscription) .settings(settings) .theme(|_| Theme::Dark) - .run() -} - -#[derive(Debug, Clone)] -pub enum Message { - BrowserWidget(widgets::Message), -} - -struct Browser { - widgets: BrowserWidget, -} - -impl Browser { - fn update(&mut self, message: Message) { - match message { - Message::BrowserWidget(msg) => { - self.widgets.update(msg); - } - } - } - - 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 } - } + .run_with(run) } ``` diff --git a/build.rs b/build.rs index 5bec7fe..8389ee8 100644 --- a/build.rs +++ b/build.rs @@ -17,8 +17,6 @@ fn main() { .parent() .unwrap(); - println!("cargo:warning={:?}", path.as_os_str()); - // ensure runtime resources exist #[cfg(feature = "ultralight")] { diff --git a/examples/basic_browser.rs b/examples/basic_browser.rs index 66433e3..88b7d25 100644 --- a/examples/basic_browser.rs +++ b/examples/basic_browser.rs @@ -1,10 +1,17 @@ -// Simple browser with familiar browser widget and the ultralight(webkit) webengine as a backend +// Simple browser with familiar browser widgets and the ultralight(webkit) webengine as a backend -use iced::Theme; -use iced::{Element, Settings, Subscription, Task}; -use std::time::Duration; +use iced::{Settings, Task, Theme}; +use icy_browser::{get_fonts, Browser, Message}; -use icy_browser::{get_fonts, widgets, BrowserWidget, Ultralight}; +fn run() -> (Browser, Task) { + ( + Browser::new_with_ultralight() + .with_tab_bar() + .with_nav_bar() + .build(), + Task::none(), + ) +} fn main() -> iced::Result { let settings = Settings { @@ -12,48 +19,9 @@ fn main() -> iced::Result { ..Default::default() }; - iced::application("Basic Browser Example", Browser::update, Browser::view) + iced::application("Basic 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, -} - -struct Browser { - widgets: BrowserWidget, -} - -impl Default for Browser { - fn default() -> Self { - // Customize the look and feel of the browser here - let widgets = BrowserWidget::new_with_ultralight() - .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), - } - } - - fn view(&self) -> Element { - self.widgets.view().map(Message::BrowserWidget) - } - - fn subscription(&self) -> Subscription { - iced::time::every(Duration::from_millis(10)).map(move |_| Message::Update) - } + .run_with(run) } diff --git a/src/lib.rs b/src/lib.rs index f4b68e7..e7b245e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,9 @@ pub use widgets::{nav_bar, tab_bar, BrowserWidget, Message}; mod shortcut; pub use shortcut::{KeyType, Shortcut, ShortcutBuilder, ShortcutModifier, Shortcuts}; +#[cfg(feature = "ultralight")] +pub type Browser = BrowserWidget; + // Helper function to ensure required icons are imported pub fn get_fonts() -> Vec> { vec![BOOTSTRAP_FONT_BYTES.into()] From ff2af9f4f46ec47eef4e6d23a4d6330208ac860e Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Mon, 23 Sep 2024 23:37:13 -0600 Subject: [PATCH 06/21] can create stateless custom widgets --- README.md | 10 +-- examples/basic_browser.rs | 10 +-- examples/custom_widgets.rs | 121 ++++++++++++++++++++++++++++++++++++ examples/keyboard_driven.rs | 11 ++-- src/lib.rs | 9 ++- src/widgets/mod.rs | 120 +++++++++++++++++++++++++---------- src/widgets/tab_bar.rs | 2 +- 7 files changed, 231 insertions(+), 52 deletions(-) create mode 100644 examples/custom_widgets.rs diff --git a/README.md b/README.md index 61bbb2b..9cfb9dc 100644 --- a/README.md +++ b/README.md @@ -27,11 +27,11 @@ ``` Rust use iced::{Settings, Task, Theme}; -use icy_browser::{get_fonts, Browser, Message}; +use icy_browser::{get_fonts, BasicBrowser, Message}; -fn run() -> (Browser, Task) { +fn run() -> (BasicBrowser, Task) { ( - Browser::new_with_ultralight() + BasicBrowser::new_basic() .with_tab_bar() .with_nav_bar() .build(), @@ -45,8 +45,8 @@ fn main() -> iced::Result { ..Default::default() }; - iced::application("Basic Browser", Browser::update, Browser::view) - .subscription(Browser::subscription) + iced::application("Basic Browser", BasicBrowser::update, BasicBrowser::view) + .subscription(BasicBrowser::subscription) .settings(settings) .theme(|_| Theme::Dark) .run_with(run) diff --git a/examples/basic_browser.rs b/examples/basic_browser.rs index 88b7d25..5a6a243 100644 --- a/examples/basic_browser.rs +++ b/examples/basic_browser.rs @@ -1,11 +1,11 @@ // Simple browser with familiar browser widgets and the ultralight(webkit) webengine as a backend use iced::{Settings, Task, Theme}; -use icy_browser::{get_fonts, Browser, Message}; +use icy_browser::{get_fonts, BasicBrowser, Message}; -fn run() -> (Browser, Task) { +fn run() -> (BasicBrowser, Task) { ( - Browser::new_with_ultralight() + BasicBrowser::new_basic() .with_tab_bar() .with_nav_bar() .build(), @@ -19,8 +19,8 @@ fn main() -> iced::Result { ..Default::default() }; - iced::application("Basic Browser", Browser::update, Browser::view) - .subscription(Browser::subscription) + iced::application("Basic Browser", BasicBrowser::update, BasicBrowser::view) + .subscription(BasicBrowser::subscription) .settings(settings) .theme(|_| Theme::Dark) .run_with(run) diff --git a/examples/custom_widgets.rs b/examples/custom_widgets.rs new file mode 100644 index 0000000..50551ed --- /dev/null +++ b/examples/custom_widgets.rs @@ -0,0 +1,121 @@ +// Custom view example with rainbow border + +use iced::widget::{column, container}; +use iced::{time, Border}; +use iced::{Color, Element, Length, Settings, Subscription, Task, Theme}; +use std::time::{Duration, Instant}; + +use icy_browser::{ + browser_view, get_fonts, hoverable, nav_bar, tab_bar, widgets, BrowserEngine, BrowserWidget, + Ultralight, +}; + +fn main() -> iced::Result { + let settings = Settings { + fonts: get_fonts(), + ..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, + Tick, +} + +#[derive(Debug, Clone)] +struct CustomWidgetState { + border_colors: Vec, + start_time: Instant, +} + +struct Browser { + widgets: BrowserWidget, +} + +impl Default for Browser { + fn default() -> Self { + Self { + widgets: BrowserWidget::new() + .with_custom_view( + custom_view, + CustomWidgetState { + border_colors: vec![ + Color::from_rgb(1.0, 0.0, 0.0), // Red + Color::from_rgb(1.0, 0.5, 0.0), // Orange + Color::from_rgb(1.0, 1.0, 0.0), // Yellow + Color::from_rgb(0.0, 1.0, 0.0), // Green + Color::from_rgb(0.0, 0.0, 1.0), // Blue + Color::from_rgb(0.29, 0.0, 0.51), // Indigo + Color::from_rgb(0.56, 0.0, 1.0), // Violet + ], + start_time: Instant::now(), + }, + ) + .build(), + } + } +} + +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::Tick => Task::none(), // Tick + } + } + + fn view(&self) -> Element { + self.widgets.view().map(Message::BrowserWidget) + } + + fn subscription(&self) -> Subscription { + Subscription::batch([ + time::every(Duration::from_millis(10)).map(move |_| Message::Update), + time::every(Duration::from_millis(16)).map(|_| Message::Tick), + ]) + } +} + +// fn custom_update<'a, Engine: BrowserEngine, CustomViewState>( +// browser_widget: &'a BrowserWidget, +// custom_view_state: &'a mut CustomViewState, +// ) -> Element<'a, icy_browser::Message> { + +// } + +fn custom_view( + browser_widget: &BrowserWidget, + widget_state: CustomWidgetState, +) -> Element { + let elapsed = widget_state.start_time.elapsed().as_secs_f32(); + let color_index = (elapsed * 2.0) as usize % widget_state.border_colors.len(); + let color = widget_state.border_colors[color_index]; + + container(column![ + tab_bar(browser_widget.engine().get_tabs()), + hoverable(nav_bar(&browser_widget.nav_bar_state)) + .on_focus_change(icy_browser::Message::UpdateUrl), + browser_view( + browser_widget.view_size, + browser_widget.engine().get_tabs().get_current().get_view(), + !browser_widget.show_overlay, + ), + ]) + .center_x(Length::Fill) + .center_y(Length::Fill) + .padding(20) + .style(move |_theme| container::Style { + border: Border::default().color(color).width(20), + ..Default::default() + }) + .into() +} diff --git a/examples/keyboard_driven.rs b/examples/keyboard_driven.rs index 1ffe445..df2c851 100644 --- a/examples/keyboard_driven.rs +++ b/examples/keyboard_driven.rs @@ -6,8 +6,8 @@ use iced::{Element, Settings, Subscription, Task}; use std::time::Duration; use icy_browser::{ - get_fonts, widgets, BrowserWidget, KeyType, Message as WidgetMessage, ShortcutBuilder, - ShortcutModifier, Ultralight, + get_fonts, widgets, BasicBrowser, BrowserWidget, KeyType, Message as WidgetMessage, + ShortcutBuilder, ShortcutModifier, }; fn main() -> iced::Result { @@ -16,6 +16,8 @@ fn main() -> iced::Result { ..Default::default() }; + println!("Press Crtl - E to open to Command palatte"); + iced::application("Keyboard Driven Browser", Browser::update, Browser::view) .subscription(Browser::subscription) .settings(settings) @@ -31,7 +33,7 @@ pub enum Message { } struct Browser { - widgets: BrowserWidget, + widgets: BasicBrowser, } impl Default for Browser { @@ -45,10 +47,9 @@ impl Default for Browser { ], ) .build(); - let widgets = BrowserWidget::new_with_ultralight() + let widgets = BrowserWidget::new_basic() .with_custom_shortcuts(shortcuts) .with_tab_bar() - .with_nav_bar() .build(); Self { widgets } diff --git a/src/lib.rs b/src/lib.rs index e7b245e..eba6bc4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ use iced::widget::image::{Handle, Image}; pub use iced_fonts::BOOTSTRAP_FONT_BYTES; +pub use iced_on_focus_widget::hoverable; use std::borrow::Cow; use url::{ParseError, Url}; @@ -10,13 +11,17 @@ pub use engines::{BrowserEngine, PixelFormat, Tab, TabInfo, Tabs}; pub use engines::ultralight::Ultralight; pub mod widgets; -pub use widgets::{nav_bar, tab_bar, BrowserWidget, Message}; +pub use widgets::{browser_view, command_window, nav_bar, tab_bar, BrowserWidget, Message}; mod shortcut; pub use shortcut::{KeyType, Shortcut, ShortcutBuilder, ShortcutModifier, Shortcuts}; +// Used when Not using custom views +#[derive(Clone)] +pub struct NoCustomView; + #[cfg(feature = "ultralight")] -pub type Browser = BrowserWidget; +pub type BasicBrowser = BrowserWidget; // Helper function to ensure required icons are imported pub fn get_fonts() -> Vec> { diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index 993fd37..23f556c 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -1,10 +1,12 @@ use command_window::CommandWindowState; +use iced::event::{self, Event}; use iced::keyboard::{self, key}; use iced::widget::{self, column}; -use iced::{event::Event, mouse, Element, Point, Size, Task}; +use iced::{mouse, Element, Point, Size, Subscription, Task}; use iced_on_focus_widget::hoverable; use nav_bar::NavBarState; use std::string::ToString; +use std::time::Duration; use strum_macros::{Display, EnumIter}; use url::Url; @@ -51,6 +53,7 @@ pub enum Message { HideOverlay, // Internal only - for widgets + Update, UrlChanged(String), UpdateUrl, QueryChanged(String), @@ -73,19 +76,33 @@ impl Default for TabSelectionType { } } -pub struct BrowserWidget { - engine: Option, - home: Url, - nav_bar_state: NavBarState, - command_window_state: CommandWindowState, - with_tab_bar: bool, - with_nav_bar: bool, - show_overlay: bool, - shortcuts: Shortcuts, - view_size: Size, +/// Allows users to create their own views with the same information of native widgets +pub type CustomView = fn( + &BrowserWidget, + custom_view_state: CustomViewState, +) -> Element; + +/// Allows users to create their own updates with the same information of native widgets +pub type CustomUpdate<'a, Engine, CustomViewState> = fn( + &BrowserWidget, + custom_view_state: &mut CustomViewState, +) -> Element<'a, Message>; + +pub struct BrowserWidget { + pub engine: Option, + pub home: Url, + pub nav_bar_state: NavBarState, + pub command_window_state: CommandWindowState, + custom_view: Option>, + custom_view_state: Option, + pub with_tab_bar: bool, + pub with_nav_bar: bool, + pub show_overlay: bool, + pub shortcuts: Shortcuts, + pub view_size: Size, } -impl Default for BrowserWidget +impl Default for BrowserWidget where Engine: BrowserEngine, { @@ -96,6 +113,8 @@ where home, nav_bar_state: NavBarState::new(), command_window_state: CommandWindowState::new(), + custom_view: None, + custom_view_state: None, with_tab_bar: false, with_nav_bar: false, show_overlay: false, @@ -109,8 +128,8 @@ where use crate::engines::ultralight::Ultralight; #[cfg(feature = "ultralight")] -impl BrowserWidget { - pub fn new_with_ultralight() -> BrowserWidget { +impl<'a, CustomViewState: Clone> BrowserWidget { + pub fn new_basic() -> BrowserWidget { BrowserWidget { engine: Some(Ultralight::new()), ..BrowserWidget::default() @@ -118,7 +137,7 @@ impl BrowserWidget { } } -impl BrowserWidget +impl BrowserWidget where Engine: BrowserEngine, { @@ -151,6 +170,16 @@ where self } + pub fn with_custom_view( + mut self, + custom_view: CustomView, + custom_view_state: CustomViewState, + ) -> Self { + self.custom_view = Some(custom_view); + self.custom_view_state = Some(custom_view_state); + self + } + pub fn build(self) -> Self { assert!(self.engine.is_some()); @@ -159,7 +188,7 @@ where build } - fn engine(&self) -> &Engine { + pub fn engine(&self) -> &Engine { self.engine .as_ref() .expect("Browser was created without a backend engine!") @@ -220,6 +249,7 @@ where pub fn update(&mut self, message: Message) -> Task { let task = match message { + Message::Update => self.force_update(), Message::UpdateViewSize(size) => { self.view_size = size; self.engine_mut().resize(size); @@ -373,27 +403,49 @@ where } pub fn view(&self) -> Element { - let mut column = column![]; + match self.custom_view { + Some(custom_view) => { + if let Some(state) = &self.custom_view_state { + custom_view(self, state.clone()) + } else { + panic!( + "If Custom View is set, Custom View State must be set too. Use () if unused", + ) + } + } + None => { + let mut column = column![]; - if self.with_tab_bar { - column = column.push(tab_bar(self.engine().get_tabs())) - } - if self.with_nav_bar { - column = column - .push(hoverable(nav_bar(&self.nav_bar_state)).on_focus_change(Message::UpdateUrl)) - } + if self.with_tab_bar { + column = column.push(tab_bar(self.engine().get_tabs())) + } + if self.with_nav_bar { + column = column.push( + hoverable(nav_bar(&self.nav_bar_state)).on_focus_change(Message::UpdateUrl), + ) + } - let browser_view = browser_view( - self.view_size, - self.engine().get_tabs().get_current().get_view(), - !self.show_overlay, - ); - if self.show_overlay { - column = column.push(command_window(browser_view, &self.command_window_state)) - } else { - column = column.push(browser_view); + let browser_view = browser_view( + self.view_size, + self.engine().get_tabs().get_current().get_view(), + !self.show_overlay, + ); + if self.show_overlay { + column = column.push(command_window(browser_view, &self.command_window_state)) + } else { + column = column.push(browser_view); + } + + column.into() + } } + } - column.into() + pub 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(|e: Event| Message::Event(Some(e))), + ]) } } diff --git a/src/widgets/tab_bar.rs b/src/widgets/tab_bar.rs index 945c7d9..73d4015 100644 --- a/src/widgets/tab_bar.rs +++ b/src/widgets/tab_bar.rs @@ -7,7 +7,7 @@ use super::{Message, TabSelectionType}; use crate::engines::{TabInfo, Tabs}; // helper function to create navigation bar -pub fn tab_bar(tabs: &Tabs) -> Element<'static, Message> { +pub fn tab_bar(tabs: &Tabs) -> Element { let current_id = tabs.get_current_id(); let active_tab = tabs .tabs() From 63ccc5aa82e638b85ee7064e2913a5a954c4a499 Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Tue, 24 Sep 2024 17:17:06 -0600 Subject: [PATCH 07/21] create bookmark widget --- examples/basic_browser.rs | 6 +++++- examples/keyboard_driven.rs | 12 ++++++++++-- src/lib.rs | 26 ++++++++++++++++++++++++-- src/widgets/bookmark_bar.rs | 17 +++++++++++++++++ src/widgets/command_window.rs | 32 +++++++++++++++++++++++--------- src/widgets/mod.rs | 26 +++++++++++++++++++++++++- src/widgets/nav_bar.rs | 1 + src/widgets/tab_bar.rs | 2 +- 8 files changed, 106 insertions(+), 16 deletions(-) create mode 100644 src/widgets/bookmark_bar.rs diff --git a/examples/basic_browser.rs b/examples/basic_browser.rs index 5a6a243..c4cc16e 100644 --- a/examples/basic_browser.rs +++ b/examples/basic_browser.rs @@ -1,13 +1,17 @@ // Simple browser with familiar browser widgets and the ultralight(webkit) webengine as a backend use iced::{Settings, Task, Theme}; -use icy_browser::{get_fonts, BasicBrowser, Message}; +use icy_browser::{get_fonts, BasicBrowser, Bookmark, Message}; fn run() -> (BasicBrowser, Task) { ( BasicBrowser::new_basic() .with_tab_bar() .with_nav_bar() + .with_bookmark_bar(vec![Bookmark::new( + "https://www.rust-lang.org", + "rust-lang.org", + )]) .build(), Task::none(), ) diff --git a/examples/keyboard_driven.rs b/examples/keyboard_driven.rs index df2c851..9474bf8 100644 --- a/examples/keyboard_driven.rs +++ b/examples/keyboard_driven.rs @@ -6,7 +6,7 @@ use iced::{Element, Settings, Subscription, Task}; use std::time::Duration; use icy_browser::{ - get_fonts, widgets, BasicBrowser, BrowserWidget, KeyType, Message as WidgetMessage, + get_fonts, widgets, BasicBrowser, Bookmark, BrowserWidget, KeyType, Message as WidgetMessage, ShortcutBuilder, ShortcutModifier, }; @@ -16,7 +16,7 @@ fn main() -> iced::Result { ..Default::default() }; - println!("Press Crtl - E to open to Command palatte"); + println!("Press 'Crtl + E' to open to Command palatte"); iced::application("Keyboard Driven Browser", Browser::update, Browser::view) .subscription(Browser::subscription) @@ -50,6 +50,14 @@ impl Default for Browser { let widgets = BrowserWidget::new_basic() .with_custom_shortcuts(shortcuts) .with_tab_bar() + .with_bookmark_bar(vec![ + Bookmark::new("https://www.rust-lang.org", "rust-lang.org"), + Bookmark::new( + "https://github.com/LegitCamper/icy_browser", + "icy_browser github", + ), + Bookmark::new("https://docs.rs/iced/latest/iced/", "iced docs"), + ]) .build(); Self { widgets } diff --git a/src/lib.rs b/src/lib.rs index eba6bc4..e859a63 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,11 @@ -use iced::widget::image::{Handle, Image}; +use iced::widget::{ + button, + image::{Handle, Image}, + Button, +}; pub use iced_fonts::BOOTSTRAP_FONT_BYTES; pub use iced_on_focus_widget::hoverable; -use std::borrow::Cow; +use std::{borrow::Cow, str::FromStr}; use url::{ParseError, Url}; mod engines; @@ -93,3 +97,21 @@ fn to_url(url: &str) -> Option { } } } + +pub struct Bookmark { + url: Url, + name: String, + // icon: Optional<> +} +impl Bookmark { + pub fn new(url: &str, name: &str) -> Self { + Bookmark { + url: Url::from_str(url).expect("Failed to parse url from bookmark url"), + name: name.to_string(), + } + } + + pub fn as_button(&self) -> Button { + button(self.name.as_str()).on_press(Message::GoToUrl(self.url.to_string())) + } +} diff --git a/src/widgets/bookmark_bar.rs b/src/widgets/bookmark_bar.rs new file mode 100644 index 0000000..3567da1 --- /dev/null +++ b/src/widgets/bookmark_bar.rs @@ -0,0 +1,17 @@ +use iced::{widget::Row, Element}; + +use super::Message; +use crate::Bookmark; + +/// Creates bookmark bar widget +pub fn bookmark_bar(bookmarks: &Vec) -> Element { + Row::from_vec( + bookmarks + .iter() + .map(|bookmark| bookmark.as_button().into()) + .collect(), + ) + .padding(5) + .spacing(5) + .into() +} diff --git a/src/widgets/command_window.rs b/src/widgets/command_window.rs index 99668be..63fb244 100644 --- a/src/widgets/command_window.rs +++ b/src/widgets/command_window.rs @@ -1,13 +1,18 @@ use iced::widget::{center, column, container, mouse_area, opaque, stack, text_input}; -use iced::{border, Color, Element, Length, Theme}; +use iced::{border, Color, Element, Font, Length, Theme}; use iced_aw::SelectionList; use strum::IntoEnumIterator; use super::Message; +pub enum ResultType { + Command(Message), + // Bookmark, +} + pub struct CommandWindowState { pub query: String, - actions: Vec, + commands: Vec, pub selected_action: String, pub selected_index: usize, } @@ -16,7 +21,7 @@ impl CommandWindowState { pub fn new() -> Self { Self { query: String::new(), - actions: Message::iter().map(|e| e.clone().to_string()).collect(), + commands: Message::iter().map(|e| e.clone().to_string()).collect(), selected_action: String::new(), selected_index: 0, } @@ -34,15 +39,24 @@ pub fn command_window<'a>( state: &'a CommandWindowState, ) -> Element<'a, Message> { let window = container(column![ - 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_input("Command Menu", &state.query) + .on_input(Message::QueryChanged) + .size(25), + SelectionList::new_with( + &state.commands, + Message::CommandSelectionChanged, + 15., + 5, + |theme: &Theme, _| iced_aw::style::selection_list::Style { text_color: theme.palette().text, background: theme.palette().background.into(), ..Default::default() - }), + }, + None, + Font::DEFAULT + ) + .width(Length::Fill) + .height(Length::Fill) ]) .padding(10) .center(600) diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index 23f556c..ae4fec2 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -19,9 +19,13 @@ pub use nav_bar::nav_bar; mod tab_bar; pub use tab_bar::tab_bar; +mod bookmark_bar; +pub use bookmark_bar::bookmark_bar; + mod command_window; pub use command_window::command_window; +use crate::Bookmark; use crate::{engines::BrowserEngine, shortcut::check_shortcut, to_url, ImageInfo, Shortcuts}; // Options exist only to have defaults for EnumIter @@ -58,6 +62,7 @@ pub enum Message { UpdateUrl, QueryChanged(String), CommandSelectionChanged(usize, String), + CommandSelectionSelected, SendKeyboardEvent(Option), SendMouseEvent(Point, Option), UpdateViewSize(Size), @@ -97,6 +102,7 @@ pub struct BrowserWidget { custom_view_state: Option, pub with_tab_bar: bool, pub with_nav_bar: bool, + pub bookmarks: Option>, pub show_overlay: bool, pub shortcuts: Shortcuts, pub view_size: Size, @@ -117,6 +123,7 @@ where custom_view_state: None, with_tab_bar: false, with_nav_bar: false, + bookmarks: None, show_overlay: false, shortcuts: Shortcuts::default(), view_size: Size::new(800, 800), @@ -165,6 +172,11 @@ where self } + pub fn with_bookmark_bar(mut self, bookmarks: Vec) -> Self { + self.bookmarks = Some(bookmarks); + self + } + pub fn with_custom_shortcuts(mut self, shortcuts: Shortcuts) -> Self { self.shortcuts = shortcuts; self @@ -349,6 +361,9 @@ where self.command_window_state.selected_action = name; Task::none() } + Message::CommandSelectionSelected => { + unimplemented!() + } Message::ToggleOverlay => { if self.show_overlay { Task::done(Message::HideOverlay) @@ -377,10 +392,16 @@ where } = key { // Default behaviors - if key == keyboard::Key::Named(key::Named::Escape) && self.show_overlay + // escape to exit command palatte + if self.show_overlay && key == keyboard::Key::Named(key::Named::Escape) { return Task::done(Message::HideOverlay); } + // ctrl + R = refresh + else if modifiers.control() && key == key::Key::Character("r".into()) + { + return Task::done(Message::Refresh); + } // Shortcut (Customizable) behaviors for shortcut in self.shortcuts.iter() { @@ -424,6 +445,9 @@ where hoverable(nav_bar(&self.nav_bar_state)).on_focus_change(Message::UpdateUrl), ) } + if let Some(bookmarks) = self.bookmarks.as_ref() { + column = column.push(bookmark_bar(bookmarks)) + } let browser_view = browser_view( self.view_size, diff --git a/src/widgets/nav_bar.rs b/src/widgets/nav_bar.rs index e082e0d..779afa3 100644 --- a/src/widgets/nav_bar.rs +++ b/src/widgets/nav_bar.rs @@ -19,6 +19,7 @@ impl Default for NavBarState { } } +/// Creates Navigation bar widget pub fn nav_bar(state: &NavBarState) -> Element { let back = tooltip_helper( Button::new(icon_to_text(Bootstrap::ChevronBarLeft)) diff --git a/src/widgets/tab_bar.rs b/src/widgets/tab_bar.rs index 73d4015..dac86b7 100644 --- a/src/widgets/tab_bar.rs +++ b/src/widgets/tab_bar.rs @@ -6,7 +6,7 @@ use iced_aw::{TabBar as TB, TabLabel}; use super::{Message, TabSelectionType}; use crate::engines::{TabInfo, Tabs}; -// helper function to create navigation bar +/// Creates Tab bar widget pub fn tab_bar(tabs: &Tabs) -> Element { let current_id = tabs.get_current_id(); let active_tab = tabs From a14143a8201293ccc9bd90810381c73396540a64 Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Fri, 27 Sep 2024 16:01:02 -0600 Subject: [PATCH 08/21] allow lib to be built with rustls-tls --- Cargo.lock | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 4 +++- 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 0eb24a4..9ef052c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1679,6 +1679,7 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", + "webpki-roots", ] [[package]] @@ -3178,6 +3179,54 @@ dependencies = [ "memchr", ] +[[package]] +name = "quinn" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" +dependencies = [ + "bytes", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash 2.0.0", + "rustls", + "socket2", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "quinn-proto" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" +dependencies = [ + "bytes", + "rand", + "ring", + "rustc-hash 2.0.0", + "rustls", + "slab", + "thiserror", + "tinyvec", + "tracing", +] + +[[package]] +name = "quinn-udp" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fe68c2e9e1a1234e218683dbdf9f9dfcb094113c5ac2b938dfcb9bab4c4140b" +dependencies = [ + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.59.0", +] + [[package]] name = "quote" version = "1.0.37" @@ -3336,7 +3385,10 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", + "quinn", + "rustls", "rustls-pemfile", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", @@ -3344,11 +3396,13 @@ dependencies = [ "system-configuration", "tokio", "tokio-native-tls", + "tokio-rustls", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", + "webpki-roots", "windows-registry", ] diff --git a/Cargo.toml b/Cargo.toml index a7b3067..df352f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,9 +23,11 @@ opt-level = "s" lto = "thin" [features] -default = ["webkit"] +default = ["webkit", "native-tls"] webkit = ["ultralight"] ultralight = ["ul-next"] +native-tls = ["reqwest/native-tls"] +rustls-tls = ["reqwest/rustls-tls"] [dependencies] env_home = "0.1.0" From 8c6422785015665cffb3230eb1ebe0c71d1a9ccc Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Fri, 27 Sep 2024 16:13:01 -0600 Subject: [PATCH 09/21] allow build.rs to be skipped fix --- Cargo.toml | 2 ++ build.rs | 30 +++++++++++++++--------------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index df352f3..06d3e0f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,8 @@ webkit = ["ultralight"] ultralight = ["ul-next"] native-tls = ["reqwest/native-tls"] rustls-tls = ["reqwest/rustls-tls"] +# exists to prevent build.rs from failing build +cross = [] [dependencies] env_home = "0.1.0" diff --git a/build.rs b/build.rs index 8389ee8..e8a76cc 100644 --- a/build.rs +++ b/build.rs @@ -3,23 +3,24 @@ use std::fs::{self, DirEntry}; use std::path::Path; fn main() { - let out = var("OUT_DIR").unwrap(); - // This allows it to work in this project but also other projects too - let path = Path::new(&out) - .parent() - .unwrap() - .parent() - .unwrap() - .parent() - .unwrap() - .parent() - .unwrap() - .parent() - .unwrap(); - // ensure runtime resources exist #[cfg(feature = "ultralight")] + #[cfg(not(feature = "cross"))] { + let out = var("OUT_DIR").unwrap(); + // This allows it to work in this project but also other projects too + let path = Path::new(&out) + .parent() + .unwrap() + .parent() + .unwrap() + .parent() + .unwrap() + .parent() + .unwrap() + .parent() + .unwrap(); + let mut possible_directories = Vec::new(); let target = Path::new(path).join("target"); @@ -73,7 +74,6 @@ fn main() { } } - println!("cargo:rerun-if-changed=targets"); println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=Cargo.lock"); } From 6bcb88a4300bfde5a43e62daded6eda46875603c Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Mon, 30 Sep 2024 14:15:32 -0600 Subject: [PATCH 10/21] rename cross var to ci --- Cargo.toml | 4 ++-- build.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 06d3e0f..9d771b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,8 +28,8 @@ webkit = ["ultralight"] ultralight = ["ul-next"] native-tls = ["reqwest/native-tls"] rustls-tls = ["reqwest/rustls-tls"] -# exists to prevent build.rs from failing build -cross = [] +# exists to prevent build.rs from failing build in ci/cd +ci = [] [dependencies] env_home = "0.1.0" diff --git a/build.rs b/build.rs index e8a76cc..d1bf233 100644 --- a/build.rs +++ b/build.rs @@ -5,7 +5,7 @@ use std::path::Path; fn main() { // ensure runtime resources exist #[cfg(feature = "ultralight")] - #[cfg(not(feature = "cross"))] + #[cfg(not(feature = "ci"))] { let out = var("OUT_DIR").unwrap(); // This allows it to work in this project but also other projects too From 48e94e7010c3da077e3c5dbc656abda096d5f9c6 Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Mon, 30 Sep 2024 23:13:42 -0400 Subject: [PATCH 11/21] fix paths for lib users --- Cargo.lock | 7 ------- Cargo.toml | 1 - src/engines/ultralight.rs | 22 +--------------------- 3 files changed, 1 insertion(+), 29 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9ef052c..9385847 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1013,12 +1013,6 @@ dependencies = [ "syn 2.0.77", ] -[[package]] -name = "env_home" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" - [[package]] name = "equivalent" version = "1.0.1" @@ -1963,7 +1957,6 @@ dependencies = [ name = "icy_browser" version = "0.1.0" dependencies = [ - "env_home", "iced", "iced_aw", "iced_fonts", diff --git a/Cargo.toml b/Cargo.toml index 9d771b5..1240766 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,6 @@ rustls-tls = ["reqwest/rustls-tls"] ci = [] [dependencies] -env_home = "0.1.0" iced = { version = "0.13", features = ["advanced", "image", "tokio", "lazy"] } iced_aw = { version = "0.10", features = ["tab_bar", "selection_list"] } iced_fonts = { version = "0.1.1", features = ["bootstrap"] } diff --git a/src/engines/ultralight.rs b/src/engines/ultralight.rs index 4eba7ae..9250951 100644 --- a/src/engines/ultralight.rs +++ b/src/engines/ultralight.rs @@ -15,9 +15,6 @@ use ul_next::{ }; use url::Url; -#[cfg(not(debug_assertions))] -use env_home::env_home_dir; - use super::{BrowserEngine, PixelFormat, Tab, TabInfo, Tabs}; struct UlLogger; @@ -61,26 +58,9 @@ impl Ultralight { pub fn new() -> Self { let config = Config::start().build().unwrap(); platform::enable_platform_fontloader(); - - #[cfg(not(debug_assertions))] - let mut home_dir = env_home_dir().unwrap(); - #[cfg(not(debug_assertions))] - home_dir.push(".icy_browser"); - #[cfg(not(debug_assertions))] - platform::enable_platform_filesystem(home_dir.as_path()).unwrap(); - - #[cfg(debug_assertions)] platform::enable_platform_filesystem(".").unwrap(); - platform::set_logger(UlLogger); - - #[cfg(not(debug_assertions))] - home_dir.push("logs.txt"); - #[cfg(not(debug_assertions))] - platform::enable_default_logger(home_dir.as_path()).unwrap(); - - #[cfg(debug_assertions)] - platform::enable_default_logger("./logs.txt").unwrap(); + platform::enable_default_logger("./log.txt").unwrap(); let renderer = Renderer::create(config).unwrap(); let view_config = ViewConfig::start() From 0bf3708c65668e935d482c7aca5f4b24fd245c1e Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Tue, 1 Oct 2024 00:03:18 -0400 Subject: [PATCH 12/21] fix build.rs --- Cargo.toml | 5 ++--- build.rs | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1240766..dc8a631 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,13 +23,12 @@ opt-level = "s" lto = "thin" [features] -default = ["webkit", "native-tls"] +default = ["webkit", "native-tls", "ultralight-resources"] webkit = ["ultralight"] ultralight = ["ul-next"] +ultralight-resources = [] native-tls = ["reqwest/native-tls"] rustls-tls = ["reqwest/rustls-tls"] -# exists to prevent build.rs from failing build in ci/cd -ci = [] [dependencies] iced = { version = "0.13", features = ["advanced", "image", "tokio", "lazy"] } diff --git a/build.rs b/build.rs index d1bf233..e487560 100644 --- a/build.rs +++ b/build.rs @@ -4,8 +4,7 @@ use std::path::Path; fn main() { // ensure runtime resources exist - #[cfg(feature = "ultralight")] - #[cfg(not(feature = "ci"))] + #[cfg(feature = "ultralight-resources")] { let out = var("OUT_DIR").unwrap(); // This allows it to work in this project but also other projects too @@ -74,6 +73,7 @@ fn main() { } } + println!("cargo:rerun-if-changed=target"); println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=Cargo.lock"); } From d2d7453ee862f8999492acbb0038b746e11bc296 Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Tue, 1 Oct 2024 01:36:24 -0400 Subject: [PATCH 13/21] remove logger --- src/engines/ultralight.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/engines/ultralight.rs b/src/engines/ultralight.rs index 9250951..2f7a78c 100644 --- a/src/engines/ultralight.rs +++ b/src/engines/ultralight.rs @@ -17,13 +17,6 @@ use url::Url; use super::{BrowserEngine, PixelFormat, Tab, TabInfo, Tabs}; -struct UlLogger; -impl Logger for UlLogger { - fn log_message(&mut self, log_level: LogLevel, message: String) { - println!("{:?}: {}", log_level, message); - } -} - pub struct UltalightTabInfo { surface: Surface, view: View, @@ -59,8 +52,6 @@ impl Ultralight { let config = Config::start().build().unwrap(); platform::enable_platform_fontloader(); platform::enable_platform_filesystem(".").unwrap(); - platform::set_logger(UlLogger); - platform::enable_default_logger("./log.txt").unwrap(); let renderer = Renderer::create(config).unwrap(); let view_config = ViewConfig::start() From a0d9ae9a457de4f79564b4e7a453d2a23f428f24 Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Tue, 1 Oct 2024 01:49:02 -0600 Subject: [PATCH 14/21] remove comment --- examples/custom_widgets.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/examples/custom_widgets.rs b/examples/custom_widgets.rs index 50551ed..b1f9454 100644 --- a/examples/custom_widgets.rs +++ b/examples/custom_widgets.rs @@ -85,13 +85,6 @@ impl Browser { } } -// fn custom_update<'a, Engine: BrowserEngine, CustomViewState>( -// browser_widget: &'a BrowserWidget, -// custom_view_state: &'a mut CustomViewState, -// ) -> Element<'a, icy_browser::Message> { - -// } - fn custom_view( browser_widget: &BrowserWidget, widget_state: CustomWidgetState, From cd24b21148c8e41fc218d1156f68e4a37b06fcd4 Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Tue, 1 Oct 2024 01:56:00 -0600 Subject: [PATCH 15/21] fix readme increase iced badge update example add bookmark bar to readme --- README.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 9cfb9dc..101c9b3 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,7 @@ ## Iced library to create custom browsers + [![Build](https://github.com/LegitCamper/icy_browser/actions/workflows/ci.yml/badge.svg)](https://github.com/LegitCamper/icy_browser/actions/workflows/ci.yml) - - -### Supported Platforms -| Platform | Support | -| Windows | | -| Linux | | - ### Supported Browser Engines | Browser Engine | Support | @@ -19,21 +13,26 @@ ### Browser Widgets - Navigation Bar - Tab Bar +- Bookmark Bar - Browser View ### Examples #### basic_browser.rs - + ``` Rust use iced::{Settings, Task, Theme}; -use icy_browser::{get_fonts, BasicBrowser, Message}; +use icy_browser::{get_fonts, BasicBrowser, Bookmark, Message}; fn run() -> (BasicBrowser, Task) { ( BasicBrowser::new_basic() .with_tab_bar() .with_nav_bar() + .with_bookmark_bar(vec![Bookmark::new( + "https://www.rust-lang.org", + "rust-lang.org", + )]) .build(), Task::none(), ) From c66c2d97a125d1d12b7d74a9ce30e20fbbc9bddb Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Thu, 3 Oct 2024 13:51:30 -0600 Subject: [PATCH 16/21] clipboard working --- Cargo.lock | 420 +++++++++++++++++++++++++++++++++++++- Cargo.toml | 1 + src/engines/ultralight.rs | 58 ++++-- 3 files changed, 460 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9385847..5336d3b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -69,6 +69,12 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" +[[package]] +name = "aligned-vec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" + [[package]] name = "allocator-api2" version = "0.2.18" @@ -117,6 +123,12 @@ dependencies = [ "libc", ] +[[package]] +name = "anyhow" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" + [[package]] name = "approx" version = "0.5.1" @@ -126,6 +138,23 @@ dependencies = [ "num-traits", ] +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" + +[[package]] +name = "arg_enum_proc_macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + [[package]] name = "arrayref" version = "0.3.9" @@ -308,6 +337,29 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +[[package]] +name = "av1-grain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6678909d8c5d46a42abcf571271e15fdbc0a225e3646cf23762cd415046c78bf" +dependencies = [ + "anyhow", + "arrayvec", + "log", + "nom", + "num-rational", + "v_frame", +] + +[[package]] +name = "avif-serialize" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876c75a42f6364451a033496a14c44bffe41f5f4a8236f697391f11024e596d2" +dependencies = [ + "arrayvec", +] + [[package]] name = "backtrace" version = "0.3.74" @@ -377,6 +429,12 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +[[package]] +name = "bitstream-io" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b81e1519b0d82120d2fd469d5bfb2919a9361c48b02d82d04befc1cdd2002452" + [[package]] name = "block" version = "0.1.6" @@ -414,6 +472,12 @@ dependencies = [ "piper", ] +[[package]] +name = "built" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "236e6289eda5a812bc6b53c3b024039382a2895fbbeef2d748b2931546d392c4" + [[package]] name = "bumpalo" version = "3.16.0" @@ -452,6 +516,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + [[package]] name = "bytes" version = "1.7.2" @@ -501,6 +571,16 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -533,6 +613,20 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "clipboard-rs" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd61885ce9f906c60f40d384b132fed04685f51d3da3576d27b8e4f274b6100d" +dependencies = [ + "clipboard-win", + "image 0.25.2", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "x11rb", +] + [[package]] name = "clipboard-win" version = "5.4.0" @@ -540,6 +634,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892" dependencies = [ "error-code", + "windows-win", ] [[package]] @@ -1746,7 +1841,7 @@ dependencies = [ "iced_renderer", "iced_widget", "iced_winit", - "image", + "image 0.24.9", "thiserror", ] @@ -1834,7 +1929,7 @@ dependencies = [ "half", "iced_core", "iced_futures", - "image", + "image 0.24.9", "kamadak-exif", "log", "lyon_path", @@ -1957,6 +2052,7 @@ dependencies = [ name = "icy_browser" version = "0.1.0" dependencies = [ + "clipboard-rs", "iced", "iced_aw", "iced_fonts", @@ -2001,6 +2097,45 @@ dependencies = [ "tiff", ] +[[package]] +name = "image" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99314c8a2152b8ddb211f924cdae532d8c5e4c8bb54728e12fff1b0cd5963a10" +dependencies = [ + "bytemuck", + "byteorder-lite", + "color_quant", + "exr", + "gif", + "image-webp", + "num-traits", + "png", + "qoi", + "ravif", + "rayon", + "rgb", + "tiff", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f79afb8cbee2ef20f59ccd477a218c12a93943d075b492015ecb1bb81f8ee904" +dependencies = [ + "byteorder-lite", + "quick-error", +] + +[[package]] +name = "imgref" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44feda355f4159a7c757171a77de25daf6411e217b4cabd03bd6650690468126" + [[package]] name = "indexmap" version = "2.5.0" @@ -2020,6 +2155,17 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "interpolate_name" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + [[package]] name = "ipnet" version = "2.10.0" @@ -2153,6 +2299,17 @@ version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +[[package]] +name = "libfuzzer-sys" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" +dependencies = [ + "arbitrary", + "cc", + "once_cell", +] + [[package]] name = "libloading" version = "0.7.4" @@ -2229,6 +2386,15 @@ version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + [[package]] name = "lru" version = "0.12.4" @@ -2305,6 +2471,15 @@ dependencies = [ "libc", ] +[[package]] +name = "maybe-rayon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +dependencies = [ + "cfg-if", +] + [[package]] name = "memchr" version = "2.7.4" @@ -2350,6 +2525,12 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.7.4" @@ -2463,6 +2644,12 @@ dependencies = [ "jni-sys", ] +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + [[package]] name = "nix" version = "0.29.0" @@ -2476,6 +2663,22 @@ dependencies = [ "memoffset", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "noop_proc_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" + [[package]] name = "nt-time" version = "0.8.1" @@ -2486,12 +2689,33 @@ dependencies = [ "time", ] +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + [[package]] name = "num-conv" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + [[package]] name = "num-format" version = "0.4.4" @@ -2502,6 +2726,26 @@ dependencies = [ "itoa", ] +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -3153,6 +3397,19 @@ name = "profiling" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58" +dependencies = [ + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd" +dependencies = [ + "quote", + "syn 2.0.77", +] [[package]] name = "qoi" @@ -3163,6 +3420,12 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + [[package]] name = "quick-xml" version = "0.36.2" @@ -3271,6 +3534,55 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f60fcc7d6849342eff22c4350c8b9a989ee8ceabc4b481253e8946b9fe83d684" +[[package]] +name = "rav1e" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" +dependencies = [ + "arbitrary", + "arg_enum_proc_macro", + "arrayvec", + "av1-grain", + "bitstream-io", + "built", + "cfg-if", + "interpolate_name", + "itertools 0.12.1", + "libc", + "libfuzzer-sys", + "log", + "maybe-rayon", + "new_debug_unreachable", + "noop_proc_macro", + "num-derive", + "num-traits", + "once_cell", + "paste", + "profiling", + "rand", + "rand_chacha", + "simd_helpers", + "system-deps", + "thiserror", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.11.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f0bfd976333248de2078d350bfdf182ff96e168a24d23d2436cef320dd4bdd" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error", + "rav1e", + "rgb", +] + [[package]] name = "raw-window-handle" version = "0.6.2" @@ -3399,6 +3711,15 @@ dependencies = [ "windows-registry", ] +[[package]] +name = "rgb" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" +dependencies = [ + "bytemuck", +] + [[package]] name = "ring" version = "0.17.8" @@ -3647,6 +3968,15 @@ dependencies = [ "syn 2.0.77", ] +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -3719,6 +4049,15 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote", +] + [[package]] name = "siphasher" version = "0.3.11" @@ -3982,6 +4321,25 @@ dependencies = [ "libc", ] +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck 0.5.0", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + [[package]] name = "tempfile" version = "3.12.0" @@ -4168,11 +4526,26 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + [[package]] name = "toml_datetime" version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] [[package]] name = "toml_edit" @@ -4181,6 +4554,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" dependencies = [ "indexmap", + "serde", + "serde_spanned", "toml_datetime", "winnow", ] @@ -4408,12 +4783,29 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "v_frame" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b" +dependencies = [ + "aligned-vec", + "num-traits", + "wasm-bindgen", +] + [[package]] name = "vcpkg" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + [[package]] name = "version_check" version = "0.9.5" @@ -4951,6 +5343,15 @@ dependencies = [ "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-win" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58e23e33622b3b52f948049acbec9bcc34bf6e26d74176b88941f213c75cf2dc" +dependencies = [ + "error-code", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -5333,6 +5734,12 @@ version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + [[package]] name = "zune-inflate" version = "0.2.54" @@ -5342,6 +5749,15 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "zune-jpeg" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16099418600b4d8f028622f73ff6e3deaabdff330fb9a2a131dea781ee8b0768" +dependencies = [ + "zune-core", +] + [[package]] name = "zvariant" version = "4.2.0" diff --git a/Cargo.toml b/Cargo.toml index dc8a631..1eead15 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ native-tls = ["reqwest/native-tls"] rustls-tls = ["reqwest/rustls-tls"] [dependencies] +clipboard-rs = "0.2.1" iced = { version = "0.13", features = ["advanced", "image", "tokio", "lazy"] } iced_aw = { version = "0.10", features = ["tab_bar", "selection_list"] } iced_fonts = { version = "0.1.1", features = ["bootstrap"] } diff --git a/src/engines/ultralight.rs b/src/engines/ultralight.rs index 2f7a78c..c8f47c6 100644 --- a/src/engines/ultralight.rs +++ b/src/engines/ultralight.rs @@ -1,3 +1,4 @@ +use clipboard_rs::{Clipboard, ClipboardContext, ContentFormat}; use iced::keyboard::{self}; use iced::mouse::{self, ScrollDelta}; use iced::{Point, Size}; @@ -7,7 +8,7 @@ use ul_next::{ config::Config, event::{self, KeyEventCreationInfo, MouseEvent, ScrollEvent}, key_code::VirtualKeyCode, - platform::{self, LogLevel, Logger}, + platform, renderer::Renderer, view::{View, ViewConfig}, window::Cursor, @@ -17,6 +18,22 @@ use url::Url; use super::{BrowserEngine, PixelFormat, Tab, TabInfo, Tabs}; +struct UlClipboard; +impl platform::Clipboard for UlClipboard { + fn clear(&mut self) {} + + fn read_plain_text(&mut self) -> Option { + let ctx = clipboard_rs::ClipboardContext::new().ok()?; + Some(ctx.get_text().unwrap_or("".to_string())) + } + + fn write_plain_text(&mut self, text: &str) { + let ctx = ClipboardContext::new().expect("Failed to open clipboard"); + ctx.set_text(text.into()) + .expect("Failed to set contents of clipboard"); + } +} + pub struct UltalightTabInfo { surface: Surface, view: View, @@ -52,6 +69,7 @@ impl Ultralight { let config = Config::start().build().unwrap(); platform::enable_platform_fontloader(); platform::enable_platform_filesystem(".").unwrap(); + platform::set_clipboard(UlClipboard); let renderer = Renderer::create(config).unwrap(); let view_config = ViewConfig::start() @@ -241,10 +259,11 @@ impl BrowserEngine for Ultralight { location, modifiers, text, - modified_key: _, + modified_key, physical_key: _, } => iced_key_to_ultralight_key( KeyPress::Press, + Some(modified_key), Some(key), Some(location), modifiers, @@ -256,13 +275,14 @@ impl BrowserEngine for Ultralight { modifiers, } => iced_key_to_ultralight_key( KeyPress::Unpress, + None, Some(key), Some(location), modifiers, None, ), keyboard::Event::ModifiersChanged(modifiers) => { - iced_key_to_ultralight_key(KeyPress::Press, None, None, modifiers, None) + iced_key_to_ultralight_key(KeyPress::Press, None, None, None, modifiers, None) } }; @@ -355,12 +375,13 @@ enum KeyPress { fn iced_key_to_ultralight_key( press: KeyPress, - key: Option, + modified_key: Option, + key: Option, // This one is modified by ctrl and results in wrong key _location: Option, modifiers: keyboard::Modifiers, text: Option, ) -> Option { - let (text, virtual_key, native_key) = { + let (mut text, virtual_key, native_key) = { if let Some(key) = key { let text = match key { keyboard::Key::Named(key) => { @@ -853,7 +874,16 @@ fn iced_key_to_ultralight_key( } }; - let ty = if !text.is_empty() && text.is_ascii() && press == KeyPress::Press { + let modifiers = event::KeyEventModifiers { + alt: modifiers.alt(), + ctrl: modifiers.control(), + meta: modifiers.logo(), + shift: modifiers.shift(), + }; + + let ty = if modifiers.ctrl == true { + event::KeyEventType::RawKeyDown + } else if !text.is_empty() && text.is_ascii() && press == KeyPress::Press { event::KeyEventType::Char } else { match press { @@ -862,25 +892,19 @@ fn iced_key_to_ultralight_key( } }; - let modifiers = event::KeyEventModifiers { - alt: modifiers.alt(), - ctrl: modifiers.control(), - meta: modifiers.logo(), - shift: modifiers.shift(), - }; - let creation_info = KeyEventCreationInfo { ty, modifiers, virtual_key_code: virtual_key, native_key_code: native_key, text: text.as_str(), - unmodified_text: text.as_str(), + unmodified_text: if let Some(keyboard::Key::Character(char)) = modified_key { + &char.to_string() + } else { + text.as_str() + }, is_keypad: false, is_auto_repeat: false, - #[cfg(windows)] - is_system_key: true, - #[cfg(not(windows))] is_system_key: false, }; From bac5a9a6b61be4400d9959a897c2d19ae7fa0abf Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Thu, 3 Oct 2024 13:54:35 -0600 Subject: [PATCH 17/21] cargo clippy --- src/engines/ultralight.rs | 6 +++--- src/widgets/bookmark_bar.rs | 2 +- src/widgets/command_window.rs | 8 ++++---- src/widgets/mod.rs | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/engines/ultralight.rs b/src/engines/ultralight.rs index c8f47c6..00adfc6 100644 --- a/src/engines/ultralight.rs +++ b/src/engines/ultralight.rs @@ -1,4 +1,4 @@ -use clipboard_rs::{Clipboard, ClipboardContext, ContentFormat}; +use clipboard_rs::{Clipboard, ClipboardContext}; use iced::keyboard::{self}; use iced::mouse::{self, ScrollDelta}; use iced::{Point, Size}; @@ -381,7 +381,7 @@ fn iced_key_to_ultralight_key( modifiers: keyboard::Modifiers, text: Option, ) -> Option { - let (mut text, virtual_key, native_key) = { + let (text, virtual_key, native_key) = { if let Some(key) = key { let text = match key { keyboard::Key::Named(key) => { @@ -881,7 +881,7 @@ fn iced_key_to_ultralight_key( shift: modifiers.shift(), }; - let ty = if modifiers.ctrl == true { + let ty = if modifiers.ctrl { event::KeyEventType::RawKeyDown } else if !text.is_empty() && text.is_ascii() && press == KeyPress::Press { event::KeyEventType::Char diff --git a/src/widgets/bookmark_bar.rs b/src/widgets/bookmark_bar.rs index 3567da1..dc707c4 100644 --- a/src/widgets/bookmark_bar.rs +++ b/src/widgets/bookmark_bar.rs @@ -4,7 +4,7 @@ use super::Message; use crate::Bookmark; /// Creates bookmark bar widget -pub fn bookmark_bar(bookmarks: &Vec) -> Element { +pub fn bookmark_bar(bookmarks: &[Bookmark]) -> Element { Row::from_vec( bookmarks .iter() diff --git a/src/widgets/command_window.rs b/src/widgets/command_window.rs index 63fb244..40af61f 100644 --- a/src/widgets/command_window.rs +++ b/src/widgets/command_window.rs @@ -5,10 +5,10 @@ use strum::IntoEnumIterator; use super::Message; -pub enum ResultType { - Command(Message), - // Bookmark, -} +// pub enum ResultType { +// Command(Message), +// // Bookmark, +// } pub struct CommandWindowState { pub query: String, diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index ae4fec2..a09b6b2 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -135,7 +135,7 @@ where use crate::engines::ultralight::Ultralight; #[cfg(feature = "ultralight")] -impl<'a, CustomViewState: Clone> BrowserWidget { +impl BrowserWidget { pub fn new_basic() -> BrowserWidget { BrowserWidget { engine: Some(Ultralight::new()), From 26bc7a4c92431a8f1047354e60ca8066c7eaf58f Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Thu, 3 Oct 2024 14:01:02 -0600 Subject: [PATCH 18/21] bookmarks as slice --- examples/basic_browser.rs | 5 +---- examples/keyboard_driven.rs | 2 +- src/lib.rs | 1 + src/widgets/mod.rs | 4 ++-- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/examples/basic_browser.rs b/examples/basic_browser.rs index c4cc16e..beae140 100644 --- a/examples/basic_browser.rs +++ b/examples/basic_browser.rs @@ -8,10 +8,7 @@ fn run() -> (BasicBrowser, Task) { BasicBrowser::new_basic() .with_tab_bar() .with_nav_bar() - .with_bookmark_bar(vec![Bookmark::new( - "https://www.rust-lang.org", - "rust-lang.org", - )]) + .with_bookmark_bar(&[Bookmark::new("https://www.rust-lang.org", "rust-lang.org")]) .build(), Task::none(), ) diff --git a/examples/keyboard_driven.rs b/examples/keyboard_driven.rs index 9474bf8..67f80aa 100644 --- a/examples/keyboard_driven.rs +++ b/examples/keyboard_driven.rs @@ -50,7 +50,7 @@ impl Default for Browser { let widgets = BrowserWidget::new_basic() .with_custom_shortcuts(shortcuts) .with_tab_bar() - .with_bookmark_bar(vec![ + .with_bookmark_bar(&[ Bookmark::new("https://www.rust-lang.org", "rust-lang.org"), Bookmark::new( "https://github.com/LegitCamper/icy_browser", diff --git a/src/lib.rs b/src/lib.rs index e859a63..93144da 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -98,6 +98,7 @@ fn to_url(url: &str) -> Option { } } +#[derive(Debug, Clone)] pub struct Bookmark { url: Url, name: String, diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index a09b6b2..17d4c48 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -172,8 +172,8 @@ where self } - pub fn with_bookmark_bar(mut self, bookmarks: Vec) -> Self { - self.bookmarks = Some(bookmarks); + pub fn with_bookmark_bar(mut self, bookmarks: &[Bookmark]) -> Self { + self.bookmarks = Some(bookmarks.to_vec()); self } From 6368b1086a48a5903e4b5ac8bff6a5a1ea6a28aa Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Thu, 3 Oct 2024 17:02:36 -0600 Subject: [PATCH 19/21] add remaining keys --- src/engines/ultralight.rs | 97 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 93 insertions(+), 4 deletions(-) diff --git a/src/engines/ultralight.rs b/src/engines/ultralight.rs index 00adfc6..fbd43f8 100644 --- a/src/engines/ultralight.rs +++ b/src/engines/ultralight.rs @@ -42,9 +42,7 @@ pub struct UltalightTabInfo { impl TabInfo for UltalightTabInfo { fn title(&self) -> String { - self.view - .title() - .expect("Failed to get title from ultralight") + self.view.title().unwrap_or("Title Error".to_string()) } fn url(&self) -> String { @@ -584,7 +582,7 @@ fn iced_key_to_ultralight_key( keyboard::key::Named::F12 => ( VirtualKeyCode::F12, #[cfg(windows)] - 122, + 123, #[cfg(unix)] 70, ), @@ -864,6 +862,97 @@ fn iced_key_to_ultralight_key( #[cfg(unix)] 39, ), + "-" => ( + VirtualKeyCode::OemMinus, + #[cfg(windows)] + 189, + #[cfg(unix)] + 12, + ), + "_" => ( + VirtualKeyCode::OemMinus, + #[cfg(windows)] + 189, + #[cfg(unix)] + 12, + ), + "+" => ( + VirtualKeyCode::OemPlus, + #[cfg(windows)] + 187, + #[cfg(unix)] + 78, + ), + "=" => ( + VirtualKeyCode::OemPlus, + #[cfg(windows)] + 187, + #[cfg(unix)] + 78, + ), + "\\" => ( + VirtualKeyCode::Oem5, + #[cfg(windows)] + 220, + #[cfg(unix)] + 43, + ), + "|" => ( + VirtualKeyCode::Oem5, + #[cfg(windows)] + 220, + #[cfg(unix)] + 43, + ), + "`" => ( + VirtualKeyCode::Oem3, + #[cfg(windows)] + 192, + #[cfg(unix)] + 41, + ), + "?" => ( + VirtualKeyCode::Oem2, + #[cfg(windows)] + 191, + #[cfg(unix)] + 53, + ), + "/" => ( + VirtualKeyCode::Oem2, + #[cfg(windows)] + 191, + #[cfg(unix)] + 53, + ), + ">" => ( + VirtualKeyCode::Oem102, + #[cfg(windows)] + 226, + #[cfg(unix)] + 52, + ), + "<" => ( + VirtualKeyCode::Oem102, + #[cfg(windows)] + 226, + #[cfg(unix)] + 52, + ), + "[" => ( + VirtualKeyCode::Oem4, + #[cfg(windows)] + 219, + #[cfg(unix)] + 26, + ), + "]" => ( + VirtualKeyCode::Oem6, + #[cfg(windows)] + 221, + #[cfg(unix)] + 27, + ), _ => return None, }, keyboard::Key::Unidentified => return None, From 5d697fb77cbea59260365571c91d002442caf910 Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Sun, 6 Oct 2024 14:20:36 -0600 Subject: [PATCH 20/21] refactor --- README.md | 15 +- examples/basic_browser.rs | 10 +- examples/custom_widgets.rs | 86 +++++------- examples/keyboard_driven.rs | 22 +-- src/helpers.rs | 102 ++++++++++++++ src/lib.rs | 109 +-------------- src/widgets/mod.rs | 266 ++++++++++++++---------------------- 7 files changed, 264 insertions(+), 346 deletions(-) create mode 100644 src/helpers.rs diff --git a/README.md b/README.md index 101c9b3..e3fdff5 100644 --- a/README.md +++ b/README.md @@ -22,17 +22,14 @@ ``` Rust use iced::{Settings, Task, Theme}; -use icy_browser::{get_fonts, BasicBrowser, Bookmark, Message}; +use icy_browser::{get_fonts, Bookmark, IcyBrowser, Message, Ultralight}; -fn run() -> (BasicBrowser, Task) { +fn run() -> (IcyBrowser, Task) { ( - BasicBrowser::new_basic() + IcyBrowser::new() .with_tab_bar() .with_nav_bar() - .with_bookmark_bar(vec![Bookmark::new( - "https://www.rust-lang.org", - "rust-lang.org", - )]) + .with_bookmark_bar(&[Bookmark::new("https://www.rust-lang.org", "rust-lang.org")]) .build(), Task::none(), ) @@ -44,8 +41,8 @@ fn main() -> iced::Result { ..Default::default() }; - iced::application("Basic Browser", BasicBrowser::update, BasicBrowser::view) - .subscription(BasicBrowser::subscription) + iced::application("Basic Browser", IcyBrowser::update, IcyBrowser::view) + .subscription(IcyBrowser::subscription) .settings(settings) .theme(|_| Theme::Dark) .run_with(run) diff --git a/examples/basic_browser.rs b/examples/basic_browser.rs index beae140..ee8d680 100644 --- a/examples/basic_browser.rs +++ b/examples/basic_browser.rs @@ -1,11 +1,11 @@ // Simple browser with familiar browser widgets and the ultralight(webkit) webengine as a backend use iced::{Settings, Task, Theme}; -use icy_browser::{get_fonts, BasicBrowser, Bookmark, Message}; +use icy_browser::{get_fonts, Bookmark, IcyBrowser, Message, Ultralight}; -fn run() -> (BasicBrowser, Task) { +fn run() -> (IcyBrowser, Task) { ( - BasicBrowser::new_basic() + IcyBrowser::new() .with_tab_bar() .with_nav_bar() .with_bookmark_bar(&[Bookmark::new("https://www.rust-lang.org", "rust-lang.org")]) @@ -20,8 +20,8 @@ fn main() -> iced::Result { ..Default::default() }; - iced::application("Basic Browser", BasicBrowser::update, BasicBrowser::view) - .subscription(BasicBrowser::subscription) + iced::application("Basic Browser", IcyBrowser::update, IcyBrowser::view) + .subscription(IcyBrowser::subscription) .settings(settings) .theme(|_| Theme::Dark) .run_with(run) diff --git a/examples/custom_widgets.rs b/examples/custom_widgets.rs index b1f9454..dd280fe 100644 --- a/examples/custom_widgets.rs +++ b/examples/custom_widgets.rs @@ -1,14 +1,11 @@ // Custom view example with rainbow border -use iced::widget::{column, container}; +use iced::widget::container; use iced::{time, Border}; use iced::{Color, Element, Length, Settings, Subscription, Task, Theme}; use std::time::{Duration, Instant}; -use icy_browser::{ - browser_view, get_fonts, hoverable, nav_bar, tab_bar, widgets, BrowserEngine, BrowserWidget, - Ultralight, -}; +use icy_browser::{get_fonts, widgets, IcyBrowser, Ultralight}; fn main() -> iced::Result { let settings = Settings { @@ -37,29 +34,26 @@ struct CustomWidgetState { } struct Browser { - widgets: BrowserWidget, + icy_browser: IcyBrowser, + custom_widget_state: CustomWidgetState, } impl Default for Browser { fn default() -> Self { Self { - widgets: BrowserWidget::new() - .with_custom_view( - custom_view, - CustomWidgetState { - border_colors: vec![ - Color::from_rgb(1.0, 0.0, 0.0), // Red - Color::from_rgb(1.0, 0.5, 0.0), // Orange - Color::from_rgb(1.0, 1.0, 0.0), // Yellow - Color::from_rgb(0.0, 1.0, 0.0), // Green - Color::from_rgb(0.0, 0.0, 1.0), // Blue - Color::from_rgb(0.29, 0.0, 0.51), // Indigo - Color::from_rgb(0.56, 0.0, 1.0), // Violet - ], - start_time: Instant::now(), - }, - ) - .build(), + icy_browser: IcyBrowser::new().with_tab_bar().with_nav_bar().build(), + custom_widget_state: CustomWidgetState { + border_colors: vec![ + Color::from_rgb(1.0, 0.0, 0.0), // Red + Color::from_rgb(1.0, 0.5, 0.0), // Orange + Color::from_rgb(1.0, 1.0, 0.0), // Yellow + Color::from_rgb(0.0, 1.0, 0.0), // Green + Color::from_rgb(0.0, 0.0, 1.0), // Blue + Color::from_rgb(0.29, 0.0, 0.51), // Indigo + Color::from_rgb(0.56, 0.0, 1.0), // Violet + ], + start_time: Instant::now(), + }, } } } @@ -67,14 +61,26 @@ impl Default for Browser { 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::BrowserWidget(msg) => self.icy_browser.update(msg).map(Message::BrowserWidget), + Message::Update => self.icy_browser.force_update().map(Message::BrowserWidget), Message::Tick => Task::none(), // Tick } } fn view(&self) -> Element { - self.widgets.view().map(Message::BrowserWidget) + let elapsed = self.custom_widget_state.start_time.elapsed().as_secs_f32(); + let color_index = (elapsed * 2.0) as usize % self.custom_widget_state.border_colors.len(); + let color = self.custom_widget_state.border_colors[color_index]; + + container(self.icy_browser.view().map(Message::BrowserWidget)) + .center_x(Length::Fill) + .center_y(Length::Fill) + .padding(20) + .style(move |_theme| container::Style { + border: Border::default().color(color).width(20), + ..Default::default() + }) + .into() } fn subscription(&self) -> Subscription { @@ -84,31 +90,3 @@ impl Browser { ]) } } - -fn custom_view( - browser_widget: &BrowserWidget, - widget_state: CustomWidgetState, -) -> Element { - let elapsed = widget_state.start_time.elapsed().as_secs_f32(); - let color_index = (elapsed * 2.0) as usize % widget_state.border_colors.len(); - let color = widget_state.border_colors[color_index]; - - container(column![ - tab_bar(browser_widget.engine().get_tabs()), - hoverable(nav_bar(&browser_widget.nav_bar_state)) - .on_focus_change(icy_browser::Message::UpdateUrl), - browser_view( - browser_widget.view_size, - browser_widget.engine().get_tabs().get_current().get_view(), - !browser_widget.show_overlay, - ), - ]) - .center_x(Length::Fill) - .center_y(Length::Fill) - .padding(20) - .style(move |_theme| container::Style { - border: Border::default().color(color).width(20), - ..Default::default() - }) - .into() -} diff --git a/examples/keyboard_driven.rs b/examples/keyboard_driven.rs index 67f80aa..9b948cc 100644 --- a/examples/keyboard_driven.rs +++ b/examples/keyboard_driven.rs @@ -6,8 +6,8 @@ use iced::{Element, Settings, Subscription, Task}; use std::time::Duration; use icy_browser::{ - get_fonts, widgets, BasicBrowser, Bookmark, BrowserWidget, KeyType, Message as WidgetMessage, - ShortcutBuilder, ShortcutModifier, + get_fonts, widgets, Bookmark, IcyBrowser, KeyType, Message as WidgetMessage, ShortcutBuilder, + ShortcutModifier, Ultralight, }; fn main() -> iced::Result { @@ -33,7 +33,7 @@ pub enum Message { } struct Browser { - widgets: BasicBrowser, + icy_browser: IcyBrowser, } impl Default for Browser { @@ -47,7 +47,7 @@ impl Default for Browser { ], ) .build(); - let widgets = BrowserWidget::new_basic() + let widgets = IcyBrowser::new() .with_custom_shortcuts(shortcuts) .with_tab_bar() .with_bookmark_bar(&[ @@ -60,24 +60,26 @@ impl Default for Browser { ]) .build(); - Self { widgets } + Self { + icy_browser: 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::BrowserWidget(msg) => self.icy_browser.update(msg).map(Message::BrowserWidget), + Message::Update => self.icy_browser.force_update().map(Message::BrowserWidget), Message::Event(event) => self - .widgets - .update(widgets::Message::Event(Some(event))) + .icy_browser + .update(widgets::Message::IcedEvent(Some(event))) .map(Message::BrowserWidget), } } fn view(&self) -> Element { - self.widgets.view().map(Message::BrowserWidget) + self.icy_browser.view().map(Message::BrowserWidget) } fn subscription(&self) -> Subscription { diff --git a/src/helpers.rs b/src/helpers.rs new file mode 100644 index 0000000..9fd9993 --- /dev/null +++ b/src/helpers.rs @@ -0,0 +1,102 @@ +use iced::widget::{ + button, + image::{Handle, Image}, + Button, +}; +pub use iced_fonts::BOOTSTRAP_FONT_BYTES; +use std::{borrow::Cow, str::FromStr}; +use url::{ParseError, Url}; + +use super::{Message, PixelFormat}; + +// Helper function to ensure required icons are imported +pub fn get_fonts() -> Vec> { + vec![BOOTSTRAP_FONT_BYTES.into()] +} + +// Image details for passing the view around +#[derive(Debug, Clone)] +pub struct ImageInfo { + pub pixels: Vec, + pub width: u32, + pub height: u32, +} + +impl Default for ImageInfo { + fn default() -> Self { + Self { + pixels: vec![255; (Self::WIDTH as usize * Self::HEIGHT as usize) * 4], + width: Self::WIDTH, + height: Self::HEIGHT, + } + } +} + +impl ImageInfo { + // The default dimentions + const WIDTH: u32 = 800; + const HEIGHT: u32 = 800; + + pub fn new(pixels: Vec, format: PixelFormat, width: u32, height: u32) -> Self { + // R, G, B, A + assert_eq!(pixels.len() % 4, 0); + + let pixels = match format { + PixelFormat::Rgba => pixels, + PixelFormat::Bgra => pixels + .chunks(4) + .flat_map(|chunk| [chunk[2], chunk[1], chunk[0], chunk[3]]) + .collect(), + }; + + Self { + pixels, + width, + height, + } + } + + pub fn as_image(&self) -> Image { + Image::new(Handle::from_rgba( + self.width, + self.height, + self.pixels.clone(), + )) + } +} + +pub fn to_url(url: &str) -> Option { + match Url::parse(url) { + Ok(url) => Some(url), + Err(error) => { + if let ParseError::RelativeUrlWithoutBase = error { + let mut base = String::from("https://"); + base.push_str(url); + Url::parse(&base).ok() + } else { + None + } + } + } +} + +pub type Bookmarks = Vec; + +#[derive(Debug, Clone)] +pub struct Bookmark { + url: Url, + name: String, + // icon: Optional<> +} +impl Bookmark { + pub fn new(url: &str, name: &str) -> Self { + Bookmark { + url: Url::from_str(url).expect("Failed to parse url from bookmark url"), + name: name.to_string(), + } + } + + pub fn as_button(&self) -> Button { + button(self.name.as_str()).on_press(Message::GoToUrl(self.url.to_string())) + } +} diff --git a/src/lib.rs b/src/lib.rs index 93144da..a14bb4f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,12 +1,5 @@ -use iced::widget::{ - button, - image::{Handle, Image}, - Button, -}; pub use iced_fonts::BOOTSTRAP_FONT_BYTES; pub use iced_on_focus_widget::hoverable; -use std::{borrow::Cow, str::FromStr}; -use url::{ParseError, Url}; mod engines; pub use engines::{BrowserEngine, PixelFormat, Tab, TabInfo, Tabs}; @@ -15,104 +8,10 @@ pub use engines::{BrowserEngine, PixelFormat, Tab, TabInfo, Tabs}; pub use engines::ultralight::Ultralight; pub mod widgets; -pub use widgets::{browser_view, command_window, nav_bar, tab_bar, BrowserWidget, Message}; +pub use widgets::{browser_view, command_window, nav_bar, tab_bar, IcyBrowser, Message}; + +mod helpers; +pub use helpers::{get_fonts, to_url, Bookmark, Bookmarks, ImageInfo}; mod shortcut; pub use shortcut::{KeyType, Shortcut, ShortcutBuilder, ShortcutModifier, Shortcuts}; - -// Used when Not using custom views -#[derive(Clone)] -pub struct NoCustomView; - -#[cfg(feature = "ultralight")] -pub type BasicBrowser = BrowserWidget; - -// Helper function to ensure required icons are imported -pub fn get_fonts() -> Vec> { - vec![BOOTSTRAP_FONT_BYTES.into()] -} - -// Image details for passing the view around -#[derive(Debug, Clone)] -pub struct ImageInfo { - pixels: Vec, - width: u32, - height: u32, -} - -impl Default for ImageInfo { - fn default() -> Self { - Self { - pixels: vec![255; (Self::WIDTH as usize * Self::HEIGHT as usize) * 4], - width: Self::WIDTH, - height: Self::HEIGHT, - } - } -} - -impl ImageInfo { - // The default dimentions - const WIDTH: u32 = 800; - const HEIGHT: u32 = 800; - - pub fn new(pixels: Vec, format: PixelFormat, width: u32, height: u32) -> Self { - // R, G, B, A - assert_eq!(pixels.len() % 4, 0); - - let pixels = match format { - PixelFormat::Rgba => pixels, - PixelFormat::Bgra => pixels - .chunks(4) - .flat_map(|chunk| [chunk[2], chunk[1], chunk[0], chunk[3]]) - .collect(), - }; - - Self { - pixels, - width, - height, - } - } - - fn as_image(&self) -> Image { - Image::new(Handle::from_rgba( - self.width, - self.height, - self.pixels.clone(), - )) - } -} - -fn to_url(url: &str) -> Option { - match Url::parse(url) { - Ok(url) => Some(url), - Err(error) => { - if let ParseError::RelativeUrlWithoutBase = error { - let mut base = String::from("https://"); - base.push_str(url); - Url::parse(&base).ok() - } else { - None - } - } - } -} - -#[derive(Debug, Clone)] -pub struct Bookmark { - url: Url, - name: String, - // icon: Optional<> -} -impl Bookmark { - pub fn new(url: &str, name: &str) -> Self { - Bookmark { - url: Url::from_str(url).expect("Failed to parse url from bookmark url"), - name: name.to_string(), - } - } - - pub fn as_button(&self) -> Button { - button(self.name.as_str()).on_press(Message::GoToUrl(self.url.to_string())) - } -} diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index 17d4c48..12f6a0f 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -1,8 +1,7 @@ use command_window::CommandWindowState; -use iced::event::{self, Event}; use iced::keyboard::{self, key}; use iced::widget::{self, column}; -use iced::{mouse, Element, Point, Size, Subscription, Task}; +use iced::{mouse, Element, Event, Point, Size, Subscription, Task}; use iced_on_focus_widget::hoverable; use nav_bar::NavBarState; use std::string::ToString; @@ -13,20 +12,35 @@ use url::Url; mod browser_view; pub use browser_view::browser_view; -mod nav_bar; +pub mod nav_bar; pub use nav_bar::nav_bar; -mod tab_bar; +pub mod tab_bar; pub use tab_bar::tab_bar; -mod bookmark_bar; +pub mod bookmark_bar; pub use bookmark_bar::bookmark_bar; -mod command_window; +pub mod command_window; pub use command_window::command_window; -use crate::Bookmark; -use crate::{engines::BrowserEngine, shortcut::check_shortcut, to_url, ImageInfo, Shortcuts}; +use crate::{ + engines::BrowserEngine, shortcut::check_shortcut, to_url, Bookmark, Bookmarks, ImageInfo, + Shortcuts, +}; + +/// Allows users to implement their own custom view view with custom widgets and configurations +pub trait CustomWidget { + fn update(&mut self, message: Message); + fn view( + &self, + nav_bar_state: NavBarState, + command_window_state: CommandWindowState, + bookmarks: Option>, + shortcuts: Shortcuts, + ); + fn subscription(&self) -> Subscription; +} // Options exist only to have defaults for EnumIter #[derive(Debug, Clone, PartialEq, Display, EnumIter)] @@ -66,7 +80,7 @@ pub enum Message { SendKeyboardEvent(Option), SendMouseEvent(Point, Option), UpdateViewSize(Size), - Event(Option), + IcedEvent(Option), } /// Allows different widgets to interact in their native way @@ -81,46 +95,30 @@ impl Default for TabSelectionType { } } -/// Allows users to create their own views with the same information of native widgets -pub type CustomView = fn( - &BrowserWidget, - custom_view_state: CustomViewState, -) -> Element; - -/// Allows users to create their own updates with the same information of native widgets -pub type CustomUpdate<'a, Engine, CustomViewState> = fn( - &BrowserWidget, - custom_view_state: &mut CustomViewState, -) -> Element<'a, Message>; - -pub struct BrowserWidget { - pub engine: Option, - pub home: Url, - pub nav_bar_state: NavBarState, - pub command_window_state: CommandWindowState, - custom_view: Option>, - custom_view_state: Option, - pub with_tab_bar: bool, - pub with_nav_bar: bool, - pub bookmarks: Option>, - pub show_overlay: bool, - pub shortcuts: Shortcuts, - pub view_size: Size, +pub struct IcyBrowser { + engine: Engine, + home: Url, + nav_bar_state: NavBarState, + command_window_state: CommandWindowState, + with_tab_bar: bool, + with_nav_bar: bool, + bookmarks: Option, + show_overlay: bool, + shortcuts: Shortcuts, + view_size: Size, } -impl Default for BrowserWidget +impl Default for IcyBrowser where Engine: BrowserEngine, { fn default() -> Self { let home = Url::parse(Self::HOME).unwrap(); Self { - engine: None, + engine: Engine::new(), home, nav_bar_state: NavBarState::new(), command_window_state: CommandWindowState::new(), - custom_view: None, - custom_view_state: None, with_tab_bar: false, with_nav_bar: false, bookmarks: None, @@ -131,28 +129,12 @@ where } } -#[cfg(feature = "ultralight")] -use crate::engines::ultralight::Ultralight; - -#[cfg(feature = "ultralight")] -impl BrowserWidget { - pub fn new_basic() -> BrowserWidget { - BrowserWidget { - engine: Some(Ultralight::new()), - ..BrowserWidget::default() - } - } -} - -impl BrowserWidget -where - Engine: BrowserEngine, -{ +impl IcyBrowser { const HOME: &'static str = "https://google.com"; pub fn new() -> Self { Self { - engine: Some(Engine::new()), + engine: Engine::new(), ..Default::default() } } @@ -182,51 +164,34 @@ where self } - pub fn with_custom_view( - mut self, - custom_view: CustomView, - custom_view_state: CustomViewState, - ) -> Self { - self.custom_view = Some(custom_view); - self.custom_view_state = Some(custom_view_state); - self - } - pub fn build(self) -> Self { - assert!(self.engine.is_some()); - let mut build = Self { ..self }; let _ = build.update(Message::CreateTab); // disregaurd task::none() for update build } + /// Allows creation of custom widgets that need interal info pub fn engine(&self) -> &Engine { - self.engine - .as_ref() - .expect("Browser was created without a backend engine!") + &self.engine } - fn engine_mut(&mut self) -> &mut Engine { - self.engine - .as_mut() - .expect("Browser was created without a backend engine!") + /// Allows creation of custom widgets that need interal info + pub fn mut_engine(&mut self) -> &mut Engine { + &mut self.engine } 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(); + self.engine.do_work(); + if self.engine.has_loaded() { + if self.engine.need_render() { + let (format, image_data) = self.engine.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) + self.engine.get_tabs_mut().get_current_mut().set_view(view) } } else { let view = ImageInfo { @@ -234,118 +199,108 @@ where height: self.view_size.height, ..Default::default() }; - self.engine_mut() - .get_tabs_mut() - .get_current_mut() - .set_view(view) + self.engine.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(); + self.engine.do_work(); + let (format, image_data) = self.engine.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); + self.engine.get_tabs_mut().get_current_mut().set_view(view); Task::none() } - pub fn update(&mut self, message: Message) -> Task { - let task = match message { + pub fn update(&mut self, event: Message) -> Task { + let task = match event { Message::Update => self.force_update(), Message::UpdateViewSize(size) => { self.view_size = size; - self.engine_mut().resize(size); + self.engine.resize(size); Task::none() } Message::SendKeyboardEvent(event) => { - self.engine() + self.engine .handle_keyboard_event(event.expect("Value cannot be none")); Task::none() } Message::SendMouseEvent(point, event) => { - self.engine_mut() + self.engine .handle_mouse_event(point, event.expect("Value cannot be none")); Task::none() } Message::ChangeTab(index_type) => { let id = match index_type { TabSelectionType::Id(id) => id, - TabSelectionType::Index(index) => { - self.engine_mut().get_tabs().index_to_id(index) - } + TabSelectionType::Index(index) => self.engine.get_tabs().index_to_id(index), }; - self.engine_mut().get_tabs_mut().set_current_id(id); - self.nav_bar_state.0 = self.engine().get_tabs().get_current().url(); + self.engine.get_tabs_mut().set_current_id(id); + self.nav_bar_state.0 = self.engine.get_tabs().get_current().url(); Task::none() } Message::CloseCurrentTab => Task::done(Message::CloseTab(TabSelectionType::Id( - self.engine().get_tabs().get_current_id(), + self.engine.get_tabs().get_current_id(), ))), Message::CloseTab(index_type) => { // ensure there is always at least one tab - if self.engine().get_tabs().tabs().len() == 1 { + if self.engine.get_tabs().tabs().len() == 1 { let _ = self.update(Message::CreateTab); // ignore task } let id = match index_type { TabSelectionType::Id(id) => id, - TabSelectionType::Index(index) => { - self.engine_mut().get_tabs().index_to_id(index) - } + TabSelectionType::Index(index) => self.engine.get_tabs().index_to_id(index), }; - self.engine_mut().get_tabs_mut().remove(id); - self.nav_bar_state.0 = self.engine().get_tabs().get_current().url(); + self.engine.get_tabs_mut().remove(id); + self.nav_bar_state.0 = self.engine.get_tabs().get_current().url(); Task::none() } Message::CreateTab => { 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( + let tab = self.engine.new_tab( home.clone(), Size::new(bounds.width + 10, bounds.height - 10), ); - let id = self.engine_mut().get_tabs_mut().insert(tab); - self.engine_mut().get_tabs_mut().set_current_id(id); - self.engine_mut().force_need_render(); - self.engine_mut().resize(bounds); - self.engine().goto_url(&home); + let id = self.engine.get_tabs_mut().insert(tab); + self.engine.get_tabs_mut().set_current_id(id); + self.engine.force_need_render(); + self.engine.resize(bounds); + self.engine.goto_url(&home); Task::none() } Message::GoBackward => { - self.engine().go_back(); - self.nav_bar_state.0 = self.engine().get_tabs().get_current().url(); + self.engine.go_back(); + self.nav_bar_state.0 = self.engine.get_tabs().get_current().url(); Task::none() } Message::GoForward => { - self.engine().go_forward(); - self.nav_bar_state.0 = self.engine().get_tabs().get_current().url(); + self.engine.go_forward(); + self.nav_bar_state.0 = self.engine.get_tabs().get_current().url(); Task::none() } Message::Refresh => { - self.engine().refresh(); + self.engine.refresh(); Task::none() } Message::GoHome => { - self.engine().goto_url(&self.home); + self.engine.goto_url(&self.home); Task::none() } Message::GoToUrl(url) => { - self.engine().goto_url(&to_url(&url).unwrap()); + self.engine.goto_url(&to_url(&url).unwrap()); Task::none() } Message::UpdateUrl => { - self.nav_bar_state.0 = self.engine().get_tabs().get_current().url(); + self.nav_bar_state.0 = self.engine.get_tabs().get_current().url(); Task::none() } Message::UrlChanged(url) => { @@ -379,7 +334,7 @@ where self.show_overlay = false; widget::focus_next() } - Message::Event(event) => { + Message::IcedEvent(event) => { match event { Some(Event::Keyboard(key)) => { if let iced::keyboard::Event::KeyPressed { @@ -424,52 +379,37 @@ where } pub fn view(&self) -> Element { - match self.custom_view { - Some(custom_view) => { - if let Some(state) = &self.custom_view_state { - custom_view(self, state.clone()) - } else { - panic!( - "If Custom View is set, Custom View State must be set too. Use () if unused", - ) - } - } - None => { - let mut column = column![]; - - if self.with_tab_bar { - column = column.push(tab_bar(self.engine().get_tabs())) - } - if self.with_nav_bar { - column = column.push( - hoverable(nav_bar(&self.nav_bar_state)).on_focus_change(Message::UpdateUrl), - ) - } - if let Some(bookmarks) = self.bookmarks.as_ref() { - column = column.push(bookmark_bar(bookmarks)) - } + let mut column = column![]; - let browser_view = browser_view( - self.view_size, - self.engine().get_tabs().get_current().get_view(), - !self.show_overlay, - ); - if self.show_overlay { - column = column.push(command_window(browser_view, &self.command_window_state)) - } else { - column = column.push(browser_view); - } + if self.with_tab_bar { + column = column.push(tab_bar(self.engine.get_tabs())) + } + if self.with_nav_bar { + column = column + .push(hoverable(nav_bar(&self.nav_bar_state)).on_focus_change(Message::UpdateUrl)) + } + if let Some(bookmarks) = self.bookmarks.as_ref() { + column = column.push(bookmark_bar(bookmarks)) + } - column.into() - } + let browser_view = browser_view( + self.view_size, + self.engine.get_tabs().get_current().get_view(), + !self.show_overlay, + ); + if self.show_overlay { + column = column.push(command_window(browser_view, &self.command_window_state)) + } else { + column = column.push(browser_view); } + + column.into() } pub 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(|e: Event| Message::Event(Some(e))), + iced::event::listen().map(|e: iced::Event| Message::IcedEvent(Some(e))), ]) } } From 9b094d9be9eb7436cc6373c809f403baa7b3d27b Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Sun, 6 Oct 2024 14:44:45 -0600 Subject: [PATCH 21/21] fix widgets from crashing ultralight --- build.rs | 2 +- src/widgets/mod.rs | 49 ++++++++++++++++++++++++++++------------------ 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/build.rs b/build.rs index e487560..c85e226 100644 --- a/build.rs +++ b/build.rs @@ -3,7 +3,7 @@ use std::fs::{self, DirEntry}; use std::path::Path; fn main() { - // ensure runtime resources exist + // ensure runtime resources exist - for examples & local tests #[cfg(feature = "ultralight-resources")] { let out = var("OUT_DIR").unwrap(); diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index 12f6a0f..b524bdb 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -98,7 +98,7 @@ impl Default for TabSelectionType { pub struct IcyBrowser { engine: Engine, home: Url, - nav_bar_state: NavBarState, + nav_bar_state: Option, command_window_state: CommandWindowState, with_tab_bar: bool, with_nav_bar: bool, @@ -108,16 +108,13 @@ pub struct IcyBrowser { view_size: Size, } -impl Default for IcyBrowser -where - Engine: BrowserEngine, -{ +impl Default for IcyBrowser { fn default() -> Self { let home = Url::parse(Self::HOME).unwrap(); Self { engine: Engine::new(), home, - nav_bar_state: NavBarState::new(), + nav_bar_state: None, command_window_state: CommandWindowState::new(), with_tab_bar: false, with_nav_bar: false, @@ -133,10 +130,7 @@ impl IcyBrowser { const HOME: &'static str = "https://google.com"; pub fn new() -> Self { - Self { - engine: Engine::new(), - ..Default::default() - } + Self::default() } pub fn with_homepage(mut self, homepage: &str) -> Self { @@ -151,6 +145,7 @@ impl IcyBrowser { pub fn with_nav_bar(mut self) -> Self { self.with_nav_bar = true; + self.nav_bar_state = Some(NavBarState::new()); self } @@ -242,7 +237,9 @@ impl IcyBrowser { TabSelectionType::Index(index) => self.engine.get_tabs().index_to_id(index), }; self.engine.get_tabs_mut().set_current_id(id); - self.nav_bar_state.0 = self.engine.get_tabs().get_current().url(); + if let Some(state) = self.nav_bar_state.as_mut() { + state.0 = self.engine.get_tabs().get_current().url(); + } Task::none() } Message::CloseCurrentTab => Task::done(Message::CloseTab(TabSelectionType::Id( @@ -259,11 +256,15 @@ impl IcyBrowser { TabSelectionType::Index(index) => self.engine.get_tabs().index_to_id(index), }; self.engine.get_tabs_mut().remove(id); - self.nav_bar_state.0 = self.engine.get_tabs().get_current().url(); + if let Some(state) = self.nav_bar_state.as_mut() { + state.0 = self.engine.get_tabs().get_current().url(); + } Task::none() } Message::CreateTab => { - self.nav_bar_state.0 = self.home.to_string(); + if let Some(state) = self.nav_bar_state.as_mut() { + state.0 = self.home.to_string(); + } let home = self.home.clone(); let bounds = self.view_size; let tab = self.engine.new_tab( @@ -279,12 +280,16 @@ impl IcyBrowser { } Message::GoBackward => { self.engine.go_back(); - self.nav_bar_state.0 = self.engine.get_tabs().get_current().url(); + if let Some(state) = self.nav_bar_state.as_mut() { + state.0 = self.engine.get_tabs().get_current().url(); + } Task::none() } Message::GoForward => { self.engine.go_forward(); - self.nav_bar_state.0 = self.engine.get_tabs().get_current().url(); + if let Some(state) = self.nav_bar_state.as_mut() { + state.0 = self.engine.get_tabs().get_current().url(); + } Task::none() } Message::Refresh => { @@ -300,11 +305,15 @@ impl IcyBrowser { Task::none() } Message::UpdateUrl => { - self.nav_bar_state.0 = self.engine.get_tabs().get_current().url(); + if let Some(state) = self.nav_bar_state.as_mut() { + state.0 = self.engine.get_tabs().get_current().url(); + } Task::none() } Message::UrlChanged(url) => { - self.nav_bar_state.0 = url; + if let Some(state) = self.nav_bar_state.as_mut() { + state.0 = url; + } Task::none() } Message::QueryChanged(query) => { @@ -385,8 +394,10 @@ impl IcyBrowser { column = column.push(tab_bar(self.engine.get_tabs())) } if self.with_nav_bar { - column = column - .push(hoverable(nav_bar(&self.nav_bar_state)).on_focus_change(Message::UpdateUrl)) + column = column.push( + hoverable(nav_bar(self.nav_bar_state.as_ref().unwrap())) + .on_focus_change(Message::UpdateUrl), + ) } if let Some(bookmarks) = self.bookmarks.as_ref() { column = column.push(bookmark_bar(bookmarks))