Skip to main content

zest_theme/
style.rs

1//! Catalog/Status/Appearance pattern, modeled on libcosmic but trimmed
2//! for embedded touch:
3//!
4//! - **No hover state.** Touch screens have no pointer-over phase.
5//! - **No animations / transitions.** Allocate-and-draw budget on an
6//!   ESP32 doesn't have room for them.
7//!
8//! A catalog is a function on the [`Theme`](crate::Theme): given a
9//! widget *class* (variant — `Standard`, `Suggested`, `Destructive`,
10//! …) and a *status* (`Active`, `Focused`, `Pressed`, `Disabled`), return a
11//! resolved [`ButtonAppearance`] the widget can paint directly.
12//! Widgets never reach into the theme's `Component`/`Container` fields;
13//! they call the catalog method.
14
15use embedded_graphics::pixelcolor::PixelColor;
16
17/// Interaction state of a stateful widget. Single shared enum across
18/// all interactive widgets — keeps the catalog dispatch table small
19/// and the embedded code path predictable.
20#[derive(Copy, Clone, Debug, PartialEq, Eq)]
21pub enum Status {
22    /// Resting state — not pressed, not disabled.
23    Active,
24    /// Focused through non-touch traversal.
25    Focused,
26    /// Currently being touched.
27    Pressed,
28    /// No-op state (e.g. button has no `on_press`).
29    Disabled,
30}
31
32/// Semantic variant of a button. The catalog maps a class to one of
33/// the theme's interactive components (button / accent / destructive
34/// / …) without leaking those field names to widget code.
35#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
36pub enum ButtonClass {
37    /// Default filled button. Maps to `theme.button`.
38    #[default]
39    Standard,
40    /// Primary call-to-action. Maps to `theme.accent`.
41    Suggested,
42    /// Destructive action (delete, cancel-with-loss). Maps to `theme.destructive`.
43    Destructive,
44    /// Positive confirmation. Maps to `theme.success`.
45    Success,
46    /// Cautionary action. Maps to `theme.warning`.
47    Warning,
48    /// No fill, no border — text-only. Maps to `theme.text_button`.
49    Text,
50    /// Icon-only target. Maps to `theme.icon_button`.
51    Icon,
52}
53
54/// Resolved button style — what the widget actually paints.
55///
56/// `Option`s encode "no fill" / "no border" so a `ButtonClass::Text`
57/// can return `background = None, border = None` without us inventing
58/// a sentinel transparent color.
59#[derive(Copy, Clone, Debug)]
60pub struct ButtonAppearance<C: PixelColor> {
61    /// Fill color. `None` = transparent.
62    pub background: Option<C>,
63    /// Border color. `None` = no border drawn.
64    pub border: Option<C>,
65    /// Foreground color (label text).
66    pub text: C,
67}
68
69/// Catalog for buttons. Implemented by [`crate::Theme`].
70pub trait ButtonCatalog<C: PixelColor> {
71    /// Resolve `(class, status)` to the colors the widget should paint.
72    fn button(&self, class: ButtonClass, status: Status) -> ButtonAppearance<C>;
73}