use std::collections::HashMap;
use std::rc::Rc;
use crate::shape::{Direction, Shape};
use super::{clamp_axis, ShapePtr};
pub(super) fn measure(shape: &Shape, cache: &mut HashMap<ShapePtr, (f32, f32)>) -> (f32, f32) {
let ptr = Rc::as_ptr(&shape.inner);
if let Some(size) = cache.get(&ptr) {
return *size;
}
let d = shape.inner.borrow();
let (mut content_w, mut content_h) = (0.0_f32, 0.0_f32);
if let Some(text) = &d.text {
let (tw, th) = text.natural_size();
content_w = tw;
content_h = th;
}
let n = d.children.len();
if n > 0 {
let gap = d.gap.get();
let mut main = 0.0_f32;
let mut cross = 0.0_f32;
for (i, c) in d.children.iter().enumerate() {
let (cw, ch) = measure(c, cache);
let (child_main, child_cross) = match d.direction {
Direction::Row => (cw, ch),
Direction::Column => (ch, cw),
};
main += child_main;
if i + 1 < n {
main += gap;
}
cross = cross.max(child_cross);
}
match d.direction {
Direction::Row => {
content_w = main;
content_h = cross;
}
Direction::Column => {
content_h = main;
content_w = cross;
}
}
}
let pad_l = d.pad_left.get();
let pad_r = d.pad_right.get();
let pad_t = d.pad_top.get();
let pad_b = d.pad_bottom.get();
let wsig = d.width.get();
let hsig = d.height.get();
let w = if wsig > 0.0 { wsig } else { content_w + pad_l + pad_r };
let h = if hsig > 0.0 { hsig } else { content_h + pad_t + pad_b };
let w = clamp_axis(w, d.min_width.get(), d.max_width.get());
let h = clamp_axis(h, d.min_height.get(), d.max_height.get());
drop(d);
cache.insert(ptr, (w, h));
(w, h)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::shape::Length;
#[test]
fn measures_auto_size_from_children() {
let container = Shape::rect()
.direction(Direction::Row)
.gap(10.0)
.padding(5.0)
.child(Shape::rect().size(40.0, 30.0))
.child(Shape::rect().size(20.0, 50.0));
let mut cache = HashMap::new();
let (w, h) = measure(&container, &mut cache);
assert!((w - 80.0).abs() < 1e-3, "w={w}");
assert!((h - 60.0).abs() < 1e-3, "h={h}");
}
#[test]
fn clamps_explicit_size_to_min_max() {
let s = Shape::rect().size(200.0, 50.0).max_width(120.0).min_height(80.0);
let mut cache = HashMap::new();
let (w, h) = measure(&s, &mut cache);
assert!((w - 120.0).abs() < 1e-3, "w={w}");
assert!((h - 80.0).abs() < 1e-3, "h={h}");
}
#[test]
fn min_wins_over_max_on_conflict() {
let s = Shape::rect().size(50.0, 50.0).max_width(80.0).min_width(120.0);
let mut cache = HashMap::new();
let (w, _) = measure(&s, &mut cache);
assert!((w - 120.0).abs() < 1e-3, "w={w}");
}
#[test]
fn percent_is_ignored_in_natural_size() {
let s = Shape::rect()
.width(Length::percent(100.0))
.height(Length::percent(100.0));
let mut cache = HashMap::new();
let (w, h) = measure(&s, &mut cache);
assert!((w).abs() < 1e-3 && (h).abs() < 1e-3, "w={w} h={h}");
}
}