Skip to main content

ferroui_controls_inking/
ink_input.rs

1use super::ink_events::InkEvent;
2use super::{InkPoint, InkPresenter};
3use ferroui_base::input::PointerType;
4use ferroui_base::reactive::IDisposable;
5use std::cell::Cell;
6use std::rc::{Rc, Weak};
7
8bitflags::bitflags! {
9    /// Devices admitted to the inking pipeline. Pen is the default.
10    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
11    pub struct CoreInputDeviceTypes: u32 {
12        const NONE = 0;
13        const TOUCH = 1;
14        const PEN = 2;
15        const MOUSE = 4;
16    }
17}
18
19#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
20#[repr(i32)]
21pub enum InkInputProcessingMode {
22    None = 0,
23    #[default]
24    Inking = 1,
25    Erasing = 2,
26}
27
28#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
29#[repr(i32)]
30pub enum InkInputRightDragAction {
31    #[default]
32    LeaveUnprocessed = 0,
33    AllowProcessing = 1,
34}
35
36#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
37#[repr(i32)]
38pub enum InkHighContrastAdjustment {
39    #[default]
40    UseSystemColorsWhenNecessary = 0,
41    UseSystemColors = 1,
42    UseOriginalColors = 2,
43}
44
45#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
46#[repr(i32)]
47pub enum InkPresenterPredefinedConfiguration {
48    #[default]
49    SimpleSinglePointer = 0,
50    SimpleMultiplePointer = 1,
51}
52
53#[derive(Clone, Copy, Debug, PartialEq, Eq)]
54pub enum InkPointerPhase {
55    Entered,
56    Hovered,
57    Exited,
58    Pressed,
59    Moved,
60    Released,
61    Lost,
62}
63
64/// Device-independent input; coordinates are in the canvas's local DIPs.
65/// `handled` is shared through synchronous callback delivery.
66#[derive(Clone, Debug, PartialEq)]
67pub struct InkPointerEventArgs {
68    pub pointer_id: i32,
69    pub device_type: PointerType,
70    pub point: InkPoint,
71    pub is_barrel_button_pressed: bool,
72    pub is_eraser: bool,
73    pub is_right_button_pressed: bool,
74    handled: Rc<Cell<bool>>,
75}
76
77macro_rules! identity_equality {
78    ($($type:ty),* $(,)?) => { $(impl PartialEq for $type {
79        fn eq(&self, other: &Self) -> bool { std::ptr::eq(self, other) }
80    })* };
81}
82identity_equality!(
83    InkInputProcessingConfiguration,
84    InkInputConfiguration,
85    InkStrokeInput,
86    InkUnprocessedInput
87);
88
89impl InkPointerEventArgs {
90    pub fn new(pointer_id: i32, device_type: PointerType, point: InkPoint) -> Self {
91        Self {
92            pointer_id,
93            device_type,
94            point,
95            is_barrel_button_pressed: false,
96            is_eraser: false,
97            is_right_button_pressed: false,
98            handled: Rc::new(Cell::new(false)),
99        }
100    }
101    pub fn handled(&self) -> bool {
102        self.handled.get()
103    }
104    pub fn set_handled(&self, value: bool) {
105        self.handled.set(value);
106    }
107}
108
109pub struct InkInputProcessingConfiguration {
110    presenter: Weak<InkPresenter>,
111    mode: Cell<InkInputProcessingMode>,
112    right_drag_action: Cell<InkInputRightDragAction>,
113}
114
115impl InkInputProcessingConfiguration {
116    pub(crate) fn new(presenter: Weak<InkPresenter>) -> Self {
117        Self {
118            presenter,
119            mode: Cell::new(InkInputProcessingMode::Inking),
120            right_drag_action: Cell::new(InkInputRightDragAction::LeaveUnprocessed),
121        }
122    }
123    pub fn mode(&self) -> InkInputProcessingMode {
124        self.mode.get()
125    }
126    pub fn set_mode(&self, value: InkInputProcessingMode) {
127        if self.mode.replace(value) != value {
128            if let Some(p) = self.presenter.upgrade() {
129                p.cancel_all();
130            }
131        }
132    }
133    pub fn right_drag_action(&self) -> InkInputRightDragAction {
134        self.right_drag_action.get()
135    }
136    pub fn set_right_drag_action(&self, value: InkInputRightDragAction) {
137        if self.right_drag_action.replace(value) != value {
138            if let Some(p) = self.presenter.upgrade() {
139                p.cancel_all();
140            }
141        }
142    }
143}
144
145pub struct InkInputConfiguration {
146    primary_barrel: Cell<bool>,
147    eraser: Cell<bool>,
148}
149
150impl Default for InkInputConfiguration {
151    fn default() -> Self {
152        Self {
153            primary_barrel: Cell::new(true),
154            eraser: Cell::new(true),
155        }
156    }
157}
158
159impl InkInputConfiguration {
160    pub fn is_primary_barrel_button_input_enabled(&self) -> bool {
161        self.primary_barrel.get()
162    }
163    pub fn set_is_primary_barrel_button_input_enabled(&self, value: bool) {
164        self.primary_barrel.set(value);
165    }
166    pub fn is_eraser_input_enabled(&self) -> bool {
167        self.eraser.get()
168    }
169    pub fn set_is_eraser_input_enabled(&self, value: bool) {
170        self.eraser.set(value);
171    }
172}
173
174/// Stable presenter-owned input event service, with a weak back-pointer.
175pub struct InkStrokeInput {
176    presenter: Weak<InkPresenter>,
177    pub(crate) started: InkEvent<InkPointerEventArgs>,
178    pub(crate) continued: InkEvent<InkPointerEventArgs>,
179    pub(crate) ended: InkEvent<InkPointerEventArgs>,
180    pub(crate) canceled: InkEvent<InkPointerEventArgs>,
181}
182
183impl InkStrokeInput {
184    pub(crate) fn new(presenter: Weak<InkPresenter>) -> Self {
185        Self {
186            presenter,
187            started: InkEvent::default(),
188            continued: InkEvent::default(),
189            ended: InkEvent::default(),
190            canceled: InkEvent::default(),
191        }
192    }
193    pub fn ink_presenter(&self) -> Option<Rc<InkPresenter>> {
194        self.presenter.upgrade()
195    }
196    pub fn stroke_started(
197        &self,
198        handler: impl Fn(&InkPointerEventArgs) + 'static,
199    ) -> Rc<dyn IDisposable> {
200        self.started.subscribe(handler)
201    }
202    pub fn stroke_continued(
203        &self,
204        handler: impl Fn(&InkPointerEventArgs) + 'static,
205    ) -> Rc<dyn IDisposable> {
206        self.continued.subscribe(handler)
207    }
208    pub fn stroke_ended(
209        &self,
210        handler: impl Fn(&InkPointerEventArgs) + 'static,
211    ) -> Rc<dyn IDisposable> {
212        self.ended.subscribe(handler)
213    }
214    pub fn stroke_canceled(
215        &self,
216        handler: impl Fn(&InkPointerEventArgs) + 'static,
217    ) -> Rc<dyn IDisposable> {
218        self.canceled.subscribe(handler)
219    }
220}
221
222pub struct InkUnprocessedInput {
223    presenter: Weak<InkPresenter>,
224    entered: InkEvent<InkPointerEventArgs>,
225    hovered: InkEvent<InkPointerEventArgs>,
226    exited: InkEvent<InkPointerEventArgs>,
227    pressed: InkEvent<InkPointerEventArgs>,
228    moved: InkEvent<InkPointerEventArgs>,
229    released: InkEvent<InkPointerEventArgs>,
230    lost: InkEvent<InkPointerEventArgs>,
231}
232
233impl InkUnprocessedInput {
234    pub(crate) fn new(presenter: Weak<InkPresenter>) -> Self {
235        Self {
236            presenter,
237            entered: InkEvent::default(),
238            hovered: InkEvent::default(),
239            exited: InkEvent::default(),
240            pressed: InkEvent::default(),
241            moved: InkEvent::default(),
242            released: InkEvent::default(),
243            lost: InkEvent::default(),
244        }
245    }
246    pub fn ink_presenter(&self) -> Option<Rc<InkPresenter>> {
247        self.presenter.upgrade()
248    }
249    pub fn pointer_entered(
250        &self,
251        h: impl Fn(&InkPointerEventArgs) + 'static,
252    ) -> Rc<dyn IDisposable> {
253        self.entered.subscribe(h)
254    }
255    pub fn pointer_hovered(
256        &self,
257        h: impl Fn(&InkPointerEventArgs) + 'static,
258    ) -> Rc<dyn IDisposable> {
259        self.hovered.subscribe(h)
260    }
261    pub fn pointer_exited(
262        &self,
263        h: impl Fn(&InkPointerEventArgs) + 'static,
264    ) -> Rc<dyn IDisposable> {
265        self.exited.subscribe(h)
266    }
267    pub fn pointer_pressed(
268        &self,
269        h: impl Fn(&InkPointerEventArgs) + 'static,
270    ) -> Rc<dyn IDisposable> {
271        self.pressed.subscribe(h)
272    }
273    pub fn pointer_moved(&self, h: impl Fn(&InkPointerEventArgs) + 'static) -> Rc<dyn IDisposable> {
274        self.moved.subscribe(h)
275    }
276    pub fn pointer_released(
277        &self,
278        h: impl Fn(&InkPointerEventArgs) + 'static,
279    ) -> Rc<dyn IDisposable> {
280        self.released.subscribe(h)
281    }
282    pub fn pointer_lost(&self, h: impl Fn(&InkPointerEventArgs) + 'static) -> Rc<dyn IDisposable> {
283        self.lost.subscribe(h)
284    }
285    pub(crate) fn raise(&self, phase: InkPointerPhase, e: &InkPointerEventArgs) {
286        match phase {
287            InkPointerPhase::Entered => &self.entered,
288            InkPointerPhase::Hovered => &self.hovered,
289            InkPointerPhase::Exited => &self.exited,
290            InkPointerPhase::Pressed => &self.pressed,
291            InkPointerPhase::Moved => &self.moved,
292            InkPointerPhase::Released => &self.released,
293            InkPointerPhase::Lost => &self.lost,
294        }
295        .raise(e);
296    }
297}