use embedded_graphics::primitives::Rectangle;
use crate::{ButtonTouchState, FsTheme, I18n, NavHeaderAction, StackNav, TouchEvent};
use super::{AppNavigation, AppRedraw, AppTouchResult, ViewDelegate, ViewResponse};
pub struct UiApp<'a, ViewId, Delegate, const STACK_DEPTH: usize> {
pub(super) theme: FsTheme,
pub(super) i18n: I18n<'a>,
pub(super) stack: StackNav<ViewId, STACK_DEPTH>,
pub(super) nav_touch: ButtonTouchState<NavHeaderAction>,
pub(super) delegate: Delegate,
}
impl<'a, ViewId, Delegate, const STACK_DEPTH: usize> UiApp<'a, ViewId, Delegate, STACK_DEPTH>
where
ViewId: Copy,
Delegate: ViewDelegate<'a, ViewId>,
{
pub fn new(root: ViewId, delegate: Delegate, theme: FsTheme, i18n: I18n<'a>) -> Self {
Self {
theme,
i18n,
stack: StackNav::new(root),
nav_touch: ButtonTouchState::new(),
delegate,
}
}
pub const fn delegate(&self) -> &Delegate {
&self.delegate
}
pub fn delegate_mut(&mut self) -> &mut Delegate {
&mut self.delegate
}
pub const fn theme(&self) -> &FsTheme {
&self.theme
}
pub fn theme_mut(&mut self) -> &mut FsTheme {
&mut self.theme
}
pub const fn i18n(&self) -> &I18n<'a> {
&self.i18n
}
pub fn i18n_mut(&mut self) -> &mut I18n<'a> {
&mut self.i18n
}
pub fn active_view(&self) -> ViewId {
self.stack.top()
}
pub fn is_animating(&self) -> bool {
self.stack.is_animating()
}
pub fn content_frame(&self, bounds: Rectangle) -> Rectangle {
self.nav_view(bounds).body
}
pub fn active_view_dirty(&self, bounds: Rectangle) -> Rectangle {
self.content_frame(bounds)
}
pub fn tick(&mut self, dt_ms: u32, bounds: Rectangle) -> AppRedraw {
if self.stack.advance(dt_ms) {
return AppRedraw::StackMotion;
}
let body = self.content_frame(bounds);
let response = self
.delegate
.tick(self.stack.top(), dt_ms, body, &self.theme, &self.i18n);
self.apply_response(response)
}
pub fn handle_touch(&mut self, touch: TouchEvent, bounds: Rectangle) -> AppRedraw {
self.handle_touch_result(touch, bounds).redraw
}
pub fn handle_touch_result(&mut self, touch: TouchEvent, bounds: Rectangle) -> AppTouchResult {
if self.is_animating() {
return AppTouchResult {
redraw: AppRedraw::None,
captured: false,
};
}
let nav = self.nav_view(bounds);
let chrome = nav.handle_touch(&mut self.nav_touch, touch);
if chrome.action == Some(NavHeaderAction::Back) {
self.clear_touch_state();
return AppTouchResult {
redraw: self
.stack
.pop()
.map(|_| AppRedraw::StackMotion)
.unwrap_or(AppRedraw::None),
captured: true,
};
}
if chrome.captured {
return AppTouchResult {
redraw: if chrome.redraw {
AppRedraw::Interactive
} else {
AppRedraw::None
},
captured: true,
};
}
let response = self.delegate.handle_view_touch(
self.stack.top(),
touch,
nav.body,
&self.theme,
&self.i18n,
);
AppTouchResult {
redraw: self.apply_response(response),
captured: response.captured || response.navigation.is_some(),
}
}
pub fn clear_touch_state(&mut self) {
self.nav_touch.clear();
self.delegate.clear_touch_state();
}
fn apply_response(&mut self, response: ViewResponse<ViewId>) -> AppRedraw {
let mut redraw = response.redraw;
match response.navigation {
Some(AppNavigation::Push(view)) => {
self.clear_touch_state();
if self.stack.push(view).is_ok() {
redraw = redraw.merge(AppRedraw::StackMotion);
}
}
Some(AppNavigation::Pop) => {
self.clear_touch_state();
if self.stack.pop().is_ok() {
redraw = redraw.merge(AppRedraw::StackMotion);
}
}
None => {}
}
redraw
}
}