ricecoder_tui/
event.rs

1//! Event handling for the TUI
2
3use std::path::PathBuf;
4use std::thread;
5use std::time::Duration;
6use tokio::sync::mpsc::UnboundedReceiver;
7
8/// Event types for the TUI
9#[derive(Debug, Clone)]
10pub enum Event {
11    /// Keyboard input event
12    Key(KeyEvent),
13    /// Mouse input event
14    Mouse(MouseEvent),
15    /// Terminal resize event
16    Resize { width: u16, height: u16 },
17    /// Tick event for periodic updates
18    Tick,
19    /// Drag-and-drop event with file paths
20    /// Requirements: 1.1 - Detect drag-and-drop event via crossterm
21    DragDrop { paths: Vec<PathBuf> },
22}
23
24/// Keyboard event
25#[derive(Debug, Clone, Copy)]
26pub struct KeyEvent {
27    /// Key code
28    pub code: KeyCode,
29    /// Modifier keys
30    pub modifiers: KeyModifiers,
31}
32
33/// Key codes
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
35pub enum KeyCode {
36    /// Character key
37    Char(char),
38    /// Enter key
39    Enter,
40    /// Escape key
41    Esc,
42    /// Tab key
43    Tab,
44    /// Backspace key
45    Backspace,
46    /// Delete key
47    Delete,
48    /// Arrow keys
49    Up,
50    Down,
51    Left,
52    Right,
53    /// Function keys
54    F(u8),
55    /// Other keys
56    Other,
57}
58
59/// Key modifiers
60#[derive(Debug, Clone, Copy, Default)]
61pub struct KeyModifiers {
62    /// Shift key
63    pub shift: bool,
64    /// Control key
65    pub ctrl: bool,
66    /// Alt key
67    pub alt: bool,
68}
69
70/// Mouse event
71#[derive(Debug, Clone, Copy)]
72pub struct MouseEvent {
73    /// X coordinate
74    pub x: u16,
75    /// Y coordinate
76    pub y: u16,
77    /// Mouse button
78    pub button: MouseButton,
79}
80
81/// Mouse button
82#[derive(Debug, Clone, Copy, PartialEq, Eq)]
83pub enum MouseButton {
84    /// Left button
85    Left,
86    /// Right button
87    Right,
88    /// Middle button
89    Middle,
90}
91
92/// Event loop for handling terminal events
93pub struct EventLoop {
94    /// Receiver for events
95    rx: Option<UnboundedReceiver<Event>>,
96    /// Sender for events (used for drag-and-drop and other programmatic events)
97    tx: Option<tokio::sync::mpsc::UnboundedSender<Event>>,
98}
99
100impl EventLoop {
101    /// Create a new event loop
102    pub fn new() -> Self {
103        let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
104        let tx_clone = tx.clone();
105
106        // Spawn a thread to handle terminal events
107        thread::spawn(move || {
108            // Tick event every 250ms
109            let tick_interval = Duration::from_millis(250);
110            let mut last_tick = std::time::Instant::now();
111
112            loop {
113                if last_tick.elapsed() >= tick_interval {
114                    let _ = tx_clone.send(Event::Tick);
115                    last_tick = std::time::Instant::now();
116                }
117
118                // TODO: Poll for actual terminal events with crossterm
119                thread::sleep(Duration::from_millis(10));
120            }
121        });
122
123        Self { rx: Some(rx), tx: Some(tx) }
124    }
125
126    /// Poll for the next event
127    pub async fn poll(&mut self) -> anyhow::Result<Option<Event>> {
128        if let Some(rx) = &mut self.rx {
129            Ok(rx.recv().await)
130        } else {
131            Ok(None)
132        }
133    }
134
135    /// Send a drag-and-drop event with file paths
136    /// 
137    /// # Arguments
138    /// 
139    /// * `paths` - File paths from the drag-and-drop event
140    /// 
141    /// # Requirements
142    /// 
143    /// - Req 1.1: Create interface for receiving drag-and-drop events from ricecoder-tui
144    /// - Req 1.1: Implement file path extraction from events
145    pub fn send_drag_drop_event(&self, paths: Vec<PathBuf>) -> anyhow::Result<()> {
146        if let Some(tx) = self.tx.as_ref() {
147            tx.send(Event::DragDrop { paths })?;
148        }
149        Ok(())
150    }
151}
152
153impl Default for EventLoop {
154    fn default() -> Self {
155        Self::new()
156    }
157}