use std::{
any::Any,
error::Error,
time::{Duration, SystemTime},
};
use ratatui::prelude::Backend;
use crate::{
chunks::Chunks,
events::Events,
set::{Set, Sets},
setup::{reset_terminal, restore_terminal, setup_terminal, WidgetFrame, WidgetTerminal},
states::{States, Time},
widget::{IntoWidgetSet, MultiFromStates, Widget},
widgets::message::MessageState,
};
pub struct App {
terminal: WidgetTerminal,
widgets: Vec<Box<dyn Widget>>,
pub(crate) states: States,
clock: Duration,
}
impl App {
pub fn new(clock: u64) -> Result<Self, Box<dyn Error>> {
let terminal = setup_terminal()?;
Ok(Self {
terminal,
widgets: vec![],
states: States::default(),
clock: Duration::from_millis(clock),
})
}
pub fn handle_panics(self) -> Self {
let original_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |panic| {
reset_terminal().unwrap();
original_hook(panic);
}));
self
}
pub fn widgets<I>(mut self, widget: impl IntoWidgetSet<I>) -> Self {
for widget in widget.into_widget_set() {
self.widgets.push(widget);
}
self
}
pub fn states<S: MultiFromStates>(self, state: S) -> Self {
state.insert_states(self)
}
pub fn sets(self, set: impl Sets) -> Self {
set.register_sets(self)
}
pub fn run(mut self) -> Result<(), Box<dyn Error>> {
let result = self.inner_run();
restore_terminal(self.terminal)?;
result
}
fn inner_run(&mut self) -> Result<(), Box<dyn Error>> {
self.terminal.hide_cursor()?;
loop {
self.terminal.autoresize()?;
let mut frame = self.terminal.get_frame();
{
let mut chunks = self.states.get::<Chunks>()?;
let mut chunks = chunks.get();
chunks.clear();
let mut events = self.states.get::<Events>()?;
let mut events = events.get();
let mut time = self.states.get::<Time>()?;
let mut time = time.get();
events.event = None;
let start_time = SystemTime::now();
if crossterm::event::poll(self.clock)? {
events.event = Some(crossterm::event::read()?);
}
let total_time = SystemTime::now().duration_since(start_time)?;
time.set_duration(total_time);
}
self.states.get::<Chunks>()?.get();
for widget in &mut self.widgets {
widget.call(&mut frame, &mut self.states)?;
}
self.terminal.flush()?;
self.terminal.swap_buffers();
self.terminal.backend_mut().flush()?;
if self.states.get::<Events>()?.get().exit {
return Ok(());
}
}
}
}