From e8f21035ffd11500ffbe031fcefa3916db76ae05 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Wed, 29 Mar 2023 12:48:35 +0200 Subject: [PATCH] Fix the slide_puzzle canvas size on wasm Since winit can't handle resize from the CSS or JS, work it around in the demo by calling set_size on the slint window. Also fix slint_size panicking not working on wasm as the map_state was mutably borrowed by the set_size, and this may recurse into other functions (eg, draw) causing a panic. So don't keep the state borrowed when calling winit. Another fix is fixing the initialization size of the window item when the initial size is set with set_size Fixes #547 --- examples/slide_puzzle/Cargo.toml | 2 +- examples/slide_puzzle/main.rs | 44 +++++++++++++++++++++++++++ internal/backends/winit/glwindow.rs | 47 +++++++++++++---------------- 3 files changed, 66 insertions(+), 27 deletions(-) diff --git a/examples/slide_puzzle/Cargo.toml b/examples/slide_puzzle/Cargo.toml index 6b3bdf08c4c..05eb7fa9dc7 100644 --- a/examples/slide_puzzle/Cargo.toml +++ b/examples/slide_puzzle/Cargo.toml @@ -30,6 +30,6 @@ slint-build = { path = "../../api/rs/build" } #wasm# [target.'cfg(target_arch = "wasm32")'.dependencies] #wasm# wasm-bindgen = { version = "0.2" } -#wasm# web-sys = { version = "0.3", features=["console"] } +#wasm# web-sys = { version = "0.3", features=["console", "Element", "HtmlCollection"] } #wasm# console_error_panic_hook = "0.1.5" #wasm# getrandom = { version = "0.2.2", features = ["js"] } diff --git a/examples/slide_puzzle/main.rs b/examples/slide_puzzle/main.rs index e31e9435f9a..38fc223ac62 100644 --- a/examples/slide_puzzle/main.rs +++ b/examples/slide_puzzle/main.rs @@ -172,6 +172,10 @@ pub fn main() { console_error_panic_hook::set_once(); let main_window = MainWindow::new().unwrap(); + + #[cfg(target_arch = "wasm32")] + handle_resize(main_window.as_weak()); + let state = Rc::new(RefCell::new(AppState { pieces: Rc::new(slint::VecModel::::from(vec![Piece::default(); 15])), main_window: main_window.as_weak(), @@ -231,3 +235,43 @@ pub fn main() { }); main_window.run().unwrap(); } + +#[cfg(target_arch = "wasm32")] +/// winit doesn't handle the resizing of the canvas from CSS well +/// https://github.com/rust-windowing/winit/issues/1661 +/// in the mean time, adjust the size manually +fn handle_resize(slint_window: slint::Weak) -> Option<()> { + let window = web_sys::window()?; + let resize_handler = move || { + let window = web_sys::window()?; + let doc = window.document()?; + + let container = doc.get_element_by_id("container")?; + let children = container.children(); + let mut height_sum = 0.; + for i in 0..children.length() { + let child = children.item(i).unwrap(); + if child.tag_name().to_lowercase() == "canvas" { + continue; + } + let style = window.get_computed_style(&child).ok()?; + let get_margin = |m| { + style.as_ref()?.get_property_value(m).ok()?.strip_suffix("px")?.parse::().ok() + }; + height_sum += child.scroll_height() as f32 + + get_margin("margin-top").unwrap_or(0.) + + get_margin("margin-bottom").unwrap_or(0.) + + 1.; + } + + let width = doc.body()?.client_width() as f32; + let height = doc.body()?.client_height() as f32 - height_sum; + slint_window.upgrade()?.window().set_size(slint::LogicalSize { width, height }); + Some(()) + }; + resize_handler(); + let closure = Closure::wrap(Box::new(move || drop(resize_handler())) as Box); + window.set_onresize(Some(closure.as_ref().unchecked_ref())); + closure.forget(); + Some(()) +} diff --git a/internal/backends/winit/glwindow.rs b/internal/backends/winit/glwindow.rs index 84b99bfb151..a213c911025 100644 --- a/internal/backends/winit/glwindow.rs +++ b/internal/backends/winit/glwindow.rs @@ -539,9 +539,6 @@ impl WindowAdapterSealed for GLWind window_builder.with_inner_size(window_size_to_slint(requested_size)) } } else if s.width > 0 as Coord && s.height > 0 as Coord { - // Make sure that the window's inner size is in sync with the root window item's - // width/height. - runtime_window.set_window_item_geometry(LogicalSize::new(s.width, s.height)); window_builder.with_inner_size(into_size(s)) } else { window_builder @@ -566,14 +563,12 @@ impl WindowAdapterSealed for GLWind &self_.canvas_id, )?; - WindowInner::from_pub(&self_.window).set_scale_factor( - scale_factor_override.unwrap_or_else(|| winit_window.scale_factor()) as _, - ); - // On wasm, with_inner_size on the WindowBuilder don't have effect, so apply manually - #[cfg(target_arch = "wasm32")] - if s.width > 0 as Coord && s.height > 0 as Coord { - winit_window.set_inner_size(s); - } + let scale_factor = scale_factor_override.unwrap_or_else(|| winit_window.scale_factor()); + WindowInner::from_pub(&self_.window).set_scale_factor(scale_factor as _); + let s = winit_window.inner_size().to_logical(scale_factor); + // Make sure that the window's inner size is in sync with the root window item's + // width/height. + runtime_window.set_window_item_geometry(LogicalSize::new(s.width, s.height)); let id = winit_window.id(); self_.map_state.replace(GraphicsWindowBackendState::Mapped(MappedWindow { @@ -699,30 +694,30 @@ impl WindowAdapterSealed for GLWind } fn set_position(&self, position: corelib::api::WindowPosition) { - match &mut *self.map_state.borrow_mut() { + let w = match &mut *self.map_state.borrow_mut() { GraphicsWindowBackendState::Unmapped { requested_position, .. } => { - *requested_position = Some(position) - } - GraphicsWindowBackendState::Mapped(mapped_window) => { - mapped_window.winit_window.set_outer_position(position_to_winit(&position)) + *requested_position = Some(position); + return; } - } + GraphicsWindowBackendState::Mapped(mapped_window) => mapped_window.winit_window.clone(), + }; + w.set_outer_position(position_to_winit(&position)) } fn set_size(&self, size: corelib::api::WindowSize) { if self.in_resize_event.get() { return; } - if let Ok(mut map_state) = self.map_state.try_borrow_mut() { - match &mut *map_state { - GraphicsWindowBackendState::Unmapped { requested_size, .. } => { - *requested_size = Some(size) - } - GraphicsWindowBackendState::Mapped(mapped_window) => { - mapped_window.winit_window.set_inner_size(window_size_to_slint(&size)); - } + let Ok(mut map_state) = self.map_state.try_borrow_mut() else { return }; + let w = match &mut *map_state { + GraphicsWindowBackendState::Unmapped { requested_size, .. } => { + *requested_size = Some(size); + return; } - } + GraphicsWindowBackendState::Mapped(mapped_window) => mapped_window.winit_window.clone(), + }; + drop(map_state); + w.set_inner_size(window_size_to_slint(&size)) } fn dark_color_scheme(&self) -> bool {