use super::{GrabMode, Press, PressSource, velocity};
use crate::config::EventWindowConfig;
use crate::event::{Event, EventCx, EventState, FocusSource, NavAdvance, PressStart};
use crate::geom::{Affine, DVec2, Vec2};
use crate::{ActionRedraw, Id, Node};
use cast::{Cast, CastApprox, CastFloat, Conv};
use smallvec::SmallVec;
use winit::event::FingerId;
const MAX_TOUCHES: usize = 10;
const MAX_PANS: usize = 2;
const VELOCITY_LEN: usize = 2;
const MAX_PAN_GRABS: usize = 2;
#[derive(Clone, Debug)]
pub(super) struct TouchGrab {
finger_id: FingerId,
pub(super) start_id: Id,
pub(super) depress: Option<Id>,
over: Option<Id>,
last_position: DVec2,
mode: GrabMode,
pan_grab: (u16, u16),
vel_index: u16,
cancel: bool,
}
impl TouchGrab {
fn flush_click_move(&mut self) -> Option<ActionRedraw> {
if self.mode == GrabMode::Click {
if self.start_id == self.over {
if self.depress != self.over {
self.depress = self.over.clone();
return Some(ActionRedraw);
}
} else if self.depress.is_some() {
self.depress = None;
return Some(ActionRedraw);
}
}
None
}
}
#[derive(Clone, Debug)]
struct PanGrab {
id: Id,
mode: (bool, bool), n: u16,
coords: [(DVec2, DVec2); MAX_PAN_GRABS],
}
#[derive(Default)]
pub(crate) struct Touch {
pub(super) touch_grab: SmallVec<[TouchGrab; MAX_TOUCHES]>,
pan_grab: SmallVec<[PanGrab; MAX_PANS]>,
velocity: [velocity::Samples; VELOCITY_LEN],
}
impl Touch {
pub(super) fn set_pan_on(&mut self, id: Id, mode: (bool, bool), p: DVec2) -> (u16, u16) {
for (gi, grab) in self.pan_grab.iter_mut().enumerate() {
if grab.id == id {
debug_assert_eq!(grab.mode, mode);
let index = grab.n;
if usize::from(index) < MAX_PAN_GRABS {
grab.coords[usize::from(index)] = (p, p);
}
grab.n = index + 1;
return (gi.cast(), index);
}
}
if self.pan_grab.len() >= MAX_PANS {
return (u16::MAX, 0);
}
let gj = self.pan_grab.len().cast();
let n = 1;
let mut coords: [(DVec2, DVec2); MAX_PAN_GRABS] = Default::default();
coords[0] = (p, p);
log::trace!("set_pan_on: index={}, id={id}", self.pan_grab.len());
self.pan_grab.push(PanGrab {
id,
mode,
n,
coords,
});
(gj, 0)
}
fn remove_pan(&mut self, index: usize) {
log::trace!("remove_pan: index={index}");
self.pan_grab.remove(index);
for grab in self.touch_grab.iter_mut() {
let p0 = grab.pan_grab.0;
if usize::from(p0) >= index && p0 != u16::MAX {
grab.pan_grab.0 = p0 - 1;
}
}
}
fn remove_pan_grab(&mut self, g: (u16, u16)) {
if let Some(grab) = self.pan_grab.get_mut(usize::from(g.0)) {
grab.n -= 1;
if grab.n == 0 {
return self.remove_pan(g.0.into());
}
for i in (usize::from(g.1))..(usize::from(grab.n) - 1) {
grab.coords[i] = grab.coords[i + 1];
}
} else {
return;
}
for grab in self.touch_grab.iter_mut() {
if grab.pan_grab.0 == g.0 && grab.pan_grab.1 > g.1 {
grab.pan_grab.1 -= 1;
if usize::from(grab.pan_grab.1) == MAX_PAN_GRABS - 1 {
let p = grab.last_position;
self.pan_grab[usize::from(g.0)].coords[usize::from(grab.pan_grab.1)] = (p, p);
}
}
}
}
pub(in crate::event::cx) fn cancel_event_focus(&mut self, target: &Id) {
for grab in self.touch_grab.iter_mut() {
if grab.start_id == target {
grab.cancel = true;
}
}
}
#[inline]
fn get_touch_index(&self, finger_id: FingerId) -> Option<usize> {
self.touch_grab
.iter()
.enumerate()
.find_map(|(i, grab)| (grab.finger_id == finger_id).then_some(i))
}
#[inline]
pub(super) fn get_touch(&mut self, finger_id: FingerId) -> Option<&mut TouchGrab> {
self.touch_grab
.iter_mut()
.find(|grab| grab.finger_id == finger_id)
}
pub(super) fn start_grab(
&mut self,
finger_id: FingerId,
id: Id,
position: DVec2,
mode: GrabMode,
) -> bool {
let mut velocity = u16::MAX;
if mode == GrabMode::Grab {
let mut used = [false; VELOCITY_LEN];
for grab in &self.touch_grab {
if (grab.vel_index as usize) < VELOCITY_LEN {
used[grab.vel_index as usize] = true;
}
}
for i in 0..VELOCITY_LEN {
if !used[i] {
self.velocity[i].clear();
velocity = i as u16;
break;
}
}
}
if let Some(grab) = self.get_touch(finger_id) {
if grab.start_id != id || grab.mode != mode || grab.cancel {
return false;
}
grab.depress = Some(id.clone());
grab.over = Some(id.clone());
grab.last_position = position;
grab.vel_index = velocity;
true
} else if self.touch_grab.len() < MAX_TOUCHES {
let mut pan_grab = (u16::MAX, 0);
if let GrabMode::Pan { scale, rotate } = mode {
pan_grab = self.set_pan_on(id.clone(), (scale, rotate), position);
}
self.touch_grab.push(TouchGrab {
finger_id,
start_id: id.clone(),
depress: Some(id.clone()),
over: Some(id.clone()),
last_position: position,
mode,
pan_grab,
vel_index: velocity,
cancel: false,
});
true
} else {
false
}
}
pub(super) fn velocity(&self, finger_id: FingerId, evc: EventWindowConfig<'_>) -> Option<Vec2> {
let v = self
.touch_grab
.iter()
.find(|grab| grab.finger_id == finger_id)
.map(|grab| grab.vel_index)
.unwrap_or(u16::MAX);
self.velocity
.get(v as usize)
.map(|sampler| sampler.velocity(evc.kinetic_timeout()))
}
}
impl EventState {
fn remove_touch(&mut self, index: usize) -> TouchGrab {
let mut grab = self.touch.touch_grab.remove(index);
log::trace!(
"remove_touch: {:?}, start_id={}",
grab.finger_id,
grab.start_id
);
self.opt_redraw(grab.depress.clone());
self.touch.remove_pan_grab(grab.pan_grab);
self.action_redraw(grab.flush_click_move());
grab
}
}
impl<'a> EventCx<'a> {
pub(in crate::event::cx) fn touch_handle_pending(&mut self, mut node: Node<'_>) {
let mut i = 0;
while i < self.touch.touch_grab.len() {
let redraw = self.touch.touch_grab[i].flush_click_move();
self.action_redraw(redraw);
if self.touch.touch_grab[i].cancel {
let grab = self.remove_touch(i);
let press = Press {
source: PressSource::touch(grab.finger_id),
id: grab.over,
coord: grab.last_position.cast_nearest(),
};
let event = Event::PressEnd {
press,
success: false,
};
self.send_event(node.re(), grab.start_id, event);
} else {
i += 1;
}
}
if self.action_moved.is_some() {
for grab in self.touch.touch_grab.iter_mut() {
grab.over = node.try_probe(grab.last_position.cast_nearest());
}
}
}
pub(in crate::event::cx) fn touch_frame_update(&mut self, mut node: Node<'_>) {
for gi in 0..self.touch.pan_grab.len() {
let grab = &mut self.touch.pan_grab[gi];
assert!(grab.n > 0);
let (p1, q1) = (grab.coords[0].0, grab.coords[0].1);
grab.coords[0].0 = grab.coords[0].1;
let transform = if grab.n == 1 {
Affine::translate(q1 - p1)
} else {
let (p2, q2) = (grab.coords[1].0, grab.coords[1].1);
grab.coords[1].0 = grab.coords[1].1;
Affine::pan(p1, q1, p2, q2, grab.mode)
};
let id = grab.id.clone();
if transform.is_finite() && transform != Affine::IDENTITY {
let event = Event::Pan(transform);
self.send_event(node.re(), id, event);
}
}
}
pub(in crate::event::cx) fn handle_touch_start(
&mut self,
node: Node<'_>,
finger_id: FingerId,
position: DVec2,
) {
let coord = position.cast_nearest();
let over = node.try_probe(coord);
self.close_non_ancestors_of(over.as_ref());
if let Some(id) = over {
if self.config.event().touch_nav_focus()
&& let Some(id) = self.nav_next(node.as_tile(), Some(&id), NavAdvance::None)
{
self.set_nav_focus(id, FocusSource::Pointer);
}
let press = PressStart {
source: PressSource::touch(finger_id),
id: Some(id.clone()),
position,
};
let event = Event::PressStart(press);
self.send_event(node, id, event);
}
}
pub(in crate::event::cx) fn handle_touch_moved(
&mut self,
node: Node<'_>,
finger_id: FingerId,
position: DVec2,
) {
let coord = position.cast_nearest();
let over = node.try_probe(coord);
let mut pan_grab = None;
let grab_index = self
.touch
.touch_grab
.iter()
.enumerate()
.find_map(|(i, grab)| (grab.finger_id == finger_id).then_some(i));
if let Some(index) = grab_index {
let last_pos =
std::mem::replace(&mut self.touch.touch_grab[index].last_position, position);
let delta: Vec2 = (position - last_pos).cast_approx();
let vi = self.touch.touch_grab[index].vel_index as usize;
if vi < VELOCITY_LEN {
self.touch.velocity[vi].push_delta(delta);
}
let grab = &mut self.touch.touch_grab[index];
grab.over = over;
match grab.mode {
GrabMode::Click => {}
GrabMode::Grab => {
let target = grab.start_id.clone();
let press = Press {
source: PressSource::touch(grab.finger_id),
id: grab.over.clone(),
coord,
};
let event = Event::PressMove { press, delta };
self.send_event(node, target, event);
}
GrabMode::Pan { .. } => {
pan_grab = Some(grab.pan_grab);
}
}
}
if let Some(pan_grab) = pan_grab {
self.need_frame_update = true;
if usize::conv(pan_grab.1) < MAX_PAN_GRABS
&& let Some(pan) = self.touch.pan_grab.get_mut(usize::conv(pan_grab.0))
{
pan.coords[usize::conv(pan_grab.1)].1 = position;
}
}
}
pub(in crate::event::cx) fn handle_touch_end(
&mut self,
node: Node<'_>,
finger_id: FingerId,
position: DVec2,
) {
if let Some(index) = self.touch.get_touch_index(finger_id) {
let mut to_send = None;
if let Some(grab) = self.touch.touch_grab.get(index)
&& !grab.mode.is_pan()
{
let source = PressSource::touch(finger_id);
let id = grab.over.clone();
let success = id.is_some();
let coord = position.cast_nearest();
let press = Press { source, id, coord };
let event = Event::PressEnd { press, success };
to_send = Some((grab.start_id.clone(), event));
}
if let Some((id, event)) = to_send {
self.send_event(node, id, event);
}
self.remove_touch(index);
}
}
}