bevyengine / bevyengine/bevy

UI node with child text wrapping doesn't account for max width

Open
#21,769 1 comment 1 reaction 0 assignees View on GitHub
A-UI C-Bug D-Complex
Dominant language
Rust
Stars
48.2k
Forks
4.8k
Avg merge
3d 16h
Merged PRs (30d)
171

Description

## Bevy version and features

Bug exists on both `0.17.2` and current main (`7d84e5be`).

## What you did

Have a node with a `max_width` set and `flex_direction` set to `Column`, and add a child node with a large amount of text.

The bug also manifests in a slight different way with an additional intermediate node in-between the root node and the text:

Clipping version

```rust
use bevy::prelude::*;

fn main() -> AppExit {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.run()
}

fn setup(mut commands: Commands) {
commands.spawn(Camera2d);
commands.spawn((
Node {
max_width: Val::Px(360.0),
flex_direction: FlexDirection::Column,
..default()
},
BackgroundColor(bevy::color::palettes::tailwind::NEUTRAL_700.into()),
children![
Text::new("Tis true without lying, certain and most true."),
Text::new("That which is below is like that which is above"),
Text::new("And that which is above is like that which is below"),
Text::new("To do the miracles of one only thing"),
Text::new("And as all things have been and arose"),
Text::new("From one by the mediation of one:"),
Text::new("So all things have their birth"),
Text::new("From this one thing by adaptation."),
],
));
}
```


Overflowing version

```rust
use bevy::prelude::*;

fn main() -> AppExit {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.run()
}

fn setup(mut commands: Commands) {
commands.spawn(Camera2d);
commands.spawn((
Node {
max_width: Val::Px(360.0),
flex_direction: FlexDirection::Column,
..default()
},
children![(
Node {
flex_direction: FlexDirection::Column,
..default()
},
BackgroundColor(bevy::color::palettes::tailwind::NEUTRAL_700.into()),
children![
Text::new("Tis true without lying, certain and most true."),
Text::new("That which is below is like that which is above"),
Text::new("And that which is above is like that which is below"),
Text::new("To do the miracles of one only thing"),
Text::new("And as all things have been and arose"),
Text::new("From one by the mediation of one:"),
Text::new("So all things have their birth"),
Text::new("From this one thing by adaptation."),
]
)],
));
}
```


## What went wrong

It appears that the root node calculates its height, by accounting for text wrapping, as though no `max_width` was set. It does not account for the greater amount of text wrapping that occurs due to the reduced `max_width`.

In the aforementioned "clipping" case, wrapped lines of text get clipped and hidden when they shouldn't be.
In the "overflowing" case, all the text is visible, but the node fails to grow to the required height, so the text overflows.

## Additional information

These videos also include a separate node configured without a `max_width` set, to showcase how the bugged nodes are calculating their width in exactly the same way, despite that being incorrect for their configured `max_width`.

Clipping video

https://github.com/user-attachments/assets/a7a36f83-407a-4325-b55a-6593018fd839

Overflowing video

https://github.com/user-attachments/assets/00e6ba4b-d73c-42a2-b098-c14bd504f296


Contributor guide

Open the contributing guide

Research direction

No source file or test is named in the report. Start by reproducing the clipping and overflowing examples on current main, then trace Bevy's UI node layout handling for max_width and wrapped child text. Done means constrained nodes calculate enough height for wrapped text, including the intermediate-node case, with regression coverage.

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
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.