Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions backend-embedded-graphics/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ embedded-gui = { path = ".." }
heapless = "0.7"
az = "1.1"
object-chain = "0.1"
paste = "1.0.5"

[features]
ansi = ["embedded-text/ansi"]
Expand Down
3 changes: 2 additions & 1 deletion backend-embedded-graphics/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
//! Themes
//! ------
//!
//! This crate provides collections of commonly used widget compositions, called themes.
//! This crate provides collections of commonly used widget compositions, called themes. Rendering
//! and visual style of graphical widgets are also implemented by themes.
//!
//! - [`DefaultTheme`]: supports both `BinaryColor`, `Rgb555`, `Rgb565`, `Rgb888` draw targets.
//!
Expand Down
94 changes: 94 additions & 0 deletions backend-embedded-graphics/src/themes/basic/button/light.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
//! Light theme for buttons.

use crate::{button_style_binary_color, button_style_rgb};

button_style_binary_color!(
PrimaryButton {
font: ascii::FONT_6X10,
states: {
Inactive, Idle, Pressed: {
label: Off,
border: On,
background: On,
},
Hovered: {
label: On,
border: On,
background: Off,
}
}
},
SecondaryButton {
font: ascii::FONT_6X10,
states: {
Inactive, Idle: {
label: On,
border: Off,
background: Off,
},
Hovered: {
label: On,
border: On,
background: Off,
},
Pressed: {
label: Off,
border: Off,
background: On,
}
}
}
);

button_style_rgb!(
PrimaryButton {
font: ascii::FONT_6X10,
states: {
Inactive: {
label: CSS_LIGHT_GRAY,
border: CSS_DIM_GRAY,
background: CSS_DIM_GRAY,
},
Idle: {
label: WHITE,
border: CSS_STEEL_BLUE,
background: CSS_STEEL_BLUE,
},
Hovered: {
label: WHITE,
border: CSS_DODGER_BLUE,
background: CSS_DODGER_BLUE,
},
Pressed: {
label: WHITE,
border: CSS_LIGHT_STEEL_BLUE,
background: CSS_LIGHT_STEEL_BLUE,
}
}
},
SecondaryButton {
font: ascii::FONT_6X10,
states: {
Inactive: {
label: CSS_LIGHT_GRAY,
border: CSS_DIM_GRAY,
background: CSS_DIM_GRAY,
},
Idle: {
label: WHITE,
border: CSS_SLATE_GRAY,
background: CSS_SLATE_GRAY,
},
Hovered: {
label: WHITE,
border: CSS_LIGHT_SLATE_GRAY,
background: CSS_LIGHT_SLATE_GRAY,
},
Pressed: {
label: WHITE,
border: CSS_STEEL_BLUE,
background: CSS_STEEL_BLUE,
}
}
}
);
270 changes: 270 additions & 0 deletions backend-embedded-graphics/src/themes/basic/button/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
//! Helper macros and types to build BaseTheme buttons

// Themes supported
pub mod light;

use crate::{
themes::basic::BasicTheme,
widgets::{
background::BackgroundStyle,
border::BorderStyle,
label::{LabelStyle, LabelStyling, MonoFontLabelStyling},
},
};
use embedded_graphics::{
mono_font::{MonoFont, MonoTextStyle},
prelude::PixelColor,
};
use embedded_gui::{
state::WidgetState,
widgets::{
background::{Background, BackgroundProperties},
border::{Border, BorderProperties},
button::Button,
fill::{Center, FillParent, HorizontalAndVertical},
label::Label,
spacing::Spacing,
Widget,
},
};

/// Implementation details
// TODO: this should be merged with other widgets
#[macro_export]
macro_rules! button_style {
(@state $state:ident<$color_t:ty> {
label: $label:tt,
border: $border:tt,
background: $background:tt,
}) => {
pub struct $state;

impl $crate::themes::basic::button::ButtonStateColors<$color_t> for $state {
const LABEL_COLOR: $color_t = <$color_t>::$label;
const BORDER_COLOR: $color_t = <$color_t>::$border;
const BACKGROUND_COLOR: $color_t = <$color_t>::$background;
}
};

(@impl $($style:ident<$color_t:ty> {
font: $font_mod:tt::$font:tt,
states: {
$($($state:ident),+: $state_desc:tt),+
}
}),+) => {
$(
pub struct $style;
impl $crate::themes::basic::button::ButtonStyle<$color_t> for $style {
paste::paste! {
$($(type $state = [<$style $state>];)+)+
}

const FONT: MonoFont<'static> = mono_font::$font_mod::$font;
}

$(
$(
paste::paste! {
$crate::button_style!(@state [<$style $state>]<$color_t> $state_desc);
}
)+
)+
)+
};
}

/// BaseTheme specific binary color button style helper
#[macro_export]
macro_rules! button_style_binary_color {
($($style:ident $descriptor:tt),+) => {
#[allow(unused)]
pub mod binary_color {
use embedded_graphics::{
mono_font::{self, MonoFont},
pixelcolor::BinaryColor,
};

$(
$crate::button_style!(@impl $style<BinaryColor> $descriptor);
)+
}
};
}

/// BaseTheme specific RGB color button style helper
#[macro_export]
macro_rules! button_style_rgb {
(@color $mod:ident, $color_t:tt, $($style:ident $descriptor:tt)+) => {
#[allow(unused)]
pub mod $mod {
use embedded_graphics::{
mono_font::{self, MonoFont},
pixelcolor::$color_t,
prelude::{RgbColor, WebColors},
};
$(
$crate::button_style!(@impl $style<$color_t> $descriptor);
)+
}
};

($($style:ident $descriptor:tt),+) => {
$crate::button_style_rgb!(@color rgb555, Rgb555, $($style $descriptor)+);
$crate::button_style_rgb!(@color rgb565, Rgb565, $($style $descriptor)+);
$crate::button_style_rgb!(@color rgb666, Rgb666, $($style $descriptor)+);
$crate::button_style_rgb!(@color rgb888, Rgb888, $($style $descriptor)+);
};
}

pub trait ButtonStateColors<C: PixelColor> {
const LABEL_COLOR: C;
const BORDER_COLOR: C;
const BACKGROUND_COLOR: C;

fn apply_label<S, T>(label: &mut Label<S, T>)
where
Label<S, T>: LabelStyling<S, Color = C>,
{
label.set_text_color(Self::LABEL_COLOR);
}

fn apply_background<W, T>(background: &mut Background<W, T>)
where
T: BackgroundProperties<Color = C>,
W: Widget,
{
background.set_background_color(Self::BACKGROUND_COLOR);
}

fn apply_border<W, T>(border: &mut Border<W, T>)
where
T: BorderProperties<Color = C>,
W: Widget,
{
border.set_border_color(Self::BORDER_COLOR);
}
}

pub trait ButtonStyle<C: PixelColor> {
type Inactive: ButtonStateColors<C>;
type Idle: ButtonStateColors<C>;
type Hovered: ButtonStateColors<C>;
type Pressed: ButtonStateColors<C>;

const FONT: MonoFont<'static>;

fn apply_label<S, T>(label: &mut Label<S, T>, state: WidgetState)
where
Label<S, T>: LabelStyling<S, Color = C>,
{
if state.has_state(Button::STATE_INACTIVE) {
Self::Inactive::apply_label(label);
} else if state.has_state(Button::STATE_HOVERED) {
Self::Hovered::apply_label(label);
} else if state.has_state(Button::STATE_PRESSED) {
Self::Pressed::apply_label(label);
} else {
Self::Idle::apply_label(label);
};
}

fn apply_border<W, T>(border: &mut Border<W, T>, state: WidgetState)
where
T: BorderProperties<Color = C>,
W: Widget,
{
if state.has_state(Button::STATE_INACTIVE) {
Self::Inactive::apply_border(border);
} else if state.has_state(Button::STATE_HOVERED) {
Self::Hovered::apply_border(border);
} else if state.has_state(Button::STATE_PRESSED) {
Self::Pressed::apply_border(border);
} else {
Self::Idle::apply_border(border);
};
}

fn apply_background<W, T>(background: &mut Background<W, T>, state: WidgetState)
where
T: BackgroundProperties<Color = C>,
W: Widget,
{
if state.has_state(Button::STATE_INACTIVE) {
Self::Inactive::apply_background(background);
} else if state.has_state(Button::STATE_HOVERED) {
Self::Hovered::apply_background(background);
} else if state.has_state(Button::STATE_PRESSED) {
Self::Pressed::apply_background(background);
} else {
Self::Idle::apply_background(background);
};
}
}

pub type StyledButtonDecorator<C, W> =
Button<Border<Background<W, BackgroundStyle<C>>, BorderStyle<C>>>;

fn button<C, S, W>(inner: W) -> StyledButtonDecorator<C::PixelColor, W>
where
C: BasicTheme,
S: ButtonStyle<C::PixelColor>,
W: Widget,
{
Button::new(
Border::with_style(
Background::with_style(inner, BackgroundStyle::new(S::Idle::BACKGROUND_COLOR))
.on_state_changed(S::apply_background),
BorderStyle::new(S::Idle::BORDER_COLOR, 1),
)
.on_state_changed(S::apply_border),
)
}

// Type alias to decouple button definition from theme
pub type StyledButton<S, C> =
StyledButtonDecorator<C, Spacing<Label<S, LabelStyle<MonoTextStyle<'static, C>>>>>;

pub fn styled_button<ST, C, S>(label: ST) -> StyledButton<ST, C::PixelColor>
where
ST: AsRef<str>,
C: BasicTheme,
S: ButtonStyle<C::PixelColor>,
{
button::<C, S, _>(
Spacing::new(
C::label(label)
.font(&S::FONT)
.text_color(S::Idle::LABEL_COLOR)
.on_state_changed(S::apply_label),
)
.all(1),
)
}

pub type StyledButtonStretched<S, C> = StyledButtonDecorator<
C,
FillParent<
Label<S, LabelStyle<MonoTextStyle<'static, C>>>,
HorizontalAndVertical,
Center,
Center,
>,
>;

pub fn styled_button_stretched<ST, C, S>(label: ST) -> StyledButtonStretched<ST, C::PixelColor>
where
ST: AsRef<str>,
C: BasicTheme,
S: ButtonStyle<C::PixelColor>,
{
button::<C, S, _>(
FillParent::both(
C::label(label)
.font(&S::FONT)
.text_color(S::Idle::LABEL_COLOR)
.on_state_changed(S::apply_label),
)
.align_horizontal(Center)
.align_vertical(Center),
)
}
Loading