use cranpose_ui_layout::{
Axis, Constraints, MeasurePolicy, MeasureResult, MeasureScope, ParentData, Placement,
bias_offset, round_to_px,
};
use smallvec::SmallVec;
use crate::layout::core::{
Alignment, Arrangement, HorizontalAlignment, LinearArrangement, Measurable, VerticalAlignment,
};
#[derive(Clone, Debug, PartialEq)]
pub struct BoxMeasurePolicy {
pub content_alignment: Alignment,
pub propagate_min_constraints: bool,
pub layout_direction: crate::LayoutDirection,
}
impl BoxMeasurePolicy {
pub fn new(content_alignment: Alignment, propagate_min_constraints: bool) -> Self {
Self {
content_alignment,
propagate_min_constraints,
layout_direction: crate::LayoutDirection::Ltr,
}
}
}
impl MeasurePolicy for BoxMeasurePolicy {
fn measure(
&self,
scope: &dyn MeasureScope,
measurables: &[Box<dyn Measurable>],
constraints: Constraints,
) -> MeasureResult {
let mut placements = Vec::new();
let measurement = self.measure_into(scope, measurables, constraints, &mut placements);
MeasureResult::new(measurement, placements)
}
fn measure_hold(
&self,
measurables: &[Box<dyn Measurable>],
constraints: Constraints,
size: crate::modifier::Size,
) -> Option<cranpose_ui_layout::ConstraintsHold> {
if measurables.is_empty() {
return empty_layout_hold(constraints, size);
}
let mut width = cranpose_ui_layout::BoundRange::ANY;
let mut height = cranpose_ui_layout::BoundRange::ANY;
let mut held = cranpose_ui_layout::ConstraintsHold::sized(size.width, size.height);
let mut largest = crate::modifier::Size::default();
for measurable in measurables {
let (child, hold) = measurable.measured_hold()?;
if self.propagate_min_constraints {
held = held.intersect(hold)?;
} else {
width = width.intersect(hold.width.max)?;
height = height.intersect(hold.height.max)?;
}
largest.width = largest.width.max(child.width);
largest.height = largest.height.max(child.height);
}
if size != largest {
return None;
}
held.width.max = held.width.max.intersect(width)?;
held.height.max = held.height.max.intersect(height)?;
Some(held)
}
fn measure_into(
&self,
scope: &dyn MeasureScope,
measurables: &[Box<dyn Measurable>],
constraints: Constraints,
placements: &mut Vec<Placement>,
) -> cranpose_ui_layout::Measurement {
placements.clear();
let child_constraints = if self.propagate_min_constraints {
constraints
} else {
Constraints {
min_width: 0.0,
max_width: constraints.max_width,
min_height: 0.0,
max_height: constraints.max_height,
}
};
let mut max_width = 0.0_f32;
let mut max_height = 0.0_f32;
let mut placeables: SmallVec<[(cranpose_ui_layout::Placeable, Alignment); 8]> =
SmallVec::new();
for measurable in measurables {
let placeable = measurable.measure(child_constraints);
max_width = max_width.max(placeable.width());
max_height = max_height.max(placeable.height());
let alignment = measurable
.parent_data()
.box_alignment
.unwrap_or(self.content_alignment);
placeables.push((placeable, alignment));
}
let width = max_width.clamp(constraints.min_width, constraints.max_width);
let height = max_height.clamp(constraints.min_height, constraints.max_height);
placements.reserve(placeables.len());
for (placeable, alignment) in placeables {
let child_width = placeable.width();
let child_height = placeable.height();
let x = alignment
.horizontal
.align(width, child_width, scope.density());
let x = self.layout_direction.place_x(x, width, child_width);
let y = alignment
.vertical
.align(height, child_height, scope.density());
placeable.place(x, y);
placements.push(Placement::new(placeable.node_id(), x, y, 0));
}
crate::modifier::Size { width, height }.into()
}
fn min_intrinsic_width(&self, measurables: &[Box<dyn Measurable>], height: f32) -> f32 {
measurables
.iter()
.map(|m| m.min_intrinsic_width(height))
.fold(0.0, f32::max)
}
fn max_intrinsic_width(&self, measurables: &[Box<dyn Measurable>], height: f32) -> f32 {
measurables
.iter()
.map(|m| m.max_intrinsic_width(height))
.fold(0.0, f32::max)
}
fn min_intrinsic_height(&self, measurables: &[Box<dyn Measurable>], width: f32) -> f32 {
measurables
.iter()
.map(|m| m.min_intrinsic_height(width))
.fold(0.0, f32::max)
}
fn max_intrinsic_height(&self, measurables: &[Box<dyn Measurable>], width: f32) -> f32 {
measurables
.iter()
.map(|m| m.max_intrinsic_height(width))
.fold(0.0, f32::max)
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct FlexMeasurePolicy {
pub axis: Axis,
pub main_axis_arrangement: LinearArrangement,
pub cross_axis_alignment: CrossAxisAlignment,
pub density: f32,
pub layout_direction: crate::LayoutDirection,
}
pub(crate) struct WeightShares {
density: f32,
unit_px: f32,
remainder_px: f32,
}
impl WeightShares {
pub(crate) fn new(
remaining: f32,
weights: impl Iterator<Item = f32> + Clone,
density: f32,
) -> Self {
let density = if density > 0.0 && density.is_finite() {
density
} else {
1.0
};
let total_weight: f32 = weights.clone().sum();
let remaining_px = (remaining * density).round();
let unit_px = if total_weight > 0.0 {
remaining_px / total_weight
} else {
0.0
};
let rounded_px: f32 = weights.map(|weight| (unit_px * weight + 0.5).floor()).sum();
Self {
density,
unit_px,
remainder_px: remaining_px - rounded_px,
}
}
pub(crate) fn next_share(&mut self, weight: f32) -> f32 {
let step = if self.remainder_px > 0.0 {
1.0
} else if self.remainder_px < 0.0 {
-1.0
} else {
0.0
};
self.remainder_px -= step;
((self.unit_px * weight + 0.5).floor() + step).max(0.0) / self.density
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum CrossAxisAlignment {
Start,
Center,
End,
}
impl CrossAxisAlignment {
fn align(&self, available: f32, child: f32, density: f32) -> f32 {
let bias = match self {
CrossAxisAlignment::Start => -1.0,
CrossAxisAlignment::Center => 0.0,
CrossAxisAlignment::End => 1.0,
};
bias_offset(bias, available, child, density)
}
}
impl From<HorizontalAlignment> for CrossAxisAlignment {
fn from(alignment: HorizontalAlignment) -> Self {
match alignment {
HorizontalAlignment::Start => CrossAxisAlignment::Start,
HorizontalAlignment::CenterHorizontally => CrossAxisAlignment::Center,
HorizontalAlignment::End => CrossAxisAlignment::End,
}
}
}
impl From<VerticalAlignment> for CrossAxisAlignment {
fn from(alignment: VerticalAlignment) -> Self {
match alignment {
VerticalAlignment::Top => CrossAxisAlignment::Start,
VerticalAlignment::CenterVertically => CrossAxisAlignment::Center,
VerticalAlignment::Bottom => CrossAxisAlignment::End,
}
}
}
impl FlexMeasurePolicy {
pub fn new(
axis: Axis,
main_axis_arrangement: LinearArrangement,
cross_axis_alignment: CrossAxisAlignment,
density: f32,
) -> Self {
Self {
axis,
main_axis_arrangement,
cross_axis_alignment,
density,
layout_direction: crate::LayoutDirection::Ltr,
}
}
pub fn row(
horizontal_arrangement: LinearArrangement,
vertical_alignment: VerticalAlignment,
density: f32,
) -> Self {
Self::new(
Axis::Horizontal,
horizontal_arrangement,
vertical_alignment.into(),
density,
)
}
pub fn column(
vertical_arrangement: LinearArrangement,
horizontal_alignment: HorizontalAlignment,
density: f32,
) -> Self {
Self::new(
Axis::Vertical,
vertical_arrangement,
horizontal_alignment.into(),
density,
)
}
fn get_axis_constraints(&self, constraints: Constraints) -> (f32, f32, f32, f32) {
match self.axis {
Axis::Horizontal => (
constraints.min_width,
constraints.max_width,
constraints.min_height,
constraints.max_height,
),
Axis::Vertical => (
constraints.min_height,
constraints.max_height,
constraints.min_width,
constraints.max_width,
),
}
}
fn make_constraints(
&self,
min_main: f32,
max_main: f32,
min_cross: f32,
max_cross: f32,
) -> Constraints {
match self.axis {
Axis::Horizontal => Constraints {
min_width: min_main,
max_width: max_main,
min_height: min_cross,
max_height: max_cross,
},
Axis::Vertical => Constraints {
min_width: min_cross,
max_width: max_cross,
min_height: min_main,
max_height: max_main,
},
}
}
fn get_main_axis_size(&self, width: f32, height: f32) -> f32 {
match self.axis {
Axis::Horizontal => width,
Axis::Vertical => height,
}
}
fn get_cross_axis_size(&self, width: f32, height: f32) -> f32 {
match self.axis {
Axis::Horizontal => height,
Axis::Vertical => width,
}
}
fn measure_fixed_children(
&self,
measurables: &[Box<dyn Measurable>],
fixed_children: &[usize],
(max_main, max_cross): (f32, f32),
spacing: f32,
placeables: &mut [Option<cranpose_ui_layout::Placeable>],
) -> (f32, f32) {
let main_axis_bounded = max_main.is_finite();
let mut fixed_space = 0.0_f32;
let mut max_cross_size = 0.0_f32;
for &idx in fixed_children {
let available_main = if main_axis_bounded {
(max_main - fixed_space).max(0.0)
} else {
max_main
};
let placeable = measurables[idx].measure(self.make_constraints(
0.0,
available_main,
0.0,
max_cross,
));
let main_size = self.get_main_axis_size(placeable.width(), placeable.height());
let spacing_after = if main_axis_bounded {
spacing.min(available_main - main_size).max(0.0)
} else {
spacing
};
fixed_space += main_size + spacing_after;
max_cross_size =
max_cross_size.max(self.get_cross_axis_size(placeable.width(), placeable.height()));
placeables[idx] = Some(placeable);
}
(fixed_space, max_cross_size)
}
fn intrinsic_main_size(
&self,
measurables: &[Box<dyn Measurable>],
main_size: impl Fn(&dyn Measurable) -> f32,
) -> f32 {
if measurables.is_empty() {
return 0.0;
}
let mut fixed_space = 0.0_f32;
let mut weight_unit = 0.0_f32;
let mut total_weight = 0.0_f32;
for measurable in measurables {
let size = main_size(measurable.as_ref());
match child_weight(measurable.as_ref()) {
Some(weight) => {
total_weight += weight;
weight_unit = weight_unit.max(round_to_px(size / weight, self.density));
}
None => fixed_space += size,
}
}
round_to_px(weight_unit * total_weight, self.density)
+ fixed_space
+ self.get_spacing() * (measurables.len() - 1) as f32
}
fn intrinsic_cross_size(
&self,
measurables: &[Box<dyn Measurable>],
main_available: f32,
main_size: impl Fn(&dyn Measurable, f32) -> f32,
cross_size: impl Fn(&dyn Measurable, f32) -> f32,
) -> f32 {
if measurables.is_empty() {
return 0.0;
}
let mut fixed_space =
(self.get_spacing() * (measurables.len() - 1) as f32).min(main_available);
let mut cross = 0.0_f32;
let mut total_weight = 0.0_f32;
for measurable in measurables {
let measurable = measurable.as_ref();
if let Some(weight) = child_weight(measurable) {
total_weight += weight;
continue;
}
let space =
main_size(measurable, f32::INFINITY).min((main_available - fixed_space).max(0.0));
fixed_space += space;
cross = cross.max(cross_size(measurable, space));
}
if total_weight == 0.0 {
return cross;
}
let weight_unit = if main_available.is_finite() {
round_to_px(
(main_available - fixed_space).max(0.0) / total_weight,
self.density,
)
} else {
f32::INFINITY
};
for measurable in measurables {
let measurable = measurable.as_ref();
if let Some(weight) = child_weight(measurable) {
cross = cross.max(cross_size(
measurable,
round_to_px(weight_unit * weight, self.density),
));
}
}
cross
}
fn get_spacing(&self) -> f32 {
self.main_axis_arrangement.spacing(self.density)
}
fn measure_weighted_children(
&self,
measurables: &[Box<dyn Measurable>],
weighted_children: &[(usize, ParentData)],
(max_main, max_cross): (f32, f32),
(fixed_space, spacing): (f32, f32),
placeables: &mut [Option<cranpose_ui_layout::Placeable>],
) -> f32 {
let remaining_main =
(max_main - fixed_space - spacing * weighted_children.len().saturating_sub(1) as f32)
.max(0.0);
let mut shares = WeightShares::new(
remaining_main,
weighted_children.iter().map(|(_, data)| data.weight),
self.density,
);
let mut max_cross_size = 0.0_f32;
for &(idx, data) in weighted_children {
let constraints = if max_main.is_finite() {
let allocated = shares.next_share(data.weight);
let minimum = if data.fill { allocated } else { 0.0 };
self.make_constraints(minimum, allocated, 0.0, max_cross)
} else {
self.make_constraints(0.0, max_main, 0.0, max_cross)
};
let placeable = measurables[idx].measure(constraints);
max_cross_size =
max_cross_size.max(self.get_cross_axis_size(placeable.width(), placeable.height()));
placeables[idx] = Some(placeable);
}
max_cross_size
}
fn measured_extents(
&self,
placeables: &[cranpose_ui_layout::Placeable],
parent_data: &[cranpose_ui_layout::ParentData],
) -> (f32, f32, f32) {
let mut main = 0.0;
let mut before = 0.0_f32;
let mut after = 0.0_f32;
for (placeable, data) in placeables.iter().zip(parent_data) {
main += self.get_main_axis_size(placeable.width(), placeable.height());
if self.axis == Axis::Horizontal
&& data.row_baseline
&& let Some(baseline) = placeable.alignment_lines().first_baseline()
{
before = before.max(baseline);
after = after.max(placeable.height() - baseline);
}
}
(main, before, after)
}
fn cross_axis_position(
&self,
placeable: &cranpose_ui_layout::Placeable,
parent_data: ParentData,
container_cross: f32,
before_baseline: f32,
) -> f32 {
if self.axis == Axis::Horizontal && parent_data.row_baseline {
return placeable
.alignment_lines()
.first_baseline()
.map_or(0.0, |baseline| before_baseline - baseline);
}
let alignment = match self.axis {
Axis::Horizontal => parent_data
.row_alignment
.map_or(self.cross_axis_alignment, Into::into),
Axis::Vertical => parent_data
.column_alignment
.map_or(self.cross_axis_alignment, Into::into),
};
let child_cross = self.get_cross_axis_size(placeable.width(), placeable.height());
alignment.align(container_cross, child_cross, self.density)
}
}
fn child_main_range(
child_max: cranpose_ui_layout::BoundRange,
max_main: f32,
fixed_space: f32,
child_main: f32,
spacing: f32,
) -> Option<cranpose_ui_layout::BoundRange> {
use cranpose_ui_layout::BoundRange;
if !max_main.is_finite() {
return child_max
.contains(f32::INFINITY)
.then_some(BoundRange::exactly(f32::INFINITY));
}
if (max_main - fixed_space).max(0.0) - child_main < spacing {
return None;
}
child_max
.outset(fixed_space)
.intersect(BoundRange::from(fixed_space + child_main + spacing))
}
fn child_weight(measurable: &dyn Measurable) -> Option<f32> {
measurable
.flex_parent_data()
.map(|data| data.weight)
.filter(|weight| *weight > 0.0)
}
impl MeasurePolicy for FlexMeasurePolicy {
fn measure(
&self,
scope: &dyn MeasureScope,
measurables: &[Box<dyn Measurable>],
constraints: Constraints,
) -> MeasureResult {
let mut placements = Vec::new();
let measurement = self.measure_into(scope, measurables, constraints, &mut placements);
MeasureResult::new(measurement, placements)
}
fn measure_hold(
&self,
measurables: &[Box<dyn Measurable>],
constraints: Constraints,
size: crate::modifier::Size,
) -> Option<cranpose_ui_layout::ConstraintsHold> {
use cranpose_ui_layout::{AxisHold, BoundRange, ConstraintsHold};
if measurables.is_empty() {
return empty_layout_hold(constraints, size);
}
if measurables
.iter()
.any(|measurable| measurable.parent_data().has_weight())
{
return None;
}
let (_, max_main, _, _) = self.get_axis_constraints(constraints);
let spacing = self.get_spacing();
let mut main = BoundRange::ANY;
let mut cross = BoundRange::ANY;
let mut fixed_space = 0.0_f32;
let mut total_main = 0.0_f32;
let mut cross_size = 0.0_f32;
for measurable in measurables {
let (child, hold) = measurable.measured_hold()?;
let (main_hold, cross_hold) = match self.axis {
Axis::Horizontal => (hold.width, hold.height),
Axis::Vertical => (hold.height, hold.width),
};
let child_main = self.get_main_axis_size(child.width, child.height);
main = main.intersect(child_main_range(
main_hold.max,
max_main,
fixed_space,
child_main,
spacing,
)?)?;
cross = cross.intersect(cross_hold.max)?;
fixed_space += child_main + spacing;
total_main += child_main;
cross_size = cross_size.max(self.get_cross_axis_size(child.width, child.height));
}
total_main += spacing * (measurables.len() - 1) as f32;
let size_main = self.get_main_axis_size(size.width, size.height);
let size_cross = self.get_cross_axis_size(size.width, size.height);
if size_main != total_main || size_cross != cross_size {
return None;
}
let main = AxisHold {
min: BoundRange::up_to(size_main),
max: main.intersect(BoundRange::from(size_main))?,
};
let cross = AxisHold {
min: BoundRange::up_to(size_cross),
max: cross.intersect(BoundRange::from(size_cross))?,
};
Some(match self.axis {
Axis::Horizontal => ConstraintsHold {
width: main,
height: cross,
},
Axis::Vertical => ConstraintsHold {
width: cross,
height: main,
},
})
}
fn measure_into(
&self,
_scope: &dyn MeasureScope,
measurables: &[Box<dyn Measurable>],
constraints: Constraints,
placements: &mut Vec<Placement>,
) -> cranpose_ui_layout::Measurement {
placements.clear();
if measurables.is_empty() {
let (width, height) = constraints.constrain(0.0, 0.0);
return crate::modifier::Size { width, height }.into();
}
let (min_main, max_main, min_cross, max_cross) = self.get_axis_constraints(constraints);
let spacing = self.get_spacing();
let mut fixed_children: SmallVec<[usize; 8]> = SmallVec::new();
let parent_data: SmallVec<[ParentData; 8]> = measurables
.iter()
.map(|child| child.parent_data())
.collect();
let mut weighted_children: SmallVec<[(usize, ParentData); 8]> = SmallVec::new();
for (idx, data) in parent_data.iter().copied().enumerate() {
if data.has_weight() {
weighted_children.push((idx, data));
} else {
fixed_children.push(idx);
}
}
let child_constraints = self.make_constraints(0.0, max_main, 0.0, max_cross);
let mut placeables: SmallVec<[Option<cranpose_ui_layout::Placeable>; 8]> = SmallVec::new();
placeables.resize_with(measurables.len(), || None);
let (fixed_space, mut max_cross_size) = self.measure_fixed_children(
measurables,
&fixed_children,
(max_main, max_cross),
spacing,
&mut placeables,
);
let num_children = measurables.len();
let total_spacing = if num_children > 1 {
spacing * (num_children - 1) as f32
} else {
0.0
};
max_cross_size = max_cross_size.max(self.measure_weighted_children(
measurables,
&weighted_children,
(max_main, max_cross),
(fixed_space, spacing),
&mut placeables,
));
let placeables: SmallVec<[cranpose_ui_layout::Placeable; 8]> = placeables
.into_iter()
.enumerate()
.map(|(idx, placeable)| {
placeable.unwrap_or_else(|| measurables[idx].measure(child_constraints))
})
.collect();
let (total_main, before_baseline, after_baseline) =
self.measured_extents(&placeables, &parent_data);
let total_main = total_main + total_spacing;
max_cross_size = max_cross_size.max(before_baseline + after_baseline);
let container_main = total_main.clamp(min_main, max_main);
let container_cross = max_cross_size.clamp(min_cross, max_cross);
let child_main_sizes: SmallVec<[f32; 8]> = placeables
.iter()
.map(|p| self.get_main_axis_size(p.width(), p.height()))
.collect();
let mut main_positions: SmallVec<[f32; 8]> =
SmallVec::with_capacity(child_main_sizes.len());
main_positions.resize(child_main_sizes.len(), 0.0);
let arrangement = if total_main > container_main && !self.main_axis_arrangement.is_spaced()
{
LinearArrangement::Start
} else {
self.main_axis_arrangement
};
arrangement.arrange(
self.density,
container_main,
&child_main_sizes,
&mut main_positions,
);
placements.reserve(placeables.len());
for (idx, (placeable, main_pos)) in placeables.into_iter().zip(main_positions).enumerate() {
let cross_pos = self.cross_axis_position(
&placeable,
parent_data[idx],
container_cross,
before_baseline,
);
let (x, y) = match self.axis {
Axis::Horizontal => (main_pos, cross_pos),
Axis::Vertical => (cross_pos, main_pos),
};
let width = match self.axis {
Axis::Horizontal => container_main,
Axis::Vertical => container_cross,
};
let x = self.layout_direction.place_x(x, width, placeable.width());
placeable.place(x, y);
placements.push(Placement::new(placeable.node_id(), x, y, 0));
}
let (width, height) = match self.axis {
Axis::Horizontal => (container_main, container_cross),
Axis::Vertical => (container_cross, container_main),
};
crate::modifier::Size { width, height }.into()
}
fn min_intrinsic_width(&self, measurables: &[Box<dyn Measurable>], height: f32) -> f32 {
match self.axis {
Axis::Horizontal => {
self.intrinsic_main_size(measurables, |m| m.min_intrinsic_width(height))
}
Axis::Vertical => self.intrinsic_cross_size(
measurables,
height,
|m, width| m.max_intrinsic_height(width),
|m, main| m.min_intrinsic_width(main),
),
}
}
fn max_intrinsic_width(&self, measurables: &[Box<dyn Measurable>], height: f32) -> f32 {
match self.axis {
Axis::Horizontal => {
self.intrinsic_main_size(measurables, |m| m.max_intrinsic_width(height))
}
Axis::Vertical => self.intrinsic_cross_size(
measurables,
height,
|m, width| m.max_intrinsic_height(width),
|m, main| m.max_intrinsic_width(main),
),
}
}
fn min_intrinsic_height(&self, measurables: &[Box<dyn Measurable>], width: f32) -> f32 {
match self.axis {
Axis::Horizontal => self.intrinsic_cross_size(
measurables,
width,
|m, height| m.max_intrinsic_width(height),
|m, main| m.min_intrinsic_height(main),
),
Axis::Vertical => {
self.intrinsic_main_size(measurables, |m| m.min_intrinsic_height(width))
}
}
}
fn max_intrinsic_height(&self, measurables: &[Box<dyn Measurable>], width: f32) -> f32 {
match self.axis {
Axis::Horizontal => self.intrinsic_cross_size(
measurables,
width,
|m, height| m.max_intrinsic_width(height),
|m, main| m.max_intrinsic_height(main),
),
Axis::Vertical => {
self.intrinsic_main_size(measurables, |m| m.max_intrinsic_height(width))
}
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct FlowRowMeasurePolicy {
pub main_axis_spacing: f32,
pub cross_axis_spacing: f32,
pub layout_direction: crate::LayoutDirection,
}
impl FlowRowMeasurePolicy {
pub fn new(main_axis_spacing: f32, cross_axis_spacing: f32) -> Self {
Self {
main_axis_spacing: main_axis_spacing.max(0.0),
cross_axis_spacing: cross_axis_spacing.max(0.0),
layout_direction: crate::LayoutDirection::Ltr,
}
}
fn wrapped_intrinsic_height(
&self,
measurables: &[Box<dyn Measurable>],
available_width: f32,
use_min_height: bool,
) -> f32 {
let mut cursor_x = 0.0_f32;
let mut line_top = 0.0_f32;
let mut line_height = 0.0_f32;
for measurable in measurables {
let child_width = measurable.max_intrinsic_width(f32::INFINITY);
let child_height = if use_min_height {
measurable.min_intrinsic_height(child_width)
} else {
measurable.max_intrinsic_height(child_width)
};
if cursor_x > 0.0 && cursor_x + self.main_axis_spacing + child_width > available_width {
line_top += line_height + self.cross_axis_spacing;
cursor_x = 0.0;
line_height = 0.0;
}
cursor_x += if cursor_x > 0.0 {
self.main_axis_spacing + child_width
} else {
child_width
};
line_height = line_height.max(child_height);
}
line_top + line_height
}
}
impl MeasurePolicy for FlowRowMeasurePolicy {
fn measure(
&self,
scope: &dyn MeasureScope,
measurables: &[Box<dyn Measurable>],
constraints: Constraints,
) -> MeasureResult {
let mut placements = Vec::new();
let measurement = self.measure_into(scope, measurables, constraints, &mut placements);
MeasureResult::new(measurement, placements)
}
fn measure_into(
&self,
_scope: &dyn MeasureScope,
measurables: &[Box<dyn Measurable>],
constraints: Constraints,
placements: &mut Vec<Placement>,
) -> cranpose_ui_layout::Measurement {
placements.clear();
if measurables.is_empty() {
let (width, height) = constraints.constrain(0.0, 0.0);
return crate::modifier::Size { width, height }.into();
}
let child_constraints = Constraints {
min_width: 0.0,
max_width: constraints.max_width,
min_height: 0.0,
max_height: constraints.max_height,
};
let placeables: SmallVec<[cranpose_ui_layout::Placeable; 8]> = measurables
.iter()
.map(|measurable| measurable.measure(child_constraints))
.collect();
let mut cursor_x = 0.0_f32;
let mut line_top = 0.0_f32;
let mut line_height = 0.0_f32;
let mut max_line_width = 0.0_f32;
placements.reserve(placeables.len());
for placeable in &placeables {
let child_width = placeable.width();
let child_height = placeable.height();
if cursor_x > 0.0
&& cursor_x + self.main_axis_spacing + child_width > constraints.max_width
{
max_line_width = max_line_width.max(cursor_x);
line_top += line_height + self.cross_axis_spacing;
cursor_x = 0.0;
line_height = 0.0;
}
let x = if cursor_x > 0.0 {
cursor_x + self.main_axis_spacing
} else {
0.0
};
if !self.layout_direction.is_rtl() {
placeable.place(x, line_top);
}
placements.push(Placement::new(placeable.node_id(), x, line_top, 0));
cursor_x = x + child_width;
line_height = line_height.max(child_height);
}
max_line_width = max_line_width.max(cursor_x);
let width = max_line_width.clamp(constraints.min_width, constraints.max_width);
if self.layout_direction.is_rtl() {
for (placement, placeable) in placements.iter_mut().zip(&placeables) {
placement.x = self
.layout_direction
.place_x(placement.x, width, placeable.width());
placeable.place(placement.x, placement.y);
}
}
let height = (line_top + line_height).clamp(constraints.min_height, constraints.max_height);
crate::modifier::Size { width, height }.into()
}
fn min_intrinsic_width(&self, measurables: &[Box<dyn Measurable>], height: f32) -> f32 {
measurables
.iter()
.map(|m| m.min_intrinsic_width(height))
.fold(0.0, f32::max)
}
fn max_intrinsic_width(&self, measurables: &[Box<dyn Measurable>], height: f32) -> f32 {
let total_spacing = if measurables.len() > 1 {
self.main_axis_spacing * (measurables.len() - 1) as f32
} else {
0.0
};
measurables
.iter()
.map(|m| m.max_intrinsic_width(height))
.sum::<f32>()
+ total_spacing
}
fn min_intrinsic_height(&self, measurables: &[Box<dyn Measurable>], width: f32) -> f32 {
self.wrapped_intrinsic_height(measurables, width, true)
}
fn max_intrinsic_height(&self, measurables: &[Box<dyn Measurable>], width: f32) -> f32 {
self.wrapped_intrinsic_height(measurables, width, false)
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct LeafMeasurePolicy {
pub intrinsic_size: crate::modifier::Size,
}
impl LeafMeasurePolicy {
pub fn new(intrinsic_size: crate::modifier::Size) -> Self {
Self { intrinsic_size }
}
}
impl MeasurePolicy for LeafMeasurePolicy {
fn measure(
&self,
scope: &dyn MeasureScope,
_measurables: &[Box<dyn Measurable>],
constraints: Constraints,
) -> MeasureResult {
let mut placements = Vec::new();
let measurement = self.measure_into(scope, &[], constraints, &mut placements);
MeasureResult::new(measurement, placements)
}
fn measure_into(
&self,
_scope: &dyn MeasureScope,
_measurables: &[Box<dyn Measurable>],
constraints: Constraints,
placements: &mut Vec<Placement>,
) -> cranpose_ui_layout::Measurement {
placements.clear();
let (width, height) =
constraints.constrain(self.intrinsic_size.width, self.intrinsic_size.height);
crate::modifier::Size { width, height }.into()
}
fn min_intrinsic_width(&self, _measurables: &[Box<dyn Measurable>], _height: f32) -> f32 {
self.intrinsic_size.width
}
fn max_intrinsic_width(&self, _measurables: &[Box<dyn Measurable>], _height: f32) -> f32 {
self.intrinsic_size.width
}
fn min_intrinsic_height(&self, _measurables: &[Box<dyn Measurable>], _width: f32) -> f32 {
self.intrinsic_size.height
}
fn max_intrinsic_height(&self, _measurables: &[Box<dyn Measurable>], _width: f32) -> f32 {
self.intrinsic_size.height
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct EmptyMeasurePolicy;
impl EmptyMeasurePolicy {
pub fn new() -> Self {
Self
}
}
impl Default for EmptyMeasurePolicy {
fn default() -> Self {
Self::new()
}
}
impl MeasurePolicy for EmptyMeasurePolicy {
fn measure(
&self,
scope: &dyn MeasureScope,
_measurables: &[Box<dyn Measurable>],
constraints: Constraints,
) -> MeasureResult {
let mut placements = Vec::new();
let measurement = self.measure_into(scope, &[], constraints, &mut placements);
MeasureResult::new(measurement, placements)
}
fn measure_into(
&self,
_scope: &dyn MeasureScope,
_measurables: &[Box<dyn Measurable>],
constraints: Constraints,
placements: &mut Vec<Placement>,
) -> cranpose_ui_layout::Measurement {
placements.clear();
let (width, height) = constraints.constrain(0.0, 0.0);
crate::modifier::Size { width, height }.into()
}
fn measure_hold(
&self,
_measurables: &[Box<dyn Measurable>],
constraints: Constraints,
size: crate::modifier::Size,
) -> Option<cranpose_ui_layout::ConstraintsHold> {
empty_layout_hold(constraints, size)
}
fn min_intrinsic_width(&self, _measurables: &[Box<dyn Measurable>], _height: f32) -> f32 {
0.0
}
fn max_intrinsic_width(&self, _measurables: &[Box<dyn Measurable>], _height: f32) -> f32 {
0.0
}
fn min_intrinsic_height(&self, _measurables: &[Box<dyn Measurable>], _width: f32) -> f32 {
0.0
}
fn max_intrinsic_height(&self, _measurables: &[Box<dyn Measurable>], _width: f32) -> f32 {
0.0
}
}
#[cfg(test)]
#[path = "tests/policies_tests.rs"]
mod tests;
fn empty_layout_hold(
constraints: Constraints,
size: crate::modifier::Size,
) -> Option<cranpose_ui_layout::ConstraintsHold> {
(size.width == constraints.min_width && size.height == constraints.min_height)
.then(|| cranpose_ui_layout::ConstraintsHold::at_min(size.width, size.height))
}