1use std::time::Duration;
9
10use crate::runtime::MULTI_PRESS;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum Click {
19 Single,
21 Double,
24}
25
26impl Click {
27 pub const INTERVAL: Duration = MULTI_PRESS;
30}
31
32pub(crate) fn is_double(last: Duration, now: Duration) -> bool {
34 now.saturating_sub(last) < Click::INTERVAL
35}
36
37#[derive(Debug)]
39pub(crate) struct LastPress<K> {
40 last: Option<(K, Duration)>,
41}
42
43impl<K> Default for LastPress<K> {
44 fn default() -> Self {
45 Self { last: None }
46 }
47}
48
49impl<K: PartialEq> LastPress<K> {
50 pub(crate) fn press(&mut self, row: K, now: Duration) -> bool {
53 let double = self.last.as_ref().is_some_and(|(last, at)| *last == row && is_double(*at, now));
54 self.last = if double { None } else { Some((row, now)) };
55 double
56 }
57
58 pub(crate) fn forget(&mut self) {
60 self.last = None;
61 }
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67
68 fn ms(millis: u64) -> Duration {
69 Duration::from_millis(millis)
70 }
71
72 #[test]
73 fn two_presses_on_one_row_within_the_interval_are_a_double_click_and_a_third_starts_over() {
74 let mut presses = LastPress::default();
75 assert!(!presses.press(3, ms(1_000)));
76 assert!(presses.press(3, ms(1_399)), "399 ms later is a double click");
77 assert!(!presses.press(3, ms(1_450)), "a third press starts a new count");
78 assert!(presses.press(3, ms(1_500)));
79 }
80
81 #[test]
82 fn a_slow_second_press_another_row_or_a_drag_between_are_not_a_double_click() {
83 let mut presses = LastPress::default();
84 assert!(!presses.press(3, ms(0)));
85 assert!(!presses.press(3, ms(400)), "the interval itself is too slow");
86 assert!(!presses.press(4, ms(500)), "another row");
87 presses.forget();
88 assert!(!presses.press(4, ms(600)), "the press before became a drag");
89 }
90}