embedded_menu/interaction/
mod.rs1pub mod programmed;
2pub mod single_touch;
3
4#[cfg(feature = "simulator")]
5pub mod simulator;
6
7#[derive(Copy, Clone, Debug, PartialEq, Eq)]
8pub enum Interaction<R> {
9 Navigation(Navigation),
11 Action(Action<R>),
13}
14
15#[derive(Copy, Clone, Debug, PartialEq, Eq)]
16pub enum Action<R> {
17 Select,
19 Return(R),
21}
22
23#[derive(Copy, Clone, Debug, PartialEq, Eq)]
24#[must_use]
25pub enum Navigation {
26 Previous,
28 Next,
30 ForwardWrapping(usize),
32 Forward(usize),
34 BackwardWrapping(usize),
36 Backward(usize),
38 Beginning,
40 End,
42 JumpTo(usize),
44}
45
46impl Navigation {
47 pub(crate) fn calculate_selection(
50 self,
51 mut selected: usize,
52 count: usize,
53 selectable: impl Fn(usize) -> bool,
54 ) -> usize {
55 if count == 0 {
56 return 0;
57 }
58
59 selected = selected.clamp(0, count - 1);
61 let original = selected;
62
63 #[allow(clippy::unnecessary_lazy_evaluations)]
65 match self {
66 Self::Next => loop {
67 selected = (selected + 1) % count;
68 if selectable(selected) {
69 break selected;
70 }
71 else if selected == original {
73 return 0;
74 }
75 },
76 Self::Previous => loop {
77 selected = selected.checked_sub(1).unwrap_or(count - 1);
78 if selectable(selected) {
79 break selected;
80 }
81 else if selected == original {
83 return 0;
84 }
85 },
86 Self::ForwardWrapping(n) => {
87 selected = (selected + n) % count;
88 if !selectable(selected) {
89 Self::Next.calculate_selection(selected, count, selectable)
90 } else {
91 selected
92 }
93 }
94 Self::Forward(n) => {
95 selected = selected.saturating_add(n).min(count - 1);
96 if !selectable(selected) {
97 Self::Next.calculate_selection(selected, count, selectable)
98 } else {
99 selected
100 }
101 }
102 Self::BackwardWrapping(n) => {
103 selected = selected
104 .checked_sub(n)
105 .unwrap_or_else(|| count - (n - selected) % count);
106 if !selectable(selected) {
107 Self::Previous.calculate_selection(selected, count, selectable)
108 } else {
109 selected
110 }
111 }
112 Self::Backward(n) => {
113 selected = selected.saturating_sub(n);
114 if !selectable(selected) {
115 Self::Previous.calculate_selection(selected, count, selectable)
116 } else {
117 selected
118 }
119 }
120 Self::Beginning => {
121 if !selectable(0) {
122 Self::Next.calculate_selection(0, count, selectable)
123 } else {
124 0
125 }
126 }
127 Self::End => {
128 if !selectable(count - 1) {
129 Self::Previous.calculate_selection(count - 1, count, selectable)
130 } else {
131 count - 1
132 }
133 }
134 Self::JumpTo(n) => {
135 selected = n.min(count - 1);
136 if !selectable(selected) {
137 Self::Next.calculate_selection(selected, count, selectable)
138 } else {
139 selected
140 }
141 }
142 }
143 }
144}
145
146#[derive(Copy, Clone, Debug, PartialEq, Eq)]
147pub enum InputResult<R> {
148 None,
150 StateUpdate(InputState),
152 Interaction(Interaction<R>),
154}
155
156impl<R> From<Interaction<R>> for InputResult<R> {
157 fn from(interaction: Interaction<R>) -> Self {
158 Self::Interaction(interaction)
159 }
160}
161
162impl<R> From<InputState> for InputResult<R> {
163 fn from(state: InputState) -> Self {
164 Self::StateUpdate(state)
165 }
166}
167
168#[derive(Copy, Clone, Debug, PartialEq, Eq)]
169pub enum InputState {
170 Idle,
171 InProgress(u8),
172}
173
174pub trait InputAdapterSource<R>: Copy {
175 type InputAdapter: InputAdapter<Value = R>;
176
177 fn adapter(&self) -> Self::InputAdapter;
178}
179
180pub trait InputAdapter: Copy {
181 type Input;
182 type Value;
183 type State: Default + Copy;
184
185 fn handle_input(
186 &self,
187 state: &mut Self::State,
188 action: Self::Input,
189 ) -> InputResult<Self::Value>;
190}
191
192#[cfg(test)]
193mod test {
194 use super::*;
195
196 #[test]
197 fn selection() {
198 let count = 30;
199 let mut selected = 3;
200 for _ in 0..5 {
201 selected = Navigation::Previous.calculate_selection(selected, count, |_| true);
202 }
203 assert_eq!(selected, 28);
204
205 for _ in 0..5 {
206 selected = Navigation::Next.calculate_selection(selected, count, |_| true);
207 }
208 assert_eq!(selected, 3);
209
210 for _ in 0..5 {
211 selected =
212 Navigation::BackwardWrapping(5).calculate_selection(selected, count, |_| true);
213 }
214 assert_eq!(selected, 8);
215
216 for _ in 0..5 {
217 selected =
218 Navigation::ForwardWrapping(5).calculate_selection(selected, count, |_| true);
219 }
220 assert_eq!(selected, 3);
221
222 selected = Navigation::JumpTo(20).calculate_selection(selected, count, |_| true);
223 assert_eq!(selected, 20);
224
225 selected = Navigation::Beginning.calculate_selection(selected, count, |_| true);
226 assert_eq!(selected, 0);
227
228 selected = Navigation::End.calculate_selection(selected, count, |_| true);
229 assert_eq!(selected, 29);
230
231 for _ in 0..5 {
232 selected = Navigation::Backward(5).calculate_selection(selected, count, |_| true);
233 }
234 assert_eq!(selected, 4);
235
236 for _ in 0..5 {
237 selected = Navigation::Forward(5).calculate_selection(selected, count, |_| true);
238 }
239 assert_eq!(selected, 29);
240 }
241
242 #[test]
243 fn selection_large_stupid_numbers() {
244 let count = 30;
245 let mut selected = 3;
246
247 selected = Navigation::BackwardWrapping(75).calculate_selection(selected, count, |_| true);
248 assert_eq!(selected, 18);
249
250 selected = Navigation::ForwardWrapping(75).calculate_selection(selected, count, |_| true);
251 assert_eq!(selected, 3);
252
253 selected =
254 Navigation::BackwardWrapping(100000).calculate_selection(selected, count, |_| true);
255 assert_eq!(selected, 23);
256
257 selected =
258 Navigation::ForwardWrapping(100000).calculate_selection(selected, count, |_| true);
259 assert_eq!(selected, 3);
260
261 selected = Navigation::JumpTo(100).calculate_selection(selected, count, |_| true);
262 assert_eq!(selected, 29);
263
264 selected = Navigation::JumpTo(0).calculate_selection(selected, count, |_| true);
265 assert_eq!(selected, 0);
266
267 selected = Navigation::Forward(100000).calculate_selection(selected, count, |_| true);
268 assert_eq!(selected, 29);
269
270 selected = Navigation::Backward(100000).calculate_selection(selected, count, |_| true);
271 assert_eq!(selected, 0);
272 }
273
274 #[test]
275 fn unselectable_selection_infinite_loop() {
276 let selected = Navigation::BackwardWrapping(75).calculate_selection(5, 10, |_| false);
277 assert_eq!(selected, 0);
278 let selected = Navigation::ForwardWrapping(75).calculate_selection(5, 10, |_| false);
279 assert_eq!(selected, 0);
280 let selected = Navigation::BackwardWrapping(75).calculate_selection(5, 10, |_| false);
281 assert_eq!(selected, 0);
282 let selected = Navigation::ForwardWrapping(75).calculate_selection(5, 10, |_| false);
283 assert_eq!(selected, 0);
284 let selected = Navigation::JumpTo(75).calculate_selection(5, 10, |_| false);
285 assert_eq!(selected, 0);
286 let selected = Navigation::JumpTo(75).calculate_selection(5, 10, |_| false);
287 assert_eq!(selected, 0);
288 let selected = Navigation::Forward(75).calculate_selection(5, 10, |_| false);
289 assert_eq!(selected, 0);
290 let selected = Navigation::Backward(75).calculate_selection(5, 10, |_| false);
291 assert_eq!(selected, 0);
292 let selected = Navigation::Next.calculate_selection(5, 10, |_| false);
293 assert_eq!(selected, 0);
294 let selected = Navigation::Previous.calculate_selection(5, 10, |_| false);
295 assert_eq!(selected, 0);
296 let selected = Navigation::Beginning.calculate_selection(5, 10, |_| false);
297 assert_eq!(selected, 0);
298 let selected = Navigation::End.calculate_selection(5, 10, |_| false);
299 assert_eq!(selected, 0);
300 }
301}