Skip to content

Commit

Permalink
Store both the rounded and unrounded node size in Node (#9923)
Browse files Browse the repository at this point in the history
# Objective

Text bounds are computed by the layout algorithm using the text's
measurefunc so that text will only wrap after it's used the maximum
amount of available horizontal space.

When the layout size is returned the layout coordinates are rounded and
this sometimes results in the final size of the Node not matching the
size computed with the measurefunc. This means that the text may no
longer fit the horizontal available space and instead wrap onto a new
line. However, no glyphs will be generated for this new line because no
vertical space for the extra line was allocated.

fixes #9874

## Solution

Store both the rounded and unrounded node sizes in `Node`.

Rounding is used to eliminate pixel-wide gaps between nodes that should
be touching edge to edge, but this isn't necessary for text nodes as
they don't have solid edges.

## Changelog

* Added the `rounded_size: Vec2` field to `Node`.
* `text_system` uses the unrounded node size when computing a text
layout.

---------

Co-authored-by: Rob Parrett <robparrett@gmail.com>
  • Loading branch information
ickshonpe and rparrett authored Sep 28, 2023
1 parent 96a7b4a commit edba496
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 2 deletions.
5 changes: 4 additions & 1 deletion crates/bevy_ui/src/layout/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -342,14 +342,17 @@ pub fn ui_layout_system(
inverse_target_scale_factor * Vec2::new(layout.location.x, layout.location.y);

absolute_location += layout_location;

let rounded_size = round_layout_coords(absolute_location + layout_size)
- round_layout_coords(absolute_location);

let rounded_location =
round_layout_coords(layout_location) + 0.5 * (rounded_size - parent_size);

// only trigger change detection when the new values are different
if node.calculated_size != rounded_size {
if node.calculated_size != rounded_size || node.unrounded_size != layout_size {
node.calculated_size = rounded_size;
node.unrounded_size = layout_size;
}
if transform.translation.truncate() != rounded_location {
transform.translation = rounded_location.extend(0.);
Expand Down
10 changes: 10 additions & 0 deletions crates/bevy_ui/src/ui_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ pub struct Node {
/// The size of the node as width and height in logical pixels
/// automatically calculated by [`super::layout::ui_layout_system`]
pub(crate) calculated_size: Vec2,
/// The unrounded size of the node as width and height in logical pixels
/// automatically calculated by [`super::layout::ui_layout_system`]
pub(crate) unrounded_size: Vec2,
}

impl Node {
Expand All @@ -26,6 +29,12 @@ impl Node {
self.calculated_size
}

/// The calculated node size as width and height in logical pixels before rounding
/// automatically calculated by [`super::layout::ui_layout_system`]
pub const fn unrounded_size(&self) -> Vec2 {
self.unrounded_size
}

/// Returns the size of the node in physical pixels based on the given scale factor and `UiScale`.
#[inline]
pub fn physical_size(&self, scale_factor: f64, ui_scale: f64) -> Vec2 {
Expand Down Expand Up @@ -66,6 +75,7 @@ impl Node {
impl Node {
pub const DEFAULT: Self = Self {
calculated_size: Vec2::ZERO,
unrounded_size: Vec2::ZERO,
};
}

Expand Down
5 changes: 4 additions & 1 deletion crates/bevy_ui/src/widget/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,10 @@ fn queue_text(
Vec2::splat(f32::INFINITY)
} else {
// `scale_factor` is already multiplied by `UiScale`
node.physical_size(scale_factor, 1.)
Vec2::new(
(node.unrounded_size.x as f64 * scale_factor) as f32,
(node.unrounded_size.y as f64 * scale_factor) as f32,
)
};

match text_pipeline.queue_text(
Expand Down

0 comments on commit edba496

Please sign in to comment.