gioser

tawasuyu/taffy

A high performance rust-powered UI layout library

HTML 231Rust 10TOML 3CSS 1348 Definiciones

Fix: available space in the presence of a min width (#407)

* Add `with_main` and `with_cross` methods to Size

* Add test for min_width > available_space

* Clamp available cross space by min/max height when computing flex basis

* Clamp available cross space by min/max size when computing hypothetical cross size

* Bump measure func call counts to 8

0b736d47e1e71f29aafdf940d0204d5fcdb61de4 · por Nico Burns · (hace 3 años) · padre ac79a0044 · ver el árbol en este commit

Delta semántico · 9

src/compute/flexbox.rs

src/geometry.rs

tests/caching.rs

tests/generated/measure_child_with_min_size_greater_than_available_space.rs

6 archivos · +132 −34

diff --git a/src/compute/flexbox.rs b/src/compute/flexbox.rs
index 89069bc20..a5b3916a2 100644
--- a/src/compute/flexbox.rs
+++ b/src/compute/flexbox.rs
@@ -611,9 +611,20 @@ fn determine_flex_base_size(
     available_space: Size<AvailableSpace>,
     flex_items: &mut [FlexItem],
 ) {
+    let dir = constants.dir;
+
     for child in flex_items.iter_mut() {
         let child_style = tree.style(child.node);
 
+        // Parent size for child sizing
+        let cross_axis_parent_size = constants.node_inner_size.cross(dir);
+
+        // Available space for child sizing
+        let cross_axis_margin_sum = constants.margin.cross_axis_sum(dir);
+        let child_min_cross = child.min_size.cross(dir).maybe_add(cross_axis_margin_sum);
+        let child_max_cross = child.max_size.cross(dir).maybe_add(cross_axis_margin_sum);
+        let cross_axis_available_space = cross_axis_parent_size.maybe_clamp(child_min_cross, child_max_cross).into();
+
         child.flex_basis = 'flex_basis: {
             // A. If the item has a definite used flex basis, that’s the flex base size.
 
@@ -625,8 +636,8 @@ fn determine_flex_base_size(
             // Note: `child.size` has already been resolved against aspect_ratio in generate_anonymous_flex_items
             // So B will just work here by using main_size without special handling for aspect_ratio
 
-            let flex_basis = child_style.flex_basis.maybe_resolve(constants.node_inner_size.main(constants.dir));
-            let main_size = child.size.main(constants.dir);
+            let flex_basis = child_style.flex_basis.maybe_resolve(constants.node_inner_size.main(dir));
+            let main_size = child.size.main(dir);
             if let Some(flex_basis) = flex_basis.or(main_size) {
                 break 'flex_basis flex_basis;
             };
@@ -655,15 +666,19 @@ fn determine_flex_base_size(
             //    is auto and not definite, in this calculation use fit-content as the
             //    flex item’s cross size. The flex base size is the item’s resulting main size.
 
+            let child_parent_size = Size::NONE.with_cross(dir, cross_axis_parent_size);
+            let child_available_space = available_space.with_cross(dir, cross_axis_available_space);
+
             let child_known_dimensions = {
                 let mut ckd = child.size;
-                if child.align_self == AlignSelf::Stretch && ckd.cross(constants.dir).is_none() {
+                if child.align_self == AlignSelf::Stretch && ckd.cross(dir).is_none() {
                     ckd.set_cross(
-                        constants.dir,
-                        available_space
-                            .cross(constants.dir)
+                        dir,
+                        child_available_space
+                            .cross(dir)
                             .into_option()
-                            .maybe_sub(constants.margin.cross_axis_sum(constants.dir)),
+                            .maybe_clamp(child_min_cross, child_max_cross)
+                            .maybe_sub(constants.margin.cross_axis_sum(dir)),
                     );
                 }
                 ckd
@@ -673,11 +688,11 @@ fn determine_flex_base_size(
                 tree,
                 child.node,
                 child_known_dimensions,
-                constants.node_inner_size,
-                available_space,
+                child_parent_size,
+                child_available_space,
                 SizingMode::ContentSize,
             )
-            .main(constants.dir);
+            .main(dir);
         };
 
         // Floor flex-basis by the padding_border_sum (floors inner_flex_basis at zero)
@@ -713,23 +728,15 @@ fn determine_flex_base_size(
         //
         // 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);
+            let child_parent_size = Size::NONE.with_cross(dir, cross_axis_parent_size);
+            let child_available_space = Size::MIN_CONTENT.with_cross(dir, cross_axis_available_space);
 
             GenericAlgorithm::measure_size(
                 tree,
                 child.node,
                 Size::NONE,
-                parent_size,
-                available_space,
+                child_parent_size,
+                child_available_space,
                 SizingMode::ContentSize,
             )
         };
@@ -1220,12 +1227,19 @@ fn determine_hypothetical_cross_size(
     for child in line.items.iter_mut() {
         let padding_border_sum = (child.padding + child.border).cross_axis_sum(constants.dir);
 
+        let child_known_main = constants.container_size.main(constants.dir).into();
+
         let child_cross = child
             .size
             .cross(constants.dir)
             .maybe_clamp(child.min_size.cross(constants.dir), child.max_size.cross(constants.dir))
             .maybe_max(padding_border_sum);
 
+        let child_available_cross = available_space
+            .cross(constants.dir)
+            .maybe_clamp(child.min_size.cross(constants.dir), child.max_size.cross(constants.dir))
+            .maybe_max(padding_border_sum);
+
         let child_inner_cross = child_cross.unwrap_or_else(|| {
             GenericAlgorithm::measure_size(
                 tree,
@@ -1236,16 +1250,8 @@ fn determine_hypothetical_cross_size(
                 },
                 constants.node_inner_size,
                 Size {
-                    width: if constants.is_row {
-                        constants.container_size.main(constants.dir).into()
-                    } else {
-                        available_space.width
-                    },
-                    height: if constants.is_row {
-                        available_space.height
-                    } else {
-                        constants.container_size.main(constants.dir).into()
-                    },
+                    width: if constants.is_row { child_known_main } else { child_available_cross },
+                    height: if constants.is_row { child_available_cross } else { child_known_main },
                 },
                 SizingMode::ContentSize,
             )
diff --git a/src/geometry.rs b/src/geometry.rs
index 3db779ed1..3f96d3d85 100644
--- a/src/geometry.rs
+++ b/src/geometry.rs
@@ -320,6 +320,33 @@ impl<T> Size<T> {
         }
     }
 
+    /// Creates a new value of type Self with the main axis set to value provided
+    ///
+    /// Whether this is the width or height depends on the `direction` provided
+    #[allow(dead_code)]
+    pub(crate) fn with_main(self, direction: FlexDirection, value: T) -> Self {
+        let mut new = self;
+        if direction.is_row() {
+            new.width = value
+        } else {
+            new.height = value
+        }
+        new
+    }
+
+    /// Creates a new value of type Self with the cross axis set to value provided
+    ///
+    /// Whether this is the width or height depends on the `direction` provided
+    pub(crate) fn with_cross(self, direction: FlexDirection, value: T) -> Self {
+        let mut new = self;
+        if direction.is_row() {
+            new.height = value
+        } else {
+            new.width = value
+        }
+        new
+    }
+
     /// Gets the extent of the main layout axis
     ///
     /// Whether this is the width or height depends on the `direction` provided
diff --git a/test_fixtures/measure_child_with_min_size_greater_than_available_space.html b/test_fixtures/measure_child_with_min_size_greater_than_available_space.html
new file mode 100644
index 000000000..3e46330f0
--- /dev/null
+++ b/test_fixtures/measure_child_with_min_size_greater_than_available_space.html
@@ -0,0 +1,14 @@
+<!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="display: flex; flex-direction: column; width: 100px;">
+  <div style="min-width: 200px;">HHHHHHHH&ZeroWidthSpace;HHHHHHHH</div>
+</div>
diff --git a/tests/caching.rs b/tests/caching.rs
index 387d56002..581ce43e9 100644
--- a/tests/caching.rs
+++ b/tests/caching.rs
@@ -30,7 +30,7 @@ mod caching {
 
         taffy.compute_layout(node, Size::MAX_CONTENT).unwrap();
 
-        assert_eq!(NUM_MEASURES.load(Ordering::SeqCst), 7);
+        assert_eq!(NUM_MEASURES.load(Ordering::SeqCst), 8);
     }
 
     #[test]
@@ -62,6 +62,6 @@ mod caching {
         }
 
         taffy.compute_layout(node, Size::MAX_CONTENT).unwrap();
-        assert_eq!(NUM_MEASURES.load(Ordering::SeqCst), 7);
+        assert_eq!(NUM_MEASURES.load(Ordering::SeqCst), 8);
     }
 }
diff --git a/tests/generated/measure_child_with_min_size_greater_than_available_space.rs b/tests/generated/measure_child_with_min_size_greater_than_available_space.rs
new file mode 100644
index 000000000..d42622268
--- /dev/null
+++ b/tests/generated/measure_child_with_min_size_greater_than_available_space.rs
@@ -0,0 +1,50 @@
+#[test]
+fn measure_child_with_min_size_greater_than_available_space() {
+    use slotmap::Key;
+    #[allow(unused_imports)]
+    use taffy::{layout::Layout, prelude::*};
+    let mut taffy = taffy::Taffy::new();
+    let node0 = taffy
+        .new_leaf_with_measure(
+            taffy::style::Style {
+                min_size: taffy::geometry::Size { width: taffy::style::Dimension::Points(200f32), height: auto() },
+                ..Default::default()
+            },
+            taffy::node::MeasureFunc::Raw(|known_dimensions, available_space| {
+                const TEXT: &str = "HHHHHHHH\u{200b}HHHHHHHH";
+                super::measure_standard_text(
+                    known_dimensions,
+                    available_space,
+                    TEXT,
+                    super::WritingMode::Horizontal,
+                    None,
+                )
+            }),
+        )
+        .unwrap();
+    let node = taffy
+        .new_with_children(
+            taffy::style::Style {
+                display: taffy::style::Display::Flex,
+                flex_direction: taffy::style::FlexDirection::Column,
+                size: taffy::geometry::Size { width: taffy::style::Dimension::Points(100f32), height: auto() },
+                ..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, 100f32, "width of node {:?}. Expected {}. Actual {}", node.data(), 100f32, size.width);
+    assert_eq!(size.height, 10f32, "height of node {:?}. Expected {}. Actual {}", node.data(), 10f32, 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, 10f32, "height of node {:?}. Expected {}. Actual {}", node0.data(), 10f32, 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);
+}
diff --git a/tests/generated/mod.rs b/tests/generated/mod.rs
index 482132f0b..4be363806 100644
--- a/tests/generated/mod.rs
+++ b/tests/generated/mod.rs
@@ -812,6 +812,7 @@ mod measure_child_constraint;
 mod measure_child_constraint_padding_parent;
 mod measure_child_with_flex_grow;
 mod measure_child_with_flex_shrink;
+mod measure_child_with_min_size_greater_than_available_space;
 mod measure_flex_basis_overrides_measure;
 mod measure_height_overrides_measure;
 mod measure_remeasure_child_after_growing;