spitfire_gui/
interactions.rs

1use raui_core::{
2    application::Application,
3    interactive::{
4        InteractionsEngine,
5        default_interactions_engine::{
6            DefaultInteractionsEngine, DefaultInteractionsEngineResult, Interaction, PointerButton,
7        },
8    },
9    layout::CoordsMapping,
10    widget::{
11        component::interactive::navigation::{NavJump, NavScroll, NavSignal, NavTextChange},
12        utils::Vec2,
13    },
14};
15use spitfire_input::{ArrayInputCombinator, InputActionRef, InputCharactersRef};
16
17const ZERO_THRESHOLD: f32 = 1.0e-6;
18
19#[derive(Default)]
20pub struct GuiInteractionsInputs {
21    pub trigger: InputActionRef,
22    pub context: InputActionRef,
23    pub cancel: InputActionRef,
24    pub left: InputActionRef,
25    pub right: InputActionRef,
26    pub up: InputActionRef,
27    pub down: InputActionRef,
28    pub prev: InputActionRef,
29    pub next: InputActionRef,
30    pub text: InputCharactersRef,
31    pub text_start: InputActionRef,
32    pub text_end: InputActionRef,
33    pub text_delete_left: InputActionRef,
34    pub text_delete_right: InputActionRef,
35    pub pointer_position: ArrayInputCombinator<2>,
36    pub pointer_trigger: InputActionRef,
37    pub pointer_context: InputActionRef,
38    pub scroll: ArrayInputCombinator<2>,
39}
40
41#[derive(Default)]
42pub struct GuiInteractionsEngine {
43    pub inputs: GuiInteractionsInputs,
44    pub engine: DefaultInteractionsEngine,
45    cached_pointer_position: Vec2,
46}
47
48impl GuiInteractionsEngine {
49    pub fn maintain(&mut self, mapping: &CoordsMapping) {
50        if self.engine.focused_text_input().is_some() {
51            if let Some(mut text) = self.inputs.text.write() {
52                for character in text.take().chars() {
53                    self.engine
54                        .interact(Interaction::Navigate(NavSignal::TextChange(
55                            NavTextChange::InsertCharacter(character),
56                        )));
57                }
58            }
59            if self.inputs.left.get().is_pressed() {
60                self.engine
61                    .interact(Interaction::Navigate(NavSignal::TextChange(
62                        NavTextChange::MoveCursorLeft,
63                    )));
64            }
65            if self.inputs.right.get().is_pressed() {
66                self.engine
67                    .interact(Interaction::Navigate(NavSignal::TextChange(
68                        NavTextChange::MoveCursorRight,
69                    )));
70            }
71            if self.inputs.text_start.get().is_pressed() {
72                self.engine
73                    .interact(Interaction::Navigate(NavSignal::TextChange(
74                        NavTextChange::MoveCursorStart,
75                    )));
76            }
77            if self.inputs.text_end.get().is_pressed() {
78                self.engine
79                    .interact(Interaction::Navigate(NavSignal::TextChange(
80                        NavTextChange::MoveCursorEnd,
81                    )));
82            }
83            if self.inputs.text_delete_left.get().is_pressed() {
84                self.engine
85                    .interact(Interaction::Navigate(NavSignal::TextChange(
86                        NavTextChange::DeleteLeft,
87                    )));
88            }
89            if self.inputs.text_delete_right.get().is_pressed() {
90                self.engine
91                    .interact(Interaction::Navigate(NavSignal::TextChange(
92                        NavTextChange::DeleteRight,
93                    )));
94            }
95            if self.inputs.trigger.get().is_pressed() {
96                self.engine
97                    .interact(Interaction::Navigate(NavSignal::TextChange(
98                        NavTextChange::NewLine,
99                    )));
100            }
101        } else {
102            if self.inputs.up.get().is_pressed() {
103                self.engine.interact(Interaction::Navigate(NavSignal::Up));
104            }
105            if self.inputs.down.get().is_pressed() {
106                self.engine.interact(Interaction::Navigate(NavSignal::Down));
107            }
108            if self.inputs.left.get().is_pressed() {
109                self.engine.interact(Interaction::Navigate(NavSignal::Left));
110            }
111            if self.inputs.right.get().is_pressed() {
112                self.engine
113                    .interact(Interaction::Navigate(NavSignal::Right));
114            }
115            if self.inputs.prev.get().is_pressed() {
116                self.engine.interact(Interaction::Navigate(NavSignal::Prev));
117            }
118            if self.inputs.next.get().is_pressed() {
119                self.engine.interact(Interaction::Navigate(NavSignal::Next));
120            }
121            if self.inputs.trigger.get().is_pressed() {
122                self.engine
123                    .interact(Interaction::Navigate(NavSignal::Accept(true)));
124            }
125            if self.inputs.context.get().is_pressed() {
126                self.engine
127                    .interact(Interaction::Navigate(NavSignal::Context(true)));
128            }
129            if self.inputs.cancel.get().is_pressed() {
130                self.engine
131                    .interact(Interaction::Navigate(NavSignal::Cancel(true)));
132            }
133        }
134        let pointer_position = {
135            let [x, y] = self.inputs.pointer_position.get();
136            let position = mapping.real_to_virtual_vec2(Vec2 { x, y }, false);
137            if (position.x - self.cached_pointer_position.x).abs() > ZERO_THRESHOLD
138                || (position.y - self.cached_pointer_position.y).abs() > ZERO_THRESHOLD
139            {
140                self.cached_pointer_position = position;
141                self.engine.interact(Interaction::PointerMove(position));
142            }
143            position
144        };
145        let pointer_trigger = self.inputs.pointer_trigger.get();
146        let pointer_context = self.inputs.pointer_context.get();
147        if pointer_trigger.is_pressed() {
148            self.engine.interact(Interaction::PointerDown(
149                PointerButton::Trigger,
150                pointer_position,
151            ));
152        }
153        if pointer_trigger.is_released() {
154            self.engine.interact(Interaction::PointerUp(
155                PointerButton::Trigger,
156                pointer_position,
157            ));
158        }
159        if pointer_context.is_pressed() {
160            self.engine.interact(Interaction::PointerDown(
161                PointerButton::Context,
162                pointer_position,
163            ));
164        }
165        if pointer_context.is_released() {
166            self.engine.interact(Interaction::PointerUp(
167                PointerButton::Context,
168                pointer_position,
169            ));
170        }
171        {
172            let [x, y] = self.inputs.scroll.get();
173            if x.abs() > ZERO_THRESHOLD || y.abs() > ZERO_THRESHOLD {
174                self.engine
175                    .interact(Interaction::Navigate(NavSignal::Jump(NavJump::Scroll(
176                        NavScroll::Units(Vec2 { x: -x, y: -y }, true),
177                    ))));
178            }
179        }
180    }
181}
182
183impl InteractionsEngine<DefaultInteractionsEngineResult, ()> for GuiInteractionsEngine {
184    fn perform_interactions(
185        &mut self,
186        app: &mut Application,
187    ) -> Result<DefaultInteractionsEngineResult, ()> {
188        self.engine.perform_interactions(app)
189    }
190}