From 9c1d0f5d4d768280f606580ed5efc445608a35da Mon Sep 17 00:00:00 2001 From: jprochazk <1665677+jprochazk@users.noreply.github.com> Date: Fri, 24 May 2024 13:18:42 +0200 Subject: [PATCH] use ResizeObserver instead of `resize` event --- crates/eframe/Cargo.toml | 3 ++ crates/eframe/src/web/events.rs | 43 ++++++++++++++++++++++++++++- crates/eframe/src/web/web_runner.rs | 25 +++++++++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/crates/eframe/Cargo.toml b/crates/eframe/Cargo.toml index f992c5c66a2..33199a84cdc 100644 --- a/crates/eframe/Cargo.toml +++ b/crates/eframe/Cargo.toml @@ -237,6 +237,9 @@ web-sys = { workspace = true, features = [ "MouseEvent", "Navigator", "Performance", + "ResizeObserver", + "ResizeObserverBoxOptions", + "ResizeObserverOptions", "Storage", "Touch", "TouchEvent", diff --git a/crates/eframe/src/web/events.rs b/crates/eframe/src/web/events.rs index 56d3fdf071a..2aacdb7b470 100644 --- a/crates/eframe/src/web/events.rs +++ b/crates/eframe/src/web/events.rs @@ -247,7 +247,8 @@ pub(crate) fn install_window_events(runner_ref: &WebRunner) -> Result<(), JsValu runner.save(); })?; - for event_name in &["load", "pagehide", "pageshow", "resize"] { + // NOTE: resize is handled by `ResizeObserver` below + for event_name in &["load", "pagehide", "pageshow"] { runner_ref.add_event_listener(&window, event_name, move |_: web_sys::Event, runner| { // log::debug!("{event_name:?}"); runner.needs_repaint.repaint_asap(); @@ -590,3 +591,43 @@ pub(crate) fn install_canvas_events(runner_ref: &WebRunner) -> Result<(), JsValu Ok(()) } + +trait JsValueExt { + fn get(&self, key: &str) -> Result; + fn at(&self, idx: usize) -> Result; + fn has(&self, key: &str) -> Result; +} + +impl JsValueExt for JsValue { + fn get(&self, key: &str) -> Result { + js_sys::Reflect::get(self, &JsValue::from_str(key)) + } + + fn at(&self, idx: usize) -> Result { + js_sys::Reflect::get(self, &JsValue::from_f64(idx as f64)) + } + + fn has(&self, key: &str) -> Result { + js_sys::Reflect::has(self, &JsValue::from_str(key)) + } +} + +pub(crate) fn install_resize_observer(runner_ref: &WebRunner) -> Result<(), JsValue> { + let closure = Closure::wrap(Box::new({ + let runner_ref = runner_ref.clone(); + move |entries: js_sys::Array| { + // Only call the wrapped closure if the egui code has not panicked + if let Some(mut runner_lock) = runner_ref.try_lock() { + runner_lock.needs_repaint.repaint_asap(); + } + } + }) as Box); + + let observer = web_sys::ResizeObserver::new(closure.as_ref().unchecked_ref())?; + let mut options = web_sys::ResizeObserverOptions::new(); + options.box_(web_sys::ResizeObserverBoxOptions::ContentBox); + observer.observe_with_options(runner_ref.try_lock().unwrap().canvas(), &options); + runner_ref.set_resize_observer(observer, closure); + + Ok(()) +} diff --git a/crates/eframe/src/web/web_runner.rs b/crates/eframe/src/web/web_runner.rs index f39fc0dcace..f6e3e0c4ba7 100644 --- a/crates/eframe/src/web/web_runner.rs +++ b/crates/eframe/src/web/web_runner.rs @@ -30,6 +30,8 @@ pub struct WebRunner { /// Used in `destroy` to cancel a pending frame. request_animation_frame_id: Cell>, + + resize_observer: Rc>>, } impl WebRunner { @@ -48,6 +50,7 @@ impl WebRunner { runner: Rc::new(RefCell::new(None)), events_to_unsubscribe: Rc::new(RefCell::new(Default::default())), request_animation_frame_id: Cell::new(None), + resize_observer: Default::default(), } } @@ -78,6 +81,8 @@ impl WebRunner { events::install_color_scheme_change_event(self)?; } + events::install_resize_observer(self)?; + self.request_animation_frame()?; } @@ -109,6 +114,11 @@ impl WebRunner { } } } + + if let Some(context) = self.resize_observer.take() { + context.observer.disconnect(); + drop(context.closure); + } } /// Shut down eframe and clean up resources. @@ -203,10 +213,25 @@ impl WebRunner { closure.forget(); // We must forget it, or else the callback is canceled on drop Ok(()) } + + pub(crate) fn set_resize_observer( + &self, + observer: web_sys::ResizeObserver, + closure: Closure, + ) { + self.resize_observer + .borrow_mut() + .replace(ResizeObserverContext { observer, closure }); + } } // ---------------------------------------------------------------------------- +struct ResizeObserverContext { + observer: web_sys::ResizeObserver, + closure: Closure, +} + struct TargetEvent { target: web_sys::EventTarget, event_name: String,