use super::{
Concrete, Desc, Layout, Renderable, Staged, base, check_unsized, map_unsized_area,
nuetralize_unsized,
};
use crate::{DPoint, DValue, PxDim, PxRect, RowDirection, SourceID, UNSIZED_AXIS, rtree};
use std::rc::Rc;
pub trait Prop: base::Area + base::Limits + base::Anchor + base::Padding + base::Direction {
fn rows(&self) -> &[DValue];
fn columns(&self) -> &[DValue];
fn spacing(&self) -> DPoint; }
crate::gen_from_to_dyn!(Prop);
pub trait Child: base::RLimits {
fn coord(&self) -> (usize, usize);
fn span(&self) -> (usize, usize);
}
crate::gen_from_to_dyn!(Child);
fn swap_coord((x, y): (usize, usize), (w, h): (usize, usize), dir: RowDirection) -> (usize, usize) {
match dir {
RowDirection::LeftToRight => (x, y),
RowDirection::RightToLeft => (w - 1 - x, y),
RowDirection::BottomToTop => (x, h - 1 - y),
RowDirection::TopToBottom => (w - 1 - x, h - 1 - y),
}
}
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: crate::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> {
let mut limits = outer_limits + props.limits().resolve(window.dpi);
let myarea = props.area().resolve(window.dpi);
let (unsized_x, unsized_y) = check_unsized(myarea);
let padding = props.padding().as_perimeter(window.dpi);
let allpadding = padding.topleft() + padding.bottomright();
let minmax = limits.v.as_array_mut();
if unsized_x {
minmax[2] -= allpadding.width;
minmax[0] -= allpadding.width;
}
if unsized_y {
minmax[3] -= allpadding.height;
minmax[1] -= allpadding.height;
}
let outer_safe = nuetralize_unsized(outer_area);
let inner_dim = super::limit_dim(super::eval_dim(myarea, outer_area.dim()), limits)
- padding.topleft()
- padding.bottomright();
let spacing = props.spacing().resolve(window.dpi) * outer_safe.dim();
let nrows = props.rows().len();
let ncolumns = props.columns().len();
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 evaluated_area =
crate::util::alloca_array::<f32, PxRect>((nrows + ncolumns) * 2, |x| {
let (resolved, sizes) = x.split_at_mut(nrows + ncolumns);
{
let (rows, columns) = resolved.split_at_mut(nrows);
sizes.fill(f32::NAN);
let (maxrows, maxcolumns) = sizes.split_at_mut(nrows);
for (i, row) in props.rows().iter().enumerate() {
rows[i] = row.resolve(window.dpi.height).resolve(inner_dim.height);
}
for (i, column) in props.columns().iter().enumerate() {
columns[i] = column.resolve(window.dpi.width).resolve(inner_dim.width);
}
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 (column, row) =
swap_coord(child_props.coord(), (ncolumns, nrows), props.direction());
if rows[row] == UNSIZED_AXIS || columns[column] == UNSIZED_AXIS {
let (w, h) = (columns[column], rows[row]);
let child_area = PxRect::new(0.0, 0.0, w, h);
let stage =
child
.as_ref()
.unwrap()
.stage(child_area, child_limit, window);
let area = stage.get_area();
maxrows[row] = maxrows[row].max(area.dim().height);
maxcolumns[column] = maxcolumns[column].max(area.dim().width);
}
}
}
for (i, size) in sizes.iter().enumerate() {
if resolved[i] == UNSIZED_AXIS {
resolved[i] = if size.is_nan() { 0.0 } else { *size };
}
}
let (rows, columns) = resolved.split_at_mut(nrows);
let (x_used, y_used) = (
columns.iter().fold(0.0, |x, y| x + y)
+ (spacing.y * ncolumns.saturating_sub(1) as f32),
rows.iter().fold(0.0, |x, y| x + y)
+ (spacing.x * nrows.saturating_sub(1) as f32),
);
let area = map_unsized_area(myarea, PxDim::new(x_used, y_used));
let (row_offsets, column_offsets) = sizes.split_at_mut(nrows);
let mut offset = 0.0;
for (i, row) in rows.iter().enumerate() {
row_offsets[i] = offset;
offset += row + spacing.x;
}
offset = 0.0;
for (i, column) in columns.iter().enumerate() {
column_offsets[i] = offset;
offset += column + spacing.y;
}
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 (column, row) =
swap_coord(child_props.coord(), (ncolumns, nrows), props.direction());
let (x, y) = (column_offsets[column], row_offsets[row]);
let (w, h) = (columns[column], rows[row]);
let child_area = PxRect::new(x, y, x + w, y + h);
let stage = child
.as_ref()
.unwrap()
.stage(child_area, child_limit, window);
if let Some(node) = stage.get_rtree().upgrade() {
nodes.push_back(Some(node));
}
staging.push_back(Some(stage));
}
let evaluated_area = super::limit_area(area * outer_safe, limits) + padding;
let anchor = props.anchor().resolve(window.dpi) * evaluated_area.dim();
evaluated_area - anchor
});
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,
})
}
}