From b937a82d694a86650e0c0c07734875674230fcbb Mon Sep 17 00:00:00 2001 From: Nico Burns Date: Mon, 11 Sep 2023 23:10:42 +0100 Subject: [PATCH] Implement Display::Contents Add Display::Contents variant to Display enum Modify Taffy children iterator to recurse into Display::Contents nodes Add `Display::Contents` support to the gentest script Add basic tests for `Display::Contents` Fix clippy lints and document TaffyChildIter next method Fix Yoga benchmark helpers smallvec WIP Precompute display:contents vec Remove smallvec + lazy display:contents iter implementation Remove TaffySliceIter Fix clippy lints Use RefMut::map to simplify iterator Reuse Vec in children iterator cache Use a struct for the children cache Change order of find_child_recursive parameters Add Display::Contents to the release notes Fix child and child_count methods to use resolved children --- RELEASES.md | 1 + benches/src/yoga_helpers.rs | 1 + scripts/gentest/src/main.rs | 1 + src/style/mod.rs | 4 + src/tree/taffy_tree/tree.rs | 94 +++++++++++-- src/util/debug.rs | 4 +- .../contents/contents_flex_basic.html | 23 ++++ .../contents/contents_flex_nested.html | 26 ++++ .../contents/contents_flex_nested2.html | 28 ++++ .../generated/contents/contents_flex_basic.rs | 95 +++++++++++++ .../contents/contents_flex_nested.rs | 117 ++++++++++++++++ .../contents/contents_flex_nested2.rs | 128 ++++++++++++++++++ tests/generated/contents/mod.rs | 3 + tests/generated/mod.rs | 1 + 14 files changed, 514 insertions(+), 12 deletions(-) create mode 100644 test_fixtures/contents/contents_flex_basic.html create mode 100644 test_fixtures/contents/contents_flex_nested.html create mode 100644 test_fixtures/contents/contents_flex_nested2.html create mode 100644 tests/generated/contents/contents_flex_basic.rs create mode 100644 tests/generated/contents/contents_flex_nested.rs create mode 100644 tests/generated/contents/contents_flex_nested2.rs create mode 100644 tests/generated/contents/mod.rs diff --git a/RELEASES.md b/RELEASES.md index 3fb26f800..f81f8f28a 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -87,6 +87,7 @@ Example usage change: ### Added - Support for [CSS Block layout](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flow_Layout/Block_and_Inline_Layout_in_Normal_Flow#elements_participating_in_a_block_formatting_context) has been added. This can be used via the new `Display::Block` variant of the `Display` enum. Note that inline, inline-block and float have *not* been implemented. The use case supported is block container nodes which contain block-level children. +- Support for [`Display::Contents`](https://css-tricks.com/get-ready-for-display-contents/) - Support for running each layout algorithm individually on a single node via the following top-level functions: - `compute_flexbox_layout` - `compute_grid_layout` diff --git a/benches/src/yoga_helpers.rs b/benches/src/yoga_helpers.rs index cd2f72229..69f4baa40 100644 --- a/benches/src/yoga_helpers.rs +++ b/benches/src/yoga_helpers.rs @@ -187,6 +187,7 @@ fn apply_taffy_style(node: &mut yg::Node, style: &tf::Style) { node.set_display(match style.display { tf::Display::None => yg::Display::None, tf::Display::Flex => yg::Display::Flex, + tf::Display::Contents => panic!("Yoga does not support display: contents"), tf::Display::Grid => panic!("Yoga does not support CSS Grid layout"), tf::Display::Block => panic!("Yoga does not support CSS Block layout"), }); diff --git a/scripts/gentest/src/main.rs b/scripts/gentest/src/main.rs index 4163ccc38..c0d5b1534 100644 --- a/scripts/gentest/src/main.rs +++ b/scripts/gentest/src/main.rs @@ -352,6 +352,7 @@ fn generate_node(ident: &str, node: &Value) -> TokenStream { let display = match style["display"] { Value::String(ref value) => match value.as_ref() { "none" => quote!(display: taffy::style::Display::None,), + "contents" => quote!(display: taffy::style::Display::Contents,), "block" => quote!(display: taffy::style::Display::Block,), "grid" => quote!(display: taffy::style::Display::Grid,), _ => quote!(display: taffy::style::Display::Flex,), diff --git a/src/style/mod.rs b/src/style/mod.rs index 80acfd506..05673323d 100644 --- a/src/style/mod.rs +++ b/src/style/mod.rs @@ -44,6 +44,9 @@ pub enum Display { /// The children will follow the CSS Grid layout algorithm #[cfg(feature = "grid")] Grid, + /// The node will behave as if it does not exist, and it's children will be laid + /// out as if they were direct children of the node's parent. + Contents, /// The children will not be laid out, and will follow absolute positioning None, } @@ -70,6 +73,7 @@ impl core::fmt::Display for Display { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Display::None => write!(f, "NONE"), + Display::Contents => write!(f, "CONTENTS"), #[cfg(feature = "block_layout")] Display::Block => write!(f, "BLOCK"), #[cfg(feature = "flexbox")] diff --git a/src/tree/taffy_tree/tree.rs b/src/tree/taffy_tree/tree.rs index 85f83e7c7..28d8c6d0a 100644 --- a/src/tree/taffy_tree/tree.rs +++ b/src/tree/taffy_tree/tree.rs @@ -1,6 +1,8 @@ //! UI node types and related data structures. //! //! Layouts are composed of multiple nodes, which live in a tree-like data structure. +use core::cell::{RefCell, RefMut}; + use slotmap::{DefaultKey, SlotMap, SparseSecondaryMap}; use crate::geometry::Size; @@ -31,6 +33,21 @@ impl Default for TaffyConfig { } } +/// Used to cache the resolved children of a node (taking into account `Display::contents`) during layout +/// so that repeated calls to the children method don't need to re-resolve the list. +struct ChildrenCache { + /// The NodeId of the node whose children we are caching (the cache key) + node_id: NodeId, + /// The actual list of child ids + children: Vec, +} +impl ChildrenCache { + /// Create a new empty cache + fn new() -> ChildrenCache { + ChildrenCache { node_id: NodeId::new(0), children: Vec::new() } + } +} + /// A tree of UI nodes suitable for UI layout pub struct Taffy { /// The [`NodeData`] for each node stored in this tree @@ -59,13 +76,33 @@ impl Default for Taffy { } } -/// Iterator that wraps a slice of nodes, lazily converting them to u64 -pub(crate) struct TaffyChildIter<'a>(core::slice::Iter<'a, NodeId>); -impl<'a> Iterator for TaffyChildIter<'a> { +/// Iterator over the Vec in a RefMut<'a, Vec> +pub struct RefCellVecIter<'a> { + /// The items to iterate over + children: RefMut<'a, Vec>, + /// The next index to return + index: usize, +} +impl<'a> Iterator for RefCellVecIter<'a> { type Item = NodeId; fn next(&mut self) -> Option { - self.0.next().copied() + let item = self.children.get(self.index).copied(); + self.index += 1; + item + } +} + +/// Iterates over children, checking the Display type of the node +/// If the node is `Display::Contents`, then we recurse it's children, else we simply push the `NodeId` into the list +fn find_children_recursive(tree: &Taffy, node: NodeId, out: &mut Vec) { + for child_id in tree.children[node.into()].iter() { + let child_key: DefaultKey = (*child_id).into(); + let display = tree.nodes[child_key].style.display; + match display { + Display::Contents => find_children_recursive(tree, *child_id, out), + _ => out.push(*child_id), + } } } @@ -80,27 +117,57 @@ where pub(crate) taffy: &'t mut Taffy, /// The context provided for passing to measure functions if layout is run over this struct pub(crate) measure_function: MeasureFunction, + /// Used to cache the resolved children of a node (taking into account `Display::contents`) during layout + /// so that repeated calls to the children method don't need to re-resolve the list. + node_children_cache: RefCell, +} + +impl<'t, NodeContext, MeasureFunction> TaffyView<'t, NodeContext, MeasureFunction> +where + MeasureFunction: FnMut(Size>, Size, NodeId, Option<&mut NodeContext>) -> Size, +{ + /// Create a new TaffyView from a Taffy and a measure function + pub(crate) fn new(taffy: &'t mut Taffy, measure_function: MeasureFunction) -> Self { + TaffyView { taffy, measure_function, node_children_cache: RefCell::new(ChildrenCache::new()) } + } + + /// Returns the resolved children, taking into account `Display::Contents` + /// Will use cached result if available, else compute and cache. + fn resolve_children(&self, node: NodeId) -> RefMut<'_, Vec> { + let mut cache = self.node_children_cache.borrow_mut(); + + // If the cache key does not match the requested node_id, then recompute the children for + // the requested node and update the cache in-place. + if cache.node_id != node { + cache.node_id = node; + cache.children.clear(); + find_children_recursive(self.taffy, node, &mut cache.children); + } + + // In all cases, return a reference into the cache + RefMut::map(cache, |c| &mut c.children) + } } impl<'t, NodeContext, MeasureFunction> PartialLayoutTree for TaffyView<'t, NodeContext, MeasureFunction> where MeasureFunction: FnMut(Size>, Size, NodeId, Option<&mut NodeContext>) -> Size, { - type ChildIter<'a> = TaffyChildIter<'a> where Self: 'a; + type ChildIter<'a> = RefCellVecIter<'a> where Self: 'a; #[inline(always)] fn child_ids(&self, node: NodeId) -> Self::ChildIter<'_> { - TaffyChildIter(self.taffy.children[node.into()].iter()) + RefCellVecIter { children: self.resolve_children(node), index: 0 } } #[inline(always)] fn child_count(&self, node: NodeId) -> usize { - self.taffy.children[node.into()].len() + self.resolve_children(node).len() } #[inline(always)] fn get_child_id(&self, node: NodeId, id: usize) -> NodeId { - self.taffy.children[node.into()][id] + self.resolve_children(node)[id] } #[inline(always)] @@ -148,6 +215,11 @@ where // Dispatch to a layout algorithm based on the node's display style and whether the node has children or not. match (display_mode, has_children) { (Display::None, _) => compute_hidden_layout(tree, node), + (Display::Contents, _) => { + *tree.get_unrounded_layout_mut(node) = Layout::with_order(0); + tree.get_cache_mut(node).clear(); + LayoutOutput::HIDDEN + } #[cfg(feature = "block_layout")] (Display::Block, true) => compute_block_layout(tree, node, inputs), #[cfg(feature = "flexbox")] @@ -505,7 +577,7 @@ impl Taffy { MeasureFunction: FnMut(Size>, Size, NodeId, Option<&mut NodeContext>) -> Size, { let use_rounding = self.config.use_rounding; - let mut taffy_view = TaffyView { taffy: self, measure_function }; + let mut taffy_view = TaffyView::new(self, measure_function); compute_layout(&mut taffy_view, node_id, available_space); if use_rounding { round_layout(&mut taffy_view, node_id); @@ -521,14 +593,14 @@ impl Taffy { /// Prints a debug representation of the tree's layout #[cfg(feature = "std")] pub fn print_tree(&mut self, root: NodeId) { - let taffy_view = TaffyView { taffy: self, measure_function: |_, _, _, _| Size::ZERO }; + let taffy_view = TaffyView::new(self, |_, _, _, _| Size::ZERO); crate::util::print_tree(&taffy_view, root) } /// Returns an instance of LayoutTree representing the Taffy #[cfg(test)] pub(crate) fn as_layout_tree(&mut self) -> impl LayoutTree + '_ { - TaffyView { taffy: self, measure_function: |_, _, _, _| Size::ZERO } + TaffyView::new(self, |_, _, _, _| Size::ZERO) } } diff --git a/src/util/debug.rs b/src/util/debug.rs index 6962b8215..9375876d5 100644 --- a/src/util/debug.rs +++ b/src/util/debug.rs @@ -23,6 +23,7 @@ fn print_node(tree: &impl LayoutTree, node: NodeId, has_sibling: bool, lines_str let display = match (num_children, style.display) { (_, style::Display::None) => "NONE", + (_, style::Display::Contents) => "CONTENTS", (0, _) => "LEAF", #[cfg(feature = "block_layout")] (_, style::Display::Block) => "BLOCK", @@ -48,7 +49,8 @@ fn print_node(tree: &impl LayoutTree, node: NodeId, has_sibling: bool, lines_str let new_string = lines_string + bar; // Recurse into children - for (index, child) in tree.child_ids(node).enumerate() { + let child_ids : Vec = tree.child_ids(node).collect(); + for (index, child) in child_ids.into_iter().enumerate() { let has_sibling = index < num_children - 1; print_node(tree, child, has_sibling, new_string.clone()); } diff --git a/test_fixtures/contents/contents_flex_basic.html b/test_fixtures/contents/contents_flex_basic.html new file mode 100644 index 000000000..eee9e648e --- /dev/null +++ b/test_fixtures/contents/contents_flex_basic.html @@ -0,0 +1,23 @@ + + + + + + + Test description + + + + +
+
+
+
+
+
+
+
+
+ + + \ No newline at end of file diff --git a/test_fixtures/contents/contents_flex_nested.html b/test_fixtures/contents/contents_flex_nested.html new file mode 100644 index 000000000..de2898722 --- /dev/null +++ b/test_fixtures/contents/contents_flex_nested.html @@ -0,0 +1,26 @@ + + + + + + + Test description + + + + +
+
+
+
+
+
+
+
+
+
+
+
+ + + \ No newline at end of file diff --git a/test_fixtures/contents/contents_flex_nested2.html b/test_fixtures/contents/contents_flex_nested2.html new file mode 100644 index 000000000..86d4afcee --- /dev/null +++ b/test_fixtures/contents/contents_flex_nested2.html @@ -0,0 +1,28 @@ + + + + + + + Test description + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + \ No newline at end of file diff --git a/tests/generated/contents/contents_flex_basic.rs b/tests/generated/contents/contents_flex_basic.rs new file mode 100644 index 000000000..d0ebd1867 --- /dev/null +++ b/tests/generated/contents/contents_flex_basic.rs @@ -0,0 +1,95 @@ +#[test] +fn contents_flex_basic() { + #[allow(unused_imports)] + use taffy::{prelude::*, tree::Layout, Taffy}; + let mut taffy: Taffy = Taffy::new(); + let node0 = taffy + .new_leaf(taffy::style::Style { + size: taffy::geometry::Size { width: taffy::style::Dimension::Length(30f32), height: auto() }, + ..Default::default() + }) + .unwrap(); + let node1 = taffy + .new_leaf(taffy::style::Style { + size: taffy::geometry::Size { width: taffy::style::Dimension::Length(30f32), height: auto() }, + ..Default::default() + }) + .unwrap(); + let node2 = taffy + .new_leaf(taffy::style::Style { + size: taffy::geometry::Size { width: taffy::style::Dimension::Length(30f32), height: auto() }, + ..Default::default() + }) + .unwrap(); + let node30 = taffy + .new_leaf(taffy::style::Style { + size: taffy::geometry::Size { width: taffy::style::Dimension::Length(30f32), height: auto() }, + ..Default::default() + }) + .unwrap(); + let node31 = taffy + .new_leaf(taffy::style::Style { + size: taffy::geometry::Size { width: taffy::style::Dimension::Length(30f32), height: auto() }, + ..Default::default() + }) + .unwrap(); + let node3 = taffy + .new_with_children( + taffy::style::Style { display: taffy::style::Display::Contents, ..Default::default() }, + &[node30, node31], + ) + .unwrap(); + let node = taffy + .new_with_children( + taffy::style::Style { + display: taffy::style::Display::Flex, + justify_content: Some(taffy::style::JustifyContent::SpaceBetween), + size: taffy::geometry::Size { + width: taffy::style::Dimension::Length(400f32), + height: taffy::style::Dimension::Length(300f32), + }, + ..Default::default() + }, + &[node0, node1, node2, node3], + ) + .unwrap(); + taffy.compute_layout_with_measure(node, taffy::geometry::Size::MAX_CONTENT, crate::test_measure_function).unwrap(); + println!("\nComputed tree:"); + taffy.print_tree(node); + println!(); + let Layout { size, location, .. } = taffy.layout(node).unwrap(); + assert_eq!(size.width, 400f32, "width of node {:?}. Expected {}. Actual {}", node, 400f32, size.width); + assert_eq!(size.height, 300f32, "height of node {:?}. Expected {}. Actual {}", node, 300f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node, 0f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node, 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node0).unwrap(); + assert_eq!(size.width, 30f32, "width of node {:?}. Expected {}. Actual {}", node0, 30f32, size.width); + assert_eq!(size.height, 300f32, "height of node {:?}. Expected {}. Actual {}", node0, 300f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node0, 0f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node0, 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node1).unwrap(); + assert_eq!(size.width, 30f32, "width of node {:?}. Expected {}. Actual {}", node1, 30f32, size.width); + assert_eq!(size.height, 300f32, "height of node {:?}. Expected {}. Actual {}", node1, 300f32, size.height); + assert_eq!(location.x, 93f32, "x of node {:?}. Expected {}. Actual {}", node1, 93f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node1, 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node2).unwrap(); + assert_eq!(size.width, 30f32, "width of node {:?}. Expected {}. Actual {}", node2, 30f32, size.width); + assert_eq!(size.height, 300f32, "height of node {:?}. Expected {}. Actual {}", node2, 300f32, size.height); + assert_eq!(location.x, 185f32, "x of node {:?}. Expected {}. Actual {}", node2, 185f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node2, 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node3).unwrap(); + assert_eq!(size.width, 0f32, "width of node {:?}. Expected {}. Actual {}", node3, 0f32, size.width); + assert_eq!(size.height, 0f32, "height of node {:?}. Expected {}. Actual {}", node3, 0f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node3, 0f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node3, 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node30).unwrap(); + assert_eq!(size.width, 30f32, "width of node {:?}. Expected {}. Actual {}", node30, 30f32, size.width); + assert_eq!(size.height, 300f32, "height of node {:?}. Expected {}. Actual {}", node30, 300f32, size.height); + assert_eq!(location.x, 278f32, "x of node {:?}. Expected {}. Actual {}", node30, 278f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node30, 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node31).unwrap(); + assert_eq!(size.width, 30f32, "width of node {:?}. Expected {}. Actual {}", node31, 30f32, size.width); + assert_eq!(size.height, 300f32, "height of node {:?}. Expected {}. Actual {}", node31, 300f32, size.height); + assert_eq!(location.x, 370f32, "x of node {:?}. Expected {}. Actual {}", node31, 370f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node31, 0f32, location.y); +} diff --git a/tests/generated/contents/contents_flex_nested.rs b/tests/generated/contents/contents_flex_nested.rs new file mode 100644 index 000000000..95e016fdb --- /dev/null +++ b/tests/generated/contents/contents_flex_nested.rs @@ -0,0 +1,117 @@ +#[test] +fn contents_flex_nested() { + #[allow(unused_imports)] + use taffy::{prelude::*, tree::Layout, Taffy}; + let mut taffy: Taffy = Taffy::new(); + let node0 = taffy + .new_leaf(taffy::style::Style { + size: taffy::geometry::Size { width: taffy::style::Dimension::Length(30f32), height: auto() }, + ..Default::default() + }) + .unwrap(); + let node1 = taffy + .new_leaf(taffy::style::Style { + size: taffy::geometry::Size { width: taffy::style::Dimension::Length(30f32), height: auto() }, + ..Default::default() + }) + .unwrap(); + let node2 = taffy + .new_leaf(taffy::style::Style { + size: taffy::geometry::Size { width: taffy::style::Dimension::Length(30f32), height: auto() }, + ..Default::default() + }) + .unwrap(); + let node300 = taffy + .new_leaf(taffy::style::Style { + size: taffy::geometry::Size { width: taffy::style::Dimension::Length(30f32), height: auto() }, + ..Default::default() + }) + .unwrap(); + let node301 = taffy + .new_leaf(taffy::style::Style { + size: taffy::geometry::Size { width: taffy::style::Dimension::Length(30f32), height: auto() }, + ..Default::default() + }) + .unwrap(); + let node30 = taffy + .new_with_children( + taffy::style::Style { display: taffy::style::Display::Contents, ..Default::default() }, + &[node300, node301], + ) + .unwrap(); + let node31 = taffy + .new_leaf(taffy::style::Style { + size: taffy::geometry::Size { width: taffy::style::Dimension::Length(30f32), height: auto() }, + ..Default::default() + }) + .unwrap(); + let node3 = taffy + .new_with_children( + taffy::style::Style { display: taffy::style::Display::Contents, ..Default::default() }, + &[node30, node31], + ) + .unwrap(); + let node = taffy + .new_with_children( + taffy::style::Style { + display: taffy::style::Display::Flex, + justify_content: Some(taffy::style::JustifyContent::SpaceBetween), + size: taffy::geometry::Size { + width: taffy::style::Dimension::Length(400f32), + height: taffy::style::Dimension::Length(300f32), + }, + ..Default::default() + }, + &[node0, node1, node2, node3], + ) + .unwrap(); + taffy.compute_layout_with_measure(node, taffy::geometry::Size::MAX_CONTENT, crate::test_measure_function).unwrap(); + println!("\nComputed tree:"); + taffy.print_tree(node); + println!(); + let Layout { size, location, .. } = taffy.layout(node).unwrap(); + assert_eq!(size.width, 400f32, "width of node {:?}. Expected {}. Actual {}", node, 400f32, size.width); + assert_eq!(size.height, 300f32, "height of node {:?}. Expected {}. Actual {}", node, 300f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node, 0f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node, 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node0).unwrap(); + assert_eq!(size.width, 30f32, "width of node {:?}. Expected {}. Actual {}", node0, 30f32, size.width); + assert_eq!(size.height, 300f32, "height of node {:?}. Expected {}. Actual {}", node0, 300f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node0, 0f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node0, 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node1).unwrap(); + assert_eq!(size.width, 30f32, "width of node {:?}. Expected {}. Actual {}", node1, 30f32, size.width); + assert_eq!(size.height, 300f32, "height of node {:?}. Expected {}. Actual {}", node1, 300f32, size.height); + assert_eq!(location.x, 74f32, "x of node {:?}. Expected {}. Actual {}", node1, 74f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node1, 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node2).unwrap(); + assert_eq!(size.width, 30f32, "width of node {:?}. Expected {}. Actual {}", node2, 30f32, size.width); + assert_eq!(size.height, 300f32, "height of node {:?}. Expected {}. Actual {}", node2, 300f32, size.height); + assert_eq!(location.x, 148f32, "x of node {:?}. Expected {}. Actual {}", node2, 148f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node2, 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node3).unwrap(); + assert_eq!(size.width, 0f32, "width of node {:?}. Expected {}. Actual {}", node3, 0f32, size.width); + assert_eq!(size.height, 0f32, "height of node {:?}. Expected {}. Actual {}", node3, 0f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node3, 0f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node3, 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node30).unwrap(); + assert_eq!(size.width, 0f32, "width of node {:?}. Expected {}. Actual {}", node30, 0f32, size.width); + assert_eq!(size.height, 0f32, "height of node {:?}. Expected {}. Actual {}", node30, 0f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node30, 0f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node30, 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node300).unwrap(); + assert_eq!(size.width, 30f32, "width of node {:?}. Expected {}. Actual {}", node300, 30f32, size.width); + assert_eq!(size.height, 300f32, "height of node {:?}. Expected {}. Actual {}", node300, 300f32, size.height); + assert_eq!(location.x, 222f32, "x of node {:?}. Expected {}. Actual {}", node300, 222f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node300, 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node301).unwrap(); + assert_eq!(size.width, 30f32, "width of node {:?}. Expected {}. Actual {}", node301, 30f32, size.width); + assert_eq!(size.height, 300f32, "height of node {:?}. Expected {}. Actual {}", node301, 300f32, size.height); + assert_eq!(location.x, 296f32, "x of node {:?}. Expected {}. Actual {}", node301, 296f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node301, 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node31).unwrap(); + assert_eq!(size.width, 30f32, "width of node {:?}. Expected {}. Actual {}", node31, 30f32, size.width); + assert_eq!(size.height, 300f32, "height of node {:?}. Expected {}. Actual {}", node31, 300f32, size.height); + assert_eq!(location.x, 370f32, "x of node {:?}. Expected {}. Actual {}", node31, 370f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node31, 0f32, location.y); +} diff --git a/tests/generated/contents/contents_flex_nested2.rs b/tests/generated/contents/contents_flex_nested2.rs new file mode 100644 index 000000000..8b27f3090 --- /dev/null +++ b/tests/generated/contents/contents_flex_nested2.rs @@ -0,0 +1,128 @@ +#[test] +fn contents_flex_nested2() { + #[allow(unused_imports)] + use taffy::{prelude::*, tree::Layout, Taffy}; + let mut taffy: Taffy = Taffy::new(); + let node0 = taffy + .new_leaf(taffy::style::Style { + size: taffy::geometry::Size { width: taffy::style::Dimension::Length(30f32), height: auto() }, + ..Default::default() + }) + .unwrap(); + let node1 = taffy + .new_leaf(taffy::style::Style { + size: taffy::geometry::Size { width: taffy::style::Dimension::Length(30f32), height: auto() }, + ..Default::default() + }) + .unwrap(); + let node20 = taffy + .new_leaf(taffy::style::Style { + size: taffy::geometry::Size { width: taffy::style::Dimension::Length(30f32), height: auto() }, + ..Default::default() + }) + .unwrap(); + let node2100 = taffy + .new_leaf(taffy::style::Style { + size: taffy::geometry::Size { width: taffy::style::Dimension::Length(30f32), height: auto() }, + ..Default::default() + }) + .unwrap(); + let node2101 = taffy + .new_leaf(taffy::style::Style { + size: taffy::geometry::Size { width: taffy::style::Dimension::Length(30f32), height: auto() }, + ..Default::default() + }) + .unwrap(); + let node210 = taffy + .new_with_children( + taffy::style::Style { display: taffy::style::Display::Contents, ..Default::default() }, + &[node2100, node2101], + ) + .unwrap(); + let node211 = taffy + .new_leaf(taffy::style::Style { + size: taffy::geometry::Size { width: taffy::style::Dimension::Length(30f32), height: auto() }, + ..Default::default() + }) + .unwrap(); + let node21 = taffy + .new_with_children( + taffy::style::Style { display: taffy::style::Display::Contents, ..Default::default() }, + &[node210, node211], + ) + .unwrap(); + let node2 = taffy + .new_with_children( + taffy::style::Style { display: taffy::style::Display::Contents, ..Default::default() }, + &[node20, node21], + ) + .unwrap(); + let node = taffy + .new_with_children( + taffy::style::Style { + display: taffy::style::Display::Flex, + justify_content: Some(taffy::style::JustifyContent::SpaceBetween), + size: taffy::geometry::Size { + width: taffy::style::Dimension::Length(400f32), + height: taffy::style::Dimension::Length(300f32), + }, + ..Default::default() + }, + &[node0, node1, node2], + ) + .unwrap(); + taffy.compute_layout_with_measure(node, taffy::geometry::Size::MAX_CONTENT, crate::test_measure_function).unwrap(); + println!("\nComputed tree:"); + taffy.print_tree(node); + println!(); + let Layout { size, location, .. } = taffy.layout(node).unwrap(); + assert_eq!(size.width, 400f32, "width of node {:?}. Expected {}. Actual {}", node, 400f32, size.width); + assert_eq!(size.height, 300f32, "height of node {:?}. Expected {}. Actual {}", node, 300f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node, 0f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node, 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node0).unwrap(); + assert_eq!(size.width, 30f32, "width of node {:?}. Expected {}. Actual {}", node0, 30f32, size.width); + assert_eq!(size.height, 300f32, "height of node {:?}. Expected {}. Actual {}", node0, 300f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node0, 0f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node0, 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node1).unwrap(); + assert_eq!(size.width, 30f32, "width of node {:?}. Expected {}. Actual {}", node1, 30f32, size.width); + assert_eq!(size.height, 300f32, "height of node {:?}. Expected {}. Actual {}", node1, 300f32, size.height); + assert_eq!(location.x, 74f32, "x of node {:?}. Expected {}. Actual {}", node1, 74f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node1, 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node2).unwrap(); + assert_eq!(size.width, 0f32, "width of node {:?}. Expected {}. Actual {}", node2, 0f32, size.width); + assert_eq!(size.height, 0f32, "height of node {:?}. Expected {}. Actual {}", node2, 0f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node2, 0f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node2, 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node20).unwrap(); + assert_eq!(size.width, 30f32, "width of node {:?}. Expected {}. Actual {}", node20, 30f32, size.width); + assert_eq!(size.height, 300f32, "height of node {:?}. Expected {}. Actual {}", node20, 300f32, size.height); + assert_eq!(location.x, 148f32, "x of node {:?}. Expected {}. Actual {}", node20, 148f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node20, 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node21).unwrap(); + assert_eq!(size.width, 0f32, "width of node {:?}. Expected {}. Actual {}", node21, 0f32, size.width); + assert_eq!(size.height, 0f32, "height of node {:?}. Expected {}. Actual {}", node21, 0f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node21, 0f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node21, 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node210).unwrap(); + assert_eq!(size.width, 0f32, "width of node {:?}. Expected {}. Actual {}", node210, 0f32, size.width); + assert_eq!(size.height, 0f32, "height of node {:?}. Expected {}. Actual {}", node210, 0f32, size.height); + assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node210, 0f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node210, 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node2100).unwrap(); + assert_eq!(size.width, 30f32, "width of node {:?}. Expected {}. Actual {}", node2100, 30f32, size.width); + assert_eq!(size.height, 300f32, "height of node {:?}. Expected {}. Actual {}", node2100, 300f32, size.height); + assert_eq!(location.x, 222f32, "x of node {:?}. Expected {}. Actual {}", node2100, 222f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node2100, 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node2101).unwrap(); + assert_eq!(size.width, 30f32, "width of node {:?}. Expected {}. Actual {}", node2101, 30f32, size.width); + assert_eq!(size.height, 300f32, "height of node {:?}. Expected {}. Actual {}", node2101, 300f32, size.height); + assert_eq!(location.x, 296f32, "x of node {:?}. Expected {}. Actual {}", node2101, 296f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node2101, 0f32, location.y); + let Layout { size, location, .. } = taffy.layout(node211).unwrap(); + assert_eq!(size.width, 30f32, "width of node {:?}. Expected {}. Actual {}", node211, 30f32, size.width); + assert_eq!(size.height, 300f32, "height of node {:?}. Expected {}. Actual {}", node211, 300f32, size.height); + assert_eq!(location.x, 370f32, "x of node {:?}. Expected {}. Actual {}", node211, 370f32, location.x); + assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node211, 0f32, location.y); +} diff --git a/tests/generated/contents/mod.rs b/tests/generated/contents/mod.rs new file mode 100644 index 000000000..43ced5d4b --- /dev/null +++ b/tests/generated/contents/mod.rs @@ -0,0 +1,3 @@ +mod contents_flex_basic; +mod contents_flex_nested; +mod contents_flex_nested2; diff --git a/tests/generated/mod.rs b/tests/generated/mod.rs index e02bffaf5..d09d97e94 100644 --- a/tests/generated/mod.rs +++ b/tests/generated/mod.rs @@ -1,6 +1,7 @@ mod block; mod blockflex; mod blockgrid; +mod contents; mod flex; mod grid; mod gridflex;