use std::time::Duration;
use crossterm::event::{Event as CrosstermEvent, EventStream, KeyEvent, MouseEvent};
use futures::StreamExt;
use tokio::sync::mpsc;
use tokio::time::{Interval, interval};
use tokio_util::sync::CancellationToken;
use super::actions::ActionResult;
#[derive(Debug)]
pub enum AppEvent {
Tick,
Render,
Key(KeyEvent),
#[allow(dead_code)]
Mouse(MouseEvent),
#[allow(dead_code)]
Resize(u16, u16),
ActionResult(ActionResult),
Error(String),
Quit,
}
pub struct EventHandler {
result_rx: mpsc::UnboundedReceiver<ActionResult>,
cancel: CancellationToken,
tick_interval: Interval,
render_interval: Interval,
crossterm_events: EventStream,
}
impl EventHandler {
pub fn new(result_rx: mpsc::UnboundedReceiver<ActionResult>) -> Self {
let tick_rate = Duration::from_millis(250); let render_rate = Duration::from_millis(50);
Self {
result_rx,
cancel: CancellationToken::new(),
tick_interval: interval(tick_rate),
render_interval: interval(render_rate),
crossterm_events: EventStream::new(),
}
}
#[allow(dead_code)]
pub fn cancel_token(&self) -> CancellationToken {
self.cancel.clone()
}
#[allow(dead_code)]
pub fn cancel(&self) {
self.cancel.cancel();
}
pub async fn next(&mut self) -> AppEvent {
loop {
tokio::select! {
biased;
Some(result) = self.result_rx.recv() => {
return AppEvent::ActionResult(result);
}
maybe_event = self.crossterm_events.next() => {
match maybe_event {
Some(Ok(event)) => {
match event {
CrosstermEvent::Key(key) => {
return AppEvent::Key(key);
}
CrosstermEvent::Mouse(mouse) => {
return AppEvent::Mouse(mouse);
}
CrosstermEvent::Resize(w, h) => {
return AppEvent::Resize(w, h);
}
CrosstermEvent::FocusGained | CrosstermEvent::FocusLost => {
continue;
}
CrosstermEvent::Paste(_) => {
continue;
}
}
}
Some(Err(e)) => {
return AppEvent::Error(format!("Input error: {}", e));
}
None => {
return AppEvent::Quit;
}
}
}
_ = self.tick_interval.tick() => {
return AppEvent::Tick;
}
_ = self.render_interval.tick() => {
return AppEvent::Render;
}
_ = self.cancel.cancelled() => {
return AppEvent::Quit;
}
}
}
}
}