Skip to main content

cove_cli/sidebar/
event.rs

1// ── Event loop for sidebar ──
2
3use std::time::Duration;
4
5use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyModifiers};
6
7// ── Types ──
8
9pub enum Action {
10    Up,
11    Down,
12    Select,
13    Quit,
14    Tick,
15}
16
17// ── Public API ──
18
19/// Poll for input events with a 500ms timeout. Returns accumulated actions.
20/// Batches rapid arrow presses into single moves (key draining).
21/// The 500ms timeout means ~2 wakeups/sec when idle (vs 10 at 100ms).
22pub fn poll() -> Vec<Action> {
23    let mut actions = Vec::new();
24
25    if event::poll(Duration::from_millis(500)).unwrap_or(false) {
26        // Process first event
27        if let Ok(Event::Key(key)) = event::read()
28            && let Some(action) = key_to_action(key)
29        {
30            actions.push(action);
31        }
32
33        // Drain queued keys (batch rapid arrow presses)
34        while event::poll(Duration::from_millis(0)).unwrap_or(false) {
35            if let Ok(Event::Key(key)) = event::read()
36                && let Some(action) = key_to_action(key)
37            {
38                actions.push(action);
39            }
40        }
41    }
42
43    if actions.is_empty() {
44        actions.push(Action::Tick);
45    }
46
47    actions
48}
49
50// ── Helpers ──
51
52fn key_to_action(key: KeyEvent) -> Option<Action> {
53    // Only handle key press events (ignore release/repeat)
54    if key.kind != crossterm::event::KeyEventKind::Press {
55        return None;
56    }
57
58    match key.code {
59        KeyCode::Up | KeyCode::Char('k') => Some(Action::Up),
60        KeyCode::Down | KeyCode::Char('j') => Some(Action::Down),
61        KeyCode::Enter => Some(Action::Select),
62        KeyCode::Char('q') => Some(Action::Quit),
63        KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => Some(Action::Quit),
64        _ => None,
65    }
66}