From 43261a53965aa57dd12e6e9a2a91137b582b7450 Mon Sep 17 00:00:00 2001 From: Aiden Date: Wed, 19 Feb 2025 01:01:07 +0800 Subject: [PATCH] Add pointer events and focus handling for apps run in a Shadow DOM (#5627) * [x] I have followed the instructions in the PR template This PR handles pointer events and focus which did following changes: - `element_from_point` and focus is now acquired from root node object by using `get_root_node` from document or a shadow root. - `TextAgent` is appended individually in each shadow root. These changes handles pointer events and focus well in a web app that are running in a shadow dom, or else the hover pointer actions and keyboard input events are not triggered in a shadow dom. Helpful for building embeddable/multi-view web-apps. --- crates/eframe/Cargo.toml | 1 + crates/eframe/src/web/events.rs | 19 +++++++++++++------ crates/eframe/src/web/mod.rs | 20 ++++++++++++-------- crates/eframe/src/web/text_agent.rs | 15 +++++++++++++-- crates/eframe/src/web/web_runner.rs | 2 +- 5 files changed, 40 insertions(+), 17 deletions(-) diff --git a/crates/eframe/Cargo.toml b/crates/eframe/Cargo.toml index 883ec7d5916b..774bcbbe9ffd 100644 --- a/crates/eframe/Cargo.toml +++ b/crates/eframe/Cargo.toml @@ -253,6 +253,7 @@ web-sys = { workspace = true, features = [ "ResizeObserverEntry", "ResizeObserverOptions", "ResizeObserverSize", + "ShadowRoot", "Storage", "Touch", "TouchEvent", diff --git a/crates/eframe/src/web/events.rs b/crates/eframe/src/web/events.rs index 762f202fa649..6a1b7b6db8e6 100644 --- a/crates/eframe/src/web/events.rs +++ b/crates/eframe/src/web/events.rs @@ -1,5 +1,3 @@ -use web_sys::EventTarget; - use crate::web::string_from_js_value; use super::{ @@ -10,6 +8,8 @@ use super::{ DEBUG_RESIZE, }; +use web_sys::{Document, EventTarget, ShadowRoot}; + // TODO(emilk): there are more calls to `prevent_default` and `stop_propagation` // than what is probably needed. @@ -570,10 +570,17 @@ fn install_pointerup(runner_ref: &WebRunner, target: &EventTarget) -> Result<(), /// Returns true if the cursor is above the canvas, or if we're dragging something. /// Pass in the position in browser viewport coordinates (usually event.clientX/Y). fn is_interested_in_pointer_event(runner: &AppRunner, pos: egui::Pos2) -> bool { - let document = web_sys::window().unwrap().document().unwrap(); - let is_hovering_canvas = document - .element_from_point(pos.x, pos.y) - .is_some_and(|element| element.eq(runner.canvas())); + let root_node = runner.canvas().get_root_node(); + + let element_at_point = if let Some(document) = root_node.dyn_ref::() { + document.element_from_point(pos.x, pos.y) + } else if let Some(shadow) = root_node.dyn_ref::() { + shadow.element_from_point(pos.x, pos.y) + } else { + None + }; + + let is_hovering_canvas = element_at_point.is_some_and(|element| element.eq(runner.canvas())); let is_pointer_down = runner .egui_ctx() .input(|i| i.pointer.any_down() || i.any_touches()); diff --git a/crates/eframe/src/web/mod.rs b/crates/eframe/src/web/mod.rs index 3dc7d7f8b3b0..c67fa69e61eb 100644 --- a/crates/eframe/src/web/mod.rs +++ b/crates/eframe/src/web/mod.rs @@ -41,7 +41,7 @@ pub(crate) type ActiveWebPainter = web_painter_wgpu::WebPainterWgpu; pub use backend::*; use wasm_bindgen::prelude::*; -use web_sys::MediaQueryList; +use web_sys::{Document, MediaQueryList, Node}; use input::{ button_from_mouse_event, modifiers_from_kb_event, modifiers_from_mouse_event, @@ -64,18 +64,22 @@ pub(crate) fn string_from_js_value(value: &JsValue) -> String { /// - ``/`` with an `href` attribute /// - ``/`