use super::{
Concrete, Desc, Layout, Renderable, Staged, base, check_unsized_abs, map_unsized_area,
nuetralize_unsized,
};
use crate::{PxDim, PxPoint, PxRect, RowDirection, SourceID, rtree};
use std::rc::Rc;
pub trait Prop: base::Area + base::Limits + base::Direction + base::Anchor {}
crate::gen_from_to_dyn!(Prop);
pub trait Child: base::RLimits + base::Margin + base::Order {}
crate::gen_from_to_dyn!(Child);
impl Desc for dyn Prop {
type Props = dyn Prop;
type Child = dyn Child;
type Children = im::Vector<Option<Box<dyn Layout<Self::Child>>>>;
fn stage<'a>(
props: &Self::Props,
outer_area: PxRect,
outer_limits: crate::PxLimits,
children: &Self::Children,
id: std::sync::Weak<SourceID>,
renderable: Option<Rc<dyn Renderable>>,
window: &mut crate::component::window::WindowState,
) -> Box<dyn Staged + 'a> {
use super::Swappable;
let limits = outer_limits + props.limits().resolve(window.dpi);
let myarea = props.area().resolve(window.dpi);
let dir = props.direction();
let xaxis = match dir {
RowDirection::LeftToRight | RowDirection::RightToLeft => true,
RowDirection::TopToBottom | RowDirection::BottomToTop => false,
};
let inner_dim = super::limit_dim(super::eval_dim(myarea, outer_area.dim()), limits);
let outer_safe = nuetralize_unsized(outer_area);
let (main_limit, _) = inner_dim.min(limits.max()).swap_axis(xaxis);
let mut aux_margins: im::Vector<f32> = im::Vector::new();
let mut areas: im::Vector<Option<(PxRect, f32)>> = im::Vector::new();
let area = {
let mut cur = PxPoint::zero();
let mut max_main = 0.0;
let mut max_aux: f32 = 0.0;
let mut prev_margin = f32::NAN;
let mut aux_margin: f32 = 0.0;
let mut aux_margin_bottom = f32::NAN;
let mut prev_aux_margin = f32::NAN;
let inner_area = PxRect::from(inner_dim);
for child in children.iter() {
let child_props = child.as_ref().unwrap().get_props();
let child_limit = super::apply_limit(inner_dim, limits, *child_props.rlimits());
let child_margin = child_props
.margin()
.resolve(window.dpi)
.to_perimeter(outer_safe);
let stage = child
.as_ref()
.unwrap()
.stage(inner_area, child_limit, window);
let area = stage.get_area();
let (margin_main, child_margin_aux) = child_margin.topleft().swap_axis(xaxis);
let (main, aux) = area.dim().swap_axis(xaxis);
let mut margin = super::merge_margin(prev_margin, margin_main);
areas.push_back(Some((area, margin)));
if !prev_margin.is_nan() && cur.x + main + margin > main_limit {
max_main = cur.x.max(max_main);
cur.x = 0.0;
let aux_merge = super::merge_margin(prev_aux_margin, aux_margin);
aux_margins.push_back(aux_merge);
cur.y += max_aux + aux_merge;
max_aux = 0.0;
margin = 0.0;
aux_margin = 0.0;
prev_aux_margin = aux_margin_bottom;
aux_margin_bottom = f32::NAN;
}
cur.x += main + margin;
aux_margin = aux_margin.max(child_margin_aux);
max_aux = max_aux.max(aux);
let (margin, child_margin_aux) = child_margin.bottomright().swap_axis(xaxis);
prev_margin = margin;
aux_margin_bottom = aux_margin_bottom.max(child_margin_aux);
}
max_main = cur.x.max(max_main);
let aux_merge = super::merge_margin(prev_aux_margin, aux_margin);
aux_margins.push_back(aux_merge);
cur.y += max_aux + aux_margin;
let (bounds_x, bounds_y) = super::swap_pair(xaxis, (max_main, cur.y));
map_unsized_area(myarea, PxDim::new(bounds_x, bounds_y))
};
let evaluated_area = super::limit_area(area * outer_safe, limits);
let anchor = props.anchor().resolve(window.dpi) * evaluated_area.dim();
let evaluated_area = evaluated_area - anchor;
let mut staging: im::Vector<Option<Box<dyn Staged>>> = im::Vector::new();
let mut nodes: im::Vector<Option<Rc<rtree::Node>>> = im::Vector::new();
let (unsized_x, unsized_y) = check_unsized_abs(outer_area.bottomright());
if (unsized_x && xaxis) || (unsized_y && !xaxis) {
debug_assert!(evaluated_area.v.is_finite().all());
return Box::new(Concrete {
area: evaluated_area,
renderable: None,
rtree: rtree::Node::new(evaluated_area.to_untyped(), None, nodes, id, window),
children: staging,
layer: None,
});
}
let evaluated_dim = evaluated_area.dim();
let mut cur = match dir {
RowDirection::LeftToRight | RowDirection::TopToBottom => PxPoint::zero(),
RowDirection::RightToLeft => PxPoint::new(evaluated_dim.width, 0.0),
RowDirection::BottomToTop => PxPoint::new(0.0, evaluated_dim.height),
};
let mut maxaux: f32 = 0.0;
aux_margins.pop_front();
let mut aux_margin = aux_margins.pop_front().unwrap_or_default();
for (i, child) in children.iter().enumerate() {
let child = child.as_ref().unwrap();
let (area, margin) = areas[i].unwrap();
let dim = area.dim();
let (_, aux) = dim.swap_axis(xaxis);
match dir {
RowDirection::RightToLeft => {
if cur.x - dim.width - margin < 0.0 {
cur.y += maxaux + aux_margin;
aux_margin = aux_margins.pop_front().unwrap_or_default();
maxaux = 0.0;
cur.x = evaluated_dim.width - dim.width;
} else {
cur.x -= dim.width + margin
}
}
RowDirection::BottomToTop => {
if cur.y - dim.height - margin < 0.0 {
cur.x += maxaux + aux_margin;
aux_margin = aux_margins.pop_front().unwrap_or_default();
maxaux = 0.0;
cur.y = evaluated_dim.height - dim.height;
} else {
cur.y -= dim.height + margin
}
}
RowDirection::LeftToRight => {
if cur.x + dim.width + margin > evaluated_dim.width {
cur.y += maxaux + aux_margin;
aux_margin = aux_margins.pop_front().unwrap_or_default();
maxaux = 0.0;
cur.x = 0.0;
} else {
cur.x += margin;
}
}
RowDirection::TopToBottom => {
if cur.y + dim.height + margin > evaluated_dim.height {
cur.x += maxaux + aux_margin;
aux_margin = aux_margins.pop_front().unwrap_or_default();
maxaux = 0.0;
cur.y = 0.0;
} else {
cur.y += margin;
}
}
};
let child_area = area + cur;
match dir {
RowDirection::LeftToRight => cur.x += dim.width,
RowDirection::TopToBottom => cur.y += dim.height,
_ => (),
};
maxaux = maxaux.max(aux);
let child_limit = *child.get_props().rlimits() * evaluated_dim;
let stage = child.stage(child_area, child_limit, window);
if let Some(node) = stage.get_rtree().upgrade() {
nodes.push_back(Some(node));
}
staging.push_back(Some(stage));
}
debug_assert!(evaluated_area.v.is_finite().all());
Box::new(Concrete {
area: evaluated_area,
renderable,
rtree: rtree::Node::new(evaluated_area.to_untyped(), None, nodes, id, window),
children: staging,
layer: None,
})
}
}