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

Remove bevy_ui::Interaction in favor of a bevy_picking based solution #15550

Open
alice-i-cecile opened this issue Sep 30, 2024 · 3 comments · May be fixed by #15597
Open

Remove bevy_ui::Interaction in favor of a bevy_picking based solution #15550

alice-i-cecile opened this issue Sep 30, 2024 · 3 comments · May be fixed by #15597
Assignees
Labels
A-Picking Pointing at and selecting objects of all sorts A-UI Graphical user interfaces, styles, layouts, and widgets C-Code-Quality A section of code that is hard to understand or change C-Feature A new feature, making something new possible C-Usability A targeted quality-of-life change that makes Bevy easier to use D-Complex Quite challenging from either a design or technical perspective. Ask for help! S-Needs-Design This issue requires design work to think about how it would best be accomplished
Milestone

Comments

@alice-i-cecile
Copy link
Member

bevy_ui's Interaction component is clunky and crude. Now that we have proper picking support, we should replace it completely in some form. See #8157, #10141, #9240, #7257, #420, #5769, #1239 and #2322 for prior related discussions.

I'm aiming to tackle this over the next week as my primary task in the lead-up to the Bevy 0.15 release candidate :)

@alice-i-cecile alice-i-cecile added C-Feature A new feature, making something new possible A-UI Graphical user interfaces, styles, layouts, and widgets C-Code-Quality A section of code that is hard to understand or change C-Usability A targeted quality-of-life change that makes Bevy easier to use S-Needs-Design This issue requires design work to think about how it would best be accomplished A-Picking Pointing at and selecting objects of all sorts labels Sep 30, 2024
@alice-i-cecile alice-i-cecile self-assigned this Sep 30, 2024
@alice-i-cecile alice-i-cecile added this to the 0.15 milestone Sep 30, 2024
@alice-i-cecile
Copy link
Member Author

alice-i-cecile commented Oct 1, 2024

Bevy's Interaction component is very simple.

enum Interaction {
    Pressed,
    Hovered,
    None
}

Ever since its inception, it has had a number of serious problems:

  1. It lives in bevy_ui, despite covering concepts "pressed" and "hovered" that are useful when referring to gameplay entities, including in games that don't use bevy_ui.
  2. It doesn't cover related but distinct states that interact with the same properties: "focused" and "grayed out".
  3. It's nowhere near sophisticated enough to handle complex but common behaviors like drag-and-drop.
  4. Detecting if a button has been "just pressed" requires an O(1) check using change detection for each button.
  5. Information about which button was pressed is lost, and so this cannot be reused to e.g. handle right clicks to open a context menu.

With the successful creation of bevy_picking (upstreamed from bevy_mod_picking), the time is right to finally improve the situation.

Use cases

There are several things you might want to do with an Interaction-shaped solution.
Let's lay them out, so we can evaluate each proposed solution against them.

Use case: clicking a button with the mouse

The user moves their mouse over a button and left-clicks it. The button is fired as soon as the button is pressed.

Use case: clicking a focused button using a gamepad or keyboard

Suppose we have some way of determining which element is focused.

The user presses Enter or A on their gamepad, and the focused button is fired as soon as the button is pressed.

Use case: context menus

The user moves their mouse over an element and right-clicks it. A context menu is opened with information and actions to perform relating to that object.

Use case: drag and drop

The user moves their mouse over an object, presses left click, moves their mouse somewhere else and releases. The object is moved (in a literal or abstract sense) to the new destination.

Use case: draggable tabs

The application has a collection of tabs, representing different workspaces.

If a user presses and releases the left mouse button on the tab without dragging (a click), the tab is selected.

But if the user instead clicks, drags and then releases, the tab is moved without focus swapping.

Use case: tooltips

The user moves their mouse over a button, waits for a second, but does not press any buttons. Some explanatory text appears about that object.

Use case: Dynamic button styling

All buttons in an app dynamically change their style (font, color, texture, drop shadow etc) based on their state.
We want the following states to be distinguishable:

  1. Grayed out: the button is disabled for some reason.
  2. Pressed: the left-mouse button or equivalent is currently depressed over top of the button.
  3. Hovered: the pointer is over top of the button.
  4. Neutral: no interactions with the button are happening.

Grayed-out state should take priority over pressed should take priority over hover should take priority over neutral.

Additionally, we need to be able to distinguish where or not an element is focused.

Proposed solutions

We could solve this in several ways.

Solution: boolean flags

Keep track of the following properties for each button-like UI element: hovered, pressed, focused and disabled.

For an initial implementation, only hovered and pressed will be set by the engine: the other fields are just dummies to allow users to hook into them in a future-compatible way.

This can be done in a single component, or a set of related components, which can be gathered via a QueryData impl. A single ElementState component is easier to work with but couples all of these notions together, which may or may not be desirable.

These values are updated by listening for picking events, except for hovering, which uses bevy_picking's hover map directly.

This solution has the advantage of making dynamic button-styling super easy: the data needed is directly on the object, and we can match on the boolean flags to determine how to style our objects.

Solution: yeet it completely

bevy_picking emits a wide range of events.

Rather than trying to reconstruct an event stream from stateful information (as is currently done in Bevy 0.14),
users should simple read these events to perform actions.

The only use case where this stateful information is helpful is for dynamic button styling. In that case, a dedicated GrayedOut (bikeshed pending) component can be combined with the HoverMap resource to extract the required information on a dynamic, as-needed basis.

This design requires a bit more complexity for the users and has a more painful migration path, but is much more CPU and memory efficient.

Path forward

I'm going to start implementing this tomorrow, aiming for a "just yeet it solution". If I can't get the ergonomics and clarity to where I want it to, I'll add a dedicated ElementPointerState component to replace Interaction, but encourage users to use events for every use case other than dynamic button styling.

@BenjaminBrienen
Copy link
Contributor

BenjaminBrienen commented Oct 14, 2024

Another example of Hover/Pressed outside of UI is minecraft's feature where looking at a block gives it a black outline/border and clicking starts to break the block. This is a great place to use picking. Minecraft clones are a huge userbase! 😄

@BenjaminBrienen BenjaminBrienen added the D-Straightforward Simple bug fixes and API improvements, docs, test and examples label Oct 14, 2024
@alice-i-cecile alice-i-cecile added D-Complex Quite challenging from either a design or technical perspective. Ask for help! and removed D-Straightforward Simple bug fixes and API improvements, docs, test and examples labels Nov 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Picking Pointing at and selecting objects of all sorts A-UI Graphical user interfaces, styles, layouts, and widgets C-Code-Quality A section of code that is hard to understand or change C-Feature A new feature, making something new possible C-Usability A targeted quality-of-life change that makes Bevy easier to use D-Complex Quite challenging from either a design or technical perspective. Ask for help! S-Needs-Design This issue requires design work to think about how it would best be accomplished
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants