cranpose-ui 0.1.164

UI primitives for Cranpose
Documentation
use cranpose_foundation::{InvalidationKind, ModifierInvalidation, ModifierNode, NodeCapabilities};

use super::*;
use crate::{modifier::Color, modifier_nodes::PaddingNode};

#[test]
fn attaches_padding_node_and_invalidates_layout() {
    let mut handle = ModifierChainHandle::new();

    let _ = handle.update(&Modifier::empty().padding(8.0));

    assert_eq!(handle.chain().len(), 1);

    let invalidations = handle.take_invalidations();
    assert_eq!(
        invalidations,
        vec![ModifierInvalidation::new(
            InvalidationKind::Layout,
            NodeCapabilities::LAYOUT
        )]
    );
}

#[test]
fn reuses_nodes_between_updates() {
    let mut handle = ModifierChainHandle::new();

    let _ = handle.update(&Modifier::empty().padding(12.0));
    let first_ptr = node_ptr::<PaddingNode>(&handle);
    handle.take_invalidations();

    let _ = handle.update(&Modifier::empty().padding(12.0));
    let second_ptr = node_ptr::<PaddingNode>(&handle);

    assert_eq!(first_ptr, second_ptr, "expected the node to be reused");
    assert!(
        handle.take_invalidations().is_empty(),
        "no additional invalidations should be issued for a pure update"
    );
}

#[test]
fn modifier_slices_capture_background_and_shape() {
    use crate::modifier::slices::collect_modifier_slices;

    let mut handle = ModifierChainHandle::new();
    let _ = handle.update(
        &Modifier::empty()
            .background(Color(0.2, 0.3, 0.4, 1.0))
            .then(Modifier::empty().rounded_corners(8.0)),
    );

    let slices = collect_modifier_slices(handle.chain());
    assert!(
        !slices.draw_commands().is_empty(),
        "Expected draw commands for background"
    );

    let _ = handle.update(
        &Modifier::empty()
            .rounded_corners(4.0)
            .then(Modifier::empty().background(Color(0.9, 0.1, 0.1, 1.0))),
    );
    let slices = collect_modifier_slices(handle.chain());
    assert!(
        !slices.draw_commands().is_empty(),
        "Expected draw commands after update"
    );

    let _ = handle.update(&Modifier::empty());
    let slices = collect_modifier_slices(handle.chain());
    assert!(
        slices.draw_commands().is_empty(),
        "Expected no draw commands with empty modifier"
    );
}

#[test]
fn capability_mask_updates_with_chain() {
    let mut handle = ModifierChainHandle::new();
    let _ = handle.update(&Modifier::empty().padding(4.0));
    assert_eq!(handle.capabilities(), NodeCapabilities::LAYOUT);
    assert!(handle.has_layout_nodes());
    assert!(!handle.has_draw_nodes());
    handle.take_invalidations();

    let color = Color(0.5, 0.6, 0.7, 1.0);
    let _ = handle.update(&Modifier::empty().background(color));
    assert_eq!(handle.capabilities(), NodeCapabilities::DRAW);
    assert!(handle.has_draw_nodes());
    assert!(!handle.has_layout_nodes());
}

#[test]
fn offset_update_invalidates_layout_for_retained_placement() {
    let mut handle = ModifierChainHandle::new();
    let _ = handle.update(&Modifier::empty().offset(0.0, 0.0));
    handle.take_invalidations();

    let _ = handle.update(&Modifier::empty().offset(12.0, 0.0));
    let invalidations = handle.take_invalidations();

    assert!(
        invalidations
            .iter()
            .any(|invalidation| invalidation.kind() == InvalidationKind::Layout),
        "expected offset value changes to refresh retained placement"
    );
    assert!(
        invalidations
            .iter()
            .all(|invalidation| invalidation.kind() != InvalidationKind::Draw),
        "offset value changes must not leave retained geometry stale through a draw-only invalidation"
    );
    assert_eq!(handle.resolved_modifiers().offset(), Point::new(12.0, 0.0));
}

fn node_ptr<N: ModifierNode + 'static>(handle: &ModifierChainHandle) -> *const N {
    handle
        .chain()
        .node::<N>(0)
        .map(|node| &*node as *const N)
        .expect("expected node to exist")
}