lgui-core 0.2.0

Platform-neutral runtime and UI primitives for LGUI
Documentation
use super::*;
use crate::core::{EdgeInsets, HostTree, UiNode, UiNodeKind};

#[test]
fn layout_tree_positions_a_stack_and_translates_descendants() {
    let root_id = UiId::owned("root");
    let child_id = UiId::owned("child");
    let grandchild_id = UiId::owned("grandchild");
    let mut tree = HostTree::new();
    tree.push(
        UiNode::new(
            root_id.clone(),
            UiNodeKind::Group,
            UiRect::new(10.0, 20.0, 210.0, 220.0),
        )
        .layout(LayoutSpec::Stack {
            axis: Axis::Vertical,
            gap: 4.0,
            padding: EdgeInsets::all(8.0),
            align: Align::Start,
        }),
    );
    tree.push(
        UiNode::new(
            child_id.clone(),
            UiNodeKind::Group,
            UiRect::new(0.0, 0.0, 40.0, 30.0),
        )
        .parent(root_id)
        .layout(LayoutSpec::Fixed(Size::new(40.0, 30.0))),
    );
    tree.push(
        UiNode::new(
            grandchild_id.clone(),
            UiNodeKind::Text,
            UiRect::new(5.0, 5.0, 15.0, 15.0),
        )
        .parent(child_id.clone()),
    );

    apply_layout_tree(&mut tree);

    assert_eq!(
        tree.node(&child_id).unwrap().layout_rect,
        UiRect::new(18.0, 28.0, 58.0, 58.0)
    );
    assert_eq!(
        tree.node(&grandchild_id).unwrap().layout_rect,
        UiRect::new(23.0, 33.0, 33.0, 43.0)
    );
}

#[test]
fn retained_layout_reuses_results_when_only_paint_content_changes() {
    let id = UiId::owned("text");
    let mut runtime = LayoutRuntime::new();
    let mut first = HostTree::new();
    first.push(UiNode::new(
        id.clone(),
        UiNodeKind::Text,
        UiRect::new(0.0, 0.0, 20.0, 10.0),
    ));
    assert_eq!(runtime.update(&mut first).laid_out_nodes, 1);

    let mut second = HostTree::new();
    second.push(
        UiNode::new(id, UiNodeKind::Text, UiRect::new(0.0, 0.0, 20.0, 10.0)).text(
            "changed",
            crate::core::TextStyle::new(crate::core::Color::WHITE, 12.0, 400),
        ),
    );
    let metrics = runtime.update(&mut second);
    assert_eq!(metrics.laid_out_nodes, 0);
    assert_eq!(metrics.reused_nodes, 1);
}

#[test]
fn leaf_geometry_change_lays_out_only_its_nearest_stack_boundary() {
    let left = UiId::owned("left-stack");
    let left_child = UiId::owned("left-child");
    let right = UiId::owned("right-stack");
    let right_child = UiId::owned("right-child");
    let build = |left_width: f32| {
        let mut tree = HostTree::new();
        for (id, rect) in [
            (left.clone(), UiRect::new(0.0, 0.0, 100.0, 100.0)),
            (right.clone(), UiRect::new(100.0, 0.0, 200.0, 100.0)),
        ] {
            tree.push(
                UiNode::new(id, UiNodeKind::Group, rect).layout(LayoutSpec::Stack {
                    axis: Axis::Vertical,
                    gap: 0.0,
                    padding: EdgeInsets::all(0.0),
                    align: Align::Start,
                }),
            );
        }
        tree.push(
            UiNode::new(
                left_child.clone(),
                UiNodeKind::Panel,
                UiRect::new(0.0, 0.0, left_width, 20.0),
            )
            .parent(left.clone())
            .layout(LayoutSpec::Fixed(Size::new(left_width, 20.0))),
        );
        tree.push(
            UiNode::new(
                right_child.clone(),
                UiNodeKind::Panel,
                UiRect::new(0.0, 0.0, 40.0, 20.0),
            )
            .parent(right.clone())
            .layout(LayoutSpec::Fixed(Size::new(40.0, 20.0))),
        );
        tree
    };
    let mut runtime = LayoutRuntime::new();
    runtime.update(&mut build(30.0));

    let metrics = runtime.update(&mut build(50.0));

    assert_eq!(metrics.laid_out_nodes, 2);
    assert_eq!(metrics.reused_nodes, 2);
}

#[test]
fn inserting_a_stack_child_invalidates_its_layout_boundary() {
    let root = UiId::owned("stack");
    let first_child = UiId::owned("first");
    let mut first = HostTree::new();
    first.push(
        UiNode::new(
            root.clone(),
            UiNodeKind::Group,
            UiRect::new(0.0, 0.0, 100.0, 100.0),
        )
        .layout(LayoutSpec::Stack {
            axis: Axis::Vertical,
            gap: 4.0,
            padding: EdgeInsets::all(0.0),
            align: Align::Start,
        }),
    );
    first.push(
        UiNode::new(
            first_child.clone(),
            UiNodeKind::Panel,
            UiRect::new(0.0, 0.0, 20.0, 20.0),
        )
        .parent(root.clone())
        .layout(LayoutSpec::Fixed(Size::new(20.0, 20.0))),
    );
    let mut runtime = LayoutRuntime::new();
    runtime.update(&mut first);
    let mut second = first.clone();
    second.push(
        UiNode::new(
            UiId::owned("second"),
            UiNodeKind::Panel,
            UiRect::new(0.0, 0.0, 20.0, 20.0),
        )
        .parent(root)
        .layout(LayoutSpec::Fixed(Size::new(20.0, 20.0))),
    );

    let metrics = runtime.update(&mut second);

    assert_eq!(metrics.laid_out_nodes, 3);
    assert_eq!(
        second.node(&UiId::owned("second")).unwrap().layout_rect.top,
        24.0
    );
}