pub mod widget;
use std::fmt::Debug;
use std::time::Duration;
use ratatui::Viewport;
use tokio::sync::broadcast;
use tokio::sync::mpsc::UnboundedReceiver;
use crate::event::Event;
use crate::store::Update;
use crate::task::Interrupted;
use crate::terminal;
use crate::ui::rm::widget::RenderProps;
use crate::ui::rm::widget::Widget;
const RENDERING_TICK_RATE: Duration = Duration::from_millis(250);
#[derive(Default)]
pub struct Frontend {}
impl Frontend {
pub async fn run<S, M, R>(
self,
mut root: Widget<S, M>,
mut state_rx: UnboundedReceiver<S>,
mut interrupt_rx: broadcast::Receiver<Interrupted<R>>,
viewport: Viewport,
) -> anyhow::Result<Interrupted<R>>
where
S: Update<M, Return = R> + 'static,
M: 'static,
R: Clone + Send + Sync + Debug,
{
let mut ticker = tokio::time::interval(RENDERING_TICK_RATE);
let mut terminal = terminal::setup(viewport)?;
let mut events_rx = terminal::events();
let mut root = {
let state = state_rx.recv().await.unwrap();
root.update(&state);
root
};
let result: anyhow::Result<Interrupted<R>> = loop {
tokio::select! {
_ = ticker.tick() => (),
Some(event) = events_rx.recv() => match event {
Event::Key(key) => root.handle_event(key),
Event::Resize => (),
},
Some(state) = state_rx.recv() => {
root.update(&state);
},
Ok(interrupted) = interrupt_rx.recv() => {
let size = terminal.get_frame().size();
let _ = terminal.set_cursor(size.x, size.y);
break Ok(interrupted);
}
}
terminal.draw(|frame| root.render(RenderProps::from(frame.size()), frame))?;
};
terminal::restore(&mut terminal)?;
result
}
}