gioser

tawasuyu/taffy

A high performance rust-powered UI layout library

Rust 501HTML 254C 19JavaScript 81412 Definiciones

Add support for custom measure function

612f8e0a029a70d701f458ebb53331264d12191f · por Emil Sjölander · (hace 7 años) · padre 97b3de870 · ver el árbol en este commit

Delta semántico · 18

src/algo.rs

src/style.rs

  • cambió de firma struct Node pub
  • cambió de firma fn default

3 archivos · +342 −10

diff --git a/src/algo.rs b/src/algo.rs
index 9df68861b..64872b51b 100644
--- a/src/algo.rs
+++ b/src/algo.rs
@@ -4,7 +4,7 @@ use std::f32;
 use crate::layout;
 
 use crate::style;
-use crate::style::{AlignContent, AlignSelf, Dimension, Display, FlexWrap, JustifyContent, PositionType};
+use crate::style::*;
 
 use crate::number::Number::*;
 use crate::number::*;
@@ -18,7 +18,7 @@ struct ComputeResult {
 }
 
 struct FlexItem<'a> {
-    node: &'a style::Node,
+    node: &'a style::Node<'a>,
 
     size: Size<Number>,
     min_size: Size<Number>,
@@ -112,6 +112,17 @@ fn compute_internal(
     parent_size: Size<Number>,
     percent_calc_base: Number,
 ) -> ComputeResult {
+    // If this is a leaf node we can skip a lot of this function in some cases
+    if node.children.is_empty() {
+        if node_size.width.is_defined() && node_size.height.is_defined() {
+            return ComputeResult { size: node_size.map(|s| s.or_else(0.0)), children: vec![] };
+        }
+
+        if let Some(measure) = node.measure {
+            return ComputeResult { size: measure(node_size), children: vec![] };
+        }
+    }
+
     // Define some general constants we will need for the remainder
     // of the algorithm.
 
@@ -501,8 +512,9 @@ fn compute_internal(
                 // TODO - not really spec abiding but needs to be done somewhere. probably somewhere else though.
                 // The following logic was developed not from the spec but by trail and error looking into how
                 // webkit handled various scenarios. Can probably be solved better by passing in
-                // min-content max-content constraints fromt the top
-                let min_main = if is_row {
+                // min-content max-content constraints fromt the top. Need to figure out correct thing to do here as 
+                // just piling on more conditionals.
+                let min_main = if is_row && child.node.measure.is_none() {
                     compute_internal(
                         child.node,
                         Size { width: Undefined, height: Undefined },
diff --git a/src/style.rs b/src/style.rs
index a4353140a..4d0342b4a 100644
--- a/src/style.rs
+++ b/src/style.rs
@@ -191,8 +191,7 @@ impl Default for Size<Dimension> {
     }
 }
 
-#[derive(Debug)]
-pub struct Node {
+pub struct Node<'a> {
     pub display: Display,
 
     pub position_type: PositionType,
@@ -222,12 +221,13 @@ pub struct Node {
     pub max_size: Size<Dimension>,
 
     pub aspect_ratio: Number,
+    pub measure: Option<&'a Fn(Size<Number>) -> Size<f32>>,
 
-    pub children: Vec<Node>,
+    pub children: Vec<Node<'a>>,
 }
 
-impl Default for Node {
-    fn default() -> Node {
+impl<'a> Default for Node<'a> {
+    fn default() -> Node<'a> {
         Node {
             display: Default::default(),
 
@@ -258,13 +258,14 @@ impl Default for Node {
             max_size: Default::default(),
 
             aspect_ratio: Default::default(),
+            measure: None,
 
             children: vec![],
         }
     }
 }
 
-impl Node {
+impl<'a> Node<'a> {
     pub(crate) fn min_main_size(&self, direction: FlexDirection) -> Dimension {
         match direction {
             FlexDirection::Row | FlexDirection::RowReverse => self.min_size.width,
diff --git a/tests/measure.rs b/tests/measure.rs
new file mode 100644
index 000000000..fd33e9291
--- /dev/null
+++ b/tests/measure.rs
@@ -0,0 +1,319 @@
+#[cfg(test)]
+mod measure {
+    use stretch::number::OrElse;
+
+    #[test]
+    fn measure_root() {
+        let layout = stretch::compute(&stretch::style::Node {
+            measure: Some(&|constraint| stretch::geometry::Size {
+                width: constraint.width.or_else(100.0),
+                height: constraint.height.or_else(100.0),
+            }),
+            ..Default::default()
+        });
+
+        assert_eq!(layout.size.width, 100.0);
+        assert_eq!(layout.size.height, 100.0);
+    }
+
+    #[test]
+    fn measure_child() {
+        let layout = stretch::compute(&stretch::style::Node {
+            children: vec![stretch::style::Node {
+                measure: Some(&|constraint| stretch::geometry::Size {
+                    width: constraint.width.or_else(100.0),
+                    height: constraint.height.or_else(100.0),
+                }),
+                ..Default::default()
+            }],
+            ..Default::default()
+        });
+
+        assert_eq!(layout.size.width, 100.0);
+        assert_eq!(layout.size.height, 100.0);
+
+        assert_eq!(layout.children[0].size.width, 100.0);
+        assert_eq!(layout.children[0].size.height, 100.0);
+    }
+
+    #[test]
+    fn measure_child_constraint() {
+        let layout = stretch::compute(&stretch::style::Node {
+            size: stretch::geometry::Size { width: stretch::style::Dimension::Points(50.0), ..Default::default() },
+            children: vec![stretch::style::Node {
+                measure: Some(&|constraint| stretch::geometry::Size {
+                    width: constraint.width.or_else(100.0),
+                    height: constraint.height.or_else(100.0),
+                }),
+                ..Default::default()
+            }],
+            ..Default::default()
+        });
+
+        assert_eq!(layout.size.width, 50.0);
+        assert_eq!(layout.size.height, 100.0);
+
+        assert_eq!(layout.children[0].size.width, 50.0);
+        assert_eq!(layout.children[0].size.height, 100.0);
+    }
+
+    #[test]
+    fn measure_child_constraint_padding_parent() {
+        let layout = stretch::compute(&stretch::style::Node {
+            size: stretch::geometry::Size { width: stretch::style::Dimension::Points(50.0), ..Default::default() },
+            padding: stretch::geometry::Rect {
+                start: stretch::style::Dimension::Points(10.0),
+                end: stretch::style::Dimension::Points(10.0),
+                top: stretch::style::Dimension::Points(10.0),
+                bottom: stretch::style::Dimension::Points(10.0),
+            },
+            children: vec![stretch::style::Node {
+                measure: Some(&|constraint| stretch::geometry::Size {
+                    width: constraint.width.or_else(100.0),
+                    height: constraint.height.or_else(100.0),
+                }),
+                ..Default::default()
+            }],
+            ..Default::default()
+        });
+
+        assert_eq!(layout.size.width, 50.0);
+        assert_eq!(layout.size.height, 120.0);
+
+        assert_eq!(layout.children[0].size.width, 30.0);
+        assert_eq!(layout.children[0].size.height, 100.0);
+    }
+
+    #[test]
+    fn measure_child_with_flex_grow() {
+        let layout = stretch::compute(&stretch::style::Node {
+            size: stretch::geometry::Size { width: stretch::style::Dimension::Points(100.0), ..Default::default() },
+            children: vec![
+                stretch::style::Node {
+                    size: stretch::geometry::Size {
+                        width: stretch::style::Dimension::Points(50.0),
+                        height: stretch::style::Dimension::Points(50.0),
+                    },
+                    ..Default::default()
+                },
+                stretch::style::Node {
+                    measure: Some(&|constraint| stretch::geometry::Size {
+                        width: constraint.width.or_else(10.0),
+                        height: constraint.height.or_else(50.0),
+                    }),
+                    flex_grow: 1.0,
+                    ..Default::default()
+                },
+            ],
+            ..Default::default()
+        });
+
+        assert_eq!(layout.children[1].size.width, 50.0);
+        assert_eq!(layout.children[1].size.height, 50.0);
+    }
+
+    #[test]
+    fn measure_child_with_flex_shrink() {
+        let layout = stretch::compute(&stretch::style::Node {
+            size: stretch::geometry::Size { width: stretch::style::Dimension::Points(100.0), ..Default::default() },
+            children: vec![
+                stretch::style::Node {
+                    size: stretch::geometry::Size {
+                        width: stretch::style::Dimension::Points(50.0),
+                        height: stretch::style::Dimension::Points(50.0),
+                    },
+                    flex_shrink: 0.0,
+                    ..Default::default()
+                },
+                stretch::style::Node {
+                    measure: Some(&|constraint| stretch::geometry::Size {
+                        width: constraint.width.or_else(100.0),
+                        height: constraint.height.or_else(50.0),
+                    }),
+                    ..Default::default()
+                },
+            ],
+            ..Default::default()
+        });
+
+        assert_eq!(layout.children[1].size.width, 50.0);
+        assert_eq!(layout.children[1].size.height, 50.0);
+    }
+
+    #[test]
+    fn remeasure_child_after_growing() {
+        let layout = stretch::compute(&stretch::style::Node {
+            size: stretch::geometry::Size {
+                width: stretch::style::Dimension::Points(100.0), 
+                ..Default::default()
+            },
+            align_items: stretch::style::AlignItems::FlexStart,
+            children: vec![
+                stretch::style::Node {
+                    size: stretch::geometry::Size {
+                        width: stretch::style::Dimension::Points(50.0),
+                        height: stretch::style::Dimension::Points(50.0),
+                    },
+                    ..Default::default()
+                },
+                stretch::style::Node {
+                    measure: Some(&|constraint| {
+                        let width = constraint.width.or_else(10.0);
+                        let height = constraint.height.or_else(width * 2.0);
+                        stretch::geometry::Size { width, height }
+                    }),
+                    flex_grow: 1.0,
+                    ..Default::default()
+                },
+            ],
+            ..Default::default()
+        });
+
+        assert_eq!(layout.children[1].size.width, 50.0);
+        assert_eq!(layout.children[1].size.height, 100.0);
+    }
+
+    #[test]
+    fn remeasure_child_after_shrinking() {
+        let layout = stretch::compute(&stretch::style::Node {
+            size: stretch::geometry::Size {
+                width: stretch::style::Dimension::Points(100.0), 
+                ..Default::default()
+            },
+            align_items: stretch::style::AlignItems::FlexStart,
+            children: vec![
+                stretch::style::Node {
+                    size: stretch::geometry::Size {
+                        width: stretch::style::Dimension::Points(50.0),
+                        height: stretch::style::Dimension::Points(50.0),
+                    },
+                    flex_shrink: 0.0,
+                    ..Default::default()
+                },
+                stretch::style::Node {
+                    measure: Some(&|constraint| {
+                        let width = constraint.width.or_else(100.0);
+                        let height = constraint.height.or_else(width * 2.0);
+                        stretch::geometry::Size { width, height }
+                    }),
+                    ..Default::default()
+                },
+            ],
+            ..Default::default()
+        });
+
+        assert_eq!(layout.children[1].size.width, 50.0);
+        assert_eq!(layout.children[1].size.height, 100.0);
+    }
+
+    #[test]
+    fn width_overrides_measure() {
+        let layout = stretch::compute(&stretch::style::Node {
+            children: vec![stretch::style::Node {
+                size: stretch::geometry::Size { width: stretch::style::Dimension::Points(50.0), ..Default::default() },
+                measure: Some(&|constraint| stretch::geometry::Size {
+                    width: constraint.width.or_else(100.0),
+                    height: constraint.height.or_else(100.0),
+                }),
+                ..Default::default()
+            }],
+            ..Default::default()
+        });
+
+        assert_eq!(layout.children[0].size.width, 50.0);
+        assert_eq!(layout.children[0].size.height, 100.0);
+    }
+
+    #[test]
+    fn height_overrides_measure() {
+        let layout = stretch::compute(&stretch::style::Node {
+            children: vec![stretch::style::Node {
+                size: stretch::geometry::Size { height: stretch::style::Dimension::Points(50.0), ..Default::default() },
+                measure: Some(&|constraint| stretch::geometry::Size {
+                    width: constraint.width.or_else(100.0),
+                    height: constraint.height.or_else(100.0),
+                }),
+                ..Default::default()
+            }],
+            ..Default::default()
+        });
+
+        assert_eq!(layout.children[0].size.width, 100.0);
+        assert_eq!(layout.children[0].size.height, 50.0);
+    }
+
+    #[test]
+    fn flex_basis_overrides_measure() {
+        let layout = stretch::compute(&stretch::style::Node {
+            size: stretch::geometry::Size {
+                width: stretch::style::Dimension::Points(200.0),
+                height: stretch::style::Dimension::Points(100.0),
+            },
+            children: vec![
+                stretch::style::Node {
+                    flex_basis: stretch::style::Dimension::Points(50.0),
+                    flex_grow: 1.0,
+                    ..Default::default()
+                },
+                stretch::style::Node {
+                    flex_basis: stretch::style::Dimension::Points(50.0),
+                    flex_grow: 1.0,
+                    measure: Some(&|constraint| stretch::geometry::Size {
+                        width: constraint.width.or_else(100.0),
+                        height: constraint.height.or_else(100.0),
+                    }),
+                    ..Default::default()
+                },
+            ],
+            ..Default::default()
+        });
+
+        assert_eq!(layout.children[0].size.width, 100.0);
+        assert_eq!(layout.children[0].size.height, 100.0);
+        assert_eq!(layout.children[1].size.width, 100.0);
+        assert_eq!(layout.children[1].size.height, 100.0);
+    }
+
+    #[test]
+    fn stretch_overrides_measure() {
+        let layout = stretch::compute(&stretch::style::Node {
+            size: stretch::geometry::Size {
+                width: stretch::style::Dimension::Points(100.0),
+                height: stretch::style::Dimension::Points(100.0),
+            },
+            children: vec![stretch::style::Node {
+                measure: Some(&|constraint| stretch::geometry::Size {
+                    width: constraint.width.or_else(50.0),
+                    height: constraint.height.or_else(50.0),
+                }),
+                ..Default::default()
+            }],
+            ..Default::default()
+        });
+
+        assert_eq!(layout.children[0].size.width, 50.0);
+        assert_eq!(layout.children[0].size.height, 100.0);
+    }
+
+    #[test]
+    fn measure_absolute_child() {
+        let layout = stretch::compute(&stretch::style::Node {
+            size: stretch::geometry::Size {
+                width: stretch::style::Dimension::Points(100.0),
+                height: stretch::style::Dimension::Points(100.0),
+            },
+            children: vec![stretch::style::Node {
+                position_type: stretch::style::PositionType::Absolute,
+                measure: Some(&|constraint| stretch::geometry::Size {
+                    width: constraint.width.or_else(50.0),
+                    height: constraint.height.or_else(50.0),
+                }),
+                ..Default::default()
+            }],
+            ..Default::default()
+        });
+
+        assert_eq!(layout.children[0].size.width, 50.0);
+        assert_eq!(layout.children[0].size.height, 50.0);
+    }
+}