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
tests/generated/bevy_issue_8082.rs
tests/generated/bevy_issue_8082_percent.rs
tests/generated/percentage_sizes_should_not_prevent_flex_shrinking.rs
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);
}