use repose_core::Vec2;
use crate::input::*;
use std::rc::Rc;
use web_time::{Duration, Instant};
pub struct GestureDetector {
on_tap: Option<Rc<dyn Fn(Vec2)>>,
on_double_tap: Option<Rc<dyn Fn(Vec2)>>,
on_long_press: Option<Rc<dyn Fn(Vec2)>>,
on_drag: Option<Rc<dyn Fn(DragEvent)>>,
on_swipe: Option<Rc<dyn Fn(SwipeDirection)>>,
last_tap: Option<Instant>,
press_start: Option<(Instant, Vec2)>,
drag_start: Option<Vec2>,
}
pub struct DragEvent {
pub start: Vec2,
pub current: Vec2,
pub delta: Vec2,
pub velocity: Vec2,
}
pub enum SwipeDirection {
Up,
Down,
Left,
Right,
}
impl Default for GestureDetector {
fn default() -> Self {
Self::new()
}
}
impl GestureDetector {
pub fn new() -> Self {
Self {
on_tap: None,
on_double_tap: None,
on_long_press: None,
on_drag: None,
on_swipe: None,
last_tap: None,
press_start: None,
drag_start: None,
}
}
pub fn handle_pointer(&mut self, event: &PointerEvent) {
match event.event {
PointerEventKind::Down(_) => {
self.press_start = Some((Instant::now(), event.position));
self.drag_start = Some(event.position);
if let Some(last) = self.last_tap
&& (Instant::now() - last) < Duration::from_millis(300)
{
if let Some(cb) = &self.on_double_tap {
cb(event.position);
}
self.last_tap = None;
}
}
PointerEventKind::Up(_) => {
if let Some((start_time, start_pos)) = self.press_start {
let elapsed = Instant::now() - start_time;
let distance = ((event.position.x - start_pos.x).powi(2)
+ (event.position.y - start_pos.y).powi(2))
.sqrt();
if elapsed < Duration::from_millis(200) && distance < 10.0 {
if let Some(cb) = &self.on_tap {
cb(event.position);
}
self.last_tap = Some(Instant::now());
} else if distance > 50.0 {
let dx = event.position.x - start_pos.x;
let dy = event.position.y - start_pos.y;
if let Some(cb) = &self.on_swipe {
let dir = if dx.abs() > dy.abs() {
if dx > 0.0 {
SwipeDirection::Right
} else {
SwipeDirection::Left
}
} else if dy > 0.0 {
SwipeDirection::Down
} else {
SwipeDirection::Up
};
cb(dir);
}
}
}
self.press_start = None;
self.drag_start = None;
}
PointerEventKind::Move => {
if let Some(start) = self.drag_start
&& let Some(cb) = &self.on_drag
{
cb(DragEvent {
start,
current: event.position,
delta: Vec2 {
x: event.position.x - start.x,
y: event.position.y - start.y,
},
velocity: Vec2::default(), });
}
if let Some((start_time, pos)) = self.press_start
&& (Instant::now() - start_time) > Duration::from_millis(500)
{
if let Some(cb) = &self.on_long_press {
cb(pos);
}
self.press_start = None; }
}
_ => {}
}
}
}