mod exit;
mod managed_terminal;
pub use exit::{request_exit, reset_exit, should_exit};
pub use managed_terminal::{ManagedTerminal, restore_terminal, setup_terminal};
use anyhow::Result;
use crossterm::event::Event;
use reratui_core::Element;
use reratui_hooks::frame::FrameContext;
use reratui_hooks::hook_context::HookContext;
use std::{
rc::Rc,
time::{Duration, Instant},
};
pub async fn render<F>(initializer: F) -> Result<()>
where
F: Fn() -> Element + 'static,
{
reratui_panic::setup_panic_handler();
let mut terminal = setup_terminal()?;
let hook_context = Rc::new(HookContext::new());
reratui_hooks::hook_context::set_hook_context(hook_context.clone());
let element = initializer();
let mut frame_count: u64 = 0;
let mut last_frame_time = Instant::now();
use crossterm::event::EventStream;
use tokio_stream::StreamExt;
let mut events = EventStream::new();
loop {
let current_time = Instant::now();
let delta = current_time.duration_since(last_frame_time);
last_frame_time = current_time;
hook_context.reset_hook_index();
let timeout = tokio::time::sleep(Duration::from_millis(16));
tokio::pin!(timeout);
tokio::select! {
Some(Ok(event)) = events.next() => {
let processed = if let Event::Key(key_event) = &event {
reratui_hooks::event::global_events::process_global_event(key_event)
} else {
false
};
if !processed {
reratui_hooks::event::set_current_event(Some(std::sync::Arc::new(event)));
}
}
_ = &mut timeout => {
reratui_hooks::event::set_current_event(None);
}
}
if should_exit() {
break;
}
terminal.draw(|frame| {
let frame_ctx = unsafe { FrameContext::new(frame, frame_count, delta, current_time) };
let _frame_context = reratui_hooks::context::use_context_provider(|| frame_ctx);
let area = frame.area();
element.render(area, frame.buffer_mut());
})?;
reratui_core::component::cleanup_unmounted();
frame_count += 1;
}
reratui_hooks::event::set_current_event(None);
reratui_hooks::hook_context::clear_hook_context();
restore_terminal()?;
Ok(())
}