faststep 0.1.0

UIKit-inspired embedded UI framework built on embedded-graphics
Documentation
use core::marker::PhantomData;

use crate::{
    DisplayPort, FsTheme, I18n, TouchEvent, UiView, ViewEnvironment, ViewEvent, ViewRedraw,
    ViewRegistration,
};

/// Owns the root view, display, theme, localization, and registration state.
pub struct UiSystem<'text, Display, Root, ViewId, Message, const N: usize>
where
    Display: DisplayPort,
    Root: UiView<'text, ViewId, Message, N>,
{
    display: Display,
    root: Root,
    theme: FsTheme,
    i18n: I18n<'text>,
    registration: ViewRegistration<'text, ViewId, N>,
    marker: PhantomData<Message>,
}

impl<'text, Display, Root, ViewId, Message, const N: usize>
    UiSystem<'text, Display, Root, ViewId, Message, N>
where
    Display: DisplayPort,
    Root: UiView<'text, ViewId, Message, N>,
{
    /// Creates a fully configured UI system and runs the root `configure` pass.
    pub fn new(display: Display, root: Root, theme: FsTheme, i18n: I18n<'text>) -> Self {
        let registration = ViewRegistration::new(display.bounds());
        let mut system = Self {
            display,
            root,
            theme,
            i18n,
            registration,
            marker: PhantomData,
        };
        system.configure_root();
        system
    }

    /// Re-runs the root configuration against the current display bounds.
    pub fn configure_root(&mut self) {
        self.registration.clear_children();
        self.registration.set_frame(self.display.bounds());
        self.root.configure(&mut self.registration);
    }

    /// Returns the root view.
    pub const fn root(&self) -> &Root {
        &self.root
    }

    /// Returns the root view mutably.
    pub fn root_mut(&mut self) -> &mut Root {
        &mut self.root
    }

    /// Returns the current root registration.
    pub const fn registration(&self) -> &ViewRegistration<'text, ViewId, N> {
        &self.registration
    }

    /// Returns the current root registration mutably.
    pub fn registration_mut(&mut self) -> &mut ViewRegistration<'text, ViewId, N> {
        &mut self.registration
    }

    /// Returns the active theme.
    pub const fn theme(&self) -> &FsTheme {
        &self.theme
    }

    /// Returns the active theme mutably.
    pub fn theme_mut(&mut self) -> &mut FsTheme {
        &mut self.theme
    }

    /// Returns the active localization tables.
    pub const fn i18n(&self) -> &I18n<'text> {
        &self.i18n
    }

    /// Returns the active localization tables mutably.
    pub fn i18n_mut(&mut self) -> &mut I18n<'text> {
        &mut self.i18n
    }

    /// Returns the display port.
    pub const fn display(&self) -> &Display {
        &self.display
    }

    /// Returns the display port mutably.
    pub fn display_mut(&mut self) -> &mut Display {
        &mut self.display
    }

    /// Borrows the display mutably and the root immutably for one closure.
    pub fn with_display_and_root<R>(&mut self, apply: impl FnOnce(&mut Display, &Root) -> R) -> R {
        let display = &mut self.display;
        let root = &self.root;
        apply(display, root)
    }

    /// Borrows the display and root mutably for one closure.
    pub fn with_display_and_root_mut<R>(
        &mut self,
        apply: impl FnOnce(&mut Display, &mut Root) -> R,
    ) -> R {
        let display = &mut self.display;
        let root = &mut self.root;
        apply(display, root)
    }

    /// Runs one logical update tick on the root view.
    pub fn update(&mut self, dt_ms: u32) -> ViewEvent<Message> {
        let env = ViewEnvironment {
            theme: &self.theme,
            i18n: &self.i18n,
        };
        self.root.update(dt_ms, &self.registration, &env)
    }

    /// Routes one touch event into the root view.
    pub fn handle_touch(&mut self, touch: TouchEvent) -> ViewEvent<Message> {
        let env = ViewEnvironment {
            theme: &self.theme,
            i18n: &self.i18n,
        };
        self.root.handle_touch(touch, &self.registration, &env)
    }

    /// Draws a full frame through the display port.
    pub fn draw(&mut self) -> Result<(), Display::Error> {
        let env = ViewEnvironment {
            theme: &self.theme,
            i18n: &self.i18n,
        };
        self.display.draw_frame(|canvas| {
            self.root.draw(canvas, &self.registration, &env);
        })
    }

    /// Draws only what the supplied redraw request requires.
    pub fn draw_redraw(&mut self, redraw: ViewRedraw) -> Result<(), Display::Error> {
        match redraw {
            ViewRedraw::None => Ok(()),
            ViewRedraw::Full => self.draw(),
            ViewRedraw::Dirty(area) => {
                let env = ViewEnvironment {
                    theme: &self.theme,
                    i18n: &self.i18n,
                };
                self.display.draw_dirty(area, |canvas| {
                    self.root.draw(canvas, &self.registration, &env);
                })
            }
        }
    }
}