use crate::{Event, Result, TerminalWindow, Window};
use crossterm::terminal;
use std::time::{Duration, Instant};
pub struct App<S> {
window: TerminalWindow,
state: S,
frame_rate: Option<Duration>,
}
impl<S> App<S> {
pub fn new(initial_state: S) -> Result<Self> {
let mut window = TerminalWindow::new()?;
window.set_auto_flush(false);
Ok(App {
window,
state: initial_state,
frame_rate: None, })
}
pub fn window(&self) -> &TerminalWindow {
&self.window
}
pub fn window_mut(&mut self) -> &mut TerminalWindow {
&mut self.window
}
pub fn with_frame_rate(mut self, frame_rate: Duration) -> Self {
self.frame_rate = Some(frame_rate);
self
}
pub fn run<U, D>(&mut self, mut update: U, mut draw: D) -> Result<()>
where
U: FnMut(&mut S, Event) -> bool, D: FnMut(&mut S, &mut dyn Window) -> Result<()>,
{
let mut last_tick = Instant::now();
loop {
const MAX_EVENTS_PER_FRAME: usize = 256;
for _ in 0..MAX_EVENTS_PER_FRAME {
match self.window.poll_input()? {
Some(event) => {
if !update(&mut self.state, event) {
return Ok(()); }
}
None => break, }
}
if let Some(frame_rate) = self.frame_rate
&& last_tick.elapsed() >= frame_rate
{
if !update(&mut self.state, Event::Frame) {
break; }
last_tick = Instant::now();
}
if let Ok((cols, rows)) = terminal::size() {
self.window.handle_resize(cols, rows);
}
self.window.clear_screen()?;
draw(&mut self.state, &mut self.window)?;
std::thread::sleep(Duration::from_millis(1));
}
Ok(())
}
}