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 child_constraints = if self.propagate_min_constraints {
32 constraints
33 } else {
34 Constraints {
35 min_width: 0.0,
36 max_width: constraints.max_width,
37 min_height: 0.0,
38 max_height: constraints.max_height,
39 }
40 };
41
42 let mut max_width = 0.0_f32;
43 let mut max_height = 0.0_f32;
44 let mut placeables = Vec::with_capacity(measurables.len());
45
46 for measurable in measurables {
47 let placeable = measurable.measure(child_constraints);
48 max_width = max_width.max(placeable.width());
49 max_height = max_height.max(placeable.height());
50 placeables.push(placeable);
51 }
52
53 let width = max_width.clamp(constraints.min_width, constraints.max_width);
54 let height = max_height.clamp(constraints.min_height, constraints.max_height);
55
56 let mut placements = Vec::with_capacity(placeables.len());
57 for placeable in placeables {
58 let child_width = placeable.width();
59 let child_height = placeable.height();
60
61 let x = match self.content_alignment.horizontal {
62 HorizontalAlignment::Start => 0.0,
63 HorizontalAlignment::CenterHorizontally => ((width - child_width) / 2.0).max(0.0),
64 HorizontalAlignment::End => (width - child_width).max(0.0),
65 };
66
67 let y = match self.content_alignment.vertical {
68 VerticalAlignment::Top => 0.0,
69 VerticalAlignment::CenterVertically => ((height - child_height) / 2.0).max(0.0),
70 VerticalAlignment::Bottom => (height - child_height).max(0.0),
71 };
72
73 placeable.place(x, y);
74 placements.push(Placement::new(placeable.node_id(), x, y, 0));
75 }
76
77 MeasureResult::new(crate::modifier::Size { width, height }, placements)
78 }
79
80 fn min_intrinsic_width(&self, measurables: &[Box<dyn Measurable>], height: f32) -> f32 {
81 measurables
82 .iter()
83 .map(|m| m.min_intrinsic_width(height))
84 .fold(0.0, f32::max)
85 }
86
87 fn max_intrinsic_width(&self, measurables: &[Box<dyn Measurable>], height: f32) -> f32 {
88 measurables
89 .iter()
90 .map(|m| m.max_intrinsic_width(height))
91 .fold(0.0, f32::max)
92 }
93
94 fn min_intrinsic_height(&self, measurables: &[Box<dyn Measurable>], width: f32) -> f32 {
95 measurables
96 .iter()
97 .map(|m| m.min_intrinsic_height(width))
98 .fold(0.0, f32::max)
99 }
100
101 fn max_intrinsic_height(&self, measurables: &[Box<dyn Measurable>], width: f32) -> f32 {
102 measurables
103 .iter()
104 .map(|m| m.max_intrinsic_height(width))
105 .fold(0.0, f32::max)
106 }
107}
108
109#[derive(Clone, Debug, PartialEq)]
149pub struct FlexMeasurePolicy {
150 pub axis: Axis,
152 pub main_axis_arrangement: LinearArrangement,
154 pub cross_axis_alignment: CrossAxisAlignment,
156}
157
158#[derive(Clone, Copy, Debug, PartialEq)]
161pub enum CrossAxisAlignment {
162 Start,
164 Center,
166 End,
168}
169
170impl CrossAxisAlignment {
171 fn align(&self, available: f32, child: f32) -> f32 {
173 match self {
174 CrossAxisAlignment::Start => 0.0,
175 CrossAxisAlignment::Center => ((available - child) / 2.0).max(0.0),
176 CrossAxisAlignment::End => (available - child).max(0.0),
177 }
178 }
179}
180
181impl From<HorizontalAlignment> for CrossAxisAlignment {
182 fn from(alignment: HorizontalAlignment) -> Self {
183 match alignment {
184 HorizontalAlignment::Start => CrossAxisAlignment::Start,
185 HorizontalAlignment::CenterHorizontally => CrossAxisAlignment::Center,
186 HorizontalAlignment::End => CrossAxisAlignment::End,
187 }
188 }
189}
190
191impl From<VerticalAlignment> for CrossAxisAlignment {
192 fn from(alignment: VerticalAlignment) -> Self {
193 match alignment {
194 VerticalAlignment::Top => CrossAxisAlignment::Start,
195 VerticalAlignment::CenterVertically => CrossAxisAlignment::Center,
196 VerticalAlignment::Bottom => CrossAxisAlignment::End,
197 }
198 }
199}
200
201impl FlexMeasurePolicy {
202 pub fn new(
203 axis: Axis,
204 main_axis_arrangement: LinearArrangement,
205 cross_axis_alignment: CrossAxisAlignment,
206 ) -> Self {
207 Self {
208 axis,
209 main_axis_arrangement,
210 cross_axis_alignment,
211 }
212 }
213
214 pub fn row(
216 horizontal_arrangement: LinearArrangement,
217 vertical_alignment: VerticalAlignment,
218 ) -> Self {
219 Self::new(
220 Axis::Horizontal,
221 horizontal_arrangement,
222 vertical_alignment.into(),
223 )
224 }
225
226 pub fn column(
228 vertical_arrangement: LinearArrangement,
229 horizontal_alignment: HorizontalAlignment,
230 ) -> Self {
231 Self::new(
232 Axis::Vertical,
233 vertical_arrangement,
234 horizontal_alignment.into(),
235 )
236 }
237
238 fn get_axis_constraints(&self, constraints: Constraints) -> (f32, f32, f32, f32) {
240 match self.axis {
241 Axis::Horizontal => (
242 constraints.min_width,
243 constraints.max_width,
244 constraints.min_height,
245 constraints.max_height,
246 ),
247 Axis::Vertical => (
248 constraints.min_height,
249 constraints.max_height,
250 constraints.min_width,
251 constraints.max_width,
252 ),
253 }
254 }
255
256 fn make_constraints(
258 &self,
259 min_main: f32,
260 max_main: f32,
261 min_cross: f32,
262 max_cross: f32,
263 ) -> Constraints {
264 match self.axis {
265 Axis::Horizontal => Constraints {
266 min_width: min_main,
267 max_width: max_main,
268 min_height: min_cross,
269 max_height: max_cross,
270 },
271 Axis::Vertical => Constraints {
272 min_width: min_cross,
273 max_width: max_cross,
274 min_height: min_main,
275 max_height: max_main,
276 },
277 }
278 }
279
280 fn get_main_axis_size(&self, width: f32, height: f32) -> f32 {
282 match self.axis {
283 Axis::Horizontal => width,
284 Axis::Vertical => height,
285 }
286 }
287
288 fn get_cross_axis_size(&self, width: f32, height: f32) -> f32 {
290 match self.axis {
291 Axis::Horizontal => height,
292 Axis::Vertical => width,
293 }
294 }
295
296 fn get_spacing(&self) -> f32 {
298 match self.main_axis_arrangement {
299 LinearArrangement::SpacedBy(value) => value.max(0.0),
300 _ => 0.0,
301 }
302 }
303}
304
305impl MeasurePolicy for FlexMeasurePolicy {
306 fn measure(
307 &self,
308 measurables: &[Box<dyn Measurable>],
309 constraints: Constraints,
310 ) -> MeasureResult {
311 if measurables.is_empty() {
312 let (width, height) = constraints.constrain(0.0, 0.0);
313 return MeasureResult::new(crate::modifier::Size { width, height }, vec![]);
314 }
315
316 let (min_main, max_main, min_cross, max_cross) = self.get_axis_constraints(constraints);
317 let main_axis_bounded = max_main.is_finite();
318 let spacing = self.get_spacing();
319
320 let mut fixed_children: SmallVec<[usize; 8]> = SmallVec::new();
322 let mut weighted_children: SmallVec<[(usize, FlexParentData); 8]> = SmallVec::new();
323
324 for (idx, measurable) in measurables.iter().enumerate() {
325 let parent_data = measurable.flex_parent_data().unwrap_or_default();
326 if parent_data.has_weight() {
327 weighted_children.push((idx, parent_data));
328 } else {
329 fixed_children.push(idx);
330 }
331 }
332
333 let child_constraints = self.make_constraints(0.0, max_main, 0.0, max_cross);
336
337 let mut placeables: SmallVec<[Option<cranpose_ui_layout::Placeable>; 8]> = SmallVec::new();
338 placeables.resize_with(measurables.len(), || None);
339 let mut fixed_main_size = 0.0_f32;
340 let mut max_cross_size = 0.0_f32;
341
342 for &idx in &fixed_children {
343 let measurable = &measurables[idx];
344 let placeable = measurable.measure(child_constraints);
345 let main_size = self.get_main_axis_size(placeable.width(), placeable.height());
346 let cross_size = self.get_cross_axis_size(placeable.width(), placeable.height());
347
348 fixed_main_size += main_size;
349 max_cross_size = max_cross_size.max(cross_size);
350 placeables[idx] = Some(placeable);
351 }
352
353 let num_children = measurables.len();
355 let total_spacing = if num_children > 1 {
356 spacing * (num_children - 1) as f32
357 } else {
358 0.0
359 };
360
361 if !weighted_children.is_empty() {
363 if main_axis_bounded {
364 let used_main = fixed_main_size + total_spacing;
366 let remaining_main = (max_main - used_main).max(0.0);
367
368 let total_weight: f32 = weighted_children.iter().map(|(_, data)| data.weight).sum();
370
371 for &(idx, parent_data) in &weighted_children {
373 let measurable = &measurables[idx];
374 let allocated = if total_weight > 0.0 {
375 remaining_main * (parent_data.weight / total_weight)
376 } else {
377 0.0
378 };
379
380 let weighted_constraints = if parent_data.fill {
381 self.make_constraints(allocated, allocated, 0.0, max_cross)
383 } else {
384 self.make_constraints(0.0, allocated, 0.0, max_cross)
386 };
387
388 let placeable = measurable.measure(weighted_constraints);
389 let cross_size =
390 self.get_cross_axis_size(placeable.width(), placeable.height());
391 max_cross_size = max_cross_size.max(cross_size);
392 placeables[idx] = Some(placeable);
393 }
394 } else {
395 for &(idx, _) in &weighted_children {
397 let measurable = &measurables[idx];
398 let placeable = measurable.measure(child_constraints);
399 let cross_size =
400 self.get_cross_axis_size(placeable.width(), placeable.height());
401 max_cross_size = max_cross_size.max(cross_size);
402 placeables[idx] = Some(placeable);
403 }
404 }
405 }
406
407 let placeables: SmallVec<[cranpose_ui_layout::Placeable; 8]> = placeables
409 .into_iter()
410 .map(|p| p.expect("placeable missing"))
411 .collect();
412
413 let total_main: f32 = placeables
415 .iter()
416 .map(|p| self.get_main_axis_size(p.width(), p.height()))
417 .sum::<f32>()
418 + total_spacing;
419
420 let container_main = total_main.clamp(min_main, max_main);
422 let container_cross = max_cross_size.clamp(min_cross, max_cross);
423
424 let child_main_sizes: SmallVec<[f32; 8]> = placeables
426 .iter()
427 .map(|p| self.get_main_axis_size(p.width(), p.height()))
428 .collect();
429
430 let mut main_positions: SmallVec<[f32; 8]> =
431 SmallVec::with_capacity(child_main_sizes.len());
432 main_positions.resize(child_main_sizes.len(), 0.0);
433
434 let arrangement = if total_main > container_main {
436 LinearArrangement::Start
437 } else {
438 self.main_axis_arrangement
439 };
440 arrangement.arrange(container_main, &child_main_sizes, &mut main_positions);
441
442 let mut placements: SmallVec<[Placement; 8]> = SmallVec::with_capacity(placeables.len());
444 for (placeable, main_pos) in placeables.into_iter().zip(main_positions.into_iter()) {
445 let child_cross = self.get_cross_axis_size(placeable.width(), placeable.height());
446 let cross_pos = self
447 .cross_axis_alignment
448 .align(container_cross, child_cross);
449
450 let (x, y) = match self.axis {
451 Axis::Horizontal => (main_pos, cross_pos),
452 Axis::Vertical => (cross_pos, main_pos),
453 };
454
455 placeable.place(x, y);
456 placements.push(Placement::new(placeable.node_id(), x, y, 0));
457 }
458
459 let (width, height) = match self.axis {
461 Axis::Horizontal => (container_main, container_cross),
462 Axis::Vertical => (container_cross, container_main),
463 };
464
465 MeasureResult::new(
466 crate::modifier::Size { width, height },
467 placements.into_vec(),
468 )
469 }
470
471 fn min_intrinsic_width(&self, measurables: &[Box<dyn Measurable>], height: f32) -> f32 {
472 let spacing = self.get_spacing();
473 let total_spacing = if measurables.len() > 1 {
474 spacing * (measurables.len() - 1) as f32
475 } else {
476 0.0
477 };
478
479 match self.axis {
480 Axis::Horizontal => {
481 measurables
483 .iter()
484 .map(|m| m.min_intrinsic_width(height))
485 .sum::<f32>()
486 + total_spacing
487 }
488 Axis::Vertical => {
489 measurables
491 .iter()
492 .map(|m| m.min_intrinsic_width(height))
493 .fold(0.0, f32::max)
494 }
495 }
496 }
497
498 fn max_intrinsic_width(&self, measurables: &[Box<dyn Measurable>], height: f32) -> f32 {
499 let spacing = self.get_spacing();
500 let total_spacing = if measurables.len() > 1 {
501 spacing * (measurables.len() - 1) as f32
502 } else {
503 0.0
504 };
505
506 match self.axis {
507 Axis::Horizontal => {
508 measurables
510 .iter()
511 .map(|m| m.max_intrinsic_width(height))
512 .sum::<f32>()
513 + total_spacing
514 }
515 Axis::Vertical => {
516 measurables
518 .iter()
519 .map(|m| m.max_intrinsic_width(height))
520 .fold(0.0, f32::max)
521 }
522 }
523 }
524
525 fn min_intrinsic_height(&self, measurables: &[Box<dyn Measurable>], width: f32) -> f32 {
526 let spacing = self.get_spacing();
527 let total_spacing = if measurables.len() > 1 {
528 spacing * (measurables.len() - 1) as f32
529 } else {
530 0.0
531 };
532
533 match self.axis {
534 Axis::Horizontal => {
535 measurables
537 .iter()
538 .map(|m| m.min_intrinsic_height(width))
539 .fold(0.0, f32::max)
540 }
541 Axis::Vertical => {
542 measurables
544 .iter()
545 .map(|m| m.min_intrinsic_height(width))
546 .sum::<f32>()
547 + total_spacing
548 }
549 }
550 }
551
552 fn max_intrinsic_height(&self, measurables: &[Box<dyn Measurable>], width: f32) -> f32 {
553 let spacing = self.get_spacing();
554 let total_spacing = if measurables.len() > 1 {
555 spacing * (measurables.len() - 1) as f32
556 } else {
557 0.0
558 };
559
560 match self.axis {
561 Axis::Horizontal => {
562 measurables
564 .iter()
565 .map(|m| m.max_intrinsic_height(width))
566 .fold(0.0, f32::max)
567 }
568 Axis::Vertical => {
569 measurables
571 .iter()
572 .map(|m| m.max_intrinsic_height(width))
573 .sum::<f32>()
574 + total_spacing
575 }
576 }
577 }
578}
579
580#[derive(Clone, Debug, PartialEq)]
583pub struct LeafMeasurePolicy {
584 pub intrinsic_size: crate::modifier::Size,
585}
586
587impl LeafMeasurePolicy {
588 pub fn new(intrinsic_size: crate::modifier::Size) -> Self {
589 Self { intrinsic_size }
590 }
591}
592
593impl MeasurePolicy for LeafMeasurePolicy {
594 fn measure(
595 &self,
596 _measurables: &[Box<dyn Measurable>],
597 constraints: Constraints,
598 ) -> MeasureResult {
599 let (width, height) =
601 constraints.constrain(self.intrinsic_size.width, self.intrinsic_size.height);
602
603 MeasureResult::new(
604 crate::modifier::Size { width, height },
605 vec![], )
607 }
608
609 fn min_intrinsic_width(&self, _measurables: &[Box<dyn Measurable>], _height: f32) -> f32 {
610 self.intrinsic_size.width
611 }
612
613 fn max_intrinsic_width(&self, _measurables: &[Box<dyn Measurable>], _height: f32) -> f32 {
614 self.intrinsic_size.width
615 }
616
617 fn min_intrinsic_height(&self, _measurables: &[Box<dyn Measurable>], _width: f32) -> f32 {
618 self.intrinsic_size.height
619 }
620
621 fn max_intrinsic_height(&self, _measurables: &[Box<dyn Measurable>], _width: f32) -> f32 {
622 self.intrinsic_size.height
623 }
624}
625
626#[derive(Clone, Debug, PartialEq)]
632pub struct EmptyMeasurePolicy;
633
634impl EmptyMeasurePolicy {
635 pub fn new() -> Self {
636 Self
637 }
638}
639
640impl Default for EmptyMeasurePolicy {
641 fn default() -> Self {
642 Self::new()
643 }
644}
645
646impl MeasurePolicy for EmptyMeasurePolicy {
647 fn measure(
648 &self,
649 _measurables: &[Box<dyn Measurable>],
650 constraints: Constraints,
651 ) -> MeasureResult {
652 let (width, height) = constraints.constrain(0.0, 0.0);
655
656 MeasureResult::new(
657 crate::modifier::Size { width, height },
658 vec![], )
660 }
661
662 fn min_intrinsic_width(&self, _measurables: &[Box<dyn Measurable>], _height: f32) -> f32 {
663 0.0
664 }
665
666 fn max_intrinsic_width(&self, _measurables: &[Box<dyn Measurable>], _height: f32) -> f32 {
667 0.0
668 }
669
670 fn min_intrinsic_height(&self, _measurables: &[Box<dyn Measurable>], _width: f32) -> f32 {
671 0.0
672 }
673
674 fn max_intrinsic_height(&self, _measurables: &[Box<dyn Measurable>], _width: f32) -> f32 {
675 0.0
676 }
677}
678
679#[cfg(test)]
680#[path = "tests/policies_tests.rs"]
681mod tests;