Inconsistent default behavior between UI picking and Interaction
- Dominant language
- Rust
- Stars
- 48.2k
- Forks
- 4.8k
- Avg merge
- 3d 16h
- Merged PRs (30d)
- 171
Description
## Bevy version
0.15.0-rc.3
## Additional information
Bevy currently offers two ways to interact with UI nodes:
- Via the `Interaction` component (old-style)
- Controlled via the `FocusPolicy` component
- Via the UI Picking backend (new-style)
- Controlled via the `PickingBehavior` component
I call them "old-style" and "new-style", because `Interaction` has existed for the entire history of `bevy_ui`, whereas picking is new in 0.15. And AFAIK, the idea is to eventually deprecate and remove `Interaction` and for picking to become the primary way of detecting UI Node interactions.
## What went wrong
Currently, the default behavior differs between the two APIs.
The default `FocusPolicy` is `Pass`, which allows the parent to receive interaction updates when the pointer hovers its children. This is the correct / less surprising default for UI nodes.
The default `PickingBehavior` has `should_block_lower: true`, which prevents the parent from receiving interaction updates when the pointer hovers its children.
This is extremely annoying, because it leads to buggy UI when using picking, unless you override the defaults on almost every entity.
Consider the simple example of a button with some text. If the mouse is over the text, that blocks the button from receiving clicks. I have to manually override the `PickingBehavior` on the text, to get the button to work when clicking on the text.
## Suggested fix
`should_block_lower: true` is a reasonable global default, considering that picking is used for many other cases besides UI, such as interacting with 2D and 3D entities.
However, for UI Nodes, there should be a different default:
```rust
PickingBehavior {
should_block_lower: false,
is_hoverable: true,
}
```
This can be done by adding `PickingBehavior` to `Node`'s required components list, with a non-default constructor.
## Open questions
- Should `Text` entities also set `is_hoverable: false` (aka `PickingBehavior::IGNORE`)?
Text is generally expected to be non-interactable. In the use cases where it is (such as hyperlinks), the user can override the default.
- How to prevent picking from going past the root node?
Typically, users expect that UI blocks the cursor from interacting with the game world behind it. The suggested change above would fix the behavior in the context of nested UI, but the new default would also apply to the root node, causing picking to go past the UI and into the 2D/3D entities rendered behind it. This is undesirable.
It is easy enough to workaround (users can just override the default on their UI root node; certainly better than having to override the default on *every other* node).
This issue did not exist with the old-style `Interaction` API, because it is UI-specific, whereas picking is more general.
Contributor guide
Research direction
Start by comparing the Node required-components setup with the default FocusPolicy and PickingBehavior behavior used by UI picking. Reproduce the nested button-and-text interaction described in the issue, then determine how UI roots should block lower-world entities and whether Text should be non-hoverable. Done means the defaults are consistent without per-entity overrides and the root-node behavior is defined.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- game-dev
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100