Skip to content

Commit

Permalink
Fix taffy breaking changes
Browse files Browse the repository at this point in the history
  • Loading branch information
Kanabenki committed Mar 4, 2024
1 parent 27d8c46 commit 1aa7746
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 76 deletions.
48 changes: 25 additions & 23 deletions crates/bevy_ui/src/layout/convert.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use bevy_utils::default;
use taffy::style_helpers;

use crate::{
Expand All @@ -18,31 +19,31 @@ impl Val {
Val::Auto => taffy::style::LengthPercentageAuto::Auto,
Val::Percent(value) => taffy::style::LengthPercentageAuto::Percent(value / 100.),
Val::Px(value) => {
taffy::style::LengthPercentageAuto::Points(context.scale_factor * value)
taffy::style::LengthPercentageAuto::Length(context.scale_factor * value)
}
Val::VMin(value) => {
taffy::style::LengthPercentageAuto::Points(context.min_size * value / 100.)
taffy::style::LengthPercentageAuto::Length(context.min_size * value / 100.)
}
Val::VMax(value) => {
taffy::style::LengthPercentageAuto::Points(context.max_size * value / 100.)
taffy::style::LengthPercentageAuto::Length(context.max_size * value / 100.)
}
Val::Vw(value) => {
taffy::style::LengthPercentageAuto::Points(context.physical_size.x * value / 100.)
taffy::style::LengthPercentageAuto::Length(context.physical_size.x * value / 100.)
}
Val::Vh(value) => {
taffy::style::LengthPercentageAuto::Points(context.physical_size.y * value / 100.)
taffy::style::LengthPercentageAuto::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::Points(0.0),
taffy::style::LengthPercentageAuto::Auto => taffy::style::LengthPercentage::Length(0.0),
taffy::style::LengthPercentageAuto::Percent(value) => {
taffy::style::LengthPercentage::Percent(value)
}
taffy::style::LengthPercentageAuto::Points(value) => {
taffy::style::LengthPercentage::Points(value)
taffy::style::LengthPercentageAuto::Length(value) => {
taffy::style::LengthPercentage::Length(value)
}
}
}
Expand Down Expand Up @@ -133,6 +134,7 @@ pub fn from_style(context: &LayoutContext, style: &Style) -> taffy::style::Style
.collect::<Vec<_>>(),
grid_row: style.grid_row.into(),
grid_column: style.grid_column.into(),
..default()
}
}

Expand Down Expand Up @@ -478,7 +480,7 @@ mod tests {
);
assert_eq!(
taffy_style.inset.top,
taffy::style::LengthPercentageAuto::Points(12.)
taffy::style::LengthPercentageAuto::Length(12.)
);
assert_eq!(
taffy_style.inset.bottom,
Expand Down Expand Up @@ -513,7 +515,7 @@ mod tests {
);
assert_eq!(
taffy_style.margin.right,
taffy::style::LengthPercentageAuto::Points(10.)
taffy::style::LengthPercentageAuto::Length(10.)
);
assert_eq!(
taffy_style.margin.top,
Expand All @@ -529,7 +531,7 @@ mod tests {
);
assert_eq!(
taffy_style.padding.right,
taffy::style::LengthPercentage::Points(21.)
taffy::style::LengthPercentage::Length(21.)
);
assert_eq!(
taffy_style.padding.top,
Expand All @@ -541,7 +543,7 @@ mod tests {
);
assert_eq!(
taffy_style.border.left,
taffy::style::LengthPercentage::Points(14.)
taffy::style::LengthPercentage::Length(14.)
);
assert_eq!(
taffy_style.border.right,
Expand Down Expand Up @@ -570,18 +572,18 @@ mod tests {
);
assert_eq!(
taffy_style.grid_template_rows,
vec![sh::points(10.0), sh::percent(0.5), sh::fr(1.0)]
vec![sh::length(10.0), sh::percent(0.5), sh::fr(1.0)]
);
assert_eq!(
taffy_style.grid_template_columns,
vec![sh::repeat(5, vec![sh::points(10.0)])]
vec![sh::repeat(5, vec![sh::length(10.0)])]
);
assert_eq!(
taffy_style.grid_auto_rows,
vec![
sh::fit_content(taffy::style::LengthPercentage::Points(10.0)),
sh::fit_content(taffy::style::LengthPercentage::Length(10.0)),
sh::fit_content(taffy::style::LengthPercentage::Percent(0.25)),
sh::minmax(sh::points(0.0), sh::fr(2.0)),
sh::minmax(sh::length(0.0), sh::fr(2.0)),
]
);
assert_eq!(
Expand All @@ -603,17 +605,17 @@ mod tests {
use taffy::style::LengthPercentage;
let context = LayoutContext::new(2.0, bevy_math::Vec2::new(800., 600.));
let cases = [
(Val::Auto, LengthPercentage::Points(0.)),
(Val::Auto, LengthPercentage::Length(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.)),
(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::Points(a), LengthPercentage::Points(b))
(LengthPercentage::Length(a), LengthPercentage::Length(b))
| (LengthPercentage::Percent(a), LengthPercentage::Percent(b)) =>
(a - b).abs() < 0.0001,
_ => false,
Expand Down
15 changes: 7 additions & 8 deletions crates/bevy_ui/src/layout/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@ use crate::UiSurface;
use bevy_ecs::prelude::Entity;
use bevy_utils::HashMap;
use std::fmt::Write;
use taffy::prelude::Node;
use taffy::tree::LayoutTree;
use taffy::{tree::NodeId, TraversePartialTree};

/// Prints a debug representation of the computed layout of the UI layout tree for each window.
pub fn print_ui_layout_tree(ui_surface: &UiSurface) {
let taffy_to_entity: HashMap<Node, Entity> = ui_surface
let taffy_to_entity: HashMap<NodeId, Entity> = ui_surface
.entity_to_taffy
.iter()
.map(|(entity, node)| (*node, *entity))
Expand All @@ -32,9 +31,9 @@ pub fn print_ui_layout_tree(ui_surface: &UiSurface) {
/// Recursively navigates the layout tree printing each node's information.
fn print_node(
ui_surface: &UiSurface,
taffy_to_entity: &HashMap<Node, Entity>,
taffy_to_entity: &HashMap<NodeId, Entity>,
entity: Entity,
node: Node,
node: NodeId,
has_sibling: bool,
lines_string: String,
acc: &mut String,
Expand All @@ -43,13 +42,14 @@ fn print_node(
let layout = tree.layout(node).unwrap();
let style = tree.style(node).unwrap();

let num_children = tree.child_count(node).unwrap();
let num_children = tree.child_count(node);

let display_variant = match (num_children, style.display) {
(_, taffy::style::Display::None) => "NONE",
(0, _) => "LEAF",
(_, taffy::style::Display::Flex) => "FLEX",
(_, taffy::style::Display::Grid) => "GRID",
(_, taffy::style::Display::Block) => "BLOCK",
};

let fork_string = if has_sibling {
Expand All @@ -59,15 +59,14 @@ fn print_node(
};
writeln!(
acc,
"{lines}{fork} {display} [x: {x:<4} y: {y:<4} width: {width:<4} height: {height:<4}] ({entity:?}) {measured}",
"{lines}{fork} {display} [x: {x:<4} y: {y:<4} width: {width:<4} height: {height:<4}] ({entity:?})",
lines = lines_string,
fork = fork_string,
display = display_variant,
x = layout.location.x,
y = layout.location.y,
width = layout.size.width,
height = layout.size.height,
measured = if tree.needs_measure(node) { "measured" } else { "" }
).ok();
let bar = if has_sibling { "│ " } else { " " };
let new_string = lines_string + bar;
Expand Down
74 changes: 45 additions & 29 deletions crates/bevy_ui/src/layout/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
mod convert;
pub mod debug;

use crate::{ContentSize, DefaultUiCamera, Node, Outline, Style, TargetCamera, UiScale};
use crate::{ContentSize, DefaultUiCamera, Measure, Node, Outline, Style, TargetCamera, UiScale};
use bevy_ecs::entity::EntityHashMap;
use bevy_ecs::{
change_detection::{DetectChanges, DetectChangesMut},
Expand All @@ -20,7 +20,7 @@ use bevy_utils::tracing::warn;
use bevy_utils::{default, HashMap, HashSet};
use bevy_window::{PrimaryWindow, Window, WindowScaleFactorChanged};
use std::fmt;
use taffy::{tree::LayoutTree, Taffy};
use taffy::TaffyTree;
use thiserror::Error;

pub struct LayoutContext {
Expand All @@ -45,22 +45,24 @@ impl LayoutContext {
#[derive(Debug, Clone, PartialEq, Eq)]
struct RootNodePair {
// The implicit "viewport" node created by Bevy
implicit_viewport_node: taffy::node::Node,
implicit_viewport_node: taffy::tree::NodeId,
// The root (parentless) node specified by the user
user_root_node: taffy::node::Node,
user_root_node: taffy::tree::NodeId,
}

type UiTaffyTree = TaffyTree<Box<dyn Measure>>;

#[derive(Resource)]
pub struct UiSurface {
entity_to_taffy: EntityHashMap<taffy::node::Node>,
entity_to_taffy: EntityHashMap<taffy::tree::NodeId>,
camera_roots: EntityHashMap<Vec<RootNodePair>>,
taffy: Taffy,
taffy: UiTaffyTree,
}

fn _assert_send_sync_ui_surface_impl_safe() {
fn _assert_send_sync<T: Send + Sync>() {}
_assert_send_sync::<EntityHashMap<taffy::node::Node>>();
_assert_send_sync::<Taffy>();
_assert_send_sync::<EntityHashMap<taffy::tree::NodeId>>();
_assert_send_sync::<UiTaffyTree>();
_assert_send_sync::<UiSurface>();
}

Expand All @@ -75,7 +77,7 @@ impl fmt::Debug for UiSurface {

impl Default for UiSurface {
fn default() -> Self {
let mut taffy = Taffy::new();
let mut taffy = TaffyTree::new();
taffy.disable_rounding();
Self {
entity_to_taffy: Default::default(),
Expand Down Expand Up @@ -103,15 +105,11 @@ impl UiSurface {
}
}

/// Update the `MeasureFunc` of the taffy node corresponding to the given [`Entity`] if the node exists.
pub fn try_update_measure(
&mut self,
entity: Entity,
measure_func: taffy::node::MeasureFunc,
) -> Option<()> {
/// Update the [`Box<dyn Measure>`] context of the taffy node corresponding to the given [`Entity`] if the node exists.
pub fn try_update_measure(&mut self, entity: Entity, measure: Box<dyn Measure>) -> Option<()> {
let taffy_node = self.entity_to_taffy.get(&entity)?;

self.taffy.set_measure(*taffy_node, Some(measure_func)).ok()
self.taffy.set_node_context(*taffy_node, Some(measure)).ok()
}

/// Update the children of the taffy node corresponding to the given [`Entity`].
Expand Down Expand Up @@ -144,7 +142,7 @@ without UI components as a child of an entity with UI components, results may be
/// Removes the measure from the entity's taffy node if it exists. Does nothing otherwise.
pub fn try_remove_measure(&mut self, entity: Entity) {
if let Some(taffy_node) = self.entity_to_taffy.get(&entity) {
self.taffy.set_measure(*taffy_node, None).unwrap();
self.taffy.set_node_context(*taffy_node, None).unwrap();
}
}

Expand Down Expand Up @@ -214,7 +212,30 @@ without UI components as a child of an entity with UI components, results may be
};
for root_nodes in camera_root_nodes {
self.taffy
.compute_layout(root_nodes.implicit_viewport_node, available_space)
.compute_layout_with_measure(
root_nodes.implicit_viewport_node,
available_space,
|known_dimensions: taffy::geometry::Size<Option<f32>>,
available_space: taffy::geometry::Size<taffy::AvailableSpace>,
_node_id: taffy::tree::NodeId,
node_context: Option<&mut Box<dyn Measure>>| {
let size = node_context
.map(|measure| {
measure.measure(
known_dimensions.width,
known_dimensions.height,
available_space.width,
available_space.height,
)
})
.unwrap_or(Vec2::ZERO);

taffy::Size {
width: size.x,
height: size.y,
}
},
)
.unwrap();
}
}
Expand All @@ -230,7 +251,7 @@ without UI components as a child of an entity with UI components, results may be

/// Get the layout geometry for the taffy node corresponding to the ui node [`Entity`].
/// Does not compute the layout geometry, `compute_window_layouts` should be run before using this function.
pub fn get_layout(&self, entity: Entity) -> Result<&taffy::layout::Layout, LayoutError> {
pub fn get_layout(&self, entity: Entity) -> Result<&taffy::Layout, LayoutError> {
if let Some(taffy_node) = self.entity_to_taffy.get(&entity) {
self.taffy
.layout(*taffy_node)
Expand All @@ -250,7 +271,7 @@ pub enum LayoutError {
#[error("Invalid hierarchy")]
InvalidHierarchy,
#[error("Taffy error: {0}")]
TaffyError(#[from] taffy::error::TaffyError),
TaffyError(#[from] 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.
Expand Down Expand Up @@ -541,7 +562,7 @@ mod tests {
use bevy_window::WindowResized;
use bevy_window::WindowResolution;
use bevy_window::WindowScaleFactorChanged;
use taffy::tree::LayoutTree;
use taffy::TraversePartialTree;

#[test]
fn round_layout_coords_must_round_ties_up() {
Expand Down Expand Up @@ -696,7 +717,7 @@ mod tests {
let ui_parent_node = ui_surface.entity_to_taffy[&ui_parent_entity];

// `ui_parent_node` shouldn't have any children yet
assert_eq!(ui_surface.taffy.child_count(ui_parent_node).unwrap(), 0);
assert_eq!(ui_surface.taffy.child_count(ui_parent_node), 0);

let mut ui_child_entities = (0..10)
.map(|_| {
Expand All @@ -715,7 +736,7 @@ mod tests {
1 + ui_child_entities.len()
);
assert_eq!(
ui_surface.taffy.child_count(ui_parent_node).unwrap(),
ui_surface.taffy.child_count(ui_parent_node),
ui_child_entities.len()
);

Expand Down Expand Up @@ -746,7 +767,7 @@ mod tests {
1 + ui_child_entities.len()
);
assert_eq!(
ui_surface.taffy.child_count(ui_parent_node).unwrap(),
ui_surface.taffy.child_count(ui_parent_node),
ui_child_entities.len()
);

Expand Down Expand Up @@ -835,10 +856,7 @@ mod tests {
ui_schedule.run(&mut world);

let ui_surface = world.resource::<UiSurface>();
let ui_node = ui_surface.entity_to_taffy[&ui_entity];

// a node with a content size needs to be measured
assert!(ui_surface.taffy.needs_measure(ui_node));
let layout = ui_surface.get_layout(ui_entity).unwrap();
assert_eq!(layout.size.width, content_size.x);
assert_eq!(layout.size.height, content_size.y);
Expand All @@ -848,8 +866,6 @@ mod tests {
ui_schedule.run(&mut world);

let ui_surface = world.resource::<UiSurface>();
// a node without a content size does not need to be measured
assert!(!ui_surface.taffy.needs_measure(ui_node));

// Without a content size, the node has no width or height constraints so the length of both dimensions is 0.
let layout = ui_surface.get_layout(ui_entity).unwrap();
Expand Down
Loading

0 comments on commit 1aa7746

Please sign in to comment.