embedded_menu/interaction/
single_touch.rs1use core::marker::PhantomData;
2
3use crate::{
4 interaction::{
5 Action, InputAdapter, InputAdapterSource, InputResult, InputState, Interaction, Navigation,
6 },
7 selection_indicator::style::interpolate,
8};
9
10#[derive(Default, Debug, Clone, Copy)]
11pub struct State {
12 interaction_time: u32,
13 was_released: bool,
14 repeated: bool,
15}
16
17#[derive(Clone, Copy)]
22pub struct SingleTouch {
23 pub ignore_time: u32,
25
26 pub debounce_time: u32,
28
29 pub max_time: u32,
31}
32
33impl<R> InputAdapterSource<R> for SingleTouch {
34 type InputAdapter = SingleTouchAdapter<R>;
35
36 fn adapter(&self) -> Self::InputAdapter {
37 SingleTouchAdapter {
38 ignore_time: self.ignore_time,
39 debounce_time: self.debounce_time,
40 max_time: self.max_time,
41 marker: PhantomData,
42 }
43 }
44}
45
46pub struct SingleTouchAdapter<R> {
51 ignore_time: u32,
52 debounce_time: u32,
53 max_time: u32,
54 marker: PhantomData<R>,
55}
56
57impl<R> Clone for SingleTouchAdapter<R> {
58 fn clone(&self) -> Self {
59 *self
60 }
61}
62
63impl<R> Copy for SingleTouchAdapter<R> {}
64
65impl<R> InputAdapter for SingleTouchAdapter<R> {
66 type Input = bool;
67 type Value = R;
68 type State = State;
69
70 fn handle_input(
71 &self,
72 state: &mut Self::State,
73 action: Self::Input,
74 ) -> InputResult<Self::Value> {
75 if !state.was_released {
76 if action {
77 return InputResult::from(InputState::Idle);
78 }
79 state.was_released = true;
80 }
81
82 if action {
83 state.interaction_time = state.interaction_time.saturating_add(1);
84 if state.interaction_time <= self.ignore_time && !state.repeated {
85 InputResult::from(InputState::Idle)
86 } else if state.interaction_time < self.max_time {
87 let ignore_time = if state.repeated { 0 } else { self.ignore_time };
88 InputResult::from(InputState::InProgress(interpolate(
89 state.interaction_time - ignore_time,
90 0,
91 self.max_time - ignore_time,
92 0,
93 255,
94 ) as u8))
95 } else {
96 state.repeated = true;
97 state.interaction_time = 0;
98 InputResult::from(Interaction::Action(Action::Select))
99 }
100 } else {
101 let time = core::mem::replace(&mut state.interaction_time, 0);
102
103 if self.debounce_time < time && time < self.max_time && !state.repeated {
104 InputResult::from(Interaction::Navigation(Navigation::Next))
105 } else {
106 state.repeated = false;
108 InputResult::from(InputState::Idle)
109 }
110 }
111 }
112}
113
114#[cfg(test)]
115mod test {
116 use crate::interaction::{
117 single_touch::SingleTouch, Action, InputAdapter, InputAdapterSource, InputResult,
118 InputState, Interaction, Navigation,
119 };
120
121 #[test]
122 fn test_interaction() {
123 let controller = SingleTouch {
125 ignore_time: 1,
126 debounce_time: 1,
127 max_time: 5,
128 }
129 .adapter();
130
131 let expectations: [&[(bool, InputResult<()>)]; 6] = [
132 &[
133 (false, InputState::Idle.into()),
134 (false, InputState::Idle.into()),
135 (false, InputState::Idle.into()),
136 (false, InputState::Idle.into()),
137 (false, InputState::Idle.into()),
138 (false, InputState::Idle.into()),
139 ],
140 &[
142 (true, InputState::Idle.into()),
143 (false, InputState::Idle.into()),
144 (true, InputState::Idle.into()),
145 (false, InputState::Idle.into()),
146 (true, InputState::Idle.into()),
147 (false, InputState::Idle.into()),
148 ],
149 &[
151 (false, InputState::Idle.into()),
152 (true, InputState::Idle.into()),
153 (true, InputState::InProgress(63).into()),
154 (false, Interaction::Navigation(Navigation::Next).into()),
155 ],
156 &[
157 (false, InputState::Idle.into()),
158 (true, InputState::Idle.into()),
159 (true, InputState::InProgress(63).into()),
160 (true, InputState::InProgress(127).into()),
161 (false, Interaction::Navigation(Navigation::Next).into()),
162 ],
163 &[
165 (false, InputState::Idle.into()),
166 (true, InputState::Idle.into()),
167 (true, InputState::InProgress(63).into()),
168 (true, InputState::InProgress(127).into()),
169 (true, InputState::InProgress(191).into()),
170 (false, Interaction::Navigation(Navigation::Next).into()),
171 ],
172 &[
174 (false, InputState::Idle.into()),
175 (true, InputState::Idle.into()),
176 (true, InputState::InProgress(63).into()),
177 (true, InputState::InProgress(127).into()),
178 (true, InputState::InProgress(191).into()),
179 (true, Interaction::Action(Action::Select).into()),
180 (true, InputState::InProgress(51).into()),
181 (true, InputState::InProgress(102).into()),
182 (true, InputState::InProgress(153).into()),
183 (true, InputState::InProgress(204).into()),
184 (true, Interaction::Action(Action::Select).into()),
185 (true, InputState::InProgress(51).into()),
186 (true, InputState::InProgress(102).into()),
187 (true, InputState::InProgress(153).into()),
188 (false, InputState::Idle.into()),
189 ],
190 ];
191
192 for (row, &inputs) in expectations.iter().enumerate() {
193 let mut controller_state = Default::default();
194
195 for (sample, (input, expectation)) in inputs.iter().enumerate() {
196 let ret = controller.handle_input(&mut controller_state, *input);
197
198 assert_eq!(
199 ret, *expectation,
200 "Mismatch at row {}, sample {}",
201 row, sample
202 );
203 }
204 }
205 }
206}