use super::{cache::LayoutCacheKey, Layer, LayoutNodeAccess, NodeBehaviour, NodeState}; pub fn recalculate(node: LayoutNodeAccess) { if let NodeBehaviour::Fixed { area } = node.behaviour() { let layer = Layer::default(); // propagate relayout flags up to the root node.cache.propagate_relayouts(); // propagate rerender flags down to the leaves node.cache.propagate_rerenders(); arrangement_pass(None, node, layer); node.child_arrangement.layout_step(node, area); } else { log::warn!("Layout root node does not have Fixed mode"); } } fn arrangement_pass(parent: Option, node: LayoutNodeAccess, layer: Layer) { // early-out check if node.cache.with_state(node.cache_key, |st| st.needs_update) == Some(false) { return; } for child in node.child_iter() { arrangement_pass(Some(node.cache_key), child, layer.nest()); } // construct net size policy let child_policies = node .child_iter() .map(|child| { node.cache .with_state(child.cache_key, |s| s.net_policy) .unwrap() }) .collect::>(); let (wpol, hpol) = node.child_arrangement.arrange_step(node, child_policies); let child_ids = node.child_iter().map(|ch| ch.cache_key).collect(); if node.cache.has_state_for(node.cache_key) { node.cache.with_state(node.cache_key, |ns| { ns.net_policy = (wpol, hpol); ns.children = child_ids; }); } else { node.cache.store( node.cache_key, parent, NodeState { needs_update: false, needs_render: true, net_policy: (wpol, hpol), area: None, layer, children: child_ids, }, ); } } #[cfg(test)] mod test { use kahlo::math::{PixelBox, PixelPoint, PixelRect, PixelSize}; use crate::layout::{ cache::LayoutCache, ChildArrangement, LayoutNode, LayoutNodeAccess, LayoutNodeContainer, NodeBehaviour, SizePolicy, }; use super::recalculate; struct LayoutTree { children: Vec, node: LayoutNode, } impl LayoutNodeContainer for LayoutTree { fn layout_node(&self) -> &LayoutNode { &self.node } fn layout_child(&self, ndx: usize) -> Option> { self.children.get(ndx).map(|t| LayoutNodeAccess::new(t)) } fn layout_child_count(&self) -> usize { self.children.len() } } #[test] fn simple_test() { let cache = LayoutCache::new(); let mut root = LayoutTree { children: vec![ { let mut lt = LayoutTree { children: vec![], node: LayoutNode::new(cache.clone()), }; lt.node.set_height_policy(SizePolicy { minimum: 1, desired: 1, slack_weight: 1, }); lt }, { let mut lt = LayoutTree { children: vec![], node: LayoutNode::new(cache.clone()), }; lt.node.set_height_policy(SizePolicy { minimum: 2, desired: 3, slack_weight: 0, }); lt }, ], node: LayoutNode::new(cache.clone()), }; root.node.set_behaviour(NodeBehaviour::Fixed { area: PixelBox::from_origin_and_size(PixelPoint::new(1, 1), PixelSize::new(2, 5)), }); root.node.child_arrangement = ChildArrangement::Column; recalculate(LayoutNodeAccess::new(&root)); println!("cache: {:?}", cache); // check that final rects match expectations assert_eq!( cache .with_state(root.node.cache_key, |ns| ns.area) .flatten(), Some(PixelBox::from_origin_and_size( PixelPoint::new(1, 1), PixelSize::new(2, 5) )) ); assert_eq!( cache .with_state(root.children[0].node.cache_key, |ns| ns.area) .flatten(), Some(PixelBox::from_origin_and_size( PixelPoint::new(1, 1), PixelSize::new(2, 2) )) ); } }