Skip to content

Commit

Permalink
Simplify simple template
Browse files Browse the repository at this point in the history
  • Loading branch information
joshka committed Aug 26, 2024
1 parent 7be2a70 commit 5e8984d
Show file tree
Hide file tree
Showing 15 changed files with 386 additions and 564 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions simple-generated/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ license = "MIT"
edition = "2021"

[dependencies]
crossterm = "0.28.1"
ratatui = "0.28.1"
color-eyre = "0.6.3"
49 changes: 0 additions & 49 deletions simple-generated/src/app.rs

This file was deleted.

145 changes: 87 additions & 58 deletions simple-generated/src/event.rs
Original file line number Diff line number Diff line change
@@ -1,87 +1,116 @@
use crate::app::AppResult;
use ratatui::crossterm::event::{
self, Event as CrosstermEvent, KeyEvent, KeyEventKind, MouseEvent,
};
use crossterm::event::{self, Event as CrosstermEvent, KeyEvent, KeyEventKind, MouseEvent};
use std::sync::mpsc;
use std::thread;
use std::time::{Duration, Instant};

/// Terminal events.
#[derive(Clone, Copy, Debug)]
#[derive(Debug)]
pub enum Event {
/// Terminal tick.
/// The tick event is sent at a regular interval and can be used to trigger application state
/// updates or animations.
Tick,

/// Key press.
Key(KeyEvent),

/// Mouse click/scroll.
Mouse(MouseEvent),

/// Terminal resize.
///
/// This event may be fired multiple times in quick succession, so it is recommended not to
/// render the UI on every resize event.
Resize(u16, u16),

/// An error occurred.
Error(std::io::Error),
}

/// Terminal event handler.
#[allow(dead_code)]
///
/// This struct is responsible for listening to terminal events and sending them to the main thread.
///
/// It sends a tick event at a regular interval. The tick rate is specified by the `tick_rate`
/// parameter. If an error occurs, it sends an error event to the main thread and then stops
/// running.
#[derive(Debug)]
pub struct EventHandler {
/// Event sender channel.
sender: mpsc::Sender<Event>,
pub struct EventSource {
/// Event receiver channel.
receiver: mpsc::Receiver<Event>,
/// Event handler thread.
handler: thread::JoinHandle<()>,
}

impl EventHandler {
impl EventSource {
/// Constructs a new instance of [`EventHandler`].
pub fn new(tick_rate: u64) -> Self {
let tick_rate = Duration::from_millis(tick_rate);
pub fn new(tick_rate: Duration) -> Self {
let (sender, receiver) = mpsc::channel();
let handler = {
let sender = sender.clone();
thread::spawn(move || {
let mut last_tick = Instant::now();
loop {
let timeout = tick_rate
.checked_sub(last_tick.elapsed())
.unwrap_or(tick_rate);

if event::poll(timeout).expect("failed to poll new events") {
match event::read().expect("unable to read event") {
CrosstermEvent::Key(e) => {
if e.kind == KeyEventKind::Press {
sender.send(Event::Key(e))
} else {
Ok(())
}
}
CrosstermEvent::Mouse(e) => sender.send(Event::Mouse(e)),
CrosstermEvent::Resize(w, h) => sender.send(Event::Resize(w, h)),
CrosstermEvent::FocusGained => Ok(()),
CrosstermEvent::FocusLost => Ok(()),
CrosstermEvent::Paste(_) => unimplemented!(),
}
.expect("failed to send terminal event")
}

if last_tick.elapsed() >= tick_rate {
sender.send(Event::Tick).expect("failed to send tick event");
last_tick = Instant::now();
}
}
})
};
Self {
sender,
receiver,
handler,
}
thread::spawn(move || event_thread(sender, tick_rate));
Self { receiver }
}

/// Receive the next event from the handler thread.
///
/// This function will always block the current thread if
/// there is no data available and it's possible for more data to be sent.
pub fn next(&self) -> AppResult<Event> {
Ok(self.receiver.recv()?)
/// This function will always block the current thread if there is no data available unless the
/// event source has been closed.
pub fn next(&self) -> color_eyre::Result<Event> {
let event = self.receiver.recv()?;
Ok(event)
}
}

/// An event thread that listens for terminal events.
///
/// This function is responsible for listening to terminal events and sending them to the main
/// thread. It sends a tick event at a regular interval. The tick rate is specified by the
/// `tick_rate` parameter. If an error occurs, it sends an error event to the main thread and then
/// stops running.
fn event_thread(sender: mpsc::Sender<Event>, tick_rate: Duration) {
let mut last_tick: Option<Instant> = None;
loop {
if last_tick
.map(|tick| tick.elapsed() >= tick_rate)
.unwrap_or(true)
{
sender.send(Event::Tick).expect("failed to send tick event");
last_tick = Some(Instant::now());
}

let timeout = last_tick.map_or(Duration::ZERO, |tick| {
tick_rate.saturating_sub(tick.elapsed())
});

match event::poll(timeout) {
Ok(false) => {
// no new events waiting
continue;
}
Ok(true) => {}
Err(e) => {
let _ = sender.send(Event::Error(e));
break;
}
}

match event::read() {
Err(err) => {
let _ = sender.send(Event::Error(err));
break;
}
Ok(event) => match event {
CrosstermEvent::Key(e) if e.kind == KeyEventKind::Press => {
// ignore key release / repeat events
let _ = sender.send(Event::Key(e));
}
CrosstermEvent::Key(_) => {}
CrosstermEvent::Mouse(e) => {
let _ = sender.send(Event::Mouse(e));
}
CrosstermEvent::Resize(w, h) => {
let _ = sender.send(Event::Resize(w, h));
}
CrosstermEvent::FocusGained => {}
CrosstermEvent::FocusLost => {}
CrosstermEvent::Paste(_) => {}
},
}
}
}
28 changes: 0 additions & 28 deletions simple-generated/src/handler.rs

This file was deleted.

Loading

0 comments on commit 5e8984d

Please sign in to comment.