use crate::{DisplayPort, FsTheme, I18n, TouchEvent, UiSystem, UiView, ViewEvent};
pub trait UiRuntimeDriver {
type Error;
fn idle_poll_ms(&self) -> u32;
fn active_poll_ms(&self) -> u32;
fn needs_active_poll(&self) -> bool;
fn delay_ms(&mut self, delay_ms: u32);
fn now_ms(&self) -> u32;
fn poll_touch(&mut self, now_ms: u32) -> Result<Option<TouchEvent>, Self::Error>;
}
pub trait UiRuntimePresenter<'text, Display, Root, ViewId, Message, const N: usize>
where
Display: DisplayPort,
Root: UiView<'text, ViewId, Message, N>,
{
type Pending: Copy;
type Error;
fn idle_pending(&self) -> Self::Pending;
fn merge_pending(&self, current: Self::Pending, next: Self::Pending) -> Self::Pending;
fn pending_for_event(&mut self, event: ViewEvent<Message>) -> Self::Pending;
fn should_present(
&mut self,
pending: Self::Pending,
since_present_ms: u32,
system: &UiSystem<'text, Display, Root, ViewId, Message, N>,
) -> bool;
fn present(
&mut self,
pending: Self::Pending,
system: &mut UiSystem<'text, Display, Root, ViewId, Message, N>,
) -> Result<(), Self::Error>;
fn did_present(
&mut self,
_pending: Self::Pending,
_system: &UiSystem<'text, Display, Root, ViewId, Message, N>,
) {
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum UiRuntimeError<DriverError, PresentError> {
Driver(DriverError),
Present(PresentError),
}
pub fn run_ui_system<'text, Display, Root, ViewId, Message, Driver, Presenter, const N: usize>(
display: Display,
root: Root,
theme: FsTheme,
i18n: I18n<'text>,
mut driver: Driver,
mut presenter: Presenter,
) -> Result<(), UiRuntimeError<Driver::Error, Presenter::Error>>
where
Display: DisplayPort,
Root: UiView<'text, ViewId, Message, N>,
Driver: UiRuntimeDriver,
Presenter: UiRuntimePresenter<'text, Display, Root, ViewId, Message, N>,
{
let mut system = UiSystem::new(display, root, theme, i18n);
let mut pending = presenter.idle_pending();
let mut last_tick_ms = driver.now_ms();
let mut last_present_ms = last_tick_ms;
loop {
let poll_delay_ms = if driver.needs_active_poll() {
driver.active_poll_ms()
} else {
driver.idle_poll_ms()
};
driver.delay_ms(poll_delay_ms);
let now_ms = driver.now_ms();
let dt_ms = now_ms.saturating_sub(last_tick_ms).max(1);
last_tick_ms = now_ms;
let update_pending = presenter.pending_for_event(system.update(dt_ms));
pending = presenter.merge_pending(pending, update_pending);
if let Some(touch) = driver.poll_touch(now_ms).map_err(UiRuntimeError::Driver)? {
let touch_pending = presenter.pending_for_event(system.handle_touch(touch));
pending = presenter.merge_pending(pending, touch_pending);
}
let since_present_ms = now_ms.saturating_sub(last_present_ms);
if presenter.should_present(pending, since_present_ms, &system) {
presenter
.present(pending, &mut system)
.map_err(UiRuntimeError::Present)?;
presenter.did_present(pending, &system);
pending = presenter.idle_pending();
last_present_ms = driver.now_ms();
}
}
}