cranpose-ui 0.9.5

UI primitives for Cranpose
Documentation
use std::{cell::Cell, rc::Rc};

use cranpose_foundation::PointerEventKind;
use cranpose_ui_layout::Axis;

use super::draggable_tests::{event, pointer_handler_for, with_test_runtime};
use crate::{Modifier, ScrollableState};

#[test]
fn custom_scroll_fling_follows_the_drag_and_stops_at_its_boundary_or_a_new_press() {
    let _app_context = crate::render_state::app_context_test_scope();
    for axis in [Axis::Horizontal, Axis::Vertical] {
        for interrupt in [false, true] {
            with_test_runtime(|| {
                let position = Rc::new(Cell::new(0.0_f32));
                let consumed = Rc::clone(&position);
                let state = ScrollableState::new(move |delta| {
                    let before = consumed.get();
                    let after = (before + delta).clamp(0.0, 240.0);
                    consumed.set(after);
                    after - before
                });
                let (handler, _chain) =
                    pointer_handler_for(Modifier::empty().scrollable(axis, state));
                let pointer = |kind, distance, time| {
                    let (x, y) = if axis.is_vertical() {
                        (0.0, distance)
                    } else {
                        (distance, 0.0)
                    };
                    handler(event(kind, x, y).with_time_ms(Some(time)));
                };
                pointer(PointerEventKind::Down, 0.0, 0);
                for step in 1..=6 {
                    pointer(PointerEventKind::Move, step as f32 * 12.0, step * 16);
                }
                let held = position.get();
                pointer(PointerEventKind::Up, 72.0, 104);
                let runtime = cranpose_core::current_runtime_handle().expect("scroll runtime");
                for frame in 0..=5 {
                    runtime.drain_frame_callbacks(frame * 16_666_667);
                }
                assert!(
                    position.get() > held + 20.0,
                    "release continues in the drag direction"
                );
                let after_fling = position.get();
                if interrupt {
                    pointer(PointerEventKind::Down, 72.0, 200);
                }
                for frame in 6..=240 {
                    runtime.drain_frame_callbacks(frame * 16_666_667);
                }
                if interrupt {
                    assert_eq!(position.get(), after_fling, "a new press stops the coast");
                } else {
                    assert_eq!(
                        position.get(),
                        240.0,
                        "the boundary consumes only the remaining distance"
                    );
                }
            });
        }
    }
}