use super::CommonEvent;
use crate::{
impl_all_event, impl_common_event_deref, impl_compose_child_for_listener, impl_listener,
impl_multi_event_listener, prelude::*,
};
use rxrust::prelude::*;
use std::{
convert::Infallible,
time::{Duration, Instant},
};
mod from_mouse;
const MULTI_TAP_DURATION: Duration = Duration::from_millis(250);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PointerId(usize);
#[derive(Debug)]
pub struct PointerEvent {
pub id: PointerId,
pub width: f32,
pub height: f32,
pub pressure: f32,
pub tilt_x: f32,
pub tilt_y: f32,
pub twist: f32,
pub point_type: PointerType,
pub is_primary: bool,
pub common: CommonEvent,
}
bitflags! {
#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
pub struct MouseButtons: u8 {
const PRIMARY = 0b0000_0001;
const SECONDARY = 0b0000_0010;
const AUXILIARY = 0b0000_0100;
const FOURTH = 0b0000_1000;
const FIFTH = 0b0001_0000;
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum PointerType {
Mouse,
Pen,
Touch,
}
impl_multi_event_listener!(
"The listener use to fire and listen pointer events.",
Pointer,
"",
PointerDown,
"",
PointerDownCapture,
"",
PointerUp,
"",
PointerUpCapture,
"",
PointerMove,
"",
PointerMoveCapture,
"",
PointerCancel,
"",
PointerEnter,
"",
PointerLeave,
"",
Tap,
"",
TapCapture
);
impl_common_event_deref!(PointerEvent);
pub type PointerSubject = MutRefItemSubject<'static, AllPointer, Infallible>;
impl_compose_child_for_listener!(PointerListener);
fn x_times_tap_map_filter(
x: usize,
dur: Duration,
capture: bool,
) -> impl FnMut(&mut AllPointer) -> Option<&mut PointerEvent> {
assert!(x > 0);
struct TapInfo {
pointer_id: PointerId,
stamps: Vec<Instant>,
}
let mut type_info: Option<TapInfo> = None;
move |e: &mut AllPointer| {
let e = match e {
AllPointer::Tap(e) if !capture => e,
AllPointer::TapCapture(e) if capture => e,
_ => return None,
};
let now = Instant::now();
match &mut type_info {
Some(info) if info.pointer_id == e.id => {
if info.stamps.len() + 1 == x {
if now.duration_since(info.stamps[0]) <= dur {
type_info = None;
Some(e)
} else {
info.stamps.remove(0);
info.stamps.push(now);
None
}
} else {
info.stamps.push(now);
None
}
}
_ => {
type_info = Some(TapInfo { pointer_id: e.id, stamps: vec![now] });
None
}
}
}
}
impl PointerListener {
pub fn on_double_tap(self, handler: impl FnMut(&mut PointerEvent) + 'static) -> Self {
self.on_x_times_tap((2, handler))
}
pub fn on_double_tap_capture(self, handler: impl FnMut(&mut PointerEvent) + 'static) -> Self {
self.on_x_times_tap_capture((2, handler))
}
pub fn on_triple_tap(self, handler: impl FnMut(&mut PointerEvent) + 'static) -> Self {
self.on_x_times_tap((3, handler))
}
pub fn on_triple_tap_capture(self, handler: impl FnMut(&mut PointerEvent) + 'static) -> Self {
self.on_x_times_tap_capture((3, handler))
}
pub fn on_x_times_tap(
self,
(times, handler): (usize, impl FnMut(&mut PointerEvent) + 'static),
) -> Self {
self.on_x_times_tap_impl(times, MULTI_TAP_DURATION, false, handler)
}
pub fn on_x_times_tap_capture(
self,
(times, handler): (usize, impl FnMut(&mut PointerEvent) + 'static),
) -> Self {
self.on_x_times_tap_impl(times, MULTI_TAP_DURATION, true, handler)
}
fn on_x_times_tap_impl(
mut self,
times: usize,
dur: Duration,
capture: bool,
handler: impl FnMut(&mut PointerEvent) + 'static,
) -> Self {
self
.subject()
.filter_map(x_times_tap_map_filter(times, dur, capture))
.subscribe(handler);
self
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
reset_test_env,
test_helper::{MockBox, MockMulti, TestWindow},
};
use std::{cell::RefCell, rc::Rc};
use winit::{
dpi::LogicalPosition,
event::{DeviceId, ElementState, MouseButton, WindowEvent},
};
fn tap_on(wnd: &Window, x: f32, y: f32) {
let device_id = unsafe { DeviceId::dummy() };
let logical = LogicalPosition::new(x, y);
#[allow(deprecated)]
wnd.processes_native_event(WindowEvent::CursorMoved {
device_id,
position: logical.to_physical(1.),
});
wnd.process_mouse_input(device_id, ElementState::Pressed, MouseButton::Left);
wnd.process_mouse_input(device_id, ElementState::Released, MouseButton::Left);
}
#[test]
fn tap_focus() {
reset_test_env!();
let tap_cnt = Rc::new(RefCell::new(0));
let is_focused = Rc::new(RefCell::new(false));
let tap_cnt1 = tap_cnt.clone();
let tap_cnt2 = tap_cnt.clone();
let is_focused2 = is_focused.clone();
let w = fn_widget! {
let mut host = @MockMulti {};
watch!($host.has_focus())
.subscribe(move |v| *is_focused2.borrow_mut() = v);
@$host {
@MockBox {
size: Size::new(50., 50.,),
on_tap: move |_| *tap_cnt1.borrow_mut() += 1,
}
@MockBox {
size: Size::new(50., 50.,),
on_tap: move |_| *tap_cnt2.borrow_mut() += 1,
on_key_down: move |_| println!("dummy code"),
}
}
};
let mut wnd = TestWindow::new_with_size(w, Size::new(100., 100.));
wnd.draw_frame();
tap_on(&wnd, 25., 25.);
wnd.draw_frame();
assert_eq!(*tap_cnt.borrow(), 1);
assert!(!*is_focused.borrow());
tap_on(&wnd, 75., 25.);
wnd.draw_frame();
assert_eq!(*tap_cnt.borrow(), 2);
assert!(*is_focused.borrow());
}
}