diff --git a/masonry/src/render_root.rs b/masonry/src/render_root.rs index ddd1483dd..7de7173b9 100644 --- a/masonry/src/render_root.rs +++ b/masonry/src/render_root.rs @@ -4,10 +4,11 @@ use std::collections::{HashMap, VecDeque}; use accesskit::{ActionRequest, TreeUpdate}; +use dpi::PhysicalPosition; use parley::fontique::{self, Collection, CollectionOptions}; use parley::{FontContext, LayoutContext}; use tracing::{info_span, warn}; -use vello::kurbo::{self, Rect}; +use vello::kurbo::{self, Rect, Vec2}; use vello::Scene; #[cfg(not(target_arch = "wasm32"))] @@ -325,6 +326,28 @@ impl RenderRoot { self.cursor_icon } + pub fn is_passthrough(&self, pos: PhysicalPosition) -> bool { + let pos = Vec2::new(pos.x, pos.y) / self.scale_factor; + + let mut target_id = self + .get_root_widget() + .find_widget_at_pos(pos.to_point()) + .map(|widget| widget.id()); + + while let Some(widget_id) = target_id { + let parent_id = self.widget_arena.parent_of(widget_id); + let widget = self.widget_arena.get_widget(widget_id); + + if !widget.item.is_passthrough() { + return false; + } + + target_id = parent_id; + } + + true + } + // --- MARK: ACCESS WIDGETS--- /// Get a [`WidgetRef`] to the root widget. pub fn get_root_widget(&self) -> WidgetRef { diff --git a/masonry/src/testing/helper_widgets.rs b/masonry/src/testing/helper_widgets.rs index a2cf4745c..a85c908a9 100644 --- a/masonry/src/testing/helper_widgets.rs +++ b/masonry/src/testing/helper_widgets.rs @@ -378,6 +378,10 @@ impl Widget for ModularWidget { self.accepts_text_input } + fn is_passthrough(&self) -> bool { + false + } + fn make_trace_span(&self) -> tracing::Span { trace_span!("ModularWidget") } @@ -578,6 +582,10 @@ impl Widget for Recorder { self.child.accepts_text_input() } + fn is_passthrough(&self) -> bool { + self.child.is_passthrough() + } + fn make_trace_span(&self) -> tracing::Span { self.child.make_trace_span() } diff --git a/masonry/src/widget/flex.rs b/masonry/src/widget/flex.rs index 78a7e34e3..522771bfd 100644 --- a/masonry/src/widget/flex.rs +++ b/masonry/src/widget/flex.rs @@ -1202,6 +1202,10 @@ impl Widget for Flex { .collect() } + fn is_passthrough(&self) -> bool { + true + } + fn make_trace_span(&self) -> Span { trace_span!("Flex") } diff --git a/masonry/src/widget/grid.rs b/masonry/src/widget/grid.rs index 7e5849610..0367aabe0 100644 --- a/masonry/src/widget/grid.rs +++ b/masonry/src/widget/grid.rs @@ -311,6 +311,10 @@ impl Widget for Grid { .collect() } + fn is_passthrough(&self) -> bool { + true + } + fn make_trace_span(&self) -> Span { trace_span!("Grid") } diff --git a/masonry/src/widget/sized_box.rs b/masonry/src/widget/sized_box.rs index a7c2019f0..1b0049a2e 100644 --- a/masonry/src/widget/sized_box.rs +++ b/masonry/src/widget/sized_box.rs @@ -401,6 +401,10 @@ impl Widget for SizedBox { } } + fn is_passthrough(&self) -> bool { + self.background.is_none() + } + fn make_trace_span(&self) -> Span { trace_span!("SizedBox") } diff --git a/masonry/src/widget/widget.rs b/masonry/src/widget/widget.rs index b2793af56..1dd83d941 100644 --- a/masonry/src/widget/widget.rs +++ b/masonry/src/widget/widget.rs @@ -199,6 +199,13 @@ pub trait Widget: AsAny { false } + /// Whether this widgets obstructs pointer events for something behind the window. + /// + /// Somewhat experimental. + fn is_passthrough(&self) -> bool { + false + } + // TODO - Write a generic default implementation once // `const std::any::type_name` is stable. // See https://github.com/rust-lang/rust/issues/63084 @@ -467,6 +474,10 @@ impl Widget for Box { self.deref().accepts_text_input() } + fn is_passthrough(&self) -> bool { + self.deref().is_passthrough() + } + fn make_trace_span(&self) -> Span { self.deref().make_trace_span() }