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_into(
43 &self,
44 scope: &dyn MeasureScope,
45 measurables: &[Box<dyn Measurable>],
46 constraints: Constraints,
47 placements: &mut Vec<Placement>,
48 ) -> cranpose_ui_layout::Measurement {
49 placements.clear();
50 let child_constraints = if self.propagate_min_constraints {
51 constraints
52 } else {
53 Constraints {
54 min_width: 0.0,
55 max_width: constraints.max_width,
56 min_height: 0.0,
57 max_height: constraints.max_height,
58 }
59 };
60
61 let mut max_width = 0.0_f32;
62 let mut max_height = 0.0_f32;
63 let mut placeables: SmallVec<[(cranpose_ui_layout::Placeable, Alignment); 8]> =
64 SmallVec::new();
65
66 for measurable in measurables {
67 let placeable = measurable.measure(child_constraints);
68 max_width = max_width.max(placeable.width());
69 max_height = max_height.max(placeable.height());
70 let alignment = measurable
71 .parent_data()
72 .box_alignment
73 .unwrap_or(self.content_alignment);
74 placeables.push((placeable, alignment));
75 }
76
77 let width = max_width.clamp(constraints.min_width, constraints.max_width);
78 let height = max_height.clamp(constraints.min_height, constraints.max_height);
79
80 placements.reserve(placeables.len());
81 for (placeable, alignment) in placeables {
82 let child_width = placeable.width();
83 let child_height = placeable.height();
84
85 let x = alignment
86 .horizontal
87 .align(width, child_width, scope.density());
88 let x = self.layout_direction.place_x(x, width, child_width);
89 let y = alignment
90 .vertical
91 .align(height, child_height, scope.density());
92
93 placeable.place(x, y);
94 placements.push(Placement::new(placeable.node_id(), x, y, 0));
95 }
96
97 crate::modifier::Size { width, height }.into()
98 }
99
100 fn min_intrinsic_width(&self, measurables: &[Box<dyn Measurable>], height: f32) -> f32 {
101 measurables
102 .iter()
103 .map(|m| m.min_intrinsic_width(height))
104 .fold(0.0, f32::max)
105 }
106
107 fn max_intrinsic_width(&self, measurables: &[Box<dyn Measurable>], height: f32) -> f32 {
108 measurables
109 .iter()
110 .map(|m| m.max_intrinsic_width(height))
111 .fold(0.0, f32::max)
112 }
113
114 fn min_intrinsic_height(&self, measurables: &[Box<dyn Measurable>], width: f32) -> f32 {
115 measurables
116 .iter()
117 .map(|m| m.min_intrinsic_height(width))
118 .fold(0.0, f32::max)
119 }
120
121 fn max_intrinsic_height(&self, measurables: &[Box<dyn Measurable>], width: f32) -> f32 {
122 measurables
123 .iter()
124 .map(|m| m.max_intrinsic_height(width))
125 .fold(0.0, f32::max)
126 }
127}
128
129#[derive(Clone, Debug, PartialEq)]
167pub struct FlexMeasurePolicy {
168 pub axis: Axis,
170 pub main_axis_arrangement: LinearArrangement,
172 pub cross_axis_alignment: CrossAxisAlignment,
174 pub density: f32,
177 pub layout_direction: crate::LayoutDirection,
179}
180
181pub(crate) struct WeightShares {
185 density: f32,
186 unit_px: f32,
187 remainder_px: f32,
188}
189
190impl WeightShares {
191 pub(crate) fn new(
192 remaining: f32,
193 weights: impl Iterator<Item = f32> + Clone,
194 density: f32,
195 ) -> Self {
196 let density = if density > 0.0 && density.is_finite() {
197 density
198 } else {
199 1.0
200 };
201 let total_weight: f32 = weights.clone().sum();
202 let remaining_px = (remaining * density).round();
203 let unit_px = if total_weight > 0.0 {
204 remaining_px / total_weight
205 } else {
206 0.0
207 };
208 let rounded_px: f32 = weights.map(|weight| (unit_px * weight + 0.5).floor()).sum();
209 Self {
210 density,
211 unit_px,
212 remainder_px: remaining_px - rounded_px,
213 }
214 }
215
216 pub(crate) fn next_share(&mut self, weight: f32) -> f32 {
218 let step = if self.remainder_px > 0.0 {
219 1.0
220 } else if self.remainder_px < 0.0 {
221 -1.0
222 } else {
223 0.0
224 };
225 self.remainder_px -= step;
226 ((self.unit_px * weight + 0.5).floor() + step).max(0.0) / self.density
227 }
228}
229
230#[derive(Clone, Copy, Debug, PartialEq)]
233pub enum CrossAxisAlignment {
234 Start,
236 Center,
238 End,
240}
241
242impl CrossAxisAlignment {
243 fn align(&self, available: f32, child: f32, density: f32) -> f32 {
245 let bias = match self {
246 CrossAxisAlignment::Start => -1.0,
247 CrossAxisAlignment::Center => 0.0,
248 CrossAxisAlignment::End => 1.0,
249 };
250 bias_offset(bias, available, child, density)
251 }
252}
253
254impl From<HorizontalAlignment> for CrossAxisAlignment {
255 fn from(alignment: HorizontalAlignment) -> Self {
256 match alignment {
257 HorizontalAlignment::Start => CrossAxisAlignment::Start,
258 HorizontalAlignment::CenterHorizontally => CrossAxisAlignment::Center,
259 HorizontalAlignment::End => CrossAxisAlignment::End,
260 }
261 }
262}
263
264impl From<VerticalAlignment> for CrossAxisAlignment {
265 fn from(alignment: VerticalAlignment) -> Self {
266 match alignment {
267 VerticalAlignment::Top => CrossAxisAlignment::Start,
268 VerticalAlignment::CenterVertically => CrossAxisAlignment::Center,
269 VerticalAlignment::Bottom => CrossAxisAlignment::End,
270 }
271 }
272}
273
274impl FlexMeasurePolicy {
275 pub fn new(
277 axis: Axis,
278 main_axis_arrangement: LinearArrangement,
279 cross_axis_alignment: CrossAxisAlignment,
280 density: f32,
281 ) -> Self {
282 Self {
283 axis,
284 main_axis_arrangement,
285 cross_axis_alignment,
286 density,
287 layout_direction: crate::LayoutDirection::Ltr,
288 }
289 }
290
291 pub fn row(
293 horizontal_arrangement: LinearArrangement,
294 vertical_alignment: VerticalAlignment,
295 density: f32,
296 ) -> Self {
297 Self::new(
298 Axis::Horizontal,
299 horizontal_arrangement,
300 vertical_alignment.into(),
301 density,
302 )
303 }
304
305 pub fn column(
307 vertical_arrangement: LinearArrangement,
308 horizontal_alignment: HorizontalAlignment,
309 density: f32,
310 ) -> Self {
311 Self::new(
312 Axis::Vertical,
313 vertical_arrangement,
314 horizontal_alignment.into(),
315 density,
316 )
317 }
318
319 fn get_axis_constraints(&self, constraints: Constraints) -> (f32, f32, f32, f32) {
320 match self.axis {
321 Axis::Horizontal => (
322 constraints.min_width,
323 constraints.max_width,
324 constraints.min_height,
325 constraints.max_height,
326 ),
327 Axis::Vertical => (
328 constraints.min_height,
329 constraints.max_height,
330 constraints.min_width,
331 constraints.max_width,
332 ),
333 }
334 }
335
336 fn make_constraints(
337 &self,
338 min_main: f32,
339 max_main: f32,
340 min_cross: f32,
341 max_cross: f32,
342 ) -> Constraints {
343 match self.axis {
344 Axis::Horizontal => Constraints {
345 min_width: min_main,
346 max_width: max_main,
347 min_height: min_cross,
348 max_height: max_cross,
349 },
350 Axis::Vertical => Constraints {
351 min_width: min_cross,
352 max_width: max_cross,
353 min_height: min_main,
354 max_height: max_main,
355 },
356 }
357 }
358
359 fn get_main_axis_size(&self, width: f32, height: f32) -> f32 {
360 match self.axis {
361 Axis::Horizontal => width,
362 Axis::Vertical => height,
363 }
364 }
365
366 fn get_cross_axis_size(&self, width: f32, height: f32) -> f32 {
367 match self.axis {
368 Axis::Horizontal => height,
369 Axis::Vertical => width,
370 }
371 }
372
373 fn measure_fixed_children(
379 &self,
380 measurables: &[Box<dyn Measurable>],
381 fixed_children: &[usize],
382 (max_main, max_cross): (f32, f32),
383 spacing: f32,
384 placeables: &mut [Option<cranpose_ui_layout::Placeable>],
385 ) -> (f32, f32) {
386 let main_axis_bounded = max_main.is_finite();
387 let mut fixed_space = 0.0_f32;
388 let mut max_cross_size = 0.0_f32;
389 for &idx in fixed_children {
390 let available_main = if main_axis_bounded {
391 (max_main - fixed_space).max(0.0)
392 } else {
393 max_main
394 };
395 let placeable = measurables[idx].measure(self.make_constraints(
396 0.0,
397 available_main,
398 0.0,
399 max_cross,
400 ));
401 let main_size = self.get_main_axis_size(placeable.width(), placeable.height());
402 let spacing_after = if main_axis_bounded {
403 spacing.min(available_main - main_size).max(0.0)
404 } else {
405 spacing
406 };
407 fixed_space += main_size + spacing_after;
408 max_cross_size =
409 max_cross_size.max(self.get_cross_axis_size(placeable.width(), placeable.height()));
410 placeables[idx] = Some(placeable);
411 }
412 (fixed_space, max_cross_size)
413 }
414
415 fn intrinsic_main_size(
419 &self,
420 measurables: &[Box<dyn Measurable>],
421 main_size: impl Fn(&dyn Measurable) -> f32,
422 ) -> f32 {
423 if measurables.is_empty() {
424 return 0.0;
425 }
426 let mut fixed_space = 0.0_f32;
427 let mut weight_unit = 0.0_f32;
428 let mut total_weight = 0.0_f32;
429 for measurable in measurables {
430 let size = main_size(measurable.as_ref());
431 match child_weight(measurable.as_ref()) {
432 Some(weight) => {
433 total_weight += weight;
434 weight_unit = weight_unit.max(round_to_px(size / weight, self.density));
435 }
436 None => fixed_space += size,
437 }
438 }
439 round_to_px(weight_unit * total_weight, self.density)
440 + fixed_space
441 + self.get_spacing() * (measurables.len() - 1) as f32
442 }
443
444 fn intrinsic_cross_size(
450 &self,
451 measurables: &[Box<dyn Measurable>],
452 main_available: f32,
453 main_size: impl Fn(&dyn Measurable, f32) -> f32,
454 cross_size: impl Fn(&dyn Measurable, f32) -> f32,
455 ) -> f32 {
456 if measurables.is_empty() {
457 return 0.0;
458 }
459 let mut fixed_space =
460 (self.get_spacing() * (measurables.len() - 1) as f32).min(main_available);
461 let mut cross = 0.0_f32;
462 let mut total_weight = 0.0_f32;
463 for measurable in measurables {
464 let measurable = measurable.as_ref();
465 if let Some(weight) = child_weight(measurable) {
466 total_weight += weight;
467 continue;
468 }
469 let space =
470 main_size(measurable, f32::INFINITY).min((main_available - fixed_space).max(0.0));
471 fixed_space += space;
472 cross = cross.max(cross_size(measurable, space));
473 }
474 if total_weight == 0.0 {
475 return cross;
476 }
477 let weight_unit = if main_available.is_finite() {
478 round_to_px(
479 (main_available - fixed_space).max(0.0) / total_weight,
480 self.density,
481 )
482 } else {
483 f32::INFINITY
484 };
485 for measurable in measurables {
486 let measurable = measurable.as_ref();
487 if let Some(weight) = child_weight(measurable) {
488 cross = cross.max(cross_size(
489 measurable,
490 round_to_px(weight_unit * weight, self.density),
491 ));
492 }
493 }
494 cross
495 }
496
497 fn get_spacing(&self) -> f32 {
498 self.main_axis_arrangement.spacing(self.density)
499 }
500
501 fn measure_weighted_children(
502 &self,
503 measurables: &[Box<dyn Measurable>],
504 weighted_children: &[(usize, ParentData)],
505 (max_main, max_cross): (f32, f32),
506 (fixed_space, spacing): (f32, f32),
507 placeables: &mut [Option<cranpose_ui_layout::Placeable>],
508 ) -> f32 {
509 let remaining_main =
510 (max_main - fixed_space - spacing * weighted_children.len().saturating_sub(1) as f32)
511 .max(0.0);
512 let mut shares = WeightShares::new(
513 remaining_main,
514 weighted_children.iter().map(|(_, data)| data.weight),
515 self.density,
516 );
517 let mut max_cross_size = 0.0_f32;
518 for &(idx, data) in weighted_children {
519 let constraints = if max_main.is_finite() {
520 let allocated = shares.next_share(data.weight);
521 let minimum = if data.fill { allocated } else { 0.0 };
522 self.make_constraints(minimum, allocated, 0.0, max_cross)
523 } else {
524 self.make_constraints(0.0, max_main, 0.0, max_cross)
525 };
526 let placeable = measurables[idx].measure(constraints);
527 max_cross_size =
528 max_cross_size.max(self.get_cross_axis_size(placeable.width(), placeable.height()));
529 placeables[idx] = Some(placeable);
530 }
531 max_cross_size
532 }
533
534 fn measured_extents(
535 &self,
536 placeables: &[cranpose_ui_layout::Placeable],
537 parent_data: &[cranpose_ui_layout::ParentData],
538 ) -> (f32, f32, f32) {
539 let mut main = 0.0;
540 let mut before = 0.0_f32;
541 let mut after = 0.0_f32;
542 for (placeable, data) in placeables.iter().zip(parent_data) {
543 main += self.get_main_axis_size(placeable.width(), placeable.height());
544 if self.axis == Axis::Horizontal
545 && data.row_baseline
546 && let Some(baseline) = placeable.alignment_lines().first_baseline()
547 {
548 before = before.max(baseline);
549 after = after.max(placeable.height() - baseline);
550 }
551 }
552 (main, before, after)
553 }
554
555 fn cross_axis_position(
556 &self,
557 placeable: &cranpose_ui_layout::Placeable,
558 parent_data: ParentData,
559 container_cross: f32,
560 before_baseline: f32,
561 ) -> f32 {
562 if self.axis == Axis::Horizontal && parent_data.row_baseline {
563 return placeable
564 .alignment_lines()
565 .first_baseline()
566 .map_or(0.0, |baseline| before_baseline - baseline);
567 }
568 let alignment = match self.axis {
569 Axis::Horizontal => parent_data
570 .row_alignment
571 .map_or(self.cross_axis_alignment, Into::into),
572 Axis::Vertical => parent_data
573 .column_alignment
574 .map_or(self.cross_axis_alignment, Into::into),
575 };
576 let child_cross = self.get_cross_axis_size(placeable.width(), placeable.height());
577 alignment.align(container_cross, child_cross, self.density)
578 }
579}
580
581fn child_weight(measurable: &dyn Measurable) -> Option<f32> {
583 measurable
584 .flex_parent_data()
585 .map(|data| data.weight)
586 .filter(|weight| *weight > 0.0)
587}
588
589impl MeasurePolicy for FlexMeasurePolicy {
590 fn measure(
591 &self,
592 scope: &dyn MeasureScope,
593 measurables: &[Box<dyn Measurable>],
594 constraints: Constraints,
595 ) -> MeasureResult {
596 let mut placements = Vec::new();
597 let measurement = self.measure_into(scope, measurables, constraints, &mut placements);
598 MeasureResult::new(measurement, placements)
599 }
600
601 fn measure_into(
602 &self,
603 _scope: &dyn MeasureScope,
604 measurables: &[Box<dyn Measurable>],
605 constraints: Constraints,
606 placements: &mut Vec<Placement>,
607 ) -> cranpose_ui_layout::Measurement {
608 placements.clear();
609 if measurables.is_empty() {
610 let (width, height) = constraints.constrain(0.0, 0.0);
611 return crate::modifier::Size { width, height }.into();
612 }
613
614 let (min_main, max_main, min_cross, max_cross) = self.get_axis_constraints(constraints);
615 let spacing = self.get_spacing();
616
617 let mut fixed_children: SmallVec<[usize; 8]> = SmallVec::new();
618 let parent_data: SmallVec<[ParentData; 8]> = measurables
619 .iter()
620 .map(|child| child.parent_data())
621 .collect();
622 let mut weighted_children: SmallVec<[(usize, ParentData); 8]> = SmallVec::new();
623
624 for (idx, data) in parent_data.iter().copied().enumerate() {
625 if data.has_weight() {
626 weighted_children.push((idx, data));
627 } else {
628 fixed_children.push(idx);
629 }
630 }
631
632 let child_constraints = self.make_constraints(0.0, max_main, 0.0, max_cross);
633
634 let mut placeables: SmallVec<[Option<cranpose_ui_layout::Placeable>; 8]> = SmallVec::new();
635 placeables.resize_with(measurables.len(), || None);
636 let (fixed_space, mut max_cross_size) = self.measure_fixed_children(
637 measurables,
638 &fixed_children,
639 (max_main, max_cross),
640 spacing,
641 &mut placeables,
642 );
643
644 let num_children = measurables.len();
645 let total_spacing = if num_children > 1 {
646 spacing * (num_children - 1) as f32
647 } else {
648 0.0
649 };
650
651 max_cross_size = max_cross_size.max(self.measure_weighted_children(
652 measurables,
653 &weighted_children,
654 (max_main, max_cross),
655 (fixed_space, spacing),
656 &mut placeables,
657 ));
658
659 let placeables: SmallVec<[cranpose_ui_layout::Placeable; 8]> = placeables
660 .into_iter()
661 .enumerate()
662 .map(|(idx, placeable)| {
663 placeable.unwrap_or_else(|| measurables[idx].measure(child_constraints))
664 })
665 .collect();
666
667 let (total_main, before_baseline, after_baseline) =
668 self.measured_extents(&placeables, &parent_data);
669 let total_main = total_main + total_spacing;
670 max_cross_size = max_cross_size.max(before_baseline + after_baseline);
671
672 let container_main = total_main.clamp(min_main, max_main);
673 let container_cross = max_cross_size.clamp(min_cross, max_cross);
674
675 let child_main_sizes: SmallVec<[f32; 8]> = placeables
676 .iter()
677 .map(|p| self.get_main_axis_size(p.width(), p.height()))
678 .collect();
679
680 let mut main_positions: SmallVec<[f32; 8]> =
681 SmallVec::with_capacity(child_main_sizes.len());
682 main_positions.resize(child_main_sizes.len(), 0.0);
683
684 let arrangement = if total_main > container_main && !self.main_axis_arrangement.is_spaced()
685 {
686 LinearArrangement::Start
687 } else {
688 self.main_axis_arrangement
689 };
690 arrangement.arrange(
691 self.density,
692 container_main,
693 &child_main_sizes,
694 &mut main_positions,
695 );
696
697 placements.reserve(placeables.len());
698 for (idx, (placeable, main_pos)) in placeables.into_iter().zip(main_positions).enumerate() {
699 let cross_pos = self.cross_axis_position(
700 &placeable,
701 parent_data[idx],
702 container_cross,
703 before_baseline,
704 );
705
706 let (x, y) = match self.axis {
707 Axis::Horizontal => (main_pos, cross_pos),
708 Axis::Vertical => (cross_pos, main_pos),
709 };
710 let width = match self.axis {
711 Axis::Horizontal => container_main,
712 Axis::Vertical => container_cross,
713 };
714 let x = self.layout_direction.place_x(x, width, placeable.width());
715
716 placeable.place(x, y);
717 placements.push(Placement::new(placeable.node_id(), x, y, 0));
718 }
719
720 let (width, height) = match self.axis {
721 Axis::Horizontal => (container_main, container_cross),
722 Axis::Vertical => (container_cross, container_main),
723 };
724
725 crate::modifier::Size { width, height }.into()
726 }
727
728 fn min_intrinsic_width(&self, measurables: &[Box<dyn Measurable>], height: f32) -> f32 {
729 match self.axis {
730 Axis::Horizontal => {
731 self.intrinsic_main_size(measurables, |m| m.min_intrinsic_width(height))
732 }
733 Axis::Vertical => self.intrinsic_cross_size(
734 measurables,
735 height,
736 |m, width| m.max_intrinsic_height(width),
737 |m, main| m.min_intrinsic_width(main),
738 ),
739 }
740 }
741
742 fn max_intrinsic_width(&self, measurables: &[Box<dyn Measurable>], height: f32) -> f32 {
743 match self.axis {
744 Axis::Horizontal => {
745 self.intrinsic_main_size(measurables, |m| m.max_intrinsic_width(height))
746 }
747 Axis::Vertical => self.intrinsic_cross_size(
748 measurables,
749 height,
750 |m, width| m.max_intrinsic_height(width),
751 |m, main| m.max_intrinsic_width(main),
752 ),
753 }
754 }
755
756 fn min_intrinsic_height(&self, measurables: &[Box<dyn Measurable>], width: f32) -> f32 {
757 match self.axis {
758 Axis::Horizontal => self.intrinsic_cross_size(
759 measurables,
760 width,
761 |m, height| m.max_intrinsic_width(height),
762 |m, main| m.min_intrinsic_height(main),
763 ),
764 Axis::Vertical => {
765 self.intrinsic_main_size(measurables, |m| m.min_intrinsic_height(width))
766 }
767 }
768 }
769
770 fn max_intrinsic_height(&self, measurables: &[Box<dyn Measurable>], width: f32) -> f32 {
771 match self.axis {
772 Axis::Horizontal => self.intrinsic_cross_size(
773 measurables,
774 width,
775 |m, height| m.max_intrinsic_width(height),
776 |m, main| m.max_intrinsic_height(main),
777 ),
778 Axis::Vertical => {
779 self.intrinsic_main_size(measurables, |m| m.max_intrinsic_height(width))
780 }
781 }
782 }
783}
784
785#[derive(Clone, Debug, PartialEq)]
798pub struct FlowRowMeasurePolicy {
799 pub main_axis_spacing: f32,
801 pub cross_axis_spacing: f32,
803 pub layout_direction: crate::LayoutDirection,
805}
806
807impl FlowRowMeasurePolicy {
808 pub fn new(main_axis_spacing: f32, cross_axis_spacing: f32) -> Self {
809 Self {
810 main_axis_spacing: main_axis_spacing.max(0.0),
811 cross_axis_spacing: cross_axis_spacing.max(0.0),
812 layout_direction: crate::LayoutDirection::Ltr,
813 }
814 }
815
816 fn wrapped_intrinsic_height(
817 &self,
818 measurables: &[Box<dyn Measurable>],
819 available_width: f32,
820 use_min_height: bool,
821 ) -> f32 {
822 let mut cursor_x = 0.0_f32;
823 let mut line_top = 0.0_f32;
824 let mut line_height = 0.0_f32;
825
826 for measurable in measurables {
827 let child_width = measurable.max_intrinsic_width(f32::INFINITY);
828 let child_height = if use_min_height {
829 measurable.min_intrinsic_height(child_width)
830 } else {
831 measurable.max_intrinsic_height(child_width)
832 };
833
834 if cursor_x > 0.0 && cursor_x + self.main_axis_spacing + child_width > available_width {
835 line_top += line_height + self.cross_axis_spacing;
836 cursor_x = 0.0;
837 line_height = 0.0;
838 }
839 cursor_x += if cursor_x > 0.0 {
840 self.main_axis_spacing + child_width
841 } else {
842 child_width
843 };
844 line_height = line_height.max(child_height);
845 }
846
847 line_top + line_height
848 }
849}
850
851impl MeasurePolicy for FlowRowMeasurePolicy {
852 fn measure(
853 &self,
854 scope: &dyn MeasureScope,
855 measurables: &[Box<dyn Measurable>],
856 constraints: Constraints,
857 ) -> MeasureResult {
858 let mut placements = Vec::new();
859 let measurement = self.measure_into(scope, measurables, constraints, &mut placements);
860 MeasureResult::new(measurement, placements)
861 }
862
863 fn measure_into(
864 &self,
865 _scope: &dyn MeasureScope,
866 measurables: &[Box<dyn Measurable>],
867 constraints: Constraints,
868 placements: &mut Vec<Placement>,
869 ) -> cranpose_ui_layout::Measurement {
870 placements.clear();
871 if measurables.is_empty() {
872 let (width, height) = constraints.constrain(0.0, 0.0);
873 return crate::modifier::Size { width, height }.into();
874 }
875
876 let child_constraints = Constraints {
877 min_width: 0.0,
878 max_width: constraints.max_width,
879 min_height: 0.0,
880 max_height: constraints.max_height,
881 };
882
883 let placeables: SmallVec<[cranpose_ui_layout::Placeable; 8]> = measurables
884 .iter()
885 .map(|measurable| measurable.measure(child_constraints))
886 .collect();
887
888 let mut cursor_x = 0.0_f32;
889 let mut line_top = 0.0_f32;
890 let mut line_height = 0.0_f32;
891 let mut max_line_width = 0.0_f32;
892
893 placements.reserve(placeables.len());
894 for placeable in &placeables {
895 let child_width = placeable.width();
896 let child_height = placeable.height();
897
898 if cursor_x > 0.0
899 && cursor_x + self.main_axis_spacing + child_width > constraints.max_width
900 {
901 max_line_width = max_line_width.max(cursor_x);
902 line_top += line_height + self.cross_axis_spacing;
903 cursor_x = 0.0;
904 line_height = 0.0;
905 }
906
907 let x = if cursor_x > 0.0 {
908 cursor_x + self.main_axis_spacing
909 } else {
910 0.0
911 };
912 if !self.layout_direction.is_rtl() {
913 placeable.place(x, line_top);
914 }
915 placements.push(Placement::new(placeable.node_id(), x, line_top, 0));
916
917 cursor_x = x + child_width;
918 line_height = line_height.max(child_height);
919 }
920 max_line_width = max_line_width.max(cursor_x);
921
922 let width = max_line_width.clamp(constraints.min_width, constraints.max_width);
923 if self.layout_direction.is_rtl() {
924 for (placement, placeable) in placements.iter_mut().zip(&placeables) {
925 placement.x = self
926 .layout_direction
927 .place_x(placement.x, width, placeable.width());
928 placeable.place(placement.x, placement.y);
929 }
930 }
931 let height = (line_top + line_height).clamp(constraints.min_height, constraints.max_height);
932 crate::modifier::Size { width, height }.into()
933 }
934
935 fn min_intrinsic_width(&self, measurables: &[Box<dyn Measurable>], height: f32) -> f32 {
936 measurables
937 .iter()
938 .map(|m| m.min_intrinsic_width(height))
939 .fold(0.0, f32::max)
940 }
941
942 fn max_intrinsic_width(&self, measurables: &[Box<dyn Measurable>], height: f32) -> f32 {
943 let total_spacing = if measurables.len() > 1 {
944 self.main_axis_spacing * (measurables.len() - 1) as f32
945 } else {
946 0.0
947 };
948 measurables
949 .iter()
950 .map(|m| m.max_intrinsic_width(height))
951 .sum::<f32>()
952 + total_spacing
953 }
954
955 fn min_intrinsic_height(&self, measurables: &[Box<dyn Measurable>], width: f32) -> f32 {
956 self.wrapped_intrinsic_height(measurables, width, true)
957 }
958
959 fn max_intrinsic_height(&self, measurables: &[Box<dyn Measurable>], width: f32) -> f32 {
960 self.wrapped_intrinsic_height(measurables, width, false)
961 }
962}
963
964#[derive(Clone, Debug, PartialEq)]
967pub struct LeafMeasurePolicy {
968 pub intrinsic_size: crate::modifier::Size,
969}
970
971impl LeafMeasurePolicy {
972 pub fn new(intrinsic_size: crate::modifier::Size) -> Self {
973 Self { intrinsic_size }
974 }
975}
976
977impl MeasurePolicy for LeafMeasurePolicy {
978 fn measure(
979 &self,
980 scope: &dyn MeasureScope,
981 _measurables: &[Box<dyn Measurable>],
982 constraints: Constraints,
983 ) -> MeasureResult {
984 let mut placements = Vec::new();
985 let measurement = self.measure_into(scope, &[], constraints, &mut placements);
986 MeasureResult::new(measurement, placements)
987 }
988
989 fn measure_into(
990 &self,
991 _scope: &dyn MeasureScope,
992 _measurables: &[Box<dyn Measurable>],
993 constraints: Constraints,
994 placements: &mut Vec<Placement>,
995 ) -> cranpose_ui_layout::Measurement {
996 placements.clear();
997 let (width, height) =
998 constraints.constrain(self.intrinsic_size.width, self.intrinsic_size.height);
999
1000 crate::modifier::Size { width, height }.into()
1001 }
1002
1003 fn min_intrinsic_width(&self, _measurables: &[Box<dyn Measurable>], _height: f32) -> f32 {
1004 self.intrinsic_size.width
1005 }
1006
1007 fn max_intrinsic_width(&self, _measurables: &[Box<dyn Measurable>], _height: f32) -> f32 {
1008 self.intrinsic_size.width
1009 }
1010
1011 fn min_intrinsic_height(&self, _measurables: &[Box<dyn Measurable>], _width: f32) -> f32 {
1012 self.intrinsic_size.height
1013 }
1014
1015 fn max_intrinsic_height(&self, _measurables: &[Box<dyn Measurable>], _width: f32) -> f32 {
1016 self.intrinsic_size.height
1017 }
1018}
1019
1020#[derive(Clone, Debug, PartialEq)]
1026pub struct EmptyMeasurePolicy;
1027
1028impl EmptyMeasurePolicy {
1029 pub fn new() -> Self {
1030 Self
1031 }
1032}
1033
1034impl Default for EmptyMeasurePolicy {
1035 fn default() -> Self {
1036 Self::new()
1037 }
1038}
1039
1040impl MeasurePolicy for EmptyMeasurePolicy {
1041 fn measure(
1042 &self,
1043 scope: &dyn MeasureScope,
1044 _measurables: &[Box<dyn Measurable>],
1045 constraints: Constraints,
1046 ) -> MeasureResult {
1047 let mut placements = Vec::new();
1048 let measurement = self.measure_into(scope, &[], constraints, &mut placements);
1049 MeasureResult::new(measurement, placements)
1050 }
1051
1052 fn measure_into(
1053 &self,
1054 _scope: &dyn MeasureScope,
1055 _measurables: &[Box<dyn Measurable>],
1056 constraints: Constraints,
1057 placements: &mut Vec<Placement>,
1058 ) -> cranpose_ui_layout::Measurement {
1059 placements.clear();
1060 let (width, height) = constraints.constrain(0.0, 0.0);
1061
1062 crate::modifier::Size { width, height }.into()
1063 }
1064
1065 fn min_intrinsic_width(&self, _measurables: &[Box<dyn Measurable>], _height: f32) -> f32 {
1066 0.0
1067 }
1068
1069 fn max_intrinsic_width(&self, _measurables: &[Box<dyn Measurable>], _height: f32) -> f32 {
1070 0.0
1071 }
1072
1073 fn min_intrinsic_height(&self, _measurables: &[Box<dyn Measurable>], _width: f32) -> f32 {
1074 0.0
1075 }
1076
1077 fn max_intrinsic_height(&self, _measurables: &[Box<dyn Measurable>], _width: f32) -> f32 {
1078 0.0
1079 }
1080}
1081
1082#[cfg(test)]
1083#[path = "tests/policies_tests.rs"]
1084mod tests;