use teksilo_tokens::PointerKindMask;
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Axis {
X,
Y,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct TouchAction(u8);
impl TouchAction {
const PAN_X_BIT: u8 = 1 << 0;
const PAN_Y_BIT: u8 = 1 << 1;
const PINCH_ZOOM_BIT: u8 = 1 << 2;
pub const AUTO: Self = Self(u8::MAX);
pub const NONE: Self = Self(0);
pub const PAN_X: Self = Self(Self::PAN_X_BIT);
pub const PAN_Y: Self = Self(Self::PAN_Y_BIT);
pub const PAN: Self = Self(Self::PAN_X_BIT | Self::PAN_Y_BIT);
pub const PINCH_ZOOM: Self = Self(Self::PINCH_ZOOM_BIT);
pub const MANIPULATION: Self = Self(Self::PAN_X_BIT | Self::PAN_Y_BIT | Self::PINCH_ZOOM_BIT);
pub const fn intersect(self, other: Self) -> Self {
Self(self.0 & other.0)
}
pub const fn union(self, other: Self) -> Self {
Self(self.0 | other.0)
}
pub const fn allows_pan(self, axis: Axis) -> bool {
let bit = match axis {
Axis::X => Self::PAN_X_BIT,
Axis::Y => Self::PAN_Y_BIT,
};
self.0 & bit != 0
}
pub const fn allows_pan_x(self) -> bool {
self.allows_pan(Axis::X)
}
pub const fn allows_pan_y(self) -> bool {
self.allows_pan(Axis::Y)
}
pub const fn allows_pinch(self) -> bool {
self.0 & Self::PINCH_ZOOM_BIT != 0
}
pub const fn allows_delayed_gestures(self) -> bool {
!self.is_none()
}
pub const fn is_none(self) -> bool {
self.0 == Self::NONE.0
}
}
impl std::ops::BitOr for TouchAction {
type Output = Self;
fn bitor(self, rhs: Self) -> Self {
self.union(rhs)
}
}
impl std::ops::BitAnd for TouchAction {
type Output = Self;
fn bitand(self, rhs: Self) -> Self {
self.intersect(rhs)
}
}
impl Default for TouchAction {
fn default() -> Self {
Self::AUTO
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
pub struct PanAxes(u8);
impl PanAxes {
const X_BIT: u8 = 1 << 0;
const Y_BIT: u8 = 1 << 1;
pub const NONE: Self = Self(0);
pub const X: Self = Self(Self::X_BIT);
pub const Y: Self = Self(Self::Y_BIT);
pub const BOTH: Self = Self(Self::X_BIT | Self::Y_BIT);
pub const fn contains(self, axis: Axis) -> bool {
let bit = match axis {
Axis::X => Self::X_BIT,
Axis::Y => Self::Y_BIT,
};
self.0 & bit != 0
}
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct PanClaim {
pub axes: PanAxes,
pub devices: PointerKindMask,
pub kinetic: bool,
}
impl PanClaim {
pub fn vertical() -> Self {
Self {
axes: PanAxes::Y,
..Self::default()
}
}
pub fn horizontal() -> Self {
Self {
axes: PanAxes::X,
..Self::default()
}
}
pub fn both() -> Self {
Self {
axes: PanAxes::BOTH,
..Self::default()
}
}
}
impl Default for PanClaim {
fn default() -> Self {
Self {
axes: PanAxes::NONE,
devices: PointerKindMask::DIRECT,
kinetic: false,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn intersect_forms_a_commutative_monoid_with_auto_and_none_at_its_poles() {
let values = [
TouchAction::AUTO,
TouchAction::NONE,
TouchAction::PAN_X,
TouchAction::PAN_Y,
TouchAction::PAN,
TouchAction::PINCH_ZOOM,
TouchAction::MANIPULATION,
];
for &a in &values {
assert_eq!(
a.intersect(TouchAction::AUTO),
a,
"AUTO must be an identity"
);
assert_eq!(
TouchAction::AUTO.intersect(a),
a,
"AUTO must be an identity"
);
assert_eq!(
a.intersect(TouchAction::NONE),
TouchAction::NONE,
"NONE must absorb"
);
assert_eq!(
TouchAction::NONE.intersect(a),
TouchAction::NONE,
"NONE must absorb"
);
for &b in &values {
assert_eq!(
a.intersect(b),
b.intersect(a),
"intersect must be commutative"
);
for &c in &values {
assert_eq!(
a.intersect(b).intersect(c),
a.intersect(b.intersect(c)),
"intersect must be associative"
);
}
}
}
}
#[test]
fn union_is_the_dual_operator() {
assert_eq!(
TouchAction::PAN_X.union(TouchAction::PAN_Y),
TouchAction::PAN
);
assert_eq!(
TouchAction::PAN.union(TouchAction::PINCH_ZOOM),
TouchAction::MANIPULATION
);
assert_eq!(TouchAction::PAN_X | TouchAction::PAN_Y, TouchAction::PAN);
assert_eq!(TouchAction::PAN & TouchAction::PAN_X, TouchAction::PAN_X);
}
#[test]
fn default_is_auto() {
assert_eq!(TouchAction::default(), TouchAction::AUTO);
}
#[test]
fn allows_pan_matches_the_named_constants() {
assert!(TouchAction::AUTO.allows_pan_x() && TouchAction::AUTO.allows_pan_y());
assert!(TouchAction::AUTO.allows_pinch());
assert!(!TouchAction::NONE.allows_pan_x() && !TouchAction::NONE.allows_pan_y());
assert!(!TouchAction::NONE.allows_pinch());
assert!(TouchAction::PAN_X.allows_pan_x());
assert!(!TouchAction::PAN_X.allows_pan_y());
assert!(!TouchAction::PAN_X.allows_pinch());
assert!(TouchAction::PAN_Y.allows_pan_y());
assert!(!TouchAction::PAN_Y.allows_pan_x());
assert!(!TouchAction::PAN_Y.allows_pinch());
assert!(TouchAction::PAN.allows_pan_x() && TouchAction::PAN.allows_pan_y());
assert!(!TouchAction::PAN.allows_pinch());
assert!(TouchAction::PINCH_ZOOM.allows_pinch());
assert!(!TouchAction::PINCH_ZOOM.allows_pan_x() && !TouchAction::PINCH_ZOOM.allows_pan_y());
assert!(
TouchAction::MANIPULATION.allows_pan_x() && TouchAction::MANIPULATION.allows_pan_y()
);
assert!(TouchAction::MANIPULATION.allows_pinch());
}
#[test]
fn allows_delayed_gestures_is_false_only_for_none() {
assert!(TouchAction::AUTO.allows_delayed_gestures());
assert!(TouchAction::PAN_X.allows_delayed_gestures());
assert!(!TouchAction::NONE.allows_delayed_gestures());
assert!(TouchAction::NONE.is_none());
assert!(!TouchAction::AUTO.is_none());
}
#[test]
fn pan_axes_contains_per_axis() {
assert!(PanAxes::BOTH.contains(Axis::X) && PanAxes::BOTH.contains(Axis::Y));
assert!(PanAxes::X.contains(Axis::X) && !PanAxes::X.contains(Axis::Y));
assert!(PanAxes::Y.contains(Axis::Y) && !PanAxes::Y.contains(Axis::X));
assert!(!PanAxes::NONE.contains(Axis::X) && !PanAxes::NONE.contains(Axis::Y));
assert_eq!(PanAxes::default(), PanAxes::NONE);
}
#[test]
fn pan_claim_default_devices_excludes_mouse() {
let claim = PanClaim::default();
assert!(!claim.devices.contains(teksilo_tokens::PointerKind::Mouse));
assert!(claim.devices.contains(teksilo_tokens::PointerKind::Touch));
assert!(claim.devices.contains(teksilo_tokens::PointerKind::Pen(
teksilo_tokens::PenKind::Pen
)));
assert!(!claim.kinetic);
assert_eq!(claim.axes, PanAxes::NONE);
}
#[test]
fn vertical_horizontal_both_helpers_set_only_their_axes() {
assert_eq!(PanClaim::vertical().axes, PanAxes::Y);
assert_eq!(PanClaim::horizontal().axes, PanAxes::X);
assert_eq!(PanClaim::both().axes, PanAxes::BOTH);
assert_eq!(PanClaim::vertical().devices, PointerKindMask::DIRECT);
assert!(!PanClaim::vertical().kinetic);
}
}