spitfire_gui/
interactions.rs1use 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}