Event handling (#5)

- Refactor `event` handling (heavily inspired by [crates-tui](https://github.com/ratatui/crates-tui/) via [Tui with Terminal and EventHandler](https://ratatui.rs/recipes/apps/terminal-and-event-handler/))
- Refactor widget structure
- Disable `nixos-unstable` temporarily
- Add `.rustfmt.toml`
This commit is contained in:
Jens K.
2024-12-02 15:43:04 +01:00
committed by GitHub
parent db5909f3d9
commit 2f587c97b5
17 changed files with 469 additions and 95 deletions

76
src/events.rs Normal file
View File

@@ -0,0 +1,76 @@
use crossterm::event::{Event as CrosstermEvent, EventStream, KeyEvent, KeyEventKind};
use futures::{Stream, StreamExt};
use std::{pin::Pin, time::Duration};
use tokio::time::interval;
use tokio_stream::{wrappers::IntervalStream, StreamMap};
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
enum StreamKey {
Ticks,
Render,
Crossterm,
}
#[derive(Clone, Debug)]
pub enum Event {
Init,
Quit,
Error,
Tick,
Render,
Key(KeyEvent),
Resize(u16, u16),
}
pub struct Events {
streams: StreamMap<StreamKey, Pin<Box<dyn Stream<Item = Event>>>>,
}
impl Default for Events {
fn default() -> Self {
Self {
streams: StreamMap::from_iter([
(StreamKey::Ticks, tick_stream()),
(StreamKey::Render, render_stream()),
(StreamKey::Crossterm, crossterm_stream()),
]),
}
}
}
impl Events {
pub fn new() -> Self {
Self::default()
}
pub async fn next(&mut self) -> Option<Event> {
self.streams.next().await.map(|(_, event)| event)
}
}
fn tick_stream() -> Pin<Box<dyn Stream<Item = Event>>> {
let tick_interval = interval(Duration::from_secs_f64(1.0 / 10.0));
Box::pin(IntervalStream::new(tick_interval).map(|_| Event::Tick))
}
fn render_stream() -> Pin<Box<dyn Stream<Item = Event>>> {
let render_interval = interval(Duration::from_secs_f64(1.0 / 60.0)); // 60 FPS
Box::pin(IntervalStream::new(render_interval).map(|_| Event::Render))
}
fn crossterm_stream() -> Pin<Box<dyn Stream<Item = Event>>> {
Box::pin(
EventStream::new()
.fuse()
// we are not interested in all events
.filter_map(|event| async move {
match event {
Ok(CrosstermEvent::Key(key)) if key.kind == KeyEventKind::Press => {
Some(Event::Key(key))
}
Ok(CrosstermEvent::Resize(x, y)) => Some(Event::Resize(x, y)),
Err(_) => Some(Event::Error),
_ => None,
}
}),
)
}