Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

masonry: set pointer-captured ancestors of hover target as hot #572

Closed
wants to merge 2 commits into from

Conversation

tomcur
Copy link
Member

@tomcur tomcur commented Sep 3, 2024

Currently when a widget has pointer capture and the pointer is over one of its descendants, no widget is considered hot. This changes the hot logic such that the widget with the pointer capture and its ancestors are considered hot, but not its descendants.

The current behavior is troublesome. For example, when applying the following patch, a button does not register as having been pressed when the pointer is pressed while over its descendant label. That's because during the PointerUp event, the button is not considered hot (it has captured the pointer, but the pointer is not directly over the button: it's over its descendant label).

diff --git a/masonry/src/widget/widget_ref.rs b/masonry/src/widget/widget_ref.rs
index 2f85ea5..3494fcd 100644
--- a/masonry/src/widget/widget_ref.rs
+++ b/masonry/src/widget/widget_ref.rs
@@ -191,7 +191,8 @@ impl<'w> WidgetRef<'w, dyn Widget> {
             }
             // 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)
+                child.state().window_layout_rect().contains(pos)
             }) {
                 innermost_widget = child;
             } else {

Currently when a widget has pointer capture and the pointer is over one
of its descendants, no widget is considered hot. This changes the hot
logic such that the widget with the pointer capture and its ancestors
are are considered hot, but not its descendants.
next_hovered_path
.into_iter()
.skip_while(|id| *id != capture_target)
.collect()
Copy link
Member Author

@tomcur tomcur Sep 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a potentially expensive memory operation that might be avoidable. We could short-circuit if there is a pointer capture target by traversing up the tree from that target, and then traverse downwards again to determine whether the pointer is inside the widget's and its ancestors' layout boxes. The search with find_widget_at_pos is then also unnecessary.

(For keyboard focus, the focus chain is kept in the widget state, something similar could be considered for pointer capture targets.)

@PoignardAzur
Copy link
Contributor

I think the current design is closer to right.

Part of the problem is that child label widgets are currently used to implement text layout for buttons and checkboxes, but really, button and checkboxes should fundamentally be unified widgets without children. skip_pointer is mostly an escape hatch.

@tomcur
Copy link
Member Author

tomcur commented Sep 10, 2024

I guess that makes sense, with has_pointer_capture being true exactly when that specific widget has captured the pointer (and not a descendant). For the keyboard, has_focus is true when any descendant has focus, though.

@PoignardAzur
Copy link
Contributor

For the keyboard, has_focus is true when any descendant has focus, though.

We could probably rename it, it's not a great name.

@PoignardAzur
Copy link
Contributor

I'd suggest closing this PR and renaming has_focus to subtree_has_focus in a different PR, if that's fine with you?

@tomcur
Copy link
Member Author

tomcur commented Sep 27, 2024

I agree.

button and checkboxes should fundamentally be unified widgets without children. skip_pointer is mostly an escape hatch.

An alternative to unified widgets, could be to generalize skip_pointer to something like non-interactive subtrees: then you're still able to compose widgets (e.g., a button with an arbitrary widget acting as an icon).

@PoignardAzur
Copy link
Contributor

Yeah, I do see the appeal. "Non-interactive" would basically be a top-down flag like "disabled" and "stashed" currently work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants