use crate::{Constraints, ComputedLayout, Mounted, View};
pub fn has_layout_builder<Msg>(view: &View<Msg>) -> bool {
view.layout_builder.is_some() || view.children.iter().any(has_layout_builder)
}
pub fn collect_builder_constraints<Msg>(
mounted: &Mounted<Msg>,
computed: &ComputedLayout,
) -> Vec<Constraints> {
mounted
.nodes
.iter()
.filter(|n| n.is_layout_builder)
.map(|n| {
computed
.get(n.id)
.map(|r| Constraints { max_width: r.w, max_height: r.h })
.unwrap_or(Constraints { max_width: 0.0, max_height: 0.0 })
})
.collect()
}
pub fn expand_layout_builders<Msg>(view: View<Msg>, cons: &[Constraints]) -> View<Msg> {
let mut idx = 0;
expand_rec(view, cons, &mut idx)
}
fn expand_rec<Msg>(mut view: View<Msg>, cons: &[Constraints], idx: &mut usize) -> View<Msg> {
if let Some(builder) = view.layout_builder.take() {
let c = cons
.get(*idx)
.copied()
.unwrap_or(Constraints { max_width: 0.0, max_height: 0.0 });
*idx += 1;
let child = builder(c);
view.children = vec![child];
view
} else {
let children = std::mem::take(&mut view.children);
view.children = children
.into_iter()
.map(|c| expand_rec(c, cons, idx))
.collect();
view
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{mount, Constraints};
use llimphi_layout::taffy::prelude::*;
use llimphi_layout::{LayoutTree, Style};
#[test]
fn sin_builder_es_noop() {
let v = View::<()>::new(Style::default())
.children(vec![View::<()>::new(Style::default())]);
assert!(!has_layout_builder(&v));
let v = expand_layout_builders(v, &[]);
assert_eq!(v.children.len(), 1);
}
#[test]
fn detecta_builder_anidado_en_hijos() {
let v = View::<()>::new(Style::default()).children(vec![
View::<()>::new(Style::default()),
View::<()>::new(Style::default()).layout_builder(|_c| View::<()>::new(Style::default())),
]);
assert!(has_layout_builder(&v));
}
#[test]
fn expand_invoca_closure_con_constraints() {
let build_col = |c: Constraints| {
let n = if c.max_width < 300.0 { 1 } else { 2 };
View::<()>::new(Style::default())
.children((0..n).map(|_| View::<()>::new(Style::default())).collect())
};
let root = View::<()>::new(Style {
size: Size { width: length(400.0), height: length(100.0) },
flex_direction: FlexDirection::Row,
..Default::default()
})
.children(vec![
View::<()>::new(Style {
size: Size { width: percent(0.5), height: percent(1.0) },
..Default::default()
})
.layout_builder(build_col),
View::<()>::new(Style {
size: Size { width: percent(0.5), height: percent(1.0) },
..Default::default()
}),
]);
let mut l1 = LayoutTree::new();
let m1 = mount(&mut l1, root);
let c1 = l1.compute(m1.root, (400.0, 100.0)).expect("layout");
let cons = collect_builder_constraints(&m1, &c1);
assert_eq!(cons.len(), 1, "un solo builder");
assert!((cons[0].max_width - 200.0).abs() < 1.0, "slot 200px: {:?}", cons[0]);
let root2 = View::<()>::new(Style {
size: Size { width: length(400.0), height: length(100.0) },
flex_direction: FlexDirection::Row,
..Default::default()
})
.children(vec![
View::<()>::new(Style {
size: Size { width: percent(0.5), height: percent(1.0) },
..Default::default()
})
.layout_builder(build_col),
View::<()>::new(Style {
size: Size { width: percent(0.5), height: percent(1.0) },
..Default::default()
}),
]);
let expanded = expand_layout_builders(root2, &cons);
let col_izq = &expanded.children[0];
assert!(col_izq.layout_builder.is_none(), "ya expandido");
assert_eq!(col_izq.children.len(), 1, "200px angosto → 1 hijo");
}
#[test]
fn slot_ancho_produce_mas_hijos() {
let build_col = |c: Constraints| {
let n = if c.max_width < 300.0 { 1 } else { 2 };
View::<()>::new(Style::default())
.children((0..n).map(|_| View::<()>::new(Style::default())).collect())
};
let v = View::<()>::new(Style::default()).layout_builder(build_col);
let expanded = expand_layout_builders(v, &[Constraints { max_width: 500.0, max_height: 100.0 }]);
assert_eq!(expanded.children.len(), 1, "el builder produce 1 contenedor");
assert_eq!(expanded.children[0].children.len(), 2, "ancho → 2 columnas");
}
#[test]
fn dos_builders_reciben_constraints_en_preorden() {
let mk = |w: f32| {
move |_c: Constraints| {
View::<()>::new(Style {
size: Size { width: length(w), height: length(10.0) },
..Default::default()
})
}
};
let root = View::<()>::new(Style::default()).children(vec![
View::<()>::new(Style::default()).layout_builder(mk(1.0)),
View::<()>::new(Style::default()).layout_builder(mk(2.0)),
]);
let cons = vec![
Constraints { max_width: 111.0, max_height: 0.0 },
Constraints { max_width: 222.0, max_height: 0.0 },
];
let expanded = expand_layout_builders(root, &cons);
assert!(expanded.children[0].layout_builder.is_none());
assert!(expanded.children[1].layout_builder.is_none());
assert_eq!(expanded.children[0].children.len(), 1);
assert_eq!(expanded.children[1].children.len(), 1);
}
}