faststep 0.1.0

UIKit-inspired embedded UI framework built on embedded-graphics
Documentation
use embedded_graphics::{
    draw_target::DrawTarget,
    mono_font::MonoTextStyle,
    pixelcolor::Rgb565,
    prelude::*,
    primitives::Rectangle,
    text::{Text, TextStyle},
};

use crate::{Button, ButtonKind, ButtonSpec, Localized, NavHeaderAction, StackNav, lerp_i32};

use super::stack::HeaderTransition;

const BACK_BUTTON_WIDTH: u32 = 112;

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(super) struct HeaderTitle<'a> {
    pub title: Localized<'a>,
    pub center: Point,
}

pub(super) fn show_back_button<K: Copy, const N: usize>(nav: &StackNav<K, N>) -> bool {
    nav.depth() > 1 || matches!(nav.header_transition(), Some(HeaderTransition::Pop { .. }))
}

pub(super) fn header_titles<'a, K: Copy, F, const N: usize>(
    nav: &StackNav<K, N>,
    header: Rectangle,
    title_for: &F,
    back_button: Option<&Button<'a, NavHeaderAction>>,
) -> (
    Option<Localized<'a>>,
    HeaderTitle<'a>,
    Option<HeaderTitle<'a>>,
)
where
    F: Fn(K) -> Localized<'a>,
{
    let center = header.center();
    let right = Point::new(center.x + header.size.width as i32, center.y);
    let back = back_title_center(back_button);
    match nav.header_transition() {
        Some(HeaderTransition::Push { from, to, progress }) => (
            None,
            HeaderTitle {
                title: title_for(from),
                center: Point::new(lerp_i32(center.x, back.x, progress), center.y),
            },
            Some(HeaderTitle {
                title: title_for(to),
                center: Point::new(lerp_i32(right.x, center.x, progress), center.y),
            }),
        ),
        Some(HeaderTransition::Pop { from, to, progress }) => (
            None,
            HeaderTitle {
                title: title_for(to),
                center: Point::new(lerp_i32(back.x, center.x, progress), center.y),
            },
            Some(HeaderTitle {
                title: title_for(from),
                center: Point::new(lerp_i32(center.x, right.x, progress), center.y),
            }),
        ),
        None => (
            nav.previous().map(title_for),
            HeaderTitle {
                title: title_for(nav.top()),
                center,
            },
            None,
        ),
    }
}

pub(super) fn draw_title<D>(
    display: &mut D,
    title: &str,
    center: Point,
    style: MonoTextStyle<'_, Rgb565>,
    text_style: TextStyle,
) where
    D: DrawTarget<Color = Rgb565>,
{
    Text::with_text_style(title, center, style, text_style)
        .draw(display)
        .ok();
}

pub(super) fn back_title_center(button: Option<&Button<'_, NavHeaderAction>>) -> Point {
    if let Some(button) = button {
        Point::new(
            button.frame.top_left.x + 68,
            button.frame.top_left.y + (button.frame.size.height / 2) as i32,
        )
    } else {
        Point::zero()
    }
}

pub(super) fn back_button<'a>(header: Rectangle) -> Button<'a, NavHeaderAction> {
    Button::new(
        Rectangle::new(
            header.top_left + Point::new(16, 12),
            Size::new(BACK_BUTTON_WIDTH, header.size.height.saturating_sub(24)),
        ),
        ButtonSpec {
            key: NavHeaderAction::Back,
            icon: None,
            label: Localized::new("", ""),
            kind: ButtonKind::Ghost,
        },
    )
}