1use crate::layout::core::{
2 Alignment, Arrangement, HorizontalAlignment, LinearArrangement, Measurable, VerticalAlignment,
3};
4use cranpose_ui_layout::{
5 Axis, Constraints, FlexParentData, MeasurePolicy, MeasureResult, Placement,
6};
7use smallvec::SmallVec;
8
9#[derive(Clone, Debug, PartialEq)]
11pub struct BoxMeasurePolicy {
12 pub content_alignment: Alignment,
13 pub propagate_min_constraints: bool,
14}
15
16impl BoxMeasurePolicy {
17 pub fn new(content_alignment: Alignment, propagate_min_constraints: bool) -> Self {
18 Self {
19 content_alignment,
20 propagate_min_constraints,
21 }
22 }
23}
24
25impl MeasurePolicy for BoxMeasurePolicy {
26 fn measure(
27 &self,
28 measurables: &[Box<dyn Measurable>],
29 constraints: Constraints,
30 ) -> MeasureResult {
31 let mut placements = Vec::new();
32 let size = self.measure_into(measurables, constraints, &mut placements);
33 MeasureResult::new(size, placements)
34 }
35
36 fn measure_into(
37 &self,
38 measurables: &[Box<dyn Measurable>],
39 constraints: Constraints,
40 placements: &mut Vec<Placement>,
41 ) -> crate::modifier::Size {
42 placements.clear();
43 let child_constraints = if self.propagate_min_constraints {
44 constraints
45 } else {
46 Constraints {
47 min_width: 0.0,
48 max_width: constraints.max_width,
49 min_height: 0.0,
50 max_height: constraints.max_height,
51 }
52 };
53
54 let mut max_width = 0.0_f32;
55 let mut max_height = 0.0_f32;
56 let mut placeables: SmallVec<[cranpose_ui_layout::Placeable; 8]> = SmallVec::new();
57
58 for measurable in measurables {
59 let placeable = measurable.measure(child_constraints);
60 max_width = max_width.max(placeable.width());
61 max_height = max_height.max(placeable.height());
62 placeables.push(placeable);
63 }
64
65 let width = max_width.clamp(constraints.min_width, constraints.max_width);
66 let height = max_height.clamp(constraints.min_height, constraints.max_height);
67
68 placements.reserve(placeables.len());
69 for placeable in placeables {
70 let child_width = placeable.width();
71 let child_height = placeable.height();
72
73 let x = match self.content_alignment.horizontal {
74 HorizontalAlignment::Start => 0.0,
75 HorizontalAlignment::CenterHorizontally => ((width - child_width) / 2.0).max(0.0),
76 HorizontalAlignment::End => (width - child_width).max(0.0),
77 };
78
79 let y = match self.content_alignment.vertical {
80 VerticalAlignment::Top => 0.0,
81 VerticalAlignment::CenterVertically => ((height - child_height) / 2.0).max(0.0),
82 VerticalAlignment::Bottom => (height - child_height).max(0.0),
83 };
84
85 placeable.place(x, y);
86 placements.push(Placement::new(placeable.node_id(), x, y, 0));
87 }
88
89 crate::modifier::Size { width, height }
90 }
91
92 fn min_intrinsic_width(&self, measurables: &[Box<dyn Measurable>], height: f32) -> f32 {
93 measurables
94 .iter()
95 .map(|m| m.min_intrinsic_width(height))
96 .fold(0.0, f32::max)
97 }
98
99 fn max_intrinsic_width(&self, measurables: &[Box<dyn Measurable>], height: f32) -> f32 {
100 measurables
101 .iter()
102 .map(|m| m.max_intrinsic_width(height))
103 .fold(0.0, f32::max)
104 }
105
106 fn min_intrinsic_height(&self, measurables: &[Box<dyn Measurable>], width: f32) -> f32 {
107 measurables
108 .iter()
109 .map(|m| m.min_intrinsic_height(width))
110 .fold(0.0, f32::max)
111 }
112
113 fn max_intrinsic_height(&self, measurables: &[Box<dyn Measurable>], width: f32) -> f32 {
114 measurables
115 .iter()
116 .map(|m| m.max_intrinsic_height(width))
117 .fold(0.0, f32::max)
118 }
119}
120
121#[derive(Clone, Debug, PartialEq)]
161pub struct FlexMeasurePolicy {
162 pub axis: Axis,
164 pub main_axis_arrangement: LinearArrangement,
166 pub cross_axis_alignment: CrossAxisAlignment,
168}
169
170#[derive(Clone, Copy, Debug, PartialEq)]
173pub enum CrossAxisAlignment {
174 Start,
176 Center,
178 End,
180}
181
182impl CrossAxisAlignment {
183 fn align(&self, available: f32, child: f32) -> f32 {
185 match self {
186 CrossAxisAlignment::Start => 0.0,
187 CrossAxisAlignment::Center => ((available - child) / 2.0).max(0.0),
188 CrossAxisAlignment::End => (available - child).max(0.0),
189 }
190 }
191}
192
193impl From<HorizontalAlignment> for CrossAxisAlignment {
194 fn from(alignment: HorizontalAlignment) -> Self {
195 match alignment {
196 HorizontalAlignment::Start => CrossAxisAlignment::Start,
197 HorizontalAlignment::CenterHorizontally => CrossAxisAlignment::Center,
198 HorizontalAlignment::End => CrossAxisAlignment::End,
199 }
200 }
201}
202
203impl From<VerticalAlignment> for CrossAxisAlignment {
204 fn from(alignment: VerticalAlignment) -> Self {
205 match alignment {
206 VerticalAlignment::Top => CrossAxisAlignment::Start,
207 VerticalAlignment::CenterVertically => CrossAxisAlignment::Center,
208 VerticalAlignment::Bottom => CrossAxisAlignment::End,
209 }
210 }
211}
212
213impl FlexMeasurePolicy {
214 pub fn new(
215 axis: Axis,
216 main_axis_arrangement: LinearArrangement,
217 cross_axis_alignment: CrossAxisAlignment,
218 ) -> Self {
219 Self {
220 axis,
221 main_axis_arrangement,
222 cross_axis_alignment,
223 }
224 }
225
226 pub fn row(
228 horizontal_arrangement: LinearArrangement,
229 vertical_alignment: VerticalAlignment,
230 ) -> Self {
231 Self::new(
232 Axis::Horizontal,
233 horizontal_arrangement,
234 vertical_alignment.into(),
235 )
236 }
237
238 pub fn column(
240 vertical_arrangement: LinearArrangement,
241 horizontal_alignment: HorizontalAlignment,
242 ) -> Self {
243 Self::new(
244 Axis::Vertical,
245 vertical_arrangement,
246 horizontal_alignment.into(),
247 )
248 }
249
250 fn get_axis_constraints(&self, constraints: Constraints) -> (f32, f32, f32, f32) {
252 match self.axis {
253 Axis::Horizontal => (
254 constraints.min_width,
255 constraints.max_width,
256 constraints.min_height,
257 constraints.max_height,
258 ),
259 Axis::Vertical => (
260 constraints.min_height,
261 constraints.max_height,
262 constraints.min_width,
263 constraints.max_width,
264 ),
265 }
266 }
267
268 fn make_constraints(
270 &self,
271 min_main: f32,
272 max_main: f32,
273 min_cross: f32,
274 max_cross: f32,
275 ) -> Constraints {
276 match self.axis {
277 Axis::Horizontal => Constraints {
278 min_width: min_main,
279 max_width: max_main,
280 min_height: min_cross,
281 max_height: max_cross,
282 },
283 Axis::Vertical => Constraints {
284 min_width: min_cross,
285 max_width: max_cross,
286 min_height: min_main,
287 max_height: max_main,
288 },
289 }
290 }
291
292 fn get_main_axis_size(&self, width: f32, height: f32) -> f32 {
294 match self.axis {
295 Axis::Horizontal => width,
296 Axis::Vertical => height,
297 }
298 }
299
300 fn get_cross_axis_size(&self, width: f32, height: f32) -> f32 {
302 match self.axis {
303 Axis::Horizontal => height,
304 Axis::Vertical => width,
305 }
306 }
307
308 fn get_spacing(&self) -> f32 {
310 match self.main_axis_arrangement {
311 LinearArrangement::SpacedBy(value) => value.max(0.0),
312 _ => 0.0,
313 }
314 }
315}
316
317impl MeasurePolicy for FlexMeasurePolicy {
318 fn measure(
319 &self,
320 measurables: &[Box<dyn Measurable>],
321 constraints: Constraints,
322 ) -> MeasureResult {
323 let mut placements = Vec::new();
324 let size = self.measure_into(measurables, constraints, &mut placements);
325 MeasureResult::new(size, placements)
326 }
327
328 fn measure_into(
329 &self,
330 measurables: &[Box<dyn Measurable>],
331 constraints: Constraints,
332 placements: &mut Vec<Placement>,
333 ) -> crate::modifier::Size {
334 placements.clear();
335 if measurables.is_empty() {
336 let (width, height) = constraints.constrain(0.0, 0.0);
337 return crate::modifier::Size { width, height };
338 }
339
340 let (min_main, max_main, min_cross, max_cross) = self.get_axis_constraints(constraints);
341 let main_axis_bounded = max_main.is_finite();
342 let spacing = self.get_spacing();
343
344 let mut fixed_children: SmallVec<[usize; 8]> = SmallVec::new();
346 let mut weighted_children: SmallVec<[(usize, FlexParentData); 8]> = SmallVec::new();
347
348 for (idx, measurable) in measurables.iter().enumerate() {
349 let parent_data = measurable.flex_parent_data().unwrap_or_default();
350 if parent_data.has_weight() {
351 weighted_children.push((idx, parent_data));
352 } else {
353 fixed_children.push(idx);
354 }
355 }
356
357 let child_constraints = self.make_constraints(0.0, max_main, 0.0, max_cross);
360
361 let mut placeables: SmallVec<[Option<cranpose_ui_layout::Placeable>; 8]> = SmallVec::new();
362 placeables.resize_with(measurables.len(), || None);
363 let mut fixed_main_size = 0.0_f32;
364 let mut max_cross_size = 0.0_f32;
365
366 for &idx in &fixed_children {
367 let measurable = &measurables[idx];
368 let placeable = measurable.measure(child_constraints);
369 let main_size = self.get_main_axis_size(placeable.width(), placeable.height());
370 let cross_size = self.get_cross_axis_size(placeable.width(), placeable.height());
371
372 fixed_main_size += main_size;
373 max_cross_size = max_cross_size.max(cross_size);
374 placeables[idx] = Some(placeable);
375 }
376
377 let num_children = measurables.len();
379 let total_spacing = if num_children > 1 {
380 spacing * (num_children - 1) as f32
381 } else {
382 0.0
383 };
384
385 if !weighted_children.is_empty() {
387 if main_axis_bounded {
388 let used_main = fixed_main_size + total_spacing;
390 let remaining_main = (max_main - used_main).max(0.0);
391
392 let total_weight: f32 = weighted_children.iter().map(|(_, data)| data.weight).sum();
394
395 for &(idx, parent_data) in &weighted_children {
397 let measurable = &measurables[idx];
398 let allocated = if total_weight > 0.0 {
399 remaining_main * (parent_data.weight / total_weight)
400 } else {
401 0.0
402 };
403
404 let weighted_constraints = if parent_data.fill {
405 self.make_constraints(allocated, allocated, 0.0, max_cross)
407 } else {
408 self.make_constraints(0.0, allocated, 0.0, max_cross)
410 };
411
412 let placeable = measurable.measure(weighted_constraints);
413 let cross_size =
414 self.get_cross_axis_size(placeable.width(), placeable.height());
415 max_cross_size = max_cross_size.max(cross_size);
416 placeables[idx] = Some(placeable);
417 }
418 } else {
419 for &(idx, _) in &weighted_children {
421 let measurable = &measurables[idx];
422 let placeable = measurable.measure(child_constraints);
423 let cross_size =
424 self.get_cross_axis_size(placeable.width(), placeable.height());
425 max_cross_size = max_cross_size.max(cross_size);
426 placeables[idx] = Some(placeable);
427 }
428 }
429 }
430
431 let placeables: SmallVec<[cranpose_ui_layout::Placeable; 8]> = placeables
432 .into_iter()
433 .enumerate()
434 .map(|(idx, placeable)| {
435 placeable.unwrap_or_else(|| measurables[idx].measure(child_constraints))
436 })
437 .collect();
438
439 let total_main: f32 = placeables
441 .iter()
442 .map(|p| self.get_main_axis_size(p.width(), p.height()))
443 .sum::<f32>()
444 + total_spacing;
445
446 let container_main = total_main.clamp(min_main, max_main);
448 let container_cross = max_cross_size.clamp(min_cross, max_cross);
449
450 let child_main_sizes: SmallVec<[f32; 8]> = placeables
452 .iter()
453 .map(|p| self.get_main_axis_size(p.width(), p.height()))
454 .collect();
455
456 let mut main_positions: SmallVec<[f32; 8]> =
457 SmallVec::with_capacity(child_main_sizes.len());
458 main_positions.resize(child_main_sizes.len(), 0.0);
459
460 let arrangement = if total_main > container_main
463 && !matches!(self.main_axis_arrangement, LinearArrangement::SpacedBy(_))
464 {
465 LinearArrangement::Start
466 } else {
467 self.main_axis_arrangement
468 };
469 arrangement.arrange(container_main, &child_main_sizes, &mut main_positions);
470
471 placements.reserve(placeables.len());
473 for (placeable, main_pos) in placeables.into_iter().zip(main_positions) {
474 let child_cross = self.get_cross_axis_size(placeable.width(), placeable.height());
475 let cross_pos = self
476 .cross_axis_alignment
477 .align(container_cross, child_cross);
478
479 let (x, y) = match self.axis {
480 Axis::Horizontal => (main_pos, cross_pos),
481 Axis::Vertical => (cross_pos, main_pos),
482 };
483
484 placeable.place(x, y);
485 placements.push(Placement::new(placeable.node_id(), x, y, 0));
486 }
487
488 let (width, height) = match self.axis {
490 Axis::Horizontal => (container_main, container_cross),
491 Axis::Vertical => (container_cross, container_main),
492 };
493
494 crate::modifier::Size { width, height }
495 }
496
497 fn min_intrinsic_width(&self, measurables: &[Box<dyn Measurable>], height: f32) -> f32 {
498 let spacing = self.get_spacing();
499 let total_spacing = if measurables.len() > 1 {
500 spacing * (measurables.len() - 1) as f32
501 } else {
502 0.0
503 };
504
505 match self.axis {
506 Axis::Horizontal => {
507 measurables
509 .iter()
510 .map(|m| m.min_intrinsic_width(height))
511 .sum::<f32>()
512 + total_spacing
513 }
514 Axis::Vertical => {
515 measurables
517 .iter()
518 .map(|m| m.min_intrinsic_width(height))
519 .fold(0.0, f32::max)
520 }
521 }
522 }
523
524 fn max_intrinsic_width(&self, measurables: &[Box<dyn Measurable>], height: f32) -> f32 {
525 let spacing = self.get_spacing();
526 let total_spacing = if measurables.len() > 1 {
527 spacing * (measurables.len() - 1) as f32
528 } else {
529 0.0
530 };
531
532 match self.axis {
533 Axis::Horizontal => {
534 measurables
536 .iter()
537 .map(|m| m.max_intrinsic_width(height))
538 .sum::<f32>()
539 + total_spacing
540 }
541 Axis::Vertical => {
542 measurables
544 .iter()
545 .map(|m| m.max_intrinsic_width(height))
546 .fold(0.0, f32::max)
547 }
548 }
549 }
550
551 fn min_intrinsic_height(&self, measurables: &[Box<dyn Measurable>], width: f32) -> f32 {
552 let spacing = self.get_spacing();
553 let total_spacing = if measurables.len() > 1 {
554 spacing * (measurables.len() - 1) as f32
555 } else {
556 0.0
557 };
558
559 match self.axis {
560 Axis::Horizontal => {
561 measurables
563 .iter()
564 .map(|m| m.min_intrinsic_height(width))
565 .fold(0.0, f32::max)
566 }
567 Axis::Vertical => {
568 measurables
570 .iter()
571 .map(|m| m.min_intrinsic_height(width))
572 .sum::<f32>()
573 + total_spacing
574 }
575 }
576 }
577
578 fn max_intrinsic_height(&self, measurables: &[Box<dyn Measurable>], width: f32) -> f32 {
579 let spacing = self.get_spacing();
580 let total_spacing = if measurables.len() > 1 {
581 spacing * (measurables.len() - 1) as f32
582 } else {
583 0.0
584 };
585
586 match self.axis {
587 Axis::Horizontal => {
588 measurables
590 .iter()
591 .map(|m| m.max_intrinsic_height(width))
592 .fold(0.0, f32::max)
593 }
594 Axis::Vertical => {
595 measurables
597 .iter()
598 .map(|m| m.max_intrinsic_height(width))
599 .sum::<f32>()
600 + total_spacing
601 }
602 }
603 }
604}
605
606#[derive(Clone, Debug, PartialEq)]
609pub struct LeafMeasurePolicy {
610 pub intrinsic_size: crate::modifier::Size,
611}
612
613impl LeafMeasurePolicy {
614 pub fn new(intrinsic_size: crate::modifier::Size) -> Self {
615 Self { intrinsic_size }
616 }
617}
618
619impl MeasurePolicy for LeafMeasurePolicy {
620 fn measure(
621 &self,
622 _measurables: &[Box<dyn Measurable>],
623 constraints: Constraints,
624 ) -> MeasureResult {
625 let mut placements = Vec::new();
626 let size = self.measure_into(&[], constraints, &mut placements);
627 MeasureResult::new(size, placements)
628 }
629
630 fn measure_into(
631 &self,
632 _measurables: &[Box<dyn Measurable>],
633 constraints: Constraints,
634 placements: &mut Vec<Placement>,
635 ) -> crate::modifier::Size {
636 placements.clear();
637 let (width, height) =
639 constraints.constrain(self.intrinsic_size.width, self.intrinsic_size.height);
640
641 crate::modifier::Size { width, height }
642 }
643
644 fn min_intrinsic_width(&self, _measurables: &[Box<dyn Measurable>], _height: f32) -> f32 {
645 self.intrinsic_size.width
646 }
647
648 fn max_intrinsic_width(&self, _measurables: &[Box<dyn Measurable>], _height: f32) -> f32 {
649 self.intrinsic_size.width
650 }
651
652 fn min_intrinsic_height(&self, _measurables: &[Box<dyn Measurable>], _width: f32) -> f32 {
653 self.intrinsic_size.height
654 }
655
656 fn max_intrinsic_height(&self, _measurables: &[Box<dyn Measurable>], _width: f32) -> f32 {
657 self.intrinsic_size.height
658 }
659}
660
661#[derive(Clone, Debug, PartialEq)]
667pub struct EmptyMeasurePolicy;
668
669impl EmptyMeasurePolicy {
670 pub fn new() -> Self {
671 Self
672 }
673}
674
675impl Default for EmptyMeasurePolicy {
676 fn default() -> Self {
677 Self::new()
678 }
679}
680
681impl MeasurePolicy for EmptyMeasurePolicy {
682 fn measure(
683 &self,
684 _measurables: &[Box<dyn Measurable>],
685 constraints: Constraints,
686 ) -> MeasureResult {
687 let mut placements = Vec::new();
688 let size = self.measure_into(&[], constraints, &mut placements);
689 MeasureResult::new(size, placements)
690 }
691
692 fn measure_into(
693 &self,
694 _measurables: &[Box<dyn Measurable>],
695 constraints: Constraints,
696 placements: &mut Vec<Placement>,
697 ) -> crate::modifier::Size {
698 placements.clear();
699 let (width, height) = constraints.constrain(0.0, 0.0);
702
703 crate::modifier::Size { width, height }
704 }
705
706 fn min_intrinsic_width(&self, _measurables: &[Box<dyn Measurable>], _height: f32) -> f32 {
707 0.0
708 }
709
710 fn max_intrinsic_width(&self, _measurables: &[Box<dyn Measurable>], _height: f32) -> f32 {
711 0.0
712 }
713
714 fn min_intrinsic_height(&self, _measurables: &[Box<dyn Measurable>], _width: f32) -> f32 {
715 0.0
716 }
717
718 fn max_intrinsic_height(&self, _measurables: &[Box<dyn Measurable>], _width: f32) -> f32 {
719 0.0
720 }
721}
722
723#[cfg(test)]
724#[path = "tests/policies_tests.rs"]
725mod tests;