1use std::cell::RefCell;
2
3use cranpose_ui_graphics::whole;
4use cranpose_ui_layout::{
5 Axis, Constraints, MeasurePolicy, MeasureResult, MeasureScope, ParentData, Placement,
6 bias_offset, round_to_px,
7};
8use smallvec::SmallVec;
9
10use super::empty_measure_policy::empty_layout_hold;
11use crate::{
12 layout::core::{
13 Arrangement, HorizontalAlignment, LinearArrangement, Measurable, VerticalAlignment,
14 },
15 thread_state,
16};
17
18#[derive(Clone, Debug, PartialEq)]
56pub struct FlexMeasurePolicy {
57 pub axis: Axis,
59 pub main_axis_arrangement: LinearArrangement,
61 pub cross_axis_alignment: CrossAxisAlignment,
63 pub density: f32,
66 pub layout_direction: crate::LayoutDirection,
68}
69
70pub(crate) struct WeightShares {
74 density: f32,
75 unit_px: f32,
76 remainder_px: f32,
77}
78
79impl WeightShares {
80 pub(crate) fn new(
81 remaining: f32,
82 weights: impl Iterator<Item = f32> + Clone,
83 density: f32,
84 ) -> Self {
85 let density = if density > 0.0 && density.is_finite() {
86 density
87 } else {
88 1.0
89 };
90 let total_weight: f32 = weights.clone().sum();
91 let remaining_px = whole::round(remaining * density);
92 let unit_px = if total_weight > 0.0 {
93 remaining_px / total_weight
94 } else {
95 0.0
96 };
97 let rounded_px: f32 = weights
98 .map(|weight| whole::floor(unit_px * weight + 0.5))
99 .sum();
100 Self {
101 density,
102 unit_px,
103 remainder_px: remaining_px - rounded_px,
104 }
105 }
106
107 pub(crate) fn next_share(&mut self, weight: f32) -> f32 {
109 let step = if self.remainder_px > 0.0 {
110 1.0
111 } else if self.remainder_px < 0.0 {
112 -1.0
113 } else {
114 0.0
115 };
116 self.remainder_px -= step;
117 (whole::floor(self.unit_px * weight + 0.5) + step).max(0.0) / self.density
118 }
119}
120
121#[derive(Clone, Copy, Debug, PartialEq)]
124pub enum CrossAxisAlignment {
125 Start,
127 Center,
129 End,
131}
132
133impl CrossAxisAlignment {
134 fn align(&self, available: f32, child: f32, density: f32) -> f32 {
136 let bias = match self {
137 Self::Start => -1.0,
138 Self::Center => 0.0,
139 Self::End => 1.0,
140 };
141 bias_offset(bias, available, child, density)
142 }
143}
144
145impl From<HorizontalAlignment> for CrossAxisAlignment {
146 fn from(alignment: HorizontalAlignment) -> Self {
147 match alignment {
148 HorizontalAlignment::Start => Self::Start,
149 HorizontalAlignment::CenterHorizontally => Self::Center,
150 HorizontalAlignment::End => Self::End,
151 }
152 }
153}
154
155impl From<VerticalAlignment> for CrossAxisAlignment {
156 fn from(alignment: VerticalAlignment) -> Self {
157 match alignment {
158 VerticalAlignment::Top => Self::Start,
159 VerticalAlignment::CenterVertically => Self::Center,
160 VerticalAlignment::Bottom => Self::End,
161 }
162 }
163}
164
165impl FlexMeasurePolicy {
166 pub fn new(
168 axis: Axis,
169 main_axis_arrangement: LinearArrangement,
170 cross_axis_alignment: CrossAxisAlignment,
171 density: f32,
172 ) -> Self {
173 Self {
174 axis,
175 main_axis_arrangement,
176 cross_axis_alignment,
177 density,
178 layout_direction: crate::LayoutDirection::Ltr,
179 }
180 }
181
182 pub fn row(
184 horizontal_arrangement: LinearArrangement,
185 vertical_alignment: VerticalAlignment,
186 density: f32,
187 ) -> Self {
188 Self::new(
189 Axis::Horizontal,
190 horizontal_arrangement,
191 vertical_alignment.into(),
192 density,
193 )
194 }
195
196 pub fn column(
198 vertical_arrangement: LinearArrangement,
199 horizontal_alignment: HorizontalAlignment,
200 density: f32,
201 ) -> Self {
202 Self::new(
203 Axis::Vertical,
204 vertical_arrangement,
205 horizontal_alignment.into(),
206 density,
207 )
208 }
209
210 fn get_axis_constraints(&self, constraints: Constraints) -> (f32, f32, f32, f32) {
211 match self.axis {
212 Axis::Horizontal => (
213 constraints.min_width,
214 constraints.max_width,
215 constraints.min_height,
216 constraints.max_height,
217 ),
218 Axis::Vertical => (
219 constraints.min_height,
220 constraints.max_height,
221 constraints.min_width,
222 constraints.max_width,
223 ),
224 }
225 }
226
227 fn make_constraints(
228 &self,
229 min_main: f32,
230 max_main: f32,
231 min_cross: f32,
232 max_cross: f32,
233 ) -> Constraints {
234 match self.axis {
235 Axis::Horizontal => Constraints {
236 min_width: min_main,
237 max_width: max_main,
238 min_height: min_cross,
239 max_height: max_cross,
240 },
241 Axis::Vertical => Constraints {
242 min_width: min_cross,
243 max_width: max_cross,
244 min_height: min_main,
245 max_height: max_main,
246 },
247 }
248 }
249
250 fn get_main_axis_size(&self, width: f32, height: f32) -> f32 {
251 match self.axis {
252 Axis::Horizontal => width,
253 Axis::Vertical => height,
254 }
255 }
256
257 fn get_cross_axis_size(&self, width: f32, height: f32) -> f32 {
258 match self.axis {
259 Axis::Horizontal => height,
260 Axis::Vertical => width,
261 }
262 }
263
264 fn measure_fixed_children(
270 &self,
271 measurables: &[Box<dyn Measurable>],
272 children: &mut [FlexChild],
273 (max_main, max_cross): (f32, f32),
274 spacing: f32,
275 ) -> (f32, f32) {
276 let main_axis_bounded = max_main.is_finite();
277 let mut fixed_space = 0.0_f32;
278 let mut max_cross_size = 0.0_f32;
279 for (child, measurable) in children.iter_mut().zip(measurables) {
280 if child.data.has_weight() {
281 continue;
282 }
283 let available_main = if main_axis_bounded {
284 (max_main - fixed_space).max(0.0)
285 } else {
286 max_main
287 };
288 let placeable =
289 measurable.measure(self.make_constraints(0.0, available_main, 0.0, max_cross));
290 let main_size = self.get_main_axis_size(placeable.width(), placeable.height());
291 let spacing_after = if main_axis_bounded {
292 spacing.min(available_main - main_size).max(0.0)
293 } else {
294 spacing
295 };
296 fixed_space += main_size + spacing_after;
297 max_cross_size =
298 max_cross_size.max(self.get_cross_axis_size(placeable.width(), placeable.height()));
299 child.placeable = Some(placeable);
300 }
301 (fixed_space, max_cross_size)
302 }
303
304 fn intrinsic_main_size(
308 &self,
309 measurables: &[Box<dyn Measurable>],
310 main_size: impl Fn(&dyn Measurable) -> f32,
311 ) -> f32 {
312 if measurables.is_empty() {
313 return 0.0;
314 }
315 let mut fixed_space = 0.0_f32;
316 let mut weight_unit = 0.0_f32;
317 let mut total_weight = 0.0_f32;
318 for measurable in measurables {
319 let size = main_size(measurable.as_ref());
320 match child_weight(measurable.as_ref()) {
321 Some(weight) => {
322 total_weight += weight;
323 weight_unit = weight_unit.max(round_to_px(size / weight, self.density));
324 }
325 None => fixed_space += size,
326 }
327 }
328 round_to_px(weight_unit * total_weight, self.density)
329 + fixed_space
330 + self.get_spacing() * (measurables.len() - 1) as f32
331 }
332
333 fn intrinsic_cross_size(
339 &self,
340 measurables: &[Box<dyn Measurable>],
341 main_available: f32,
342 main_size: impl Fn(&dyn Measurable, f32) -> f32,
343 cross_size: impl Fn(&dyn Measurable, f32) -> f32,
344 ) -> f32 {
345 if measurables.is_empty() {
346 return 0.0;
347 }
348 let mut fixed_space =
349 (self.get_spacing() * (measurables.len() - 1) as f32).min(main_available);
350 let mut cross = 0.0_f32;
351 let mut total_weight = 0.0_f32;
352 for measurable in measurables {
353 let measurable = measurable.as_ref();
354 if let Some(weight) = child_weight(measurable) {
355 total_weight += weight;
356 continue;
357 }
358 let space =
359 main_size(measurable, f32::INFINITY).min((main_available - fixed_space).max(0.0));
360 fixed_space += space;
361 cross = cross.max(cross_size(measurable, space));
362 }
363 if total_weight == 0.0 {
364 return cross;
365 }
366 let weight_unit = if main_available.is_finite() {
367 round_to_px(
368 (main_available - fixed_space).max(0.0) / total_weight,
369 self.density,
370 )
371 } else {
372 f32::INFINITY
373 };
374 for measurable in measurables {
375 let measurable = measurable.as_ref();
376 if let Some(weight) = child_weight(measurable) {
377 cross = cross.max(cross_size(
378 measurable,
379 round_to_px(weight_unit * weight, self.density),
380 ));
381 }
382 }
383 cross
384 }
385
386 fn get_spacing(&self) -> f32 {
387 self.main_axis_arrangement.spacing(self.density)
388 }
389
390 fn measure_weighted_children(
391 &self,
392 measurables: &[Box<dyn Measurable>],
393 children: &mut [FlexChild],
394 (max_main, max_cross): (f32, f32),
395 (fixed_space, spacing): (f32, f32),
396 ) -> f32 {
397 let weights = children
398 .iter()
399 .filter(|child| child.data.has_weight())
400 .map(|child| child.data.weight);
401 let weighted = weights.clone().count();
402 if weighted == 0 {
403 return 0.0;
404 }
405 let remaining_main = (max_main - fixed_space - spacing * (weighted - 1) as f32).max(0.0);
406 let mut shares = WeightShares::new(remaining_main, weights, self.density);
407 let mut max_cross_size = 0.0_f32;
408 for (child, measurable) in children.iter_mut().zip(measurables) {
409 let data = &child.data;
410 if !data.has_weight() {
411 continue;
412 }
413 let constraints = if max_main.is_finite() {
414 let allocated = shares.next_share(data.weight);
415 let minimum = if data.fill { allocated } else { 0.0 };
416 self.make_constraints(minimum, allocated, 0.0, max_cross)
417 } else {
418 self.make_constraints(0.0, max_main, 0.0, max_cross)
419 };
420 let placeable = measurable.measure(constraints);
421 max_cross_size =
422 max_cross_size.max(self.get_cross_axis_size(placeable.width(), placeable.height()));
423 child.placeable = Some(placeable);
424 }
425 max_cross_size
426 }
427
428 fn measured_extents(&self, children: &[FlexChild]) -> (f32, f32, f32) {
429 let mut main = 0.0;
430 let mut before = 0.0_f32;
431 let mut after = 0.0_f32;
432 for (placeable, data) in measured(children) {
433 main += self.get_main_axis_size(placeable.width(), placeable.height());
434 if self.axis == Axis::Horizontal
435 && data.row_baseline
436 && let Some(baseline) = placeable.alignment_lines().first_baseline()
437 {
438 before = before.max(baseline);
439 after = after.max(placeable.height() - baseline);
440 }
441 }
442 (main, before, after)
443 }
444
445 fn cross_axis_position(
446 &self,
447 placeable: &cranpose_ui_layout::Placeable,
448 parent_data: &ParentData,
449 container_cross: f32,
450 before_baseline: f32,
451 ) -> f32 {
452 if self.axis == Axis::Horizontal && parent_data.row_baseline {
453 return placeable
454 .alignment_lines()
455 .first_baseline()
456 .map_or(0.0, |baseline| before_baseline - baseline);
457 }
458 let alignment = match self.axis {
459 Axis::Horizontal => parent_data
460 .row_alignment
461 .map_or(self.cross_axis_alignment, Into::into),
462 Axis::Vertical => parent_data
463 .column_alignment
464 .map_or(self.cross_axis_alignment, Into::into),
465 };
466 let child_cross = self.get_cross_axis_size(placeable.width(), placeable.height());
467 alignment.align(container_cross, child_cross, self.density)
468 }
469}
470
471fn child_main_range(
475 child_max: cranpose_ui_layout::BoundRange,
476 max_main: f32,
477 fixed_space: f32,
478 child_main: f32,
479 spacing: f32,
480) -> Option<cranpose_ui_layout::BoundRange> {
481 use cranpose_ui_layout::BoundRange;
482 if !max_main.is_finite() {
483 return child_max
484 .contains(f32::INFINITY)
485 .then_some(BoundRange::exactly(f32::INFINITY));
486 }
487 if (max_main - fixed_space).max(0.0) - child_main < spacing {
488 return None;
489 }
490 child_max
491 .outset(fixed_space)
492 .intersect(BoundRange::from(fixed_space + child_main + spacing))
493}
494
495fn child_weight(measurable: &dyn Measurable) -> Option<f32> {
497 measurable
498 .flex_parent_data()
499 .map(|data| data.weight)
500 .filter(|weight| *weight > 0.0)
501}
502
503struct FlexChild {
506 data: ParentData,
507 placeable: Option<cranpose_ui_layout::Placeable>,
508}
509
510pub(crate) struct SpareFlexChildren(RefCell<Vec<Vec<FlexChild>>>);
514
515const SPARE_LISTS: usize = 32;
518
519impl SpareFlexChildren {
520 pub(crate) const fn new() -> Self {
521 Self(RefCell::new(Vec::new()))
522 }
523
524 fn take() -> Vec<FlexChild> {
525 thread_state::with(|state| state.spare_flex_children.0.borrow_mut().pop())
526 .unwrap_or_default()
527 }
528
529 fn give(mut children: Vec<FlexChild>) {
530 children.clear();
531 thread_state::with(|state| {
532 let mut spares = state.spare_flex_children.0.borrow_mut();
533 if spares.len() < SPARE_LISTS {
534 spares.push(children);
535 }
536 });
537 }
538}
539
540fn measured(
543 children: &[FlexChild],
544) -> impl Iterator<Item = (&cranpose_ui_layout::Placeable, &ParentData)> {
545 children
546 .iter()
547 .filter_map(|child| Some((child.placeable.as_ref()?, &child.data)))
548}
549
550impl MeasurePolicy for FlexMeasurePolicy {
551 fn measure(
552 &self,
553 scope: &dyn MeasureScope,
554 measurables: &[Box<dyn Measurable>],
555 constraints: Constraints,
556 ) -> MeasureResult {
557 let mut placements = Vec::new();
558 let measurement = self.measure_into(scope, measurables, constraints, &mut placements);
559 MeasureResult::new(measurement, placements)
560 }
561
562 fn measure_hold(
566 &self,
567 measurables: &[Box<dyn Measurable>],
568 constraints: Constraints,
569 size: crate::modifier::Size,
570 ) -> Option<cranpose_ui_layout::ConstraintsHold> {
571 use cranpose_ui_layout::{AxisHold, BoundRange, ConstraintsHold};
572 if measurables.is_empty() {
573 return empty_layout_hold(constraints, size);
574 }
575 if measurables
576 .iter()
577 .any(|measurable| measurable.parent_data().has_weight())
578 {
579 return None;
580 }
581 let (_, max_main, _, _) = self.get_axis_constraints(constraints);
582 let spacing = self.get_spacing();
583 let mut main = BoundRange::ANY;
584 let mut cross = BoundRange::ANY;
585 let mut fixed_space = 0.0_f32;
586 let mut total_main = 0.0_f32;
587 let mut cross_size = 0.0_f32;
588 for measurable in measurables {
589 let (child, hold) = measurable.measured_hold()?;
590 let (main_hold, cross_hold) = match self.axis {
591 Axis::Horizontal => (hold.width, hold.height),
592 Axis::Vertical => (hold.height, hold.width),
593 };
594 let child_main = self.get_main_axis_size(child.width, child.height);
595 main = main.intersect(child_main_range(
596 main_hold.max,
597 max_main,
598 fixed_space,
599 child_main,
600 spacing,
601 )?)?;
602 cross = cross.intersect(cross_hold.max)?;
603 fixed_space += child_main + spacing;
604 total_main += child_main;
605 cross_size = cross_size.max(self.get_cross_axis_size(child.width, child.height));
606 }
607 total_main += spacing * (measurables.len() - 1) as f32;
608 let size_main = self.get_main_axis_size(size.width, size.height);
609 let size_cross = self.get_cross_axis_size(size.width, size.height);
610 if size_main != total_main || size_cross != cross_size {
612 return None;
613 }
614 let main = AxisHold {
615 min: BoundRange::up_to(size_main),
616 max: main.intersect(BoundRange::from(size_main))?,
617 };
618 let cross = AxisHold {
619 min: BoundRange::up_to(size_cross),
620 max: cross.intersect(BoundRange::from(size_cross))?,
621 };
622 Some(match self.axis {
623 Axis::Horizontal => ConstraintsHold {
624 width: main,
625 height: cross,
626 },
627 Axis::Vertical => ConstraintsHold {
628 width: cross,
629 height: main,
630 },
631 })
632 }
633
634 fn measure_into(
635 &self,
636 _scope: &dyn MeasureScope,
637 measurables: &[Box<dyn Measurable>],
638 constraints: Constraints,
639 placements: &mut Vec<Placement>,
640 ) -> cranpose_ui_layout::Measurement {
641 placements.clear();
642 if measurables.is_empty() {
643 let (width, height) = constraints.constrain(0.0, 0.0);
644 return crate::modifier::Size { width, height }.into();
645 }
646
647 let (min_main, max_main, min_cross, max_cross) = self.get_axis_constraints(constraints);
648 let spacing = self.get_spacing();
649
650 let mut children = SpareFlexChildren::take();
651 children.extend(measurables.iter().map(|child| FlexChild {
652 data: child.parent_data(),
653 placeable: None,
654 }));
655 let (fixed_space, mut max_cross_size) =
656 self.measure_fixed_children(measurables, &mut children, (max_main, max_cross), spacing);
657
658 let num_children = measurables.len();
659 let total_spacing = if num_children > 1 {
660 spacing * (num_children - 1) as f32
661 } else {
662 0.0
663 };
664
665 max_cross_size = max_cross_size.max(self.measure_weighted_children(
666 measurables,
667 &mut children,
668 (max_main, max_cross),
669 (fixed_space, spacing),
670 ));
671
672 let (total_main, before_baseline, after_baseline) = self.measured_extents(&children);
673 let total_main = total_main + total_spacing;
674 max_cross_size = max_cross_size.max(before_baseline + after_baseline);
675
676 let container_main = total_main.clamp(min_main, max_main);
677 let container_cross = max_cross_size.clamp(min_cross, max_cross);
678
679 let child_main_sizes: SmallVec<[f32; 8]> = measured(&children)
680 .map(|(p, _)| self.get_main_axis_size(p.width(), p.height()))
681 .collect();
682
683 let mut main_positions: SmallVec<[f32; 8]> =
684 SmallVec::with_capacity(child_main_sizes.len());
685 main_positions.resize(child_main_sizes.len(), 0.0);
686
687 let arrangement = if total_main > container_main && !self.main_axis_arrangement.is_spaced()
688 {
689 LinearArrangement::Start
690 } else {
691 self.main_axis_arrangement
692 };
693 arrangement.arrange(
694 self.density,
695 container_main,
696 &child_main_sizes,
697 &mut main_positions,
698 );
699
700 placements.reserve(children.len());
701 for ((placeable, data), &main_pos) in measured(&children).zip(&main_positions) {
702 let cross_pos =
703 self.cross_axis_position(placeable, data, container_cross, before_baseline);
704
705 let (x, y) = match self.axis {
706 Axis::Horizontal => (main_pos, cross_pos),
707 Axis::Vertical => (cross_pos, main_pos),
708 };
709 let width = match self.axis {
710 Axis::Horizontal => container_main,
711 Axis::Vertical => container_cross,
712 };
713 let x = self.layout_direction.place_x(x, width, placeable.width());
714
715 placeable.place(x, y);
716 placements.push(Placement::new(placeable.node_id(), x, y, 0.0));
717 }
718 SpareFlexChildren::give(children);
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}