gioser

tawasuyu/taffy

A high performance rust-powered UI layout library

HTML 5364Rust 86Markdown 12TOML 91841 Definiciones

Fix wrapping nodes generating an incorrect min-content size (#395)

* Add tests for bevyengine/bevy#8082

* Debug log parent size

* Pass correct cross-axis parent_size/available_space when computing a flex item's min-content contribution

328880bac5865c69f8ce1e7262d69a3ef421d389 · por Nico Burns · (hace 3 años) · padre fb0bb3a8c · ver el árbol en este commit

Delta semántico · 7

src/compute/flexbox.rs

src/compute/mod.rs

tests/generated/bevy_issue_8082.rs

tests/generated/bevy_issue_8082_percent.rs

tests/generated/mod.rs

tests/generated/percentage_sizes_should_not_prevent_flex_shrinking.rs

8 archivos · +322 −15

diff --git a/src/compute/flexbox.rs b/src/compute/flexbox.rs
index 27bd419de..89069bc20 100644
--- a/src/compute/flexbox.rs
+++ b/src/compute/flexbox.rs
@@ -706,17 +706,33 @@ fn determine_flex_base_size(
         child.hypothetical_inner_size.set_main(constants.dir, hypothetical_inner_size);
         child.hypothetical_outer_size.set_main(constants.dir, hypothetical_outer_size);
 
-        // Note that it is important that the `parent_size` parameter is not set for this function call
-        // as it used to resolve percentages against, and percentage size should not contribute to a
-        // min-content contribution. See https://drafts.csswg.org/css-sizing-3/#min-percentage-contribution
-        let min_content_size = GenericAlgorithm::measure_size(
-            tree,
-            child.node,
-            Size::NONE,
-            Size::NONE,
-            Size::MIN_CONTENT,
-            SizingMode::ContentSize,
-        );
+        // Note that it is important that the `parent_size` parameter in the main axis is not set for this
+        // function call as it used for resolving percentages, and percentage size in an axis should not contribute
+        // to a min-content contribution in that same axis. However the `parent_size` and `available_space` *should*
+        // be set to their usual values in the cross axis so that wrapping content can wrap correctly.
+        //
+        // See https://drafts.csswg.org/css-sizing-3/#min-percentage-contribution
+        let min_content_size = {
+            let cross_axis_parent_size = constants.node_inner_size.cross(constants.dir);
+            let cross_axis_available_space = cross_axis_parent_size
+                .maybe_clamp(child.min_size.cross(constants.dir), child.max_size.cross(constants.dir))
+                .into();
+
+            let mut parent_size = Size::NONE;
+            parent_size.set_cross(constants.dir, cross_axis_parent_size);
+
+            let mut available_space: Size<AvailableSpace> = Size::MIN_CONTENT;
+            available_space.set_cross(constants.dir, cross_axis_available_space);
+
+            GenericAlgorithm::measure_size(
+                tree,
+                child.node,
+                Size::NONE,
+                parent_size,
+                available_space,
+                SizingMode::ContentSize,
+            )
+        };
 
         // 4.5. Automatic Minimum Size of Flex Items
         // https://www.w3.org/TR/css-flexbox-1/#min-size-auto
diff --git a/src/compute/mod.rs b/src/compute/mod.rs
index 50280f474..099d1634d 100644
--- a/src/compute/mod.rs
+++ b/src/compute/mod.rs
@@ -247,6 +247,7 @@ fn debug_log_node(
     NODE_LOGGER.debug_log(run_mode);
     NODE_LOGGER.labelled_debug_log("sizing_mode", sizing_mode);
     NODE_LOGGER.labelled_debug_log("known_dimensions", known_dimensions);
+    NODE_LOGGER.labelled_debug_log("parent_size", parent_size);
     NODE_LOGGER.labelled_debug_log("available_space", available_space);
 }
 
diff --git a/test_fixtures/bevy_issue_8082.html b/test_fixtures/bevy_issue_8082.html
new file mode 100644
index 000000000..bd7c9d5ef
--- /dev/null
+++ b/test_fixtures/bevy_issue_8082.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <script src="../scripts/gentest/test_helper.js"></script>
+  <link rel="stylesheet" type="text/css" href="../scripts/gentest/test_base_style.css">
+  <title>
+    Test description
+  </title>
+<head/>
+<body>
+
+<div id="test-root" style="width: 200px; height: 400px; align-content: center;justify-content: flex-start;align-items:stretch;flex-direction: column;">
+  <div style="flex-wrap: wrap;align-items:flex-start;align-content:center;justify-content: center;">
+    <div style="width: 50px; height: 50px;margin: 10px"></div>
+    <div style="width: 50px; height: 50px;margin: 10px"></div>
+    <div style="width: 50px; height: 50px;margin: 10px"></div>
+    <div style="width: 50px; height: 50px;margin: 10px"></div>
+  </div>
+</div>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/test_fixtures/bevy_issue_8082_percent.html b/test_fixtures/bevy_issue_8082_percent.html
new file mode 100644
index 000000000..ca81dbeeb
--- /dev/null
+++ b/test_fixtures/bevy_issue_8082_percent.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <script src="../scripts/gentest/test_helper.js"></script>
+  <link rel="stylesheet" type="text/css" href="../scripts/gentest/test_base_style.css">
+  <title>
+    Test description
+  </title>
+<head/>
+<body>
+
+<div id="test-root" style="width: 200px; height: 400px; align-content: center;justify-content: flex-start;align-items:stretch;flex-direction: column;">
+  <div style="flex-wrap: wrap;align-items:flex-start;align-content:center;justify-content: center;width: 50%">
+    <div style="width: 50px; height: 50px"></div>
+    <div style="width: 50px; height: 50px"></div>
+    <div style="width: 50px; height: 50px"></div>
+    <div style="width: 50px; height: 50px"></div>
+  </div>
+</div>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/tests/generated/bevy_issue_8082.rs b/tests/generated/bevy_issue_8082.rs
new file mode 100644
index 000000000..cefd7548f
--- /dev/null
+++ b/tests/generated/bevy_issue_8082.rs
@@ -0,0 +1,129 @@
+#[test]
+fn bevy_issue_8082() {
+    use slotmap::Key;
+    #[allow(unused_imports)]
+    use taffy::{layout::Layout, prelude::*};
+    let mut taffy = taffy::Taffy::new();
+    let node00 = taffy
+        .new_leaf(taffy::style::Style {
+            size: taffy::geometry::Size {
+                width: taffy::style::Dimension::Points(50f32),
+                height: taffy::style::Dimension::Points(50f32),
+            },
+            margin: taffy::geometry::Rect {
+                left: taffy::style::LengthPercentageAuto::Points(10f32),
+                right: taffy::style::LengthPercentageAuto::Points(10f32),
+                top: taffy::style::LengthPercentageAuto::Points(10f32),
+                bottom: taffy::style::LengthPercentageAuto::Points(10f32),
+            },
+            ..Default::default()
+        })
+        .unwrap();
+    let node01 = taffy
+        .new_leaf(taffy::style::Style {
+            size: taffy::geometry::Size {
+                width: taffy::style::Dimension::Points(50f32),
+                height: taffy::style::Dimension::Points(50f32),
+            },
+            margin: taffy::geometry::Rect {
+                left: taffy::style::LengthPercentageAuto::Points(10f32),
+                right: taffy::style::LengthPercentageAuto::Points(10f32),
+                top: taffy::style::LengthPercentageAuto::Points(10f32),
+                bottom: taffy::style::LengthPercentageAuto::Points(10f32),
+            },
+            ..Default::default()
+        })
+        .unwrap();
+    let node02 = taffy
+        .new_leaf(taffy::style::Style {
+            size: taffy::geometry::Size {
+                width: taffy::style::Dimension::Points(50f32),
+                height: taffy::style::Dimension::Points(50f32),
+            },
+            margin: taffy::geometry::Rect {
+                left: taffy::style::LengthPercentageAuto::Points(10f32),
+                right: taffy::style::LengthPercentageAuto::Points(10f32),
+                top: taffy::style::LengthPercentageAuto::Points(10f32),
+                bottom: taffy::style::LengthPercentageAuto::Points(10f32),
+            },
+            ..Default::default()
+        })
+        .unwrap();
+    let node03 = taffy
+        .new_leaf(taffy::style::Style {
+            size: taffy::geometry::Size {
+                width: taffy::style::Dimension::Points(50f32),
+                height: taffy::style::Dimension::Points(50f32),
+            },
+            margin: taffy::geometry::Rect {
+                left: taffy::style::LengthPercentageAuto::Points(10f32),
+                right: taffy::style::LengthPercentageAuto::Points(10f32),
+                top: taffy::style::LengthPercentageAuto::Points(10f32),
+                bottom: taffy::style::LengthPercentageAuto::Points(10f32),
+            },
+            ..Default::default()
+        })
+        .unwrap();
+    let node0 = taffy
+        .new_with_children(
+            taffy::style::Style {
+                flex_wrap: taffy::style::FlexWrap::Wrap,
+                align_items: Some(taffy::style::AlignItems::FlexStart),
+                align_content: Some(taffy::style::AlignContent::Center),
+                justify_content: Some(taffy::style::JustifyContent::Center),
+                ..Default::default()
+            },
+            &[node00, node01, node02, node03],
+        )
+        .unwrap();
+    let node = taffy
+        .new_with_children(
+            taffy::style::Style {
+                flex_direction: taffy::style::FlexDirection::Column,
+                align_items: Some(taffy::style::AlignItems::Stretch),
+                align_content: Some(taffy::style::AlignContent::Center),
+                justify_content: Some(taffy::style::JustifyContent::FlexStart),
+                size: taffy::geometry::Size {
+                    width: taffy::style::Dimension::Points(200f32),
+                    height: taffy::style::Dimension::Points(400f32),
+                },
+                ..Default::default()
+            },
+            &[node0],
+        )
+        .unwrap();
+    taffy.compute_layout(node, taffy::geometry::Size::MAX_CONTENT).unwrap();
+    println!("\nComputed tree:");
+    taffy::debug::print_tree(&taffy, node);
+    println!();
+    let Layout { size, location, .. } = taffy.layout(node).unwrap();
+    assert_eq!(size.width, 200f32, "width of node {:?}. Expected {}. Actual {}", node.data(), 200f32, size.width);
+    assert_eq!(size.height, 400f32, "height of node {:?}. Expected {}. Actual {}", node.data(), 400f32, size.height);
+    assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node.data(), 0f32, location.x);
+    assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node.data(), 0f32, location.y);
+    let Layout { size, location, .. } = taffy.layout(node0).unwrap();
+    assert_eq!(size.width, 200f32, "width of node {:?}. Expected {}. Actual {}", node0.data(), 200f32, size.width);
+    assert_eq!(size.height, 140f32, "height of node {:?}. Expected {}. Actual {}", node0.data(), 140f32, size.height);
+    assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node0.data(), 0f32, location.x);
+    assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node0.data(), 0f32, location.y);
+    let Layout { size, location, .. } = taffy.layout(node00).unwrap();
+    assert_eq!(size.width, 50f32, "width of node {:?}. Expected {}. Actual {}", node00.data(), 50f32, size.width);
+    assert_eq!(size.height, 50f32, "height of node {:?}. Expected {}. Actual {}", node00.data(), 50f32, size.height);
+    assert_eq!(location.x, 40f32, "x of node {:?}. Expected {}. Actual {}", node00.data(), 40f32, location.x);
+    assert_eq!(location.y, 10f32, "y of node {:?}. Expected {}. Actual {}", node00.data(), 10f32, location.y);
+    let Layout { size, location, .. } = taffy.layout(node01).unwrap();
+    assert_eq!(size.width, 50f32, "width of node {:?}. Expected {}. Actual {}", node01.data(), 50f32, size.width);
+    assert_eq!(size.height, 50f32, "height of node {:?}. Expected {}. Actual {}", node01.data(), 50f32, size.height);
+    assert_eq!(location.x, 110f32, "x of node {:?}. Expected {}. Actual {}", node01.data(), 110f32, location.x);
+    assert_eq!(location.y, 10f32, "y of node {:?}. Expected {}. Actual {}", node01.data(), 10f32, location.y);
+    let Layout { size, location, .. } = taffy.layout(node02).unwrap();
+    assert_eq!(size.width, 50f32, "width of node {:?}. Expected {}. Actual {}", node02.data(), 50f32, size.width);
+    assert_eq!(size.height, 50f32, "height of node {:?}. Expected {}. Actual {}", node02.data(), 50f32, size.height);
+    assert_eq!(location.x, 40f32, "x of node {:?}. Expected {}. Actual {}", node02.data(), 40f32, location.x);
+    assert_eq!(location.y, 80f32, "y of node {:?}. Expected {}. Actual {}", node02.data(), 80f32, location.y);
+    let Layout { size, location, .. } = taffy.layout(node03).unwrap();
+    assert_eq!(size.width, 50f32, "width of node {:?}. Expected {}. Actual {}", node03.data(), 50f32, size.width);
+    assert_eq!(size.height, 50f32, "height of node {:?}. Expected {}. Actual {}", node03.data(), 50f32, size.height);
+    assert_eq!(location.x, 110f32, "x of node {:?}. Expected {}. Actual {}", node03.data(), 110f32, location.x);
+    assert_eq!(location.y, 80f32, "y of node {:?}. Expected {}. Actual {}", node03.data(), 80f32, location.y);
+}
diff --git a/tests/generated/bevy_issue_8082_percent.rs b/tests/generated/bevy_issue_8082_percent.rs
new file mode 100644
index 000000000..a18290a1a
--- /dev/null
+++ b/tests/generated/bevy_issue_8082_percent.rs
@@ -0,0 +1,106 @@
+#[test]
+fn bevy_issue_8082_percent() {
+    use slotmap::Key;
+    #[allow(unused_imports)]
+    use taffy::{layout::Layout, prelude::*};
+    let mut taffy = taffy::Taffy::new();
+    let node00 = taffy
+        .new_leaf(taffy::style::Style {
+            size: taffy::geometry::Size {
+                width: taffy::style::Dimension::Points(50f32),
+                height: taffy::style::Dimension::Points(50f32),
+            },
+            ..Default::default()
+        })
+        .unwrap();
+    let node01 = taffy
+        .new_leaf(taffy::style::Style {
+            size: taffy::geometry::Size {
+                width: taffy::style::Dimension::Points(50f32),
+                height: taffy::style::Dimension::Points(50f32),
+            },
+            ..Default::default()
+        })
+        .unwrap();
+    let node02 = taffy
+        .new_leaf(taffy::style::Style {
+            size: taffy::geometry::Size {
+                width: taffy::style::Dimension::Points(50f32),
+                height: taffy::style::Dimension::Points(50f32),
+            },
+            ..Default::default()
+        })
+        .unwrap();
+    let node03 = taffy
+        .new_leaf(taffy::style::Style {
+            size: taffy::geometry::Size {
+                width: taffy::style::Dimension::Points(50f32),
+                height: taffy::style::Dimension::Points(50f32),
+            },
+            ..Default::default()
+        })
+        .unwrap();
+    let node0 = taffy
+        .new_with_children(
+            taffy::style::Style {
+                flex_wrap: taffy::style::FlexWrap::Wrap,
+                align_items: Some(taffy::style::AlignItems::FlexStart),
+                align_content: Some(taffy::style::AlignContent::Center),
+                justify_content: Some(taffy::style::JustifyContent::Center),
+                size: taffy::geometry::Size { width: taffy::style::Dimension::Percent(0.5f32), height: auto() },
+                ..Default::default()
+            },
+            &[node00, node01, node02, node03],
+        )
+        .unwrap();
+    let node = taffy
+        .new_with_children(
+            taffy::style::Style {
+                flex_direction: taffy::style::FlexDirection::Column,
+                align_items: Some(taffy::style::AlignItems::Stretch),
+                align_content: Some(taffy::style::AlignContent::Center),
+                justify_content: Some(taffy::style::JustifyContent::FlexStart),
+                size: taffy::geometry::Size {
+                    width: taffy::style::Dimension::Points(200f32),
+                    height: taffy::style::Dimension::Points(400f32),
+                },
+                ..Default::default()
+            },
+            &[node0],
+        )
+        .unwrap();
+    taffy.compute_layout(node, taffy::geometry::Size::MAX_CONTENT).unwrap();
+    println!("\nComputed tree:");
+    taffy::debug::print_tree(&taffy, node);
+    println!();
+    let Layout { size, location, .. } = taffy.layout(node).unwrap();
+    assert_eq!(size.width, 200f32, "width of node {:?}. Expected {}. Actual {}", node.data(), 200f32, size.width);
+    assert_eq!(size.height, 400f32, "height of node {:?}. Expected {}. Actual {}", node.data(), 400f32, size.height);
+    assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node.data(), 0f32, location.x);
+    assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node.data(), 0f32, location.y);
+    let Layout { size, location, .. } = taffy.layout(node0).unwrap();
+    assert_eq!(size.width, 100f32, "width of node {:?}. Expected {}. Actual {}", node0.data(), 100f32, size.width);
+    assert_eq!(size.height, 100f32, "height of node {:?}. Expected {}. Actual {}", node0.data(), 100f32, size.height);
+    assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node0.data(), 0f32, location.x);
+    assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node0.data(), 0f32, location.y);
+    let Layout { size, location, .. } = taffy.layout(node00).unwrap();
+    assert_eq!(size.width, 50f32, "width of node {:?}. Expected {}. Actual {}", node00.data(), 50f32, size.width);
+    assert_eq!(size.height, 50f32, "height of node {:?}. Expected {}. Actual {}", node00.data(), 50f32, size.height);
+    assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node00.data(), 0f32, location.x);
+    assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node00.data(), 0f32, location.y);
+    let Layout { size, location, .. } = taffy.layout(node01).unwrap();
+    assert_eq!(size.width, 50f32, "width of node {:?}. Expected {}. Actual {}", node01.data(), 50f32, size.width);
+    assert_eq!(size.height, 50f32, "height of node {:?}. Expected {}. Actual {}", node01.data(), 50f32, size.height);
+    assert_eq!(location.x, 50f32, "x of node {:?}. Expected {}. Actual {}", node01.data(), 50f32, location.x);
+    assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node01.data(), 0f32, location.y);
+    let Layout { size, location, .. } = taffy.layout(node02).unwrap();
+    assert_eq!(size.width, 50f32, "width of node {:?}. Expected {}. Actual {}", node02.data(), 50f32, size.width);
+    assert_eq!(size.height, 50f32, "height of node {:?}. Expected {}. Actual {}", node02.data(), 50f32, size.height);
+    assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node02.data(), 0f32, location.x);
+    assert_eq!(location.y, 50f32, "y of node {:?}. Expected {}. Actual {}", node02.data(), 50f32, location.y);
+    let Layout { size, location, .. } = taffy.layout(node03).unwrap();
+    assert_eq!(size.width, 50f32, "width of node {:?}. Expected {}. Actual {}", node03.data(), 50f32, size.width);
+    assert_eq!(size.height, 50f32, "height of node {:?}. Expected {}. Actual {}", node03.data(), 50f32, size.height);
+    assert_eq!(location.x, 50f32, "x of node {:?}. Expected {}. Actual {}", node03.data(), 50f32, location.x);
+    assert_eq!(location.y, 50f32, "y of node {:?}. Expected {}. Actual {}", node03.data(), 50f32, location.y);
+}
diff --git a/tests/generated/mod.rs b/tests/generated/mod.rs
index 47cb45127..482132f0b 100644
--- a/tests/generated/mod.rs
+++ b/tests/generated/mod.rs
@@ -203,6 +203,8 @@ mod bevy_issue_7976_4_level;
 mod bevy_issue_7976_reduced;
 mod bevy_issue_8017;
 mod bevy_issue_8017_reduced;
+mod bevy_issue_8082;
+mod bevy_issue_8082_percent;
 mod border_center_child;
 mod border_container_match_child;
 mod border_flex_child;
diff --git a/tests/generated/percentage_sizes_should_not_prevent_flex_shrinking.rs b/tests/generated/percentage_sizes_should_not_prevent_flex_shrinking.rs
index fa40fec88..9864cf8e2 100644
--- a/tests/generated/percentage_sizes_should_not_prevent_flex_shrinking.rs
+++ b/tests/generated/percentage_sizes_should_not_prevent_flex_shrinking.rs
@@ -4,11 +4,15 @@ fn percentage_sizes_should_not_prevent_flex_shrinking() {
     #[allow(unused_imports)]
     use taffy::{layout::Layout, prelude::*};
     let mut taffy = taffy::Taffy::new();
+    let node00 = taffy.new_leaf(taffy::style::Style { ..Default::default() }).unwrap();
     let node0 = taffy
-        .new_leaf(taffy::style::Style {
-            size: taffy::geometry::Size { width: taffy::style::Dimension::Percent(1.2f32), height: auto() },
-            ..Default::default()
-        })
+        .new_with_children(
+            taffy::style::Style {
+                size: taffy::geometry::Size { width: taffy::style::Dimension::Percent(1.2f32), height: auto() },
+                ..Default::default()
+            },
+            &[node00],
+        )
         .unwrap();
     let node = taffy
         .new_with_children(
@@ -36,4 +40,9 @@ fn percentage_sizes_should_not_prevent_flex_shrinking() {
     assert_eq!(size.height, 200f32, "height of node {:?}. Expected {}. Actual {}", node0.data(), 200f32, size.height);
     assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node0.data(), 0f32, location.x);
     assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node0.data(), 0f32, location.y);
+    let Layout { size, location, .. } = taffy.layout(node00).unwrap();
+    assert_eq!(size.width, 0f32, "width of node {:?}. Expected {}. Actual {}", node00.data(), 0f32, size.width);
+    assert_eq!(size.height, 200f32, "height of node {:?}. Expected {}. Actual {}", node00.data(), 200f32, size.height);
+    assert_eq!(location.x, 0f32, "x of node {:?}. Expected {}. Actual {}", node00.data(), 0f32, location.x);
+    assert_eq!(location.y, 0f32, "y of node {:?}. Expected {}. Actual {}", node00.data(), 0f32, location.y);
 }