1use crate::compute::LayoutAlgorithm;
2use crate::geometry::{Line, Point, Rect, Size};
3use crate::style::{
4 AlignContent, AlignItems, AlignSelf, AvailableSpace, Dimension, Display, FlexWrap, JustifyContent,
5 LengthPercentageAuto, Position,
6};
7use crate::style::{FlexDirection, Style};
8use crate::tree::LayoutTree;
9use crate::tree::NodeId;
10use crate::util::{MaybeMath, MaybeResolve};
11use crate::tree::{Layout, RunMode, SizeBaselinesAndMargins, SizingMode};
12
13/// The public interface to Taffy's Flexbox algorithm implementation
14pub struct MorphormAlgorithm;
15impl LayoutAlgorithm for MorphormAlgorithm {
16 const NAME: &'static str = "MORPHORM";
17
18 fn perform_layout(
19 tree: &mut impl LayoutTree,
20 node: NodeId,
21 known_dimensions: Size<Option<f32>>,
22 parent_size: Size<Option<f32>>,
23 available_space: Size<AvailableSpace>,
24 _sizing_mode: SizingMode,
25 _vertical_margins_are_collapsible: Line<bool>,
26 ) -> SizeBaselinesAndMargins {
27 compute(tree, node, known_dimensions, parent_size, available_space, RunMode::PerformLayout)
28 }
29
30 fn measure_size(
31 tree: &mut impl LayoutTree,
32 node: NodeId,
33 known_dimensions: Size<Option<f32>>,
34 parent_size: Size<Option<f32>>,
35 available_space: Size<AvailableSpace>,
36 _sizing_mode: SizingMode,
37 _vertical_margins_are_collapsible: Line<bool>,
38 ) -> Size<f32> {
39 compute(tree, node, known_dimensions, parent_size, available_space, RunMode::ComputeSize).size
40 }
41}
42
43#[derive(Debug)]
44pub struct ChildNode {
45 // A reference to the node.
46 node: NodeId,
47 // Whether the child is in flow or not
48 is_in_flow: bool,
49 // The index of the node.
50 index: usize,
51
52 style: Style,
53
54
55 // // The stretch factor of the node.
56 // stretch_factor: Size<f32>,
57 // // The minimum constraint of the node.
58 // min: Size<Option<f32>>,
59 // // The maximum constraint of the node.
60 // max: Size<Option<f32>>,
61
62 // // Sum of the flex factors on the main axis of the node.
63 // main_flex_sum: f32,
64 // // The available free space on the main axis of the node.
65 // main_non_flex: f32,
66 // // A remainder used during stretch computation.
67 // main_remainder: f32,
68 // // Sum of the cross_before, cross, and cross_after flex factors of the node.
69 // cross_flex_sum: f32,
70
71 // cross_non_flex: f32,
72 // cross: f32,
73 // cross_remainder: f32,
74
75 // // Computed main-before space of the node.
76 // main_before: f32,
77 // // Computed main-after space of the node.
78 // main_after: f32,
79 // // Computed cross-before space of the node.
80 // cross_before: f32,
81 // // Computed cross-after space of the node.
82 // cross_after: f32,
83}
84
85/// Computes the layout of [`LayoutTree`] according to the flexbox algorithm
86pub fn compute(
87 tree: &mut impl LayoutTree,
88 node: NodeId,
89 known_dimensions: Size<Option<f32>>,
90 parent_size: Size<Option<f32>>,
91 available_space: Size<AvailableSpace>,
92 run_mode: RunMode,
93) -> SizeBaselinesAndMargins {
94
95 // TODO: Min/Max constraints for space and size
96
97 // The layout type of the node. Determines the main and cross axes of the children.
98 let style = tree.style(node).clone();
99 // let layout_type = style.flex_direction;
100 let is_row = style.flex_direction.is_row();
101
102 // // The desired main-axis and cross-axis sizes of the node.
103 // let main = node.main(store, parent_layout_type);
104 // let cross = node.cross(store, parent_layout_type);
105
106 // // Compute main-axis size.
107 // let mut computed_main = match main {
108 // Pixels(val) => val,
109
110 // Percentage(val) => (parent_main * (val / 100.0)).round(),
111
112 // Stretch(_) => parent_main,
113
114 // Auto => 0.0,
115 // };
116
117 let min_size = style.min_size.maybe_resolve(parent_size);
118 let max_size = style.max_size.maybe_resolve(parent_size);
119 let clamped_style_size =
120 style.size.maybe_resolve(parent_size).maybe_clamp(min_size, max_size);
121
122 // If both min and max in a given axis are set and max <= min then this determines the size in that axis
123 let min_max_definite_size = min_size.zip_map(max_size, |min, max| match (min, max) {
124 (Some(min), Some(max)) if max <= min => Some(min),
125 _ => None,
126 });
127 let styled_based_known_dimensions = known_dimensions.or(min_max_definite_size).or(clamped_style_size);
128
129 // Short-circuit layout if the container's size is fully determined by the container's size and the run mode
130 // is ComputeSize (and thus the container's size is all that we're interested in)
131 if run_mode == RunMode::ComputeSize {
132 if let Size { width: Some(width), height: Some(height) } = styled_based_known_dimensions {
133 return Size { width, height }.into();
134 }
135 }
136
137
138
139
140 let in_flow_children = tree.children(node)
141 .map(|child| (child, tree.style(*child)))
142 .filter(|(_, style)| style.position != Position::Absolute)
143 .filter(|(_, style)| style.display != Display::None)
144 .enumerate()
145 .map(|(index, (child, child_style))| {
146
147 let child_min_size = child_style.min_size.maybe_resolve(parent_size);
148 let max_size = style.max_size.maybe_resolve(parent_size);
149 let clamped_style_size =
150 style.size.maybe_resolve(parent_size).maybe_clamp(min_size, max_size);
151
152 // If both min and max in a given axis are set and max <= min then this determines the size in that axis
153 let min_max_definite_size = min_size.zip_map(max_size, |min, max| match (min, max) {
154 (Some(min), Some(max)) if max <= min => Some(min),
155 _ => None,
156 });
157 let styled_based_known_dimensions = min_max_definite_size.or(clamped_style_size);
158
159 let content_size = if child_style.size.width == Dimension::Auto || child_style.size.height == Dimension::Auto {
160 tree.measure_child_size(node, known_dimensions, parent_size, available_space, SizingMode::InherentSize, Line::NONE)
161 } else {
162 Size::zero()
163 };
164
165 ChildNode {
166 node,
167 is_in_flow: child_style.position != Position::Absolute
168 index,
169 style: child_style.clone(),
170 }
171 })
172 .collect();
173
174
175 // Sum of all non-flexible space and size on the main-axis of the node.
176 let mut main_non_flex = 0.0f32;
177
178 // Sum of all space and size flex factors on the main-axis of the node.
179 let mut main_flex_sum = 0.0;
180
181 // Sum of all child nodes on the main-axis.
182 let mut main_sum = 0.0f32;
183
184 // Maximum of all child nodes on the cross-axis.
185 let mut cross_max = 0.0f32;
186
187 // List of child nodes for the current node.
188 let mut children = SmallVec::<[ChildNode<N>; 3]>::new();
189
190 // List of stretch nodes for the current node.
191 // A stretch node is any flexible space/size. e.g. main_before, main, and main_after are separate stretch nodes
192 let mut stretch_nodes = SmallVec::<[StretchNode<N>; 3]>::new();
193
194 // Parent overrides for child auto space.
195 let node_child_main_before = node.child_main_before(store, layout_type);
196 let node_child_main_after = node.child_main_after(store, layout_type);
197 let node_child_cross_before = node.child_cross_before(store, layout_type);
198 let node_child_cross_after = node.child_cross_after(store, layout_type);
199 let node_child_main_between = node.main_between(store, layout_type);
200
201 // Determine index of first and last parent-directed child nodes.
202 let mut iter = node
203 .children(tree)
204 .enumerate()
205 .filter(|(_, child)| child.position_type(store).unwrap_or_default() == PositionType::ParentDirected);
206
207 let first = iter.next().map(|(index, _)| index);
208 let last = iter.last().map_or(first, |(index, _)| Some(index));
209
210 let num_children = node.children(tree).count();
211
212 // Compute non-flexible children.
213 for (index, child) in node.children(tree).enumerate() {
214 let child_position_type = child.position_type(store).unwrap_or_default();
215
216 let mut child_main_before = child.main_before(store, layout_type);
217 let child_main = child.main(store, layout_type);
218 let mut child_main_after = child.main_after(store, layout_type);
219
220 let mut child_cross_before = child.cross_before(store, layout_type);
221 let child_cross = child.cross(store, layout_type);
222 let mut child_cross_after = child.cross_after(store, layout_type);
223
224 // Apply parent overrides to auto child space.
225 if child_main_before == Units::Auto {
226 if first == Some(index) || child_position_type == PositionType::SelfDirected {
227 child_main_before = node_child_main_before;
228 } else {
229 child_main_before = node_child_main_between;
230 }
231 }
232
233 if child_main_after == Units::Auto && (last == Some(index) || child_position_type == PositionType::SelfDirected)
234 {
235 child_main_after = node_child_main_after;
236 }
237
238 if child_cross_before == Units::Auto {
239 child_cross_before = node_child_cross_before;
240 }
241
242 if child_cross_after == Units::Auto {
243 child_cross_after = node_child_cross_after;
244 }
245
246 // Sum of flex factors on the main and cross axes of the child node.
247 let mut child_main_flex_sum = 0.0;
248 let mut child_cross_flex_sum = 0.0;
249
250 let mut computed_child_main_before = 0.0;
251 let mut computed_child_main = 0.0;
252 let mut computed_child_main_after = 0.0;
253
254 let mut computed_child_cross_before = 0.0;
255 let mut computed_child_cross = 0.0;
256 let mut computed_child_cross_after = 0.0;
257
258 match child_cross_before {
259 Pixels(val) => {
260 computed_child_cross_before = val;
261 }
262
263 Percentage(val) => {
264 computed_child_cross_before = (parent_cross * (val / 100.0)).round();
265 }
266
267 Stretch(factor) => {
268 child_cross_flex_sum += factor;
269 }
270
271 _ => {}
272 }
273
274 let child_min_cross_before = child.min_cross_before(store, layout_type).to_px(parent_cross, DEFAULT_MIN);
275 let child_max_cross_before = child.max_cross_before(store, layout_type).to_px(parent_cross, DEFAULT_MAX);
276
277 computed_child_cross_before = computed_child_cross_before.clamp(child_min_cross_before, child_max_cross_before);
278
279 match child_cross_after {
280 Pixels(val) => {
281 computed_child_cross_after = val;
282 }
283
284 Percentage(val) => {
285 computed_child_cross_after = (parent_cross * (val / 100.0)).round();
286 }
287
288 Stretch(factor) => {
289 child_cross_flex_sum += factor;
290 }
291
292 _ => {}
293 }
294
295 let child_min_cross_after = child.min_cross_after(store, layout_type).to_px(parent_cross, DEFAULT_MIN);
296 let child_max_cross_after = child.max_cross_after(store, layout_type).to_px(parent_cross, DEFAULT_MAX);
297
298 computed_child_cross_after = computed_child_cross_after.clamp(child_min_cross_after, child_max_cross_after);
299
300 match child_cross {
301 Pixels(val) => {
302 computed_child_cross = val;
303 }
304
305 Percentage(val) => {
306 computed_child_cross = (parent_cross * (val / 100.0)).round();
307 }
308
309 Stretch(factor) => {
310 child_cross_flex_sum += factor;
311 }
312
313 _ => {}
314 }
315
316 let child_min_cross = child.min_cross(store, layout_type).to_px(parent_cross, DEFAULT_MIN);
317 let child_max_cross = child.max_cross(store, layout_type).to_px(parent_cross, DEFAULT_MAX);
318
319 computed_child_cross = computed_child_cross.clamp(child_min_cross, child_max_cross);
320
321 let child_min_main_before = child.min_main_before(store, layout_type).to_px(parent_main, DEFAULT_MIN);
322 let child_max_main_before = child.max_main_before(store, layout_type).to_px(parent_main, DEFAULT_MAX);
323
324 match child_main_before {
325 Pixels(val) => {
326 computed_child_main_before = val;
327 }
328
329 Percentage(val) => {
330 computed_child_main_before = (parent_main * (val / 100.0)).round();
331 }
332
333 Stretch(factor) => {
334 child_main_flex_sum += factor;
335
336 stretch_nodes.push(StretchNode {
337 node: child,
338 index,
339 factor,
340 min: child_min_main_before,
341 max: child_max_main_before,
342 axis: Axis::MainBefore,
343 });
344 }
345
346 _ => {}
347 }
348
349 computed_child_main_before = computed_child_main_before.clamp(child_min_main_before, child_max_main_before);
350
351 let child_min_main_after = child.min_main_after(store, layout_type).to_px(parent_main, DEFAULT_MIN);
352 let child_max_main_after = child.max_main_after(store, layout_type).to_px(parent_main, DEFAULT_MAX);
353
354 match child_main_after {
355 Pixels(val) => {
356 computed_child_main_after = val;
357 }
358
359 Percentage(val) => {
360 computed_child_main_after = (parent_main * (val / 100.0)).round();
361 }
362
363 Stretch(factor) => {
364 child_main_flex_sum += factor;
365
366 stretch_nodes.push(StretchNode {
367 node: child,
368 index,
369 factor,
370 min: child_min_main_after,
371 max: child_max_main_after,
372 axis: Axis::MainAfter,
373 });
374 }
375
376 _ => {}
377 }
378
379 computed_child_main_after = computed_child_main_after.clamp(child_min_main_after, child_max_main_after);
380
381 // Total computed size on the cross-axis of the child.
382 let mut child_cross_non_flex = computed_child_cross_before + computed_child_cross + computed_child_cross_after;
383
384 match child_main {
385 Stretch(factor) => {
386 child_main_flex_sum += factor;
387
388 stretch_nodes.push(StretchNode {
389 node: child,
390 index,
391 factor,
392 min: DEFAULT_MIN,
393 max: DEFAULT_MAX,
394 axis: Axis::Main,
395 });
396 }
397
398 _ => {
399 let child_size = layout(child, layout_type, parent_main, computed_child_cross, cache, tree, store);
400
401 computed_child_main = child_size.main;
402 computed_child_cross = child_size.cross;
403
404 if child_cross == Units::Auto {
405 child_cross_non_flex += computed_child_cross;
406 }
407 }
408 }
409
410 // Total computed size on the main-axis of the child.
411 let child_main_non_flex = computed_child_main_before + computed_child_main + computed_child_main_after;
412
413 if child_position_type == PositionType::ParentDirected {
414 main_non_flex += child_main_non_flex;
415 main_flex_sum += child_main_flex_sum;
416
417 main_sum += child_main_non_flex;
418 } else {
419 main_sum = main_sum.max(child_main_non_flex);
420 }
421
422 cross_max = cross_max.max(child_cross_non_flex);
423
424 children.push(ChildNode {
425 node: child,
426 main_flex_sum: child_main_flex_sum,
427 main_non_flex: child_main_non_flex,
428 main_remainder: 0.0,
429 cross_flex_sum: child_cross_flex_sum,
430 cross_non_flex: child_cross_non_flex,
431 cross: computed_child_cross,
432 cross_remainder: 0.0,
433 main_before: computed_child_main_before,
434 main_after: computed_child_main_after,
435 cross_before: computed_child_cross_before,
436 cross_after: computed_child_cross_after,
437 });
438 }
439
440 // Calculate free space on the main-axis for the node.
441 let free_main_space = (parent_main.max(main_sum) - main_non_flex).max(0.0);
442 let mut remainder: f32 = 0.0;
443 let main_px_per_flex = free_main_space / main_flex_sum;
444
445 // Compute flexible space and size on the main axis.
446 for item in stretch_nodes.iter() {
447 let child_position_type = item.node.position_type(store).unwrap_or_default();
448
449 let actual_main = if child_position_type == PositionType::SelfDirected {
450 let child_main_free_space = (parent_main.max(main_sum) - children[item.index].main_non_flex).max(0.0);
451 let px_per_flex = child_main_free_space / children[item.index].main_flex_sum;
452 let desired_main = item.factor * px_per_flex + children[item.index].main_remainder;
453 let actual_main = desired_main.round();
454 children[item.index].main_remainder = desired_main - actual_main;
455 actual_main
456 } else {
457 let desired_main = item.factor * main_px_per_flex + remainder;
458 let actual_main = desired_main.round();
459 remainder = desired_main - actual_main;
460 actual_main
461 };
462
463 let computed_child_cross = children[item.index].cross;
464
465 match item.axis {
466 Axis::MainBefore => {
467 children[item.index].main_before = actual_main;
468 }
469
470 Axis::MainAfter => {
471 children[item.index].main_after = actual_main;
472 }
473
474 Axis::Main => {
475 let child_size = layout(item.node, layout_type, actual_main, computed_child_cross, cache, tree, store);
476
477 children[item.index].cross_non_flex += child_size.cross;
478 cross_max = cross_max.max(children[item.index].cross_non_flex);
479 }
480 }
481 }
482
483 // Compute flexible space and size on the cross-axis.
484 // This needs to be done after computing the main-axis because layout computation for stretch children may cause
485 // the cross space to change due to content size.
486 for child in children.iter_mut() {
487 let child_cross_free_space = parent_cross.max(cross_max) - child.cross_non_flex;
488 let cross_px_per_flex = child_cross_free_space / child.cross_flex_sum;
489
490 let child_cross_before = child.node.cross_before(store, layout_type);
491 let child_cross = child.node.cross(store, layout_type);
492 let child_cross_after = child.node.cross_after(store, layout_type);
493
494 if let Stretch(factor) = child_cross_before {
495 let desired_cross = factor * cross_px_per_flex + child.cross_remainder;
496 let actual_cross = desired_cross.round();
497 child.cross_remainder = desired_cross - actual_cross;
498 child.cross_before = actual_cross;
499 }
500
501 if let Stretch(factor) = child_cross {
502 let desired_cross = factor * cross_px_per_flex + child.cross_remainder;
503 let actual_cross = desired_cross.round();
504 child.cross_remainder = desired_cross - actual_cross;
505
506 // if cross == Units::Auto && num_children != 0 {
507 let size = layout(child.node, layout_type, parent_main, actual_cross, cache, tree, store);
508 // println!("size: {:?}", size);
509 // } else {
510 // At this stage stretch nodes on the cross-axis can only be the determined size so we can set it directly
511 // in the cache without needing to call layout again.
512 // match layout_type {
513 // LayoutType::Row => {
514 // cache.set_height(child.node.key(), actual_cross);
515 // }
516
517 // LayoutType::Column => {
518 // cache.set_width(child.node.key(), actual_cross);
519 // }
520 // }
521 // }
522 }
523
524 if let Stretch(factor) = child_cross_after {
525 let desired_cross = factor * cross_px_per_flex + child.cross_remainder;
526 let actual_cross = desired_cross.round();
527 child.cross_remainder = desired_cross - actual_cross;
528 child.cross_after = actual_cross;
529 }
530 }
531
532 // Position children.
533 let mut main_pos = 0.0;
534 for child in children.iter() {
535 let child_position_type = child.node.position_type(store).unwrap_or_default();
536
537 match child_position_type {
538 PositionType::SelfDirected => match layout_type {
539 LayoutType::Row => {
540 cache.set_posx(child.node.key(), child.main_before);
541 cache.set_posy(child.node.key(), child.cross_before);
542 }
543
544 LayoutType::Column => {
545 cache.set_posy(child.node.key(), child.main_before);
546 cache.set_posx(child.node.key(), child.cross_before);
547 }
548 },
549
550 PositionType::ParentDirected => {
551 main_pos += child.main_before;
552
553 match layout_type {
554 LayoutType::Row => {
555 cache.set_posx(child.node.key(), main_pos);
556 cache.set_posy(child.node.key(), child.cross_before);
557 let child_width = cache.width(child.node.key());
558 main_pos += child_width;
559 }
560
561 LayoutType::Column => {
562 cache.set_posy(child.node.key(), main_pos);
563 cache.set_posx(child.node.key(), child.cross_before);
564 let child_height = cache.height(child.node.key());
565 main_pos += child_height;
566 }
567 }
568
569 main_pos += child.main_after;
570 }
571 };
572 }
573
574 // 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
575 // TODO: Make it so a node can only have content size if it has no children?
576 // TODO: Potentially split and move this to before stretch calculations.
577 if num_children != 0 {
578 if parent_layout_type == layout_type {
579 if let Auto = main {
580 computed_main = main_sum;
581 }
582
583 if let Auto = cross {
584 computed_cross = cross_max;
585 }
586 } else {
587 if let Auto = main {
588 computed_main = cross_max;
589 }
590
591 if let Auto = cross {
592 computed_cross = main_sum;
593 }
594 }
595 }
596
597 // Set the computed size of the node in the cache.
598 match parent_layout_type {
599 LayoutType::Row => {
600 cache.set_width(node.key(), computed_main);
601 cache.set_height(node.key(), computed_cross);
602 }
603
604 LayoutType::Column => {
605 cache.set_height(node.key(), computed_main);
606 cache.set_width(node.key(), computed_cross);
607 }
608 }
609
610 // Return the computed size, propagating it back up the tree.
611 Size { main: computed_main, cross: computed_cross }.into()
612
613}