use std::{
cell::RefCell,
collections::{HashMap, HashSet},
fmt,
rc::Rc,
};
use incular_core::{Offset, PointerPhase, time::Instant};
use crate::{
GestureAction, GestureDecision, GestureDisposition, GestureRecognizer, PointerDeviceKind,
PointerEvent, RawPointerEvent,
};
pub(crate) fn compute_hit_slop(kind: PointerDeviceKind) -> f32 {
match kind {
PointerDeviceKind::Mouse => crate::constants::PRECISE_POINTER_HIT_SLOP,
PointerDeviceKind::Touch
| PointerDeviceKind::Stylus
| PointerDeviceKind::InvertedStylus
| PointerDeviceKind::Trackpad
| PointerDeviceKind::Unknown => crate::constants::TOUCH_SLOP,
}
}
pub(crate) fn compute_pan_slop(kind: PointerDeviceKind) -> f32 {
match kind {
PointerDeviceKind::Mouse => crate::constants::PRECISE_POINTER_PAN_SLOP,
PointerDeviceKind::Touch
| PointerDeviceKind::Stylus
| PointerDeviceKind::InvertedStylus
| PointerDeviceKind::Trackpad
| PointerDeviceKind::Unknown => crate::constants::PAN_SLOP,
}
}
pub(crate) fn distance(offset: Offset) -> f32 {
offset.x.hypot(offset.y)
}
pub type ArenaResolver = Box<dyn FnMut(u64, GestureDisposition) -> Option<GestureDisposition>>;
#[derive(Default)]
pub(crate) struct ArenaLink {
external: Option<ArenaResolver>,
open: HashSet<u64>,
positions: HashMap<u64, (Offset, Offset)>,
}
impl fmt::Debug for ArenaLink {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ArenaLink")
.field("external", &self.external.is_some())
.field("open", &self.open)
.finish_non_exhaustive()
}
}
impl ArenaLink {
pub(crate) fn set_external(&mut self, resolver: ArenaResolver) {
self.external = Some(resolver);
self.open.clear();
}
pub(crate) fn add(&mut self, pointer: u64) {
if self.external.is_none() {
self.open.insert(pointer);
}
}
#[must_use]
pub(crate) fn resolve(
&mut self,
pointer: u64,
disposition: GestureDisposition,
) -> Option<GestureDisposition> {
if let Some(resolver) = self.external.as_mut() {
return resolver(pointer, disposition);
}
match disposition {
GestureDisposition::Rejected => self
.open
.remove(&pointer)
.then_some(GestureDisposition::Rejected),
GestureDisposition::Accepted
| GestureDisposition::Pending
| GestureDisposition::Cancelled => None,
}
}
#[must_use]
pub(crate) fn close(&mut self, pointer: u64) -> Option<GestureDisposition> {
self.open
.remove(&pointer)
.then_some(GestureDisposition::Accepted)
}
pub(crate) fn with_deltas(&mut self, mut event: RawPointerEvent) -> RawPointerEvent {
let current = (event.position, event.local_position);
let previous = match event.phase {
PointerPhase::Down => None,
_ => self.positions.get(&event.pointer).copied(),
};
if let Some((global, local)) = previous {
event.delta = current.0 - global;
event.local_delta = current.1 - local;
} else {
event.delta = Offset::ZERO;
event.local_delta = Offset::ZERO;
}
match event.phase {
PointerPhase::Up | PointerPhase::Cancel => {
self.positions.remove(&event.pointer);
}
PointerPhase::Down | PointerPhase::Move => {
self.positions.insert(event.pointer, current);
}
PointerPhase::Enter | PointerPhase::Exit => {}
}
event
}
}
pub(crate) trait ArenaMember {
fn link(&mut self) -> &mut ArenaLink;
fn member_add_pointer(&mut self, event: RawPointerEvent);
fn member_handle_event(&mut self, event: RawPointerEvent);
fn member_accept(&mut self, pointer: u64);
fn member_reject(&mut self, pointer: u64);
fn member_poll(&mut self, now: Instant);
fn apply(&mut self, pointer: u64, disposition: Option<GestureDisposition>) {
match disposition {
Some(GestureDisposition::Accepted) => self.member_accept(pointer),
Some(GestureDisposition::Rejected) => self.member_reject(pointer),
_ => {}
}
}
fn settle(&mut self, pointer: u64, disposition: GestureDisposition) {
let decided = self.link().resolve(pointer, disposition);
self.apply(pointer, decided);
}
fn drive(&mut self, event: RawPointerEvent) {
self.member_poll(event.time);
match event.phase {
PointerPhase::Enter | PointerPhase::Exit => {}
PointerPhase::Down => {
self.member_add_pointer(event);
self.member_handle_event(event);
let decided = self.link().close(event.pointer);
self.apply(event.pointer, decided);
}
PointerPhase::Move | PointerPhase::Up | PointerPhase::Cancel => {
self.member_handle_event(event);
}
}
}
}
macro_rules! arena_recognizer {
($recognizer:ty) => {
impl $crate::arena_member::ArenaMember for $recognizer {
fn link(&mut self) -> &mut $crate::arena_member::ArenaLink {
&mut self.link
}
fn member_add_pointer(&mut self, event: $crate::RawPointerEvent) {
self.add_pointer(event);
}
fn member_handle_event(&mut self, event: $crate::RawPointerEvent) {
self.handle_event(event);
}
fn member_accept(&mut self, pointer: u64) {
self.accept_gesture(pointer);
}
fn member_reject(&mut self, pointer: u64) {
self.reject_gesture(pointer);
}
fn member_poll(&mut self, now: incular_core::time::Instant) {
self.poll(now);
}
}
impl $crate::arena_member::ArenaRecognizer for $recognizer {
fn set_external_arena(&mut self, resolver: $crate::ArenaResolver) {
self.link.set_external(resolver);
}
fn add_pointer(&mut self, event: $crate::RawPointerEvent) {
<$recognizer>::add_pointer(self, event);
}
fn handle_event(&mut self, event: $crate::RawPointerEvent) {
<$recognizer>::handle_event(self, event);
}
fn accept_gesture(&mut self, pointer: u64) {
<$recognizer>::accept_gesture(self, pointer);
}
fn reject_gesture(&mut self, pointer: u64) {
<$recognizer>::reject_gesture(self, pointer);
}
fn poll(&mut self, now: incular_core::time::Instant) {
<$recognizer>::poll(self, now);
}
}
impl $recognizer {
#[must_use]
pub fn with_external_arena(
mut self,
resolver: impl FnMut(
u64,
$crate::GestureDisposition,
) -> Option<$crate::GestureDisposition>
+ 'static,
) -> Self {
self.link.set_external(Box::new(resolver));
self
}
pub fn handle_raw(&mut self, event: $crate::RawPointerEvent) {
$crate::arena_member::ArenaMember::drive(self, event);
}
}
};
}
pub(crate) use arena_recognizer;
pub trait ArenaRecognizer: 'static {
fn set_external_arena(&mut self, resolver: ArenaResolver);
fn add_pointer(&mut self, event: RawPointerEvent);
fn handle_event(&mut self, event: RawPointerEvent);
fn accept_gesture(&mut self, pointer: u64);
fn reject_gesture(&mut self, pointer: u64);
fn poll(&mut self, now: Instant);
}
type Requests = Rc<RefCell<Vec<(u64, GestureDisposition)>>>;
pub struct ArenaGestureRecognizer<R: ArenaRecognizer> {
recognizer: R,
requests: Requests,
pointers: HashMap<u64, bool>,
last: Option<(u64, PointerPhase)>,
}
impl<R: ArenaRecognizer> ArenaGestureRecognizer<R> {
#[must_use]
pub fn new(mut recognizer: R) -> Self {
let requests = Requests::default();
let sink = requests.clone();
recognizer.set_external_arena(Box::new(move |pointer, disposition| {
sink.borrow_mut().push((pointer, disposition));
None
}));
Self {
recognizer,
requests,
pointers: HashMap::new(),
last: None,
}
}
#[must_use]
pub fn recognizer(&self) -> &R {
&self.recognizer
}
pub fn recognizer_mut(&mut self) -> &mut R {
&mut self.recognizer
}
fn decide(&mut self, pointer: u64, phase: PointerPhase) -> GestureDecision {
let requests = std::mem::take(&mut *self.requests.borrow_mut());
let mut decision = GestureDecision::Pending;
for (requested, disposition) in requests {
let Some(accepted) = self.pointers.get(&requested).copied() else {
continue;
};
match disposition {
GestureDisposition::Accepted if !accepted && requested == pointer => {
decision = GestureDecision::Accept(GestureAction::ArenaAccepted { pointer });
}
GestureDisposition::Rejected if !accepted && requested == pointer => {
self.pointers.remove(&requested);
self.recognizer.reject_gesture(requested);
return GestureDecision::Reject;
}
GestureDisposition::Rejected => {
self.pointers.remove(&requested);
self.recognizer.reject_gesture(requested);
}
GestureDisposition::Accepted
| GestureDisposition::Pending
| GestureDisposition::Cancelled => {}
}
}
if decision == GestureDecision::Pending
&& phase == PointerPhase::Up
&& self.pointers.get(&pointer) == Some(&false)
{
decision = GestureDecision::Accept(GestureAction::ArenaAccepted { pointer });
}
if matches!(phase, PointerPhase::Up | PointerPhase::Cancel)
&& self.pointers.get(&pointer) == Some(&true)
{
self.pointers.remove(&pointer);
}
decision
}
}
impl<R: ArenaRecognizer> GestureRecognizer for ArenaGestureRecognizer<R> {
fn observe(&mut self, event: PointerEvent) -> GestureDecision {
self.observe_raw(RawPointerEvent::from(event))
}
fn observe_raw(&mut self, event: RawPointerEvent) -> GestureDecision {
self.recognizer.poll(event.time);
match event.phase {
PointerPhase::Enter | PointerPhase::Exit => return GestureDecision::Pending,
PointerPhase::Down => {
self.pointers.insert(event.pointer, false);
self.recognizer.add_pointer(event);
}
PointerPhase::Move | PointerPhase::Up | PointerPhase::Cancel => {
if !self.pointers.contains_key(&event.pointer) {
return GestureDecision::Pending;
}
}
}
self.last = Some((event.pointer, event.phase));
self.recognizer.handle_event(event);
self.decide(event.pointer, event.phase)
}
fn dispatch(&mut self, action: GestureAction) {
if let GestureAction::ArenaAccepted { pointer } = action
&& let Some(accepted) = self.pointers.get_mut(&pointer)
{
*accepted = true;
self.recognizer.accept_gesture(pointer);
let _ = self.decide(pointer, PointerPhase::Move);
if matches!(
self.last,
Some((last, PointerPhase::Up | PointerPhase::Cancel)) if last == pointer
) {
self.pointers.remove(&pointer);
}
}
}
fn cancel(&mut self) {
if let Some((pointer, _)) = self.last
&& self.pointers.get(&pointer) == Some(&false)
{
self.pointers.remove(&pointer);
self.recognizer.reject_gesture(pointer);
}
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
}