1use cranpose_ui_graphics::whole;
2use cranpose_ui_layout::{
3 Axis, Constraints, FlexParentData, MeasurePolicy, MeasureResult, MeasureScope, ParentData,
4 Placement, bias_offset, round_to_px,
5};
6use smallvec::SmallVec;
7
8use crate::layout::core::{
9 Alignment, Arrangement, HorizontalAlignment, LinearArrangement, Measurable, VerticalAlignment,
10};
11
12#[derive(Clone, Debug, PartialEq)]
14pub struct BoxMeasurePolicy {
15 pub content_alignment: Alignment,
17 pub propagate_min_constraints: bool,
20 pub layout_direction: crate::LayoutDirection,
22}
23
24impl BoxMeasurePolicy {
25 pub fn new(content_alignment: Alignment, propagate_min_constraints: bool) -> Self {
28 Self {
29 content_alignment,
30 propagate_min_constraints,
31 layout_direction: crate::LayoutDirection::Ltr,
32 }
33 }
34}
35
36impl MeasurePolicy for BoxMeasurePolicy {
37 fn measure(
38 &self,
39 scope: &dyn MeasureScope,
40 measurables: &[Box<dyn Measurable>],
41 constraints: Constraints,
42 ) -> MeasureResult {
43 let mut placements = Vec::new();
44 let measurement = self.measure_into(scope, measurables, constraints, &mut placements);
45 MeasureResult::new(measurement, placements)
46 }
47
48 fn measure_hold(
51 &self,
52 measurables: &[Box<dyn Measurable>],
53 constraints: Constraints,
54 size: crate::modifier::Size,
55 ) -> Option<cranpose_ui_layout::ConstraintsHold> {
56 if measurables.is_empty() {
57 return empty_layout_hold(constraints, size);
58 }
59 let mut width = cranpose_ui_layout::BoundRange::ANY;
60 let mut height = cranpose_ui_layout::BoundRange::ANY;
61 let mut held = cranpose_ui_layout::ConstraintsHold::sized(size.width, size.height);
62 let mut largest = crate::modifier::Size::default();
63 for measurable in measurables {
64 let (child, hold) = measurable.measured_hold()?;
65 if self.propagate_min_constraints {
66 held = held.intersect(hold)?;
67 } else {
68 width = width.intersect(hold.width.max)?;
69 height = height.intersect(hold.height.max)?;
70 }
71 largest.width = largest.width.max(child.width);
72 largest.height = largest.height.max(child.height);
73 }
74 if size != largest {
76 return None;
77 }
78 held.width.max = held.width.max.intersect(width)?;
79 held.height.max = held.height.max.intersect(height)?;
80 Some(held)
81 }
82
83 fn measure_into(
84 &self,
85 scope: &dyn MeasureScope,
86 measurables: &[Box<dyn Measurable>],
87 constraints: Constraints,
88 placements: &mut Vec<Placement>,
89 ) -> cranpose_ui_layout::Measurement {
90 placements.clear();
91 let child_constraints = if self.propagate_min_constraints {
92 constraints
93 } else {
94 Constraints {
95 min_width: 0.0,
96 max_width: constraints.max_width,
97 min_height: 0.0,
98 max_height: constraints.max_height,
99 }
100 };
101
102 let mut max_width = 0.0_f32;
103 let mut max_height = 0.0_f32;
104 let mut placeables: SmallVec<[(cranpose_ui_layout::Placeable, Alignment); 8]> =
105 SmallVec::new();
106
107 for measurable in measurables {
108 let placeable = measurable.measure(child_constraints);
109 max_width = max_width.max(placeable.width());
110 max_height = max_height.max(placeable.height());
111 let alignment = measurable
112 .parent_data()
113 .box_alignment
114 .unwrap_or(self.content_alignment);
115 placeables.push((placeable, alignment));
116 }
117
118 let width = max_width.clamp(constraints.min_width, constraints.max_width);
119 let height = max_height.clamp(constraints.min_height, constraints.max_height);
120
121 placements.reserve(placeables.len());
122 for (placeable, alignment) in &placeables {
125 let child_width = placeable.width();
126 let child_height = placeable.height();
127
128 let x = alignment
129 .horizontal
130 .align(width, child_width, scope.density());
131 let x = self.layout_direction.place_x(x, width, child_width);
132 let y = alignment
133 .vertical
134 .align(height, child_height, scope.density());
135
136 placeable.place(x, y);
137 placements.push(Placement::new(placeable.node_id(), x, y, 0));
138 }
139
140 crate::modifier::Size { width, height }.into()
141 }
142
143 fn min_intrinsic_width(&self, measurables: &[Box<dyn Measurable>], height: f32) -> f32 {
144 measurables
145 .iter()
146 .map(|m| m.min_intrinsic_width(height))
147 .fold(0.0, f32::max)
148 }
149
150 fn max_intrinsic_width(&self, measurables: &[Box<dyn Measurable>], height: f32) -> f32 {
151 measurables
152 .iter()
153 .map(|m| m.max_intrinsic_width(height))
154 .fold(0.0, f32::max)
155 }
156
157 fn min_intrinsic_height(&self, measurables: &[Box<dyn Measurable>], width: f32) -> f32 {
158 measurables
159 .iter()
160 .map(|m| m.min_intrinsic_height(width))
161 .fold(0.0, f32::max)
162 }
163
164 fn max_intrinsic_height(&self, measurables: &[Box<dyn Measurable>], width: f32) -> f32 {
165 measurables
166 .iter()
167 .map(|m| m.max_intrinsic_height(width))
168 .fold(0.0, f32::max)
169 }
170}
171
172#[derive(Clone, Debug, PartialEq)]
210pub struct FlexMeasurePolicy {
211 pub axis: Axis,
213 pub main_axis_arrangement: LinearArrangement,
215 pub cross_axis_alignment: CrossAxisAlignment,
217 pub density: f32,
220 pub layout_direction: crate::LayoutDirection,
222}
223
224pub(crate) struct WeightShares {
228 density: f32,
229 unit_px: f32,
230 remainder_px: f32,
231}
232
233impl WeightShares {
234 pub(crate) fn new(
235 remaining: f32,
236 weights: impl Iterator<Item = f32> + Clone,
237 density: f32,
238 ) -> Self {
239 let density = if density > 0.0 && density.is_finite() {
240 density
241 } else {
242 1.0
243 };
244 let total_weight: f32 = weights.clone().sum();
245 let remaining_px = whole::round(remaining * density);
246 let unit_px = if total_weight > 0.0 {
247 remaining_px / total_weight
248 } else {
249 0.0
250 };
251 let rounded_px: f32 = weights
252 .map(|weight| whole::floor(unit_px * weight + 0.5))
253 .sum();
254 Self {
255 density,
256 unit_px,
257 remainder_px: remaining_px - rounded_px,
258 }
259 }
260
261 pub(crate) fn next_share(&mut self, weight: f32) -> f32 {
263 let step = if self.remainder_px > 0.0 {
264 1.0
265 } else if self.remainder_px < 0.0 {
266 -1.0
267 } else {
268 0.0
269 };
270 self.remainder_px -= step;
271 (whole::floor(self.unit_px * weight + 0.5) + step).max(0.0) / self.density
272 }
273}
274
275#[derive(Clone, Copy, Debug, PartialEq)]
278pub enum CrossAxisAlignment {
279 Start,
281 Center,
283 End,
285}
286
287impl CrossAxisAlignment {
288 fn align(&self, available: f32, child: f32, density: f32) -> f32 {
290 let bias = match self {
291 CrossAxisAlignment::Start => -1.0,
292 CrossAxisAlignment::Center => 0.0,
293 CrossAxisAlignment::End => 1.0,
294 };
295 bias_offset(bias, available, child, density)
296 }
297}
298
299impl From<HorizontalAlignment> for CrossAxisAlignment {
300 fn from(alignment: HorizontalAlignment) -> Self {
301 match alignment {
302 HorizontalAlignment::Start => CrossAxisAlignment::Start,
303 HorizontalAlignment::CenterHorizontally => CrossAxisAlignment::Center,
304 HorizontalAlignment::End => CrossAxisAlignment::End,
305 }
306 }
307}
308
309impl From<VerticalAlignment> for CrossAxisAlignment {
310 fn from(alignment: VerticalAlignment) -> Self {
311 match alignment {
312 VerticalAlignment::Top => CrossAxisAlignment::Start,
313 VerticalAlignment::CenterVertically => CrossAxisAlignment::Center,
314 VerticalAlignment::Bottom => CrossAxisAlignment::End,
315 }
316 }
317}
318
319impl FlexMeasurePolicy {
320 pub fn new(
322 axis: Axis,
323 main_axis_arrangement: LinearArrangement,
324 cross_axis_alignment: CrossAxisAlignment,
325 density: f32,
326 ) -> Self {
327 Self {
328 axis,
329 main_axis_arrangement,
330 cross_axis_alignment,
331 density,
332 layout_direction: crate::LayoutDirection::Ltr,
333 }
334 }
335
336 pub fn row(
338 horizontal_arrangement: LinearArrangement,
339 vertical_alignment: VerticalAlignment,
340 density: f32,
341 ) -> Self {
342 Self::new(
343 Axis::Horizontal,
344 horizontal_arrangement,
345 vertical_alignment.into(),
346 density,
347 )
348 }
349
350 pub fn column(
352 vertical_arrangement: LinearArrangement,
353 horizontal_alignment: HorizontalAlignment,
354 density: f32,
355 ) -> Self {
356 Self::new(
357 Axis::Vertical,
358 vertical_arrangement,
359 horizontal_alignment.into(),
360 density,
361 )
362 }
363
364 fn get_axis_constraints(&self, constraints: Constraints) -> (f32, f32, f32, f32) {
365 match self.axis {
366 Axis::Horizontal => (
367 constraints.min_width,
368 constraints.max_width,
369 constraints.min_height,
370 constraints.max_height,
371 ),
372 Axis::Vertical => (
373 constraints.min_height,
374 constraints.max_height,
375 constraints.min_width,
376 constraints.max_width,
377 ),
378 }
379 }
380
381 fn make_constraints(
382 &self,
383 min_main: f32,
384 max_main: f32,
385 min_cross: f32,
386 max_cross: f32,
387 ) -> Constraints {
388 match self.axis {
389 Axis::Horizontal => Constraints {
390 min_width: min_main,
391 max_width: max_main,
392 min_height: min_cross,
393 max_height: max_cross,
394 },
395 Axis::Vertical => Constraints {
396 min_width: min_cross,
397 max_width: max_cross,
398 min_height: min_main,
399 max_height: max_main,
400 },
401 }
402 }
403
404 fn get_main_axis_size(&self, width: f32, height: f32) -> f32 {
405 match self.axis {
406 Axis::Horizontal => width,
407 Axis::Vertical => height,
408 }
409 }
410
411 fn get_cross_axis_size(&self, width: f32, height: f32) -> f32 {
412 match self.axis {
413 Axis::Horizontal => height,
414 Axis::Vertical => width,
415 }
416 }
417
418 fn measure_fixed_children(
424 &self,
425 measurables: &[Box<dyn Measurable>],
426 fixed_children: &[usize],
427 (max_main, max_cross): (f32, f32),
428 spacing: f32,
429 placeables: &mut [Option<cranpose_ui_layout::Placeable>],
430 ) -> (f32, f32) {
431 let main_axis_bounded = max_main.is_finite();
432 let mut fixed_space = 0.0_f32;
433 let mut max_cross_size = 0.0_f32;
434 for &idx in fixed_children {
435 let available_main = if main_axis_bounded {
436 (max_main - fixed_space).max(0.0)
437 } else {
438 max_main
439 };
440 let placeable = measurables[idx].measure(self.make_constraints(
441 0.0,
442 available_main,
443 0.0,
444 max_cross,
445 ));
446 let main_size = self.get_main_axis_size(placeable.width(), placeable.height());
447 let spacing_after = if main_axis_bounded {
448 spacing.min(available_main - main_size).max(0.0)
449 } else {
450 spacing
451 };
452 fixed_space += main_size + spacing_after;
453 max_cross_size =
454 max_cross_size.max(self.get_cross_axis_size(placeable.width(), placeable.height()));
455 placeables[idx] = Some(placeable);
456 }
457 (fixed_space, max_cross_size)
458 }
459
460 fn intrinsic_main_size(
464 &self,
465 measurables: &[Box<dyn Measurable>],
466 main_size: impl Fn(&dyn Measurable) -> f32,
467 ) -> f32 {
468 if measurables.is_empty() {
469 return 0.0;
470 }
471 let mut fixed_space = 0.0_f32;
472 let mut weight_unit = 0.0_f32;
473 let mut total_weight = 0.0_f32;
474 for measurable in measurables {
475 let size = main_size(measurable.as_ref());
476 match child_weight(measurable.as_ref()) {
477 Some(weight) => {
478 total_weight += weight;
479 weight_unit = weight_unit.max(round_to_px(size / weight, self.density));
480 }
481 None => fixed_space += size,
482 }
483 }
484 round_to_px(weight_unit * total_weight, self.density)
485 + fixed_space
486 + self.get_spacing() * (measurables.len() - 1) as f32
487 }
488
489 fn intrinsic_cross_size(
495 &self,
496 measurables: &[Box<dyn Measurable>],
497 main_available: f32,
498 main_size: impl Fn(&dyn Measurable, f32) -> f32,
499 cross_size: impl Fn(&dyn Measurable, f32) -> f32,
500 ) -> f32 {
501 if measurables.is_empty() {
502 return 0.0;
503 }
504 let mut fixed_space =
505 (self.get_spacing() * (measurables.len() - 1) as f32).min(main_available);
506 let mut cross = 0.0_f32;
507 let mut total_weight = 0.0_f32;
508 for measurable in measurables {
509 let measurable = measurable.as_ref();
510 if let Some(weight) = child_weight(measurable) {
511 total_weight += weight;
512 continue;
513 }
514 let space =
515 main_size(measurable, f32::INFINITY).min((main_available - fixed_space).max(0.0));
516 fixed_space += space;
517 cross = cross.max(cross_size(measurable, space));
518 }
519 if total_weight == 0.0 {
520 return cross;
521 }
522 let weight_unit = if main_available.is_finite() {
523 round_to_px(
524 (main_available - fixed_space).max(0.0) / total_weight,
525 self.density,
526 )
527 } else {
528 f32::INFINITY
529 };
530 for measurable in measurables {
531 let measurable = measurable.as_ref();
532 if let Some(weight) = child_weight(measurable) {
533 cross = cross.max(cross_size(
534 measurable,
535 round_to_px(weight_unit * weight, self.density),
536 ));
537 }
538 }
539 cross
540 }
541
542 fn get_spacing(&self) -> f32 {
543 self.main_axis_arrangement.spacing(self.density)
544 }
545
546 fn measure_weighted_children(
547 &self,
548 measurables: &[Box<dyn Measurable>],
549 weighted_children: &[(usize, FlexParentData)],
550 (max_main, max_cross): (f32, f32),
551 (fixed_space, spacing): (f32, f32),
552 placeables: &mut [Option<cranpose_ui_layout::Placeable>],
553 ) -> f32 {
554 let remaining_main =
555 (max_main - fixed_space - spacing * weighted_children.len().saturating_sub(1) as f32)
556 .max(0.0);
557 let mut shares = WeightShares::new(
558 remaining_main,
559 weighted_children.iter().map(|(_, data)| data.weight),
560 self.density,
561 );
562 let mut max_cross_size = 0.0_f32;
563 for &(idx, data) in weighted_children {
564 let constraints = if max_main.is_finite() {
565 let allocated = shares.next_share(data.weight);
566 let minimum = if data.fill { allocated } else { 0.0 };
567 self.make_constraints(minimum, allocated, 0.0, max_cross)
568 } else {
569 self.make_constraints(0.0, max_main, 0.0, max_cross)
570 };
571 let placeable = measurables[idx].measure(constraints);
572 max_cross_size =
573 max_cross_size.max(self.get_cross_axis_size(placeable.width(), placeable.height()));
574 placeables[idx] = Some(placeable);
575 }
576 max_cross_size
577 }
578
579 fn measured_extents(
580 &self,
581 placeables: &[cranpose_ui_layout::Placeable],
582 parent_data: &[cranpose_ui_layout::ParentData],
583 ) -> (f32, f32, f32) {
584 let mut main = 0.0;
585 let mut before = 0.0_f32;
586 let mut after = 0.0_f32;
587 for (placeable, data) in placeables.iter().zip(parent_data) {
588 main += self.get_main_axis_size(placeable.width(), placeable.height());
589 if self.axis == Axis::Horizontal
590 && data.row_baseline
591 && let Some(baseline) = placeable.alignment_lines().first_baseline()
592 {
593 before = before.max(baseline);
594 after = after.max(placeable.height() - baseline);
595 }
596 }
597 (main, before, after)
598 }
599
600 fn cross_axis_position(
601 &self,
602 placeable: &cranpose_ui_layout::Placeable,
603 parent_data: &ParentData,
604 container_cross: f32,
605 before_baseline: f32,
606 ) -> f32 {
607 if self.axis == Axis::Horizontal && parent_data.row_baseline {
608 return placeable
609 .alignment_lines()
610 .first_baseline()
611 .map_or(0.0, |baseline| before_baseline - baseline);
612 }
613 let alignment = match self.axis {
614 Axis::Horizontal => parent_data
615 .row_alignment
616 .map_or(self.cross_axis_alignment, Into::into),
617 Axis::Vertical => parent_data
618 .column_alignment
619 .map_or(self.cross_axis_alignment, Into::into),
620 };
621 let child_cross = self.get_cross_axis_size(placeable.width(), placeable.height());
622 alignment.align(container_cross, child_cross, self.density)
623 }
624}
625
626fn child_main_range(
630 child_max: cranpose_ui_layout::BoundRange,
631 max_main: f32,
632 fixed_space: f32,
633 child_main: f32,
634 spacing: f32,
635) -> Option<cranpose_ui_layout::BoundRange> {
636 use cranpose_ui_layout::BoundRange;
637 if !max_main.is_finite() {
638 return child_max
639 .contains(f32::INFINITY)
640 .then_some(BoundRange::exactly(f32::INFINITY));
641 }
642 if (max_main - fixed_space).max(0.0) - child_main < spacing {
643 return None;
644 }
645 child_max
646 .outset(fixed_space)
647 .intersect(BoundRange::from(fixed_space + child_main + spacing))
648}
649
650fn child_weight(measurable: &dyn Measurable) -> Option<f32> {
652 measurable
653 .flex_parent_data()
654 .map(|data| data.weight)
655 .filter(|weight| *weight > 0.0)
656}
657
658impl MeasurePolicy for FlexMeasurePolicy {
659 fn measure(
660 &self,
661 scope: &dyn MeasureScope,
662 measurables: &[Box<dyn Measurable>],
663 constraints: Constraints,
664 ) -> MeasureResult {
665 let mut placements = Vec::new();
666 let measurement = self.measure_into(scope, measurables, constraints, &mut placements);
667 MeasureResult::new(measurement, placements)
668 }
669
670 fn measure_hold(
674 &self,
675 measurables: &[Box<dyn Measurable>],
676 constraints: Constraints,
677 size: crate::modifier::Size,
678 ) -> Option<cranpose_ui_layout::ConstraintsHold> {
679 use cranpose_ui_layout::{AxisHold, BoundRange, ConstraintsHold};
680 if measurables.is_empty() {
681 return empty_layout_hold(constraints, size);
682 }
683 if measurables
684 .iter()
685 .any(|measurable| measurable.parent_data().has_weight())
686 {
687 return None;
688 }
689 let (_, max_main, _, _) = self.get_axis_constraints(constraints);
690 let spacing = self.get_spacing();
691 let mut main = BoundRange::ANY;
692 let mut cross = BoundRange::ANY;
693 let mut fixed_space = 0.0_f32;
694 let mut total_main = 0.0_f32;
695 let mut cross_size = 0.0_f32;
696 for measurable in measurables {
697 let (child, hold) = measurable.measured_hold()?;
698 let (main_hold, cross_hold) = match self.axis {
699 Axis::Horizontal => (hold.width, hold.height),
700 Axis::Vertical => (hold.height, hold.width),
701 };
702 let child_main = self.get_main_axis_size(child.width, child.height);
703 main = main.intersect(child_main_range(
704 main_hold.max,
705 max_main,
706 fixed_space,
707 child_main,
708 spacing,
709 )?)?;
710 cross = cross.intersect(cross_hold.max)?;
711 fixed_space += child_main + spacing;
712 total_main += child_main;
713 cross_size = cross_size.max(self.get_cross_axis_size(child.width, child.height));
714 }
715 total_main += spacing * (measurables.len() - 1) as f32;
716 let size_main = self.get_main_axis_size(size.width, size.height);
717 let size_cross = self.get_cross_axis_size(size.width, size.height);
718 if size_main != total_main || size_cross != cross_size {
720 return None;
721 }
722 let main = AxisHold {
723 min: BoundRange::up_to(size_main),
724 max: main.intersect(BoundRange::from(size_main))?,
725 };
726 let cross = AxisHold {
727 min: BoundRange::up_to(size_cross),
728 max: cross.intersect(BoundRange::from(size_cross))?,
729 };
730 Some(match self.axis {
731 Axis::Horizontal => ConstraintsHold {
732 width: main,
733 height: cross,
734 },
735 Axis::Vertical => ConstraintsHold {
736 width: cross,
737 height: main,
738 },
739 })
740 }
741
742 fn measure_into(
743 &self,
744 _scope: &dyn MeasureScope,
745 measurables: &[Box<dyn Measurable>],
746 constraints: Constraints,
747 placements: &mut Vec<Placement>,
748 ) -> cranpose_ui_layout::Measurement {
749 placements.clear();
750 if measurables.is_empty() {
751 let (width, height) = constraints.constrain(0.0, 0.0);
752 return crate::modifier::Size { width, height }.into();
753 }
754
755 let (min_main, max_main, min_cross, max_cross) = self.get_axis_constraints(constraints);
756 let spacing = self.get_spacing();
757
758 let mut fixed_children: SmallVec<[usize; 8]> = SmallVec::new();
759 let parent_data: SmallVec<[ParentData; 8]> = measurables
760 .iter()
761 .map(|child| child.parent_data())
762 .collect();
763 let mut weighted_children: SmallVec<[(usize, FlexParentData); 8]> = SmallVec::new();
764
765 for (idx, data) in parent_data.iter().enumerate() {
766 if data.has_weight() {
767 weighted_children.push((idx, FlexParentData::new(data.weight, data.fill)));
768 } else {
769 fixed_children.push(idx);
770 }
771 }
772
773 let child_constraints = self.make_constraints(0.0, max_main, 0.0, max_cross);
774
775 let mut placeables: SmallVec<[Option<cranpose_ui_layout::Placeable>; 8]> = SmallVec::new();
776 placeables.resize_with(measurables.len(), || None);
777 let (fixed_space, mut max_cross_size) = self.measure_fixed_children(
778 measurables,
779 &fixed_children,
780 (max_main, max_cross),
781 spacing,
782 &mut placeables,
783 );
784
785 let num_children = measurables.len();
786 let total_spacing = if num_children > 1 {
787 spacing * (num_children - 1) as f32
788 } else {
789 0.0
790 };
791
792 max_cross_size = max_cross_size.max(self.measure_weighted_children(
793 measurables,
794 &weighted_children,
795 (max_main, max_cross),
796 (fixed_space, spacing),
797 &mut placeables,
798 ));
799
800 let placeables: SmallVec<[cranpose_ui_layout::Placeable; 8]> = placeables
801 .drain(..)
802 .enumerate()
803 .map(|(idx, placeable)| {
804 placeable.unwrap_or_else(|| measurables[idx].measure(child_constraints))
805 })
806 .collect();
807
808 let (total_main, before_baseline, after_baseline) =
809 self.measured_extents(&placeables, &parent_data);
810 let total_main = total_main + total_spacing;
811 max_cross_size = max_cross_size.max(before_baseline + after_baseline);
812
813 let container_main = total_main.clamp(min_main, max_main);
814 let container_cross = max_cross_size.clamp(min_cross, max_cross);
815
816 let child_main_sizes: SmallVec<[f32; 8]> = placeables
817 .iter()
818 .map(|p| self.get_main_axis_size(p.width(), p.height()))
819 .collect();
820
821 let mut main_positions: SmallVec<[f32; 8]> =
822 SmallVec::with_capacity(child_main_sizes.len());
823 main_positions.resize(child_main_sizes.len(), 0.0);
824
825 let arrangement = if total_main > container_main && !self.main_axis_arrangement.is_spaced()
826 {
827 LinearArrangement::Start
828 } else {
829 self.main_axis_arrangement
830 };
831 arrangement.arrange(
832 self.density,
833 container_main,
834 &child_main_sizes,
835 &mut main_positions,
836 );
837
838 placements.reserve(placeables.len());
839 for (idx, (placeable, &main_pos)) in placeables.iter().zip(&main_positions).enumerate() {
840 let cross_pos = self.cross_axis_position(
841 placeable,
842 &parent_data[idx],
843 container_cross,
844 before_baseline,
845 );
846
847 let (x, y) = match self.axis {
848 Axis::Horizontal => (main_pos, cross_pos),
849 Axis::Vertical => (cross_pos, main_pos),
850 };
851 let width = match self.axis {
852 Axis::Horizontal => container_main,
853 Axis::Vertical => container_cross,
854 };
855 let x = self.layout_direction.place_x(x, width, placeable.width());
856
857 placeable.place(x, y);
858 placements.push(Placement::new(placeable.node_id(), x, y, 0));
859 }
860
861 let (width, height) = match self.axis {
862 Axis::Horizontal => (container_main, container_cross),
863 Axis::Vertical => (container_cross, container_main),
864 };
865
866 crate::modifier::Size { width, height }.into()
867 }
868
869 fn min_intrinsic_width(&self, measurables: &[Box<dyn Measurable>], height: f32) -> f32 {
870 match self.axis {
871 Axis::Horizontal => {
872 self.intrinsic_main_size(measurables, |m| m.min_intrinsic_width(height))
873 }
874 Axis::Vertical => self.intrinsic_cross_size(
875 measurables,
876 height,
877 |m, width| m.max_intrinsic_height(width),
878 |m, main| m.min_intrinsic_width(main),
879 ),
880 }
881 }
882
883 fn max_intrinsic_width(&self, measurables: &[Box<dyn Measurable>], height: f32) -> f32 {
884 match self.axis {
885 Axis::Horizontal => {
886 self.intrinsic_main_size(measurables, |m| m.max_intrinsic_width(height))
887 }
888 Axis::Vertical => self.intrinsic_cross_size(
889 measurables,
890 height,
891 |m, width| m.max_intrinsic_height(width),
892 |m, main| m.max_intrinsic_width(main),
893 ),
894 }
895 }
896
897 fn min_intrinsic_height(&self, measurables: &[Box<dyn Measurable>], width: f32) -> f32 {
898 match self.axis {
899 Axis::Horizontal => self.intrinsic_cross_size(
900 measurables,
901 width,
902 |m, height| m.max_intrinsic_width(height),
903 |m, main| m.min_intrinsic_height(main),
904 ),
905 Axis::Vertical => {
906 self.intrinsic_main_size(measurables, |m| m.min_intrinsic_height(width))
907 }
908 }
909 }
910
911 fn max_intrinsic_height(&self, measurables: &[Box<dyn Measurable>], width: f32) -> f32 {
912 match self.axis {
913 Axis::Horizontal => self.intrinsic_cross_size(
914 measurables,
915 width,
916 |m, height| m.max_intrinsic_width(height),
917 |m, main| m.max_intrinsic_height(main),
918 ),
919 Axis::Vertical => {
920 self.intrinsic_main_size(measurables, |m| m.max_intrinsic_height(width))
921 }
922 }
923 }
924}
925
926#[derive(Clone, Copy, Debug, PartialEq, Eq)]
928pub enum FlowAxis {
929 Horizontal,
932 Vertical,
935}
936
937#[derive(Clone, Debug, PartialEq)]
951pub struct FlowMeasurePolicy {
952 pub axis: FlowAxis,
954 pub main_axis_spacing: f32,
956 pub cross_axis_spacing: f32,
958 pub layout_direction: crate::LayoutDirection,
960}
961
962impl FlowMeasurePolicy {
963 pub fn new(axis: FlowAxis, main_axis_spacing: f32, cross_axis_spacing: f32) -> Self {
966 Self {
967 axis,
968 main_axis_spacing: main_axis_spacing.max(0.0),
969 cross_axis_spacing: cross_axis_spacing.max(0.0),
970 layout_direction: crate::LayoutDirection::Ltr,
971 }
972 }
973
974 fn along(&self, width: f32, height: f32) -> (f32, f32) {
976 match self.axis {
977 FlowAxis::Horizontal => (width, height),
978 FlowAxis::Vertical => (height, width),
979 }
980 }
981
982 fn main_intrinsic(&self, measurable: &dyn Measurable, cross: f32, min: bool) -> f32 {
985 match (self.axis, min) {
986 (FlowAxis::Horizontal, true) => measurable.min_intrinsic_width(cross),
987 (FlowAxis::Horizontal, false) => measurable.max_intrinsic_width(cross),
988 (FlowAxis::Vertical, true) => measurable.min_intrinsic_height(cross),
989 (FlowAxis::Vertical, false) => measurable.max_intrinsic_height(cross),
990 }
991 }
992
993 fn cross_intrinsic(&self, measurable: &dyn Measurable, main: f32, min: bool) -> f32 {
995 match (self.axis, min) {
996 (FlowAxis::Horizontal, true) => measurable.min_intrinsic_height(main),
997 (FlowAxis::Horizontal, false) => measurable.max_intrinsic_height(main),
998 (FlowAxis::Vertical, true) => measurable.min_intrinsic_width(main),
999 (FlowAxis::Vertical, false) => measurable.max_intrinsic_width(main),
1000 }
1001 }
1002
1003 fn wrapped_cross(
1006 &self,
1007 measurables: &[Box<dyn Measurable>],
1008 available_main: f32,
1009 min: bool,
1010 ) -> f32 {
1011 let mut cursor = 0.0_f32;
1012 let mut line_start = 0.0_f32;
1013 let mut line_cross = 0.0_f32;
1014
1015 for measurable in measurables {
1016 let child_main = self.main_intrinsic(measurable.as_ref(), f32::INFINITY, false);
1017 let child_cross = self.cross_intrinsic(measurable.as_ref(), child_main, min);
1018
1019 if cursor > 0.0 && cursor + self.main_axis_spacing + child_main > available_main {
1020 line_start += line_cross + self.cross_axis_spacing;
1021 cursor = 0.0;
1022 line_cross = 0.0;
1023 }
1024 cursor += if cursor > 0.0 {
1025 self.main_axis_spacing + child_main
1026 } else {
1027 child_main
1028 };
1029 line_cross = line_cross.max(child_cross);
1030 }
1031
1032 line_start + line_cross
1033 }
1034
1035 fn unwrapped_main(&self, measurables: &[Box<dyn Measurable>], cross: f32) -> f32 {
1037 let total_spacing = self.main_axis_spacing * measurables.len().saturating_sub(1) as f32;
1038 measurables
1039 .iter()
1040 .map(|m| self.main_intrinsic(m.as_ref(), cross, false))
1041 .sum::<f32>()
1042 + total_spacing
1043 }
1044
1045 fn narrowest_main(&self, measurables: &[Box<dyn Measurable>], cross: f32) -> f32 {
1047 measurables
1048 .iter()
1049 .map(|m| self.main_intrinsic(m.as_ref(), cross, true))
1050 .fold(0.0, f32::max)
1051 }
1052}
1053
1054impl MeasurePolicy for FlowMeasurePolicy {
1055 fn measure(
1056 &self,
1057 scope: &dyn MeasureScope,
1058 measurables: &[Box<dyn Measurable>],
1059 constraints: Constraints,
1060 ) -> MeasureResult {
1061 let mut placements = Vec::new();
1062 let measurement = self.measure_into(scope, measurables, constraints, &mut placements);
1063 MeasureResult::new(measurement, placements)
1064 }
1065
1066 fn measure_into(
1067 &self,
1068 _scope: &dyn MeasureScope,
1069 measurables: &[Box<dyn Measurable>],
1070 constraints: Constraints,
1071 placements: &mut Vec<Placement>,
1072 ) -> cranpose_ui_layout::Measurement {
1073 placements.clear();
1074 if measurables.is_empty() {
1075 let (width, height) = constraints.constrain(0.0, 0.0);
1076 return crate::modifier::Size { width, height }.into();
1077 }
1078
1079 let child_constraints = Constraints {
1080 min_width: 0.0,
1081 max_width: constraints.max_width,
1082 min_height: 0.0,
1083 max_height: constraints.max_height,
1084 };
1085
1086 let placeables: SmallVec<[cranpose_ui_layout::Placeable; 8]> = measurables
1087 .iter()
1088 .map(|measurable| measurable.measure(child_constraints))
1089 .collect();
1090
1091 let (max_main, _) = self.along(constraints.max_width, constraints.max_height);
1092 let mut cursor = 0.0_f32;
1093 let mut line_start = 0.0_f32;
1094 let mut line_cross = 0.0_f32;
1095 let mut longest_line = 0.0_f32;
1096
1097 placements.reserve(placeables.len());
1098 for placeable in &placeables {
1099 let (child_main, child_cross) = self.along(placeable.width(), placeable.height());
1100
1101 if cursor > 0.0 && cursor + self.main_axis_spacing + child_main > max_main {
1102 longest_line = longest_line.max(cursor);
1103 line_start += line_cross + self.cross_axis_spacing;
1104 cursor = 0.0;
1105 line_cross = 0.0;
1106 }
1107
1108 let main = if cursor > 0.0 {
1109 cursor + self.main_axis_spacing
1110 } else {
1111 0.0
1112 };
1113 let (x, y) = self.along(main, line_start);
1114 if !self.layout_direction.is_rtl() {
1115 placeable.place(x, y);
1116 }
1117 placements.push(Placement::new(placeable.node_id(), x, y, 0));
1118
1119 cursor = main + child_main;
1120 line_cross = line_cross.max(child_cross);
1121 }
1122 longest_line = longest_line.max(cursor);
1123
1124 let (width, height) = self.along(longest_line, line_start + line_cross);
1125 let width = width.clamp(constraints.min_width, constraints.max_width);
1126 let height = height.clamp(constraints.min_height, constraints.max_height);
1127 if self.layout_direction.is_rtl() {
1128 for (placement, placeable) in placements.iter_mut().zip(&placeables) {
1129 placement.x = self
1130 .layout_direction
1131 .place_x(placement.x, width, placeable.width());
1132 placeable.place(placement.x, placement.y);
1133 }
1134 }
1135 crate::modifier::Size { width, height }.into()
1136 }
1137
1138 fn min_intrinsic_width(&self, measurables: &[Box<dyn Measurable>], height: f32) -> f32 {
1139 match self.axis {
1140 FlowAxis::Horizontal => self.narrowest_main(measurables, height),
1141 FlowAxis::Vertical => self.wrapped_cross(measurables, height, true),
1142 }
1143 }
1144
1145 fn max_intrinsic_width(&self, measurables: &[Box<dyn Measurable>], height: f32) -> f32 {
1146 match self.axis {
1147 FlowAxis::Horizontal => self.unwrapped_main(measurables, height),
1148 FlowAxis::Vertical => self.wrapped_cross(measurables, height, false),
1149 }
1150 }
1151
1152 fn min_intrinsic_height(&self, measurables: &[Box<dyn Measurable>], width: f32) -> f32 {
1153 match self.axis {
1154 FlowAxis::Horizontal => self.wrapped_cross(measurables, width, true),
1155 FlowAxis::Vertical => self.narrowest_main(measurables, width),
1156 }
1157 }
1158
1159 fn max_intrinsic_height(&self, measurables: &[Box<dyn Measurable>], width: f32) -> f32 {
1160 match self.axis {
1161 FlowAxis::Horizontal => self.wrapped_cross(measurables, width, false),
1162 FlowAxis::Vertical => self.unwrapped_main(measurables, width),
1163 }
1164 }
1165}
1166
1167#[derive(Clone, Debug, PartialEq)]
1170pub struct LeafMeasurePolicy {
1171 pub intrinsic_size: crate::modifier::Size,
1174}
1175
1176impl LeafMeasurePolicy {
1177 pub fn new(intrinsic_size: crate::modifier::Size) -> Self {
1179 Self { intrinsic_size }
1180 }
1181}
1182
1183impl MeasurePolicy for LeafMeasurePolicy {
1184 fn measure(
1185 &self,
1186 scope: &dyn MeasureScope,
1187 _measurables: &[Box<dyn Measurable>],
1188 constraints: Constraints,
1189 ) -> MeasureResult {
1190 let mut placements = Vec::new();
1191 let measurement = self.measure_into(scope, &[], constraints, &mut placements);
1192 MeasureResult::new(measurement, placements)
1193 }
1194
1195 fn measure_into(
1196 &self,
1197 _scope: &dyn MeasureScope,
1198 _measurables: &[Box<dyn Measurable>],
1199 constraints: Constraints,
1200 placements: &mut Vec<Placement>,
1201 ) -> cranpose_ui_layout::Measurement {
1202 placements.clear();
1203 let (width, height) =
1204 constraints.constrain(self.intrinsic_size.width, self.intrinsic_size.height);
1205
1206 crate::modifier::Size { width, height }.into()
1207 }
1208
1209 fn min_intrinsic_width(&self, _measurables: &[Box<dyn Measurable>], _height: f32) -> f32 {
1210 self.intrinsic_size.width
1211 }
1212
1213 fn max_intrinsic_width(&self, _measurables: &[Box<dyn Measurable>], _height: f32) -> f32 {
1214 self.intrinsic_size.width
1215 }
1216
1217 fn min_intrinsic_height(&self, _measurables: &[Box<dyn Measurable>], _width: f32) -> f32 {
1218 self.intrinsic_size.height
1219 }
1220
1221 fn max_intrinsic_height(&self, _measurables: &[Box<dyn Measurable>], _width: f32) -> f32 {
1222 self.intrinsic_size.height
1223 }
1224}
1225
1226#[derive(Clone, Debug, PartialEq)]
1232pub struct EmptyMeasurePolicy;
1233
1234impl EmptyMeasurePolicy {
1235 pub fn new() -> Self {
1237 Self
1238 }
1239}
1240
1241impl Default for EmptyMeasurePolicy {
1242 fn default() -> Self {
1243 Self::new()
1244 }
1245}
1246
1247impl MeasurePolicy for EmptyMeasurePolicy {
1248 fn measure(
1249 &self,
1250 scope: &dyn MeasureScope,
1251 _measurables: &[Box<dyn Measurable>],
1252 constraints: Constraints,
1253 ) -> MeasureResult {
1254 let mut placements = Vec::new();
1255 let measurement = self.measure_into(scope, &[], constraints, &mut placements);
1256 MeasureResult::new(measurement, placements)
1257 }
1258
1259 fn measure_into(
1260 &self,
1261 _scope: &dyn MeasureScope,
1262 _measurables: &[Box<dyn Measurable>],
1263 constraints: Constraints,
1264 placements: &mut Vec<Placement>,
1265 ) -> cranpose_ui_layout::Measurement {
1266 placements.clear();
1267 let (width, height) = constraints.constrain(0.0, 0.0);
1268
1269 crate::modifier::Size { width, height }.into()
1270 }
1271
1272 fn measure_hold(
1273 &self,
1274 _measurables: &[Box<dyn Measurable>],
1275 constraints: Constraints,
1276 size: crate::modifier::Size,
1277 ) -> Option<cranpose_ui_layout::ConstraintsHold> {
1278 empty_layout_hold(constraints, size)
1279 }
1280
1281 fn min_intrinsic_width(&self, _measurables: &[Box<dyn Measurable>], _height: f32) -> f32 {
1282 0.0
1283 }
1284
1285 fn max_intrinsic_width(&self, _measurables: &[Box<dyn Measurable>], _height: f32) -> f32 {
1286 0.0
1287 }
1288
1289 fn min_intrinsic_height(&self, _measurables: &[Box<dyn Measurable>], _width: f32) -> f32 {
1290 0.0
1291 }
1292
1293 fn max_intrinsic_height(&self, _measurables: &[Box<dyn Measurable>], _width: f32) -> f32 {
1294 0.0
1295 }
1296}
1297
1298#[cfg(test)]
1299#[path = "tests/policies_tests.rs"]
1300mod tests;
1301
1302fn empty_layout_hold(
1306 constraints: Constraints,
1307 size: crate::modifier::Size,
1308) -> Option<cranpose_ui_layout::ConstraintsHold> {
1309 (size.width == constraints.min_width && size.height == constraints.min_height)
1310 .then(|| cranpose_ui_layout::ConstraintsHold::at_min(size.width, size.height))
1311}