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<Box<dyn cranpose_ui_layout::Placeable>>; 8]> =
338 SmallVec::new();
339 placeables.resize_with(measurables.len(), || None);
340 let mut fixed_main_size = 0.0_f32;
341 let mut max_cross_size = 0.0_f32;
342
343 for &idx in &fixed_children {
344 let measurable = &measurables[idx];
345 let placeable = measurable.measure(child_constraints);
346 let main_size = self.get_main_axis_size(placeable.width(), placeable.height());
347 let cross_size = self.get_cross_axis_size(placeable.width(), placeable.height());
348
349 fixed_main_size += main_size;
350 max_cross_size = max_cross_size.max(cross_size);
351 placeables[idx] = Some(placeable);
352 }
353
354 let num_children = measurables.len();
356 let total_spacing = if num_children > 1 {
357 spacing * (num_children - 1) as f32
358 } else {
359 0.0
360 };
361
362 if !weighted_children.is_empty() {
364 if main_axis_bounded {
365 let used_main = fixed_main_size + total_spacing;
367 let remaining_main = (max_main - used_main).max(0.0);
368
369 let total_weight: f32 = weighted_children.iter().map(|(_, data)| data.weight).sum();
371
372 for &(idx, parent_data) in &weighted_children {
374 let measurable = &measurables[idx];
375 let allocated = if total_weight > 0.0 {
376 remaining_main * (parent_data.weight / total_weight)
377 } else {
378 0.0
379 };
380
381 let weighted_constraints = if parent_data.fill {
382 self.make_constraints(allocated, allocated, 0.0, max_cross)
384 } else {
385 self.make_constraints(0.0, allocated, 0.0, max_cross)
387 };
388
389 let placeable = measurable.measure(weighted_constraints);
390 let cross_size =
391 self.get_cross_axis_size(placeable.width(), placeable.height());
392 max_cross_size = max_cross_size.max(cross_size);
393 placeables[idx] = Some(placeable);
394 }
395 } else {
396 for &(idx, _) in &weighted_children {
398 let measurable = &measurables[idx];
399 let placeable = measurable.measure(child_constraints);
400 let cross_size =
401 self.get_cross_axis_size(placeable.width(), placeable.height());
402 max_cross_size = max_cross_size.max(cross_size);
403 placeables[idx] = Some(placeable);
404 }
405 }
406 }
407
408 let placeables: SmallVec<[Box<dyn cranpose_ui_layout::Placeable>; 8]> = placeables
410 .into_iter()
411 .map(|p| p.expect("placeable missing"))
412 .collect();
413
414 let total_main: f32 = placeables
416 .iter()
417 .map(|p| self.get_main_axis_size(p.width(), p.height()))
418 .sum::<f32>()
419 + total_spacing;
420
421 let container_main = total_main.clamp(min_main, max_main);
423 let container_cross = max_cross_size.clamp(min_cross, max_cross);
424
425 let child_main_sizes: SmallVec<[f32; 8]> = placeables
427 .iter()
428 .map(|p| self.get_main_axis_size(p.width(), p.height()))
429 .collect();
430
431 let mut main_positions: SmallVec<[f32; 8]> =
432 SmallVec::with_capacity(child_main_sizes.len());
433 main_positions.resize(child_main_sizes.len(), 0.0);
434
435 let arrangement = if total_main > container_main {
437 LinearArrangement::Start
438 } else {
439 self.main_axis_arrangement
440 };
441 arrangement.arrange(container_main, &child_main_sizes, &mut main_positions);
442
443 let mut placements: SmallVec<[Placement; 8]> = SmallVec::with_capacity(placeables.len());
445 for (placeable, main_pos) in placeables.into_iter().zip(main_positions.into_iter()) {
446 let child_cross = self.get_cross_axis_size(placeable.width(), placeable.height());
447 let cross_pos = self
448 .cross_axis_alignment
449 .align(container_cross, child_cross);
450
451 let (x, y) = match self.axis {
452 Axis::Horizontal => (main_pos, cross_pos),
453 Axis::Vertical => (cross_pos, main_pos),
454 };
455
456 placeable.place(x, y);
457 placements.push(Placement::new(placeable.node_id(), x, y, 0));
458 }
459
460 let (width, height) = match self.axis {
462 Axis::Horizontal => (container_main, container_cross),
463 Axis::Vertical => (container_cross, container_main),
464 };
465
466 MeasureResult::new(
467 crate::modifier::Size { width, height },
468 placements.into_vec(),
469 )
470 }
471
472 fn min_intrinsic_width(&self, measurables: &[Box<dyn Measurable>], height: f32) -> f32 {
473 let spacing = self.get_spacing();
474 let total_spacing = if measurables.len() > 1 {
475 spacing * (measurables.len() - 1) as f32
476 } else {
477 0.0
478 };
479
480 match self.axis {
481 Axis::Horizontal => {
482 measurables
484 .iter()
485 .map(|m| m.min_intrinsic_width(height))
486 .sum::<f32>()
487 + total_spacing
488 }
489 Axis::Vertical => {
490 measurables
492 .iter()
493 .map(|m| m.min_intrinsic_width(height))
494 .fold(0.0, f32::max)
495 }
496 }
497 }
498
499 fn max_intrinsic_width(&self, measurables: &[Box<dyn Measurable>], height: f32) -> f32 {
500 let spacing = self.get_spacing();
501 let total_spacing = if measurables.len() > 1 {
502 spacing * (measurables.len() - 1) as f32
503 } else {
504 0.0
505 };
506
507 match self.axis {
508 Axis::Horizontal => {
509 measurables
511 .iter()
512 .map(|m| m.max_intrinsic_width(height))
513 .sum::<f32>()
514 + total_spacing
515 }
516 Axis::Vertical => {
517 measurables
519 .iter()
520 .map(|m| m.max_intrinsic_width(height))
521 .fold(0.0, f32::max)
522 }
523 }
524 }
525
526 fn min_intrinsic_height(&self, measurables: &[Box<dyn Measurable>], width: f32) -> f32 {
527 let spacing = self.get_spacing();
528 let total_spacing = if measurables.len() > 1 {
529 spacing * (measurables.len() - 1) as f32
530 } else {
531 0.0
532 };
533
534 match self.axis {
535 Axis::Horizontal => {
536 measurables
538 .iter()
539 .map(|m| m.min_intrinsic_height(width))
540 .fold(0.0, f32::max)
541 }
542 Axis::Vertical => {
543 measurables
545 .iter()
546 .map(|m| m.min_intrinsic_height(width))
547 .sum::<f32>()
548 + total_spacing
549 }
550 }
551 }
552
553 fn max_intrinsic_height(&self, measurables: &[Box<dyn Measurable>], width: f32) -> f32 {
554 let spacing = self.get_spacing();
555 let total_spacing = if measurables.len() > 1 {
556 spacing * (measurables.len() - 1) as f32
557 } else {
558 0.0
559 };
560
561 match self.axis {
562 Axis::Horizontal => {
563 measurables
565 .iter()
566 .map(|m| m.max_intrinsic_height(width))
567 .fold(0.0, f32::max)
568 }
569 Axis::Vertical => {
570 measurables
572 .iter()
573 .map(|m| m.max_intrinsic_height(width))
574 .sum::<f32>()
575 + total_spacing
576 }
577 }
578 }
579}
580
581#[derive(Clone, Debug, PartialEq)]
584pub struct LeafMeasurePolicy {
585 pub intrinsic_size: crate::modifier::Size,
586}
587
588impl LeafMeasurePolicy {
589 pub fn new(intrinsic_size: crate::modifier::Size) -> Self {
590 Self { intrinsic_size }
591 }
592}
593
594impl MeasurePolicy for LeafMeasurePolicy {
595 fn measure(
596 &self,
597 _measurables: &[Box<dyn Measurable>],
598 constraints: Constraints,
599 ) -> MeasureResult {
600 let (width, height) =
602 constraints.constrain(self.intrinsic_size.width, self.intrinsic_size.height);
603
604 MeasureResult::new(
605 crate::modifier::Size { width, height },
606 vec![], )
608 }
609
610 fn min_intrinsic_width(&self, _measurables: &[Box<dyn Measurable>], _height: f32) -> f32 {
611 self.intrinsic_size.width
612 }
613
614 fn max_intrinsic_width(&self, _measurables: &[Box<dyn Measurable>], _height: f32) -> f32 {
615 self.intrinsic_size.width
616 }
617
618 fn min_intrinsic_height(&self, _measurables: &[Box<dyn Measurable>], _width: f32) -> f32 {
619 self.intrinsic_size.height
620 }
621
622 fn max_intrinsic_height(&self, _measurables: &[Box<dyn Measurable>], _width: f32) -> f32 {
623 self.intrinsic_size.height
624 }
625}
626
627#[derive(Clone, Debug, PartialEq)]
633pub struct EmptyMeasurePolicy;
634
635impl EmptyMeasurePolicy {
636 pub fn new() -> Self {
637 Self
638 }
639}
640
641impl Default for EmptyMeasurePolicy {
642 fn default() -> Self {
643 Self::new()
644 }
645}
646
647impl MeasurePolicy for EmptyMeasurePolicy {
648 fn measure(
649 &self,
650 _measurables: &[Box<dyn Measurable>],
651 constraints: Constraints,
652 ) -> MeasureResult {
653 let (width, height) = constraints.constrain(0.0, 0.0);
656
657 MeasureResult::new(
658 crate::modifier::Size { width, height },
659 vec![], )
661 }
662
663 fn min_intrinsic_width(&self, _measurables: &[Box<dyn Measurable>], _height: f32) -> f32 {
664 0.0
665 }
666
667 fn max_intrinsic_width(&self, _measurables: &[Box<dyn Measurable>], _height: f32) -> f32 {
668 0.0
669 }
670
671 fn min_intrinsic_height(&self, _measurables: &[Box<dyn Measurable>], _width: f32) -> f32 {
672 0.0
673 }
674
675 fn max_intrinsic_height(&self, _measurables: &[Box<dyn Measurable>], _width: f32) -> f32 {
676 0.0
677 }
678}
679
680#[cfg(test)]
681#[path = "tests/policies_tests.rs"]
682mod tests;