1use crossterm::event::{Event as CrosstermEvent, KeyEvent, MouseEvent};
2use futures_util::stream::Stream;
3use std::pin::Pin;
4use std::task::{Context, Poll};
5use tokio::sync::mpsc;
6
7#[derive(Debug, Clone)]
9pub enum AppEvent {
10 Input(InputEvent),
12 Tick,
14 Quit,
16 Resize(u16, u16),
18 Custom(String),
20}
21
22#[derive(Debug, Clone)]
24pub enum InputEvent {
25 Key(KeyEvent),
27 Mouse(MouseEvent),
29 FocusGained,
31 FocusLost,
33 Paste(String),
35}
36
37pub struct EventHandler {
39 event_rx: mpsc::UnboundedReceiver<AppEvent>,
40 _event_tx: mpsc::UnboundedSender<AppEvent>,
41}
42
43impl EventHandler {
44 pub fn new() -> Self {
46 let (event_tx, event_rx) = mpsc::unbounded_channel();
47
48 let tx = event_tx.clone();
50 tokio::spawn(async move {
51 let mut event_stream = crossterm::event::EventStream::new();
52
53 loop {
54 use futures_util::StreamExt;
55
56 if let Some(Ok(event)) = event_stream.next().await {
57 let app_event = match event {
58 CrosstermEvent::Key(key) => {
59 if key.code == crossterm::event::KeyCode::Char('c')
61 && key.modifiers.contains(crossterm::event::KeyModifiers::CONTROL)
62 {
63 AppEvent::Quit
64 } else {
65 AppEvent::Input(InputEvent::Key(key))
66 }
67 }
68 CrosstermEvent::Mouse(mouse) => AppEvent::Input(InputEvent::Mouse(mouse)),
69 CrosstermEvent::Resize(width, height) => AppEvent::Resize(width, height),
70 CrosstermEvent::FocusGained => AppEvent::Input(InputEvent::FocusGained),
71 CrosstermEvent::FocusLost => AppEvent::Input(InputEvent::FocusLost),
72 CrosstermEvent::Paste(data) => AppEvent::Input(InputEvent::Paste(data)),
73 };
74
75 if tx.send(app_event).is_err() {
76 break;
77 }
78 }
79 }
80 });
81
82 Self {
83 event_rx,
84 _event_tx: event_tx,
85 }
86 }
87
88 pub async fn next(&mut self) -> Option<AppEvent> {
90 self.event_rx.recv().await
91 }
92
93 pub fn try_next(&mut self) -> Option<AppEvent> {
95 self.event_rx.try_recv().ok()
96 }
97}
98
99impl Stream for EventHandler {
100 type Item = AppEvent;
101
102 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
103 self.event_rx.poll_recv(cx)
104 }
105}
106
107#[derive(Debug, Clone)]
109pub struct KeybindHandler {
110 bindings: std::collections::HashMap<String, String>,
111 leader_key: Option<String>,
112 leader_sequence: bool,
113}
114
115impl KeybindHandler {
116 pub fn new() -> Self {
118 Self {
119 bindings: std::collections::HashMap::new(),
120 leader_key: None,
121 leader_sequence: false,
122 }
123 }
124
125 pub fn set_leader_key(&mut self, key: String) {
127 self.leader_key = Some(key);
128 }
129
130 pub fn bind(&mut self, key: String, action: String) {
132 self.bindings.insert(key, action);
133 }
134
135 pub fn handle_key(&mut self, key: &KeyEvent) -> Option<String> {
137 let key_string = self.key_to_string(key);
138
139 if let Some(leader) = &self.leader_key {
141 if key_string == *leader && !self.leader_sequence {
142 self.leader_sequence = true;
143 return None;
144 }
145 }
146
147 if self.leader_sequence {
149 self.leader_sequence = false;
150 let leader_binding = format!("leader+{}", key_string);
151 return self.bindings.get(&leader_binding).cloned();
152 }
153
154 self.bindings.get(&key_string).cloned()
156 }
157
158 fn key_to_string(&self, key: &KeyEvent) -> String {
160 use crossterm::event::{KeyCode, KeyModifiers};
161
162 let mut parts = Vec::new();
163
164 if key.modifiers.contains(KeyModifiers::CONTROL) {
165 parts.push("ctrl");
166 }
167 if key.modifiers.contains(KeyModifiers::ALT) {
168 parts.push("alt");
169 }
170 if key.modifiers.contains(KeyModifiers::SHIFT) {
171 parts.push("shift");
172 }
173
174 let key_part = match key.code {
175 KeyCode::Char(c) => c.to_string(),
176 KeyCode::Enter => "enter".to_string(),
177 KeyCode::Tab => "tab".to_string(),
178 KeyCode::Backspace => "backspace".to_string(),
179 KeyCode::Delete => "delete".to_string(),
180 KeyCode::Insert => "insert".to_string(),
181 KeyCode::Home => "home".to_string(),
182 KeyCode::End => "end".to_string(),
183 KeyCode::PageUp => "pageup".to_string(),
184 KeyCode::PageDown => "pagedown".to_string(),
185 KeyCode::Up => "up".to_string(),
186 KeyCode::Down => "down".to_string(),
187 KeyCode::Left => "left".to_string(),
188 KeyCode::Right => "right".to_string(),
189 KeyCode::Esc => "esc".to_string(),
190 KeyCode::F(n) => format!("f{}", n),
191 _ => "unknown".to_string(),
192 };
193
194 parts.push(&key_part);
195 parts.join("+")
196 }
197}
198
199impl Default for KeybindHandler {
200 fn default() -> Self {
201 Self::new()
202 }
203}
204
205#[derive(Debug, Clone)]
207pub struct MouseHandler {
208 last_position: (u16, u16),
209 drag_state: Option<DragState>,
210}
211
212#[derive(Debug, Clone)]
213struct DragState {
214 start_position: (u16, u16),
215 current_position: (u16, u16),
216}
217
218impl MouseHandler {
219 pub fn new() -> Self {
221 Self {
222 last_position: (0, 0),
223 drag_state: None,
224 }
225 }
226
227 pub fn handle_mouse(&mut self, event: &MouseEvent) -> MouseAction {
229 use crossterm::event::{MouseButton, MouseEventKind};
230
231 match event.kind {
232 MouseEventKind::Down(button) => {
233 self.last_position = (event.column, event.row);
234 match button {
235 MouseButton::Left => {
236 self.drag_state = Some(DragState {
237 start_position: (event.column, event.row),
238 current_position: (event.column, event.row),
239 });
240 MouseAction::LeftClick(event.column, event.row)
241 }
242 MouseButton::Right => MouseAction::RightClick(event.column, event.row),
243 MouseButton::Middle => MouseAction::MiddleClick(event.column, event.row),
244 }
245 }
246 MouseEventKind::Up(MouseButton::Left) => {
247 if let Some(drag) = self.drag_state.take() {
248 if drag.start_position != drag.current_position {
249 MouseAction::DragEnd(drag.start_position, drag.current_position)
250 } else {
251 MouseAction::LeftClick(event.column, event.row)
252 }
253 } else {
254 MouseAction::LeftClick(event.column, event.row)
255 }
256 }
257 MouseEventKind::Up(_) => MouseAction::None,
258 MouseEventKind::Drag(MouseButton::Left) => {
259 if let Some(ref mut drag) = self.drag_state {
260 drag.current_position = (event.column, event.row);
261 MouseAction::Drag(drag.start_position, drag.current_position)
262 } else {
263 MouseAction::None
264 }
265 }
266 MouseEventKind::Moved => {
267 self.last_position = (event.column, event.row);
268 MouseAction::Move(event.column, event.row)
269 }
270 MouseEventKind::ScrollDown => MouseAction::ScrollDown(event.column, event.row),
271 MouseEventKind::ScrollUp => MouseAction::ScrollUp(event.column, event.row),
272 MouseEventKind::ScrollLeft => MouseAction::ScrollLeft(event.column, event.row),
273 MouseEventKind::ScrollRight => MouseAction::ScrollRight(event.column, event.row),
274 _ => MouseAction::None,
275 }
276 }
277}
278
279impl Default for MouseHandler {
280 fn default() -> Self {
281 Self::new()
282 }
283}
284
285#[derive(Debug, Clone)]
287pub enum MouseAction {
288 None,
289 LeftClick(u16, u16),
290 RightClick(u16, u16),
291 MiddleClick(u16, u16),
292 Move(u16, u16),
293 Drag((u16, u16), (u16, u16)), DragEnd((u16, u16), (u16, u16)), ScrollUp(u16, u16),
296 ScrollDown(u16, u16),
297 ScrollLeft(u16, u16),
298 ScrollRight(u16, u16),
299}
300
301pub struct EventDispatcher {
303 handlers: Vec<Box<dyn EventConsumer>>,
304}
305
306impl EventDispatcher {
307 pub fn new() -> Self {
309 Self {
310 handlers: Vec::new(),
311 }
312 }
313
314 pub fn add_handler(&mut self, handler: Box<dyn EventConsumer>) {
316 self.handlers.push(handler);
317 }
318
319 pub fn dispatch(&mut self, event: &AppEvent) -> bool {
321 for handler in &mut self.handlers {
322 if handler.handle_event(event) {
323 return true; }
325 }
326 false
327 }
328}
329
330impl Default for EventDispatcher {
331 fn default() -> Self {
332 Self::new()
333 }
334}
335
336pub trait EventConsumer {
338 fn handle_event(&mut self, event: &AppEvent) -> bool;
340}
341
342#[cfg(test)]
343mod tests {
344 use super::*;
345 use crossterm::event::{KeyCode, KeyModifiers};
346
347 #[test]
348 fn test_key_to_string() {
349 let handler = KeybindHandler::new();
350
351 let key = KeyEvent::new(KeyCode::Char('a'), KeyModifiers::NONE);
352 assert_eq!(handler.key_to_string(&key), "a");
353
354 let key = KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL);
355 assert_eq!(handler.key_to_string(&key), "ctrl+a");
356
357 let key = KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE);
358 assert_eq!(handler.key_to_string(&key), "enter");
359 }
360
361 #[test]
362 fn test_keybind_handler() {
363 let mut handler = KeybindHandler::new();
364 handler.bind("q".to_string(), "quit".to_string());
365 handler.bind("ctrl+c".to_string(), "interrupt".to_string());
366
367 let key = KeyEvent::new(KeyCode::Char('q'), KeyModifiers::NONE);
368 assert_eq!(handler.handle_key(&key), Some("quit".to_string()));
369
370 let key = KeyEvent::new(KeyCode::Char('c'), KeyModifiers::CONTROL);
371 assert_eq!(handler.handle_key(&key), Some("interrupt".to_string()));
372 }
373
374 #[test]
375 fn test_leader_sequence() {
376 let mut handler = KeybindHandler::new();
377 handler.set_leader_key("ctrl+x".to_string());
378 handler.bind("leader+s".to_string(), "save".to_string());
379
380 let leader_key = KeyEvent::new(KeyCode::Char('x'), KeyModifiers::CONTROL);
382 assert_eq!(handler.handle_key(&leader_key), None);
383
384 let bound_key = KeyEvent::new(KeyCode::Char('s'), KeyModifiers::NONE);
386 assert_eq!(handler.handle_key(&bound_key), Some("save".to_string()));
387 }
388}