use kui_core::Size;
use kui_core::key::Key;
use kui_core::layout::{TextMeasure, compute};
use kui_core::resources::ImageId;
use kui_core::scroll::ScrollStore;
use kui_core::spec::{NodeSpec, Sizing};
use kui_core::tree::{NIL, NodeContent, OriginId, TextId, Tree};
struct Stub;
impl TextMeasure for Stub {
fn intrinsic(&mut self, id: TextId) -> Size {
Size::new(10.0 * id.0 as f32, 20.0)
}
fn wrapped(&mut self, id: TextId, _max_w: f32) -> Size {
Size::new(10.0 * id.0 as f32, 20.0)
}
fn min_content(&mut self, id: TextId) -> f32 {
10.0 * id.0 as f32
}
fn image_size(&mut self, _id: ImageId) -> Size {
Size::new(0.0, 0.0)
}
}
struct T {
tree: Tree,
}
impl T {
fn new(spec: NodeSpec) -> Self {
let mut tree = Tree::new();
tree.push(
NIL,
Key::ROOT,
OriginId::HOST,
spec.width(300.0),
NodeContent::Container,
);
T { tree }
}
fn node(&mut self, parent: u32, spec: NodeSpec) -> u32 {
let key = Key::ROOT.index(self.tree.len() as u64);
self.tree
.push(parent, key, OriginId::HOST, spec, NodeContent::Container)
}
fn text(&mut self, parent: u32, chars: u32) -> u32 {
let key = Key::ROOT.index(self.tree.len() as u64);
self.tree.push(
parent,
key,
OriginId::HOST,
NodeSpec::default(),
NodeContent::Text(TextId(chars)),
)
}
fn line(&mut self, parent: u32) -> u32 {
let r = self.node(parent, NodeSpec::row());
self.text(r, 5);
r
}
fn run(&mut self) {
let mut scroll = ScrollStore::default();
compute(
&mut self.tree,
&mut Stub,
&mut scroll,
Size::new(1000.0, 600.0),
1.0,
);
}
fn h(&self, i: u32) -> f32 {
self.tree.size[i as usize].h
}
fn y(&self, i: u32) -> f32 {
self.tree.pos[i as usize].y
}
}
#[test]
fn a_row_keeps_the_height_of_its_text() {
let mut t = T::new(NodeSpec::column().height(50.0));
let rows: Vec<u32> = (0..4).map(|_| t.line(0)).collect();
t.run();
for (k, r) in rows.iter().enumerate() {
assert_eq!(t.h(*r), 20.0, "row {k}");
assert_eq!(t.y(*r), 20.0 * k as f32, "row {k} under the one before");
}
}
#[test]
fn a_header_is_whole_over_a_list_that_grows() {
let mut t = T::new(NodeSpec::column().height(50.0));
let head = t.node(0, NodeSpec::column().gap(4.0));
let lines: Vec<u32> = (0..3).map(|_| t.line(head)).collect();
let list = t.node(0, NodeSpec::column().height(Sizing::Grow(1.0)));
t.line(list);
t.run();
assert_eq!(t.h(head), 68.0, "three lines and two gaps");
assert!(lines.iter().all(|l| t.h(*l) == 20.0));
assert_eq!(t.h(list), 0.0, "the list takes what is left");
}
#[test]
fn a_scroller_and_a_clip_still_give() {
let mut t = T::new(NodeSpec::column().height(100.0));
t.line(0);
let list = t.node(0, NodeSpec::column().scroll_y());
for _ in 0..10 {
t.line(list);
}
t.run();
assert_eq!(t.h(list), 80.0, "the scroller, under the 20 px title");
let mut t = T::new(NodeSpec::column().height(100.0));
t.line(0);
let clip = t.node(0, NodeSpec::column().clip());
for _ in 0..10 {
t.line(clip);
}
t.run();
assert_eq!(t.h(clip), 80.0, "the clip likewise");
let mut t = T::new(NodeSpec::column().height(100.0));
let body = t.node(0, NodeSpec::column());
t.line(body);
let list = t.node(body, NodeSpec::column().scroll_y());
for _ in 0..10 {
t.line(list);
}
t.run();
assert_eq!(t.h(body), 100.0, "the column around them fits the root");
assert_eq!(t.h(list), 80.0, "its scroller gives what it had to");
}
#[test]
fn a_declared_floor_wins() {
let mut t = T::new(NodeSpec::column().height(30.0));
let a = t.node(0, NodeSpec::column().min_height(0.0));
t.line(a);
t.line(a);
let b = t.node(0, NodeSpec::column().min_height(10.0));
t.line(b);
t.line(b);
t.run();
assert_eq!(t.h(a) + t.h(b), 30.0, "{} + {}", t.h(a), t.h(b));
assert!(t.h(b) >= 10.0, "b stops at its floor: {}", t.h(b));
}
#[test]
fn a_row_still_squeezes_its_labels() {
let mut t = T::new(NodeSpec::row().height(20.0));
let a = t.node(0, NodeSpec::row());
t.text(a, 25);
let b = t.node(0, NodeSpec::row());
t.text(b, 10);
t.run();
let w = |i: u32| t.tree.size[i as usize].w;
assert_eq!((w(a), w(b)), (200.0, 100.0), "the 250 px label pays the 50");
}
#[test]
fn a_wrapped_row_keeps_its_lines() {
let mut t = T::new(NodeSpec::column().height(30.0));
let chips = t.node(
0,
NodeSpec::row().grow_width().wrap().gap(10.0).cross_gap(5.0),
);
for _ in 0..4 {
t.node(chips, NodeSpec::row().width(100.0).height(20.0));
}
let under = t.line(0);
t.run();
assert_eq!(t.h(chips), 45.0, "two lines and the gap between");
assert_eq!(t.y(under), 45.0, "the row under it below it");
}