Rework find_widget_at_pos method

Rename to find_widget_under_pointer
Make function body more readable.
Make default implementation use only public items.
This commit is contained in:
Olivier FAURE 2025-04-17 15:03:47 +02:00
parent 324ff2444b
commit fd56e3d69d
8 changed files with 49 additions and 37 deletions

View File

@ -790,6 +790,13 @@ impl_context_method!(
self.widget_state.window_origin()
}
/// Global transform of this widget in the window coordinate space.
///
/// Computed from all `transform` and `scroll_translation` values from this to the root widget.
pub fn window_transform(&self) -> Affine {
self.widget_state.window_transform
}
/// The bounding rect of the widget in window coordinates.
///
/// See [bounding rect documentation](crate::doc::doc_06_masonry_concepts#bounding-rect)

View File

@ -30,7 +30,7 @@ pub use events::{
pub use object_fit::ObjectFit;
pub use properties::{Properties, PropertiesMut, PropertiesRef};
pub use text::{ArcStr, BrushIndex, StyleProperty, StyleSet, render_text};
pub use widget::find_widget_at_pos;
pub use widget::find_widget_under_pointer;
pub use widget::{AllowRawMut, FromDynWidget, Widget, WidgetId};
pub use widget_mut::WidgetMut;
pub use widget_pod::WidgetPod;

View File

@ -354,13 +354,13 @@ pub trait Widget: AsAny + AsDynWidget {
///
/// **pos** - the position in global coordinates (e.g. `(0,0)` is the top-left corner of the
/// window).
fn find_widget_at_pos<'c>(
fn find_widget_under_pointer<'c>(
&'c self,
ctx: QueryCtx<'c>,
props: PropertiesRef<'c>,
pos: Point,
) -> Option<WidgetRef<'c, dyn Widget>> {
find_widget_at_pos(
find_widget_under_pointer(
&WidgetRef {
widget: self.as_dyn(),
properties: props,
@ -410,38 +410,43 @@ pub trait Widget: AsAny + AsDynWidget {
}
/// See [`Widget::find_widget_at_pos`] for more details.
pub fn find_widget_at_pos<'c>(
pub fn find_widget_under_pointer<'c>(
widget: &WidgetRef<'c, dyn Widget>,
pos: Point,
) -> Option<WidgetRef<'c, dyn Widget>> {
if widget.ctx.widget_state.bounding_rect.contains(pos) {
let local_pos = widget.ctx().widget_state.window_transform.inverse() * pos;
let ctx = widget.ctx();
if widget.ctx.is_stashed()
|| Some(false) == widget.ctx.clip_path().map(|clip| clip.contains(local_pos))
{
if !ctx.bounding_rect().contains(pos) {
return None;
}
if ctx.is_stashed() {
return None;
}
let local_pos = ctx.window_transform().inverse() * pos;
if let Some(clip) = ctx.clip_path() {
if !clip.contains(local_pos) {
return None;
}
}
// Assumes `Self::children_ids` is in increasing "z-order", picking the last child in case
// of overlapping children.
for child_id in widget.children_ids().iter().rev() {
let child_ref = widget.ctx.get(*child_id);
if let Some(child) =
child_ref
.widget
.find_widget_at_pos(child_ref.ctx, child_ref.properties, pos)
{
return Some(child);
}
}
if widget.ctx.accepts_pointer_interaction()
&& widget.ctx.size().to_rect().contains(local_pos)
// Assumes `Self::children_ids` is in increasing "z-order", picking the last child in case
// of overlapping children.
for child_id in widget.children_ids().iter().rev() {
let child_ref = ctx.get(*child_id);
if let Some(child) =
child_ref
.widget
.find_widget_under_pointer(child_ref.ctx, child_ref.properties, pos)
{
Some(*widget)
} else {
None
return Some(child);
}
}
// If no child is under pointer, test the current widget.
if ctx.accepts_pointer_interaction() && ctx.size().to_rect().contains(local_pos) {
Some(*widget)
} else {
None
}

View File

@ -188,9 +188,9 @@ impl WidgetRef<'_, dyn Widget> {
///
/// **pos** - the position in global coordinates (e.g. `(0,0)` is the top-left corner of the
/// window).
pub fn find_widget_at_pos(&self, pos: Point) -> Option<Self> {
pub fn find_widget_under_pointer(&self, pos: Point) -> Option<Self> {
self.widget
.find_widget_at_pos(self.ctx, self.properties, pos)
.find_widget_under_pointer(self.ctx, self.properties, pos)
}
}

View File

@ -27,7 +27,7 @@ fn get_pointer_target(
let pointer_pos = (pointer_pos.x, pointer_pos.y).into();
return root
.get_root_widget()
.find_widget_at_pos(pointer_pos)
.find_widget_under_pointer(pointer_pos)
.map(|widget| widget.id());
}

View File

@ -682,7 +682,7 @@ pub(crate) fn run_update_pointer_pass(root: &mut RenderRoot) {
if let Some(pos) = pointer_pos {
root.global_state.inspector_state.hovered_widget = root
.get_root_widget()
.find_widget_at_pos(pos)
.find_widget_under_pointer(pos)
.map(|widget| widget.id());
}
root.root_state_mut().needs_paint = true;
@ -701,7 +701,7 @@ pub(crate) fn run_update_pointer_pass(root: &mut RenderRoot) {
let mut next_hovered_widget = if let Some(pos) = pointer_pos {
// TODO - Apply scale?
root.get_root_widget()
.find_widget_at_pos(pos)
.find_widget_under_pointer(pos)
.map(|widget| widget.id())
} else {
None

View File

@ -514,7 +514,7 @@ impl TestHarness {
if self
.render_root
.get_root_widget()
.find_widget_at_pos(widget_center)
.find_widget_under_pointer(widget_center)
.map(|w| w.id())
!= Some(id)
{

View File

@ -22,7 +22,7 @@ use crate::AsAny;
use crate::core::{
AccessCtx, AccessEvent, BoxConstraints, ComposeCtx, EventCtx, LayoutCtx, PaintCtx,
PointerEvent, PropertiesMut, PropertiesRef, QueryCtx, RegisterCtx, TextEvent, Update,
UpdateCtx, Widget, WidgetId, WidgetPod, WidgetRef, find_widget_at_pos,
UpdateCtx, Widget, WidgetId, WidgetPod, WidgetRef, find_widget_under_pointer,
};
use crate::kurbo::{Point, Size};
use crate::widgets::SizedBox;
@ -454,13 +454,13 @@ impl<S: 'static> Widget for ModularWidget<S> {
CursorIcon::Default
}
fn find_widget_at_pos<'c>(
fn find_widget_under_pointer<'c>(
&'c self,
ctx: QueryCtx<'c>,
props: PropertiesRef<'c>,
pos: Point,
) -> Option<WidgetRef<'c, dyn Widget>> {
find_widget_at_pos(
find_widget_under_pointer(
&WidgetRef {
widget: self,
properties: props,
@ -718,13 +718,13 @@ impl<W: Widget> Widget for Recorder<W> {
self.child.get_cursor(ctx, pos)
}
fn find_widget_at_pos<'c>(
fn find_widget_under_pointer<'c>(
&'c self,
ctx: QueryCtx<'c>,
props: PropertiesRef<'c>,
pos: Point,
) -> Option<WidgetRef<'c, dyn Widget>> {
self.child.find_widget_at_pos(ctx, props, pos)
self.child.find_widget_under_pointer(ctx, props, pos)
}
fn type_name(&self) -> &'static str {