From b8a9aedc60fcaa8478184c95c227216bb13fe2d9 Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Sun, 18 Aug 2024 20:16:18 -0600 Subject: [PATCH] fix new tabs image being warped --- examples/basic_browser.rs | 4 +- src/engines/mod.rs | 11 +- src/engines/ultralight.rs | 38 ++--- src/lib.rs | 6 +- src/widgets/browser_view.rs | 19 +-- src/widgets/browser_widgets.rs | 240 ------------------------------ src/widgets/mod.rs | 257 ++++++++++++++++++++++++++++++++- src/widgets/nav_bar.rs | 2 +- src/widgets/tab_bar.rs | 9 +- 9 files changed, 291 insertions(+), 295 deletions(-) delete mode 100644 src/widgets/browser_widgets.rs diff --git a/examples/basic_browser.rs b/examples/basic_browser.rs index 256f156..50cf393 100644 --- a/examples/basic_browser.rs +++ b/examples/basic_browser.rs @@ -3,7 +3,7 @@ use iced::{Sandbox, Settings, Theme}; use iced_aw::BOOTSTRAP_FONT_BYTES; -use icy_browser::{browser_widgets, BrowserWidget, Ultralight}; +use icy_browser::{widgets, BrowserWidget, Ultralight}; fn main() -> Result<(), iced::Error> { // This imports `icons` for widgets @@ -21,7 +21,7 @@ struct Browser { #[derive(Debug, Clone)] pub enum Message { - BrowserWidget(browser_widgets::Message), + BrowserWidget(widgets::Message), } impl Sandbox for Browser { diff --git a/src/engines/mod.rs b/src/engines/mod.rs index b91ce5c..ec7b5a0 100644 --- a/src/engines/mod.rs +++ b/src/engines/mod.rs @@ -1,7 +1,6 @@ use iced::keyboard; use iced::mouse::{self, Interaction}; use iced::Size; -use std::sync::{Arc, RwLock}; // use iced::widget::image::{Handle, Image}; use iced::Point; use rand::Rng; @@ -28,14 +27,14 @@ pub trait BrowserEngine { fn force_need_render(&self); fn render(&mut self); fn size(&self) -> (u32, u32); - fn resize(&mut self, size: Size); + fn resize(&mut self, size: Size); fn pixel_buffer(&mut self) -> (PixelFormat, Vec); fn get_cursor(&self) -> Interaction; // fn get_icon(&self) -> Image; fn goto_url(&self, url: &Url); fn has_loaded(&self) -> bool; - fn new_tab(&mut self, url: Url, size: Size) -> Tab; + fn new_tab(&mut self, url: Url, size: Size) -> Tab; fn get_tabs(&self) -> &Tabs; fn get_tabs_mut(&mut self) -> &mut Tabs; @@ -61,19 +60,15 @@ pub trait TabInfo { // to automatically update it when it changes pub struct Tab { id: u32, - _url: Arc>, - _title: Arc>, view: ImageInfo, info: Info, } impl Tab { - pub fn new(_url: Arc>, _title: Arc>, info: Info) -> Self { + pub fn new(info: Info) -> Self { let id = rand::thread_rng().gen(); Self { id, - _url, - _title, view: ImageInfo::default(), info, } diff --git a/src/engines/ultralight.rs b/src/engines/ultralight.rs index f5bd287..ec262df 100644 --- a/src/engines/ultralight.rs +++ b/src/engines/ultralight.rs @@ -126,13 +126,11 @@ impl BrowserEngine for Ultralight { } fn size(&self) -> (u32, u32) { - ( - self.tabs.get_current().info.view.width(), - self.tabs.get_current().info.view.height(), - ) + let view = &self.tabs.get_current().info.view; + (view.width(), view.height()) } - fn resize(&mut self, size: Size) { + fn resize(&mut self, size: Size) { let (width, height) = (size.width as u32, size.height as u32); self.tabs.tabs.iter().for_each(|tab| { tab.info.view.resize(width, height); @@ -181,15 +179,10 @@ impl BrowserEngine for Ultralight { &mut self.tabs } - fn new_tab(&mut self, url: Url, size: Size) -> Tab { + fn new_tab(&mut self, url: Url, size: Size) -> Tab { let view = self .renderer - .create_view( - size.width as u32, - size.height as u32, - &self.view_config, - None, - ) + .create_view(size.width, size.height, &self.view_config, None) .unwrap(); let surface = view.surface().unwrap(); @@ -198,19 +191,6 @@ impl BrowserEngine for Ultralight { // RGBA debug_assert!(surface.row_bytes() / size.width as u32 == 4); - // set callbacks - let site_url = Arc::new(RwLock::new(url.to_string())); - let cb_url = site_url.clone(); - view.set_change_url_callback(move |_view, url| { - *cb_url.write().unwrap() = url; - }); - - let title = Arc::new(RwLock::new(view.title().unwrap())); - let cb_title = title.clone(); - view.set_change_title_callback(move |_view, title| { - *cb_title.write().unwrap() = title; - }); - let cursor = Arc::new(RwLock::new(mouse::Interaction::Idle)); let cb_cursor = cursor.clone(); view.set_change_cursor_callback(move |_view, cursor_update| { @@ -239,7 +219,7 @@ impl BrowserEngine for Ultralight { cursor, }; - Tab::new(site_url, title, info) + Tab::new(info) } fn refresh(&self) { @@ -265,9 +245,9 @@ impl BrowserEngine for Ultralight { fn scroll(&self, delta: ScrollDelta) { let scroll_event = match delta { ScrollDelta::Lines { x, y } => ScrollEvent::new( - ul_next::event::ScrollEventType::ScrollByPage, - x as i32, - y as i32, + ul_next::event::ScrollEventType::ScrollByPixel, + x as i32 * 100, + y as i32 * 100, ) .unwrap(), ScrollDelta::Pixels { x, y } => ScrollEvent::new( diff --git a/src/lib.rs b/src/lib.rs index cc016b2..353e438 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,8 +7,8 @@ pub use engines::{BrowserEngine, PixelFormat, Tab, TabInfo, Tabs}; #[cfg(feature = "ultralight")] pub use engines::ultralight::Ultralight; -mod widgets; -pub use widgets::{browser_widgets, nav_bar, tab_bar, BrowserWidget}; +pub mod widgets; +pub use widgets::{nav_bar, tab_bar, BrowserWidget}; // Image details for passing the view around #[derive(Debug, Clone)] @@ -45,6 +45,8 @@ impl ImageInfo { .collect(), }; + println!("Image dimentions: width {:?}, height {:?}", width, height); + Self { pixels, width, diff --git a/src/widgets/browser_view.rs b/src/widgets/browser_view.rs index f7ed701..9271171 100644 --- a/src/widgets/browser_view.rs +++ b/src/widgets/browser_view.rs @@ -13,9 +13,9 @@ use iced::{theme::Theme, Element, Event, Length, Point, Rectangle, Size}; use crate::ImageInfo; pub fn browser_view( - bounds: Size, + bounds: Size, image: &ImageInfo, - send_bounds: Box Message>, + send_bounds: Box) -> Message>, keyboard_event: Box Message>, mouse_event: Box Message>, ) -> BrowserView { @@ -23,18 +23,18 @@ pub fn browser_view( } pub struct BrowserView { - bounds: Size, + bounds: Size, image: Image, - send_bounds: Box Message>, + send_bounds: Box) -> Message>, keyboard_event: Box Message>, mouse_event: Box Message>, } impl BrowserView { pub fn new( - bounds: Size, + bounds: Size, image: &ImageInfo, - send_bounds: Box Message>, + send_bounds: Box) -> Message>, keyboard_event: Box Message>, mouse_event: Box Message>, ) -> Self { @@ -102,9 +102,10 @@ where _viewport: &Rectangle, ) -> event::Status { // Send updates back if bounds change - let bounds = layout.bounds().size(); - if self.bounds != bounds { - shell.publish((self.send_bounds)(bounds)); + // convert to u32 because Image takes u32 + let size = Size::new(layout.bounds().width as u32, layout.bounds().height as u32); + if self.bounds != size { + shell.publish((self.send_bounds)(size)); } match event { diff --git a/src/widgets/browser_widgets.rs b/src/widgets/browser_widgets.rs deleted file mode 100644 index dca47e0..0000000 --- a/src/widgets/browser_widgets.rs +++ /dev/null @@ -1,240 +0,0 @@ -use iced::{keyboard, mouse, widget::column, Element, Point, Size}; -use iced_on_focus_widget::hoverable; -use url::Url; - -use super::{browser_view, nav_bar, tab_bar}; -use crate::{engines::BrowserEngine, to_url, ImageInfo}; - -#[cfg(feature = "ultralight")] -use crate::engines::ultralight::Ultralight; - -#[derive(Debug, Clone)] -pub enum Message { - GoBackward, - GoForward, - Refresh, - GoHome, - GoUrl(String), - ChangeTab(TabSelectionType), - CloseTab(TabSelectionType), - CreateTab, - UrlChanged(String), - UpdateUrl, - SendKeyboardEvent(keyboard::Event), - SendMouseEvent(Point, mouse::Event), - UpdateBounds(Size), -} - -/// Allows different widgets to interact in their native way -#[derive(Debug, Clone)] -pub enum TabSelectionType { - Id(u32), - Index(usize), -} - -pub struct BrowserWidget { - engine: Option, - home: Url, - url: String, - tab_bar: bool, - nav_bar: bool, - browser_view: bool, - view_bounds: Size, -} - -impl Default for BrowserWidget -where - Engine: BrowserEngine, -{ - fn default() -> Self { - let home = Url::parse(Self::HOME).unwrap(); - Self { - engine: None, - home, - url: String::new(), - tab_bar: false, - nav_bar: false, - browser_view: false, - view_bounds: Size::new(800., 800.), - } - } -} - -#[cfg(feature = "ultralight")] -impl BrowserWidget { - pub fn new_with_ultralight() -> BrowserWidget { - BrowserWidget { - engine: Some(Ultralight::new()), - ..BrowserWidget::default() - } - } -} - -impl BrowserWidget -where - Engine: BrowserEngine, -{ - const HOME: &'static str = "https://duckduckgo.com"; - - pub fn new() -> Self { - Self { - engine: Some(Engine::new()), - ..Default::default() - } - } - - pub fn with_homepage(mut self, homepage: &str) -> Self { - self.home = Url::parse(homepage).expect("Failed to parse homepage as a url!"); - self - } - - pub fn with_tab_bar(mut self) -> Self { - self.tab_bar = true; - self - } - - pub fn with_nav_bar(mut self) -> Self { - self.nav_bar = true; - self - } - - pub fn with_browsesr_view(mut self) -> Self { - self.browser_view = true; - self - } - - pub fn build(self) -> Self { - assert!(self.engine.is_some()); - - let mut build = Self { - engine: self.engine, - home: self.home, - tab_bar: self.tab_bar, - nav_bar: self.nav_bar, - url: self.url, - browser_view: self.browser_view, - view_bounds: self.view_bounds, - }; - build.update(Message::CreateTab); - build - } - - fn engine(&self) -> &Engine { - self.engine - .as_ref() - .expect("Browser was created without a backend engine!") - } - - fn engine_mut(&mut self) -> &mut Engine { - self.engine - .as_mut() - .expect("Browser was created without a backend engine!") - } - - pub fn update(&mut self, message: Message) { - self.engine().do_work(); - - match message { - Message::UpdateBounds(bounds) => { - self.view_bounds = bounds; - self.engine_mut().resize(bounds); - } - Message::SendKeyboardEvent(event) => { - self.engine().handle_keyboard_event(event); - } - Message::SendMouseEvent(point, event) => { - self.engine_mut().handle_mouse_event(point, event); - } - 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) - } - }; - self.engine_mut().get_tabs_mut().set_current_id(id); - self.url = self.engine().get_tabs().get_current().url(); - } - Message::CloseTab(index_type) => { - // ensure there is still a tab - if self.engine().get_tabs().tabs().len() == 1 { - self.update(Message::CreateTab) - } - - let id = match index_type { - TabSelectionType::Id(id) => id, - TabSelectionType::Index(index) => { - self.engine_mut().get_tabs().index_to_id(index) - } - }; - self.engine_mut().get_tabs_mut().remove(id); - self.url = self.engine().get_tabs().get_current().url(); - } - Message::CreateTab => { - self.url = self.home.to_string(); - let home = self.home.clone(); - let bounds = self.view_bounds; - let tab = self.engine_mut().new_tab(home, bounds); - 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); - } - Message::GoBackward => { - self.engine().go_back(); - self.url = self.engine().get_tabs().get_current().url(); - } - Message::GoForward => { - self.engine().go_forward(); - self.url = self.engine().get_tabs().get_current().url(); - } - Message::Refresh => self.engine().refresh(), - Message::GoHome => { - self.engine().goto_url(&self.home); - } - Message::GoUrl(url) => { - self.engine().goto_url(&to_url(&url).unwrap()); - } - Message::UpdateUrl => { - self.url = self.engine().get_tabs().get_current().url(); - } - Message::UrlChanged(url) => self.url = url, - } - - if self.engine().need_render() { - let (format, image_data) = self.engine_mut().pixel_buffer(); - let view = ImageInfo::new( - image_data, - format, - self.view_bounds.width as u32, - self.view_bounds.height as u32, - ); - self.engine_mut() - .get_tabs_mut() - .get_current_mut() - .set_view(view) - } - } - - pub fn view(&self) -> Element { - let mut column = column![]; - - if self.tab_bar { - column = column.push(tab_bar(self.engine().get_tabs())) - } - if self.nav_bar { - column = column.push(hoverable(nav_bar(&self.url)).on_unfocus(Message::UpdateUrl)) - } - if self.browser_view { - column = column.push(browser_view( - self.view_bounds, - self.engine().get_tabs().get_current().get_view(), - Box::new(Message::UpdateBounds), - Box::new(Message::SendKeyboardEvent), - Box::new(Message::SendMouseEvent), - )) - } - - column.into() - } -} diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index c73a75d..8102a22 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -1,3 +1,7 @@ +use iced::{keyboard, mouse, widget::column, Element, Point, Size}; +use iced_on_focus_widget::hoverable; +use url::Url; + mod browser_view; pub use browser_view::browser_view; @@ -7,5 +11,254 @@ pub use nav_bar::nav_bar; mod tab_bar; pub use tab_bar::tab_bar; -pub mod browser_widgets; -pub use browser_widgets::BrowserWidget; +use crate::{engines::BrowserEngine, to_url, ImageInfo}; + +#[derive(Debug, Clone)] +pub enum Message { + GoBackward, + GoForward, + Refresh, + GoHome, + GoUrl(String), + ChangeTab(TabSelectionType), + CloseTab(TabSelectionType), + CreateTab, + UrlChanged(String), + UpdateUrl, + SendKeyboardEvent(keyboard::Event), + SendMouseEvent(Point, mouse::Event), + UpdateViewSize(Size), +} + +/// Allows different widgets to interact in their native way +#[derive(Debug, Clone)] +pub enum TabSelectionType { + Id(u32), + Index(usize), +} + +pub struct BrowserWidget { + engine: Option, + home: Url, + url: String, + tab_bar: bool, + nav_bar: bool, + browser_view: bool, + view_size: Size, +} + +impl Default for BrowserWidget +where + Engine: BrowserEngine, +{ + fn default() -> Self { + let home = Url::parse(Self::HOME).unwrap(); + Self { + engine: None, + home, + url: String::new(), + tab_bar: false, + nav_bar: false, + browser_view: false, + view_size: Size::new(800, 800), + } + } +} + +#[cfg(feature = "ultralight")] +use crate::engines::ultralight::Ultralight; + +#[cfg(feature = "ultralight")] +impl BrowserWidget { + pub fn new_with_ultralight() -> BrowserWidget { + BrowserWidget { + engine: Some(Ultralight::new()), + ..BrowserWidget::default() + } + } +} + +impl BrowserWidget +where + Engine: BrowserEngine, +{ + const HOME: &'static str = "https://google.com"; + + pub fn new() -> Self { + Self { + engine: Some(Engine::new()), + ..Default::default() + } + } + + pub fn with_homepage(mut self, homepage: &str) -> Self { + self.home = Url::parse(homepage).expect("Failed to parse homepage as a url!"); + self + } + + pub fn with_tab_bar(mut self) -> Self { + self.tab_bar = true; + self + } + + pub fn with_nav_bar(mut self) -> Self { + self.nav_bar = true; + self + } + + pub fn with_browsesr_view(mut self) -> Self { + self.browser_view = true; + self + } + + pub fn build(self) -> Self { + assert!(self.engine.is_some()); + + let mut build = Self { + engine: self.engine, + home: self.home, + tab_bar: self.tab_bar, + nav_bar: self.nav_bar, + url: self.url, + browser_view: self.browser_view, + view_size: self.view_size, + }; + build.update(Message::CreateTab); + build + } + + fn engine(&self) -> &Engine { + self.engine + .as_ref() + .expect("Browser was created without a backend engine!") + } + + fn engine_mut(&mut self) -> &mut Engine { + self.engine + .as_mut() + .expect("Browser was created without a backend engine!") + } + + pub fn update(&mut self, message: Message) { + self.engine().do_work(); + + match message { + Message::UpdateViewSize(size) => { + self.view_size = size; + self.engine_mut().resize(size); + } + Message::SendKeyboardEvent(event) => { + self.engine().handle_keyboard_event(event); + } + Message::SendMouseEvent(point, event) => { + self.engine_mut().handle_mouse_event(point, event); + } + 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) + } + }; + self.engine_mut().get_tabs_mut().set_current_id(id); + self.url = self.engine().get_tabs().get_current().url(); + } + Message::CloseTab(index_type) => { + // ensure there is still a tab + if self.engine().get_tabs().tabs().len() == 1 { + self.update(Message::CreateTab) + } + + let id = match index_type { + TabSelectionType::Id(id) => id, + TabSelectionType::Index(index) => { + self.engine_mut().get_tabs().index_to_id(index) + } + }; + self.engine_mut().get_tabs_mut().remove(id); + self.url = self.engine().get_tabs().get_current().url(); + } + Message::CreateTab => { + self.url = self.home.to_string(); + let home = self.home.clone(); + let bounds = self.view_size; + let tab = self.engine_mut().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); + } + Message::GoBackward => { + self.engine().go_back(); + self.url = self.engine().get_tabs().get_current().url(); + } + Message::GoForward => { + self.engine().go_forward(); + self.url = self.engine().get_tabs().get_current().url(); + } + Message::Refresh => self.engine().refresh(), + Message::GoHome => { + self.engine().goto_url(&self.home); + } + Message::GoUrl(url) => { + self.engine().goto_url(&to_url(&url).unwrap()); + } + Message::UpdateUrl => { + self.url = self.engine().get_tabs().get_current().url(); + } + Message::UrlChanged(url) => self.url = url, + } + + if self.engine().has_loaded() { + if self.engine().need_render() { + let (format, image_data) = self.engine_mut().pixel_buffer(); + let view = ImageInfo::new( + image_data, + format, + self.view_size.width, + self.view_size.height, + ); + self.engine_mut() + .get_tabs_mut() + .get_current_mut() + .set_view(view) + } + } else { + let view = ImageInfo { + width: self.view_size.width, + height: self.view_size.height, + ..Default::default() + }; + self.engine_mut() + .get_tabs_mut() + .get_current_mut() + .set_view(view) + } + } + + pub fn view(&self) -> Element { + let mut column = column![]; + + if self.tab_bar { + column = column.push(tab_bar(self.engine().get_tabs())) + } + if self.nav_bar { + column = column.push(hoverable(nav_bar(&self.url)).on_unfocus(Message::UpdateUrl)) + } + if self.browser_view { + column = column.push(browser_view( + self.view_size, + self.engine().get_tabs().get_current().get_view(), + Box::new(Message::UpdateViewSize), + Box::new(Message::SendKeyboardEvent), + Box::new(Message::SendMouseEvent), + )) + } + + column.into() + } +} diff --git a/src/widgets/nav_bar.rs b/src/widgets/nav_bar.rs index 3ab8e03..dbf6e8e 100644 --- a/src/widgets/nav_bar.rs +++ b/src/widgets/nav_bar.rs @@ -2,7 +2,7 @@ use iced::widget::{row, text::LineHeight, text_input, tooltip, tooltip::Position use iced::{Element, Length}; use iced_aw::core::icons::bootstrap::{icon_to_text, Bootstrap}; -use super::browser_widgets::Message; +use super::Message; pub fn nav_bar(url: &str) -> Element { let back = tooltip_helper( diff --git a/src/widgets/tab_bar.rs b/src/widgets/tab_bar.rs index 0907bfa..945c7d9 100644 --- a/src/widgets/tab_bar.rs +++ b/src/widgets/tab_bar.rs @@ -3,7 +3,7 @@ use iced::{self, Element, Length}; use iced_aw::core::icons::bootstrap::{icon_to_text, Bootstrap}; use iced_aw::{TabBar as TB, TabLabel}; -use super::browser_widgets::{Message, TabSelectionType}; +use super::{Message, TabSelectionType}; use crate::engines::{TabInfo, Tabs}; // helper function to create navigation bar @@ -22,7 +22,12 @@ pub fn tab_bar(tabs: &Tabs) -> Element<'static, Message> { TB::new(|index| Message::ChangeTab(TabSelectionType::Index(index))), |tab_bar, tab| { let id = tab_bar.size(); - tab_bar.push(id, TabLabel::Text(tab.title())) + let title = if tab.title().is_empty() { + String::from("New Tab") + } else { + tab.title() + }; + tab_bar.push(id, TabLabel::Text(title)) }, ) .set_active_tab(&active_tab)