diff --git a/crates/bevy_ui/src/flex/convert.rs b/crates/bevy_ui/src/flex/convert.rs index 60e22f0e0b9ba..a2bb1af48c053 100644 --- a/crates/bevy_ui/src/flex/convert.rs +++ b/crates/bevy_ui/src/flex/convert.rs @@ -1,99 +1,74 @@ -use taffy::style::LengthPercentageAuto; - use crate::{ AlignContent, AlignItems, AlignSelf, Display, FlexDirection, FlexWrap, JustifyContent, PositionType, Size, Style, UiRect, Val, }; -impl Val { - fn scaled(self, scale_factor: f64) -> Self { - match self { - Val::Auto => Val::Auto, - Val::Percent(value) => Val::Percent(value), - Val::Px(value) => Val::Px((scale_factor * value as f64) as f32), - } - } +use super::LayoutContext; - fn to_inset(self) -> LengthPercentageAuto { +impl Val { + fn into_length_percentage_auto( + self, + context: &LayoutContext, + ) -> taffy::style::LengthPercentageAuto { match self { Val::Auto => taffy::style::LengthPercentageAuto::Auto, - Val::Percent(value) => taffy::style::LengthPercentageAuto::Percent(value / 100.0), - Val::Px(value) => taffy::style::LengthPercentageAuto::Points(value), + Val::Percent(value) => taffy::style::LengthPercentageAuto::Percent(value / 100.), + Val::Px(value) => taffy::style::LengthPercentageAuto::Points( + (context.scale_factor * value as f64) as f32, + ), + Val::VMin(value) => { + taffy::style::LengthPercentageAuto::Points(context.min_size * value / 100.) + } + Val::VMax(value) => { + taffy::style::LengthPercentageAuto::Points(context.max_size * value / 100.) + } + Val::Vw(value) => { + taffy::style::LengthPercentageAuto::Points(context.physical_size.x * value / 100.) + } + Val::Vh(value) => { + taffy::style::LengthPercentageAuto::Points(context.physical_size.y * value / 100.) + } } } -} -impl UiRect { - fn scaled(self, scale_factor: f64) -> Self { - Self { - left: self.left.scaled(scale_factor), - right: self.right.scaled(scale_factor), - top: self.top.scaled(scale_factor), - bottom: self.bottom.scaled(scale_factor), + fn into_length_percentage(self, context: &LayoutContext) -> taffy::style::LengthPercentage { + match self.into_length_percentage_auto(context) { + taffy::style::LengthPercentageAuto::Auto => taffy::style::LengthPercentage::Points(0.0), + taffy::style::LengthPercentageAuto::Percent(value) => { + taffy::style::LengthPercentage::Percent(value) + } + taffy::style::LengthPercentageAuto::Points(value) => { + taffy::style::LengthPercentage::Points(value) + } } } -} -impl Size { - fn scaled(self, scale_factor: f64) -> Self { - Self { - width: self.width.scaled(scale_factor), - height: self.height.scaled(scale_factor), - } - } -} - -impl> From for taffy::prelude::Rect { - fn from(value: UiRect) -> Self { - Self { - left: value.left.into(), - right: value.right.into(), - top: value.top.into(), - bottom: value.bottom.into(), - } + fn into_dimension(self, context: &LayoutContext) -> taffy::style::Dimension { + self.into_length_percentage_auto(context).into() } } -impl> From for taffy::prelude::Size { - fn from(value: Size) -> Self { - Self { - width: value.width.into(), - height: value.height.into(), - } - } -} - -impl From for taffy::style::Dimension { - fn from(value: Val) -> Self { - match value { - Val::Auto => taffy::style::Dimension::Auto, - Val::Percent(value) => taffy::style::Dimension::Percent(value / 100.0), - Val::Px(value) => taffy::style::Dimension::Points(value), - } - } -} - -impl From for taffy::style::LengthPercentage { - fn from(value: Val) -> Self { - match value { - Val::Auto => taffy::style::LengthPercentage::Points(0.0), - Val::Percent(value) => taffy::style::LengthPercentage::Percent(value / 100.0), - Val::Px(value) => taffy::style::LengthPercentage::Points(value), +impl UiRect { + fn map_to_taffy_rect(self, map_fn: impl Fn(Val) -> T) -> taffy::geometry::Rect { + taffy::geometry::Rect { + left: map_fn(self.left), + right: map_fn(self.right), + top: map_fn(self.top), + bottom: map_fn(self.bottom), } } } -impl From for taffy::style::LengthPercentageAuto { - fn from(value: Val) -> Self { - match value { - Val::Auto => taffy::style::LengthPercentageAuto::Auto, - Val::Percent(value) => taffy::style::LengthPercentageAuto::Percent(value / 100.0), - Val::Px(value) => taffy::style::LengthPercentageAuto::Points(value), +impl Size { + fn map_to_taffy_size(self, map_fn: impl Fn(Val) -> T) -> taffy::geometry::Size { + taffy::geometry::Size { + width: map_fn(self.width), + height: map_fn(self.height), } } } -pub fn from_style(scale_factor: f64, style: &Style) -> taffy::style::Style { +pub fn from_style(context: &LayoutContext, style: &Style) -> taffy::style::Style { taffy::style::Style { display: style.display.into(), position: style.position_type.into(), @@ -104,22 +79,34 @@ pub fn from_style(scale_factor: f64, style: &Style) -> taffy::style::Style { align_content: Some(style.align_content.into()), justify_content: Some(style.justify_content.into()), inset: taffy::prelude::Rect { - left: style.left.scaled(scale_factor).to_inset(), - right: style.right.scaled(scale_factor).to_inset(), - top: style.top.scaled(scale_factor).to_inset(), - bottom: style.bottom.scaled(scale_factor).to_inset(), + left: style.left.into_length_percentage_auto(context), + right: style.right.into_length_percentage_auto(context), + top: style.top.into_length_percentage_auto(context), + bottom: style.bottom.into_length_percentage_auto(context), }, - margin: style.margin.scaled(scale_factor).into(), - padding: style.padding.scaled(scale_factor).into(), - border: style.border.scaled(scale_factor).into(), + margin: style + .margin + .map_to_taffy_rect(|m| m.into_length_percentage_auto(context)), + padding: style + .padding + .map_to_taffy_rect(|m| m.into_length_percentage(context)), + border: style + .border + .map_to_taffy_rect(|m| m.into_length_percentage(context)), flex_grow: style.flex_grow, flex_shrink: style.flex_shrink, - flex_basis: style.flex_basis.scaled(scale_factor).into(), - size: style.size.scaled(scale_factor).into(), - min_size: style.min_size.scaled(scale_factor).into(), - max_size: style.max_size.scaled(scale_factor).into(), + flex_basis: style.flex_basis.into_dimension(context), + size: style.size.map_to_taffy_size(|s| s.into_dimension(context)), + min_size: style + .min_size + .map_to_taffy_size(|s| s.into_dimension(context)), + max_size: style + .max_size + .map_to_taffy_size(|s| s.into_dimension(context)), aspect_ratio: style.aspect_ratio, - gap: style.gap.scaled(scale_factor).into(), + gap: style + .gap + .map_to_taffy_size(|s| s.into_length_percentage(context)), justify_self: None, } } @@ -283,7 +270,8 @@ mod tests { height: Val::Percent(0.), }, }; - let taffy_style = from_style(1.0, &bevy_style); + let viewport_values = LayoutContext::new(1.0, bevy_math::Vec2::new(800., 600.)); + let taffy_style = from_style(&viewport_values, &bevy_style); assert_eq!(taffy_style.display, taffy::style::Display::Flex); assert_eq!(taffy_style.position, taffy::style::Position::Absolute); assert!(matches!( @@ -408,4 +396,27 @@ mod tests { taffy::style::LengthPercentage::Percent(0.) ); } + + #[test] + fn test_into_length_percentage() { + use taffy::style::LengthPercentage; + let context = LayoutContext::new(2.0, bevy_math::Vec2::new(800., 600.)); + let cases = [ + (Val::Auto, LengthPercentage::Points(0.)), + (Val::Percent(1.), LengthPercentage::Percent(0.01)), + (Val::Px(1.), LengthPercentage::Points(2.)), + (Val::Vw(1.), LengthPercentage::Points(8.)), + (Val::Vh(1.), LengthPercentage::Points(6.)), + (Val::VMin(2.), LengthPercentage::Points(12.)), + (Val::VMax(2.), LengthPercentage::Points(16.)), + ]; + for (val, length) in cases { + assert!(match (val.into_length_percentage(&context), length) { + (LengthPercentage::Points(a), LengthPercentage::Points(b)) + | (LengthPercentage::Percent(a), LengthPercentage::Percent(b)) => + (a - b).abs() < 0.0001, + _ => false, + },); + } + } } diff --git a/crates/bevy_ui/src/flex/mod.rs b/crates/bevy_ui/src/flex/mod.rs index 8fc866fdb1fa5..62ab50196e5ee 100644 --- a/crates/bevy_ui/src/flex/mod.rs +++ b/crates/bevy_ui/src/flex/mod.rs @@ -22,6 +22,25 @@ use taffy::{ Taffy, }; +pub struct LayoutContext { + pub scale_factor: f64, + pub physical_size: Vec2, + pub min_size: f32, + pub max_size: f32, +} + +impl LayoutContext { + /// create new a [`LayoutContext`] from the window's physical size and scale factor + fn new(scale_factor: f64, physical_size: Vec2) -> Self { + Self { + scale_factor, + physical_size, + min_size: physical_size.x.min(physical_size.y), + max_size: physical_size.x.max(physical_size.y), + } + } +} + #[derive(Resource)] pub struct FlexSurface { entity_to_taffy: HashMap, @@ -59,19 +78,17 @@ impl Default for FlexSurface { } impl FlexSurface { - pub fn upsert_node(&mut self, entity: Entity, style: &Style, scale_factor: f64) { + pub fn upsert_node(&mut self, entity: Entity, style: &Style, context: &LayoutContext) { let mut added = false; let taffy = &mut self.taffy; let taffy_node = self.entity_to_taffy.entry(entity).or_insert_with(|| { added = true; - taffy - .new_leaf(convert::from_style(scale_factor, style)) - .unwrap() + taffy.new_leaf(convert::from_style(context, style)).unwrap() }); if !added { self.taffy - .set_style(*taffy_node, convert::from_style(scale_factor, style)) + .set_style(*taffy_node, convert::from_style(context, style)) .unwrap(); } } @@ -81,10 +98,11 @@ impl FlexSurface { entity: Entity, style: &Style, calculated_size: CalculatedSize, - scale_factor: f64, + context: &LayoutContext, ) { let taffy = &mut self.taffy; - let taffy_style = convert::from_style(scale_factor, style); + let taffy_style = convert::from_style(context, style); + let scale_factor = context.scale_factor; let measure = taffy::node::MeasureFunc::Boxed(Box::new( move |constraints: Size>, _available: Size| { let mut size = Size { @@ -229,6 +247,7 @@ pub fn flex_node_system( windows: Query<(Entity, &Window)>, ui_scale: Res, mut scale_factor_events: EventReader, + mut resize_events: EventReader, mut flex_surface: ResMut, root_node_query: Query, Without)>, node_query: Query<(Entity, &Style, Option<&CalculatedSize>), (With, Changed