cranpose-ui 0.1.164

UI primitives for Cranpose
Documentation
use cranpose_core::{Composition, MemoryApplier};

use super::*;

#[test]
fn interaction_ids_do_not_use_process_global_counters() {
    let source = include_str!("../interaction.rs");
    let source_counter = ["static ", "NEXT_SOURCE_ID"].concat();
    let press_counter = ["static ", "NEXT_PRESS_ID"].concat();

    assert!(
        !source.contains(&source_counter) && !source.contains(&press_counter),
        "interaction source and press ids must be owned by the interaction source instance"
    );
}

#[test]
fn interaction_source_tracks_active_press_state() {
    let composition = Composition::new(MemoryApplier::new());
    let source = MutableInteractionSource::with_runtime(composition.runtime_handle());
    let pressed = source.collectIsPressedAsState();

    assert!(!pressed.get());

    let first = source.press(Point { x: 1.0, y: 2.0 });
    assert!(pressed.get());

    let second = source.press(Point { x: 3.0, y: 4.0 });
    assert_ne!(first.id(), second.id());
    source.release(first);
    assert!(pressed.get());

    source.cancel(second);
    assert!(!pressed.get());
}

#[test]
fn interaction_source_ids_are_instance_owned() {
    let composition = Composition::new(MemoryApplier::new());
    let first = MutableInteractionSource::with_runtime(composition.runtime_handle());
    let first_clone = first;
    let second = MutableInteractionSource::with_runtime(composition.runtime_handle());

    assert_eq!(first.id(), first_clone.id());
    assert_ne!(first.id(), second.id());
    assert_eq!(first.press(Point { x: 0.0, y: 0.0 }).id(), 1);
    assert_eq!(first.press(Point { x: 1.0, y: 1.0 }).id(), 2);
    assert_eq!(second.press(Point { x: 0.0, y: 0.0 }).id(), 1);
}

#[test]
fn collect_is_pressed_as_state_tracks_press_emissions() {
    let composition = Composition::new(MemoryApplier::new());
    let source = MutableInteractionSource::with_runtime(composition.runtime_handle());
    let pressed = collect_is_pressed_as_state(&source);

    assert!(!pressed.get());

    let press = PressInteractionPress {
        id: 7,
        press_position: Point { x: 4.0, y: 5.0 },
    };
    source.emit(Interaction::Press(PressInteraction::Press(press)));
    assert!(pressed.get(), "pressed after a Press emission");

    source.emit(Interaction::Press(PressInteraction::Release(
        PressInteractionRelease { press },
    )));
    assert!(!pressed.get(), "released after a Release emission");

    source.emit(Interaction::Press(PressInteraction::Press(press)));
    assert!(pressed.get(), "pressed again after a new Press emission");

    source.emit(Interaction::Press(PressInteraction::Cancel(
        PressInteractionCancel { press },
    )));
    assert!(!pressed.get(), "released after a Cancel emission");
}

#[composable]
fn PressedReader(
    observed: Rc<RefCell<Vec<bool>>>,
    source_slot: Rc<RefCell<Option<MutableInteractionSource>>>,
) {
    let source = rememberMutableInteractionSource();
    source_slot.borrow_mut().replace(source);
    let pressed = collect_is_pressed_as_state(&source);
    observed.borrow_mut().push(pressed.value());
}

#[test]
fn collect_is_pressed_as_state_recomposes_readers() {
    let observed = Rc::new(RefCell::new(Vec::<bool>::new()));
    let source_slot = Rc::new(RefCell::new(None::<MutableInteractionSource>));

    let mut composition = {
        let observed = Rc::clone(&observed);
        let source_slot = Rc::clone(&source_slot);
        crate::run_test_composition(move || {
            PressedReader(Rc::clone(&observed), Rc::clone(&source_slot));
        })
    };

    assert_eq!(observed.borrow().as_slice(), &[false]);

    let source = *source_slot
        .borrow()
        .as_ref()
        .expect("interaction source captured");
    let press = composition.with_app_context(|| source.press(Point { x: 1.0, y: 1.0 }));
    while composition
        .process_invalid_scopes()
        .expect("process press invalidation")
    {}
    assert_eq!(
        observed.borrow().last(),
        Some(&true),
        "press emission should recompose readers with pressed=true"
    );

    composition.with_app_context(|| source.release(press));
    while composition
        .process_invalid_scopes()
        .expect("process release invalidation")
    {}
    assert_eq!(
        observed.borrow().last(),
        Some(&false),
        "release emission should recompose readers with pressed=false"
    );
}

#[test]
fn interaction_source_exposes_latest_interaction() {
    let composition = Composition::new(MemoryApplier::new());
    let source = MutableInteractionSource::with_runtime(composition.runtime_handle());
    let last_interaction = source.collectLastInteractionAsState();

    assert_eq!(last_interaction.get(), None);

    let press = source.press(Point { x: 8.0, y: 12.0 });
    assert_eq!(
        last_interaction.get(),
        Some(Interaction::Press(PressInteraction::Press(press)))
    );
    assert_eq!(press.press_position, Point { x: 8.0, y: 12.0 });

    source.release(press);
    assert_eq!(
        last_interaction.get(),
        Some(Interaction::Press(PressInteraction::Release(
            PressInteractionRelease { press }
        )))
    );
}