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