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