mirui 0.11.2

A lightweight, no_std ECS-driven UI framework for embedded, desktop, and WebAssembly
Documentation
pub mod event;
pub mod recognizer;
pub mod system;

pub use event::GestureEvent;
pub use recognizer::GestureRecognizer;
pub use system::{GestureEvents, GestureSystem};

#[cfg(test)]
mod tests {
    use crate::ecs::Entity;
    use crate::event::input::InputEvent;
    use crate::types::Fixed;

    use super::recognizer::GestureState;
    use super::*;

    fn entity(id: u32) -> Entity {
        Entity { id, generation: 0 }
    }

    #[test]
    fn tap_on_quick_release() {
        let mut rec = GestureRecognizer::new();
        let mut events = GestureEvents::new();
        let target = entity(1);

        rec.update(
            &InputEvent::PointerDown {
                id: 0,
                x: Fixed::from_int(50),
                y: Fixed::from_int(50),
            },
            0,
            Some(target),
            &mut events,
        );
        assert!(events.buffer.is_empty());

        rec.update(
            &InputEvent::PointerUp {
                id: 0,
                x: Fixed::from_int(50),
                y: Fixed::from_int(50),
            },
            100,
            None,
            &mut events,
        );
        assert_eq!(events.buffer.len(), 1);
        assert!(matches!(events.buffer[0], GestureEvent::Tap { .. }));
    }

    #[test]
    fn drag_after_threshold() {
        let mut rec = GestureRecognizer::new();
        let mut events = GestureEvents::new();
        let target = entity(2);

        rec.update(
            &InputEvent::PointerDown {
                id: 0,
                x: Fixed::from_int(10),
                y: Fixed::from_int(10),
            },
            0,
            Some(target),
            &mut events,
        );

        rec.update(
            &InputEvent::PointerMove {
                id: 0,
                x: Fixed::from_int(25),
                y: Fixed::from_int(10),
            },
            50,
            None,
            &mut events,
        );
        assert_eq!(events.buffer.len(), 1);
        assert!(matches!(events.buffer[0], GestureEvent::DragStart { .. }));

        events.clear();
        rec.update(
            &InputEvent::PointerMove {
                id: 0,
                x: Fixed::from_int(30),
                y: Fixed::from_int(10),
            },
            80,
            None,
            &mut events,
        );
        assert_eq!(events.buffer.len(), 1);
        assert!(matches!(events.buffer[0], GestureEvent::DragMove { .. }));

        events.clear();
        rec.update(
            &InputEvent::PointerUp {
                id: 0,
                x: Fixed::from_int(30),
                y: Fixed::from_int(10),
            },
            100,
            None,
            &mut events,
        );
        assert_eq!(events.buffer.len(), 1);
        assert!(matches!(events.buffer[0], GestureEvent::DragEnd { .. }));
    }

    #[test]
    fn long_press_fires() {
        let mut rec = GestureRecognizer::new();
        let mut events = GestureEvents::new();
        let target = entity(3);

        rec.update(
            &InputEvent::PointerDown {
                id: 0,
                x: Fixed::from_int(50),
                y: Fixed::from_int(50),
            },
            1000,
            Some(target),
            &mut events,
        );

        rec.check_long_press(1400, &mut events);
        assert!(events.buffer.is_empty());

        rec.check_long_press(1501, &mut events);
        assert_eq!(events.buffer.len(), 1);
        assert!(matches!(events.buffer[0], GestureEvent::LongPress { .. }));
    }

    #[test]
    fn scroll_claimed_suppresses_gesture() {
        let mut rec = GestureRecognizer::new();
        let mut events = GestureEvents::new();
        let target = entity(4);

        rec.update(
            &InputEvent::PointerDown {
                id: 0,
                x: Fixed::from_int(10),
                y: Fixed::from_int(10),
            },
            0,
            Some(target),
            &mut events,
        );
        rec.scroll_claimed = true;

        rec.update(
            &InputEvent::PointerMove {
                id: 0,
                x: Fixed::from_int(50),
                y: Fixed::from_int(50),
            },
            50,
            None,
            &mut events,
        );
        assert!(events.buffer.is_empty());

        rec.update(
            &InputEvent::PointerUp {
                id: 0,
                x: Fixed::from_int(50),
                y: Fixed::from_int(50),
            },
            100,
            None,
            &mut events,
        );
        assert!(events.buffer.is_empty());
        assert_eq!(rec.state, GestureState::Idle);
    }
}