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

WIP: Support scrolling (Overflow::Scroll) in bevy_ui #8104

Closed
wants to merge 9 commits into from

Conversation

nicoburns
Copy link
Contributor

@nicoburns nicoburns commented Mar 16, 2023

Objective

Allow users to create scrollable regions using bevy_ui.
Fixes #8074

Prior Art

https://github.com/Piefayth/bevy_ui_scroll

Solution

This is adapted from the scrolling implementation in the examples/ui/ui.rs example, but is improved in that it does not use the style system in order to apply offsets (which requires a relayout each time the scroll position changes, and interferes with users wanting to use that same style):

  • A new Overflow::Scroll variant has been added to the Overflow enum to mark nodes that should be scrollable
  • A system has been added that responds to mousewheel events and updates the scroll position
  • Nodes are laid out like usual (layout using Taffy is not affected)
  • The position of nodes is offset by the scroll position of their parent after layout while performing transform change detection

Requirements from Taffy:

Tasks

  • Add Overflow::Scroll variant to Overflow enum
  • Add ScrollPosition component
  • Add update_scroll_position system
  • Only scroll the item under the cursor
  • Horizontal scrolling
  • Separate Overflow in each axis (Split UI Overflow by axis #8095)
  • Update scrolling on relayout (height of node or contents may have changed)
  • Make ScrollPosition component optional for ui nodes to avoid checking every node on scroll
  • AccessKit (need to export scroll_x/scroll_y and scroll_x_max/scroll_y_max to accessibility tree)
  • Touch-based scrolling for mobile platforms
  • Nested scrollviews

Changelog

  • Add support for Overflow::Scroll

@nicoburns nicoburns added the A-UI Graphical user interfaces, styles, layouts, and widgets label Mar 16, 2023
@doup doup mentioned this pull request Mar 22, 2023
@nicoburns nicoburns force-pushed the overflow-scroll branch 3 times, most recently from 29aa3a0 to 4379fd5 Compare April 18, 2023 12:35
@nicoburns nicoburns added C-Feature A new feature, making something new possible M-Needs-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide labels Apr 18, 2023
@github-actions
Copy link
Contributor

It looks like your PR is a breaking change, but you didn't provide a migration guide.

Could you add some context on what users should update when this change get released in a new version of Bevy?
It will be used to help writing the migration guide for the version. Putting it after a ## Migration Guide will help it get automatically picked up by our tooling.

@nicoburns nicoburns removed the M-Needs-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide label Apr 18, 2023
@nicoburns
Copy link
Contributor Author

Hmm... This adds new variants to an enum and a new component to a bundle. Would bevy consider that a breaking change?

@viridia
Copy link
Contributor

viridia commented Dec 2, 2023

@nicoburns Question I have is, how will games control the appearance of the scrollbars? On the web, the ability to customize the look of scrollbars is quite limited (more so in some browsers than others), and the web community has generally punted on this issue by converging on a fairly common (and boring) visual design for scrollbars which nearly every UI toolkit adheres to. However, games want novel and fancy UI designs, and I think scrollbars are no exception. Granted, not many game HUDs have scrollbars, but there are exceptions:

  • Chat logs in multiplayer games
  • Quest and Lore pages
  • Character bios in RPGs
  • Help pages
  • etc.

Editors and other tools built on Bevy UI are of course going to have lots of scrollbars, but their need for visual novelty is not quite as acute.

A related question is the API for controlling scroll position. "Scroll selected item into view" is probably the most common of these. If scrolling is just mucking around with the node transform, then user code which explicitly sets the translation is probably going to mess things up. We really ought to define an API to let user code do the standard things (scroll to top, scroll to bottom, show selected, etc.) without having to directly touch the node transforms.

As someone in the process of writing a UI framework, I'm perfectly prepared to do my own "scrollview" widget implementation if that's what it takes. I know that on the web, scrolling is a style property that is inherent in every element, but that is not the case in desktop toolkits such as Java Swing or Qt, where scrolling is a specific widget type. I actually think that model may be more appropriate, given the general rarity of scrolling views in games, but the question then becomes where do we draw the line between the responsibilities of bevy_ui and the responsibilities of the toolkit?

@alice-i-cecile alice-i-cecile removed this from the 0.13 milestone Jan 24, 2024
github-merge-queue bot pushed a commit that referenced this pull request Apr 30, 2024
# Objective

- Enables support for `Display::Block`
- Enables support for `Overflow::Hidden`
- Allows for cleaner integration with text, image and other content
layout.
- Unblocks #8104
- Unlocks the possibility of Bevy creating a custom layout tree over
which Taffy operates.
- Enables #8808 / #10193 to remove a Mutex around the font system.

## Todo

- [x] ~Fix rendering of text/images to account for padding/border on
nodes (should size/position to content box rather than border box)~ In
order get this into a mergeable state this PR instead zeroes out
padding/border when syncing leaf node styles into Taffy to preserve the
existing behaviour. #6879 can
be fixed in a followup PR.

## Solution

- Update the version of Taffy
- Update code to work with the new version

Note: Taffy 0.4 has not yet been released. This PR is being created in
advance of the release to ensure that there are no blockers to upgrading
once the release occurs.

---

## Changelog

- Bevy now supports the `Display::Block` and `Overflow::Hidden` styles.
@zmarlon
Copy link
Contributor

zmarlon commented May 26, 2024

Is there anything new to announce?

Piefayth added a commit to Piefayth/bevy that referenced this pull request Sep 16, 2024
Piefayth added a commit to Piefayth/bevy that referenced this pull request Sep 16, 2024
@Piefayth Piefayth mentioned this pull request Sep 18, 2024
3 tasks
@alice-i-cecile
Copy link
Member

Closing as adopted <3

github-merge-queue bot pushed a commit that referenced this pull request 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 this pull request may close these issues.

Support scrollable UI nodes (Overflow::Scroll?)
4 participants