use alloc::boxed::Box;
use core::fmt;
use crate::{
handler::{BoxedAction, Handler, boxed_action},
metadata::MetadataKey,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GesturePhase {
Started,
Updated,
Ended,
Cancelled,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct GesturePoint {
pub x: f32,
pub y: f32,
}
impl GesturePoint {
#[must_use]
pub const fn new(x: f32, y: f32) -> Self {
Self { x, y }
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct TapEvent {
pub location: GesturePoint,
pub count: u32,
}
#[derive(Debug, Clone, PartialEq)]
pub struct LongPressEvent {
pub location: GesturePoint,
pub duration: f32,
}
#[derive(Debug, Clone, PartialEq)]
pub struct DragEvent {
pub phase: GesturePhase,
pub location: GesturePoint,
pub translation: GesturePoint,
pub velocity: GesturePoint,
}
#[derive(Debug, Clone, PartialEq)]
pub struct MagnificationEvent {
pub phase: GesturePhase,
pub center: GesturePoint,
pub scale: f32,
pub velocity: f32,
}
#[derive(Debug, Clone, PartialEq)]
pub struct RotationEvent {
pub phase: GesturePhase,
pub center: GesturePoint,
pub angle: f32,
pub velocity: f32,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct TapGesture {
pub count: u32,
}
impl TapGesture {
#[must_use]
pub const fn repeat(count: u32) -> Self {
Self { count }
}
#[must_use]
pub const fn new() -> Self {
Self { count: 1 }
}
}
impl Default for TapGesture {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct LongPressGesture {
pub duration: u32,
}
impl LongPressGesture {
#[must_use]
pub const fn new(duration: u32) -> Self {
Self { duration }
}
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub struct DragGesture {
pub min_distance: f32,
}
impl DragGesture {
#[must_use]
pub const fn new(min_distance: f32) -> Self {
Self { min_distance }
}
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub struct MagnificationGesture {
pub initial_scale: f32,
}
impl MagnificationGesture {
#[must_use]
pub const fn new(initial_scale: f32) -> Self {
Self { initial_scale }
}
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub struct RotationGesture {
pub initial_angle: f32,
}
impl RotationGesture {
#[must_use]
pub const fn new(initial_angle: f32) -> Self {
Self { initial_angle }
}
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum Gesture {
Tap(TapGesture),
LongPress(LongPressGesture),
Drag(DragGesture),
Magnification(MagnificationGesture),
Rotation(RotationGesture),
Then(Box<Then>),
Simultaneous(Box<Simultaneous>),
Exclusive(Box<Exclusive>),
}
#[derive(Debug, Clone, PartialEq)]
pub struct Then {
first: Gesture,
then: Gesture,
}
impl Then {
#[must_use]
pub const fn first(&self) -> &Gesture {
&self.first
}
#[must_use]
pub const fn then(&self) -> &Gesture {
&self.then
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Simultaneous {
first: Gesture,
second: Gesture,
}
impl Simultaneous {
#[must_use]
pub const fn first(&self) -> &Gesture {
&self.first
}
#[must_use]
pub const fn second(&self) -> &Gesture {
&self.second
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Exclusive {
first: Gesture,
second: Gesture,
}
impl Exclusive {
#[must_use]
pub const fn first(&self) -> &Gesture {
&self.first
}
#[must_use]
pub const fn second(&self) -> &Gesture {
&self.second
}
}
impl Gesture {
#[must_use]
pub fn then(self, other: impl Into<Self>) -> Self {
Self::Then(Box::new(Then {
first: self,
then: other.into(),
}))
}
#[must_use]
pub fn sequenced_before(self, other: impl Into<Self>) -> Self {
self.then(other)
}
#[must_use]
pub fn simultaneously_with(self, other: impl Into<Self>) -> Self {
Self::Simultaneous(Box::new(Simultaneous {
first: self,
second: other.into(),
}))
}
#[must_use]
pub fn exclusively_before(self, other: impl Into<Self>) -> Self {
Self::Exclusive(Box::new(Exclusive {
first: self,
second: other.into(),
}))
}
}
macro_rules! impl_gesture {
($(($name:ty, $variant:ident)),*) => {
$(
impl $name {
/// Chains another gesture to run after this one succeeds.
#[must_use]
pub fn then(self, other: impl Into<Gesture>) -> Gesture {
Gesture::$variant(self).then(other)
}
#[must_use]
pub fn sequenced_before(self, other: impl Into<Gesture>) -> Gesture {
self.then(other)
}
#[must_use]
pub fn simultaneously_with(self, other: impl Into<Gesture>) -> Gesture {
Gesture::$variant(self).simultaneously_with(other)
}
#[must_use]
pub fn exclusively_before(self, other: impl Into<Gesture>) -> Gesture {
Gesture::$variant(self).exclusively_before(other)
}
}
impl From<$name> for Gesture {
fn from(gesture: $name) -> Self {
Gesture::$variant(gesture)
}
}
)*
};
}
impl_gesture! {
(TapGesture, Tap),
(LongPressGesture, LongPress),
(DragGesture, Drag),
(MagnificationGesture, Magnification),
(RotationGesture, Rotation)
}
#[non_exhaustive]
pub struct GestureObserver {
pub gesture: Gesture,
pub action: BoxedAction<()>,
}
impl fmt::Debug for GestureObserver {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("GestureObserver")
.field("gesture", &self.gesture)
.finish_non_exhaustive()
}
}
impl MetadataKey for GestureObserver {}
impl GestureObserver {
#[must_use]
pub fn new<Args>(gesture: impl Into<Gesture>, action: impl Handler<Args, ()>) -> Self {
Self {
gesture: gesture.into(),
action: boxed_action(action),
}
}
#[must_use]
pub fn builder(gesture: impl Into<Gesture>) -> GestureObserverBuilder {
GestureObserverBuilder::new(gesture)
}
}
#[derive(Debug)]
pub struct GestureObserverBuilder {
gesture: Gesture,
}
impl GestureObserverBuilder {
#[must_use]
pub fn new(gesture: impl Into<Gesture>) -> Self {
Self {
gesture: gesture.into(),
}
}
#[must_use]
pub fn action<Args>(self, action: impl Handler<Args, ()>) -> GestureObserver {
GestureObserver::new(self.gesture, action)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sequenced_before_aliases_then() {
let gesture = TapGesture::new().sequenced_before(LongPressGesture::new(300));
match gesture {
Gesture::Then(pair) => {
assert!(matches!(pair.first(), Gesture::Tap(_)));
assert!(matches!(pair.then(), Gesture::LongPress(_)));
}
other => panic!("expected Gesture::Then, got {other:?}"),
}
}
#[test]
fn simultaneous_composition_contains_both_gestures() {
let gesture = TapGesture::new().simultaneously_with(DragGesture::new(8.0));
match gesture {
Gesture::Simultaneous(pair) => {
assert!(matches!(pair.first(), Gesture::Tap(_)));
assert!(matches!(pair.second(), Gesture::Drag(_)));
}
other => panic!("expected Gesture::Simultaneous, got {other:?}"),
}
}
#[test]
fn exclusive_composition_contains_primary_and_fallback() {
let gesture = TapGesture::new().exclusively_before(LongPressGesture::new(500));
match gesture {
Gesture::Exclusive(pair) => {
assert!(matches!(pair.first(), Gesture::Tap(_)));
assert!(matches!(pair.second(), Gesture::LongPress(_)));
}
other => panic!("expected Gesture::Exclusive, got {other:?}"),
}
}
}