use embedded_graphics::primitives::Rectangle;
use crate::{
AlertSpec, ButtonTouchResponse, ButtonTouchState, FsTheme, I18n, Localized, NavHeaderAction,
NavView, StackError, StackMotion, StackNav, TabBar, TabSpec, TouchEvent,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SplitAxis {
Horizontal,
Vertical,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SplitLayout {
pub primary: Rectangle,
pub secondary: Rectangle,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SplitView {
axis: SplitAxis,
primary_permille: u16,
spacing: u32,
}
impl SplitView {
pub const fn new(axis: SplitAxis, primary_permille: u16, spacing: u32) -> Self {
Self {
axis,
primary_permille,
spacing,
}
}
pub const fn axis(&self) -> SplitAxis {
self.axis
}
pub const fn primary_permille(&self) -> u16 {
self.primary_permille
}
pub fn set_primary_permille(&mut self, primary_permille: u16) {
self.primary_permille = primary_permille.min(1000);
}
pub const fn spacing(&self) -> u32 {
self.spacing
}
pub fn set_spacing(&mut self, spacing: u32) {
self.spacing = spacing;
}
pub fn layout(&self, frame: Rectangle) -> SplitLayout {
match self.axis {
SplitAxis::Horizontal => horizontal_split(frame, self.primary_permille, self.spacing),
SplitAxis::Vertical => vertical_split(frame, self.primary_permille, self.spacing),
}
}
}
pub struct StackView<'a, ViewId, TitleFor, const N: usize>
where
TitleFor: Fn(ViewId) -> Localized<'a>,
{
nav: StackNav<ViewId, N>,
title_for: TitleFor,
header_touch: ButtonTouchState<NavHeaderAction>,
}
impl<'a, ViewId, TitleFor, const N: usize> StackView<'a, ViewId, TitleFor, N>
where
ViewId: Copy,
TitleFor: Fn(ViewId) -> Localized<'a>,
{
pub fn new(root: ViewId, title_for: TitleFor) -> Self {
Self {
nav: StackNav::new(root),
title_for,
header_touch: ButtonTouchState::new(),
}
}
pub fn set_root(&mut self, root: ViewId) {
self.nav = StackNav::new(root);
self.header_touch.clear();
}
pub fn push_view(&mut self, view: ViewId) -> Result<(), StackError> {
self.nav.push(view)
}
pub fn pop_view(&mut self) -> Result<ViewId, StackError> {
self.nav.pop()
}
pub fn top_view(&self) -> ViewId {
self.nav.top()
}
pub fn depth(&self) -> usize {
self.nav.depth()
}
pub fn is_animating(&self) -> bool {
self.nav.is_animating()
}
pub fn advance(&mut self, dt_ms: u32) -> bool {
self.nav.advance(dt_ms)
}
pub fn nav_view(&self, frame: Rectangle) -> NavView<'a, ViewId> {
self.nav.nav_view(frame, &self.title_for)
}
pub fn clear_touch_state(&mut self) {
self.header_touch.clear();
}
pub fn handle_touch(
&mut self,
touch: TouchEvent,
frame: Rectangle,
) -> ButtonTouchResponse<NavHeaderAction> {
self.nav_view(frame)
.handle_touch(&mut self.header_touch, touch)
}
pub fn draw_chrome<D>(
&self,
display: &mut D,
frame: Rectangle,
theme: &FsTheme,
i18n: &I18n<'a>,
) where
D: embedded_graphics::draw_target::DrawTarget<
Color = embedded_graphics::pixelcolor::Rgb565,
>,
{
self.nav_view(frame)
.draw_chrome(display, theme, i18n, &self.header_touch);
}
pub fn motion(&self, frame: Rectangle) -> Option<StackMotion> {
let nav = self.nav_view(frame);
nav.layers.overlay.map(|overlay| StackMotion {
header: nav.header,
body: nav.body,
overlay: overlay.translated_frame(),
})
}
}
pub struct TabView<'a, TabId, const N: usize> {
tabs: TabBar<'a, TabId, N>,
}
impl<'a, TabId, const N: usize> TabView<'a, TabId, N>
where
TabId: Copy,
{
pub const fn new(specs: [TabSpec<'a, TabId>; N], active_index: usize) -> Self {
Self {
tabs: TabBar::new(specs, active_index),
}
}
pub fn set_tabs(&mut self, specs: [TabSpec<'a, TabId>; N], active_index: usize) {
self.tabs = TabBar::new(specs, active_index);
}
pub fn active_tab(&self) -> TabId {
self.tabs.active()
}
pub fn select_tab(&mut self, index: usize) {
self.tabs.select(index);
}
pub fn content_frame(&self, bounds: Rectangle, theme: &FsTheme) -> Rectangle {
self.tabs.content_frame(bounds, theme)
}
pub fn handle_touch(
&mut self,
touch: TouchEvent,
bounds: Rectangle,
theme: &FsTheme,
) -> Option<TabId> {
self.tabs.handle_touch(touch, bounds, theme)
}
pub fn draw_bar<D>(&self, display: &mut D, bounds: Rectangle, theme: &FsTheme, i18n: &I18n<'a>)
where
D: embedded_graphics::draw_target::DrawTarget<
Color = embedded_graphics::pixelcolor::Rgb565,
>,
{
self.tabs.draw_bar(display, bounds, theme, i18n);
}
}
pub struct AlertConfiguration<'a, const N: usize> {
pub spec: AlertSpec<'a, N>,
pub dim_alpha: u8,
}
fn horizontal_split(frame: Rectangle, primary_permille: u16, spacing: u32) -> SplitLayout {
let available = frame.size.width.saturating_sub(spacing);
let primary_width = ((available as u64 * primary_permille.min(1000) as u64) / 1000) as u32;
let secondary_width = available.saturating_sub(primary_width);
let secondary_x = frame.top_left.x + primary_width as i32 + spacing as i32;
SplitLayout {
primary: Rectangle::new(
frame.top_left,
embedded_graphics::prelude::Size::new(primary_width, frame.size.height),
),
secondary: Rectangle::new(
embedded_graphics::prelude::Point::new(secondary_x, frame.top_left.y),
embedded_graphics::prelude::Size::new(secondary_width, frame.size.height),
),
}
}
fn vertical_split(frame: Rectangle, primary_permille: u16, spacing: u32) -> SplitLayout {
let available = frame.size.height.saturating_sub(spacing);
let primary_height = ((available as u64 * primary_permille.min(1000) as u64) / 1000) as u32;
let secondary_height = available.saturating_sub(primary_height);
let secondary_y = frame.top_left.y + primary_height as i32 + spacing as i32;
SplitLayout {
primary: Rectangle::new(
frame.top_left,
embedded_graphics::prelude::Size::new(frame.size.width, primary_height),
),
secondary: Rectangle::new(
embedded_graphics::prelude::Point::new(frame.top_left.x, secondary_y),
embedded_graphics::prelude::Size::new(frame.size.width, secondary_height),
),
}
}