Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve theme definition #58

Draft
wants to merge 27 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
b9625a6
Experimental button style helper macro
bugadani Aug 18, 2021
c09e7e0
Move stuff, add secondary button
bugadani Aug 19, 2021
81474e7
Stipped-down button constructors
bugadani Aug 19, 2021
7247780
Basic label style
bugadani Aug 19, 2021
338f697
Reorganize
bugadani Aug 20, 2021
2882792
Add back style to button
bugadani Aug 20, 2021
cce03a9
Specify button's label color
bugadani Aug 20, 2021
100975f
Clean up todos
bugadani Aug 20, 2021
aa40f1f
Allow other types to be used in labels
bugadani Aug 20, 2021
ec0c411
Label: use background color
bugadani Aug 20, 2021
349280d
Add stretched buttons
bugadani Aug 23, 2021
d1b7b53
Extract common parts of the styled button
bugadani Aug 23, 2021
17093ff
Styled toggle button
bugadani Aug 23, 2021
f05a1af
Checkbox and radio button
bugadani Aug 24, 2021
f777834
Button: Allow using other fonts
bugadani Aug 27, 2021
35b2913
Add scrollbar and slider impl, add note
bugadani Aug 27, 2021
729bf5f
Unify macros a bit, allow reusing states
bugadani Aug 28, 2021
9c4e6ba
Allow str-like objects as labels
bugadani Aug 28, 2021
603a0cd
Add Title
bugadani Aug 28, 2021
58fc243
TextBlock
bugadani Aug 28, 2021
8920192
TextBox
bugadani Aug 28, 2021
e4c9d12
Use new theme, remove old theme
bugadani Aug 28, 2021
5172342
Fix toggle button states
bugadani Aug 28, 2021
a2ca565
Move graphical widget rendering to theme specific modules
bugadani Sep 19, 2021
70d7921
Allow defining multiple themes, fix struct name in impl
bugadani Feb 25, 2022
771049e
Add background color to theme
bugadani Feb 25, 2022
5b54b9b
Import theme with alias
bugadani Feb 25, 2022
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,
}
}
}
);
269 changes: 269 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,269 @@
//! 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 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