use eye_declare_engine::escape::CursorStyle;
use futures_core::Stream;
use crate::element::Element;
use crate::input::Keymap;
use crate::subscription::Subscriptions;
use crate::task::{Effect, PersistTracker, Task, spawn_effect, spawn_once_effect};
use crate::timeline::Timeline;
pub trait App: Sized {
type Msg;
type Output: Default;
fn init(&mut self, ctx: &mut Ctx<'_, Self>) {
let _ = ctx;
}
fn update(&mut self, msg: Self::Msg, ctx: &mut Ctx<'_, Self>);
fn tail(&self) -> impl Element + '_;
fn keymap(&self) -> Keymap<Self::Msg> {
Keymap::new()
}
fn subscriptions(&self) -> Subscriptions<Self::Msg> {
Subscriptions::new()
}
fn cursor_style(&self) -> CursorStyle {
CursorStyle::DefaultUserShape
}
fn on_resize(&self, width: u16, height: u16) -> Option<Self::Msg> {
let _ = (width, height);
None
}
}
pub struct Ctx<'a, A: App> {
pub(crate) timeline: &'a mut Timeline,
pub(crate) output: &'a mut Vec<u8>,
pub(crate) effects: &'a mut Vec<Effect<A::Msg>>,
pub(crate) persists: &'a PersistTracker,
pub(crate) exit: Option<A::Output>,
}
impl<A: App> Ctx<'_, A> {
pub fn push(&mut self, block: impl Element) {
let bytes = self.timeline.push(block);
self.output.extend_from_slice(&bytes);
}
#[must_use]
pub fn spawn(&mut self, stream: impl Stream<Item = A::Msg> + Send + 'static) -> Task {
let (effect, task) = spawn_effect(stream);
self.effects.push(effect);
task
}
#[must_use]
pub fn perform(&mut self, future: impl Future<Output = A::Msg> + Send + 'static) -> Task {
let (effect, task) = spawn_once_effect(future);
self.effects.push(effect);
task
}
pub fn persist(&mut self, future: impl Future<Output = A::Msg> + Send + 'static) {
let guard = self.persists.guard();
let (effect, task) = spawn_once_effect(async move {
let _guard = guard;
future.await
});
task.detach();
self.effects.push(effect);
}
pub fn exit(&mut self, output: A::Output) {
self.exit = Some(output);
}
}