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