pub mod ext;
pub mod layout;
pub mod span;
pub mod theme;
pub mod widget;
use std::fmt::Debug;
use std::time::Duration;
use tokio::sync::broadcast;
use tokio::sync::mpsc::UnboundedReceiver;
use crate::ui::widget::RenderProps;
use self::widget::Widget;
use super::event::Event;
use super::store::State;
use super::task::Interrupted;
use super::terminal;
const RENDERING_TICK_RATE: Duration = Duration::from_millis(250);
const INLINE_HEIGHT: usize = 20;
pub const RENDER_WIDTH_XSMALL: usize = 50;
pub const RENDER_WIDTH_SMALL: usize = 70;
pub const RENDER_WIDTH_MEDIUM: usize = 150;
pub const RENDER_WIDTH_LARGE: usize = usize::MAX;
#[derive(Default)]
pub struct Frontend {}
impl Frontend {
pub async fn main_loop<S, M, P>(
self,
mut root: Widget<S, M>,
mut state_rx: UnboundedReceiver<S>,
mut interrupt_rx: broadcast::Receiver<Interrupted<P>>,
) -> anyhow::Result<Interrupted<P>>
where
S: State<P> + 'static,
M: 'static,
P: Clone + Send + Sync + Debug,
{
let mut ticker = tokio::time::interval(RENDERING_TICK_RATE);
let mut terminal = terminal::setup(INLINE_HEIGHT)?;
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<P>> = 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
}
}