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

Do all widget interaction at the start of the frame #3936

Closed
Tracked by #3284
emilk opened this issue Feb 1, 2024 · 2 comments · Fixed by #4026
Closed
Tracked by #3284

Do all widget interaction at the start of the frame #3936

emilk opened this issue Feb 1, 2024 · 2 comments · Fixed by #4026

Comments

@emilk
Copy link
Owner

emilk commented Feb 1, 2024

Context uses WidgetRects to store the rectangles of all widgets from the previous frame. Context::interact uses this to check that no tother widget is covering the current widget.

This code could be simplified by doing the hit-test once at the start of the frame, using the WidgetRects from the previous frame.

With full knowledge, we could to a smart hit-test where we look for all widget within a certain small radius of the pointer, and hover/click/drag the closest one. This allows for slightly bigger hit-boxes, making things easier to click. This could replace the current hack of growing hit boxes by item_spacing / 2.

Note that we needs to find the top widget for clicks and for drags separately, because the top widget that is click-sensitive (e.g. a button) can be different from the top widget that is drag-sensitive (e.g. a ScrollArea).

@emilk emilk added this to the Next Major Release milestone Feb 1, 2024
@emilk emilk added the egui label Feb 1, 2024
@abey79
Copy link
Collaborator

abey79 commented Feb 1, 2024

Side note for drag-sensitive areas, in the context of e.g. hierarchical list (as in #3903). It's rather important that the drop zone be exactly contiguous. Otherwise, this could introduce a discontinuity when the mouse moves from the bottom of a widget to the top of the next one, with the insertion marker disappearing and the drop potentially failing.

Export-1706807772224

Here are the drop zones for this example:

image

This is easy to achieve with custom widget and item_spacing = 0, as the response.rect are already that way. It's slightly less convenient with non-zero spacing.

@emilk emilk changed the title Refactor hit-test click/drag interaction code Improve hit-test click/drag interaction code Feb 2, 2024
@emilk
Copy link
Owner Author

emilk commented Feb 10, 2024

I just realized this is going to make styling a lot easier, since we know before adding a widget wether or not it is hovered, dragged etc, which means we can pick its colors (and margins) based on ints interaction state before allocating space for the widget!

This makes this issue an important part of:

@emilk emilk mentioned this issue Feb 10, 2024
8 tasks
@emilk emilk changed the title Improve hit-test click/drag interaction code Do all widget interaction at the start of the frame Feb 10, 2024
@emilk emilk mentioned this issue Feb 11, 2024
14 tasks
emilk added a commit that referenced this issue Feb 17, 2024
* Closes #3936
* Closes #3923
* Closes #4058

The interaction code is now done at the start of the frame, using stored
`WidgetRect`s from the previous frame.

The intention is that the new interaction code should be more accurate,
making it easier to hit widgets, and better respecting the rules of
overlapping widgets.

There is a new `style::Interaction::interact_radius` controlling how far
away from a widget the cursor can be and still hit it. This helps big
fat fingers hit small widgets on touch screens.

This PR adds a new `Context::read_response` which lets you read the
`Response` of a `Widget` _before_ you create the widget. This can be
used for styling, or for reading the result of an interaction early (to
prevent frame-delay) for a widget you add late (so it is on top of other
widgets).

# ⚠️ BREAKING CHANGES
`Memory::dragged_id`, `Memory::set_dragged_id` etc have been moved to
`Context`.
The semantics for `Context::dragged_id` is slightly different: a widget
is not considered dragged until egui it is sure this is not a
click-in-progress. For a widget that is only sensitive to drags, that is
right away, but for widgets sensitive to both clicks and drags it is not
until the mouse has moved a certain distance.

# TODO
* [x] Fix panel resizing
* [x] Fix scroll hover weirdness
* [x] Fix Resize widget
* [x] Fix drag-and-drop
* [x] Test all of egui_demo_app
* [x] Change `is_dragging` API
* [x] Consistent naming of start/stop or begin/end drag
* [x] Test `egui_tiles`
* [x] Test Rerun
* [x] Document
* [x] Document breaking changes in PR description
* [x] Test one final time

# Saving for a later PR
* [ ] Fix #4047
* [ ] Specify what the response order for e.g. `ui.horizontal` is

I think both these can be fixed if each `Ui` registers themselves as a
`WidgetRect`, with the possibility to interact with it later, as if the
interaction was under all widgets on top of it.
emilk added a commit to emilk/egui_plot that referenced this issue Jul 15, 2024
* Closes emilk/egui#3936
* Closes emilk/egui#3923
* Closes emilk/egui#4058

The interaction code is now done at the start of the frame, using stored
`WidgetRect`s from the previous frame.

The intention is that the new interaction code should be more accurate,
making it easier to hit widgets, and better respecting the rules of
overlapping widgets.

There is a new `style::Interaction::interact_radius` controlling how far
away from a widget the cursor can be and still hit it. This helps big
fat fingers hit small widgets on touch screens.

This PR adds a new `Context::read_response` which lets you read the
`Response` of a `Widget` _before_ you create the widget. This can be
used for styling, or for reading the result of an interaction early (to
prevent frame-delay) for a widget you add late (so it is on top of other
widgets).

# ⚠️ BREAKING CHANGES
`Memory::dragged_id`, `Memory::set_dragged_id` etc have been moved to
`Context`.
The semantics for `Context::dragged_id` is slightly different: a widget
is not considered dragged until egui it is sure this is not a
click-in-progress. For a widget that is only sensitive to drags, that is
right away, but for widgets sensitive to both clicks and drags it is not
until the mouse has moved a certain distance.

# TODO
* [x] Fix panel resizing
* [x] Fix scroll hover weirdness
* [x] Fix Resize widget
* [x] Fix drag-and-drop
* [x] Test all of egui_demo_app
* [x] Change `is_dragging` API
* [x] Consistent naming of start/stop or begin/end drag
* [x] Test `egui_tiles`
* [x] Test Rerun
* [x] Document
* [x] Document breaking changes in PR description
* [x] Test one final time

# Saving for a later PR
* [ ] Fix emilk/egui#4047
* [ ] Specify what the response order for e.g. `ui.horizontal` is

I think both these can be fixed if each `Ui` registers themselves as a
`WidgetRect`, with the possibility to interact with it later, as if the
interaction was under all widgets on top of it.
hacknus pushed a commit to hacknus/egui that referenced this issue Oct 30, 2024
* Closes emilk#3936
* Closes emilk#3923
* Closes emilk#4058

The interaction code is now done at the start of the frame, using stored
`WidgetRect`s from the previous frame.

The intention is that the new interaction code should be more accurate,
making it easier to hit widgets, and better respecting the rules of
overlapping widgets.

There is a new `style::Interaction::interact_radius` controlling how far
away from a widget the cursor can be and still hit it. This helps big
fat fingers hit small widgets on touch screens.

This PR adds a new `Context::read_response` which lets you read the
`Response` of a `Widget` _before_ you create the widget. This can be
used for styling, or for reading the result of an interaction early (to
prevent frame-delay) for a widget you add late (so it is on top of other
widgets).

# ⚠️ BREAKING CHANGES
`Memory::dragged_id`, `Memory::set_dragged_id` etc have been moved to
`Context`.
The semantics for `Context::dragged_id` is slightly different: a widget
is not considered dragged until egui it is sure this is not a
click-in-progress. For a widget that is only sensitive to drags, that is
right away, but for widgets sensitive to both clicks and drags it is not
until the mouse has moved a certain distance.

# TODO
* [x] Fix panel resizing
* [x] Fix scroll hover weirdness
* [x] Fix Resize widget
* [x] Fix drag-and-drop
* [x] Test all of egui_demo_app
* [x] Change `is_dragging` API
* [x] Consistent naming of start/stop or begin/end drag
* [x] Test `egui_tiles`
* [x] Test Rerun
* [x] Document
* [x] Document breaking changes in PR description
* [x] Test one final time

# Saving for a later PR
* [ ] Fix emilk#4047
* [ ] Specify what the response order for e.g. `ui.horizontal` is

I think both these can be fixed if each `Ui` registers themselves as a
`WidgetRect`, with the possibility to interact with it later, as if the
interaction was under all widgets on top of it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants