From 3b0e81b5b30afec100b27c077df90bf5b391453b Mon Sep 17 00:00:00 2001 From: Ross Leonardy Date: Mon, 27 Oct 2025 20:11:09 -0400 Subject: [PATCH 01/11] Update to taffy 0.9 --- crates/bevy_ui/Cargo.toml | 2 +- crates/bevy_ui/src/layout/convert.rs | 220 ++++++++++++++---------- crates/bevy_ui/src/layout/mod.rs | 2 +- crates/bevy_ui/src/layout/ui_surface.rs | 44 ++++- crates/bevy_ui/src/widget/image.rs | 29 +++- 5 files changed, 190 insertions(+), 107 deletions(-) diff --git a/crates/bevy_ui/Cargo.toml b/crates/bevy_ui/Cargo.toml index 2f1f6a8eaf973..6ae90c9692345 100644 --- a/crates/bevy_ui/Cargo.toml +++ b/crates/bevy_ui/Cargo.toml @@ -34,7 +34,7 @@ bevy_platform = { path = "../bevy_platform", version = "0.18.0-dev", default-fea ] } # other -taffy = { version = "0.7" } +taffy = { version = "0.9", default-features = false, features = ["std", "block_layout", "flexbox", "grid", "content_size", "alloc", "taffy_tree"] } serde = { version = "1", features = ["derive"], optional = true } uuid = { version = "1.1", features = ["v4"], optional = true } thiserror = { version = "2", default-features = false } diff --git a/crates/bevy_ui/src/layout/convert.rs b/crates/bevy_ui/src/layout/convert.rs index 161fc12751650..a7cb894560b56 100644 --- a/crates/bevy_ui/src/layout/convert.rs +++ b/crates/bevy_ui/src/layout/convert.rs @@ -1,4 +1,4 @@ -use taffy::style_helpers; +use taffy::{style_helpers, CheapCloneStr}; use crate::{ AlignContent, AlignItems, AlignSelf, BoxSizing, Display, FlexDirection, FlexWrap, GridAutoFlow, @@ -15,35 +15,33 @@ impl Val { context: &LayoutContext, ) -> taffy::style::LengthPercentageAuto { match self { - Val::Auto => taffy::style::LengthPercentageAuto::Auto, - Val::Percent(value) => taffy::style::LengthPercentageAuto::Percent(value / 100.), - Val::Px(value) => { - taffy::style::LengthPercentageAuto::Length(context.scale_factor * value) + Val::Auto => style_helpers::auto(), + Val::Percent(value) => style_helpers::percent(value / 100.), + Val::Px(value) => style_helpers::length(context.scale_factor * value), + Val::VMin(value) => { + style_helpers::length(context.physical_size.min_element() * value / 100.) } - Val::VMin(value) => taffy::style::LengthPercentageAuto::Length( - context.physical_size.min_element() * value / 100., - ), - Val::VMax(value) => taffy::style::LengthPercentageAuto::Length( - context.physical_size.max_element() * value / 100., - ), - Val::Vw(value) => { - taffy::style::LengthPercentageAuto::Length(context.physical_size.x * value / 100.) - } - Val::Vh(value) => { - taffy::style::LengthPercentageAuto::Length(context.physical_size.y * value / 100.) + Val::VMax(value) => { + style_helpers::length(context.physical_size.max_element() * value / 100.) } + Val::Vw(value) => style_helpers::length(context.physical_size.x * value / 100.), + Val::Vh(value) => style_helpers::length(context.physical_size.y * value / 100.), } } fn into_length_percentage(self, context: &LayoutContext) -> taffy::style::LengthPercentage { - match self.into_length_percentage_auto(context) { - taffy::style::LengthPercentageAuto::Auto => taffy::style::LengthPercentage::Length(0.0), - taffy::style::LengthPercentageAuto::Percent(value) => { - taffy::style::LengthPercentage::Percent(value) + match self { + Val::Auto => style_helpers::length(0.), + Val::Percent(value) => style_helpers::percent(value / 100.), + Val::Px(value) => style_helpers::length(context.scale_factor * value), + Val::VMin(value) => { + style_helpers::length(context.physical_size.min_element() * value / 100.) } - taffy::style::LengthPercentageAuto::Length(value) => { - taffy::style::LengthPercentage::Length(value) + Val::VMax(value) => { + style_helpers::length(context.physical_size.max_element() * value / 100.) } + Val::Vw(value) => style_helpers::length(context.physical_size.x * value / 100.), + Val::Vh(value) => style_helpers::length(context.physical_size.y * value / 100.), } } @@ -146,6 +144,7 @@ pub fn from_node(node: &Node, context: &LayoutContext, ignore_border: bool) -> t .collect::>(), grid_row: node.grid_row.into(), grid_column: node.grid_column.into(), + ..Default::default() } } @@ -335,26 +334,48 @@ impl From for taffy::geometry::Line impl MinTrackSizingFunction { fn into_taffy(self, context: &LayoutContext) -> taffy::style::MinTrackSizingFunction { match self { - MinTrackSizingFunction::Px(val) => taffy::style::MinTrackSizingFunction::Fixed( - Val::Px(val).into_length_percentage(context), + MinTrackSizingFunction::Px(val) => taffy::style::MinTrackSizingFunction::length( + Val::Px(val) + .into_length_percentage(context) + .into_raw() + .value(), ), - MinTrackSizingFunction::Percent(val) => taffy::style::MinTrackSizingFunction::Fixed( - Val::Percent(val).into_length_percentage(context), + MinTrackSizingFunction::Percent(val) => taffy::style::MinTrackSizingFunction::length( + Val::Percent(val) + .into_length_percentage(context) + .into_raw() + .value(), ), - MinTrackSizingFunction::Auto => taffy::style::MinTrackSizingFunction::Auto, - MinTrackSizingFunction::MinContent => taffy::style::MinTrackSizingFunction::MinContent, - MinTrackSizingFunction::MaxContent => taffy::style::MinTrackSizingFunction::MaxContent, - MinTrackSizingFunction::VMin(val) => taffy::style::MinTrackSizingFunction::Fixed( - Val::VMin(val).into_length_percentage(context), + MinTrackSizingFunction::Auto => taffy::style::MinTrackSizingFunction::auto(), + MinTrackSizingFunction::MinContent => { + taffy::style::MinTrackSizingFunction::min_content() + } + MinTrackSizingFunction::MaxContent => { + taffy::style::MinTrackSizingFunction::max_content() + } + MinTrackSizingFunction::VMin(val) => taffy::style::MinTrackSizingFunction::length( + Val::VMin(val) + .into_length_percentage(context) + .into_raw() + .value(), ), - MinTrackSizingFunction::VMax(val) => taffy::style::MinTrackSizingFunction::Fixed( - Val::VMax(val).into_length_percentage(context), + MinTrackSizingFunction::VMax(val) => taffy::style::MinTrackSizingFunction::length( + Val::VMax(val) + .into_length_percentage(context) + .into_raw() + .value(), ), - MinTrackSizingFunction::Vh(val) => taffy::style::MinTrackSizingFunction::Fixed( - Val::Vh(val).into_length_percentage(context), + MinTrackSizingFunction::Vh(val) => taffy::style::MinTrackSizingFunction::length( + Val::Vh(val) + .into_length_percentage(context) + .into_raw() + .value(), ), - MinTrackSizingFunction::Vw(val) => taffy::style::MinTrackSizingFunction::Fixed( - Val::Vw(val).into_length_percentage(context), + MinTrackSizingFunction::Vw(val) => taffy::style::MinTrackSizingFunction::length( + Val::Vw(val) + .into_length_percentage(context) + .into_raw() + .value(), ), } } @@ -363,49 +384,58 @@ impl MinTrackSizingFunction { impl MaxTrackSizingFunction { fn into_taffy(self, context: &LayoutContext) -> taffy::style::MaxTrackSizingFunction { match self { - MaxTrackSizingFunction::Px(val) => taffy::style::MaxTrackSizingFunction::Fixed( + MaxTrackSizingFunction::Px(val) => taffy::style::MaxTrackSizingFunction::length( Val::Px(val).into_length_percentage(context), ), - MaxTrackSizingFunction::Percent(val) => taffy::style::MaxTrackSizingFunction::Fixed( - Val::Percent(val).into_length_percentage(context), - ), - MaxTrackSizingFunction::Auto => taffy::style::MaxTrackSizingFunction::Auto, - MaxTrackSizingFunction::MinContent => taffy::style::MaxTrackSizingFunction::MinContent, - MaxTrackSizingFunction::MaxContent => taffy::style::MaxTrackSizingFunction::MaxContent, + MaxTrackSizingFunction::Percent(val) => { + taffy::style::MaxTrackSizingFunction::percent(val) + } + MaxTrackSizingFunction::Auto => taffy::style::MaxTrackSizingFunction::auto(), + MaxTrackSizingFunction::MinContent => { + taffy::style::MaxTrackSizingFunction::min_content() + } + MaxTrackSizingFunction::MaxContent => { + taffy::style::MaxTrackSizingFunction::max_content() + } MaxTrackSizingFunction::FitContentPx(val) => { - taffy::style::MaxTrackSizingFunction::FitContent( - Val::Px(val).into_length_percentage(context), - ) + taffy::style::MaxTrackSizingFunction::fit_content_px(val) } MaxTrackSizingFunction::FitContentPercent(val) => { - taffy::style::MaxTrackSizingFunction::FitContent( - Val::Percent(val).into_length_percentage(context), - ) + taffy::style::MaxTrackSizingFunction::fit_content_percent(val) } MaxTrackSizingFunction::Fraction(fraction) => { - taffy::style::MaxTrackSizingFunction::Fraction(fraction) + taffy::style::MaxTrackSizingFunction::fr(fraction) } - MaxTrackSizingFunction::VMin(val) => taffy::style::MaxTrackSizingFunction::Fixed( - Val::VMin(val).into_length_percentage(context), + MaxTrackSizingFunction::VMin(val) => taffy::style::MaxTrackSizingFunction::length( + Val::VMin(val) + .into_length_percentage(context) + .into_raw() + .value(), ), - MaxTrackSizingFunction::VMax(val) => taffy::style::MaxTrackSizingFunction::Fixed( - Val::VMax(val).into_length_percentage(context), + MaxTrackSizingFunction::VMax(val) => taffy::style::MaxTrackSizingFunction::length( + Val::VMax(val) + .into_length_percentage(context) + .into_raw() + .value(), ), - MaxTrackSizingFunction::Vh(val) => taffy::style::MaxTrackSizingFunction::Fixed( - Val::Vh(val).into_length_percentage(context), + MaxTrackSizingFunction::Vh(val) => taffy::style::MaxTrackSizingFunction::length( + Val::Vh(val) + .into_length_percentage(context) + .into_raw() + .value(), ), - MaxTrackSizingFunction::Vw(val) => taffy::style::MaxTrackSizingFunction::Fixed( - Val::Vw(val).into_length_percentage(context), + MaxTrackSizingFunction::Vw(val) => taffy::style::MaxTrackSizingFunction::length( + Val::Vw(val) + .into_length_percentage(context) + .into_raw() + .value(), ), } } } impl GridTrack { - fn into_taffy_track( - self, - context: &LayoutContext, - ) -> taffy::style::NonRepeatedTrackSizingFunction { + fn into_taffy_track(self, context: &LayoutContext) -> taffy::style::TrackSizingFunction { let min = self.min_sizing_function.into_taffy(context); let max = self.max_sizing_function.into_taffy(context); style_helpers::minmax(min, max) @@ -413,15 +443,18 @@ impl GridTrack { } impl RepeatedGridTrack { - fn clone_into_repeated_taffy_track( + fn clone_into_repeated_taffy_track( &self, context: &LayoutContext, - ) -> taffy::style::TrackSizingFunction { + ) -> taffy::style::GridTemplateComponent + where + S: CheapCloneStr, + { if self.tracks.len() == 1 && self.repetition == GridTrackRepetition::Count(1) { let min = self.tracks[0].min_sizing_function.into_taffy(context); let max = self.tracks[0].max_sizing_function.into_taffy(context); let taffy_track = style_helpers::minmax(min, max); - taffy::style::TrackSizingFunction::Single(taffy_track) + taffy::GridTemplateComponent::Single(taffy_track) } else { let taffy_tracks: Vec<_> = self .tracks @@ -436,10 +469,10 @@ impl RepeatedGridTrack { match self.repetition { GridTrackRepetition::Count(count) => style_helpers::repeat(count, taffy_tracks), GridTrackRepetition::AutoFit => { - style_helpers::repeat(taffy::style::GridTrackRepetition::AutoFit, taffy_tracks) + style_helpers::repeat(taffy::style::RepetitionCount::AutoFit, taffy_tracks) } GridTrackRepetition::AutoFill => { - style_helpers::repeat(taffy::style::GridTrackRepetition::AutoFill, taffy_tracks) + style_helpers::repeat(taffy::style::RepetitionCount::AutoFill, taffy_tracks) } } } @@ -537,15 +570,15 @@ mod tests { ); assert_eq!( taffy_style.inset.right, - taffy::style::LengthPercentageAuto::Percent(0.5) + taffy::style::LengthPercentageAuto::percent(0.5) ); assert_eq!( taffy_style.inset.top, - taffy::style::LengthPercentageAuto::Length(12.) + taffy::style::LengthPercentageAuto::length(12.) ); assert_eq!( taffy_style.inset.bottom, - taffy::style::LengthPercentageAuto::Auto + taffy::style::LengthPercentageAuto::auto() ); assert_eq!( taffy_style.flex_direction, @@ -576,23 +609,23 @@ mod tests { ); assert_eq!( taffy_style.margin.right, - taffy::style::LengthPercentageAuto::Length(10.) + taffy::style::LengthPercentageAuto::length(10.) ); assert_eq!( taffy_style.margin.top, - taffy::style::LengthPercentageAuto::Percent(0.15) + taffy::style::LengthPercentageAuto::percent(0.15) ); assert_eq!( taffy_style.margin.bottom, - taffy::style::LengthPercentageAuto::Auto + taffy::style::LengthPercentageAuto::auto() ); assert_eq!( taffy_style.padding.left, - taffy::style::LengthPercentage::Percent(0.13) + taffy::style::LengthPercentage::percent(0.13) ); assert_eq!( taffy_style.padding.right, - taffy::style::LengthPercentage::Length(21.) + taffy::style::LengthPercentage::length(21.) ); assert_eq!( taffy_style.padding.top, @@ -604,7 +637,7 @@ mod tests { ); assert_eq!( taffy_style.border.left, - taffy::style::LengthPercentage::Length(14.) + taffy::style::LengthPercentage::length(14.) ); assert_eq!( taffy_style.border.right, @@ -613,16 +646,16 @@ mod tests { assert_eq!(taffy_style.border.top, taffy::style::LengthPercentage::ZERO); assert_eq!( taffy_style.border.bottom, - taffy::style::LengthPercentage::Percent(0.31) + taffy::style::LengthPercentage::percent(0.31) ); assert_eq!(taffy_style.flex_grow, 1.); assert_eq!(taffy_style.flex_shrink, 0.); assert_eq!(taffy_style.flex_basis, taffy::style::Dimension::ZERO); assert_eq!(taffy_style.size.width, taffy::style::Dimension::ZERO); - assert_eq!(taffy_style.size.height, taffy::style::Dimension::Auto); + assert_eq!(taffy_style.size.height, taffy::style::Dimension::auto()); assert_eq!(taffy_style.min_size.width, taffy::style::Dimension::ZERO); assert_eq!(taffy_style.min_size.height, taffy::style::Dimension::ZERO); - assert_eq!(taffy_style.max_size.width, taffy::style::Dimension::Auto); + assert_eq!(taffy_style.max_size.width, taffy::style::Dimension::auto()); assert_eq!(taffy_style.max_size.height, taffy::style::Dimension::ZERO); assert_eq!(taffy_style.aspect_ratio, None); assert_eq!(taffy_style.scrollbar_width, 7.); @@ -643,8 +676,8 @@ mod tests { assert_eq!( taffy_style.grid_auto_rows, vec![ - sh::fit_content(taffy::style::LengthPercentage::Length(10.0)), - sh::fit_content(taffy::style::LengthPercentage::Percent(0.25)), + sh::fit_content(taffy::style::LengthPercentage::length(10.0)), + sh::fit_content(taffy::style::LengthPercentage::percent(0.25)), sh::minmax(sh::length(0.0), sh::fr(2.0)), ] ); @@ -667,20 +700,19 @@ mod tests { use taffy::style::LengthPercentage; let context = LayoutContext::new(2.0, Vec2::new(800., 600.)); let cases = [ - (Val::Auto, LengthPercentage::Length(0.)), - (Val::Percent(1.), LengthPercentage::Percent(0.01)), - (Val::Px(1.), LengthPercentage::Length(2.)), - (Val::Vw(1.), LengthPercentage::Length(8.)), - (Val::Vh(1.), LengthPercentage::Length(6.)), - (Val::VMin(2.), LengthPercentage::Length(12.)), - (Val::VMax(2.), LengthPercentage::Length(16.)), + (Val::Auto, LengthPercentage::length(0.)), + (Val::Percent(1.), LengthPercentage::percent(0.01)), + (Val::Px(1.), LengthPercentage::length(2.)), + (Val::Vw(1.), LengthPercentage::length(8.)), + (Val::Vh(1.), LengthPercentage::length(6.)), + (Val::VMin(2.), LengthPercentage::length(12.)), + (Val::VMax(2.), LengthPercentage::length(16.)), ]; for (val, length) in cases { - assert!(match (val.into_length_percentage(&context), length) { - (LengthPercentage::Length(a), LengthPercentage::Length(b)) - | (LengthPercentage::Percent(a), LengthPercentage::Percent(b)) => - (a - b).abs() < 0.0001, - _ => false, + assert!({ + let lhs = val.into_length_percentage(&context).into_raw().value(); + let rhs = length.into_raw().value(); + (lhs - rhs).abs() < 0.0001 }); } } diff --git a/crates/bevy_ui/src/layout/mod.rs b/crates/bevy_ui/src/layout/mod.rs index 3de05629ff294..913c195d52208 100644 --- a/crates/bevy_ui/src/layout/mod.rs +++ b/crates/bevy_ui/src/layout/mod.rs @@ -66,7 +66,7 @@ pub enum LayoutError { #[error("Invalid hierarchy")] InvalidHierarchy, #[error("Taffy error: {0}")] - TaffyError(taffy::TaffyError), + TaffyError(taffy::tree::TaffyError), } /// Updates the UI's layout tree, computes the new layout geometry and then updates the sizes and transforms of all the UI nodes. diff --git a/crates/bevy_ui/src/layout/ui_surface.rs b/crates/bevy_ui/src/layout/ui_surface.rs index 2df6afa947dac..b446dd39af17b 100644 --- a/crates/bevy_ui/src/layout/ui_surface.rs +++ b/crates/bevy_ui/src/layout/ui_surface.rs @@ -1,4 +1,5 @@ use core::fmt; +use std::ops::{Deref, DerefMut}; use bevy_platform::collections::hash_map::Entry; use taffy::TaffyTree; @@ -30,18 +31,51 @@ impl From for LayoutNode { } } +pub(crate) struct BevyTaffyTree(TaffyTree); + +/// # Safety +/// Taffy Tree becomes thread unsafe when you use calc(), which we do not implement +/// +#[expect( + unsafe_code, + reason = "This wrapper is safe while the calc feature is disabled." +)] +unsafe impl Send for BevyTaffyTree {} + +/// # Safety +/// Taffy Tree becomes thread unsafe when you use calc(), which we do not implement +/// +#[expect( + unsafe_code, + reason = "This wrapper is safe while the calc feature is disabled." +)] +unsafe impl Sync for BevyTaffyTree {} + +impl Deref for BevyTaffyTree { + type Target = TaffyTree; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for BevyTaffyTree { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + #[derive(Resource)] pub struct UiSurface { pub root_entity_to_viewport_node: EntityHashMap, pub(super) entity_to_taffy: EntityHashMap, - pub(super) taffy: TaffyTree, + pub(super) taffy: BevyTaffyTree, taffy_children_scratch: Vec, } fn _assert_send_sync_ui_surface_impl_safe() { fn _assert_send_sync() {} _assert_send_sync::>(); - _assert_send_sync::>(); + _assert_send_sync::>(); _assert_send_sync::(); } @@ -56,7 +90,7 @@ impl fmt::Debug for UiSurface { impl Default for UiSurface { fn default() -> Self { - let taffy: TaffyTree = TaffyTree::new(); + let taffy: BevyTaffyTree = BevyTaffyTree(TaffyTree::new()); Self { root_entity_to_viewport_node: Default::default(), entity_to_taffy: Default::default(), @@ -166,8 +200,8 @@ impl UiSurface { // Note: Taffy percentages are floats ranging from 0.0 to 1.0. // So this is setting width:100% and height:100% size: taffy::geometry::Size { - width: taffy::style::Dimension::Percent(1.0), - height: taffy::style::Dimension::Percent(1.0), + width: taffy::style_helpers::percent(1.0), + height: taffy::style_helpers::percent(1.0), }, align_items: Some(taffy::style::AlignItems::Start), justify_items: Some(taffy::style::JustifyItems::Start), diff --git a/crates/bevy_ui/src/widget/image.rs b/crates/bevy_ui/src/widget/image.rs index 92e951a263f9d..5b2c166eab4e7 100644 --- a/crates/bevy_ui/src/widget/image.rs +++ b/crates/bevy_ui/src/widget/image.rs @@ -205,6 +205,11 @@ pub struct ImageMeasure { pub size: Vec2, } +// NOOP function used to call into taffy API +fn resolve_calc(_calc_ptr: *const (), _parent_size: f32) -> f32 { + 0.0 +} + impl Measure for ImageMeasure { fn measure(&mut self, measure_args: MeasureArgs, style: &taffy::Style) -> Vec2 { let MeasureArgs { @@ -221,12 +226,24 @@ impl Measure for ImageMeasure { // Resolve styles let s_aspect_ratio = style.aspect_ratio; - let s_width = style.size.width.maybe_resolve(parent_width); - let s_min_width = style.min_size.width.maybe_resolve(parent_width); - let s_max_width = style.max_size.width.maybe_resolve(parent_width); - let s_height = style.size.height.maybe_resolve(parent_height); - let s_min_height = style.min_size.height.maybe_resolve(parent_height); - let s_max_height = style.max_size.height.maybe_resolve(parent_height); + let s_width = style.size.width.maybe_resolve(parent_width, resolve_calc); + let s_min_width = style + .min_size + .width + .maybe_resolve(parent_width, resolve_calc); + let s_max_width = style + .max_size + .width + .maybe_resolve(parent_width, resolve_calc); + let s_height = style.size.height.maybe_resolve(parent_height, resolve_calc); + let s_min_height = style + .min_size + .height + .maybe_resolve(parent_height, resolve_calc); + let s_max_height = style + .max_size + .height + .maybe_resolve(parent_height, resolve_calc); // Determine width and height from styles and known_sizes (if a size is available // from any of these sources) From 6bc8a5ee4e2f8ae12957b729539bdd1da606977c Mon Sep 17 00:00:00 2001 From: Ross Leonardy Date: Mon, 27 Oct 2025 20:17:46 -0400 Subject: [PATCH 02/11] Fix call here --- crates/bevy_ui/src/layout/convert.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/crates/bevy_ui/src/layout/convert.rs b/crates/bevy_ui/src/layout/convert.rs index a7cb894560b56..c882d1bdf6efc 100644 --- a/crates/bevy_ui/src/layout/convert.rs +++ b/crates/bevy_ui/src/layout/convert.rs @@ -385,11 +385,17 @@ impl MaxTrackSizingFunction { fn into_taffy(self, context: &LayoutContext) -> taffy::style::MaxTrackSizingFunction { match self { MaxTrackSizingFunction::Px(val) => taffy::style::MaxTrackSizingFunction::length( - Val::Px(val).into_length_percentage(context), + Val::Px(val) + .into_length_percentage(context) + .into_raw() + .value(), + ), + MaxTrackSizingFunction::Percent(val) => taffy::style::MaxTrackSizingFunction::percent( + Val::Percent(val) + .into_length_percentage(context) + .into_raw() + .value(), ), - MaxTrackSizingFunction::Percent(val) => { - taffy::style::MaxTrackSizingFunction::percent(val) - } MaxTrackSizingFunction::Auto => taffy::style::MaxTrackSizingFunction::auto(), MaxTrackSizingFunction::MinContent => { taffy::style::MaxTrackSizingFunction::min_content() From 8fe8b0ca8f428038de5d17d2db8f3f76e9c2d064 Mon Sep 17 00:00:00 2001 From: Ross Leonardy Date: Mon, 27 Oct 2025 20:50:32 -0400 Subject: [PATCH 03/11] Fix ci suggestions --- crates/bevy_ui/src/layout/ui_surface.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bevy_ui/src/layout/ui_surface.rs b/crates/bevy_ui/src/layout/ui_surface.rs index b446dd39af17b..d856a8dd80402 100644 --- a/crates/bevy_ui/src/layout/ui_surface.rs +++ b/crates/bevy_ui/src/layout/ui_surface.rs @@ -1,5 +1,5 @@ use core::fmt; -use std::ops::{Deref, DerefMut}; +use core::ops::{Deref, DerefMut}; use bevy_platform::collections::hash_map::Entry; use taffy::TaffyTree; @@ -34,7 +34,7 @@ impl From for LayoutNode { pub(crate) struct BevyTaffyTree(TaffyTree); /// # Safety -/// Taffy Tree becomes thread unsafe when you use calc(), which we do not implement +/// Taffy Tree becomes thread unsafe when you use the calc feature, which we do not implement /// #[expect( unsafe_code, @@ -43,7 +43,7 @@ pub(crate) struct BevyTaffyTree(TaffyTree); unsafe impl Send for BevyTaffyTree {} /// # Safety -/// Taffy Tree becomes thread unsafe when you use calc(), which we do not implement +/// Taffy Tree becomes thread unsafe when you use the calc feature, which we do not implement /// #[expect( unsafe_code, From 409e97b2314ba9d0bdc1373cfb07feafb0ecfc51 Mon Sep 17 00:00:00 2001 From: Ross Leonardy Date: Mon, 27 Oct 2025 21:11:38 -0400 Subject: [PATCH 04/11] fix cargo toml --- crates/bevy_ui/Cargo.toml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/crates/bevy_ui/Cargo.toml b/crates/bevy_ui/Cargo.toml index 6ae90c9692345..4a4768a865280 100644 --- a/crates/bevy_ui/Cargo.toml +++ b/crates/bevy_ui/Cargo.toml @@ -34,7 +34,15 @@ bevy_platform = { path = "../bevy_platform", version = "0.18.0-dev", default-fea ] } # other -taffy = { version = "0.9", default-features = false, features = ["std", "block_layout", "flexbox", "grid", "content_size", "alloc", "taffy_tree"] } +taffy = { version = "0.9", default-features = false, features = [ + "std", + "block_layout", + "flexbox", + "grid", + "content_size", + "alloc", + "taffy_tree", +] } serde = { version = "1", features = ["derive"], optional = true } uuid = { version = "1.1", features = ["v4"], optional = true } thiserror = { version = "2", default-features = false } From 0d7a63bc596f509bc0bad3fb3fd8bd5d96b3520f Mon Sep 17 00:00:00 2001 From: Ross Leonardy Date: Mon, 27 Oct 2025 21:18:31 -0400 Subject: [PATCH 05/11] min track sizing function fix --- crates/bevy_ui/src/layout/convert.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ui/src/layout/convert.rs b/crates/bevy_ui/src/layout/convert.rs index c882d1bdf6efc..166830737c531 100644 --- a/crates/bevy_ui/src/layout/convert.rs +++ b/crates/bevy_ui/src/layout/convert.rs @@ -340,7 +340,7 @@ impl MinTrackSizingFunction { .into_raw() .value(), ), - MinTrackSizingFunction::Percent(val) => taffy::style::MinTrackSizingFunction::length( + MinTrackSizingFunction::Percent(val) => taffy::style::MinTrackSizingFunction::percent( Val::Percent(val) .into_length_percentage(context) .into_raw() From 92e17133241a93a9ff0857d0f7f12dd7fe4133b4 Mon Sep 17 00:00:00 2001 From: Ross Leonardy Date: Mon, 27 Oct 2025 21:54:15 -0400 Subject: [PATCH 06/11] Fix safety messages --- crates/bevy_ui/src/layout/ui_surface.rs | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/crates/bevy_ui/src/layout/ui_surface.rs b/crates/bevy_ui/src/layout/ui_surface.rs index d856a8dd80402..5b128ce71b610 100644 --- a/crates/bevy_ui/src/layout/ui_surface.rs +++ b/crates/bevy_ui/src/layout/ui_surface.rs @@ -33,23 +33,13 @@ impl From for LayoutNode { pub(crate) struct BevyTaffyTree(TaffyTree); -/// # Safety -/// Taffy Tree becomes thread unsafe when you use the calc feature, which we do not implement -/// -#[expect( - unsafe_code, - reason = "This wrapper is safe while the calc feature is disabled." -)] -unsafe impl Send for BevyTaffyTree {} - -/// # Safety -/// Taffy Tree becomes thread unsafe when you use the calc feature, which we do not implement -/// -#[expect( - unsafe_code, - reason = "This wrapper is safe while the calc feature is disabled." -)] -unsafe impl Sync for BevyTaffyTree {} +#[expect(unsafe_code, reason = "TaffyTree is safe as long as calc is not used")] +/// SAFETY: Taffy Tree becomes thread unsafe when you use the calc feature, which we do not implement +unsafe impl Send for BevyTaffyTree {} + +#[expect(unsafe_code, reason = "TaffyTree is safe as long as calc is not used")] +/// SAFETY: Taffy Tree becomes thread unsafe when you use the calc feature, which we do not implement +unsafe impl Sync for BevyTaffyTree {} impl Deref for BevyTaffyTree { type Target = TaffyTree; From d5cd7e3d9797e430103e69aaf9f43c85da1c86d9 Mon Sep 17 00:00:00 2001 From: Ross Leonardy Date: Tue, 28 Oct 2025 10:08:55 -0400 Subject: [PATCH 07/11] Fix maxtracksizingfunction --- crates/bevy_ui/src/layout/convert.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ui/src/layout/convert.rs b/crates/bevy_ui/src/layout/convert.rs index 166830737c531..b12cb0f17cfb3 100644 --- a/crates/bevy_ui/src/layout/convert.rs +++ b/crates/bevy_ui/src/layout/convert.rs @@ -404,10 +404,20 @@ impl MaxTrackSizingFunction { taffy::style::MaxTrackSizingFunction::max_content() } MaxTrackSizingFunction::FitContentPx(val) => { - taffy::style::MaxTrackSizingFunction::fit_content_px(val) + taffy::style::MaxTrackSizingFunction::fit_content_px( + Val::Px(val) + .into_length_percentage(context) + .into_raw() + .value(), + ) } MaxTrackSizingFunction::FitContentPercent(val) => { - taffy::style::MaxTrackSizingFunction::fit_content_percent(val) + taffy::style::MaxTrackSizingFunction::fit_content_percent( + Val::Percent(val) + .into_length_percentage(context) + .into_raw() + .value(), + ) } MaxTrackSizingFunction::Fraction(fraction) => { taffy::style::MaxTrackSizingFunction::fr(fraction) From 3305bf2cc2c75d5be7476d35e592cfdff158b63b Mon Sep 17 00:00:00 2001 From: Ross Leonardy Date: Fri, 31 Oct 2025 20:48:17 -0400 Subject: [PATCH 08/11] Fix cargo features for bevy ui --- crates/bevy_ui/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/bevy_ui/Cargo.toml b/crates/bevy_ui/Cargo.toml index 4a4768a865280..a3267fbee8716 100644 --- a/crates/bevy_ui/Cargo.toml +++ b/crates/bevy_ui/Cargo.toml @@ -40,7 +40,6 @@ taffy = { version = "0.9", default-features = false, features = [ "flexbox", "grid", "content_size", - "alloc", "taffy_tree", ] } serde = { version = "1", features = ["derive"], optional = true } From 083d333061fa5dd32d8c0abf2b1a53a3e7468b6f Mon Sep 17 00:00:00 2001 From: Ross Leonardy Date: Fri, 31 Oct 2025 21:03:34 -0400 Subject: [PATCH 09/11] More into() --- crates/bevy_ui/src/layout/convert.rs | 96 +++++++--------------------- 1 file changed, 24 insertions(+), 72 deletions(-) diff --git a/crates/bevy_ui/src/layout/convert.rs b/crates/bevy_ui/src/layout/convert.rs index b12cb0f17cfb3..13f8ac94fd84e 100644 --- a/crates/bevy_ui/src/layout/convert.rs +++ b/crates/bevy_ui/src/layout/convert.rs @@ -334,18 +334,10 @@ impl From for taffy::geometry::Line impl MinTrackSizingFunction { fn into_taffy(self, context: &LayoutContext) -> taffy::style::MinTrackSizingFunction { match self { - MinTrackSizingFunction::Px(val) => taffy::style::MinTrackSizingFunction::length( - Val::Px(val) - .into_length_percentage(context) - .into_raw() - .value(), - ), - MinTrackSizingFunction::Percent(val) => taffy::style::MinTrackSizingFunction::percent( - Val::Percent(val) - .into_length_percentage(context) - .into_raw() - .value(), - ), + MinTrackSizingFunction::Px(val) => Val::Px(val).into_length_percentage(context).into(), + MinTrackSizingFunction::Percent(val) => { + Val::Percent(val).into_length_percentage(context).into() + } MinTrackSizingFunction::Auto => taffy::style::MinTrackSizingFunction::auto(), MinTrackSizingFunction::MinContent => { taffy::style::MinTrackSizingFunction::min_content() @@ -353,30 +345,14 @@ impl MinTrackSizingFunction { MinTrackSizingFunction::MaxContent => { taffy::style::MinTrackSizingFunction::max_content() } - MinTrackSizingFunction::VMin(val) => taffy::style::MinTrackSizingFunction::length( - Val::VMin(val) - .into_length_percentage(context) - .into_raw() - .value(), - ), - MinTrackSizingFunction::VMax(val) => taffy::style::MinTrackSizingFunction::length( - Val::VMax(val) - .into_length_percentage(context) - .into_raw() - .value(), - ), - MinTrackSizingFunction::Vh(val) => taffy::style::MinTrackSizingFunction::length( - Val::Vh(val) - .into_length_percentage(context) - .into_raw() - .value(), - ), - MinTrackSizingFunction::Vw(val) => taffy::style::MinTrackSizingFunction::length( - Val::Vw(val) - .into_length_percentage(context) - .into_raw() - .value(), - ), + MinTrackSizingFunction::VMin(val) => { + Val::VMin(val).into_length_percentage(context).into() + } + MinTrackSizingFunction::VMax(val) => { + Val::VMax(val).into_length_percentage(context).into() + } + MinTrackSizingFunction::Vh(val) => Val::Vh(val).into_length_percentage(context).into(), + MinTrackSizingFunction::Vw(val) => Val::Vw(val).into_length_percentage(context).into(), } } } @@ -384,18 +360,10 @@ impl MinTrackSizingFunction { impl MaxTrackSizingFunction { fn into_taffy(self, context: &LayoutContext) -> taffy::style::MaxTrackSizingFunction { match self { - MaxTrackSizingFunction::Px(val) => taffy::style::MaxTrackSizingFunction::length( - Val::Px(val) - .into_length_percentage(context) - .into_raw() - .value(), - ), - MaxTrackSizingFunction::Percent(val) => taffy::style::MaxTrackSizingFunction::percent( - Val::Percent(val) - .into_length_percentage(context) - .into_raw() - .value(), - ), + MaxTrackSizingFunction::Px(val) => Val::Px(val).into_length_percentage(context).into(), + MaxTrackSizingFunction::Percent(val) => { + Val::Percent(val).into_length_percentage(context).into() + } MaxTrackSizingFunction::Auto => taffy::style::MaxTrackSizingFunction::auto(), MaxTrackSizingFunction::MinContent => { taffy::style::MaxTrackSizingFunction::min_content() @@ -422,30 +390,14 @@ impl MaxTrackSizingFunction { MaxTrackSizingFunction::Fraction(fraction) => { taffy::style::MaxTrackSizingFunction::fr(fraction) } - MaxTrackSizingFunction::VMin(val) => taffy::style::MaxTrackSizingFunction::length( - Val::VMin(val) - .into_length_percentage(context) - .into_raw() - .value(), - ), - MaxTrackSizingFunction::VMax(val) => taffy::style::MaxTrackSizingFunction::length( - Val::VMax(val) - .into_length_percentage(context) - .into_raw() - .value(), - ), - MaxTrackSizingFunction::Vh(val) => taffy::style::MaxTrackSizingFunction::length( - Val::Vh(val) - .into_length_percentage(context) - .into_raw() - .value(), - ), - MaxTrackSizingFunction::Vw(val) => taffy::style::MaxTrackSizingFunction::length( - Val::Vw(val) - .into_length_percentage(context) - .into_raw() - .value(), - ), + MaxTrackSizingFunction::VMin(val) => { + Val::VMin(val).into_length_percentage(context).into() + } + MaxTrackSizingFunction::VMax(val) => { + Val::VMax(val).into_length_percentage(context).into() + } + MaxTrackSizingFunction::Vh(val) => Val::Vh(val).into_length_percentage(context).into(), + MaxTrackSizingFunction::Vw(val) => Val::Vw(val).into_length_percentage(context).into(), } } } From bcf397b4bbcfe1304daf4094b5b9d2a8827098a7 Mon Sep 17 00:00:00 2001 From: Ross Leonardy Date: Fri, 31 Oct 2025 21:06:32 -0400 Subject: [PATCH 10/11] Rename bevytaffytree --- crates/bevy_ui/src/layout/ui_surface.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/bevy_ui/src/layout/ui_surface.rs b/crates/bevy_ui/src/layout/ui_surface.rs index 5b128ce71b610..3f33363a32bc6 100644 --- a/crates/bevy_ui/src/layout/ui_surface.rs +++ b/crates/bevy_ui/src/layout/ui_surface.rs @@ -31,24 +31,24 @@ impl From for LayoutNode { } } -pub(crate) struct BevyTaffyTree(TaffyTree); +pub(crate) struct UiTree(TaffyTree); #[expect(unsafe_code, reason = "TaffyTree is safe as long as calc is not used")] /// SAFETY: Taffy Tree becomes thread unsafe when you use the calc feature, which we do not implement -unsafe impl Send for BevyTaffyTree {} +unsafe impl Send for UiTree {} #[expect(unsafe_code, reason = "TaffyTree is safe as long as calc is not used")] /// SAFETY: Taffy Tree becomes thread unsafe when you use the calc feature, which we do not implement -unsafe impl Sync for BevyTaffyTree {} +unsafe impl Sync for UiTree {} -impl Deref for BevyTaffyTree { +impl Deref for UiTree { type Target = TaffyTree; fn deref(&self) -> &Self::Target { &self.0 } } -impl DerefMut for BevyTaffyTree { +impl DerefMut for UiTree { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } @@ -58,14 +58,14 @@ impl DerefMut for BevyTaffyTree { pub struct UiSurface { pub root_entity_to_viewport_node: EntityHashMap, pub(super) entity_to_taffy: EntityHashMap, - pub(super) taffy: BevyTaffyTree, + pub(super) taffy: UiTree, taffy_children_scratch: Vec, } fn _assert_send_sync_ui_surface_impl_safe() { fn _assert_send_sync() {} _assert_send_sync::>(); - _assert_send_sync::>(); + _assert_send_sync::>(); _assert_send_sync::(); } @@ -80,7 +80,7 @@ impl fmt::Debug for UiSurface { impl Default for UiSurface { fn default() -> Self { - let taffy: BevyTaffyTree = BevyTaffyTree(TaffyTree::new()); + let taffy: UiTree = UiTree(TaffyTree::new()); Self { root_entity_to_viewport_node: Default::default(), entity_to_taffy: Default::default(), From 7decca3e50c6e0662f3f93fa08e8739eb03f5bec Mon Sep 17 00:00:00 2001 From: Ross Leonardy Date: Sat, 1 Nov 2025 09:30:28 -0400 Subject: [PATCH 11/11] Fix generics --- crates/bevy_ui/src/layout/convert.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/crates/bevy_ui/src/layout/convert.rs b/crates/bevy_ui/src/layout/convert.rs index 13f8ac94fd84e..c5ed6b6aa332c 100644 --- a/crates/bevy_ui/src/layout/convert.rs +++ b/crates/bevy_ui/src/layout/convert.rs @@ -1,4 +1,4 @@ -use taffy::{style_helpers, CheapCloneStr}; +use taffy::style_helpers; use crate::{ AlignContent, AlignItems, AlignSelf, BoxSizing, Display, FlexDirection, FlexWrap, GridAutoFlow, @@ -310,7 +310,7 @@ impl From for taffy::style::GridAutoFlow { } } -impl From for taffy::geometry::Line { +impl From for taffy::geometry::Line> { fn from(value: GridPlacement) -> Self { let span = value.get_span().unwrap_or(1); match (value.get_start(), value.get_end()) { @@ -411,13 +411,10 @@ impl GridTrack { } impl RepeatedGridTrack { - fn clone_into_repeated_taffy_track( + fn clone_into_repeated_taffy_track( &self, context: &LayoutContext, - ) -> taffy::style::GridTemplateComponent - where - S: CheapCloneStr, - { + ) -> taffy::style::GridTemplateComponent { if self.tracks.len() == 1 && self.repetition == GridTrackRepetition::Count(1) { let min = self.tracks[0].min_sizing_function.into_taffy(context); let max = self.tracks[0].max_sizing_function.into_taffy(context);