use ratatui::Terminal;
use ratatui::backend::Backend;
use super::model::App;
use crate::input::EventQueue;
use crate::overlay::{Overlay, OverlayAction, OverlayStack};
use crate::theme::Theme;
pub(crate) struct RuntimeCore<A: App, B: Backend> {
pub(crate) state: A::State,
pub(crate) terminal: Terminal<B>,
pub(crate) events: EventQueue,
pub(crate) overlay_stack: OverlayStack<A::Message>,
pub(crate) theme: Theme,
pub(crate) should_quit: bool,
pub(crate) max_messages_per_tick: usize,
}
impl<A: App, B: Backend> RuntimeCore<A, B> {
pub(crate) fn render(&mut self) -> crate::error::Result<()> {
#[cfg(feature = "tracing")]
let _span = tracing::debug_span!("render").entered();
let theme = &self.theme;
let overlay_stack = &self.overlay_stack;
self.terminal.draw(|frame| {
A::view(&self.state, frame);
let area = frame.area();
let mut ctx = crate::component::RenderContext::new(frame, area, theme);
overlay_stack.render(&mut ctx);
})?;
Ok(())
}
pub(crate) fn process_event(&mut self) -> ProcessEventResult<A::Message> {
if let Some(event) = self.events.pop() {
#[cfg(feature = "tracing")]
tracing::debug!(event = ?event, "processing event from queue");
match self.overlay_stack.handle_event(&event) {
OverlayAction::Consumed => ProcessEventResult::Consumed,
OverlayAction::KeepAndMessage(msg) => ProcessEventResult::Dispatch(msg),
OverlayAction::Dismiss => {
self.overlay_stack.pop();
ProcessEventResult::Consumed
}
OverlayAction::DismissWithMessage(msg) => {
self.overlay_stack.pop();
ProcessEventResult::Dispatch(msg)
}
OverlayAction::Propagate => {
if let Some(msg) = A::handle_event_with_state(&self.state, &event) {
ProcessEventResult::Dispatch(msg)
} else {
ProcessEventResult::Consumed
}
}
}
} else {
ProcessEventResult::NoEvent
}
}
pub(crate) fn push_overlay(&mut self, overlay: Box<dyn Overlay<A::Message>>) {
self.overlay_stack.push(overlay);
}
pub(crate) fn pop_overlay(&mut self) -> Option<Box<dyn Overlay<A::Message>>> {
self.overlay_stack.pop()
}
pub(crate) fn clear_overlays(&mut self) {
self.overlay_stack.clear();
}
pub(crate) fn has_overlays(&self) -> bool {
self.overlay_stack.is_active()
}
pub(crate) fn overlay_count(&self) -> usize {
self.overlay_stack.len()
}
}
pub(crate) enum ProcessEventResult<M> {
NoEvent,
Consumed,
Dispatch(M),
}
#[cfg(test)]
mod tests;