Delta semántico · 18
src/style.rs
- cambió de firma struct Node pub
- cambió de firma fn default
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);
+ }
+}