From 3b2d1a76437cbd62b95e14e290702f35da1f8b3c Mon Sep 17 00:00:00 2001 From: daxpedda Date: Tue, 11 Jul 2023 13:14:40 +0200 Subject: [PATCH] On Web, implement and fix missing methods on `Window(Builder)` (#2949) --- CHANGELOG.md | 4 + src/platform_impl/web/web_sys/canvas.rs | 47 ++++++++--- src/platform_impl/web/web_sys/mod.rs | 106 ++++++++++++++++++++---- src/platform_impl/web/window.rs | 66 ++++++++------- src/window.rs | 6 +- 5 files changed, 168 insertions(+), 61 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ff82497..2586d129 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,10 @@ And please only add new entries to the top of this list, right below the `# Unre - On Web, fix touch location to be as accurate as mouse position. - On Web, account for CSS `padding`, `border`, and `margin` when getting or setting the canvas position. - On Web, add Fullscreen API compatibility for Safari. +- On Web, implement `Window::set_(min|max)_inner_size()`. +- On Web, fix some `Window` methods using incorrect HTML attributes instead of CSS properties. +- On Web, fix some `WindowBuilder` methods doing nothing. +- On Web, implement `Window::focus_window()`. # 0.29.0-beta.0 diff --git a/src/platform_impl/web/web_sys/canvas.rs b/src/platform_impl/web/web_sys/canvas.rs index 3fc61d1f..a73ef5b4 100644 --- a/src/platform_impl/web/web_sys/canvas.rs +++ b/src/platform_impl/web/web_sys/canvas.rs @@ -88,21 +88,46 @@ impl Canvas { // this can't fail: we aren't using a pseudo-element .expect("Invalid pseudo-element"); + let common = Common { + window, + document, + raw: canvas, + style, + old_size: Rc::default(), + current_size: Rc::default(), + wants_fullscreen: Rc::new(RefCell::new(false)), + }; + if let Some(size) = attr.inner_size { - let size = size.to_logical(super::scale_factor(&window)); - super::set_canvas_size(&document, &canvas, &style, size); + let size = size.to_logical(super::scale_factor(&common.window)); + super::set_canvas_size(&common.document, &common.raw, &common.style, size); + } + + if let Some(size) = attr.min_inner_size { + let size = size.to_logical(super::scale_factor(&common.window)); + super::set_canvas_min_size(&common.document, &common.raw, &common.style, Some(size)); + } + + if let Some(size) = attr.max_inner_size { + let size = size.to_logical(super::scale_factor(&common.window)); + super::set_canvas_max_size(&common.document, &common.raw, &common.style, Some(size)); + } + + if let Some(position) = attr.position { + let position = position.to_logical(super::scale_factor(&common.window)); + super::set_canvas_position(&common.document, &common.raw, &common.style, position); + } + + if attr.fullscreen.is_some() { + common.request_fullscreen(); + } + + if attr.active { + let _ = common.raw.focus(); } Ok(Canvas { - common: Common { - window, - document, - raw: canvas, - style, - old_size: Rc::default(), - current_size: Rc::default(), - wants_fullscreen: Rc::new(RefCell::new(false)), - }, + common, id, has_focus: Arc::new(AtomicBool::new(false)), is_intersecting: None, diff --git a/src/platform_impl/web/web_sys/mod.rs b/src/platform_impl/web/web_sys/mod.rs index c30215c9..16720d76 100644 --- a/src/platform_impl/web/web_sys/mod.rs +++ b/src/platform_impl/web/web_sys/mod.rs @@ -14,7 +14,7 @@ pub use self::event_handle::EventListenerHandle; pub use self::resize_scaling::ResizeScaleHandle; pub use self::timeout::{IdleCallback, Timeout}; -use crate::dpi::LogicalSize; +use crate::dpi::{LogicalPosition, LogicalSize}; use crate::platform::web::WindowExtWebSys; use crate::window::Window; use wasm_bindgen::closure::Closure; @@ -67,35 +67,107 @@ pub fn scale_factor(window: &web_sys::Window) -> f64 { window.device_pixel_ratio() } -pub fn set_canvas_size( - document: &Document, - raw: &HtmlCanvasElement, - style: &CssStyleDeclaration, - mut new_size: LogicalSize, -) { - if !document.contains(Some(raw)) { - return; - } - - if style.get_property_value("display").unwrap() == "none" { - return; - } - +fn fix_canvas_size(style: &CssStyleDeclaration, mut size: LogicalSize) -> LogicalSize { if style.get_property_value("box-sizing").unwrap() == "border-box" { - new_size.width += style_size_property(style, "border-left-width") + size.width += style_size_property(style, "border-left-width") + style_size_property(style, "border-right-width") + style_size_property(style, "padding-left") + style_size_property(style, "padding-right"); - new_size.height += style_size_property(style, "border-top-width") + size.height += style_size_property(style, "border-top-width") + style_size_property(style, "border-bottom-width") + style_size_property(style, "padding-top") + style_size_property(style, "padding-bottom"); } + size +} + +pub fn set_canvas_size( + document: &Document, + raw: &HtmlCanvasElement, + style: &CssStyleDeclaration, + new_size: LogicalSize, +) { + if !document.contains(Some(raw)) || style.get_property_value("display").unwrap() == "none" { + return; + } + + let new_size = fix_canvas_size(style, new_size); + set_canvas_style_property(raw, "width", &format!("{}px", new_size.width)); set_canvas_style_property(raw, "height", &format!("{}px", new_size.height)); } +pub fn set_canvas_min_size( + document: &Document, + raw: &HtmlCanvasElement, + style: &CssStyleDeclaration, + dimensions: Option>, +) { + if let Some(dimensions) = dimensions { + if !document.contains(Some(raw)) || style.get_property_value("display").unwrap() == "none" { + return; + } + + let new_size = fix_canvas_size(style, dimensions); + + set_canvas_style_property(raw, "min-width", &format!("{}px", new_size.width)); + set_canvas_style_property(raw, "min-height", &format!("{}px", new_size.height)); + } else { + style + .remove_property("min-width") + .expect("Property is read only"); + style + .remove_property("min-height") + .expect("Property is read only"); + } +} + +pub fn set_canvas_max_size( + document: &Document, + raw: &HtmlCanvasElement, + style: &CssStyleDeclaration, + dimensions: Option>, +) { + if let Some(dimensions) = dimensions { + if !document.contains(Some(raw)) || style.get_property_value("display").unwrap() == "none" { + return; + } + + let new_size = fix_canvas_size(style, dimensions); + + set_canvas_style_property(raw, "max-width", &format!("{}px", new_size.width)); + set_canvas_style_property(raw, "max-height", &format!("{}px", new_size.height)); + } else { + style + .remove_property("max-width") + .expect("Property is read only"); + style + .remove_property("max-height") + .expect("Property is read only"); + } +} + +pub fn set_canvas_position( + document: &Document, + raw: &HtmlCanvasElement, + style: &CssStyleDeclaration, + mut position: LogicalPosition, +) { + if document.contains(Some(raw)) && style.get_property_value("display").unwrap() != "none" { + position.x -= style_size_property(style, "margin-left") + + style_size_property(style, "border-left-width") + + style_size_property(style, "padding-left"); + position.y -= style_size_property(style, "margin-top") + + style_size_property(style, "border-top-width") + + style_size_property(style, "padding-top"); + } + + set_canvas_style_property(raw, "position", "fixed"); + set_canvas_style_property(raw, "left", &format!("{}px", position.x)); + set_canvas_style_property(raw, "top", &format!("{}px", position.y)); +} + /// This function will panic if the element is not inserted in the DOM /// or is not a CSS property that represents a size in pixel. pub fn style_size_property(style: &CssStyleDeclaration, property: &str) -> f64 { diff --git a/src/platform_impl/web/window.rs b/src/platform_impl/web/window.rs index 805126ef..f3f1e7cd 100644 --- a/src/platform_impl/web/window.rs +++ b/src/platform_impl/web/window.rs @@ -7,7 +7,7 @@ use crate::window::{ }; use raw_window_handle::{RawDisplayHandle, RawWindowHandle, WebDisplayHandle, WebWindowHandle}; -use web_sys::{CssStyleDeclaration, Document, HtmlCanvasElement}; +use web_sys::{Document, HtmlCanvasElement}; use super::r#async::Dispatcher; use super::{backend, monitor::MonitorHandle, EventLoopWindowTarget, Fullscreen}; @@ -28,7 +28,6 @@ pub struct Window { pub struct Inner { pub window: web_sys::Window, document: Document, - style: CssStyleDeclaration, canvas: Rc>, previous_pointer: RefCell<&'static str>, register_redraw_request: Box, @@ -51,7 +50,6 @@ impl Window { let document = target.runner.document(); let canvas = backend::Canvas::create(id, window.clone(), document.clone(), &attr, platform_attr)?; - let style = canvas.style().clone(); let canvas = Rc::new(RefCell::new(canvas)); let register_redraw_request = Box::new(move || runner.request_redraw(RootWI(id))); @@ -68,7 +66,6 @@ impl Window { inner: Dispatcher::new(Inner { window: window.clone(), document: document.clone(), - style, canvas, previous_pointer: RefCell::new("auto"), register_redraw_request, @@ -134,24 +131,10 @@ impl Window { pub fn set_outer_position(&self, position: Position) { self.inner.dispatch(move |inner| { - let mut position = position.to_logical::(inner.scale_factor()); - let canvas = inner.canvas.borrow(); + let position = position.to_logical::(inner.scale_factor()); - if inner.document.contains(Some(canvas.raw())) - && inner.style.get_property_value("display").unwrap() != "none" - { - position.x -= backend::style_size_property(&inner.style, "margin-left") - + backend::style_size_property(&inner.style, "border-left-width") - + backend::style_size_property(&inner.style, "padding-left"); - position.y -= backend::style_size_property(&inner.style, "margin-top") - + backend::style_size_property(&inner.style, "border-top-width") - + backend::style_size_property(&inner.style, "padding-top"); - } - - canvas.set_attribute("position", "fixed"); - canvas.set_attribute("left", &position.x.to_string()); - canvas.set_attribute("top", &position.y.to_string()); + backend::set_canvas_position(canvas.document(), canvas.raw(), canvas.style(), position) }); } @@ -178,13 +161,33 @@ impl Window { } #[inline] - pub fn set_min_inner_size(&self, _dimensions: Option) { - // Intentionally a no-op: users can't resize canvas elements + pub fn set_min_inner_size(&self, dimensions: Option) { + self.inner.dispatch(move |inner| { + let dimensions = + dimensions.map(|dimensions| dimensions.to_logical(inner.scale_factor())); + let canvas = inner.canvas.borrow(); + backend::set_canvas_min_size( + canvas.document(), + canvas.raw(), + canvas.style(), + dimensions, + ) + }) } #[inline] - pub fn set_max_inner_size(&self, _dimensions: Option) { - // Intentionally a no-op: users can't resize canvas elements + pub fn set_max_inner_size(&self, dimensions: Option) { + self.inner.dispatch(move |inner| { + let dimensions = + dimensions.map(|dimensions| dimensions.to_logical(inner.scale_factor())); + let canvas = inner.canvas.borrow(); + backend::set_canvas_max_size( + canvas.document(), + canvas.raw(), + canvas.style(), + dimensions, + ) + }) } #[inline] @@ -259,12 +262,13 @@ impl Window { pub fn set_cursor_visible(&self, visible: bool) { self.inner.dispatch(move |inner| { if !visible { - inner.canvas.borrow().set_attribute("cursor", "none"); + backend::set_canvas_style_property(inner.canvas.borrow().raw(), "cursor", "none"); } else { - inner - .canvas - .borrow() - .set_attribute("cursor", &inner.previous_pointer.borrow()); + backend::set_canvas_style_property( + inner.canvas.borrow().raw(), + "cursor", + &inner.previous_pointer.borrow(), + ); } }); } @@ -364,7 +368,9 @@ impl Window { #[inline] pub fn focus_window(&self) { - // Currently a no-op as it does not seem there is good support for this on web + self.inner.dispatch(|inner| { + let _ = inner.canvas.borrow().raw().focus(); + }) } #[inline] diff --git a/src/window.rs b/src/window.rs index 49859c19..b1e23c03 100644 --- a/src/window.rs +++ b/src/window.rs @@ -730,7 +730,7 @@ impl Window { /// /// ## Platform-specific /// - /// - **iOS / Android / Web / Orbital:** Unsupported. + /// - **iOS / Android / Orbital:** Unsupported. #[inline] pub fn set_min_inner_size>(&self, min_size: Option) { self.window.set_min_inner_size(min_size.map(|s| s.into())) @@ -753,7 +753,7 @@ impl Window { /// /// ## Platform-specific /// - /// - **iOS / Android / Web / Orbital:** Unsupported. + /// - **iOS / Android / Orbital:** Unsupported. #[inline] pub fn set_max_inner_size>(&self, max_size: Option) { self.window.set_max_inner_size(max_size.map(|s| s.into())) @@ -1120,7 +1120,7 @@ impl Window { /// /// ## Platform-specific /// - /// - **iOS / Android / Web / Wayland / Orbital:** Unsupported. + /// - **iOS / Android / Wayland / Orbital:** Unsupported. #[inline] pub fn focus_window(&self) { self.window.focus_window()