tawasuyu

tawasuyu/taffy

A high performance rust-powered UI layout library

HTML 5544Rust 88Markdown 11TOML 91926 Sutichaykuna

Morphorm WIP

b570f0abd9ec730debdf8c71d48912fbee15dc42 · ruwaq Nico Burns · (3 wata ñawpaq) · tayta 838c413a7 · kay commitpi sach'ata qhaway

Semántico sayk'uy · 2

src/compute/morphorm.rs

  • silq'uyninpi t'ikrakurqan struct ChildNode pub
  • ukhunpi t'ikrakurqan fn compute pub

1 qillqakuna · +553 −29

diff --git a/src/compute/morphorm.rs b/src/compute/morphorm.rs
index ad4a889f4..5f61227d4 100644
--- a/src/compute/morphorm.rs
+++ b/src/compute/morphorm.rs
@@ -7,6 +7,7 @@ use crate::style::{
 use crate::style::{FlexDirection, Style};
 use crate::tree::LayoutTree;
 use crate::tree::NodeId;
+use crate::util::{MaybeMath, MaybeResolve};
 use crate::tree::{Layout, RunMode, SizeBaselinesAndMargins, SizingMode};
 
 /// The public interface to Taffy's Flexbox algorithm implementation
@@ -43,37 +44,42 @@ impl LayoutAlgorithm for MorphormAlgorithm {
 pub struct ChildNode {
     // A reference to the node.
     node: NodeId,
+    // Whether the child is in flow or not
+    is_in_flow: bool,
     // The index of the node.
     index: usize,
 
-    // The stretch factor of the node.
-    stretch_factor: Size<f32>,
-    // The minimum constraint of the node.
-    min: Size<Option<f32>>,
-    // The maximum constraint of the node.
-    max: Size<Option<f32>>,
-
-    // Sum of the flex factors on the main axis of the node.
-    main_flex_sum: f32,
-    // The available free space on the main axis of the node.
-    main_non_flex: f32,
-    // A remainder used during stretch computation.
-    main_remainder: f32,
-    // Sum of the cross_before, cross, and cross_after flex factors of the node.
-    cross_flex_sum: f32,
-
-    cross_non_flex: f32,
-    cross: f32,
-    cross_remainder: f32,
-
-    // Computed main-before space of the node.
-    main_before: f32,
-    // Computed main-after space of the node.
-    main_after: f32,
-    // Computed cross-before space of the node.
-    cross_before: f32,
-    // Computed cross-after space of the node.
-    cross_after: f32,
+    style: Style,
+
+
+    // // The stretch factor of the node.
+    // stretch_factor: Size<f32>,
+    // // The minimum constraint of the node.
+    // min: Size<Option<f32>>,
+    // // The maximum constraint of the node.
+    // max: Size<Option<f32>>,
+
+    // // Sum of the flex factors on the main axis of the node.
+    // main_flex_sum: f32,
+    // // The available free space on the main axis of the node.
+    // main_non_flex: f32,
+    // // A remainder used during stretch computation.
+    // main_remainder: f32,
+    // // Sum of the cross_before, cross, and cross_after flex factors of the node.
+    // cross_flex_sum: f32,
+
+    // cross_non_flex: f32,
+    // cross: f32,
+    // cross_remainder: f32,
+
+    // // Computed main-before space of the node.
+    // main_before: f32,
+    // // Computed main-after space of the node.
+    // main_after: f32,
+    // // Computed cross-before space of the node.
+    // cross_before: f32,
+    // // Computed cross-after space of the node.
+    // cross_after: f32,
 }
 
 /// Computes the layout of [`LayoutTree`] according to the flexbox algorithm
@@ -85,5 +91,523 @@ pub fn compute(
     available_space: Size<AvailableSpace>,
     run_mode: RunMode,
 ) -> SizeBaselinesAndMargins {
-    Size::zero().into()
+
+    // TODO: Min/Max constraints for space and size
+
+    // The layout type of the node. Determines the main and cross axes of the children.
+    let style = tree.style(node).clone();
+    // let layout_type = style.flex_direction;
+    let is_row = style.flex_direction.is_row();
+
+    // // The desired main-axis and cross-axis sizes of the node.
+    // let main = node.main(store, parent_layout_type);
+    // let cross = node.cross(store, parent_layout_type);
+
+    // // Compute main-axis size.
+    // let mut computed_main = match main {
+    //     Pixels(val) => val,
+
+    //     Percentage(val) => (parent_main * (val / 100.0)).round(),
+
+    //     Stretch(_) => parent_main,
+
+    //     Auto => 0.0,
+    // };
+
+    let min_size = style.min_size.maybe_resolve(parent_size);
+    let max_size = style.max_size.maybe_resolve(parent_size);
+    let clamped_style_size =
+        style.size.maybe_resolve(parent_size).maybe_clamp(min_size, max_size);
+
+    // If both min and max in a given axis are set and max <= min then this determines the size in that axis
+    let min_max_definite_size = min_size.zip_map(max_size, |min, max| match (min, max) {
+        (Some(min), Some(max)) if max <= min => Some(min),
+        _ => None,
+    });
+    let styled_based_known_dimensions = known_dimensions.or(min_max_definite_size).or(clamped_style_size);
+
+    // Short-circuit layout if the container's size is fully determined by the container's size and the run mode
+    // is ComputeSize (and thus the container's size is all that we're interested in)
+    if run_mode == RunMode::ComputeSize {
+        if let Size { width: Some(width), height: Some(height) } = styled_based_known_dimensions {
+            return Size { width, height }.into();
+        }
+    }
+
+
+
+
+    let in_flow_children =  tree.children(node)
+        .map(|child| (child, tree.style(*child)))
+        .filter(|(_, style)| style.position != Position::Absolute)
+        .filter(|(_, style)| style.display != Display::None)
+        .enumerate()
+        .map(|(index, (child, child_style))| {
+
+          let child_min_size = child_style.min_size.maybe_resolve(parent_size);
+          let max_size = style.max_size.maybe_resolve(parent_size);
+          let clamped_style_size =
+              style.size.maybe_resolve(parent_size).maybe_clamp(min_size, max_size);
+
+          // If both min and max in a given axis are set and max <= min then this determines the size in that axis
+          let min_max_definite_size = min_size.zip_map(max_size, |min, max| match (min, max) {
+              (Some(min), Some(max)) if max <= min => Some(min),
+              _ => None,
+          });
+          let styled_based_known_dimensions = min_max_definite_size.or(clamped_style_size);
+
+          let content_size = if child_style.size.width == Dimension::Auto || child_style.size.height == Dimension::Auto {
+            tree.measure_child_size(node, known_dimensions, parent_size, available_space, SizingMode::InherentSize, Line::NONE)
+          } else {
+            Size::zero()
+          };
+
+          ChildNode {
+            node,
+            is_in_flow: child_style.position != Position::Absolute
+            index,
+            style: child_style.clone(),
+          }
+        })
+        .collect();
+
+
+    // Sum of all non-flexible space and size on the main-axis of the node.
+    let mut main_non_flex = 0.0f32;
+
+    // Sum of all space and size flex factors on the main-axis of the node.
+    let mut main_flex_sum = 0.0;
+
+    // Sum of all child nodes on the main-axis.
+    let mut main_sum = 0.0f32;
+
+    // Maximum of all child nodes on the cross-axis.
+    let mut cross_max = 0.0f32;
+
+    // List of child nodes for the current node.
+    let mut children = SmallVec::<[ChildNode<N>; 3]>::new();
+
+    // List of stretch nodes for the current node.
+    // A stretch node is any flexible space/size. e.g. main_before, main, and main_after are separate stretch nodes
+    let mut stretch_nodes = SmallVec::<[StretchNode<N>; 3]>::new();
+
+    // Parent overrides for child auto space.
+    let node_child_main_before = node.child_main_before(store, layout_type);
+    let node_child_main_after = node.child_main_after(store, layout_type);
+    let node_child_cross_before = node.child_cross_before(store, layout_type);
+    let node_child_cross_after = node.child_cross_after(store, layout_type);
+    let node_child_main_between = node.main_between(store, layout_type);
+
+    // Determine index of first and last parent-directed child nodes.
+    let mut iter = node
+        .children(tree)
+        .enumerate()
+        .filter(|(_, child)| child.position_type(store).unwrap_or_default() == PositionType::ParentDirected);
+
+    let first = iter.next().map(|(index, _)| index);
+    let last = iter.last().map_or(first, |(index, _)| Some(index));
+
+    let num_children = node.children(tree).count();
+
+    // Compute non-flexible children.
+    for (index, child) in node.children(tree).enumerate() {
+        let child_position_type = child.position_type(store).unwrap_or_default();
+
+        let mut child_main_before = child.main_before(store, layout_type);
+        let child_main = child.main(store, layout_type);
+        let mut child_main_after = child.main_after(store, layout_type);
+
+        let mut child_cross_before = child.cross_before(store, layout_type);
+        let child_cross = child.cross(store, layout_type);
+        let mut child_cross_after = child.cross_after(store, layout_type);
+
+        // Apply parent overrides to auto child space.
+        if child_main_before == Units::Auto {
+            if first == Some(index) || child_position_type == PositionType::SelfDirected {
+                child_main_before = node_child_main_before;
+            } else {
+                child_main_before = node_child_main_between;
+            }
+        }
+
+        if child_main_after == Units::Auto && (last == Some(index) || child_position_type == PositionType::SelfDirected)
+        {
+            child_main_after = node_child_main_after;
+        }
+
+        if child_cross_before == Units::Auto {
+            child_cross_before = node_child_cross_before;
+        }
+
+        if child_cross_after == Units::Auto {
+            child_cross_after = node_child_cross_after;
+        }
+
+        // Sum of flex factors on the main and cross axes of the child node.
+        let mut child_main_flex_sum = 0.0;
+        let mut child_cross_flex_sum = 0.0;
+
+        let mut computed_child_main_before = 0.0;
+        let mut computed_child_main = 0.0;
+        let mut computed_child_main_after = 0.0;
+
+        let mut computed_child_cross_before = 0.0;
+        let mut computed_child_cross = 0.0;
+        let mut computed_child_cross_after = 0.0;
+
+        match child_cross_before {
+            Pixels(val) => {
+                computed_child_cross_before = val;
+            }
+
+            Percentage(val) => {
+                computed_child_cross_before = (parent_cross * (val / 100.0)).round();
+            }
+
+            Stretch(factor) => {
+                child_cross_flex_sum += factor;
+            }
+
+            _ => {}
+        }
+
+        let child_min_cross_before = child.min_cross_before(store, layout_type).to_px(parent_cross, DEFAULT_MIN);
+        let child_max_cross_before = child.max_cross_before(store, layout_type).to_px(parent_cross, DEFAULT_MAX);
+
+        computed_child_cross_before = computed_child_cross_before.clamp(child_min_cross_before, child_max_cross_before);
+
+        match child_cross_after {
+            Pixels(val) => {
+                computed_child_cross_after = val;
+            }
+
+            Percentage(val) => {
+                computed_child_cross_after = (parent_cross * (val / 100.0)).round();
+            }
+
+            Stretch(factor) => {
+                child_cross_flex_sum += factor;
+            }
+
+            _ => {}
+        }
+
+        let child_min_cross_after = child.min_cross_after(store, layout_type).to_px(parent_cross, DEFAULT_MIN);
+        let child_max_cross_after = child.max_cross_after(store, layout_type).to_px(parent_cross, DEFAULT_MAX);
+
+        computed_child_cross_after = computed_child_cross_after.clamp(child_min_cross_after, child_max_cross_after);
+
+        match child_cross {
+            Pixels(val) => {
+                computed_child_cross = val;
+            }
+
+            Percentage(val) => {
+                computed_child_cross = (parent_cross * (val / 100.0)).round();
+            }
+
+            Stretch(factor) => {
+                child_cross_flex_sum += factor;
+            }
+
+            _ => {}
+        }
+
+        let child_min_cross = child.min_cross(store, layout_type).to_px(parent_cross, DEFAULT_MIN);
+        let child_max_cross = child.max_cross(store, layout_type).to_px(parent_cross, DEFAULT_MAX);
+
+        computed_child_cross = computed_child_cross.clamp(child_min_cross, child_max_cross);
+
+        let child_min_main_before = child.min_main_before(store, layout_type).to_px(parent_main, DEFAULT_MIN);
+        let child_max_main_before = child.max_main_before(store, layout_type).to_px(parent_main, DEFAULT_MAX);
+
+        match child_main_before {
+            Pixels(val) => {
+                computed_child_main_before = val;
+            }
+
+            Percentage(val) => {
+                computed_child_main_before = (parent_main * (val / 100.0)).round();
+            }
+
+            Stretch(factor) => {
+                child_main_flex_sum += factor;
+
+                stretch_nodes.push(StretchNode {
+                    node: child,
+                    index,
+                    factor,
+                    min: child_min_main_before,
+                    max: child_max_main_before,
+                    axis: Axis::MainBefore,
+                });
+            }
+
+            _ => {}
+        }
+
+        computed_child_main_before = computed_child_main_before.clamp(child_min_main_before, child_max_main_before);
+
+        let child_min_main_after = child.min_main_after(store, layout_type).to_px(parent_main, DEFAULT_MIN);
+        let child_max_main_after = child.max_main_after(store, layout_type).to_px(parent_main, DEFAULT_MAX);
+
+        match child_main_after {
+            Pixels(val) => {
+                computed_child_main_after = val;
+            }
+
+            Percentage(val) => {
+                computed_child_main_after = (parent_main * (val / 100.0)).round();
+            }
+
+            Stretch(factor) => {
+                child_main_flex_sum += factor;
+
+                stretch_nodes.push(StretchNode {
+                    node: child,
+                    index,
+                    factor,
+                    min: child_min_main_after,
+                    max: child_max_main_after,
+                    axis: Axis::MainAfter,
+                });
+            }
+
+            _ => {}
+        }
+
+        computed_child_main_after = computed_child_main_after.clamp(child_min_main_after, child_max_main_after);
+
+        // Total computed size on the cross-axis of the child.
+        let mut child_cross_non_flex = computed_child_cross_before + computed_child_cross + computed_child_cross_after;
+
+        match child_main {
+            Stretch(factor) => {
+                child_main_flex_sum += factor;
+
+                stretch_nodes.push(StretchNode {
+                    node: child,
+                    index,
+                    factor,
+                    min: DEFAULT_MIN,
+                    max: DEFAULT_MAX,
+                    axis: Axis::Main,
+                });
+            }
+
+            _ => {
+                let child_size = layout(child, layout_type, parent_main, computed_child_cross, cache, tree, store);
+
+                computed_child_main = child_size.main;
+                computed_child_cross = child_size.cross;
+
+                if child_cross == Units::Auto {
+                    child_cross_non_flex += computed_child_cross;
+                }
+            }
+        }
+
+        // Total computed size on the main-axis of the child.
+        let child_main_non_flex = computed_child_main_before + computed_child_main + computed_child_main_after;
+
+        if child_position_type == PositionType::ParentDirected {
+            main_non_flex += child_main_non_flex;
+            main_flex_sum += child_main_flex_sum;
+
+            main_sum += child_main_non_flex;
+        } else {
+            main_sum = main_sum.max(child_main_non_flex);
+        }
+
+        cross_max = cross_max.max(child_cross_non_flex);
+
+        children.push(ChildNode {
+            node: child,
+            main_flex_sum: child_main_flex_sum,
+            main_non_flex: child_main_non_flex,
+            main_remainder: 0.0,
+            cross_flex_sum: child_cross_flex_sum,
+            cross_non_flex: child_cross_non_flex,
+            cross: computed_child_cross,
+            cross_remainder: 0.0,
+            main_before: computed_child_main_before,
+            main_after: computed_child_main_after,
+            cross_before: computed_child_cross_before,
+            cross_after: computed_child_cross_after,
+        });
+    }
+
+    // Calculate free space on the main-axis for the node.
+    let free_main_space = (parent_main.max(main_sum) - main_non_flex).max(0.0);
+    let mut remainder: f32 = 0.0;
+    let main_px_per_flex = free_main_space / main_flex_sum;
+
+    // Compute flexible space and size on the main axis.
+    for item in stretch_nodes.iter() {
+        let child_position_type = item.node.position_type(store).unwrap_or_default();
+
+        let actual_main = if child_position_type == PositionType::SelfDirected {
+            let child_main_free_space = (parent_main.max(main_sum) - children[item.index].main_non_flex).max(0.0);
+            let px_per_flex = child_main_free_space / children[item.index].main_flex_sum;
+            let desired_main = item.factor * px_per_flex + children[item.index].main_remainder;
+            let actual_main = desired_main.round();
+            children[item.index].main_remainder = desired_main - actual_main;
+            actual_main
+        } else {
+            let desired_main = item.factor * main_px_per_flex + remainder;
+            let actual_main = desired_main.round();
+            remainder = desired_main - actual_main;
+            actual_main
+        };
+
+        let computed_child_cross = children[item.index].cross;
+
+        match item.axis {
+            Axis::MainBefore => {
+                children[item.index].main_before = actual_main;
+            }
+
+            Axis::MainAfter => {
+                children[item.index].main_after = actual_main;
+            }
+
+            Axis::Main => {
+                let child_size = layout(item.node, layout_type, actual_main, computed_child_cross, cache, tree, store);
+
+                children[item.index].cross_non_flex += child_size.cross;
+                cross_max = cross_max.max(children[item.index].cross_non_flex);
+            }
+        }
+    }
+
+    // Compute flexible space and size on the cross-axis.
+    // This needs to be done after computing the main-axis because layout computation for stretch children may cause
+    // the cross space to change due to content size.
+    for child in children.iter_mut() {
+        let child_cross_free_space = parent_cross.max(cross_max) - child.cross_non_flex;
+        let cross_px_per_flex = child_cross_free_space / child.cross_flex_sum;
+
+        let child_cross_before = child.node.cross_before(store, layout_type);
+        let child_cross = child.node.cross(store, layout_type);
+        let child_cross_after = child.node.cross_after(store, layout_type);
+
+        if let Stretch(factor) = child_cross_before {
+            let desired_cross = factor * cross_px_per_flex + child.cross_remainder;
+            let actual_cross = desired_cross.round();
+            child.cross_remainder = desired_cross - actual_cross;
+            child.cross_before = actual_cross;
+        }
+
+        if let Stretch(factor) = child_cross {
+            let desired_cross = factor * cross_px_per_flex + child.cross_remainder;
+            let actual_cross = desired_cross.round();
+            child.cross_remainder = desired_cross - actual_cross;
+
+            // if cross == Units::Auto && num_children != 0 {
+                let size = layout(child.node, layout_type, parent_main, actual_cross, cache, tree, store);
+            //     println!("size: {:?}", size);
+            // } else {
+                // At this stage stretch nodes on the cross-axis can only be the determined size so we can set it directly
+                // in the cache without needing to call layout again.
+                // match layout_type {
+                //     LayoutType::Row => {
+                //         cache.set_height(child.node.key(), actual_cross);
+                //     }
+
+                //     LayoutType::Column => {
+                //         cache.set_width(child.node.key(), actual_cross);
+                //     }
+                // }
+            // }
+        }
+
+        if let Stretch(factor) = child_cross_after {
+            let desired_cross = factor * cross_px_per_flex + child.cross_remainder;
+            let actual_cross = desired_cross.round();
+            child.cross_remainder = desired_cross - actual_cross;
+            child.cross_after = actual_cross;
+        }
+    }
+
+    // Position children.
+    let mut main_pos = 0.0;
+    for child in children.iter() {
+        let child_position_type = child.node.position_type(store).unwrap_or_default();
+
+        match child_position_type {
+            PositionType::SelfDirected => match layout_type {
+                LayoutType::Row => {
+                    cache.set_posx(child.node.key(), child.main_before);
+                    cache.set_posy(child.node.key(), child.cross_before);
+                }
+
+                LayoutType::Column => {
+                    cache.set_posy(child.node.key(), child.main_before);
+                    cache.set_posx(child.node.key(), child.cross_before);
+                }
+            },
+
+            PositionType::ParentDirected => {
+                main_pos += child.main_before;
+
+                match layout_type {
+                    LayoutType::Row => {
+                        cache.set_posx(child.node.key(), main_pos);
+                        cache.set_posy(child.node.key(), child.cross_before);
+                        let child_width = cache.width(child.node.key());
+                        main_pos += child_width;
+                    }
+
+                    LayoutType::Column => {
+                        cache.set_posy(child.node.key(), main_pos);
+                        cache.set_posx(child.node.key(), child.cross_before);
+                        let child_height = cache.height(child.node.key());
+                        main_pos += child_height;
+                    }
+                }
+
+                main_pos += child.main_after;
+            }
+        };
+    }
+
+    // This part is required for auto size when the node has children but conflicts with the content size when the node doesn't have children
+    // TODO: Make it so a node can only have content size if it has no children?
+    // TODO: Potentially split and move this to before stretch calculations.
+    if num_children != 0 {
+        if parent_layout_type == layout_type {
+            if let Auto = main {
+                computed_main = main_sum;
+            }
+
+            if let Auto = cross {
+                computed_cross = cross_max;
+            }
+        } else {
+            if let Auto = main {
+                computed_main = cross_max;
+            }
+
+            if let Auto = cross {
+                computed_cross = main_sum;
+            }
+        }
+    }
+
+    // Set the computed size of the node in the cache.
+    match parent_layout_type {
+        LayoutType::Row => {
+            cache.set_width(node.key(), computed_main);
+            cache.set_height(node.key(), computed_cross);
+        }
+
+        LayoutType::Column => {
+            cache.set_height(node.key(), computed_main);
+            cache.set_width(node.key(), computed_cross);
+        }
+    }
+
+    // Return the computed size, propagating it back up the tree.
+    Size { main: computed_main, cross: computed_cross }.into()
+
 }