lgui-core 0.2.0

Platform-neutral runtime and UI primitives for LGUI
Documentation
use super::*;
use crate::core::{AnimProperty, AnimationBinding, RenderPhase, UiNodeKind, VisualStyle};

#[test]
fn retained_clone_shares_unchanged_nodes_and_detaches_only_updated_nodes() {
    let first_id = UiId::new("first");
    let second_id = UiId::new("second");
    let mut original = HostTree::new();
    original.push(UiNode::new(
        first_id.clone(),
        UiNodeKind::Panel,
        UiRect::new(0.0, 0.0, 10.0, 10.0),
    ));
    original.push(UiNode::new(
        second_id.clone(),
        UiNodeKind::Panel,
        UiRect::new(10.0, 0.0, 20.0, 10.0),
    ));
    original.take_projection_changes();

    let mut next = original.clone();
    assert!(Arc::ptr_eq(&original.nodes[0], &next.nodes[0]));
    assert!(Arc::ptr_eq(&original.nodes[1], &next.nodes[1]));

    next.upsert(
        UiNode::new(
            first_id.clone(),
            UiNodeKind::Panel,
            UiRect::new(0.0, 0.0, 10.0, 10.0),
        )
        .style(VisualStyle::filled(crate::core::Color::WHITE)),
    );

    assert!(!Arc::ptr_eq(&original.nodes[0], &next.nodes[0]));
    assert!(Arc::ptr_eq(&original.nodes[1], &next.nodes[1]));
    assert_ne!(
        original.node(&first_id).unwrap().style,
        next.node(&first_id).unwrap().style
    );
    assert!(next.node(&second_id).is_some());
}

#[test]
fn runtime_sync_changes_track_only_relevant_nodes() {
    let plain_id = UiId::new("plain");
    let animated_id = UiId::new("animated");
    let mut tree = HostTree::new();
    tree.push(UiNode::new(
        plain_id.clone(),
        UiNodeKind::Panel,
        UiRect::new(0.0, 0.0, 10.0, 10.0),
    ));
    tree.push(
        UiNode::new(
            animated_id.clone(),
            UiNodeKind::Panel,
            UiRect::new(10.0, 0.0, 20.0, 10.0),
        )
        .animation(AnimationBinding::new(AnimProperty::Opacity, 0.0, 1.0))
        .animation_target(AnimProperty::Opacity, false),
    );
    assert_eq!(tree.animation_sync_ids().count(), 1);
    tree.take_projection_changes();

    tree.upsert(
        UiNode::new(
            plain_id,
            UiNodeKind::Panel,
            UiRect::new(0.0, 0.0, 10.0, 10.0),
        )
        .style(VisualStyle::filled(crate::core::Color::WHITE)),
    );

    assert_eq!(tree.animation_sync_ids().count(), 0);
    assert!(!tree.needs_focus_sync());
}

#[test]
fn popup_hit_testing_wins_over_later_siblings_and_ancestor_clips() {
    let clip_id = UiId::new("clip");
    let popup_id = UiId::new("popup");
    let sibling_id = UiId::new("sibling");
    let target = UiRect::new(20.0, 20.0, 40.0, 40.0);
    let mut tree = HostTree::new();
    tree.push(
        UiNode::new(
            clip_id.clone(),
            UiNodeKind::Clip,
            UiRect::new(0.0, 0.0, 10.0, 10.0),
        )
        .clip(UiRect::new(0.0, 0.0, 10.0, 10.0), 0.0, 0.0),
    );
    tree.push(
        UiNode::new(popup_id.clone(), UiNodeKind::Button, target)
            .parent(clip_id)
            .interaction(InteractionRole::Button)
            .render_phase(RenderPhase::Popup),
    );
    tree.push(
        UiNode::new(sibling_id, UiNodeKind::Button, target)
            .interaction(InteractionRole::Button)
            .render_phase(RenderPhase::Overlay),
    );

    let hit = tree
        .hit_test(Point::new(30.0, 30.0))
        .expect("popup should be hittable outside its ancestor clip");
    assert_eq!(hit.id, popup_id);
}

#[test]
fn interactive_titlebar_children_take_precedence_over_the_drag_region() {
    let drag_id = UiId::new("titlebar");
    let button_id = UiId::new("close");
    let mut tree = HostTree::new();
    tree.push(
        UiNode::new(
            drag_id.clone(),
            UiNodeKind::Group,
            UiRect::new(0.0, 0.0, 400.0, 64.0),
        )
        .interaction(InteractionRole::WindowDragRegion)
        .event_policy(crate::core::EventPolicy::NONE),
    );
    tree.push(
        UiNode::new(
            button_id.clone(),
            UiNodeKind::Button,
            UiRect::new(350.0, 0.0, 400.0, 64.0),
        )
        .parent(drag_id.clone())
        .interaction(InteractionRole::Button),
    );

    let drag = tree
        .hit_test(Point::new(100.0, 32.0))
        .expect("titlebar background should be draggable");
    assert_eq!(drag.id, drag_id);
    assert_eq!(drag.interaction, InteractionRole::WindowDragRegion);

    let button = tree
        .hit_test(Point::new(375.0, 32.0))
        .expect("titlebar button should remain interactive");
    assert_eq!(button.id, button_id);
    assert_eq!(button.interaction, InteractionRole::Button);
}