Skip to main content

blockworx_canvas2d/input/
mod.rs

1//! What the browser did, as values with no browser in them.
2//!
3//! The DOM's pointer, wheel and keyboard events become the samples below in
4//! [`dom`], and [`Reader`] turns a batch of samples into the [`Input`](blockworx_paint::Input) and
5//! [`Move`](blockworx_paint::Move)s the kernel reads. The split is the point:
6//! the rules that decide whether a drag is a pan or a tool's gesture are a
7//! pure function over plain values, tested without a browser.
8
9pub mod chord;
10pub mod dom;
11pub mod reader;
12
13use blockworx_geom::Pos2;
14use blockworx_paint::{Button, PointerKind, ScrollPx};
15
16pub use chord::chord_of;
17pub use reader::{Fingers, Focus, PanKey, Read, Reader};
18
19/// Identity of one pointer, so two fingers on a screen are told apart.
20#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
21pub struct PointerId(pub i32);
22
23/// Where a pointer event falls in the life of a gesture.
24#[derive(Clone, Copy, PartialEq, Eq, Debug)]
25pub enum Phase {
26    Moved,
27    Down,
28    Up,
29    /// The pointer left the canvas.
30    Left,
31    /// The platform took the gesture away — a touch it decided was a scroll,
32    /// a window that lost the device.
33    Cancelled,
34}
35
36/// Whether shift was held as an event fired.
37#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
38pub enum Shift {
39    Held,
40    #[default]
41    Free,
42}
43
44impl From<bool> for Shift {
45    fn from(held: bool) -> Self {
46        if held { Self::Held } else { Self::Free }
47    }
48}
49
50/// Which buttons were held as an event fired — the DOM's `buttons` bitmask,
51/// named. A press whose release was delivered somewhere else is recovered
52/// from this, rather than being held forever.
53#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
54pub struct Held(u8);
55
56impl Held {
57    pub const NONE: Self = Self(0);
58
59    #[must_use]
60    pub fn of(buttons: impl IntoIterator<Item = Button>) -> Self {
61        Self(buttons.into_iter().map(bit).fold(0, |mask, at| mask | at))
62    }
63
64    #[must_use]
65    pub fn holds(self, button: Button) -> bool {
66        self.0 & bit(button) != 0
67    }
68}
69
70fn bit(button: Button) -> u8 {
71    match button {
72        Button::Primary => 1,
73        Button::Secondary => 2,
74        Button::Middle => 4,
75    }
76}
77
78/// One pointer event, in canvas-relative CSS pixels.
79#[derive(Clone, Copy, PartialEq, Debug)]
80pub struct PointerSample {
81    pub id: PointerId,
82    pub at: Pos2,
83    pub phase: Phase,
84    /// The button [`Phase::Down`] and [`Phase::Up`] name; a motion names none.
85    pub button: Option<Button>,
86    pub held: Held,
87    pub kind: PointerKind,
88    pub shift: Shift,
89}
90
91/// One wheel notch, in canvas-relative CSS pixels. A trackpad pinch arrives
92/// as a `ctrl`-held wheel and is not told apart: both zoom.
93#[derive(Clone, Copy, PartialEq, Debug)]
94pub struct WheelSample {
95    pub at: Pos2,
96    pub scroll: ScrollPx,
97    pub shift: Shift,
98}
99
100/// A key this app reads off the canvas. Chords are not among them — they go
101/// through [`chord_of`] to the command registry instead.
102#[derive(Clone, Copy, PartialEq, Eq, Debug)]
103pub enum Named {
104    Escape,
105    /// Delete or Backspace, which mean the same thing on the canvas.
106    Delete,
107    /// The key that turns a primary drag into a pan.
108    Space,
109}
110
111/// Whether a key sample is the key going down or coming back up.
112#[derive(Clone, Copy, PartialEq, Eq, Debug)]
113pub enum Edge {
114    Down,
115    Up,
116}
117
118#[derive(Clone, Copy, PartialEq, Eq, Debug)]
119pub struct KeySample {
120    pub key: Named,
121    pub edge: Edge,
122    pub shift: Shift,
123}
124
125/// One thing the browser told the canvas.
126#[derive(Clone, Copy, PartialEq, Debug)]
127pub enum Sample {
128    Pointer(PointerSample),
129    Wheel(WheelSample),
130    Key(KeySample),
131}
132
133impl Sample {
134    fn shift(self) -> Shift {
135        match self {
136            Self::Pointer(sample) => sample.shift,
137            Self::Wheel(sample) => sample.shift,
138            Self::Key(sample) => sample.shift,
139        }
140    }
141}
142
143#[cfg(test)]
144mod tests {
145    use super::*;
146
147    #[test]
148    fn a_held_mask_answers_for_the_buttons_it_was_built_from() {
149        let both = Held::of([Button::Primary, Button::Middle]);
150        assert!(both.holds(Button::Primary) && both.holds(Button::Middle));
151        assert!(!both.holds(Button::Secondary));
152        assert!(!Held::NONE.holds(Button::Primary));
153    }
154}