Skip to content

Commit

Permalink
masonry: reimplement Widget::get_child_at_pos
Browse files Browse the repository at this point in the history
  • Loading branch information
tomcur committed Aug 29, 2024
1 parent ac95f25 commit f569230
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 19 deletions.
37 changes: 28 additions & 9 deletions masonry/src/widget/widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ use vello::Scene;

use crate::contexts::ComposeCtx;
use crate::event::{AccessEvent, PointerEvent, StatusChange, TextEvent};
use crate::widget::WidgetRef;
use crate::{
AccessCtx, AsAny, BoxConstraints, EventCtx, LayoutCtx, LifeCycle, LifeCycleCtx, PaintCtx, Size,
AccessCtx, AsAny, BoxConstraints, EventCtx, LayoutCtx, LifeCycle, LifeCycleCtx, PaintCtx,
Point, Size,
};

/// A unique identifier for a single [`Widget`].
Expand Down Expand Up @@ -165,20 +167,29 @@ pub trait Widget: AsAny {

// --- Auto-generated implementations ---

// FIXME
#[cfg(FALSE)]
/// Return which child, if any, has the given `pos` in its layout rect.
///
/// The child return is a direct child, not eg a grand-child. The position is in
/// The child returned is a direct child, not eg a grand-child. The position is in
/// relative coordinates. (Eg `(0,0)` is the top-left corner of `self`).
///
/// Has a default implementation, that can be overridden to search children more
/// efficiently.
fn get_child_at_pos(&self, pos: Point) -> Option<WidgetRef<'_, dyn Widget>> {
// layout_rect() is in parent coordinate space
self.children()
.into_iter()
.find(|child| child.state().layout_rect().contains(pos))
///
/// The child widget references in `children` are in the same order as returned by
/// [`Self::children_ids`].
fn get_child_at_pos<'w>(
&self,
children: &[WidgetRef<'w, dyn Widget>],
pos: Point,
) -> Option<WidgetRef<'w, dyn Widget>> {
children
.iter()
// TODO: currently assumes `Self::children_ids` is in increasing "z-order". Picks the
// last child in case of overlapping children. Is this the desired behavior?
.rev()
// A child's layout_rect() is in the parent's coordinate space.
.find(|child| !child.widget.skip_pointer() && child.state().layout_rect().contains(pos))
.copied()
}

/// Get the (verbose) type name of the widget for debugging purposes.
Expand Down Expand Up @@ -354,6 +365,14 @@ impl Widget for Box<dyn Widget> {
self.deref().get_cursor()
}

fn get_child_at_pos<'w>(
&self,
children: &[WidgetRef<'w, dyn Widget>],
pos: Point,
) -> Option<WidgetRef<'w, dyn Widget>> {
self.deref().get_child_at_pos(children, pos)
}

fn as_any(&self) -> &dyn Any {
self.deref().as_any()
}
Expand Down
21 changes: 11 additions & 10 deletions masonry/src/widget/widget_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,31 +172,32 @@ impl<'w> WidgetRef<'w, dyn Widget> {
///
/// **pos** - the position in local coordinates (zero being the top-left of the
/// inner widget).
pub fn find_widget_at_pos(&self, pos: Point) -> Option<WidgetRef<'w, dyn Widget>> {
pub fn find_widget_at_pos(&self, mut pos: Point) -> Option<WidgetRef<'w, dyn Widget>> {
let mut innermost_widget: WidgetRef<'w, dyn Widget> = *self;

if !self.state().layout_rect().contains(pos) {
return None;
}

// TODO - Rewrite more elegantly
loop {
if let Some(clip) = innermost_widget.state().clip {
let relative_pos = pos.to_vec2() - innermost_widget.state().window_origin.to_vec2();
// If the widget has a clip, the point must be inside
// else we don't iterate over children.
if !clip.contains(relative_pos.to_point()) {
// If the widget has a clip, the point must be inside, else we don't iterate over
// children.
if !clip.contains(pos) {
break;
}
}
// TODO - Use Widget::get_child_at_pos method
if let Some(child) = innermost_widget.children().into_iter().find(|child| {
!child.widget.skip_pointer() && child.state().window_layout_rect().contains(pos)
}) {

if let Some(child) = innermost_widget
.widget
.get_child_at_pos(&innermost_widget.children(), pos)
{
innermost_widget = child;
} else {
break;
}

pos -= innermost_widget.state().layout_rect().origin().to_vec2();
}

Some(innermost_widget)
Expand Down

0 comments on commit f569230

Please sign in to comment.