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

Support scrollable UI nodes (Overflow::Scroll?) #8074

Closed
nicoburns opened this issue Mar 13, 2023 · 0 comments · Fixed by #15291
Closed

Support scrollable UI nodes (Overflow::Scroll?) #8074

nicoburns opened this issue Mar 13, 2023 · 0 comments · Fixed by #15291
Labels
A-UI Graphical user interfaces, styles, layouts, and widgets C-Feature A new feature, making something new possible

Comments

@nicoburns
Copy link
Contributor

What problem does this solve or what need does it fill?

The ability to create a scrollable region is a common requirement when building a user interface.

What solution would you like?

Build in support for scrolling enabled by Overflow::Scroll or some similar marker on a node. It should be possible to control whether a node scrolls independently in each axis.

Additional context

Scrolling is currently implemented as an example in the ui/ui.rs example. But this:

  • Requires every user to implement it themselves using raw scrollwheel events
  • Does not support affordances such as scrollbars
  • Is not a great solution as it requires a relayout every time the scroll position changes
@nicoburns nicoburns added C-Feature A new feature, making something new possible S-Needs-Triage This issue needs to be labelled labels Mar 13, 2023
@nicoburns nicoburns changed the title Support Overflow::Scroll Support scrollable UI nodes (Overflow::Scroll) Mar 13, 2023
@nicoburns nicoburns added the A-UI Graphical user interfaces, styles, layouts, and widgets label Mar 13, 2023
@james7132 james7132 removed the S-Needs-Triage This issue needs to be labelled label Mar 13, 2023
@nicoburns nicoburns changed the title Support scrollable UI nodes (Overflow::Scroll) Support scrollable UI nodes (Overflow::Scroll?) Mar 13, 2023
@Piefayth Piefayth mentioned this issue Sep 18, 2024
3 tasks
github-merge-queue bot pushed a commit that referenced this issue Sep 23, 2024
# Objective

- Fixes #8074 
- Adopts / Supersedes #8104

## Solution

Adapted from #8104 and affords the same benefits.

**Additions**
- [x] Update scrolling on relayout (height of node or contents may have
changed)
- [x] Make ScrollPosition component optional for ui nodes to avoid
checking every node on scroll
- [x] Nested scrollviews

**Omissions**
- Removed input handling for scrolling from `bevy_ui`. Users should
update `ScrollPosition` directly.

### Implementation

Adds a new `ScrollPosition` component. Updating this component on a
`Node` with an overflow axis set to `OverflowAxis::Scroll` will
reposition its children by that amount when calculating node transforms.
As before, no impact on the underlying Taffy layout.

Calculating this correctly is trickier than it was in #8104 due to
`"Update scrolling on relayout"`.

**Background**

When `ScrollPosition` is updated directly by the user, it can be
trivially handled in-engine by adding the parent's scroll position to
the final location of each child node. However, _other layout actions_
may result in a situation where `ScrollPosition` needs to be updated.
Consider a 1000 pixel tall vertically scrolling list of 100 elements,
each 100 pixels tall. Scrolled to the bottom, the
`ScrollPosition.offset_y` is 9000, just enough to display the last
element in the list. When removing an element from that list, the new
desired `ScrollPosition.offset_y` is 8900, but, critically, that is not
known until after the sizes and positions of the children of the
scrollable node are resolved.

All user scrolling code today handles this by delaying the resolution by
one frame. One notable disadvantage of this is the inability to support
`WinitSettings::desktop_app()`, since there would need to be an input
AFTER the layout change that caused the scroll position to update for
the results of the scroll position update to render visually.

I propose the alternative in this PR, which allows for same-frame
resolution of scrolling layout.

**Resolution**

_Edit: Below resolution is outdated, and replaced with the simpler usage
of taffy's `Layout::content_size`._

When recursively iterating the children of a node, each child now
returns a `Vec2` representing the location of their own bottom right
corner. Then, `[[0,0, [x,y]]` represents a bounding box containing the
scrollable area filled by that child. Scrollable parents aggregate those
areas into the bounding box of _all_ children, then consider that result
against `ScrollPosition` to ensure its validity.

In the event that resolution of the layout of the children invalidates
the `ScrollPosition` (e.g. scrolled further than there were children to
scroll to), _all_ children of that node must be recursively
repositioned. The position of each child must change as a result of the
change in scroll position.

Therefore, this implementation takes care to only spend the cost of the
"second layout pass" when a specific node actually had a
`ScrollPosition` forcibly updated by the layout of its children.


## Testing

Examples in `ui/scroll.rs`. There may be more complex node/style
interactions that were unconsidered.

---

## Showcase



![scroll](https://github.com/user-attachments/assets/1331138f-93aa-4a8f-959c-6be18a04ff03)

## Alternatives

- `bevy_ui` doesn't support scrolling.
- `bevy_ui` implements scrolling with a one-frame delay on reactions to
layout changes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-UI Graphical user interfaces, styles, layouts, and widgets C-Feature A new feature, making something new possible
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants