use std::{
fmt::{self, Debug, Formatter},
sync::mpsc,
thread,
};
use ratatui::crossterm::event::KeyEvent;
use serde::{
de::{self, Deserializer, Visitor},
Deserialize,
};
pub enum AppEvent {
Key(KeyEvent),
Resize(usize, usize),
Quit,
}
#[derive(Clone)]
pub struct Sender {
tx: mpsc::Sender<AppEvent>,
}
impl Sender {
pub fn send(&self, event: AppEvent) {
let _ = self.tx.send(event);
}
}
impl Debug for Sender {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "Sender")
}
}
pub struct Receiver {
rx: mpsc::Receiver<AppEvent>,
}
impl Receiver {
pub fn recv(&self) -> AppEvent {
self.rx.recv().unwrap_or(AppEvent::Quit)
}
}
pub fn init() -> (Sender, Receiver) {
let (tx, rx) = mpsc::channel();
let tx = Sender { tx };
let rx = Receiver { rx };
let event_tx = tx.clone();
thread::spawn(move || loop {
match ratatui::crossterm::event::read() {
Ok(e) => match e {
ratatui::crossterm::event::Event::Key(key) => {
event_tx.send(AppEvent::Key(key));
}
ratatui::crossterm::event::Event::Resize(w, h) => {
event_tx.send(AppEvent::Resize(w as usize, h as usize));
}
_ => {}
},
Err(_) => {
break;
}
}
});
(tx, rx)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum UserEvent {
ForceQuit,
Quit,
HelpToggle,
Cancel,
Close,
NavigateUp,
NavigateDown,
GoToTop,
GoToBottom,
ScrollUp,
ScrollDown,
PageUp,
PageDown,
HalfPageUp,
HalfPageDown,
Confirm,
}
impl<'de> Deserialize<'de> for UserEvent {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct UserEventVisitor;
impl<'de> Visitor<'de> for UserEventVisitor {
type Value = UserEvent;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a string representing a user event")
}
fn visit_str<E>(self, value: &str) -> Result<UserEvent, E>
where
E: de::Error,
{
match value {
"force_quit" => Ok(UserEvent::ForceQuit),
"quit" => Ok(UserEvent::Quit),
"help_toggle" => Ok(UserEvent::HelpToggle),
"cancel" => Ok(UserEvent::Cancel),
"close" => Ok(UserEvent::Close),
"navigate_up" => Ok(UserEvent::NavigateUp),
"navigate_down" => Ok(UserEvent::NavigateDown),
"go_to_top" => Ok(UserEvent::GoToTop),
"go_to_bottom" => Ok(UserEvent::GoToBottom),
"scroll_up" => Ok(UserEvent::ScrollUp),
"scroll_down" => Ok(UserEvent::ScrollDown),
"page_up" => Ok(UserEvent::PageUp),
"page_down" => Ok(UserEvent::PageDown),
"half_page_up" => Ok(UserEvent::HalfPageUp),
"half_page_down" => Ok(UserEvent::HalfPageDown),
"confirm" => Ok(UserEvent::Confirm),
_ => {
let msg = format!("Unknown user event: {}", value);
Err(de::Error::custom(msg))
}
}
}
}
deserializer.deserialize_str(UserEventVisitor)
}
}