shared/
multitap.rs

1#![expect(dead_code)]
2use core::ascii::Char;
3
4use defmt::Format;
5
6use crate::{Key, held_key::HeldKey};
7mod case;
8pub use case::*;
9mod pending;
10pub use pending::*;
11
12#[derive(Debug, PartialEq, Format, Copy, Clone)]
13pub enum Event {
14    Tentative(Char),
15    Decided(Char),
16    Case(Case),
17    ShowSpecialCharacters,
18}
19
20impl Event {
21    fn decide(self) -> Option<Self> {
22        match self {
23            Self::Tentative(c) => Some(Self::Decided(c)),
24            _ => None,
25        }
26    }
27
28    fn next_char(self) -> Option<Self> {
29        match self {
30            Self::Tentative(c) => {
31                let result = match c {
32                    Char::CapitalA => Char::CapitalB,
33                    Char::CapitalB => Char::CapitalC,
34                    Char::CapitalC => Char::CapitalA,
35                    Char::CapitalD => Char::CapitalE,
36                    Char::CapitalE => Char::CapitalF,
37                    Char::CapitalF => Char::CapitalD,
38                    Char::CapitalG => Char::CapitalH,
39                    Char::CapitalH => Char::CapitalI,
40                    Char::CapitalI => Char::CapitalG,
41                    Char::CapitalJ => Char::CapitalK,
42                    Char::CapitalK => Char::CapitalL,
43                    Char::CapitalL => Char::CapitalJ,
44                    Char::CapitalM => Char::CapitalN,
45                    Char::CapitalN => Char::CapitalO,
46                    Char::CapitalO => Char::CapitalM,
47                    Char::CapitalP => Char::CapitalQ,
48                    Char::CapitalQ => Char::CapitalR,
49                    Char::CapitalR => Char::CapitalS,
50                    Char::CapitalS => Char::CapitalP,
51                    Char::CapitalT => Char::CapitalU,
52                    Char::CapitalU => Char::CapitalV,
53                    Char::CapitalV => Char::CapitalT,
54                    Char::CapitalW => Char::CapitalX,
55                    Char::CapitalX => Char::CapitalY,
56                    Char::CapitalY => Char::CapitalZ,
57                    Char::CapitalZ => Char::CapitalW,
58                    Char::SmallA => Char::SmallB,
59                    Char::SmallB => Char::SmallC,
60                    Char::SmallC => Char::SmallA,
61                    Char::SmallD => Char::SmallE,
62                    Char::SmallE => Char::SmallF,
63                    Char::SmallF => Char::SmallD,
64                    Char::SmallG => Char::SmallH,
65                    Char::SmallH => Char::SmallI,
66                    Char::SmallI => Char::SmallG,
67                    Char::SmallJ => Char::SmallK,
68                    Char::SmallK => Char::SmallL,
69                    Char::SmallL => Char::SmallJ,
70                    Char::SmallM => Char::SmallN,
71                    Char::SmallN => Char::SmallO,
72                    Char::SmallO => Char::SmallM,
73                    Char::SmallP => Char::SmallQ,
74                    Char::SmallQ => Char::SmallR,
75                    Char::SmallR => Char::SmallS,
76                    Char::SmallS => Char::SmallP,
77                    Char::SmallT => Char::SmallU,
78                    Char::SmallU => Char::SmallV,
79                    Char::SmallV => Char::SmallT,
80                    Char::SmallW => Char::SmallX,
81                    Char::SmallX => Char::SmallY,
82                    Char::SmallY => Char::SmallZ,
83                    Char::SmallZ => Char::SmallW,
84                    Char::Digit1 => Char::Digit2,
85                    Char::Digit2 => Char::Digit3,
86                    Char::Digit3 => Char::Digit4,
87                    Char::Digit4 => Char::Digit5,
88                    Char::Digit5 => Char::Digit6,
89                    Char::Digit6 => Char::Digit7,
90                    Char::Digit7 => Char::Digit8,
91                    Char::Digit8 => Char::Digit9,
92                    Char::Digit9 => Char::Digit0,
93                    Char::Digit0 => Char::Digit1,
94                    e => e,
95                };
96                Some(Self::Tentative(result))
97            }
98            _ => None,
99        }
100    }
101}
102
103#[derive(Debug)]
104pub struct Last {
105    held_key_event: Option<crate::held_key::Event>,
106    event: Option<Event>,
107}
108
109impl Last {
110    fn new() -> Self {
111        Self {
112            held_key_event: None,
113            event: None,
114        }
115    }
116
117    fn clear(&mut self) {
118        self.held_key_event = None;
119        self.event = None;
120    }
121
122    fn set_held_key_event(
123        &mut self,
124        held_key_event: Option<crate::held_key::Event>,
125    ) -> Option<crate::held_key::Event> {
126        let result = self.held_key_event.clone();
127        self.held_key_event = held_key_event;
128        result
129    }
130
131    fn set_event(&mut self, event: Option<Event>) -> Option<Event> {
132        let result = self.event;
133        self.event = event;
134        result
135    }
136}
137
138#[derive(Debug)]
139pub struct MultiTap {
140    case_state: CaseState,
141    last: Last,
142    pending: Pending<Event>,
143    held_key: HeldKey,
144    duration: u64,
145}
146
147impl MultiTap {
148    pub fn new(duration: u64) -> Self {
149        let case_state = CaseState::new(Case::Lower);
150        let mut pending = Pending::new();
151        pending.enqueue(Event::Case(case_state.case()));
152
153        Self {
154            case_state,
155            last: Last::new(),
156            pending,
157            held_key: HeldKey::new(15000, 5000),
158            duration,
159        }
160    }
161
162    fn case(&self) -> Case {
163        self.case_state.case()
164    }
165
166    pub async fn event(&mut self, keypad: &mut impl crate::Keypad) -> Option<Event> {
167        if let Some(pending) = self.pending.dequeue() {
168            self.last.set_event(Some(pending));
169            return Some(pending);
170        }
171
172        let key = self.held_key.event(keypad).await;
173        let last_key = self.last.set_held_key_event(key.clone());
174
175        let result = match key {
176            Some(crate::held_key::Event::Down(Key::Asterisk)) => Some(Event::ShowSpecialCharacters),
177            Some(crate::held_key::Event::Down(Key::Hash)) => {
178                self.case_state.cycle_case();
179                Some(Event::Case(self.case()))
180            }
181            Some(crate::held_key::Event::Down(Key::Cancel)) => {
182                Some(Event::Decided(core::ascii::Char::Backspace))
183            }
184            Some(crate::held_key::Event::Delay(Key::Hash)) => {
185                self.case_state.enable_numeric_case();
186                Some(Event::Case(self.case()))
187            }
188            Some(crate::held_key::Event::Delay(Key::Cancel)) => None,
189            Some(crate::held_key::Event::Delay(d)) => {
190                self.last.clear();
191                Some(Event::Decided(digit(d)))
192            }
193            Some(crate::held_key::Event::Down(ref now)) => {
194                if key == last_key {
195                    self.last.event.unwrap().next_char()
196                } else if last_key.is_some() {
197                    let result = self.last.event.unwrap().decide();
198                    self.pending
199                        .enqueue(Event::Tentative(lowercase(now.clone().into())));
200                    result
201                } else {
202                    Some(Event::Tentative(lowercase(now.clone().into())))
203                }
204            }
205            None | Some(crate::held_key::Event::Repeat(_)) => None,
206        };
207
208        self.last.set_event(result);
209        result
210    }
211}
212
213fn digit(k: crate::Key) -> Char {
214    match k {
215        crate::Key::One => core::ascii::Char::Digit1,
216        crate::Key::Two => core::ascii::Char::Digit2,
217        crate::Key::Three => core::ascii::Char::Digit3,
218        crate::Key::Four => core::ascii::Char::Digit4,
219        crate::Key::Five => core::ascii::Char::Digit5,
220        crate::Key::Six => core::ascii::Char::Digit6,
221        crate::Key::Seven => core::ascii::Char::Digit7,
222        crate::Key::Eight => core::ascii::Char::Digit8,
223        crate::Key::Nine => core::ascii::Char::Digit9,
224        crate::Key::Zero => core::ascii::Char::Digit0,
225        _ => core::ascii::Char::Digit0,
226    }
227}
228
229fn lowercase(c: Char) -> Char {
230    match c {
231        core::ascii::Char::CapitalA => core::ascii::Char::SmallA,
232        core::ascii::Char::CapitalB => core::ascii::Char::SmallB,
233        core::ascii::Char::CapitalC => core::ascii::Char::SmallC,
234        core::ascii::Char::CapitalD => core::ascii::Char::SmallD,
235        core::ascii::Char::CapitalE => core::ascii::Char::SmallE,
236        core::ascii::Char::CapitalF => core::ascii::Char::SmallF,
237        core::ascii::Char::CapitalG => core::ascii::Char::SmallG,
238        core::ascii::Char::CapitalH => core::ascii::Char::SmallH,
239        core::ascii::Char::CapitalI => core::ascii::Char::SmallI,
240        core::ascii::Char::CapitalJ => core::ascii::Char::SmallJ,
241        core::ascii::Char::CapitalK => core::ascii::Char::SmallK,
242        core::ascii::Char::CapitalL => core::ascii::Char::SmallL,
243        core::ascii::Char::CapitalM => core::ascii::Char::SmallM,
244        core::ascii::Char::CapitalN => core::ascii::Char::SmallN,
245        core::ascii::Char::CapitalO => core::ascii::Char::SmallO,
246        core::ascii::Char::CapitalP => core::ascii::Char::SmallP,
247        core::ascii::Char::CapitalQ => core::ascii::Char::SmallQ,
248        core::ascii::Char::CapitalR => core::ascii::Char::SmallR,
249        core::ascii::Char::CapitalS => core::ascii::Char::SmallS,
250        core::ascii::Char::CapitalT => core::ascii::Char::SmallT,
251        core::ascii::Char::CapitalU => core::ascii::Char::SmallU,
252        core::ascii::Char::CapitalV => core::ascii::Char::SmallV,
253        core::ascii::Char::CapitalW => core::ascii::Char::SmallW,
254        core::ascii::Char::CapitalX => core::ascii::Char::SmallX,
255        core::ascii::Char::CapitalY => core::ascii::Char::SmallY,
256        core::ascii::Char::CapitalZ => core::ascii::Char::SmallZ,
257        t => t,
258    }
259}
260
261#[cfg(test)]
262mod test {
263    use futures_executor::block_on;
264
265    #[test]
266    fn test_tentative() {
267        block_on(async {
268            let mut keypad = crate::test::Keypad::new(&[crate::KeyEvent::Down(crate::Key::Two)]);
269            let mut multitap = super::MultiTap::new(1000);
270            assert_eq!(
271                multitap.event(&mut keypad).await,
272                Some(super::Event::Case(super::Case::Lower))
273            );
274            assert_eq!(
275                multitap.event(&mut keypad).await,
276                Some(super::Event::Tentative(core::ascii::Char::SmallA))
277            );
278        });
279    }
280
281    #[test]
282    fn test_tentative_next() {
283        block_on(async {
284            let mut keypad = crate::test::Keypad::new(&[
285                crate::KeyEvent::Down(crate::Key::Two),
286                crate::KeyEvent::Down(crate::Key::Two),
287            ]);
288            let mut multitap = super::MultiTap::new(1000);
289            assert_eq!(
290                multitap.event(&mut keypad).await,
291                Some(super::Event::Case(super::Case::Lower))
292            );
293            assert_eq!(
294                multitap.event(&mut keypad).await,
295                Some(super::Event::Tentative(core::ascii::Char::SmallA))
296            );
297            assert_eq!(
298                multitap.event(&mut keypad).await,
299                Some(super::Event::Tentative(core::ascii::Char::SmallB))
300            );
301        });
302    }
303
304    #[test]
305    fn test_decided_by_timeout() {}
306
307    #[test]
308    fn test_hold_for_number() {}
309
310    #[test]
311    fn test_decided_by_other() {
312        block_on(async {
313            let mut keypad = crate::test::Keypad::new(&[
314                crate::KeyEvent::Down(crate::Key::Two),
315                crate::KeyEvent::Down(crate::Key::Three),
316            ]);
317            let mut multitap = super::MultiTap::new(1000);
318            assert_eq!(
319                multitap.event(&mut keypad).await,
320                Some(super::Event::Case(super::Case::Lower))
321            );
322            assert_eq!(
323                multitap.event(&mut keypad).await,
324                Some(super::Event::Tentative(core::ascii::Char::SmallA))
325            );
326            assert_eq!(
327                multitap.event(&mut keypad).await,
328                Some(super::Event::Decided(core::ascii::Char::SmallA))
329            );
330            assert_eq!(
331                multitap.event(&mut keypad).await,
332                Some(super::Event::Tentative(core::ascii::Char::SmallD))
333            );
334        });
335    }
336}