From 49dd0334cf3720d50039d797c39f7fb2e1b03b59 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Thu, 28 Sep 2023 23:42:13 +0100 Subject: [PATCH] Store both the rounded and unrounded node size in Node (#9923) # 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 --- crates/bevy_ui/src/layout/mod.rs | 5 ++++- crates/bevy_ui/src/ui_node.rs | 10 ++++++++++ crates/bevy_ui/src/widget/text.rs | 5 ++++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ui/src/layout/mod.rs b/crates/bevy_ui/src/layout/mod.rs index fbf7a986381d6..a38209e283a8a 100644 --- a/crates/bevy_ui/src/layout/mod.rs +++ b/crates/bevy_ui/src/layout/mod.rs @@ -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.); diff --git a/crates/bevy_ui/src/ui_node.rs b/crates/bevy_ui/src/ui_node.rs index 420058f35c857..d378806bfff3c 100644 --- a/crates/bevy_ui/src/ui_node.rs +++ b/crates/bevy_ui/src/ui_node.rs @@ -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 { @@ -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 { @@ -66,6 +75,7 @@ impl Node { impl Node { pub const DEFAULT: Self = Self { calculated_size: Vec2::ZERO, + unrounded_size: Vec2::ZERO, }; } diff --git a/crates/bevy_ui/src/widget/text.rs b/crates/bevy_ui/src/widget/text.rs index 9a400ed209147..5ff128ebb6f94 100644 --- a/crates/bevy_ui/src/widget/text.rs +++ b/crates/bevy_ui/src/widget/text.rs @@ -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(