use crossterm::event::{self, Event as CrosstermEvent, KeyCode, KeyEvent, KeyModifiers};
use std::time::Duration;
use tokio::sync::mpsc;
#[derive(Debug, Clone)]
pub enum Event {
Key(KeyEvent),
Resize(u16, u16),
Tick,
Quit,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Action {
Quit,
NextPanel,
PreviousPanel,
ScrollUp,
ScrollDown,
PageUp,
PageDown,
ScrollOutputUp, ScrollOutputDown, Enter,
Escape,
Help,
CommandPalette,
ToggleTheme,
ToggleAI,
ToggleSettings,
ToggleLesson,
ShowLessonMenu,
ShowAchievements,
ShowProgress,
ShowChallenges,
DismissNotification,
None,
}
pub struct EventHandler {
sender: mpsc::UnboundedSender<Event>,
receiver: mpsc::UnboundedReceiver<Event>,
}
impl EventHandler {
pub fn new() -> Self {
let (sender, receiver) = mpsc::unbounded_channel();
Self { sender, receiver }
}
pub async fn start(&self) {
let sender = self.sender.clone();
tokio::spawn(async move {
loop {
if event::poll(Duration::from_millis(100)).unwrap() {
match event::read().unwrap() {
CrosstermEvent::Key(key) => {
if sender.send(Event::Key(key)).is_err() {
break;
}
}
CrosstermEvent::Resize(width, height) => {
if sender.send(Event::Resize(width, height)).is_err() {
break;
}
}
_ => {}
}
} else {
if sender.send(Event::Tick).is_err() {
break;
}
}
}
});
}
pub async fn next(&mut self) -> Option<Event> {
self.receiver.recv().await
}
}
impl Default for EventHandler {
fn default() -> Self {
Self::new()
}
}
pub fn key_to_action(key: KeyEvent) -> Action {
match (key.code, key.modifiers) {
(KeyCode::Char('c'), KeyModifiers::CONTROL) => Action::Quit,
(KeyCode::Char('q'), KeyModifiers::NONE) => Action::Quit,
(KeyCode::Esc, _) => Action::Escape,
(KeyCode::Tab, KeyModifiers::NONE) => Action::NextPanel,
(KeyCode::BackTab, KeyModifiers::SHIFT) => Action::PreviousPanel,
(KeyCode::Up, KeyModifiers::CONTROL) => Action::ScrollOutputUp,
(KeyCode::Down, KeyModifiers::CONTROL) => Action::ScrollOutputDown,
(KeyCode::Char('j'), KeyModifiers::CONTROL) => Action::ScrollOutputDown,
(KeyCode::Char('j'), KeyModifiers::ALT) => Action::ScrollOutputDown,
(KeyCode::Char('k'), KeyModifiers::ALT) => Action::ScrollOutputUp,
(KeyCode::Up, KeyModifiers::NONE) | (KeyCode::Char('k'), KeyModifiers::NONE) => Action::ScrollUp,
(KeyCode::Down, KeyModifiers::NONE) | (KeyCode::Char('j'), KeyModifiers::NONE) => Action::ScrollDown,
(KeyCode::PageUp, _) | (KeyCode::Char('u'), KeyModifiers::CONTROL) => Action::PageUp,
(KeyCode::PageDown, _) | (KeyCode::Char('d'), KeyModifiers::CONTROL) => Action::PageDown,
(KeyCode::Enter, _) => Action::Enter,
(KeyCode::Char('?'), KeyModifiers::NONE) => Action::Help,
(KeyCode::Char('k'), KeyModifiers::CONTROL) => Action::CommandPalette,
(KeyCode::Char('t'), KeyModifiers::CONTROL) => Action::ToggleTheme,
(KeyCode::Char('a'), KeyModifiers::CONTROL) => Action::ToggleAI,
(KeyCode::Char('s'), KeyModifiers::CONTROL) => Action::ToggleSettings,
(KeyCode::Char('l'), KeyModifiers::CONTROL) => Action::ToggleLesson,
(KeyCode::Char('m'), KeyModifiers::NONE) => Action::ShowLessonMenu,
(KeyCode::Char('a'), KeyModifiers::ALT) => Action::ShowAchievements,
(KeyCode::Char('p'), KeyModifiers::ALT) => Action::ShowProgress,
(KeyCode::Char('c'), KeyModifiers::ALT) => Action::ShowChallenges,
_ => Action::None,
}
}