pub const DOUBLE_PRESS_TIMEOUT_MS: u64 = 800;
pub struct DoublePressState {
last_press_time: u64,
timeout_handle: Option<u64>,
}
impl DoublePressState {
pub fn new() -> Self {
Self {
last_press_time: 0,
timeout_handle: None,
}
}
}
impl Default for DoublePressState {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum PressResult {
FirstPress,
DoublePress,
}
pub fn handle_double_press<F1, F2>(
state: &mut DoublePressState,
timeout_ms: u64,
on_first_press: F1,
on_double_press: F2,
) -> PressResult
where
F1: FnOnce(),
F2: FnOnce(),
{
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_millis() as u64)
.unwrap_or(0);
let time_since_last_press = now.saturating_sub(state.last_press_time);
let is_double_press = time_since_last_press <= timeout_ms && state.timeout_handle.is_some();
if is_double_press {
state.timeout_handle = None;
on_double_press();
PressResult::DoublePress
} else {
on_first_press();
state.last_press_time = now;
state.timeout_handle = Some(now);
PressResult::FirstPress
}
}
pub fn clear_double_press(state: &mut DoublePressState) {
state.last_press_time = 0;
state.timeout_handle = None;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_double_press_state_new() {
let state = DoublePressState::new();
assert_eq!(state.last_press_time, 0);
assert!(state.timeout_handle.is_none());
}
#[test]
fn test_first_press() {
let mut state = DoublePressState::new();
let mut first_called = false;
let mut double_called = false;
let result = handle_double_press(
&mut state,
DOUBLE_PRESS_TIMEOUT_MS,
|| first_called = true,
|| double_called = true,
);
assert_eq!(result, PressResult::FirstPress);
assert!(first_called);
assert!(!double_called);
}
}