diff --git a/backend-embedded-graphics/Cargo.toml b/backend-embedded-graphics/Cargo.toml index 46d61da..13a2810 100644 --- a/backend-embedded-graphics/Cargo.toml +++ b/backend-embedded-graphics/Cargo.toml @@ -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"] diff --git a/backend-embedded-graphics/src/lib.rs b/backend-embedded-graphics/src/lib.rs index 1ed0633..53a32a4 100644 --- a/backend-embedded-graphics/src/lib.rs +++ b/backend-embedded-graphics/src/lib.rs @@ -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. //! diff --git a/backend-embedded-graphics/src/themes/basic/button/light.rs b/backend-embedded-graphics/src/themes/basic/button/light.rs new file mode 100644 index 0000000..08f0622 --- /dev/null +++ b/backend-embedded-graphics/src/themes/basic/button/light.rs @@ -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, + } + } + } +); diff --git a/backend-embedded-graphics/src/themes/basic/button/mod.rs b/backend-embedded-graphics/src/themes/basic/button/mod.rs new file mode 100644 index 0000000..42edc62 --- /dev/null +++ b/backend-embedded-graphics/src/themes/basic/button/mod.rs @@ -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 $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 { + const LABEL_COLOR: C; + const BORDER_COLOR: C; + const BACKGROUND_COLOR: C; + + fn apply_label(label: &mut Label) + where + Label: LabelStyling, + { + label.set_text_color(Self::LABEL_COLOR); + } + + fn apply_background(background: &mut Background) + where + T: BackgroundProperties, + W: Widget, + { + background.set_background_color(Self::BACKGROUND_COLOR); + } + + fn apply_border(border: &mut Border) + where + T: BorderProperties, + W: Widget, + { + border.set_border_color(Self::BORDER_COLOR); + } +} + +pub trait ButtonStyle { + type Inactive: ButtonStateColors; + type Idle: ButtonStateColors; + type Hovered: ButtonStateColors; + type Pressed: ButtonStateColors; + + const FONT: MonoFont<'static>; + + fn apply_label(label: &mut Label, state: WidgetState) + where + Label: LabelStyling, + { + 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(border: &mut Border, state: WidgetState) + where + T: BorderProperties, + 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(background: &mut Background, state: WidgetState) + where + T: BackgroundProperties, + 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 = + Button>, BorderStyle>>; + +fn button(inner: W) -> StyledButtonDecorator +where + C: BasicTheme, + S: ButtonStyle, + 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 = + StyledButtonDecorator>>>>; + +pub fn styled_button(label: ST) -> StyledButton +where + ST: AsRef, + C: BasicTheme, + S: ButtonStyle, +{ + button::( + 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 = StyledButtonDecorator< + C, + FillParent< + Label>>, + HorizontalAndVertical, + Center, + Center, + >, +>; + +pub fn styled_button_stretched(label: ST) -> StyledButtonStretched +where + ST: AsRef, + C: BasicTheme, + S: ButtonStyle, +{ + button::( + 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), + ) +} diff --git a/backend-embedded-graphics/src/themes/basic/check_box/light.rs b/backend-embedded-graphics/src/themes/basic/check_box/light.rs new file mode 100644 index 0000000..28d2b2b --- /dev/null +++ b/backend-embedded-graphics/src/themes/basic/check_box/light.rs @@ -0,0 +1,49 @@ +//! Light theme for toggle buttons. + +use crate::{check_box_style_binary_color, check_box_style_rgb}; + +check_box_style_binary_color!( + CheckBox { + font: ascii::FONT_6X10, + states: { + Inactive, Idle, Hovered, Pressed: { + label: On, + border: On, + background: Off, + check_mark: On, + } + } + } +); + +check_box_style_rgb!( + CheckBox { + font: ascii::FONT_6X10, + states: { + Inactive: { + label: CSS_GRAY, + border: CSS_GRAY, + background: CSS_DARK_GRAY, + check_mark: CSS_STEEL_BLUE, + }, + Idle: { + label: BLACK, + border: BLACK, + background: WHITE, + check_mark: CSS_DODGER_BLUE, + }, + Hovered: { + label: BLACK, + border: BLACK, + background: CSS_LIGHT_GRAY, + check_mark: CSS_DODGER_BLUE, + }, + Pressed: { + label: BLACK, + border: BLACK, + background: CSS_DARK_GRAY, + check_mark: CSS_DODGER_BLUE, + } + } + } +); diff --git a/backend-embedded-graphics/src/themes/basic/check_box/mod.rs b/backend-embedded-graphics/src/themes/basic/check_box/mod.rs new file mode 100644 index 0000000..6842a61 --- /dev/null +++ b/backend-embedded-graphics/src/themes/basic/check_box/mod.rs @@ -0,0 +1,225 @@ +//! Helper macros and types to build BaseTheme check boxes + +// Themes supported +pub mod light; + +use crate::{ + themes::basic::BasicTheme, + widgets::{ + graphical::checkbox::CheckBoxStyle, + label::{LabelStyle, LabelStyling, MonoFontLabelStyling}, + }, +}; +use embedded_graphics::{ + mono_font::{MonoFont, MonoTextStyle}, + prelude::PixelColor, +}; +use embedded_gui::{ + state::WidgetState, + widgets::{ + button::Button, + graphical::checkbox::{CheckBox, CheckBoxProperties}, + label::Label, + layouts::linear::{Cell, LinearLayout, Row, WithSpacing}, + toggle::Toggle, + Widget, + }, +}; +use object_chain::{Chain, Link}; + +/// BaseTheme specific binary color check box style helper +#[macro_export] +macro_rules! check_box_style { + (@state $state:ident<$color_t:ty> { + label: $label:tt, + border: $border:tt, + background: $background:tt, + check_mark: $check_mark:tt, + }) => { + pub struct $state; + + impl $crate::themes::basic::check_box::CheckBoxStateColors<$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; + const CHECK_MARK_COLOR: $color_t = <$color_t>::$check_mark; + } + }; + + (@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::check_box::CheckBoxVisualStyle<$color_t> for $style { + paste::paste! { + $($(type $state = [<$style $state>];)+)+ + } + + const FONT: MonoFont<'static> = mono_font::$font_mod::$font; + } + + $( + $( + paste::paste! { + $crate::check_box_style!(@state [<$style $state>]<$color_t> $state_desc); + } + )+ + )+ + )+ + }; +} + +/// BaseTheme specific binary color toggle button style helper +#[macro_export] +macro_rules! check_box_style_binary_color { + ($($style:ident $descriptor:tt),+) => { + #[allow(unused)] + pub mod binary_color { + use embedded_graphics::{ + mono_font::{self, MonoFont}, + pixelcolor::BinaryColor, + }; + + $( + $crate::check_box_style!(@impl $style $descriptor); + )+ + } + }; +} + +/// BaseTheme specific RGB color toggle button style helper +#[macro_export] +macro_rules! check_box_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::check_box_style!(@impl $style<$color_t> $descriptor); + )+ + } + }; + + ($($style:ident $descriptor:tt),+) => { + $crate::check_box_style_rgb!(@color rgb555, Rgb555, $($style $descriptor)+); + $crate::check_box_style_rgb!(@color rgb565, Rgb565, $($style $descriptor)+); + $crate::check_box_style_rgb!(@color rgb666, Rgb666, $($style $descriptor)+); + $crate::check_box_style_rgb!(@color rgb888, Rgb888, $($style $descriptor)+); + }; +} + +pub trait CheckBoxStateColors { + const LABEL_COLOR: C; + const BORDER_COLOR: C; + const BACKGROUND_COLOR: C; + const CHECK_MARK_COLOR: C; + + fn apply_check_box>(check_box: &mut CheckBox

) { + check_box.set_background_color(Self::BACKGROUND_COLOR); + check_box.set_border_color(Self::BORDER_COLOR); + check_box.set_check_mark_color(Self::CHECK_MARK_COLOR); + } + + fn apply_label(label: &mut Label) + where + Label: LabelStyling, + { + label.set_text_color(Self::LABEL_COLOR); + } +} + +pub trait CheckBoxVisualStyle { + type Inactive: CheckBoxStateColors; + type Idle: CheckBoxStateColors; + type Hovered: CheckBoxStateColors; + type Pressed: CheckBoxStateColors; + + const FONT: MonoFont<'static>; + + fn apply_check_box>( + check_box: &mut CheckBox

, + state: WidgetState, + ) { + check_box.set_checked(state.has_state(Toggle::STATE_CHECKED)); + if state.has_state(Toggle::STATE_INACTIVE) { + Self::Inactive::apply_check_box(check_box); + } else if state.has_state(Toggle::STATE_HOVERED) { + Self::Hovered::apply_check_box(check_box); + } else if state.has_state(Toggle::STATE_PRESSED) { + Self::Pressed::apply_check_box(check_box); + } else { + Self::Idle::apply_check_box(check_box); + }; + } + + fn apply_label(label: &mut Label, state: WidgetState) + where + Label: LabelStyling, + { + 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); + }; + } +} + +pub type StyledCheckBoxDecorator = Toggle< + LinearLayout, Chain>>>>, Row>, + (), + true, +>; + +fn check_box(inner: W) -> StyledCheckBoxDecorator +where + C: BasicTheme, + S: CheckBoxVisualStyle, + W: Widget, +{ + Toggle::new( + Row::new() + .spacing(1) + .add( + CheckBox::with_style(CheckBoxStyle { + background_color: S::Idle::BACKGROUND_COLOR, + border_color: S::Idle::BORDER_COLOR, + checkmark_color: S::Idle::CHECK_MARK_COLOR, + line_width: 1, + box_size: 9, + is_checked: false, + }) + .on_state_changed(S::apply_check_box), + ) + .add(inner), + ) +} + +// Type alias to decouple toggle button definition from theme +pub type StyledCheckBox = + StyledCheckBoxDecorator>>>; + +pub fn styled_check_box(label: ST) -> StyledCheckBox +where + ST: AsRef, + C: BasicTheme, + S: CheckBoxVisualStyle, +{ + check_box::( + C::label(label) + .font(&S::FONT) + .text_color(S::Idle::LABEL_COLOR) + .on_state_changed(S::apply_label), + ) +} diff --git a/backend-embedded-graphics/src/themes/basic/label/light.rs b/backend-embedded-graphics/src/themes/basic/label/light.rs new file mode 100644 index 0000000..ee26331 --- /dev/null +++ b/backend-embedded-graphics/src/themes/basic/label/light.rs @@ -0,0 +1,29 @@ +//! Light theme for labels. + +use crate::{label_style_binary_color, label_style_rgb}; + +label_style_binary_color!( + Label { + text: On, + background: None, + font: ascii::FONT_6X10, + }, + Title { + text: On, + background: None, + font: ascii::FONT_9X15, + } +); + +label_style_rgb!( + Label { + text: BLACK, + background: None, + font: ascii::FONT_6X10, + }, + Title { + text: BLACK, + background: None, + font: ascii::FONT_9X15, + } +); diff --git a/backend-embedded-graphics/src/themes/basic/label/mod.rs b/backend-embedded-graphics/src/themes/basic/label/mod.rs new file mode 100644 index 0000000..aa6e728 --- /dev/null +++ b/backend-embedded-graphics/src/themes/basic/label/mod.rs @@ -0,0 +1,111 @@ +//! Helper macros and types to build BaseTheme labels + +use embedded_graphics::{ + mono_font::{MonoFont, MonoTextStyle, MonoTextStyleBuilder}, + prelude::PixelColor, +}; +use embedded_gui::{geometry::BoundingBox, widgets::label::Label}; + +use crate::{themes::basic::BasicTheme, widgets::label::LabelStyle as LabelStyleStruct}; + +// Themes supported +pub mod light; + +/// BaseTheme specific binary color label style helper +#[macro_export] +macro_rules! label_style { + (@option $color_t:ty, None) => { + None + }; + (@option $color_t:ty, $color:tt) => { + Some(<$color_t>::$color) + }; + + (@impl $style:ident<$color_t:ty> { + text: $text:tt, + background: $background:tt, + font: $font_mod:tt::$font:tt, + }) => { + pub struct $style; + impl $crate::themes::basic::label::LabelStyle<$color_t> for $style { + const TEXT_COLOR: Option<$color_t> = $crate::label_style!(@option $color_t, $text); + const BACKGROUND_COLOR: Option<$color_t> = + $crate::label_style!(@option $color_t, $background); + const FONT: MonoFont<'static> = mono_font::$font_mod::$font; + } + }; +} + +/// BaseTheme specific BinaryColor color label style helper +#[macro_export] +macro_rules! label_style_binary_color { + ($($style:ident $descriptor:tt),+) => { + #[allow(unused)] + pub mod binary_color { + use embedded_graphics::{ + mono_font::{self, MonoFont}, + pixelcolor::BinaryColor, + }; + $( + $crate::label_style!(@impl $style $descriptor); + )+ + } + }; +} + +/// BaseTheme specific RGB color label style helper +#[macro_export] +macro_rules! label_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::label_style!(@impl $style<$color_t> $descriptor); + )+ + } + }; + + ($($style:ident $descriptor:tt),+) => { + $crate::label_style_rgb!(@color rgb555 Rgb555 $($style $descriptor),+); + $crate::label_style_rgb!(@color rgb565 Rgb565 $($style $descriptor),+); + $crate::label_style_rgb!(@color rgb666 Rgb666 $($style $descriptor),+); + $crate::label_style_rgb!(@color rgb888 Rgb888 $($style $descriptor),+); + }; +} + +pub trait LabelStyle { + const TEXT_COLOR: Option; + const BACKGROUND_COLOR: Option; + + const FONT: MonoFont<'static>; + + fn new>(text: S) -> Label>> { + let mut renderer = MonoTextStyleBuilder::new().font(&Self::FONT).build(); + renderer.text_color = Self::TEXT_COLOR; + renderer.background_color = Self::BACKGROUND_COLOR; + + Label { + parent_index: 0, + text, + label_properties: LabelStyleStruct::new(renderer), + bounds: BoundingBox::default(), + on_state_changed: |_, _| (), + } + } +} + +pub type StyledLabel = Label>>; + +pub fn styled_label(label: ST) -> StyledLabel +where + ST: AsRef, + C: BasicTheme, + S: LabelStyle, +{ + S::new(label) +} diff --git a/backend-embedded-graphics/src/themes/basic/mod.rs b/backend-embedded-graphics/src/themes/basic/mod.rs new file mode 100644 index 0000000..6b8aea9 --- /dev/null +++ b/backend-embedded-graphics/src/themes/basic/mod.rs @@ -0,0 +1,175 @@ +//! Basic theme implementation. + +pub mod button; +pub mod check_box; +pub mod label; +pub mod radio_button; +pub mod scrollbar; +pub mod slider; +pub mod text_block; +pub mod text_box; +pub mod toggle_button; + +use core::{borrow::BorrowMut, ops::RangeInclusive}; + +use crate::themes::basic::{ + button::{ + styled_button, styled_button_stretched, ButtonStyle, StyledButton, StyledButtonStretched, + }, + check_box::{styled_check_box, CheckBoxVisualStyle, StyledCheckBox}, + label::{styled_label, LabelStyle, StyledLabel}, + radio_button::{styled_radio_button, RadioButtonVisualStyle, StyledRadioButton}, + scrollbar::{ + horizontal_scrollbar, vertical_scrollbar, ScrollbarVisualStyle, StyledHorizontalScrollbar, + StyledVerticalScrollbar, + }, + slider::{slider, SliderVisualStyle, StyledSlider}, + text_block::{styled_text_block, StyledTextBlock, TextBlockStyle}, + text_box::{styled_text_box, StyledTextBox, TextBoxStyle}, + toggle_button::{ + styled_toggle_button, styled_toggle_button_stretched, StyledToggleButton, + StyledToggleButtonStretched, ToggleButtonStyle, + }, +}; +use embedded_graphics::prelude::PixelColor; +use heapless::String; + +pub trait BasicTheme: Sized { + type PixelColor: PixelColor; + + type LabelStyle: LabelStyle; + type TitleStyle: LabelStyle; + type TextBlockStyle: TextBlockStyle; + type TextBoxStyle: TextBoxStyle; + type PrimaryButton: ButtonStyle; + type SecondaryButton: ButtonStyle; + type ToggleButton: ToggleButtonStyle; + type CheckBox: CheckBoxVisualStyle; + type RadioButton: RadioButtonVisualStyle; + type Slider: SliderVisualStyle; + type VerticalScrollbar: ScrollbarVisualStyle; + type HorizontalScrollbar: ScrollbarVisualStyle; + + fn title>(label: S) -> StyledLabel { + styled_label::<_, Self, Self::TitleStyle>(label) + } + + fn label>(label: S) -> StyledLabel { + styled_label::<_, Self, Self::LabelStyle>(label) + } + + fn text_block>(label: S) -> StyledTextBlock { + styled_text_block::<_, Self, Self::TextBlockStyle>(label) + } + + fn text_box>, const N: usize>( + label: S, + ) -> StyledTextBox { + styled_text_box::<_, Self, Self::TextBoxStyle, N>(label) + } + + fn primary_button>(label: S) -> StyledButton { + styled_button::<_, Self, Self::PrimaryButton>(label) + } + + fn secondary_button>(label: S) -> StyledButton { + styled_button::<_, Self, Self::SecondaryButton>(label) + } + + fn primary_button_stretched>( + label: S, + ) -> StyledButtonStretched { + styled_button_stretched::<_, Self, Self::PrimaryButton>(label) + } + + fn secondary_button_stretched>( + label: S, + ) -> StyledButtonStretched { + styled_button_stretched::<_, Self, Self::SecondaryButton>(label) + } + + fn toggle_button>(label: S) -> StyledToggleButton { + styled_toggle_button::<_, Self, Self::ToggleButton>(label) + } + + fn toggle_button_stretched>( + label: S, + ) -> StyledToggleButtonStretched { + styled_toggle_button_stretched::<_, Self, Self::ToggleButton>(label) + } + + fn check_box>(label: S) -> StyledCheckBox { + styled_check_box::<_, Self, Self::CheckBox>(label) + } + + fn radio_button>(label: S) -> StyledRadioButton { + styled_radio_button::<_, Self, Self::RadioButton>(label) + } + + fn horizontal_scrollbar( + ) -> StyledHorizontalScrollbar { + horizontal_scrollbar::() + } + + fn vertical_scrollbar() -> StyledVerticalScrollbar { + vertical_scrollbar::() + } + + fn slider(range: RangeInclusive) -> StyledSlider { + slider::(range) + } +} + +/// This macro is used to define the theme structure. +macro_rules! impl_theme { + ($theme_module:ident, $theme:ident, $color_mod:ident, $color_t:ident) => { + pub mod $color_mod { + use embedded_graphics::pixelcolor::$color_t; + + use $crate::themes::basic::{ + button::{ + $theme_module::$color_mod::PrimaryButton, + $theme_module::$color_mod::SecondaryButton, + }, + check_box::$theme_module::$color_mod::CheckBox, + label::$theme_module::$color_mod::{Label, Title}, + radio_button::$theme_module::$color_mod::RadioButton, + scrollbar::$theme_module::$color_mod::{HorizontalScrollbar, VerticalScrollbar}, + slider::$theme_module::$color_mod::Slider, + text_block::$theme_module::$color_mod::TextBlock, + text_box::$theme_module::$color_mod::TextBox, + toggle_button::$theme_module::$color_mod::ToggleButton, + BasicTheme, + }; + + pub struct $theme; + impl BasicTheme for LightTheme { + type PixelColor = $color_t; + + type LabelStyle = Label; + type TitleStyle = Title; + type TextBlockStyle = TextBlock; + type TextBoxStyle = TextBox; + type PrimaryButton = PrimaryButton; + type SecondaryButton = SecondaryButton; + type ToggleButton = ToggleButton; + type CheckBox = CheckBox; + type RadioButton = RadioButton; + type Slider = Slider; + type VerticalScrollbar = VerticalScrollbar; + type HorizontalScrollbar = HorizontalScrollbar; + } + } + }; + + ($theme_module:ident, $theme:ident) => { + impl_theme!($theme_module, $theme, binary_color, BinaryColor); + impl_theme!($theme_module, $theme, rgb555, Rgb555); + impl_theme!($theme_module, $theme, rgb565, Rgb565); + impl_theme!($theme_module, $theme, rgb666, Rgb666); + impl_theme!($theme_module, $theme, rgb888, Rgb888); + }; +} + +// Theme definitions +impl_theme!(light, LightTheme); diff --git a/backend-embedded-graphics/src/themes/basic/radio_button/light.rs b/backend-embedded-graphics/src/themes/basic/radio_button/light.rs new file mode 100644 index 0000000..cd546fe --- /dev/null +++ b/backend-embedded-graphics/src/themes/basic/radio_button/light.rs @@ -0,0 +1,49 @@ +//! Light theme for toggle buttons. + +use crate::{radio_button_style_binary_color, radio_button_style_rgb}; + +radio_button_style_binary_color!( + RadioButton { + font: ascii::FONT_6X10, + states: { + Inactive, Idle, Hovered, Pressed: { + label: On, + border: On, + background: Off, + check_mark: On, + } + } + } +); + +radio_button_style_rgb!( + RadioButton { + font: ascii::FONT_6X10, + states: { + Inactive: { + label: CSS_GRAY, + border: CSS_GRAY, + background: CSS_DARK_GRAY, + check_mark: CSS_STEEL_BLUE, + }, + Idle: { + label: BLACK, + border: BLACK, + background: WHITE, + check_mark: CSS_DODGER_BLUE, + }, + Hovered: { + label: BLACK, + border: BLACK, + background: CSS_LIGHT_GRAY, + check_mark: CSS_DODGER_BLUE, + }, + Pressed: { + label: BLACK, + border: BLACK, + background: CSS_DARK_GRAY, + check_mark: CSS_DODGER_BLUE, + } + } + } +); diff --git a/backend-embedded-graphics/src/themes/basic/radio_button/mod.rs b/backend-embedded-graphics/src/themes/basic/radio_button/mod.rs new file mode 100644 index 0000000..404c8c8 --- /dev/null +++ b/backend-embedded-graphics/src/themes/basic/radio_button/mod.rs @@ -0,0 +1,225 @@ +//! Helper macros and types to build BaseTheme radio buttons + +// Themes supported +pub mod light; + +use crate::{ + themes::basic::BasicTheme, + widgets::{ + graphical::radio::RadioButtonStyle, + label::{LabelStyle, LabelStyling, MonoFontLabelStyling}, + }, +}; +use embedded_graphics::{ + mono_font::{MonoFont, MonoTextStyle}, + prelude::PixelColor, +}; +use embedded_gui::{ + state::WidgetState, + widgets::{ + button::Button, + graphical::radio::{RadioButton, RadioButtonProperties}, + label::Label, + layouts::linear::{Cell, LinearLayout, Row, WithSpacing}, + toggle::Toggle, + Widget, + }, +}; +use object_chain::{Chain, Link}; + +/// BaseTheme specific binary color radio button style helper +#[macro_export] +macro_rules! radio_button_style { + (@state $state:ident<$color_t:ty> { + label: $label:tt, + border: $border:tt, + background: $background:tt, + check_mark: $check_mark:tt, + }) => { + pub struct $state; + + impl $crate::themes::basic::radio_button::RadioButtonStateColors<$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; + const CHECK_MARK_COLOR: $color_t = <$color_t>::$check_mark; + } + }; + + (@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::radio_button::RadioButtonVisualStyle<$color_t> for $style { + paste::paste! { + $($(type $state = [<$style $state>];)+)+ + } + + const FONT: MonoFont<'static> = mono_font::$font_mod::$font; + } + + $( + $( + paste::paste! { + $crate::radio_button_style!(@state [<$style $state>]<$color_t> $state_desc); + } + )+ + )+ + )+ + }; +} + +/// BaseTheme specific binary color toggle button style helper +#[macro_export] +macro_rules! radio_button_style_binary_color { + ($($style:ident $descriptor:tt),+) => { + #[allow(unused)] + pub mod binary_color { + use embedded_graphics::{ + mono_font::{self, MonoFont}, + pixelcolor::BinaryColor, + }; + + $( + $crate::radio_button_style!(@impl $style $descriptor); + )+ + } + }; +} + +/// BaseTheme specific RGB color toggle button style helper +#[macro_export] +macro_rules! radio_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::radio_button_style!(@impl $style<$color_t> $descriptor); + )+ + } + }; + + ($($style:ident $descriptor:tt),+) => { + $crate::radio_button_style_rgb!(@color rgb555, Rgb555, $($style $descriptor)+); + $crate::radio_button_style_rgb!(@color rgb565, Rgb565, $($style $descriptor)+); + $crate::radio_button_style_rgb!(@color rgb666, Rgb666, $($style $descriptor)+); + $crate::radio_button_style_rgb!(@color rgb888, Rgb888, $($style $descriptor)+); + }; +} + +pub trait RadioButtonStateColors { + const LABEL_COLOR: C; + const BORDER_COLOR: C; + const BACKGROUND_COLOR: C; + const CHECK_MARK_COLOR: C; + + fn apply_radio_button>(radio_button: &mut RadioButton

) { + radio_button.set_background_color(Self::BACKGROUND_COLOR); + radio_button.set_border_color(Self::BORDER_COLOR); + radio_button.set_check_mark_color(Self::CHECK_MARK_COLOR); + } + + fn apply_label(label: &mut Label) + where + Label: LabelStyling, + { + label.set_text_color(Self::LABEL_COLOR); + } +} + +pub trait RadioButtonVisualStyle { + type Inactive: RadioButtonStateColors; + type Idle: RadioButtonStateColors; + type Hovered: RadioButtonStateColors; + type Pressed: RadioButtonStateColors; + + const FONT: MonoFont<'static>; + + fn apply_radio_button>( + radio_button: &mut RadioButton

, + state: WidgetState, + ) { + radio_button.set_selected(state.has_state(Toggle::STATE_CHECKED)); + if state.has_state(Toggle::STATE_INACTIVE) { + Self::Inactive::apply_radio_button(radio_button); + } else if state.has_state(Toggle::STATE_HOVERED) { + Self::Hovered::apply_radio_button(radio_button); + } else if state.has_state(Toggle::STATE_PRESSED) { + Self::Pressed::apply_radio_button(radio_button); + } else { + Self::Idle::apply_radio_button(radio_button); + }; + } + + fn apply_label(label: &mut Label, state: WidgetState) + where + Label: LabelStyling, + { + 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); + }; + } +} + +pub type StyledRadioButtonDecorator = Toggle< + LinearLayout, Chain>>>>, Row>, + (), + true, +>; + +fn radio_button(inner: W) -> StyledRadioButtonDecorator +where + C: BasicTheme, + S: RadioButtonVisualStyle, + W: Widget, +{ + Toggle::new( + Row::new() + .spacing(1) + .add( + RadioButton::with_style(RadioButtonStyle { + background_color: S::Idle::BACKGROUND_COLOR, + border_color: S::Idle::BORDER_COLOR, + checkmark_color: S::Idle::CHECK_MARK_COLOR, + line_width: 1, + box_size: 9, + is_selected: false, + }) + .on_state_changed(S::apply_radio_button), + ) + .add(inner), + ) +} + +// Type alias to decouple toggle button definition from theme +pub type StyledRadioButton = + StyledRadioButtonDecorator>>>; + +pub fn styled_radio_button(label: ST) -> StyledRadioButton +where + ST: AsRef, + C: BasicTheme, + S: RadioButtonVisualStyle, +{ + radio_button::( + C::label(label) + .font(&S::FONT) + .text_color(S::Idle::LABEL_COLOR) + .on_state_changed(S::apply_label), + ) +} diff --git a/backend-embedded-graphics/src/themes/basic/scrollbar/light.rs b/backend-embedded-graphics/src/themes/basic/scrollbar/light.rs new file mode 100644 index 0000000..c432819 --- /dev/null +++ b/backend-embedded-graphics/src/themes/basic/scrollbar/light.rs @@ -0,0 +1,60 @@ +//! Light theme for scrollbars. + +use crate::{scrollbar_style_binary_color, scrollbar_style_rgb}; + +// Note: macro defines Horizontal and Vertical variants with the same prefix to the name. +scrollbar_style_binary_color!( + Scrollbar { + thickness: 6, + states: { + Inactive, Idle: { + background_fill: None, + background_border: None, + background_border_thickness: 0, + fill: Off, + border: On, + border_thickness: 1, + }, + Hovered, Dragged: { + background_fill: None, + background_border: None, + background_border_thickness: 0, + fill: On, + border: Off, + border_thickness: 1, + } + } + } +); + +scrollbar_style_rgb!( + Scrollbar { + thickness: 6, + states: { + Inactive, Idle: { + background_fill: None, + background_border: None, + background_border_thickness: 0, + fill: CSS_SLATE_GRAY, + border: None, + border_thickness: 0, + }, + Hovered: { + background_fill: None, + background_border: None, + background_border_thickness: 0, + fill: CSS_LIGHT_SLATE_GRAY, + border: None, + border_thickness: 0, + }, + Dragged: { + background_fill: None, + background_border: None, + background_border_thickness: 0, + fill: CSS_STEEL_BLUE, + border: None, + border_thickness: 0, + } + } + } +); diff --git a/backend-embedded-graphics/src/themes/basic/scrollbar/mod.rs b/backend-embedded-graphics/src/themes/basic/scrollbar/mod.rs new file mode 100644 index 0000000..1eaf5ab --- /dev/null +++ b/backend-embedded-graphics/src/themes/basic/scrollbar/mod.rs @@ -0,0 +1,287 @@ +use core::marker::PhantomData; + +use crate::{themes::basic::BasicTheme, EgCanvas, ToRectangle}; +use embedded_graphics::{ + draw_target::DrawTarget, + prelude::{PixelColor, Primitive}, + primitives::PrimitiveStyle, + Drawable, +}; +use embedded_gui::{ + data::WidgetData, + widgets::slider::{Slider, SliderDirection, SliderFields, SliderProperties}, + WidgetRenderer, +}; + +// Supported themes +pub mod light; + +/// Implementation details +// TODO: this should be merged with other widgets +#[macro_export] +macro_rules! scrollbar_style { + (@option $color_t:ty, None) => { + None + }; + (@option $color_t:ty, $color:tt) => { + Some(<$color_t>::$color) + }; + + (@state $state:ident<$color_t:ty> { + background_fill: $background_fill:tt, + background_border: $background_border:tt, + background_border_thickness: $background_border_thickness:tt, + fill: $fill:tt, + border: $border:tt, + border_thickness: $border_thickness:tt, + }) => { + pub struct $state; + + impl $crate::themes::basic::scrollbar::ScrollbarVisualState<$color_t> for $state { + const BACKGROUND_BORDER_COLOR: Option<$color_t> = $crate::scrollbar_style!(@option $color_t, $background_border); + const BACKGROUND_FILL_COLOR: Option<$color_t> = $crate::scrollbar_style!(@option $color_t, $background_fill); + const BACKGROUND_BORDER_THICKNESS: u32 = $background_border_thickness; + const BORDER_COLOR: Option<$color_t> = $crate::scrollbar_style!(@option $color_t, $border); + const FILL_COLOR: Option<$color_t> = $crate::scrollbar_style!(@option $color_t, $fill); + const BORDER_THICKNESS: u32 = $border_thickness; + } + }; + + (@impl_dir $($style:ident<$color_t:ty> { + direction: $direction:tt, + thickness: $thickness:tt, + states: { + $($($state:ident),+: $state_desc:tt),+ + } + }),+) => { + $( + #[derive(Default)] + pub struct $style; + impl $crate::themes::basic::scrollbar::ScrollbarVisualStyle<$color_t> for $style { + type Direction = $direction; + + const THICKNESS: u32 = $thickness; + + paste::paste! { + $($(type $state = [<$style $state>];)+)+ + } + } + paste::paste! { + $( + $( + $crate::scrollbar_style!(@state [<$style $state>]<$color_t> $state_desc); + )+ + )+ + } + )+ + }; + + (@impl $($style:ident<$color_t:ty> { + thickness: $thickness:tt, + states: $state_desc:tt + }),+) => { + $( + paste::paste! { + $crate::scrollbar_style!(@impl_dir []<$color_t> { + direction: Horizontal, + thickness: $thickness, + states: $state_desc + }); + $crate::scrollbar_style!(@impl_dir []<$color_t> { + direction: Vertical, + thickness: $thickness, + states: $state_desc + }); + } + )+ + }; +} + +/// BaseTheme specific binary color scrollbar style helper +#[macro_export] +macro_rules! scrollbar_style_binary_color { + ($($style:ident $descriptor:tt),+) => { + #[allow(unused)] + pub mod binary_color { + use embedded_graphics::{ + mono_font::{self, MonoFont}, + pixelcolor::BinaryColor, + }; + use embedded_gui::widgets::slider::{Horizontal, Vertical}; + + $( + $crate::scrollbar_style!(@impl $style $descriptor); + )+ + } + }; +} + +/// BaseTheme specific RGB color scrollbar style helper +#[macro_export] +macro_rules! scrollbar_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}, + }; + use embedded_gui::widgets::slider::{Horizontal, Vertical}; + + $( + $crate::scrollbar_style!(@impl $style<$color_t> $descriptor); + )+ + } + }; + + ($($style:ident $descriptor:tt),+) => { + $crate::scrollbar_style_rgb!(@color rgb555, Rgb555, $($style $descriptor)+); + $crate::scrollbar_style_rgb!(@color rgb565, Rgb565, $($style $descriptor)+); + $crate::scrollbar_style_rgb!(@color rgb666, Rgb666, $($style $descriptor)+); + $crate::scrollbar_style_rgb!(@color rgb888, Rgb888, $($style $descriptor)+); + }; +} + +pub trait ScrollbarVisualState +where + C: PixelColor, +{ + const BACKGROUND_FILL_COLOR: Option; + const BACKGROUND_BORDER_COLOR: Option; + const BACKGROUND_BORDER_THICKNESS: u32 = 0; + const BORDER_COLOR: Option; + const FILL_COLOR: Option; + const BORDER_THICKNESS: u32 = 0; + + fn styles() -> (PrimitiveStyle, PrimitiveStyle) { + let mut style = PrimitiveStyle::default(); + let mut bg_style = PrimitiveStyle::default(); + + style.fill_color = Self::FILL_COLOR; + style.stroke_width = Self::BORDER_THICKNESS; + style.stroke_color = Self::BORDER_COLOR; + + bg_style.fill_color = Self::BACKGROUND_FILL_COLOR; + bg_style.stroke_width = Self::BACKGROUND_BORDER_THICKNESS; + bg_style.stroke_color = Self::BACKGROUND_BORDER_COLOR; + + (style, bg_style) + } +} + +pub trait ScrollbarVisualStyle: Default +where + C: PixelColor, +{ + type Direction: SliderDirection; + + const THICKNESS: u32; + + type Idle: ScrollbarVisualState; + type Hovered: ScrollbarVisualState; + type Dragged: ScrollbarVisualState; + type Inactive: ScrollbarVisualState; + + fn draw, D>( + &self, + canvas: &mut crate::EgCanvas

, + slider: &SliderFields, D>, + ) -> Result<(), DT::Error> { + let (slider_style, bg_style) = if slider.state.has_state(Slider::STATE_INACTIVE) { + Self::Inactive::styles() + } else if slider.state.has_state(Slider::STATE_DRAGGED) { + Self::Dragged::styles() + } else if slider.state.has_state(Slider::STATE_HOVERED) { + Self::Hovered::styles() + } else { + Self::Idle::styles() + }; + + // Background + slider + .bounds + .to_rectangle() + .into_styled(bg_style) + .draw(&mut canvas.target)?; + + // Foreground + slider + .slider_bounds() + .to_rectangle() + .into_styled(slider_style) + .draw(&mut canvas.target) + } +} + +pub struct ScrollbarProperties +where + C: PixelColor, +{ + visual: VS, + window_length: u32, + _marker: PhantomData, +} + +impl ScrollbarProperties +where + C: PixelColor, + VS: ScrollbarVisualStyle, +{ + pub fn new(visual: VS) -> Self { + Self { + visual, + window_length: 0, + _marker: PhantomData, + } + } +} + +impl SliderProperties for ScrollbarProperties +where + C: PixelColor, + VS: ScrollbarVisualStyle, +{ + type Direction = VS::Direction; + const THICKNESS: u32 = VS::THICKNESS; + + fn length(&self) -> u32 { + self.window_length + } + + fn set_length(&mut self, length: u32) { + self.window_length = length.max(3); + } +} + +impl WidgetRenderer> for Slider, D> +where + C: PixelColor, + DT: DrawTarget, + D: WidgetData, + VS: ScrollbarVisualStyle, +{ + fn draw(&mut self, canvas: &mut EgCanvas
) -> Result<(), DT::Error> { + self.fields.properties.visual.draw(canvas, &self.fields) + } +} + +pub type StyledVerticalScrollbar = Slider, ()>; + +pub fn vertical_scrollbar() -> StyledVerticalScrollbar +where + C: BasicTheme, + S: ScrollbarVisualStyle, +{ + Slider::new(0..=0, ScrollbarProperties::new(S::default())) +} + +pub type StyledHorizontalScrollbar = Slider, ()>; + +pub fn horizontal_scrollbar() -> StyledHorizontalScrollbar +where + C: BasicTheme, + S: ScrollbarVisualStyle, +{ + Slider::new(0..=0, ScrollbarProperties::new(S::default())) +} diff --git a/backend-embedded-graphics/src/themes/basic/slider/light.rs b/backend-embedded-graphics/src/themes/basic/slider/light.rs new file mode 100644 index 0000000..00844d0 --- /dev/null +++ b/backend-embedded-graphics/src/themes/basic/slider/light.rs @@ -0,0 +1,58 @@ +//! Light theme for buttons. + +use crate::{slider_style_binary_color, slider_style_rgb}; + +slider_style_binary_color!( + Slider { + direction: Horizontal, + thickness: 7, + width: 5, + states: { + Inactive, Idle: { + fill: Off, + border: On, + background: On, + border_thickness: 1, + background_thickness: 1, + }, + Hovered, Dragged: { + fill: On, + border: Off, + background: On, + border_thickness: 1, + background_thickness: 1, + } + } + } +); + +slider_style_rgb!( + Slider { + direction: Horizontal, + thickness: 7, + width: 5, + states: { + Inactive, Idle: { + fill: CSS_SLATE_GRAY, + border: None, + background: CSS_GRAY, + border_thickness: 0, + background_thickness: 1, + }, + Hovered: { + fill: CSS_LIGHT_SLATE_GRAY, + border: None, + background: CSS_GRAY, + border_thickness: 0, + background_thickness: 1, + }, + Dragged: { + fill: CSS_STEEL_BLUE, + border: None, + background: CSS_GRAY, + border_thickness: 0, + background_thickness: 1, + } + } + } +); diff --git a/backend-embedded-graphics/src/themes/basic/slider/mod.rs b/backend-embedded-graphics/src/themes/basic/slider/mod.rs new file mode 100644 index 0000000..7154734 --- /dev/null +++ b/backend-embedded-graphics/src/themes/basic/slider/mod.rs @@ -0,0 +1,265 @@ +//! Macros and helpers to draw numeric sliders. + +use core::{marker::PhantomData, ops::RangeInclusive}; + +use crate::{themes::basic::BasicTheme, EgCanvas, ToRectangle}; +use embedded_graphics::{ + draw_target::DrawTarget, + geometry::AnchorPoint, + prelude::{PixelColor, Primitive}, + primitives::{Line, PrimitiveStyle}, + Drawable, +}; +use embedded_gui::{ + data::WidgetData, + widgets::slider::{ + Slider, SliderDirection, SliderFields, SliderProperties as SliderPropertiesTrait, + }, + WidgetRenderer, +}; + +// Supported themes +pub mod light; + +/// Implementation details +// TODO: this should be merged with other widgets +#[macro_export] +macro_rules! slider_style { + (@option $color_t:ty, None) => { + None + }; + (@option $color_t:ty, $color:tt) => { + Some(<$color_t>::$color) + }; + + (@state $state:ident<$color_t:ty> { + fill: $fill:tt, + border: $border:tt, + background: $background:tt, + border_thickness: $border_thickness:tt, + background_thickness: $background_thickness:tt, + }) => { + pub struct $state; + + impl $crate::themes::basic::slider::SliderVisualState<$color_t> for $state { + const FILL_COLOR: Option<$color_t> = $crate::slider_style!(@option $color_t, $fill); + const BORDER_COLOR: Option<$color_t> = $crate::slider_style!(@option $color_t, $border); + const BACKGROUND_LINE_COLOR: Option<$color_t> = $crate::slider_style!(@option $color_t, $background); + const BACKGROUND_LINE_THICKNESS: u32 = $background_thickness; + const BORDER_THICKNESS: u32 = $border_thickness; + } + }; + + (@impl $($style:ident<$color_t:ty> { + direction: $direction:tt, + thickness: $thickness:tt, + width: $width:tt, + states: { + $($($state:ident),+: $state_desc:tt),+ + } + }),+) => { + $( + #[derive(Default)] + pub struct $style; + impl $crate::themes::basic::slider::SliderVisualStyle<$color_t> for $style { + type Direction = $direction; + + const THICKNESS: u32 = $thickness; + const WIDTH: u32 = $width; + + paste::paste! { + $($(type $state = [<$style $state>];)+)+ + } + } + + paste::paste! { + $( + $( + $crate::slider_style!(@state [<$style $state>]<$color_t> $state_desc); + )+ + )+ + } + )+ + }; +} + +/// BaseTheme specific binary color slider style helper +#[macro_export] +macro_rules! slider_style_binary_color { + ($($style:ident $descriptor:tt),+) => { + #[allow(unused)] + pub mod binary_color { + use embedded_graphics::{ + mono_font::{self, MonoFont}, + pixelcolor::BinaryColor, + }; + use embedded_gui::widgets::slider::{Horizontal, Vertical}; + + $( + $crate::slider_style!(@impl $style $descriptor); + )+ + } + }; +} + +/// BaseTheme specific RGB color slider style helper +#[macro_export] +macro_rules! slider_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}, + }; + use embedded_gui::widgets::slider::{Horizontal, Vertical}; + + $( + $crate::slider_style!(@impl $style<$color_t> $descriptor); + )+ + } + }; + + ($($style:ident $descriptor:tt),+) => { + $crate::slider_style_rgb!(@color rgb555, Rgb555, $($style $descriptor)+); + $crate::slider_style_rgb!(@color rgb565, Rgb565, $($style $descriptor)+); + $crate::slider_style_rgb!(@color rgb666, Rgb666, $($style $descriptor)+); + $crate::slider_style_rgb!(@color rgb888, Rgb888, $($style $descriptor)+); + }; +} + +pub trait SliderVisualState +where + C: PixelColor, +{ + const BACKGROUND_LINE_COLOR: Option; + const BACKGROUND_LINE_THICKNESS: u32 = 0; + const BORDER_COLOR: Option; + const FILL_COLOR: Option; + const BORDER_THICKNESS: u32 = 0; + + fn styles() -> (PrimitiveStyle, PrimitiveStyle) { + let mut style = PrimitiveStyle::default(); + let mut bg_style = PrimitiveStyle::default(); + + style.fill_color = Self::FILL_COLOR; + style.stroke_width = Self::BORDER_THICKNESS; + style.stroke_color = Self::BORDER_COLOR; + + bg_style.stroke_width = Self::BACKGROUND_LINE_THICKNESS; + bg_style.stroke_color = Self::BACKGROUND_LINE_COLOR; + + (style, bg_style) + } +} + +pub trait SliderVisualStyle: Default +where + C: PixelColor, +{ + type Direction: SliderDirection; + + const THICKNESS: u32; + const WIDTH: u32; + + type Idle: SliderVisualState; + type Hovered: SliderVisualState; + type Dragged: SliderVisualState; + type Inactive: SliderVisualState; + + fn draw, D>( + &self, + canvas: &mut crate::EgCanvas
, + slider: &SliderFields, D>, + ) -> Result<(), DT::Error> { + let (slider_style, bg_style) = if slider.state.has_state(Slider::STATE_INACTIVE) { + Self::Inactive::styles() + } else if slider.state.has_state(Slider::STATE_DRAGGED) { + Self::Dragged::styles() + } else if slider.state.has_state(Slider::STATE_HOVERED) { + Self::Hovered::styles() + } else { + Self::Idle::styles() + }; + + // Background + let bounds = slider.bounds.to_rectangle(); + + // TODO verify that this is correct + let start = bounds.anchor_point(AnchorPoint::CenterLeft); + let end = bounds.anchor_point(AnchorPoint::CenterRight); + + Line::new(start, end) + .into_styled(bg_style) + .draw(&mut canvas.target)?; + + // Foreground + slider + .slider_bounds() + .to_rectangle() + .into_styled(slider_style) + .draw(&mut canvas.target) + } +} + +pub struct SliderProperties +where + C: PixelColor, + VS: SliderVisualStyle, +{ + visual: VS, + _marker: PhantomData, +} + +impl SliderProperties +where + C: PixelColor, + VS: SliderVisualStyle, +{ + pub fn new(visual: VS) -> Self { + Self { + visual, + _marker: PhantomData, + } + } +} + +impl SliderPropertiesTrait for SliderProperties +where + C: PixelColor, + VS: SliderVisualStyle, +{ + type Direction = VS::Direction; + const THICKNESS: u32 = VS::THICKNESS; + + fn length(&self) -> u32 { + VS::WIDTH + } + + fn set_length(&mut self, _length: u32) { + unimplemented!("Numeric slider has no settable window length") + } +} + +impl WidgetRenderer> for Slider, D> +where + C: PixelColor, + DT: DrawTarget, + D: WidgetData, + VS: SliderVisualStyle, +{ + fn draw(&mut self, canvas: &mut EgCanvas
) -> Result<(), DT::Error> { + self.fields.properties.visual.draw(canvas, &self.fields) + } +} + +pub type StyledSlider = Slider, ()>; + +pub fn slider(range: RangeInclusive) -> StyledSlider +where + C: BasicTheme, + S: SliderVisualStyle, +{ + Slider::new(range, SliderProperties::new(S::default())) +} diff --git a/backend-embedded-graphics/src/themes/basic/text_block/light.rs b/backend-embedded-graphics/src/themes/basic/text_block/light.rs new file mode 100644 index 0000000..1a64cdf --- /dev/null +++ b/backend-embedded-graphics/src/themes/basic/text_block/light.rs @@ -0,0 +1,15 @@ +//! Light theme for labels. + +use crate::{text_block_style_binary_color, text_block_style_rgb}; + +text_block_style_binary_color!(TextBlock { + text: On, + background: None, + font: ascii::FONT_6X10, +}); + +text_block_style_rgb!(TextBlock { + text: BLACK, + background: None, + font: ascii::FONT_6X10, +}); diff --git a/backend-embedded-graphics/src/themes/basic/text_block/mod.rs b/backend-embedded-graphics/src/themes/basic/text_block/mod.rs new file mode 100644 index 0000000..18865ab --- /dev/null +++ b/backend-embedded-graphics/src/themes/basic/text_block/mod.rs @@ -0,0 +1,115 @@ +//! Helper macros and types to build BaseTheme text blocks + +use embedded_graphics::{ + mono_font::{MonoFont, MonoTextStyle, MonoTextStyleBuilder}, + prelude::PixelColor, +}; +use embedded_gui::{geometry::BoundingBox, widgets::text_block::TextBlock}; + +use crate::{ + themes::basic::BasicTheme, widgets::text_block::TextBlockStyle as TextBlockStyleStruct, +}; + +// Themes supported +pub mod light; + +/// BaseTheme specific binary color text block style helper +#[macro_export] +macro_rules! text_block_style { + (@option $color_t:ty, None) => { + None + }; + (@option $color_t:ty, $color:tt) => { + Some(<$color_t>::$color) + }; + + (@impl $style:ident<$color_t:ty> { + text: $text:tt, + background: $background:tt, + font: $font_mod:tt::$font:tt, + }) => { + pub struct $style; + impl $crate::themes::basic::text_block::TextBlockStyle<$color_t> for $style { + const TEXT_COLOR: Option<$color_t> = $crate::text_block_style!(@option $color_t, $text); + const BACKGROUND_COLOR: Option<$color_t> = + $crate::text_block_style!(@option $color_t, $background); + const FONT: MonoFont<'static> = mono_font::$font_mod::$font; + } + }; +} + +/// BaseTheme specific BinaryColor color label style helper +#[macro_export] +macro_rules! text_block_style_binary_color { + ($($style:ident $descriptor:tt),+) => { + #[allow(unused)] + pub mod binary_color { + use embedded_graphics::{ + mono_font::{self, MonoFont}, + pixelcolor::BinaryColor, + }; + $( + $crate::text_block_style!(@impl $style $descriptor); + )+ + } + }; +} + +/// BaseTheme specific RGB color label style helper +#[macro_export] +macro_rules! text_block_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::text_block_style!(@impl $style<$color_t> $descriptor); + )+ + } + }; + + ($($style:ident $descriptor:tt),+) => { + $crate::text_block_style_rgb!(@color rgb555 Rgb555 $($style $descriptor),+); + $crate::text_block_style_rgb!(@color rgb565 Rgb565 $($style $descriptor),+); + $crate::text_block_style_rgb!(@color rgb666 Rgb666 $($style $descriptor),+); + $crate::text_block_style_rgb!(@color rgb888 Rgb888 $($style $descriptor),+); + }; +} + +pub trait TextBlockStyle { + const TEXT_COLOR: Option; + const BACKGROUND_COLOR: Option; + + const FONT: MonoFont<'static>; + + fn new>( + text: S, + ) -> TextBlock>> { + let mut renderer = MonoTextStyleBuilder::new().font(&Self::FONT).build(); + renderer.text_color = Self::TEXT_COLOR; + renderer.background_color = Self::BACKGROUND_COLOR; + + TextBlock { + parent_index: 0, + text, + label_properties: TextBlockStyleStruct::new(renderer), + bounds: BoundingBox::default(), + on_state_changed: |_, _| (), + } + } +} + +pub type StyledTextBlock = TextBlock>>; + +pub fn styled_text_block(label: ST) -> StyledTextBlock +where + ST: AsRef, + C: BasicTheme, + S: TextBlockStyle, +{ + S::new(label) +} diff --git a/backend-embedded-graphics/src/themes/basic/text_box/light.rs b/backend-embedded-graphics/src/themes/basic/text_box/light.rs new file mode 100644 index 0000000..3a092ee --- /dev/null +++ b/backend-embedded-graphics/src/themes/basic/text_box/light.rs @@ -0,0 +1,15 @@ +//! Light theme for labels. + +use crate::{text_box_style_binary_color, text_box_style_rgb}; + +text_box_style_binary_color!(TextBox { + text: On, + background: None, + font: ascii::FONT_6X10, +}); + +text_box_style_rgb!(TextBox { + text: BLACK, + background: None, + font: ascii::FONT_6X10, +}); diff --git a/backend-embedded-graphics/src/themes/basic/text_box/mod.rs b/backend-embedded-graphics/src/themes/basic/text_box/mod.rs new file mode 100644 index 0000000..2238306 --- /dev/null +++ b/backend-embedded-graphics/src/themes/basic/text_box/mod.rs @@ -0,0 +1,137 @@ +//! Helper macros and types to build BaseTheme text boxes + +use core::borrow::BorrowMut; + +use embedded_graphics::{ + mono_font::{MonoFont, MonoTextStyle, MonoTextStyleBuilder}, + prelude::PixelColor, +}; +use embedded_gui::{ + geometry::BoundingBox, + state::WidgetState, + widgets::{ + text_box::{TextBox, TextBoxFields}, + utils::WidgetDataHolder, + }, +}; +use heapless::String; + +use crate::{themes::basic::BasicTheme, widgets::text_box::TextBoxStyle as TextBoxStyleStruct}; + +// Themes supported +pub mod light; + +/// BaseTheme specific binary color text box style helper +#[macro_export] +macro_rules! text_box_style { + (@option $color_t:ty, None) => { + None + }; + (@option $color_t:ty, $color:tt) => { + Some(<$color_t>::$color) + }; + + (@impl $style:ident<$color_t:ty> { + text: $text:tt, + background: $background:tt, + font: $font_mod:tt::$font:tt, + }) => { + pub struct $style; + impl $crate::themes::basic::text_box::TextBoxStyle<$color_t> for $style { + const TEXT_COLOR: Option<$color_t> = $crate::text_box_style!(@option $color_t, $text); + const BACKGROUND_COLOR: Option<$color_t> = + $crate::text_box_style!(@option $color_t, $background); + const FONT: MonoFont<'static> = mono_font::$font_mod::$font; + } + }; +} + +/// BaseTheme specific BinaryColor color label style helper +#[macro_export] +macro_rules! text_box_style_binary_color { + ($($style:ident $descriptor:tt),+) => { + #[allow(unused)] + pub mod binary_color { + use embedded_graphics::{ + mono_font::{self, MonoFont}, + pixelcolor::BinaryColor, + }; + $( + $crate::text_box_style!(@impl $style $descriptor); + )+ + } + }; +} + +/// BaseTheme specific RGB color label style helper +#[macro_export] +macro_rules! text_box_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::text_box_style!(@impl $style<$color_t> $descriptor); + )+ + } + }; + + ($($style:ident $descriptor:tt),+) => { + $crate::text_box_style_rgb!(@color rgb555 Rgb555 $($style $descriptor),+); + $crate::text_box_style_rgb!(@color rgb565 Rgb565 $($style $descriptor),+); + $crate::text_box_style_rgb!(@color rgb666 Rgb666 $($style $descriptor),+); + $crate::text_box_style_rgb!(@color rgb888 Rgb888 $($style $descriptor),+); + }; +} + +pub trait TextBoxStyle { + const TEXT_COLOR: Option; + const BACKGROUND_COLOR: Option; + + const FONT: MonoFont<'static>; + + fn new( + text: S, + ) -> TextBox>, (), N> + where + S: BorrowMut>, + { + let mut renderer = MonoTextStyleBuilder::new().font(&Self::FONT).build(); + renderer.text_color = Self::TEXT_COLOR; + renderer.background_color = Self::BACKGROUND_COLOR; + + let mut label_properties = TextBoxStyleStruct::new(renderer); + if let Some(text_color) = Self::TEXT_COLOR { + label_properties.cursor_color(text_color); + } + + TextBox { + fields: TextBoxFields { + state: WidgetState::default(), + parent_index: 0, + text, + label_properties, + bounds: BoundingBox::default(), + on_text_changed: |_, _| (), + on_parent_state_changed: |_, _| (), + }, + data_holder: WidgetDataHolder::default(), + } + } +} + +pub type StyledTextBox = + TextBox>, (), N>; + +pub fn styled_text_box(label: ST) -> StyledTextBox +where + ST: BorrowMut>, + C: BasicTheme, + S: TextBoxStyle, +{ + S::new(label) +} diff --git a/backend-embedded-graphics/src/themes/basic/toggle_button/light.rs b/backend-embedded-graphics/src/themes/basic/toggle_button/light.rs new file mode 100644 index 0000000..850fbb9 --- /dev/null +++ b/backend-embedded-graphics/src/themes/basic/toggle_button/light.rs @@ -0,0 +1,87 @@ +//! Light theme for toggle buttons. + +use crate::{toggle_button_style_binary_color, toggle_button_style_rgb}; + +toggle_button_style_binary_color!( + ToggleButton { + font: ascii::FONT_6X10, + states: { + Unchecked: { + Inactive, Idle, Pressed: { + label: On, + border: On, + background: Off, + }, + Hovered: { + label: Off, + border: Off, + background: On, + } + }, + Checked: { + Inactive, Idle, Pressed: { + label: Off, + border: On, + background: On, + }, + Hovered: { + label: On, + border: On, + background: Off, + } + } + } + } +); + +toggle_button_style_rgb!( + ToggleButton { + font: ascii::FONT_6X10, + states: { + Unchecked: { + 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, + } + }, + Checked: { + 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, + } + } + } + } +); diff --git a/backend-embedded-graphics/src/themes/basic/toggle_button/mod.rs b/backend-embedded-graphics/src/themes/basic/toggle_button/mod.rs new file mode 100644 index 0000000..db7fd14 --- /dev/null +++ b/backend-embedded-graphics/src/themes/basic/toggle_button/mod.rs @@ -0,0 +1,312 @@ +//! Helper macros and types to build BaseTheme toggle buttons + +// Themes supported +pub mod light; + +use crate::{ + themes::basic::{button::ButtonStateColors, 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}, + fill::{Center, FillParent, HorizontalAndVertical}, + label::Label, + spacing::Spacing, + toggle::Toggle, + Widget, + }, +}; + +/// BaseTheme specific binary color toggle button style helper +#[macro_export] +macro_rules! toggle_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; + } + }; + + (@selection_state $selection_state:ident<$color_t:ty> { + $($($state:ident),+: $state_desc:tt),+ + }) => { + + paste::paste! { + $($($crate::toggle_button_style!(@state [<$selection_state $state>]<$color_t> $state_desc);)+)+ + } + + pub struct $selection_state; + impl $crate::themes::basic::toggle_button::ToggleButtonStateStyle<$color_t> for $selection_state { + paste::paste! { + $($(type $state = [<$selection_state $state>];)+)+ + } + } + }; + + (@impl $($style:ident<$color_t:ty> { + font: $font_mod:tt::$font:tt, + states: { + $($($selection_state:ident),+: $selection_state_desc:tt),+ + } + }),+) => { + $( + pub struct $style; + impl $crate::themes::basic::toggle_button::ToggleButtonStyle<$color_t> for $style { + paste::paste! { + $($(type $selection_state = [<$style $selection_state>];)+)+ + } + + const FONT: MonoFont<'static> = mono_font::$font_mod::$font; + } + + paste::paste! { + $( + $( + $crate::toggle_button_style!(@selection_state [<$style $selection_state>]<$color_t> $selection_state_desc); + )+ + )+ + } + )+ + }; +} + +/// BaseTheme specific BinaryColor toggle button style helper +#[macro_export] +macro_rules! toggle_button_style_binary_color { + ($($style:ident $descriptor:tt)+) => { + #[allow(unused)] + pub mod binary_color { + use embedded_graphics::{ + mono_font::{self, MonoFont}, + pixelcolor::BinaryColor, + }; + $( + $crate::toggle_button_style!(@impl $style $descriptor); + )+ + } + }; +} + +/// BaseTheme specific RGB color toggle button style helper +#[macro_export] +macro_rules! toggle_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::toggle_button_style!(@impl $style<$color_t> $descriptor); + )+ + } + }; + + ($($style:ident $descriptor:tt),+) => { + $crate::toggle_button_style_rgb!(@color rgb555, Rgb555, $($style $descriptor)+); + $crate::toggle_button_style_rgb!(@color rgb565, Rgb565, $($style $descriptor)+); + $crate::toggle_button_style_rgb!(@color rgb666, Rgb666, $($style $descriptor)+); + $crate::toggle_button_style_rgb!(@color rgb888, Rgb888, $($style $descriptor)+); + }; +} + +pub trait ToggleButtonStateStyle { + type Inactive: ButtonStateColors; + type Idle: ButtonStateColors; + type Hovered: ButtonStateColors; + type Pressed: ButtonStateColors; + + fn apply_label(label: &mut Label, state: WidgetState) + where + Label: LabelStyling, + { + if state.has_state(Toggle::STATE_INACTIVE) { + Self::Inactive::apply_label(label); + } else if state.has_state(Toggle::STATE_HOVERED) { + Self::Hovered::apply_label(label); + } else if state.has_state(Toggle::STATE_PRESSED) { + Self::Pressed::apply_label(label); + } else { + Self::Idle::apply_label(label); + }; + } + + fn apply_border(border: &mut Border, state: WidgetState) + where + T: BorderProperties, + W: Widget, + { + if state.has_state(Toggle::STATE_INACTIVE) { + Self::Inactive::apply_border(border); + } else if state.has_state(Toggle::STATE_HOVERED) { + Self::Hovered::apply_border(border); + } else if state.has_state(Toggle::STATE_PRESSED) { + Self::Pressed::apply_border(border); + } else { + Self::Idle::apply_border(border); + }; + } + + fn apply_background(background: &mut Background, state: WidgetState) + where + T: BackgroundProperties, + W: Widget, + { + if state.has_state(Toggle::STATE_INACTIVE) { + Self::Inactive::apply_background(background); + } else if state.has_state(Toggle::STATE_HOVERED) { + Self::Hovered::apply_background(background); + } else if state.has_state(Toggle::STATE_PRESSED) { + Self::Pressed::apply_background(background); + } else { + Self::Idle::apply_background(background); + }; + } +} + +pub trait ToggleButtonStyle { + type Unchecked: ToggleButtonStateStyle; + type Checked: ToggleButtonStateStyle; + + const FONT: MonoFont<'static>; + + fn apply_label(label: &mut Label, state: WidgetState) + where + Label: LabelStyling, + { + if state.has_state(Toggle::STATE_CHECKED) { + Self::Checked::apply_label(label, state); + } else { + Self::Unchecked::apply_label(label, state); + }; + } + + fn apply_border(border: &mut Border, state: WidgetState) + where + T: BorderProperties, + W: Widget, + { + if state.has_state(Toggle::STATE_CHECKED) { + Self::Checked::apply_border(border, state); + } else { + Self::Unchecked::apply_border(border, state); + }; + } + + fn apply_background(background: &mut Background, state: WidgetState) + where + T: BackgroundProperties, + W: Widget, + { + if state.has_state(Toggle::STATE_CHECKED) { + Self::Checked::apply_background(background, state); + } else { + Self::Unchecked::apply_background(background, state); + }; + } +} + +pub type StyledToggleButtonDecorator = + Toggle>, BorderStyle>, (), true>; + +fn toggle_button(inner: W) -> StyledToggleButtonDecorator +where + C: BasicTheme, + S: ToggleButtonStyle, + W: Widget, +{ + Toggle::new( + Border::with_style( + Background::with_style( + inner, + BackgroundStyle::new( + <::PixelColor>>::Unchecked as ToggleButtonStateStyle< + ::PixelColor>>::Idle::BACKGROUND_COLOR), + ) + .on_state_changed(S::apply_background), + BorderStyle::new( + <::PixelColor>>::Unchecked as ToggleButtonStateStyle< + ::PixelColor>>::Idle::BORDER_COLOR, 1), + ) + .on_state_changed(S::apply_border), + ) +} + +// Type alias to decouple toggle button definition from theme +pub type StyledToggleButton = + StyledToggleButtonDecorator>>>>; + +pub fn styled_toggle_button(label: ST) -> StyledToggleButton +where + ST: AsRef, + C: BasicTheme, + S: ToggleButtonStyle, +{ + toggle_button::( + Spacing::new( + C::label(label) + .font(&S::FONT) + .text_color( + <::PixelColor>>::Unchecked as ToggleButtonStateStyle< + ::PixelColor>>::Idle::LABEL_COLOR) + .on_state_changed(S::apply_label), + ) + .all(1), + ) +} + +pub type StyledToggleButtonStretched = StyledToggleButtonDecorator< + C, + FillParent< + Label>>, + HorizontalAndVertical, + Center, + Center, + >, +>; + +pub fn styled_toggle_button_stretched( + label: ST, +) -> StyledToggleButtonStretched +where + ST: AsRef, + C: BasicTheme, + S: ToggleButtonStyle, +{ + toggle_button::( + FillParent::both( + C::label(label) + .font(&S::FONT) + .text_color( + <::PixelColor>>::Unchecked as ToggleButtonStateStyle< + ::PixelColor>>::Idle::LABEL_COLOR) + .on_state_changed(S::apply_label), + ) + .align_horizontal(Center) + .align_vertical(Center), + ) +} diff --git a/backend-embedded-graphics/src/themes/default/button/mod.rs b/backend-embedded-graphics/src/themes/default/button/mod.rs deleted file mode 100644 index 12a7dd1..0000000 --- a/backend-embedded-graphics/src/themes/default/button/mod.rs +++ /dev/null @@ -1,177 +0,0 @@ -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, - }, -}; - -use crate::{ - themes::default::DefaultTheme, - widgets::{ - background::BackgroundStyle, - border::BorderStyle, - label::{ascii::LabelConstructor, LabelStyle, LabelStyling, MonoFontLabelStyling}, - }, -}; - -pub mod primary; -pub mod secondary; - -pub trait ButtonStateColors { - const LABEL_COLOR: C; - const BORDER_COLOR: C; - const BACKGROUND_COLOR: C; - - fn apply_label(label: &mut Label) - where - Label: LabelStyling, - { - label.set_text_color(Self::LABEL_COLOR); - } - - fn apply_background(background: &mut Background) - where - T: BackgroundProperties, - { - background.set_background_color(Self::BACKGROUND_COLOR); - } - - fn apply_border(border: &mut Border) - where - T: BorderProperties, - { - border.set_border_color(Self::BORDER_COLOR); - } -} - -pub trait ButtonStyle { - type Inactive: ButtonStateColors; - type Idle: ButtonStateColors; - type Hovered: ButtonStateColors; - type Pressed: ButtonStateColors; - - const FONT: MonoFont<'static>; - - fn apply_label(label: &mut Label, state: WidgetState) - where - Label: LabelStyling, - { - 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(border: &mut Border, state: WidgetState) - where - T: BorderProperties, - { - 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(background: &mut Background, state: WidgetState) - where - T: BackgroundProperties, - { - 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 StyledButton<'a, C> = Button< - Background< - Border>>>, BorderStyle>, - BackgroundStyle, - >, ->; - -pub fn styled_button(label: &'static str) -> StyledButton -where - C: DefaultTheme, - S: ButtonStyle, - BorderStyle: Default, - BackgroundStyle: Default, -{ - Button::new( - Background::new( - Border::new( - Spacing::new( - Label::new(label) - .font(&S::FONT) - .on_state_changed(S::apply_label), - ) - .all(1), - ) - .on_state_changed(S::apply_border), - ) - .on_state_changed(S::apply_background), - ) -} - -pub type StyledButtonStretched<'a, C> = Button< - Background< - Border< - FillParent< - Label<&'static str, LabelStyle>>, - HorizontalAndVertical, - Center, - Center, - >, - BorderStyle, - >, - BackgroundStyle, - >, ->; - -pub fn styled_button_stretched(label: &'static str) -> StyledButtonStretched -where - C: DefaultTheme, - S: ButtonStyle, - BorderStyle: Default, - BackgroundStyle: Default, -{ - Button::new( - Background::new( - Border::new( - FillParent::both( - Label::new(label) - .font(&S::FONT) - .on_state_changed(S::apply_label), - ) - .align_horizontal(Center) - .align_vertical(Center), - ) - .on_state_changed(S::apply_border), - ) - .on_state_changed(S::apply_background), - ) -} diff --git a/backend-embedded-graphics/src/themes/default/button/primary/binary_color.rs b/backend-embedded-graphics/src/themes/default/button/primary/binary_color.rs deleted file mode 100644 index 955db81..0000000 --- a/backend-embedded-graphics/src/themes/default/button/primary/binary_color.rs +++ /dev/null @@ -1,45 +0,0 @@ -use embedded_graphics::{ - mono_font::{ascii::FONT_6X10, MonoFont}, - pixelcolor::BinaryColor, -}; - -use crate::themes::default::button::{ButtonStateColors, ButtonStyle}; - -pub struct PrimaryButtonInactive; -pub struct PrimaryButtonIdle; -pub struct PrimaryButtonHovered; -pub struct PrimaryButtonPressed; - -impl ButtonStateColors for PrimaryButtonInactive { - const LABEL_COLOR: BinaryColor = BinaryColor::Off; - const BORDER_COLOR: BinaryColor = BinaryColor::On; - const BACKGROUND_COLOR: BinaryColor = BinaryColor::On; -} - -impl ButtonStateColors for PrimaryButtonIdle { - const LABEL_COLOR: BinaryColor = BinaryColor::Off; - const BORDER_COLOR: BinaryColor = BinaryColor::On; - const BACKGROUND_COLOR: BinaryColor = BinaryColor::On; -} - -impl ButtonStateColors for PrimaryButtonHovered { - const LABEL_COLOR: BinaryColor = BinaryColor::On; - const BORDER_COLOR: BinaryColor = BinaryColor::On; - const BACKGROUND_COLOR: BinaryColor = BinaryColor::Off; -} - -impl ButtonStateColors for PrimaryButtonPressed { - const LABEL_COLOR: BinaryColor = BinaryColor::Off; - const BORDER_COLOR: BinaryColor = BinaryColor::On; - const BACKGROUND_COLOR: BinaryColor = BinaryColor::On; -} - -pub struct PrimaryButtonStyle; -impl ButtonStyle for PrimaryButtonStyle { - type Inactive = PrimaryButtonInactive; - type Idle = PrimaryButtonIdle; - type Hovered = PrimaryButtonHovered; - type Pressed = PrimaryButtonPressed; - - const FONT: MonoFont<'static> = FONT_6X10; -} diff --git a/backend-embedded-graphics/src/themes/default/button/primary/mod.rs b/backend-embedded-graphics/src/themes/default/button/primary/mod.rs deleted file mode 100644 index f3ce240..0000000 --- a/backend-embedded-graphics/src/themes/default/button/primary/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod binary_color; -pub mod rgb; diff --git a/backend-embedded-graphics/src/themes/default/button/primary/rgb.rs b/backend-embedded-graphics/src/themes/default/button/primary/rgb.rs deleted file mode 100644 index c47e30f..0000000 --- a/backend-embedded-graphics/src/themes/default/button/primary/rgb.rs +++ /dev/null @@ -1,62 +0,0 @@ -use core::marker::PhantomData; - -use embedded_graphics::{ - mono_font::{ascii::FONT_6X10, MonoFont}, - prelude::WebColors, -}; - -use crate::themes::default::button::{ButtonStateColors, ButtonStyle}; - -pub struct PrimaryButtonInactive(PhantomData); -pub struct PrimaryButtonIdle(PhantomData); -pub struct PrimaryButtonHovered(PhantomData); -pub struct PrimaryButtonPressed(PhantomData); - -impl ButtonStateColors for PrimaryButtonInactive -where - C: WebColors, -{ - const LABEL_COLOR: C = C::CSS_LIGHT_GRAY; - const BORDER_COLOR: C = C::CSS_DIM_GRAY; - const BACKGROUND_COLOR: C = C::CSS_DIM_GRAY; -} - -impl ButtonStateColors for PrimaryButtonIdle -where - C: WebColors, -{ - const LABEL_COLOR: C = C::WHITE; - const BORDER_COLOR: C = C::CSS_STEEL_BLUE; - const BACKGROUND_COLOR: C = C::CSS_STEEL_BLUE; -} - -impl ButtonStateColors for PrimaryButtonHovered -where - C: WebColors, -{ - const LABEL_COLOR: C = C::WHITE; - const BORDER_COLOR: C = C::CSS_DODGER_BLUE; - const BACKGROUND_COLOR: C = C::CSS_DODGER_BLUE; -} - -impl ButtonStateColors for PrimaryButtonPressed -where - C: WebColors, -{ - const LABEL_COLOR: C = C::WHITE; - const BORDER_COLOR: C = C::CSS_LIGHT_STEEL_BLUE; - const BACKGROUND_COLOR: C = C::CSS_LIGHT_STEEL_BLUE; -} - -pub struct PrimaryButtonStyle(PhantomData); -impl ButtonStyle for PrimaryButtonStyle -where - C: WebColors, -{ - type Inactive = PrimaryButtonInactive; - type Idle = PrimaryButtonIdle; - type Hovered = PrimaryButtonHovered; - type Pressed = PrimaryButtonPressed; - - const FONT: MonoFont<'static> = FONT_6X10; -} diff --git a/backend-embedded-graphics/src/themes/default/button/secondary/binary_color.rs b/backend-embedded-graphics/src/themes/default/button/secondary/binary_color.rs deleted file mode 100644 index cb5220c..0000000 --- a/backend-embedded-graphics/src/themes/default/button/secondary/binary_color.rs +++ /dev/null @@ -1,45 +0,0 @@ -use embedded_graphics::{ - mono_font::{ascii::FONT_6X10, MonoFont}, - pixelcolor::BinaryColor, -}; - -use crate::themes::default::button::{ButtonStateColors, ButtonStyle}; - -pub struct SecondaryButtonInactive; -pub struct SecondaryButtonIdle; -pub struct SecondaryButtonHovered; -pub struct SecondaryButtonPressed; - -impl ButtonStateColors for SecondaryButtonInactive { - const LABEL_COLOR: BinaryColor = BinaryColor::On; - const BORDER_COLOR: BinaryColor = BinaryColor::Off; - const BACKGROUND_COLOR: BinaryColor = BinaryColor::Off; -} - -impl ButtonStateColors for SecondaryButtonIdle { - const LABEL_COLOR: BinaryColor = BinaryColor::On; - const BORDER_COLOR: BinaryColor = BinaryColor::Off; - const BACKGROUND_COLOR: BinaryColor = BinaryColor::Off; -} - -impl ButtonStateColors for SecondaryButtonHovered { - const LABEL_COLOR: BinaryColor = BinaryColor::On; - const BORDER_COLOR: BinaryColor = BinaryColor::On; - const BACKGROUND_COLOR: BinaryColor = BinaryColor::Off; -} - -impl ButtonStateColors for SecondaryButtonPressed { - const LABEL_COLOR: BinaryColor = BinaryColor::Off; - const BORDER_COLOR: BinaryColor = BinaryColor::Off; - const BACKGROUND_COLOR: BinaryColor = BinaryColor::On; -} - -pub struct SecondaryButtonStyle; -impl ButtonStyle for SecondaryButtonStyle { - type Inactive = SecondaryButtonInactive; - type Idle = SecondaryButtonIdle; - type Hovered = SecondaryButtonHovered; - type Pressed = SecondaryButtonPressed; - - const FONT: MonoFont<'static> = FONT_6X10; -} diff --git a/backend-embedded-graphics/src/themes/default/button/secondary/mod.rs b/backend-embedded-graphics/src/themes/default/button/secondary/mod.rs deleted file mode 100644 index f3ce240..0000000 --- a/backend-embedded-graphics/src/themes/default/button/secondary/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod binary_color; -pub mod rgb; diff --git a/backend-embedded-graphics/src/themes/default/button/secondary/rgb.rs b/backend-embedded-graphics/src/themes/default/button/secondary/rgb.rs deleted file mode 100644 index 68ea236..0000000 --- a/backend-embedded-graphics/src/themes/default/button/secondary/rgb.rs +++ /dev/null @@ -1,62 +0,0 @@ -use core::marker::PhantomData; - -use embedded_graphics::{ - mono_font::{ascii::FONT_6X10, MonoFont}, - prelude::WebColors, -}; - -use crate::themes::default::button::{ButtonStateColors, ButtonStyle}; - -pub struct SecondaryButtonInactive(PhantomData); -pub struct SecondaryButtonIdle(PhantomData); -pub struct SecondaryButtonHovered(PhantomData); -pub struct SecondaryButtonPressed(PhantomData); - -impl ButtonStateColors for SecondaryButtonInactive -where - C: WebColors, -{ - const LABEL_COLOR: C = C::CSS_LIGHT_GRAY; - const BORDER_COLOR: C = C::CSS_DIM_GRAY; - const BACKGROUND_COLOR: C = C::CSS_DIM_GRAY; -} - -impl ButtonStateColors for SecondaryButtonIdle -where - C: WebColors, -{ - const LABEL_COLOR: C = C::WHITE; - const BORDER_COLOR: C = C::CSS_SLATE_GRAY; - const BACKGROUND_COLOR: C = C::CSS_SLATE_GRAY; -} - -impl ButtonStateColors for SecondaryButtonHovered -where - C: WebColors, -{ - const LABEL_COLOR: C = C::WHITE; - const BORDER_COLOR: C = C::CSS_LIGHT_SLATE_GRAY; - const BACKGROUND_COLOR: C = C::CSS_LIGHT_SLATE_GRAY; -} - -impl ButtonStateColors for SecondaryButtonPressed -where - C: WebColors, -{ - const LABEL_COLOR: C = C::WHITE; - const BORDER_COLOR: C = C::CSS_STEEL_BLUE; - const BACKGROUND_COLOR: C = C::CSS_STEEL_BLUE; -} - -pub struct SecondaryButtonStyle(PhantomData); -impl ButtonStyle for SecondaryButtonStyle -where - C: WebColors, -{ - type Inactive = SecondaryButtonInactive; - type Idle = SecondaryButtonIdle; - type Hovered = SecondaryButtonHovered; - type Pressed = SecondaryButtonPressed; - - const FONT: MonoFont<'static> = FONT_6X10; -} diff --git a/backend-embedded-graphics/src/themes/default/check_box/binary_color.rs b/backend-embedded-graphics/src/themes/default/check_box/binary_color.rs deleted file mode 100644 index 3555cf6..0000000 --- a/backend-embedded-graphics/src/themes/default/check_box/binary_color.rs +++ /dev/null @@ -1,49 +0,0 @@ -use embedded_graphics::{ - mono_font::{ascii::FONT_6X10, MonoFont}, - pixelcolor::BinaryColor, -}; - -use crate::themes::default::{check_box::CheckBoxStateColors, CheckBoxVisualStyle}; - -pub struct CheckBoxInactive; -pub struct CheckBoxIdle; -pub struct CheckBoxHovered; -pub struct CheckBoxPressed; - -impl CheckBoxStateColors for CheckBoxInactive { - const LABEL_COLOR: BinaryColor = BinaryColor::On; - const BORDER_COLOR: BinaryColor = BinaryColor::On; - const BACKGROUND_COLOR: BinaryColor = BinaryColor::Off; - const CHECK_MARK_COLOR: BinaryColor = BinaryColor::On; -} - -impl CheckBoxStateColors for CheckBoxIdle { - const LABEL_COLOR: BinaryColor = BinaryColor::On; - const BORDER_COLOR: BinaryColor = BinaryColor::On; - const BACKGROUND_COLOR: BinaryColor = BinaryColor::Off; - const CHECK_MARK_COLOR: BinaryColor = BinaryColor::On; -} - -impl CheckBoxStateColors for CheckBoxHovered { - const LABEL_COLOR: BinaryColor = BinaryColor::On; - const BORDER_COLOR: BinaryColor = BinaryColor::On; - const BACKGROUND_COLOR: BinaryColor = BinaryColor::Off; - const CHECK_MARK_COLOR: BinaryColor = BinaryColor::On; -} - -impl CheckBoxStateColors for CheckBoxPressed { - const LABEL_COLOR: BinaryColor = BinaryColor::On; - const BORDER_COLOR: BinaryColor = BinaryColor::On; - const BACKGROUND_COLOR: BinaryColor = BinaryColor::Off; - const CHECK_MARK_COLOR: BinaryColor = BinaryColor::On; -} - -pub struct CheckBoxStyle; -impl CheckBoxVisualStyle for CheckBoxStyle { - type Inactive = CheckBoxInactive; - type Idle = CheckBoxIdle; - type Hovered = CheckBoxHovered; - type Pressed = CheckBoxPressed; - - const FONT: MonoFont<'static> = FONT_6X10; -} diff --git a/backend-embedded-graphics/src/themes/default/check_box/mod.rs b/backend-embedded-graphics/src/themes/default/check_box/mod.rs deleted file mode 100644 index ef387af..0000000 --- a/backend-embedded-graphics/src/themes/default/check_box/mod.rs +++ /dev/null @@ -1,112 +0,0 @@ -use crate::{ - themes::default::DefaultTheme, - widgets::{ - graphical::checkbox::CheckBoxStyle, - label::{ascii::LabelConstructor, LabelStyle, LabelStyling}, - }, -}; -use embedded_graphics::{ - mono_font::{MonoFont, MonoTextStyle}, - pixelcolor::PixelColor, -}; -use embedded_gui::{ - state::WidgetState, - widgets::{ - graphical::checkbox::{CheckBox, CheckBoxProperties}, - label::Label, - layouts::linear::{ - object_chain::{Chain, Link}, - Cell, LinearLayout, Row, WithSpacing, - }, - toggle::Toggle, - }, -}; - -pub mod binary_color; -pub mod rgb; - -pub trait CheckBoxStateColors { - const LABEL_COLOR: C; - const BORDER_COLOR: C; - const BACKGROUND_COLOR: C; - const CHECK_MARK_COLOR: C; - - fn apply_check_box>(check_box: &mut CheckBox

) { - check_box.set_background_color(Self::BACKGROUND_COLOR); - check_box.set_border_color(Self::BORDER_COLOR); - check_box.set_check_mark_color(Self::CHECK_MARK_COLOR); - } - - fn apply_label(label: &mut Label) - where - Label: LabelStyling, - { - label.set_text_color(Self::LABEL_COLOR); - } -} - -pub trait CheckBoxVisualStyle { - type Inactive: CheckBoxStateColors; - type Idle: CheckBoxStateColors; - type Hovered: CheckBoxStateColors; - type Pressed: CheckBoxStateColors; - - const FONT: MonoFont<'static>; - - fn apply_check_box>( - check_box: &mut CheckBox

, - state: WidgetState, - ) { - check_box.set_checked(state.has_state(Toggle::STATE_CHECKED)); - if state.has_state(Toggle::STATE_INACTIVE) { - Self::Inactive::apply_check_box(check_box); - } else if state.has_state(Toggle::STATE_HOVERED) { - Self::Hovered::apply_check_box(check_box); - } else if state.has_state(Toggle::STATE_PRESSED) { - Self::Pressed::apply_check_box(check_box); - } else { - Self::Idle::apply_check_box(check_box); - }; - } - - fn apply_label(label: &mut Label, state: WidgetState) - where - Label: LabelStyling, - { - if state.has_state(Toggle::STATE_INACTIVE) { - Self::Inactive::apply_label(label); - } else if state.has_state(Toggle::STATE_HOVERED) { - Self::Hovered::apply_label(label); - } else if state.has_state(Toggle::STATE_PRESSED) { - Self::Pressed::apply_label(label); - } else { - Self::Idle::apply_label(label); - }; - } -} - -pub type StyledCheckBox<'a, C> = Toggle< - LinearLayout< - Link< - Cell>>>, - Chain>>>, - >, - Row, - >, - (), - true, ->; - -pub fn styled_check_box(label: &'static str) -> StyledCheckBox -where - C: DefaultTheme, - S: CheckBoxVisualStyle, - CheckBoxStyle: Default, -{ - Toggle::new( - Row::new() - .spacing(1) - .add(CheckBox::>::new().on_state_changed(S::apply_check_box)) - .add(Label::new(label).on_state_changed(S::apply_label)), - ) -} diff --git a/backend-embedded-graphics/src/themes/default/check_box/rgb.rs b/backend-embedded-graphics/src/themes/default/check_box/rgb.rs deleted file mode 100644 index 6d5e8dd..0000000 --- a/backend-embedded-graphics/src/themes/default/check_box/rgb.rs +++ /dev/null @@ -1,66 +0,0 @@ -use core::marker::PhantomData; - -use embedded_graphics::{ - mono_font::{ascii::FONT_6X10, MonoFont}, - pixelcolor::WebColors, -}; - -use crate::themes::default::check_box::{CheckBoxStateColors, CheckBoxVisualStyle}; - -pub struct CheckBoxInactive; -pub struct CheckBoxIdle; -pub struct CheckBoxHovered; -pub struct CheckBoxPressed; - -impl CheckBoxStateColors for CheckBoxInactive -where - C: WebColors, -{ - const LABEL_COLOR: C = C::CSS_GRAY; - const BORDER_COLOR: C = C::CSS_GRAY; - const BACKGROUND_COLOR: C = C::CSS_DARK_GRAY; - const CHECK_MARK_COLOR: C = C::CSS_STEEL_BLUE; -} - -impl CheckBoxStateColors for CheckBoxIdle -where - C: WebColors, -{ - const LABEL_COLOR: C = C::BLACK; - const BORDER_COLOR: C = C::BLACK; - const BACKGROUND_COLOR: C = C::WHITE; - const CHECK_MARK_COLOR: C = C::CSS_DODGER_BLUE; -} - -impl CheckBoxStateColors for CheckBoxHovered -where - C: WebColors, -{ - const LABEL_COLOR: C = C::BLACK; - const BORDER_COLOR: C = C::BLACK; - const BACKGROUND_COLOR: C = C::CSS_LIGHT_GRAY; - const CHECK_MARK_COLOR: C = C::CSS_DODGER_BLUE; -} - -impl CheckBoxStateColors for CheckBoxPressed -where - C: WebColors, -{ - const LABEL_COLOR: C = C::BLACK; - const BORDER_COLOR: C = C::BLACK; - const BACKGROUND_COLOR: C = C::CSS_DARK_GRAY; - const CHECK_MARK_COLOR: C = C::CSS_DODGER_BLUE; -} - -pub struct CheckBoxStyle(PhantomData); -impl CheckBoxVisualStyle for CheckBoxStyle -where - C: WebColors, -{ - type Inactive = CheckBoxInactive; - type Idle = CheckBoxIdle; - type Hovered = CheckBoxHovered; - type Pressed = CheckBoxPressed; - - const FONT: MonoFont<'static> = FONT_6X10; -} diff --git a/backend-embedded-graphics/src/themes/default/mod.rs b/backend-embedded-graphics/src/themes/default/mod.rs deleted file mode 100644 index 7675080..0000000 --- a/backend-embedded-graphics/src/themes/default/mod.rs +++ /dev/null @@ -1,163 +0,0 @@ -//! The "default" theme. -//! -//! This module describes two themes: -//! - a `BinaryColor` theme with `Off` background color and `On` foreground color -//! - an `Rgb555`, `Rgb565` and `Rgb888` version with a light color scheme with a blue-ish primary accent color. -//! - -use core::ops::RangeInclusive; - -use crate::themes::{ - default::{ - button::{ - primary::{ - binary_color::PrimaryButtonStyle, rgb::PrimaryButtonStyle as RgbPrimaryButtonStyle, - }, - secondary::{ - binary_color::SecondaryButtonStyle, - rgb::SecondaryButtonStyle as RgbSecondaryButtonStyle, - }, - styled_button, styled_button_stretched, ButtonStyle, StyledButton, - StyledButtonStretched, - }, - check_box::{ - binary_color::CheckBoxStyle, rgb::CheckBoxStyle as RgbCheckBoxStyle, styled_check_box, - CheckBoxVisualStyle, StyledCheckBox, - }, - radio_button::{ - binary_color::RadioButtonStyle, rgb::RadioButtonStyle as RgbRadioButtonStyle, - styled_radio_button, RadioButtonVisualStyle, StyledRadioButton, - }, - scrollbar::{ - binary_color::{HorizontalScrollbar, VerticalScrollbar}, - horizontal_scrollbar, - rgb::{ - HorizontalScrollbar as RgbHorizontalScrollbar, - VerticalScrollbar as RgbVerticalScrollbar, - }, - vertical_scrollbar, ScrollbarVisualStyle, StyledHorizontalScrollbar, - StyledVerticalScrollbar, - }, - slider::{ - binary_color::SliderStyle, rgb::SliderStyle as RgbSliderStyle, slider, - SliderVisualStyle, StyledSlider, - }, - toggle_button::{ - binary_color::ToggleButtonStyle, rgb::ToggleButtonStyle as RgbToggleButtonStyle, - styled_toggle_button, styled_toggle_button_stretched, StyledToggleButton, - StyledToggleButtonStretched, ToggleButtonStyle as ToggleButtonStyleTrait, - }, - }, - Theme, -}; - -use embedded_graphics::pixelcolor::{BinaryColor, Rgb555, Rgb565, Rgb888, RgbColor}; - -pub mod button; -pub mod check_box; -pub mod radio_button; -pub mod scrollbar; -pub mod slider; -pub mod toggle_button; - -pub trait DefaultTheme: Theme { - type PrimaryButton: ButtonStyle; - type SecondaryButton: ButtonStyle; - type ToggleButton: ToggleButtonStyleTrait; - - type CheckBox: CheckBoxVisualStyle; - type RadioButton: RadioButtonVisualStyle; - - type Slider: SliderVisualStyle; - type VerticalScrollbar: ScrollbarVisualStyle; - type HorizontalScrollbar: ScrollbarVisualStyle; - - fn primary_button(label: &'static str) -> StyledButton { - styled_button::(label) - } - - fn secondary_button(label: &'static str) -> StyledButton { - styled_button::(label) - } - - fn primary_button_stretched(label: &'static str) -> StyledButtonStretched { - styled_button_stretched::(label) - } - - fn secondary_button_stretched(label: &'static str) -> StyledButtonStretched { - styled_button_stretched::(label) - } - - fn toggle_button(label: &'static str) -> StyledToggleButton { - styled_toggle_button::(label) - } - - fn toggle_button_stretched(label: &'static str) -> StyledToggleButtonStretched { - styled_toggle_button_stretched::(label) - } - - fn check_box(label: &'static str) -> StyledCheckBox { - styled_check_box::(label) - } - - fn radio_button(label: &'static str) -> StyledRadioButton { - styled_radio_button::(label) - } - - fn slider(range: RangeInclusive) -> StyledSlider { - slider::(range) - } - - fn vertical_scrollbar() -> StyledVerticalScrollbar { - vertical_scrollbar::() - } - - fn horizontal_scrollbar() -> StyledHorizontalScrollbar { - horizontal_scrollbar::() - } -} - -impl Theme for BinaryColor { - const TEXT_COLOR: BinaryColor = BinaryColor::On; - const BORDER_COLOR: BinaryColor = BinaryColor::On; - const BACKGROUND_COLOR: BinaryColor = BinaryColor::Off; -} - -impl DefaultTheme for BinaryColor { - type PrimaryButton = PrimaryButtonStyle; - type SecondaryButton = SecondaryButtonStyle; - type ToggleButton = ToggleButtonStyle; - - type CheckBox = CheckBoxStyle; - type RadioButton = RadioButtonStyle; - - type Slider = SliderStyle; - type VerticalScrollbar = VerticalScrollbar; - type HorizontalScrollbar = HorizontalScrollbar; -} - -macro_rules! impl_rgb_default_theme { - ($type:ty) => { - impl Theme for $type { - const TEXT_COLOR: Self = Self::BLACK; - const BORDER_COLOR: Self = Self::BLACK; - const BACKGROUND_COLOR: Self = Self::WHITE; - } - impl DefaultTheme for $type { - type PrimaryButton = RgbPrimaryButtonStyle; - type SecondaryButton = RgbSecondaryButtonStyle; - type ToggleButton = RgbToggleButtonStyle; - - type CheckBox = RgbCheckBoxStyle; - type RadioButton = RgbRadioButtonStyle; - - type Slider = RgbSliderStyle; - type VerticalScrollbar = RgbVerticalScrollbar; - type HorizontalScrollbar = RgbHorizontalScrollbar; - } - }; -} - -impl_rgb_default_theme!(Rgb555); -impl_rgb_default_theme!(Rgb565); -impl_rgb_default_theme!(Rgb888); diff --git a/backend-embedded-graphics/src/themes/default/radio_button/binary_color.rs b/backend-embedded-graphics/src/themes/default/radio_button/binary_color.rs deleted file mode 100644 index e22e445..0000000 --- a/backend-embedded-graphics/src/themes/default/radio_button/binary_color.rs +++ /dev/null @@ -1,49 +0,0 @@ -use embedded_graphics::{ - mono_font::{ascii::FONT_6X10, MonoFont}, - pixelcolor::BinaryColor, -}; - -use crate::themes::default::{radio_button::RadioButtonStateColors, RadioButtonVisualStyle}; - -pub struct RadioButtonInactive; -pub struct RadioButtonIdle; -pub struct RadioButtonHovered; -pub struct RadioButtonPressed; - -impl RadioButtonStateColors for RadioButtonInactive { - const LABEL_COLOR: BinaryColor = BinaryColor::On; - const BORDER_COLOR: BinaryColor = BinaryColor::On; - const BACKGROUND_COLOR: BinaryColor = BinaryColor::Off; - const CHECK_MARK_COLOR: BinaryColor = BinaryColor::On; -} - -impl RadioButtonStateColors for RadioButtonIdle { - const LABEL_COLOR: BinaryColor = BinaryColor::On; - const BORDER_COLOR: BinaryColor = BinaryColor::On; - const BACKGROUND_COLOR: BinaryColor = BinaryColor::Off; - const CHECK_MARK_COLOR: BinaryColor = BinaryColor::On; -} - -impl RadioButtonStateColors for RadioButtonHovered { - const LABEL_COLOR: BinaryColor = BinaryColor::On; - const BORDER_COLOR: BinaryColor = BinaryColor::On; - const BACKGROUND_COLOR: BinaryColor = BinaryColor::Off; - const CHECK_MARK_COLOR: BinaryColor = BinaryColor::On; -} - -impl RadioButtonStateColors for RadioButtonPressed { - const LABEL_COLOR: BinaryColor = BinaryColor::On; - const BORDER_COLOR: BinaryColor = BinaryColor::On; - const BACKGROUND_COLOR: BinaryColor = BinaryColor::Off; - const CHECK_MARK_COLOR: BinaryColor = BinaryColor::On; -} - -pub struct RadioButtonStyle; -impl RadioButtonVisualStyle for RadioButtonStyle { - type Inactive = RadioButtonInactive; - type Idle = RadioButtonIdle; - type Hovered = RadioButtonHovered; - type Pressed = RadioButtonPressed; - - const FONT: MonoFont<'static> = FONT_6X10; -} diff --git a/backend-embedded-graphics/src/themes/default/radio_button/mod.rs b/backend-embedded-graphics/src/themes/default/radio_button/mod.rs deleted file mode 100644 index 485cc10..0000000 --- a/backend-embedded-graphics/src/themes/default/radio_button/mod.rs +++ /dev/null @@ -1,116 +0,0 @@ -use crate::{ - themes::default::DefaultTheme, - widgets::{ - graphical::radio::RadioButtonStyle, - label::{ascii::LabelConstructor, LabelStyle, LabelStyling}, - }, -}; -use embedded_graphics::{ - mono_font::{MonoFont, MonoTextStyle}, - pixelcolor::PixelColor, -}; -use embedded_gui::{ - state::WidgetState, - widgets::{ - graphical::radio::{RadioButton, RadioButtonProperties}, - label::{Label, LabelProperties}, - layouts::linear::{ - object_chain::{Chain, Link}, - Cell, LinearLayout, Row, WithSpacing, - }, - toggle::Toggle, - }, -}; - -pub mod binary_color; -pub mod rgb; - -pub trait RadioButtonStateColors { - const LABEL_COLOR: C; - const BORDER_COLOR: C; - const BACKGROUND_COLOR: C; - const CHECK_MARK_COLOR: C; - - fn apply_radio_button>(radio_button: &mut RadioButton

) { - radio_button.set_background_color(Self::BACKGROUND_COLOR); - radio_button.set_border_color(Self::BORDER_COLOR); - radio_button.set_check_mark_color(Self::CHECK_MARK_COLOR); - } - - fn apply_label(label: &mut Label) - where - Label: LabelStyling, - { - label.set_text_color(Self::LABEL_COLOR); - } -} - -pub trait RadioButtonVisualStyle { - type Inactive: RadioButtonStateColors; - type Idle: RadioButtonStateColors; - type Hovered: RadioButtonStateColors; - type Pressed: RadioButtonStateColors; - - const FONT: MonoFont<'static>; - - fn apply_radio_button>( - radio_button: &mut RadioButton

, - state: WidgetState, - ) { - radio_button.set_selected(state.has_state(Toggle::STATE_CHECKED)); - - if state.has_state(Toggle::STATE_INACTIVE) { - Self::Inactive::apply_radio_button(radio_button); - } else if state.has_state(Toggle::STATE_HOVERED) { - Self::Hovered::apply_radio_button(radio_button); - } else if state.has_state(Toggle::STATE_PRESSED) { - Self::Pressed::apply_radio_button(radio_button); - } else { - Self::Idle::apply_radio_button(radio_button); - }; - } - - fn apply_label(label: &mut Label, state: WidgetState) - where - S: AsRef, - T: LabelProperties, - Label: LabelStyling, - { - if state.has_state(Toggle::STATE_INACTIVE) { - Self::Inactive::apply_label(label); - } else if state.has_state(Toggle::STATE_HOVERED) { - Self::Hovered::apply_label(label); - } else if state.has_state(Toggle::STATE_PRESSED) { - Self::Pressed::apply_label(label); - } else { - Self::Idle::apply_label(label); - }; - } -} - -pub type StyledRadioButton<'a, C> = Toggle< - LinearLayout< - Link< - Cell>>>, - Chain>>>, - >, - Row, - >, - (), - false, ->; - -pub fn styled_radio_button(label: &'static str) -> StyledRadioButton -where - C: DefaultTheme, - S: RadioButtonVisualStyle, - RadioButtonStyle: Default, -{ - Toggle::new( - Row::new() - .spacing(1) - .add(RadioButton::>::new().on_state_changed(S::apply_radio_button)) - .add(Label::new(label).on_state_changed(S::apply_label)), - ) - .disallow_manual_uncheck() -} diff --git a/backend-embedded-graphics/src/themes/default/radio_button/rgb.rs b/backend-embedded-graphics/src/themes/default/radio_button/rgb.rs deleted file mode 100644 index bb1d606..0000000 --- a/backend-embedded-graphics/src/themes/default/radio_button/rgb.rs +++ /dev/null @@ -1,66 +0,0 @@ -use core::marker::PhantomData; - -use embedded_graphics::{ - mono_font::{ascii::FONT_6X10, MonoFont}, - pixelcolor::WebColors, -}; - -use crate::themes::default::{radio_button::RadioButtonStateColors, RadioButtonVisualStyle}; - -pub struct RadioButtonInactive; -pub struct RadioButtonIdle; -pub struct RadioButtonHovered; -pub struct RadioButtonPressed; - -impl RadioButtonStateColors for RadioButtonInactive -where - C: WebColors, -{ - const LABEL_COLOR: C = C::CSS_GRAY; - const BORDER_COLOR: C = C::CSS_GRAY; - const BACKGROUND_COLOR: C = C::CSS_DARK_GRAY; - const CHECK_MARK_COLOR: C = C::CSS_STEEL_BLUE; -} - -impl RadioButtonStateColors for RadioButtonIdle -where - C: WebColors, -{ - const LABEL_COLOR: C = C::BLACK; - const BORDER_COLOR: C = C::BLACK; - const BACKGROUND_COLOR: C = C::WHITE; - const CHECK_MARK_COLOR: C = C::CSS_DODGER_BLUE; -} - -impl RadioButtonStateColors for RadioButtonHovered -where - C: WebColors, -{ - const LABEL_COLOR: C = C::BLACK; - const BORDER_COLOR: C = C::BLACK; - const BACKGROUND_COLOR: C = C::CSS_LIGHT_GRAY; - const CHECK_MARK_COLOR: C = C::CSS_DODGER_BLUE; -} - -impl RadioButtonStateColors for RadioButtonPressed -where - C: WebColors, -{ - const LABEL_COLOR: C = C::BLACK; - const BORDER_COLOR: C = C::BLACK; - const BACKGROUND_COLOR: C = C::CSS_DARK_GRAY; - const CHECK_MARK_COLOR: C = C::CSS_DODGER_BLUE; -} - -pub struct RadioButtonStyle(PhantomData); -impl RadioButtonVisualStyle for RadioButtonStyle -where - C: WebColors, -{ - type Inactive = RadioButtonInactive; - type Idle = RadioButtonIdle; - type Hovered = RadioButtonHovered; - type Pressed = RadioButtonPressed; - - const FONT: MonoFont<'static> = FONT_6X10; -} diff --git a/backend-embedded-graphics/src/themes/default/scrollbar/binary_color.rs b/backend-embedded-graphics/src/themes/default/scrollbar/binary_color.rs deleted file mode 100644 index 41dbe74..0000000 --- a/backend-embedded-graphics/src/themes/default/scrollbar/binary_color.rs +++ /dev/null @@ -1,50 +0,0 @@ -use embedded_graphics::pixelcolor::BinaryColor; -use embedded_gui::widgets::slider::{Horizontal, Vertical}; - -use crate::themes::default::scrollbar::{ScrollbarVisualState, ScrollbarVisualStyle}; - -#[derive(Default)] -pub struct VerticalScrollbar; - -pub struct ScrollbarIdle; -impl ScrollbarVisualState for ScrollbarIdle { - const BACKGROUND_FILL_COLOR: Option = None; - const BACKGROUND_BORDER_COLOR: Option = None; - const BORDER_COLOR: Option = Some(BinaryColor::On); - const BORDER_THICKNESS: u32 = 1; - const FILL_COLOR: Option = Some(BinaryColor::Off); -} - -pub struct ScrollbarHovered; -impl ScrollbarVisualState for ScrollbarHovered { - const BACKGROUND_FILL_COLOR: Option = None; - const BACKGROUND_BORDER_COLOR: Option = None; - const BORDER_COLOR: Option = Some(BinaryColor::Off); - const BORDER_THICKNESS: u32 = 1; - const FILL_COLOR: Option = Some(BinaryColor::On); -} - -impl ScrollbarVisualStyle for VerticalScrollbar { - type Direction = Vertical; - - const THICKNESS: u32 = 6; - - type Inactive = ScrollbarIdle; - type Idle = ScrollbarIdle; - type Hovered = ScrollbarHovered; - type Dragged = ScrollbarHovered; -} - -#[derive(Default)] -pub struct HorizontalScrollbar; - -impl ScrollbarVisualStyle for HorizontalScrollbar { - type Direction = Horizontal; - - const THICKNESS: u32 = 6; - - type Inactive = ScrollbarIdle; - type Idle = ScrollbarIdle; - type Hovered = ScrollbarHovered; - type Dragged = ScrollbarHovered; -} diff --git a/backend-embedded-graphics/src/themes/default/scrollbar/mod.rs b/backend-embedded-graphics/src/themes/default/scrollbar/mod.rs deleted file mode 100644 index e7f2bcc..0000000 --- a/backend-embedded-graphics/src/themes/default/scrollbar/mod.rs +++ /dev/null @@ -1,156 +0,0 @@ -use core::marker::PhantomData; - -use embedded_graphics::{ - draw_target::DrawTarget, - prelude::{PixelColor, Primitive}, - primitives::PrimitiveStyle, - Drawable, -}; -use embedded_gui::{ - data::WidgetData, - widgets::slider::{Slider, SliderDirection, SliderFields, SliderProperties}, - WidgetRenderer, -}; - -use crate::{themes::default::DefaultTheme, EgCanvas, ToRectangle}; - -pub mod binary_color; -pub mod rgb; - -pub trait ScrollbarVisualState -where - C: PixelColor, -{ - const BACKGROUND_FILL_COLOR: Option; - const BACKGROUND_BORDER_COLOR: Option; - const BACKGROUND_BORDER_THICKNESS: u32 = 0; - const BORDER_COLOR: Option; - const FILL_COLOR: Option; - const BORDER_THICKNESS: u32 = 0; - - fn styles() -> (PrimitiveStyle, PrimitiveStyle) { - let mut style = PrimitiveStyle::default(); - let mut bg_style = PrimitiveStyle::default(); - - style.fill_color = Self::FILL_COLOR; - style.stroke_width = Self::BORDER_THICKNESS; - style.stroke_color = Self::BORDER_COLOR; - - bg_style.fill_color = Self::BACKGROUND_FILL_COLOR; - bg_style.stroke_width = Self::BACKGROUND_BORDER_THICKNESS; - bg_style.stroke_color = Self::BACKGROUND_BORDER_COLOR; - - (style, bg_style) - } -} - -pub trait ScrollbarVisualStyle: Default -where - C: PixelColor, -{ - type Direction: SliderDirection; - - const THICKNESS: u32; - - type Idle: ScrollbarVisualState; - type Hovered: ScrollbarVisualState; - type Dragged: ScrollbarVisualState; - type Inactive: ScrollbarVisualState; - - fn draw, D>( - &self, - canvas: &mut crate::EgCanvas

, - slider: &SliderFields, D>, - ) -> Result<(), DT::Error> { - let (slider_style, bg_style) = if slider.state.has_state(Slider::STATE_INACTIVE) { - Self::Inactive::styles() - } else if slider.state.has_state(Slider::STATE_DRAGGED) { - Self::Dragged::styles() - } else if slider.state.has_state(Slider::STATE_HOVERED) { - Self::Hovered::styles() - } else { - Self::Idle::styles() - }; - - // Background - slider - .bounds - .to_rectangle() - .into_styled(bg_style) - .draw(&mut canvas.target)?; - - // Foreground - slider - .slider_bounds() - .to_rectangle() - .into_styled(slider_style) - .draw(&mut canvas.target) - } -} - -pub struct ScrollbarProperties -where - C: PixelColor, - VS: ScrollbarVisualStyle, -{ - visual: VS, - window_length: u32, - _marker: PhantomData, -} - -impl ScrollbarProperties -where - C: PixelColor, - VS: ScrollbarVisualStyle, -{ - pub fn new() -> Self { - Self { - visual: VS::default(), - window_length: 0, - _marker: PhantomData, - } - } -} - -impl SliderProperties for ScrollbarProperties -where - C: PixelColor, - VS: ScrollbarVisualStyle, -{ - type Direction = VS::Direction; - const THICKNESS: u32 = VS::THICKNESS; - - fn length(&self) -> u32 { - self.window_length - } - - fn set_length(&mut self, length: u32) { - self.window_length = length.max(3); - } -} - -impl WidgetRenderer> for Slider, D> -where - C: PixelColor, - DT: DrawTarget, - D: WidgetData, - VS: ScrollbarVisualStyle, -{ - fn draw(&mut self, canvas: &mut EgCanvas
) -> Result<(), DT::Error> { - self.fields.properties.visual.draw(canvas, &self.fields) - } -} - -pub type StyledVerticalScrollbar = - Slider::VerticalScrollbar>, ()>; - -pub type StyledHorizontalScrollbar = - Slider::HorizontalScrollbar>, ()>; - -pub fn vertical_scrollbar() -> StyledVerticalScrollbar { - Slider::new(0..=0, ScrollbarProperties::new()) -} - -pub fn horizontal_scrollbar() -> StyledHorizontalScrollbar { - Slider::new(0..=0, ScrollbarProperties::new()) -} diff --git a/backend-embedded-graphics/src/themes/default/scrollbar/rgb.rs b/backend-embedded-graphics/src/themes/default/scrollbar/rgb.rs deleted file mode 100644 index 8229333..0000000 --- a/backend-embedded-graphics/src/themes/default/scrollbar/rgb.rs +++ /dev/null @@ -1,104 +0,0 @@ -use core::marker::PhantomData; - -use embedded_graphics::prelude::WebColors; -use embedded_gui::widgets::slider::{Horizontal, Vertical}; - -use crate::themes::default::scrollbar::{ScrollbarVisualState, ScrollbarVisualStyle}; - -pub struct VerticalScrollbar(pub PhantomData); - -impl Default for VerticalScrollbar -where - C: WebColors, -{ - fn default() -> Self { - Self(PhantomData) - } -} - -pub struct ScrollbarInactive(pub PhantomData); -impl ScrollbarVisualState for ScrollbarInactive -where - C: WebColors, -{ - const BACKGROUND_FILL_COLOR: Option = None; - const BACKGROUND_BORDER_COLOR: Option = None; - const BORDER_COLOR: Option = None; - const BORDER_THICKNESS: u32 = 0; - const FILL_COLOR: Option = Some(C::CSS_SLATE_GRAY); -} - -pub struct ScrollbarIdle(pub PhantomData); -impl ScrollbarVisualState for ScrollbarIdle -where - C: WebColors, -{ - const BACKGROUND_FILL_COLOR: Option = None; - const BACKGROUND_BORDER_COLOR: Option = None; - const BORDER_COLOR: Option = None; - const BORDER_THICKNESS: u32 = 0; - const FILL_COLOR: Option = Some(C::CSS_SLATE_GRAY); -} - -pub struct ScrollbarHovered(pub PhantomData); -impl ScrollbarVisualState for ScrollbarHovered -where - C: WebColors, -{ - const BACKGROUND_FILL_COLOR: Option = None; - const BACKGROUND_BORDER_COLOR: Option = None; - const BORDER_COLOR: Option = None; - const BORDER_THICKNESS: u32 = 0; - const FILL_COLOR: Option = Some(C::CSS_LIGHT_SLATE_GRAY); -} - -pub struct ScrollbarDragged(pub PhantomData); -impl ScrollbarVisualState for ScrollbarDragged -where - C: WebColors, -{ - const BACKGROUND_FILL_COLOR: Option = None; - const BACKGROUND_BORDER_COLOR: Option = None; - const BORDER_COLOR: Option = None; - const BORDER_THICKNESS: u32 = 0; - const FILL_COLOR: Option = Some(C::CSS_STEEL_BLUE); -} - -impl ScrollbarVisualStyle for VerticalScrollbar -where - C: WebColors, -{ - type Direction = Vertical; - - const THICKNESS: u32 = 6; - - type Inactive = ScrollbarInactive; - type Idle = ScrollbarIdle; - type Hovered = ScrollbarHovered; - type Dragged = ScrollbarDragged; -} - -pub struct HorizontalScrollbar(pub PhantomData); - -impl Default for HorizontalScrollbar -where - C: WebColors, -{ - fn default() -> Self { - Self(PhantomData) - } -} - -impl ScrollbarVisualStyle for HorizontalScrollbar -where - C: WebColors, -{ - type Direction = Horizontal; - - const THICKNESS: u32 = 6; - - type Inactive = ScrollbarInactive; - type Idle = ScrollbarIdle; - type Hovered = ScrollbarHovered; - type Dragged = ScrollbarDragged; -} diff --git a/backend-embedded-graphics/src/themes/default/slider/binary_color.rs b/backend-embedded-graphics/src/themes/default/slider/binary_color.rs deleted file mode 100644 index cc6ab1e..0000000 --- a/backend-embedded-graphics/src/themes/default/slider/binary_color.rs +++ /dev/null @@ -1,37 +0,0 @@ -use embedded_graphics::pixelcolor::BinaryColor; -use embedded_gui::widgets::slider::Horizontal; - -use crate::themes::default::slider::{SliderVisualState, SliderVisualStyle}; - -#[derive(Default)] -pub struct SliderStyle; - -pub struct SliderIdle; -impl SliderVisualState for SliderIdle { - const BACKGROUND_LINE_COLOR: Option = Some(BinaryColor::On); - const BACKGROUND_LINE_THICKNESS: u32 = 1; - const BORDER_COLOR: Option = Some(BinaryColor::On); - const BORDER_THICKNESS: u32 = 1; - const FILL_COLOR: Option = Some(BinaryColor::Off); -} - -pub struct SliderHovered; -impl SliderVisualState for SliderHovered { - const BACKGROUND_LINE_COLOR: Option = Some(BinaryColor::On); - const BACKGROUND_LINE_THICKNESS: u32 = 1; - const BORDER_COLOR: Option = Some(BinaryColor::Off); - const BORDER_THICKNESS: u32 = 1; - const FILL_COLOR: Option = Some(BinaryColor::On); -} - -impl SliderVisualStyle for SliderStyle { - type Direction = Horizontal; - - const THICKNESS: u32 = 7; - const WIDTH: u32 = 5; - - type Inactive = SliderIdle; - type Idle = SliderIdle; - type Hovered = SliderHovered; - type Dragged = SliderHovered; -} diff --git a/backend-embedded-graphics/src/themes/default/slider/mod.rs b/backend-embedded-graphics/src/themes/default/slider/mod.rs deleted file mode 100644 index 5b2c730..0000000 --- a/backend-embedded-graphics/src/themes/default/slider/mod.rs +++ /dev/null @@ -1,152 +0,0 @@ -use core::{marker::PhantomData, ops::RangeInclusive}; - -use embedded_graphics::{ - draw_target::DrawTarget, - prelude::{PixelColor, Point, Primitive}, - primitives::{Line, PrimitiveStyle}, - Drawable, -}; -use embedded_gui::{ - data::WidgetData, - widgets::slider::{ - Slider, SliderDirection, SliderFields, SliderProperties as SliderPropertiesTrait, - }, - WidgetRenderer, -}; - -use crate::{themes::default::DefaultTheme, EgCanvas, ToRectangle}; - -pub mod binary_color; -pub mod rgb; - -pub trait SliderVisualState -where - C: PixelColor, -{ - const BACKGROUND_LINE_COLOR: Option; - const BACKGROUND_LINE_THICKNESS: u32 = 0; - const BORDER_COLOR: Option; - const FILL_COLOR: Option; - const BORDER_THICKNESS: u32 = 0; - - fn styles() -> (PrimitiveStyle, PrimitiveStyle) { - let mut style = PrimitiveStyle::default(); - let mut bg_style = PrimitiveStyle::default(); - - style.fill_color = Self::FILL_COLOR; - style.stroke_width = Self::BORDER_THICKNESS; - style.stroke_color = Self::BORDER_COLOR; - - bg_style.stroke_width = Self::BACKGROUND_LINE_THICKNESS; - bg_style.stroke_color = Self::BACKGROUND_LINE_COLOR; - - (style, bg_style) - } -} - -pub trait SliderVisualStyle: Default -where - C: PixelColor, -{ - type Direction: SliderDirection; - - const THICKNESS: u32; - const WIDTH: u32; - - type Idle: SliderVisualState; - type Hovered: SliderVisualState; - type Dragged: SliderVisualState; - type Inactive: SliderVisualState; - - fn draw, D>( - &self, - canvas: &mut crate::EgCanvas
, - slider: &SliderFields, D>, - ) -> Result<(), DT::Error> { - let (slider_style, bg_style) = if slider.state.has_state(Slider::STATE_INACTIVE) { - Self::Inactive::styles() - } else if slider.state.has_state(Slider::STATE_DRAGGED) { - Self::Dragged::styles() - } else if slider.state.has_state(Slider::STATE_HOVERED) { - Self::Hovered::styles() - } else { - Self::Idle::styles() - }; - - // Background - - let bounds = slider.bounds.to_rectangle(); - let Point { x: x0, y: y0 } = bounds.top_left; - let Point { x: x1, y: y1 } = bounds.bottom_right().unwrap(); - - let y = (y0 + y1) / 2; - - Line::new(Point::new(x0, y), Point::new(x1, y)) - .into_styled(bg_style) - .draw(&mut canvas.target)?; - - // Foreground - slider - .slider_bounds() - .to_rectangle() - .into_styled(slider_style) - .draw(&mut canvas.target) - } -} - -pub struct SliderProperties -where - C: PixelColor, - VS: SliderVisualStyle, -{ - visual: VS, - _marker: PhantomData, -} - -impl SliderProperties -where - C: PixelColor, - VS: SliderVisualStyle, -{ - pub fn new() -> Self { - Self { - visual: VS::default(), - _marker: PhantomData, - } - } -} - -impl SliderPropertiesTrait for SliderProperties -where - C: PixelColor, - VS: SliderVisualStyle, -{ - type Direction = VS::Direction; - const THICKNESS: u32 = VS::THICKNESS; - - fn length(&self) -> u32 { - VS::WIDTH - } - - fn set_length(&mut self, _length: u32) { - unimplemented!("Numeric slider has no settable window length") - } -} - -impl WidgetRenderer> for Slider, D> -where - C: PixelColor, - DT: DrawTarget, - D: WidgetData, - VS: SliderVisualStyle, -{ - fn draw(&mut self, canvas: &mut EgCanvas
) -> Result<(), DT::Error> { - self.fields.properties.visual.draw(canvas, &self.fields) - } -} - -pub type StyledSlider = Slider::Slider>, ()>; - -pub fn slider(range: RangeInclusive) -> StyledSlider { - Slider::new(range, SliderProperties::new()) -} diff --git a/backend-embedded-graphics/src/themes/default/slider/rgb.rs b/backend-embedded-graphics/src/themes/default/slider/rgb.rs deleted file mode 100644 index 6f22616..0000000 --- a/backend-embedded-graphics/src/themes/default/slider/rgb.rs +++ /dev/null @@ -1,80 +0,0 @@ -use core::marker::PhantomData; - -use embedded_graphics::prelude::WebColors; -use embedded_gui::widgets::slider::Horizontal; - -use crate::themes::default::slider::{SliderVisualState, SliderVisualStyle}; - -pub struct SliderStyle(pub PhantomData); - -impl Default for SliderStyle -where - C: WebColors, -{ - fn default() -> Self { - Self(PhantomData) - } -} - -pub struct SliderInactive(pub PhantomData); -impl SliderVisualState for SliderInactive -where - C: WebColors, -{ - const BACKGROUND_LINE_COLOR: Option = Some(C::CSS_GRAY); - const BACKGROUND_LINE_THICKNESS: u32 = 1; - const BORDER_COLOR: Option = None; - const BORDER_THICKNESS: u32 = 0; - const FILL_COLOR: Option = Some(C::CSS_SLATE_GRAY); -} - -pub struct SliderIdle(pub PhantomData); -impl SliderVisualState for SliderIdle -where - C: WebColors, -{ - const BACKGROUND_LINE_COLOR: Option = Some(C::CSS_GRAY); - const BACKGROUND_LINE_THICKNESS: u32 = 1; - const BORDER_COLOR: Option = None; - const BORDER_THICKNESS: u32 = 0; - const FILL_COLOR: Option = Some(C::CSS_SLATE_GRAY); -} - -pub struct SliderHovered(pub PhantomData); -impl SliderVisualState for SliderHovered -where - C: WebColors, -{ - const BACKGROUND_LINE_COLOR: Option = Some(C::CSS_GRAY); - const BACKGROUND_LINE_THICKNESS: u32 = 1; - const BORDER_COLOR: Option = None; - const BORDER_THICKNESS: u32 = 0; - const FILL_COLOR: Option = Some(C::CSS_LIGHT_SLATE_GRAY); -} - -pub struct SliderDragged(pub PhantomData); -impl SliderVisualState for SliderDragged -where - C: WebColors, -{ - const BACKGROUND_LINE_COLOR: Option = Some(C::CSS_GRAY); - const BACKGROUND_LINE_THICKNESS: u32 = 1; - const BORDER_COLOR: Option = None; - const BORDER_THICKNESS: u32 = 0; - const FILL_COLOR: Option = Some(C::CSS_STEEL_BLUE); -} - -impl SliderVisualStyle for SliderStyle -where - C: WebColors, -{ - type Direction = Horizontal; - - const THICKNESS: u32 = 7; - const WIDTH: u32 = 5; - - type Inactive = SliderInactive; - type Idle = SliderIdle; - type Hovered = SliderHovered; - type Dragged = SliderDragged; -} diff --git a/backend-embedded-graphics/src/themes/default/toggle_button/binary_color.rs b/backend-embedded-graphics/src/themes/default/toggle_button/binary_color.rs deleted file mode 100644 index a2ab4af..0000000 --- a/backend-embedded-graphics/src/themes/default/toggle_button/binary_color.rs +++ /dev/null @@ -1,81 +0,0 @@ -use embedded_graphics::{ - mono_font::{ascii::FONT_6X10, MonoFont}, - pixelcolor::BinaryColor, -}; - -use crate::themes::default::toggle_button::{ - ButtonStateColors, ToggleButtonStyle as ToggleButtonStyleTrait, -}; - -pub struct ToggleButtonInactive; -pub struct ToggleButtonIdle; -pub struct ToggleButtonHovered; -pub struct ToggleButtonPressed; - -impl ButtonStateColors for ToggleButtonInactive { - const LABEL_COLOR: BinaryColor = BinaryColor::On; - const BORDER_COLOR: BinaryColor = BinaryColor::Off; - const BACKGROUND_COLOR: BinaryColor = BinaryColor::Off; -} - -impl ButtonStateColors for ToggleButtonIdle { - const LABEL_COLOR: BinaryColor = BinaryColor::On; - const BORDER_COLOR: BinaryColor = BinaryColor::On; - const BACKGROUND_COLOR: BinaryColor = BinaryColor::Off; -} - -impl ButtonStateColors for ToggleButtonHovered { - const LABEL_COLOR: BinaryColor = BinaryColor::On; - const BORDER_COLOR: BinaryColor = BinaryColor::Off; - const BACKGROUND_COLOR: BinaryColor = BinaryColor::Off; -} - -impl ButtonStateColors for ToggleButtonPressed { - const LABEL_COLOR: BinaryColor = BinaryColor::Off; - const BORDER_COLOR: BinaryColor = BinaryColor::Off; - const BACKGROUND_COLOR: BinaryColor = BinaryColor::On; -} - -pub struct ToggleButtonInactiveChecked; -pub struct ToggleButtonIdleChecked; -pub struct ToggleButtonHoveredChecked; -pub struct ToggleButtonPressedChecked; - -impl ButtonStateColors for ToggleButtonInactiveChecked { - const LABEL_COLOR: BinaryColor = BinaryColor::On; - const BORDER_COLOR: BinaryColor = BinaryColor::Off; - const BACKGROUND_COLOR: BinaryColor = BinaryColor::Off; -} - -impl ButtonStateColors for ToggleButtonIdleChecked { - const LABEL_COLOR: BinaryColor = BinaryColor::Off; - const BORDER_COLOR: BinaryColor = BinaryColor::Off; - const BACKGROUND_COLOR: BinaryColor = BinaryColor::On; -} - -impl ButtonStateColors for ToggleButtonHoveredChecked { - const LABEL_COLOR: BinaryColor = BinaryColor::On; - const BORDER_COLOR: BinaryColor = BinaryColor::On; - const BACKGROUND_COLOR: BinaryColor = BinaryColor::Off; -} - -impl ButtonStateColors for ToggleButtonPressedChecked { - const LABEL_COLOR: BinaryColor = BinaryColor::On; - const BORDER_COLOR: BinaryColor = BinaryColor::Off; - const BACKGROUND_COLOR: BinaryColor = BinaryColor::Off; -} - -pub struct ToggleButtonStyle; -impl ToggleButtonStyleTrait for ToggleButtonStyle { - type Inactive = ToggleButtonInactive; - type Idle = ToggleButtonIdle; - type Hovered = ToggleButtonHovered; - type Pressed = ToggleButtonPressed; - - type InactiveChecked = ToggleButtonInactiveChecked; - type IdleChecked = ToggleButtonIdleChecked; - type HoveredChecked = ToggleButtonHoveredChecked; - type PressedChecked = ToggleButtonPressedChecked; - - const FONT: MonoFont<'static> = FONT_6X10; -} diff --git a/backend-embedded-graphics/src/themes/default/toggle_button/mod.rs b/backend-embedded-graphics/src/themes/default/toggle_button/mod.rs deleted file mode 100644 index aeffe79..0000000 --- a/backend-embedded-graphics/src/themes/default/toggle_button/mod.rs +++ /dev/null @@ -1,207 +0,0 @@ -use embedded_graphics::{ - mono_font::{MonoFont, MonoTextStyle}, - prelude::PixelColor, -}; -use embedded_gui::{ - state::WidgetState, - widgets::{ - background::{Background, BackgroundProperties}, - border::{Border, BorderProperties}, - fill::{Center, FillParent, HorizontalAndVertical}, - label::Label, - spacing::Spacing, - toggle::Toggle, - }, -}; - -use crate::{ - themes::default::{button::ButtonStateColors, DefaultTheme}, - widgets::{ - background::BackgroundStyle, - border::BorderStyle, - label::{ascii::LabelConstructor, LabelStyle, LabelStyling, MonoFontLabelStyling}, - }, -}; - -pub mod binary_color; -pub mod rgb; - -pub trait ToggleButtonStyle { - type Inactive: ButtonStateColors; - type Idle: ButtonStateColors; - type Hovered: ButtonStateColors; - type Pressed: ButtonStateColors; - - type InactiveChecked: ButtonStateColors; - type IdleChecked: ButtonStateColors; - type HoveredChecked: ButtonStateColors; - type PressedChecked: ButtonStateColors; - - const FONT: MonoFont<'static>; - - fn apply_label(label: &mut Label, state: WidgetState) - where - Label: LabelStyling, - { - if state.has_state(Toggle::STATE_INACTIVE) { - if state.has_state(Toggle::STATE_CHECKED) { - Self::InactiveChecked::apply_label(label); - } else { - Self::Inactive::apply_label(label); - } - } else if state.has_state(Toggle::STATE_HOVERED) { - if state.has_state(Toggle::STATE_CHECKED) { - Self::HoveredChecked::apply_label(label); - } else { - Self::Hovered::apply_label(label); - } - } else if state.has_state(Toggle::STATE_PRESSED) { - if state.has_state(Toggle::STATE_CHECKED) { - Self::PressedChecked::apply_label(label); - } else { - Self::Pressed::apply_label(label); - } - } else { - if state.has_state(Toggle::STATE_CHECKED) { - Self::IdleChecked::apply_label(label); - } else { - Self::Idle::apply_label(label); - } - } - } - - fn apply_border(border: &mut Border, state: WidgetState) - where - T: BorderProperties, - { - if state.has_state(Toggle::STATE_INACTIVE) { - if state.has_state(Toggle::STATE_CHECKED) { - Self::InactiveChecked::apply_border(border); - } else { - Self::Inactive::apply_border(border); - } - } else if state.has_state(Toggle::STATE_HOVERED) { - if state.has_state(Toggle::STATE_CHECKED) { - Self::HoveredChecked::apply_border(border); - } else { - Self::Hovered::apply_border(border); - } - } else if state.has_state(Toggle::STATE_PRESSED) { - if state.has_state(Toggle::STATE_CHECKED) { - Self::PressedChecked::apply_border(border); - } else { - Self::Pressed::apply_border(border); - } - } else { - if state.has_state(Toggle::STATE_CHECKED) { - Self::IdleChecked::apply_border(border); - } else { - Self::Idle::apply_border(border); - } - } - } - - fn apply_background(background: &mut Background, state: WidgetState) - where - T: BackgroundProperties, - { - if state.has_state(Toggle::STATE_INACTIVE) { - if state.has_state(Toggle::STATE_CHECKED) { - Self::InactiveChecked::apply_background(background); - } else { - Self::Inactive::apply_background(background); - } - } else if state.has_state(Toggle::STATE_HOVERED) { - if state.has_state(Toggle::STATE_CHECKED) { - Self::HoveredChecked::apply_background(background); - } else { - Self::Hovered::apply_background(background); - } - } else if state.has_state(Toggle::STATE_PRESSED) { - if state.has_state(Toggle::STATE_CHECKED) { - Self::PressedChecked::apply_background(background); - } else { - Self::Pressed::apply_background(background); - } - } else { - if state.has_state(Toggle::STATE_CHECKED) { - Self::IdleChecked::apply_background(background); - } else { - Self::Idle::apply_background(background); - } - } - } -} - -pub type StyledToggleButton<'a, C> = Toggle< - Background< - Border>>>, BorderStyle>, - BackgroundStyle, - >, - (), - true, ->; - -pub fn styled_toggle_button(label: &'static str) -> StyledToggleButton -where - C: DefaultTheme, - S: ToggleButtonStyle, - BorderStyle: Default, - BackgroundStyle: Default, -{ - Toggle::new( - Background::new( - Border::new( - Spacing::new( - Label::new(label) - .font(&S::FONT) - .on_state_changed(S::apply_label), - ) - .all(1), - ) - .on_state_changed(S::apply_border), - ) - .on_state_changed(S::apply_background), - ) -} - -pub type StyledToggleButtonStretched<'a, C> = Toggle< - Background< - Border< - FillParent< - Label<&'static str, LabelStyle>>, - HorizontalAndVertical, - Center, - Center, - >, - BorderStyle, - >, - BackgroundStyle, - >, - (), - true, ->; - -pub fn styled_toggle_button_stretched(label: &'static str) -> StyledToggleButtonStretched -where - C: DefaultTheme, - S: ToggleButtonStyle, - BorderStyle: Default, - BackgroundStyle: Default, -{ - Toggle::new( - Background::new( - Border::new( - FillParent::both( - Label::new(label) - .font(&S::FONT) - .on_state_changed(S::apply_label), - ) - .align_horizontal(Center) - .align_vertical(Center), - ) - .on_state_changed(S::apply_border), - ) - .on_state_changed(S::apply_background), - ) -} diff --git a/backend-embedded-graphics/src/themes/default/toggle_button/rgb.rs b/backend-embedded-graphics/src/themes/default/toggle_button/rgb.rs deleted file mode 100644 index 4323df8..0000000 --- a/backend-embedded-graphics/src/themes/default/toggle_button/rgb.rs +++ /dev/null @@ -1,110 +0,0 @@ -use core::marker::PhantomData; - -use embedded_graphics::{ - mono_font::{ascii::FONT_6X10, MonoFont}, - pixelcolor::WebColors, -}; - -use crate::themes::default::toggle_button::{ - ButtonStateColors, ToggleButtonStyle as ToggleButtonStyleTrait, -}; - -pub struct ToggleButtonInactive(PhantomData); -pub struct ToggleButtonIdle(PhantomData); -pub struct ToggleButtonHovered(PhantomData); -pub struct ToggleButtonPressed(PhantomData); - -impl ButtonStateColors for ToggleButtonInactive -where - C: WebColors, -{ - const LABEL_COLOR: C = C::CSS_LIGHT_GRAY; - const BORDER_COLOR: C = C::CSS_DIM_GRAY; - const BACKGROUND_COLOR: C = C::CSS_DIM_GRAY; -} - -impl ButtonStateColors for ToggleButtonIdle -where - C: WebColors, -{ - const LABEL_COLOR: C = C::WHITE; - const BORDER_COLOR: C = C::CSS_SLATE_GRAY; - const BACKGROUND_COLOR: C = C::CSS_SLATE_GRAY; -} - -impl ButtonStateColors for ToggleButtonHovered -where - C: WebColors, -{ - const LABEL_COLOR: C = C::WHITE; - const BORDER_COLOR: C = C::CSS_LIGHT_SLATE_GRAY; - const BACKGROUND_COLOR: C = C::CSS_LIGHT_SLATE_GRAY; -} - -impl ButtonStateColors for ToggleButtonPressed -where - C: WebColors, -{ - const LABEL_COLOR: C = C::WHITE; - const BORDER_COLOR: C = C::CSS_STEEL_BLUE; - const BACKGROUND_COLOR: C = C::CSS_STEEL_BLUE; -} - -pub struct ToggleButtonInactiveChecked(PhantomData); -pub struct ToggleButtonIdleChecked(PhantomData); -pub struct ToggleButtonHoveredChecked(PhantomData); -pub struct ToggleButtonPressedChecked(PhantomData); - -impl ButtonStateColors for ToggleButtonInactiveChecked -where - C: WebColors, -{ - const LABEL_COLOR: C = C::CSS_LIGHT_GRAY; - const BORDER_COLOR: C = C::CSS_DIM_GRAY; - const BACKGROUND_COLOR: C = C::CSS_DIM_GRAY; -} - -impl ButtonStateColors for ToggleButtonIdleChecked -where - C: WebColors, -{ - const LABEL_COLOR: C = C::WHITE; - const BORDER_COLOR: C = C::CSS_STEEL_BLUE; - const BACKGROUND_COLOR: C = C::CSS_STEEL_BLUE; -} - -impl ButtonStateColors for ToggleButtonHoveredChecked -where - C: WebColors, -{ - const LABEL_COLOR: C = C::WHITE; - const BORDER_COLOR: C = C::CSS_DODGER_BLUE; - const BACKGROUND_COLOR: C = C::CSS_DODGER_BLUE; -} - -impl ButtonStateColors for ToggleButtonPressedChecked -where - C: WebColors, -{ - const LABEL_COLOR: C = C::WHITE; - const BORDER_COLOR: C = C::CSS_LIGHT_STEEL_BLUE; - const BACKGROUND_COLOR: C = C::CSS_LIGHT_STEEL_BLUE; -} - -pub struct ToggleButtonStyle(PhantomData); -impl ToggleButtonStyleTrait for ToggleButtonStyle -where - C: WebColors, -{ - type Inactive = ToggleButtonInactive; - type Idle = ToggleButtonIdle; - type Hovered = ToggleButtonHovered; - type Pressed = ToggleButtonPressed; - - type InactiveChecked = ToggleButtonInactiveChecked; - type IdleChecked = ToggleButtonIdleChecked; - type HoveredChecked = ToggleButtonHoveredChecked; - type PressedChecked = ToggleButtonPressedChecked; - - const FONT: MonoFont<'static> = FONT_6X10; -} diff --git a/backend-embedded-graphics/src/themes/mod.rs b/backend-embedded-graphics/src/themes/mod.rs index e32703d..38883ee 100644 --- a/backend-embedded-graphics/src/themes/mod.rs +++ b/backend-embedded-graphics/src/themes/mod.rs @@ -1,12 +1 @@ -use embedded_graphics::pixelcolor::PixelColor; - -// TODO: rename DefaultTheme to LightTheme and add DarkTheme -pub mod default; - -// TODO: merge this into DefaultTheme. This would allow defining different color schemes for the same -// color space. -pub trait Theme: PixelColor { - const TEXT_COLOR: Self; - const BORDER_COLOR: Self; - const BACKGROUND_COLOR: Self; -} +pub mod basic; diff --git a/backend-embedded-graphics/src/widgets/background.rs b/backend-embedded-graphics/src/widgets/background.rs index c6f6398..1bcf086 100644 --- a/backend-embedded-graphics/src/widgets/background.rs +++ b/backend-embedded-graphics/src/widgets/background.rs @@ -13,7 +13,7 @@ use embedded_gui::{ WidgetRenderer, }; -use crate::{themes::Theme, EgCanvas, ToRectangle}; +use crate::{EgCanvas, ToRectangle}; pub struct BackgroundStyle where @@ -26,19 +26,12 @@ impl BackgroundStyle where C: PixelColor, { - fn build_style(&self) -> PrimitiveStyle { - PrimitiveStyleBuilder::new().fill_color(self.color).build() + pub fn new(color: C) -> Self { + Self { color } } -} -impl Default for BackgroundStyle -where - C: Theme, -{ - fn default() -> Self { - Self { - color: C::BACKGROUND_COLOR, - } + fn build_style(&self) -> PrimitiveStyle { + PrimitiveStyleBuilder::new().fill_color(self.color).build() } } @@ -53,7 +46,6 @@ where } } -// TODO: draw target should be clipped to widget's bounds, so this can be restored to Background impl WidgetRenderer> for Background> where W: Widget + WidgetRenderer>, diff --git a/backend-embedded-graphics/src/widgets/border.rs b/backend-embedded-graphics/src/widgets/border.rs index aad5ca0..9c61592 100644 --- a/backend-embedded-graphics/src/widgets/border.rs +++ b/backend-embedded-graphics/src/widgets/border.rs @@ -13,20 +13,24 @@ use embedded_gui::{ WidgetRenderer, }; -use crate::{themes::Theme, EgCanvas, ToRectangle}; +use crate::{EgCanvas, ToRectangle}; pub struct BorderStyle where C: PixelColor, { - color: C, - width: u32, + pub color: C, + pub width: u32, } impl BorderStyle where C: PixelColor, { + pub fn new(color: C, width: u32) -> Self { + Self { color, width } + } + fn build_style(&self) -> PrimitiveStyle { PrimitiveStyleBuilder::new() .stroke_alignment(StrokeAlignment::Inside) @@ -36,18 +40,6 @@ where } } -impl Default for BorderStyle -where - C: Theme, -{ - fn default() -> Self { - Self { - color: C::BORDER_COLOR, - width: 1, - } - } -} - impl BorderProperties for BorderStyle where C: PixelColor, @@ -63,7 +55,6 @@ where } } -// TODO: draw target should be clipped to widget's bounds, so this can be restored to Border impl WidgetRenderer> for Border> where W: Widget + WidgetRenderer>, diff --git a/backend-embedded-graphics/src/widgets/graphical/checkbox.rs b/backend-embedded-graphics/src/widgets/graphical/checkbox.rs index 844079f..bb3ee81 100644 --- a/backend-embedded-graphics/src/widgets/graphical/checkbox.rs +++ b/backend-embedded-graphics/src/widgets/graphical/checkbox.rs @@ -13,7 +13,7 @@ use embedded_gui::{ WidgetRenderer, }; -use crate::{themes::Theme, EgCanvas, ToRectangle}; +use crate::{EgCanvas, ToRectangle}; pub struct CheckBoxStyle where @@ -48,22 +48,6 @@ where } } -impl Default for CheckBoxStyle -where - C: Theme, -{ - fn default() -> Self { - Self { - background_color: C::BACKGROUND_COLOR, - border_color: C::BORDER_COLOR, - checkmark_color: C::BORDER_COLOR, - line_width: 1, - box_size: 9, - is_checked: false, - } - } -} - impl CheckBoxProperties for CheckBoxStyle where C: PixelColor, diff --git a/backend-embedded-graphics/src/widgets/graphical/radio.rs b/backend-embedded-graphics/src/widgets/graphical/radio.rs index 54edbb0..5d839eb 100644 --- a/backend-embedded-graphics/src/widgets/graphical/radio.rs +++ b/backend-embedded-graphics/src/widgets/graphical/radio.rs @@ -13,7 +13,7 @@ use embedded_gui::{ WidgetRenderer, }; -use crate::{themes::Theme, EgCanvas, ToPoint}; +use crate::{EgCanvas, ToPoint}; pub struct RadioButtonStyle where @@ -48,22 +48,6 @@ where } } -impl Default for RadioButtonStyle -where - C: Theme, -{ - fn default() -> Self { - Self { - background_color: C::BACKGROUND_COLOR, - border_color: C::BORDER_COLOR, - checkmark_color: C::BORDER_COLOR, - line_width: 1, - box_size: 9, - is_selected: false, - } - } -} - impl RadioButtonProperties for RadioButtonStyle where C: PixelColor, diff --git a/backend-embedded-graphics/src/widgets/label.rs b/backend-embedded-graphics/src/widgets/label.rs index c002a31..46f798a 100644 --- a/backend-embedded-graphics/src/widgets/label.rs +++ b/backend-embedded-graphics/src/widgets/label.rs @@ -28,6 +28,10 @@ where C: PixelColor, T: TextRenderer + CharacterStyle, { + pub fn new(renderer: T) -> LabelStyle { + Self { renderer } + } + /// Customize the text color pub fn text_color(&mut self, text_color: C) { self.renderer.set_text_color(Some(text_color)); @@ -155,65 +159,3 @@ where .map(|_| ()) } } - -macro_rules! label_for_charset { - ($charset:ident, $font:ident) => { - pub mod $charset { - use embedded_graphics::{ - mono_font::{$charset, MonoTextStyle}, - pixelcolor::PixelColor, - }; - use embedded_gui::{geometry::BoundingBox, widgets::label::Label}; - - use crate::{themes::Theme, widgets::label::LabelStyle}; - - pub trait LabelConstructor<'a, S, C> - where - S: AsRef, - C: PixelColor, - { - fn new(text: S) -> Label>>; - } - - impl<'a, C, S> LabelConstructor<'a, S, C> for Label>> - where - S: AsRef, - C: PixelColor + Theme, - { - fn new(text: S) -> Self { - Label { - parent_index: 0, - text, - label_properties: LabelStyle { - renderer: MonoTextStyle::new( - &$charset::$font, - ::TEXT_COLOR, - ), - }, - bounds: BoundingBox::default(), - on_state_changed: |_, _| (), - } - } - } - } - }; - - ($charset:ident) => { - label_for_charset!($charset, FONT_6X10); - }; -} - -label_for_charset!(ascii); -label_for_charset!(iso_8859_1); -label_for_charset!(iso_8859_10); -label_for_charset!(iso_8859_13); -label_for_charset!(iso_8859_14); -label_for_charset!(iso_8859_15); -label_for_charset!(iso_8859_16); -label_for_charset!(iso_8859_2); -label_for_charset!(iso_8859_3); -label_for_charset!(iso_8859_4); -label_for_charset!(iso_8859_5); -label_for_charset!(iso_8859_7); -label_for_charset!(iso_8859_9); -label_for_charset!(jis_x0201, FONT_6X13); diff --git a/backend-embedded-graphics/src/widgets/text_block.rs b/backend-embedded-graphics/src/widgets/text_block.rs index a66db67..3395fc4 100644 --- a/backend-embedded-graphics/src/widgets/text_block.rs +++ b/backend-embedded-graphics/src/widgets/text_block.rs @@ -36,6 +36,14 @@ where C: PixelColor, T: TextRenderer + CharacterStyle, { + pub fn new(renderer: T) -> Self { + Self { + renderer, + horizontal: HorizontalAlignment::Left, + vertical: VerticalAlignment::Top, + } + } + /// Customize the text color pub fn text_color(&mut self, text_color: C) { self.renderer.set_text_color(Some(text_color)); @@ -233,69 +241,3 @@ where .map(|_| ()) } } - -macro_rules! textbox_for_charset { - ($charset:ident, $font:ident) => { - pub mod $charset { - use embedded_graphics::{ - mono_font::{$charset, MonoTextStyle}, - pixelcolor::PixelColor, - }; - use embedded_gui::{geometry::BoundingBox, widgets::text_block::TextBlock}; - use embedded_text::alignment::{HorizontalAlignment, VerticalAlignment}; - - use crate::{themes::Theme, widgets::text_block::TextBlockStyle}; - - pub trait TextBlockConstructor<'a, S, C> - where - S: AsRef, - C: PixelColor, - { - fn new(text: S) -> TextBlock>>; - } - - impl<'a, C, S> TextBlockConstructor<'a, S, C> - for TextBlock>> - where - S: AsRef, - C: PixelColor + Theme, - { - fn new(text: S) -> Self { - TextBlock { - parent_index: 0, - text, - label_properties: TextBlockStyle { - renderer: MonoTextStyle::new( - &$charset::$font, - ::TEXT_COLOR, - ), - horizontal: HorizontalAlignment::Left, - vertical: VerticalAlignment::Top, - }, - bounds: BoundingBox::default(), - on_state_changed: |_, _| (), - } - } - } - } - }; - - ($charset:ident) => { - textbox_for_charset!($charset, FONT_6X10); - }; -} - -textbox_for_charset!(ascii); -textbox_for_charset!(iso_8859_1); -textbox_for_charset!(iso_8859_10); -textbox_for_charset!(iso_8859_13); -textbox_for_charset!(iso_8859_14); -textbox_for_charset!(iso_8859_15); -textbox_for_charset!(iso_8859_16); -textbox_for_charset!(iso_8859_2); -textbox_for_charset!(iso_8859_3); -textbox_for_charset!(iso_8859_4); -textbox_for_charset!(iso_8859_5); -textbox_for_charset!(iso_8859_7); -textbox_for_charset!(iso_8859_9); -textbox_for_charset!(jis_x0201, FONT_6X13); diff --git a/backend-embedded-graphics/src/widgets/text_box.rs b/backend-embedded-graphics/src/widgets/text_box.rs index cddcdaa..7323824 100644 --- a/backend-embedded-graphics/src/widgets/text_box.rs +++ b/backend-embedded-graphics/src/widgets/text_box.rs @@ -50,6 +50,16 @@ where C: PixelColor, T: TextRenderer + CharacterStyle, { + pub fn new(renderer: T) -> Self { + Self { + renderer, + horizontal: HorizontalAlignment::Left, + vertical: VerticalAlignment::Top, + cursor: Cursor::default(), + cursor_color: None, + } + } + /// Customize the text color pub fn text_color(&mut self, text_color: C) { self.renderer.set_text_color(Some(text_color)); @@ -361,90 +371,3 @@ where } } } - -macro_rules! textbox_for_charset { - ($charset:ident, $font:ident) => { - pub mod $charset { - use core::borrow::BorrowMut; - use embedded_graphics::{ - mono_font::{$charset, MonoTextStyle}, - pixelcolor::PixelColor, - }; - use embedded_gui::{ - data::WidgetData, - geometry::BoundingBox, - state::WidgetState, - widgets::{ - text_box::{TextBox, TextBoxFields}, - utils::WidgetDataHolder, - }, - }; - use embedded_text::alignment::{HorizontalAlignment, VerticalAlignment}; - use heapless::String; - - use crate::{ - themes::Theme, - widgets::text_box::{plugin::Cursor, TextBoxStyle}, - }; - - pub trait TextBoxConstructor<'a, B, C, D, const N: usize> - where - B: BorrowMut>, - D: WidgetData, - C: PixelColor, - { - fn new(text: B) -> TextBox>, D, N>; - } - - impl<'a, B, C, const N: usize> TextBoxConstructor<'a, B, C, (), N> - for TextBox>, (), N> - where - C: PixelColor + Theme, - B: BorrowMut>, - { - fn new(text: B) -> Self { - TextBox { - fields: TextBoxFields { - state: WidgetState::default(), - parent_index: 0, - text, - label_properties: TextBoxStyle { - renderer: MonoTextStyle::new( - &$charset::$font, - ::TEXT_COLOR, - ), - horizontal: HorizontalAlignment::Left, - vertical: VerticalAlignment::Top, - cursor: Cursor::default(), - cursor_color: Some(::TEXT_COLOR), - }, - bounds: BoundingBox::default(), - on_text_changed: |_, _| (), - on_parent_state_changed: |_, _| (), - }, - data_holder: WidgetDataHolder::default(), - } - } - } - } - }; - - ($charset:ident) => { - textbox_for_charset!($charset, FONT_6X10); - }; -} - -textbox_for_charset!(ascii); -textbox_for_charset!(iso_8859_1); -textbox_for_charset!(iso_8859_10); -textbox_for_charset!(iso_8859_13); -textbox_for_charset!(iso_8859_14); -textbox_for_charset!(iso_8859_15); -textbox_for_charset!(iso_8859_16); -textbox_for_charset!(iso_8859_2); -textbox_for_charset!(iso_8859_3); -textbox_for_charset!(iso_8859_4); -textbox_for_charset!(iso_8859_5); -textbox_for_charset!(iso_8859_7); -textbox_for_charset!(iso_8859_9); -textbox_for_charset!(jis_x0201, FONT_6X13); diff --git a/examples/calculator.rs b/examples/calculator.rs index cb1e497..fd59ea3 100644 --- a/examples/calculator.rs +++ b/examples/calculator.rs @@ -1,8 +1,8 @@ use std::{fmt::Write, thread, time::Duration}; use backend_embedded_graphics::{ - themes::{default::DefaultTheme, Theme}, - widgets::label::{ascii::LabelConstructor, MonoFontLabelStyling}, + themes::basic::{binary_color::LightTheme, BasicTheme}, + widgets::label::MonoFontLabelStyling, EgCanvas, }; use embedded_graphics::{ @@ -20,7 +20,6 @@ use embedded_gui::{ prelude::*, widgets::{ fill::{FillParent, Right}, - label::Label, layouts::linear::{Column, Row}, spacing::Spacing, }, @@ -210,7 +209,7 @@ fn main() { .add( Spacing::new( FillParent::horizontal( - Label::new(String::<11>::new()) + LightTheme::label(String::<11>::new()) .font(&FONT_10X20) .bind(&calculator) .on_data_changed(|label, calc| { @@ -226,19 +225,19 @@ fn main() { Row::new() .spacing(1) .add( - DefaultTheme::primary_button_stretched("CE") + LightTheme::primary_button_stretched("CE") .bind(&calculator) .on_clicked(|calculator| calculator.clear()), ) .weight(2) .add( - DefaultTheme::secondary_button_stretched("<") + LightTheme::secondary_button_stretched("<") .bind(&calculator) .on_clicked(|calculator| calculator.delete_digit()), ) .weight(1) .add( - DefaultTheme::primary_button_stretched("/") + LightTheme::primary_button_stretched("/") .bind(&calculator) .on_clicked(|calculator| calculator.set_op(Op::Divide)), ) @@ -249,25 +248,25 @@ fn main() { Row::new() .spacing(1) .add( - DefaultTheme::secondary_button_stretched("7") + LightTheme::secondary_button_stretched("7") .bind(&calculator) .on_clicked(|calculator| calculator.add_digit(7)), ) .weight(1) .add( - DefaultTheme::secondary_button_stretched("8") + LightTheme::secondary_button_stretched("8") .bind(&calculator) .on_clicked(|calculator| calculator.add_digit(8)), ) .weight(1) .add( - DefaultTheme::secondary_button_stretched("9") + LightTheme::secondary_button_stretched("9") .bind(&calculator) .on_clicked(|calculator| calculator.add_digit(9)), ) .weight(1) .add( - DefaultTheme::primary_button_stretched("x") + LightTheme::primary_button_stretched("x") .bind(&calculator) .on_clicked(|calculator| calculator.set_op(Op::Multiply)), ) @@ -278,25 +277,25 @@ fn main() { Row::new() .spacing(1) .add( - DefaultTheme::secondary_button_stretched("4") + LightTheme::secondary_button_stretched("4") .bind(&calculator) .on_clicked(|calculator| calculator.add_digit(4)), ) .weight(1) .add( - DefaultTheme::secondary_button_stretched("5") + LightTheme::secondary_button_stretched("5") .bind(&calculator) .on_clicked(|calculator| calculator.add_digit(5)), ) .weight(1) .add( - DefaultTheme::secondary_button_stretched("6") + LightTheme::secondary_button_stretched("6") .bind(&calculator) .on_clicked(|calculator| calculator.add_digit(6)), ) .weight(1) .add( - DefaultTheme::primary_button_stretched("-") + LightTheme::primary_button_stretched("-") .bind(&calculator) .on_clicked(|calculator| calculator.set_op(Op::Subtract)), ) @@ -307,25 +306,25 @@ fn main() { Row::new() .spacing(1) .add( - DefaultTheme::secondary_button_stretched("1") + LightTheme::secondary_button_stretched("1") .bind(&calculator) .on_clicked(|calculator| calculator.add_digit(1)), ) .weight(1) .add( - DefaultTheme::secondary_button_stretched("2") + LightTheme::secondary_button_stretched("2") .bind(&calculator) .on_clicked(|calculator| calculator.add_digit(2)), ) .weight(1) .add( - DefaultTheme::secondary_button_stretched("3") + LightTheme::secondary_button_stretched("3") .bind(&calculator) .on_clicked(|calculator| calculator.add_digit(3)), ) .weight(1) .add( - DefaultTheme::primary_button_stretched("+") + LightTheme::primary_button_stretched("+") .bind(&calculator) .on_clicked(|calculator| calculator.set_op(Op::Add)), ) @@ -336,13 +335,13 @@ fn main() { Row::new() .spacing(1) .add( - DefaultTheme::secondary_button_stretched("0") + LightTheme::secondary_button_stretched("0") .bind(&calculator) .on_clicked(|calculator| calculator.add_digit(0)), ) .weight(3) .add( - DefaultTheme::primary_button_stretched("=") + LightTheme::primary_button_stretched("=") .bind(&calculator) .on_clicked(|calculator| calculator.update()), ) @@ -365,10 +364,7 @@ fn main() { let mut window = SimWindow::new("GUI demonstration", &output_settings); loop { - gui.canvas - .target - .clear(BinaryColor::BACKGROUND_COLOR) - .unwrap(); + gui.canvas.target.clear(BinaryColor::Off).unwrap(); gui.update(); gui.measure(); diff --git a/examples/calculator_rgb.rs b/examples/calculator_rgb.rs index 66f9db8..addbc0b 100644 --- a/examples/calculator_rgb.rs +++ b/examples/calculator_rgb.rs @@ -1,15 +1,15 @@ use std::{fmt::Write, thread, time::Duration}; use backend_embedded_graphics::{ - themes::{default::DefaultTheme, Theme}, - widgets::label::{ascii::LabelConstructor, MonoFontLabelStyling}, + themes::basic::{rgb888::LightTheme, BasicTheme}, + widgets::{background::BackgroundStyle, label::MonoFontLabelStyling}, EgCanvas, }; use embedded_graphics::{ draw_target::DrawTarget, mono_font::ascii::FONT_10X20, pixelcolor::{Rgb888, WebColors}, - prelude::Size as EgSize, + prelude::{RgbColor, Size as EgSize}, }; use embedded_graphics_simulator::{ sdl2::MouseButton, OutputSettingsBuilder, SimulatorDisplay, SimulatorEvent, Window as SimWindow, @@ -22,7 +22,6 @@ use embedded_gui::{ widgets::{ background::Background, fill::{FillParent, Right}, - label::Label, layouts::linear::{Column, Row}, spacing::Spacing, }, @@ -227,41 +226,39 @@ fn main() { EgCanvas::new(display), Column::new() .spacing(1) - .add( - Background::new( - Spacing::new( - FillParent::horizontal( - Label::new(String::<11>::new()) - .font(&FONT_10X20) - .bind(&calculator) - .on_data_changed(|label, calc| { - label.text.clear(); - write!(label.text, "{}", calc.current).unwrap(); - }), - ) - .align_horizontal(Right), + .add(Background::with_style( + Spacing::new( + FillParent::horizontal( + LightTheme::label(String::<11>::new()) + .font(&FONT_10X20) + .bind(&calculator) + .on_data_changed(|label, calc| { + label.text.clear(); + write!(label.text, "{}", calc.current).unwrap(); + }), ) - .all(4), + .align_horizontal(Right), ) - .background_color(Rgb888::CSS_DARK_GRAY), - ) + .all(4), + BackgroundStyle::new(Rgb888::CSS_DARK_GRAY), + )) .add( Row::new() .spacing(1) .add( - DefaultTheme::primary_button_stretched("CE") + LightTheme::primary_button_stretched("CE") .bind(&calculator) .on_clicked(|calculator| calculator.clear()), ) .weight(2) .add( - DefaultTheme::secondary_button_stretched("<") + LightTheme::secondary_button_stretched("<") .bind(&calculator) .on_clicked(|calculator| calculator.delete_digit()), ) .weight(1) .add( - DefaultTheme::primary_button_stretched("/") + LightTheme::primary_button_stretched("/") .bind(&calculator) .on_clicked(|calculator| calculator.set_op(Op::Divide)), ) @@ -272,25 +269,25 @@ fn main() { Row::new() .spacing(1) .add( - DefaultTheme::secondary_button_stretched("7") + LightTheme::secondary_button_stretched("7") .bind(&calculator) .on_clicked(|calculator| calculator.add_digit(7)), ) .weight(1) .add( - DefaultTheme::secondary_button_stretched("8") + LightTheme::secondary_button_stretched("8") .bind(&calculator) .on_clicked(|calculator| calculator.add_digit(8)), ) .weight(1) .add( - DefaultTheme::secondary_button_stretched("9") + LightTheme::secondary_button_stretched("9") .bind(&calculator) .on_clicked(|calculator| calculator.add_digit(9)), ) .weight(1) .add( - DefaultTheme::primary_button_stretched("x") + LightTheme::primary_button_stretched("x") .bind(&calculator) .on_clicked(|calculator| calculator.set_op(Op::Multiply)), ) @@ -301,25 +298,25 @@ fn main() { Row::new() .spacing(1) .add( - DefaultTheme::secondary_button_stretched("4") + LightTheme::secondary_button_stretched("4") .bind(&calculator) .on_clicked(|calculator| calculator.add_digit(4)), ) .weight(1) .add( - DefaultTheme::secondary_button_stretched("5") + LightTheme::secondary_button_stretched("5") .bind(&calculator) .on_clicked(|calculator| calculator.add_digit(5)), ) .weight(1) .add( - DefaultTheme::secondary_button_stretched("6") + LightTheme::secondary_button_stretched("6") .bind(&calculator) .on_clicked(|calculator| calculator.add_digit(6)), ) .weight(1) .add( - DefaultTheme::primary_button_stretched("-") + LightTheme::primary_button_stretched("-") .bind(&calculator) .on_clicked(|calculator| calculator.set_op(Op::Subtract)), ) @@ -330,25 +327,25 @@ fn main() { Row::new() .spacing(1) .add( - DefaultTheme::secondary_button_stretched("1") + LightTheme::secondary_button_stretched("1") .bind(&calculator) .on_clicked(|calculator| calculator.add_digit(1)), ) .weight(1) .add( - DefaultTheme::secondary_button_stretched("2") + LightTheme::secondary_button_stretched("2") .bind(&calculator) .on_clicked(|calculator| calculator.add_digit(2)), ) .weight(1) .add( - DefaultTheme::secondary_button_stretched("3") + LightTheme::secondary_button_stretched("3") .bind(&calculator) .on_clicked(|calculator| calculator.add_digit(3)), ) .weight(1) .add( - DefaultTheme::primary_button_stretched("+") + LightTheme::primary_button_stretched("+") .bind(&calculator) .on_clicked(|calculator| calculator.set_op(Op::Add)), ) @@ -359,13 +356,13 @@ fn main() { Row::new() .spacing(1) .add( - DefaultTheme::secondary_button_stretched("0") + LightTheme::secondary_button_stretched("0") .bind(&calculator) .on_clicked(|calculator| calculator.add_digit(0)), ) .weight(3) .add( - DefaultTheme::primary_button_stretched("=") + LightTheme::primary_button_stretched("=") .bind(&calculator) .on_clicked(|calculator| calculator.update()) .on_data_changed(|button, calculator| { @@ -389,7 +386,7 @@ fn main() { let mut window = SimWindow::new("GUI demonstration", &output_settings); loop { - gui.canvas.target.clear(Rgb888::BACKGROUND_COLOR).unwrap(); + gui.canvas.target.clear(Rgb888::BLACK).unwrap(); gui.update(); gui.measure(); diff --git a/examples/hello_world.rs b/examples/hello_world.rs index 4dc9acb..e159280 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -1,8 +1,8 @@ use std::{thread, time::Duration}; use backend_embedded_graphics::{ - themes::Theme, - widgets::{background::BackgroundStyle, border::BorderStyle, label::ascii::LabelConstructor}, + themes::basic::{binary_color::LightTheme, BasicTheme}, + widgets::{background::BackgroundStyle, border::BorderStyle}, EgCanvas, }; use embedded_graphics::{ @@ -23,7 +23,6 @@ use embedded_gui::{ border::Border, button::Button, fill::{Center, FillParent, HorizontalAndVertical}, - label::Label, layouts::linear::{Column, Row}, spacing::Spacing, }, @@ -114,16 +113,16 @@ fn button_with_style( >, > { Button::new( - Background::new( - Border::new( + Background::with_style( + Border::with_style( FillParent::both(inner) .align_horizontal(Center) .align_vertical(Center), + BorderStyle::new(BinaryColor::Off, 1), ) - .border_color(BinaryColor::Off) .on_state_changed(update_button_border), + BackgroundStyle::new(BinaryColor::Off), ) - .background_color(BinaryColor::Off) .on_state_changed(update_button_background), ) } @@ -138,14 +137,20 @@ fn main() { Column::new() .add( Row::new() - .add(FillParent::horizontal(Label::new("Hello,")).align_horizontal(Center)) + .add( + FillParent::horizontal(LightTheme::label("Hello,")) + .align_horizontal(Center), + ) .weight(1) - .add(FillParent::horizontal(Label::new("World!")).align_horizontal(Center)) + .add( + FillParent::horizontal(LightTheme::label("World!")) + .align_horizontal(Center), + ) .weight(1), ) .add( Spacing::new( - button_with_style(Label::new("Click me").bind(&flag).on_data_changed( + button_with_style(LightTheme::label("Click me").bind(&flag).on_data_changed( |widget, data| widget.text = if *data { "on" } else { "off" }, )) .bind(&flag) @@ -164,10 +169,7 @@ fn main() { let mut window = SimWindow::new("GUI demonstration", &output_settings); loop { - gui.canvas - .target - .clear(BinaryColor::BACKGROUND_COLOR) - .unwrap(); + gui.canvas.target.clear(BinaryColor::Off).unwrap(); gui.update(); gui.measure(); diff --git a/examples/kitchen_sink.rs b/examples/kitchen_sink.rs index e29462a..552d01b 100644 --- a/examples/kitchen_sink.rs +++ b/examples/kitchen_sink.rs @@ -1,13 +1,11 @@ use std::{fmt::Write, thread, time::Duration}; use backend_embedded_graphics::{ - themes::{default::DefaultTheme, Theme}, + themes::basic::{binary_color::LightTheme, BasicTheme}, widgets::{ - label::ascii::LabelConstructor, - text_block::{ - ascii::TextBlockConstructor, HorizontalAlignment, TextBlockStyling, VerticalAlignment, - }, - text_box::{ascii::TextBoxConstructor, TextBoxStyling}, + border::BorderStyle, + text_block::{HorizontalAlignment, TextBlockStyling, VerticalAlignment}, + text_box::TextBoxStyling, }, EgCanvas, }; @@ -26,7 +24,6 @@ use embedded_gui::{ widgets::{ border::Border, fill::FillParent, - label::Label, layouts::{ frame::Frame, linear::{Column, Row}, @@ -34,8 +31,6 @@ use embedded_gui::{ scroll::Scroll, slider::ScrollbarConnector, spacing::Spacing, - text_block::TextBlock, - text_box::TextBox, visibility::Visibility, }, }; @@ -222,28 +217,28 @@ fn main() { let tabs = Row::new() .spacing(1) .add( - DefaultTheme::toggle_button("Multiline text") + LightTheme::toggle_button("Multiline text") .disallow_manual_uncheck() .bind(&page) .on_selected_changed(|_, page| *page = Page::TextBlock) .on_data_changed(|toggle, data| toggle.set_checked(*data == Page::TextBlock)), ) .add( - DefaultTheme::toggle_button("Checkables") + LightTheme::toggle_button("Checkables") .disallow_manual_uncheck() .bind(&page) .on_selected_changed(|_, page| *page = Page::Check) .on_data_changed(|toggle, data| toggle.set_checked(*data == Page::Check)), ) .add( - DefaultTheme::toggle_button("Sliders") + LightTheme::toggle_button("Sliders") .disallow_manual_uncheck() .bind(&page) .on_selected_changed(|_, page| *page = Page::Slider) .on_data_changed(|toggle, data| toggle.set_checked(*data == Page::Slider)), ) .add( - DefaultTheme::toggle_button("Scrolling") + LightTheme::toggle_button("Scrolling") .disallow_manual_uncheck() .bind(&page) .on_selected_changed(|_, page| *page = Page::Scroll) @@ -253,20 +248,23 @@ fn main() { let text_block_page = Row::new() .spacing(1) .add( - Column::new().add(Label::new("TextBlock")).add(Border::new( - TextBlock::new( + Column::new() + .add(LightTheme::label("TextBlock")) + .add(Border::with_style( + LightTheme::text_block( "Some \x1b[4mstylish\x1b[24m multiline text that expands the widget vertically", ) .horizontal_alignment(HorizontalAlignment::Center) .vertical_alignment(VerticalAlignment::Middle), + BorderStyle::new(BinaryColor::On, 1), )), ) .weight(1) .add( Column::new() - .add(Label::new("TextBox")) - .add(Border::new( - TextBox::new(String::<100>::from( + .add(LightTheme::label("TextBox")) + .add(Border::with_style( + LightTheme::text_box(String::<100>::from( "A TextBox with editable content. Click me and start typing!", )) .horizontal_alignment(HorizontalAlignment::Center) @@ -278,10 +276,11 @@ fn main() { } }) .on_text_changed(|reset, _text| *reset = false), + BorderStyle::new(BinaryColor::On, 1), )) .weight(1) .add( - DefaultTheme::primary_button("Clear") + LightTheme::primary_button("Clear") .bind(&text_reset) .on_clicked(|reset| *reset = true), ), @@ -290,51 +289,51 @@ fn main() { let checkables_page = Column::new() .spacing(1) - .add(Label::new("Checkboxes and radio buttons")) + .add(LightTheme::label("Checkboxes and radio buttons")) .add( - DefaultTheme::check_box("Check me") + LightTheme::check_box("Check me") .bind(&checkbox) .on_selected_changed(|checked, data| *data = checked) .on_data_changed(|checkbox, data| checkbox.set_checked(*data)), ) .add( - DefaultTheme::check_box("Inactive") + LightTheme::check_box("Inactive") .bind(&checkbox) .active(false) .on_data_changed(|checkbox, data| checkbox.set_checked(*data)), ) .add( - DefaultTheme::radio_button("Can't select me") + LightTheme::radio_button("Can't select me") .bind(&radio) .on_selected_changed(|_, data| *data = 0) .on_data_changed(|radio, data| radio.set_checked(*data == 0)) .active(false), ) .add( - DefaultTheme::radio_button("Select me") + LightTheme::radio_button("Select me") .bind(&radio) .on_selected_changed(|_, data| *data = 0) .on_data_changed(|radio, data| radio.set_checked(*data == 0)), ) .add( - DefaultTheme::radio_button("... or me!") + LightTheme::radio_button("... or me!") .bind(&radio) .on_selected_changed(|_, data| *data = 1) .on_data_changed(|radio, data| radio.set_checked(*data == 1)), ) .add( - DefaultTheme::toggle_button("Click me!") + LightTheme::toggle_button("Click me!") .bind(&toggle) .on_selected_changed(|selected, data| *data = selected) .on_data_changed(|toggle, data| toggle.set_checked(*data)), ) .add( - Visibility::new(Label::new("Toggle checked")) + Visibility::new(LightTheme::label("Toggle checked")) .bind(&toggle) .on_data_changed(|widget, data| widget.set_visible(*data)), ) .add( - DefaultTheme::primary_button("Reset") + LightTheme::primary_button("Reset") .bind(&checkables) .on_clicked(|data| { data.0.update(|data| *data = 0); @@ -345,11 +344,11 @@ fn main() { let sliders_page = Column::new() .spacing(1) - .add(Label::new("Numeric sliders")) + .add(LightTheme::label("Numeric sliders")) .add( Row::new() .add(FillParent::horizontal( - Label::new(String::<11>::from("0")) + LightTheme::label(String::<11>::from("0")) .bind(&slider1_data) .on_data_changed(|label, data| { label.text.clear(); @@ -359,7 +358,7 @@ fn main() { .weight(1) .add( Spacing::new( - DefaultTheme::slider(-100..=100) + LightTheme::slider(-100..=100) .bind(&slider1_data) .on_value_changed(|data, value| *data = value) .on_data_changed(|slider, data| slider.set_value(*data)), @@ -371,7 +370,7 @@ fn main() { .add( Row::new() .add(FillParent::horizontal( - Label::new(String::<11>::from("0")) + LightTheme::label(String::<11>::from("0")) .bind(&slider2_data) .on_data_changed(|label, data| { label.text.clear(); @@ -381,7 +380,7 @@ fn main() { .weight(1) .add( Spacing::new( - DefaultTheme::slider(0..=5) + LightTheme::slider(0..=5) .bind(&slider2_data) .on_value_changed(|data, value| *data = value) .on_data_changed(|slider, data| slider.set_value(*data)), @@ -391,9 +390,9 @@ fn main() { .weight(4), ) .add( - Row::new().add(Label::new("Inactive")).add( + Row::new().add(LightTheme::label("Inactive")).add( Spacing::new( - DefaultTheme::slider(0..=5) + LightTheme::slider(0..=5) .set_active(false) .bind(&slider2_data) .on_value_changed(|data, value| *data = value) @@ -403,7 +402,7 @@ fn main() { ), ) .add( - DefaultTheme::primary_button("Reset") + LightTheme::primary_button("Reset") .bind(&sliders) .on_clicked(|data| { data.0.update(|data| *data = 0); @@ -413,7 +412,7 @@ fn main() { let scrolling_page = Column::new() .add(FillParent::horizontal( - Label::new("Scroll down") + LightTheme::label("Scroll down") .bind(&scroll_data) .on_data_changed(|label, data| { label.text = if data.offset == data.maximum_offset { @@ -427,35 +426,35 @@ fn main() { )) .add( Row::new() - .add(Border::new( + .add(Border::with_style( Scroll::vertical( Spacing::new( Column::new() - .add(Label::new("S")) - .add(Label::new("c")) - .add(Label::new("r")) - .add(Label::new("o")) - .add(Label::new("l")) - .add(Label::new("o")) - .add(Label::new("l")) - .add(Label::new("o")) - .add(Label::new("l")) - .add(Label::new("o")) - .add(Label::new("l")) - .add(Label::new("o")) - .add(Label::new("l")) - .add(Label::new("o")) - .add(Label::new("l")) - .add(Label::new("o")) - .add(Label::new("l")) - .add(Label::new("o")) - .add(Label::new("l")) - .add(Label::new("o")) - .add(Label::new("l")) - .add(Label::new("o")) - .add(Label::new("Scrollolo :)")) + .add(LightTheme::label("S")) + .add(LightTheme::label("c")) + .add(LightTheme::label("r")) + .add(LightTheme::label("o")) + .add(LightTheme::label("l")) + .add(LightTheme::label("o")) + .add(LightTheme::label("l")) + .add(LightTheme::label("o")) + .add(LightTheme::label("l")) + .add(LightTheme::label("o")) + .add(LightTheme::label("l")) + .add(LightTheme::label("o")) + .add(LightTheme::label("l")) + .add(LightTheme::label("o")) + .add(LightTheme::label("l")) + .add(LightTheme::label("o")) + .add(LightTheme::label("l")) + .add(LightTheme::label("o")) + .add(LightTheme::label("l")) + .add(LightTheme::label("o")) + .add(LightTheme::label("l")) + .add(LightTheme::label("o")) + .add(LightTheme::label("Scrollolo :)")) .add( - DefaultTheme::primary_button("Back to top") + LightTheme::primary_button("Back to top") .bind(&scroll_data) .on_clicked(|data| data.scroll_to(0)), ), @@ -467,10 +466,11 @@ fn main() { .bind(&scroll_data) .on_scroll_changed(ScrollbarConnector::on_scroll_widget_scroll_changed) .on_data_changed(ScrollbarConnector::on_scroll_widget_data_changed), + BorderStyle::new(BinaryColor::On, 1), )) .weight(8) .add( - DefaultTheme::vertical_scrollbar() + LightTheme::vertical_scrollbar() .bind(&scroll_data) .on_data_changed(ScrollbarConnector::on_scrollbar_data_changed) .on_value_changed(ScrollbarConnector::on_scrollbar_value_changed), @@ -480,7 +480,7 @@ fn main() { .add( Column::new() .add( - Scroll::horizontal(Label::new( + Scroll::horizontal(LightTheme::label( "Some very long text that can be used to demonstrate horizontal scrollbars", )) .set_active(false) @@ -489,7 +489,7 @@ fn main() { .on_data_changed(ScrollbarConnector::on_scroll_widget_data_changed), ) .add( - DefaultTheme::horizontal_scrollbar() + LightTheme::horizontal_scrollbar() .bind(&horizontal_scroll_data) .on_data_changed(ScrollbarConnector::on_scrollbar_data_changed) .on_value_changed(ScrollbarConnector::on_scrollbar_value_changed), @@ -542,10 +542,7 @@ fn main() { let mut window = SimWindow::new("Everything but the kitchen sink", &output_settings); loop { - gui.canvas - .target - .clear(BinaryColor::BACKGROUND_COLOR) - .unwrap(); + gui.canvas.target.clear(BinaryColor::Off).unwrap(); gui.update(); gui.measure(); diff --git a/examples/kitchen_sink_rgb.rs b/examples/kitchen_sink_rgb.rs index 599c49a..727a054 100644 --- a/examples/kitchen_sink_rgb.rs +++ b/examples/kitchen_sink_rgb.rs @@ -1,20 +1,18 @@ use std::{fmt::Write, thread, time::Duration}; use backend_embedded_graphics::{ - themes::{default::DefaultTheme, Theme}, + themes::basic::{rgb888::LightTheme, BasicTheme}, widgets::{ - label::ascii::LabelConstructor, - text_block::{ - ascii::TextBlockConstructor, HorizontalAlignment, TextBlockStyling, VerticalAlignment, - }, - text_box::{ascii::TextBoxConstructor, TextBoxStyling}, + border::BorderStyle, + text_block::{HorizontalAlignment, TextBlockStyling, VerticalAlignment}, + text_box::TextBoxStyling, }, EgCanvas, }; use embedded_graphics::{ draw_target::DrawTarget, pixelcolor::Rgb888, - prelude::{Size as EgSize, WebColors}, + prelude::{RgbColor, Size as EgSize, WebColors}, }; use embedded_graphics_simulator::{ sdl2::{Keycode, Mod, MouseButton}, @@ -28,7 +26,6 @@ use embedded_gui::{ widgets::{ border::Border, fill::FillParent, - label::Label, layouts::{ frame::Frame, linear::{Column, Row}, @@ -36,8 +33,6 @@ use embedded_gui::{ scroll::Scroll, slider::ScrollbarConnector, spacing::Spacing, - text_block::TextBlock, - text_box::TextBox, visibility::Visibility, }, }; @@ -224,28 +219,28 @@ fn main() { let tabs = Row::new() .spacing(1) .add( - DefaultTheme::toggle_button("Multiline text") + LightTheme::toggle_button("Multiline text") .disallow_manual_uncheck() .bind(&page) .on_selected_changed(|_, page| *page = Page::TextBlock) .on_data_changed(|toggle, data| toggle.set_checked(*data == Page::TextBlock)), ) .add( - DefaultTheme::toggle_button("Checkables") + LightTheme::toggle_button("Checkables") .disallow_manual_uncheck() .bind(&page) .on_selected_changed(|_, page| *page = Page::Check) .on_data_changed(|toggle, data| toggle.set_checked(*data == Page::Check)), ) .add( - DefaultTheme::toggle_button("Sliders") + LightTheme::toggle_button("Sliders") .disallow_manual_uncheck() .bind(&page) .on_selected_changed(|_, page| *page = Page::Slider) .on_data_changed(|toggle, data| toggle.set_checked(*data == Page::Slider)), ) .add( - DefaultTheme::toggle_button("Scrolling") + LightTheme::toggle_button("Scrolling") .disallow_manual_uncheck() .bind(&page) .on_selected_changed(|_, page| *page = Page::Scroll) @@ -255,103 +250,105 @@ fn main() { let text_block_page = Row::new() .spacing(1) .add( - Column::new().add(Label::new("TextBlock")).add( - Border::new( - TextBlock::new( + Column::new() + .add(LightTheme::label("TextBlock")) + .add(Border::with_style( + LightTheme::text_block( "Some \x1b[4mstylish\x1b[24m multiline text that expands the widget vertically", ) .horizontal_alignment(HorizontalAlignment::Center) - .vertical_alignment(VerticalAlignment::Middle) - ) - .border_color(Rgb888::CSS_LIGHT_GRAY)) + .vertical_alignment(VerticalAlignment::Middle), + BorderStyle::new(Rgb888::CSS_LIGHT_GRAY, 1), + )), ) .weight(1) .add( Column::new() - .add(Label::new("TextBox")) - .add( - Border::new( - TextBox::new( - String::<100>::from("A TextBox with editable content. Click me and start typing!") + .add(LightTheme::label("TextBox")) + .add( + Border::with_style( + LightTheme::text_box(String::<100>::from( + "A TextBox with editable content. Click me and start typing!", + )) + .horizontal_alignment(HorizontalAlignment::Center) + .vertical_alignment(VerticalAlignment::Middle) + .bind(&text_reset) + .on_data_changed(|text_box, (reset, _empty)| { + if *reset { + text_box.text.clear(); + } + }) + .on_text_changed(|(reset, empty), text| { + *reset = false; + *empty = text == ""; + }), + BorderStyle::new(Rgb888::CSS_LIGHT_GRAY, 1), ) - .horizontal_alignment(HorizontalAlignment::Center) - .vertical_alignment(VerticalAlignment::Middle) - .bind(&text_reset) - .on_data_changed(|text_box, (reset, _empty)| { - if *reset { - text_box.text.clear(); - } - }) - .on_text_changed(|(reset, empty), text| { - *reset = false; - *empty = text == ""; - }) + .border_color(Rgb888::CSS_LIGHT_GRAY), ) - .border_color(Rgb888::CSS_LIGHT_GRAY) - ) - .weight(1) - .add( - DefaultTheme::primary_button("Clear") - .bind(&text_reset) - .on_clicked(|(reset, empty)| { - *reset = true; - // Resetting means the text box will be empty - *empty = true; - }) - .on_data_changed(|button, (_, empty)| { - button.set_active(!*empty); - }) - ) + .weight(1) + .add( + LightTheme::primary_button("Clear") + .bind(&text_reset) + .on_clicked(|(reset, empty)| { + *reset = true; + // Resetting means the text box will be empty + *empty = true; + }) + .on_data_changed(|button, (_, empty)| { + button.set_active(!*empty); + }), + ), ) .weight(1); let checkables_page = Column::new() .spacing(1) - .add(Label::new("Checkboxes and radio buttons")) + .add(LightTheme::label("Checkboxes and radio buttons")) .add( - DefaultTheme::check_box("Check me") + LightTheme::check_box("Check me") .bind(&checkbox) .on_selected_changed(|checked, data| *data = checked) .on_data_changed(|checkbox, data| checkbox.set_checked(*data)), ) .add( - DefaultTheme::check_box("Inactive") + LightTheme::check_box("Inactive") .bind(&checkbox) .active(false) .on_data_changed(|checkbox, data| checkbox.set_checked(*data)), ) .add( - DefaultTheme::radio_button("Can't select me") + LightTheme::radio_button("Can't select me") .bind(&radio) .on_selected_changed(|_, data| *data = 0) .on_data_changed(|radio, data| radio.set_checked(*data == 0)) .active(false), ) .add( - DefaultTheme::radio_button("Select me") + LightTheme::radio_button("Select me") .bind(&radio) .on_selected_changed(|_, data| *data = 0) .on_data_changed(|radio, data| radio.set_checked(*data == 0)), ) .add( - DefaultTheme::radio_button("... or me!") + LightTheme::radio_button("... or me!") .bind(&radio) .on_selected_changed(|_, data| *data = 1) .on_data_changed(|radio, data| radio.set_checked(*data == 1)), ) .add( - DefaultTheme::toggle_button("Click me!") + LightTheme::toggle_button("Click me!") .bind(&toggle) .on_selected_changed(|selected, data| *data = selected) .on_data_changed(|toggle, data| toggle.set_checked(*data)), ) .add( - Visibility::new(Label::new("Toggle checked")) + Visibility::new(LightTheme::label("Toggle checked")) .bind(&toggle) .on_data_changed(|widget, data| widget.set_visible(*data)), ) .add( - DefaultTheme::primary_button("Reset") + LightTheme::primary_button("Reset") .bind(&checkables) .on_clicked(|data| { data.0.update(|data| *data = 0); @@ -362,11 +359,11 @@ fn main() { let sliders_page = Column::new() .spacing(1) - .add(Label::new("Numeric sliders")) + .add(LightTheme::label("Numeric sliders")) .add( Row::new() .add(FillParent::horizontal( - Label::new(String::<11>::from("0")) + LightTheme::label(String::<11>::from("0")) .bind(&slider1_data) .on_data_changed(|label, data| { label.text.clear(); @@ -376,7 +373,7 @@ fn main() { .weight(1) .add( Spacing::new( - DefaultTheme::slider(-100..=100) + LightTheme::slider(-100..=100) .bind(&slider1_data) .on_value_changed(|data, value| *data = value) .on_data_changed(|slider, data| slider.set_value(*data)), @@ -388,7 +385,7 @@ fn main() { .add( Row::new() .add(FillParent::horizontal( - Label::new(String::<11>::from("0")) + LightTheme::label(String::<11>::from("0")) .bind(&slider2_data) .on_data_changed(|label, data| { label.text.clear(); @@ -398,7 +395,7 @@ fn main() { .weight(1) .add( Spacing::new( - DefaultTheme::slider(0..=5) + LightTheme::slider(0..=5) .bind(&slider2_data) .on_value_changed(|data, value| *data = value) .on_data_changed(|slider, data| slider.set_value(*data)), @@ -408,9 +405,9 @@ fn main() { .weight(4), ) .add( - Row::new().add(Label::new("Inactive")).add( + Row::new().add(LightTheme::label("Inactive")).add( Spacing::new( - DefaultTheme::slider(0..=5) + LightTheme::slider(0..=5) .set_active(false) .bind(&slider2_data) .on_value_changed(|data, value| *data = value) @@ -420,7 +417,7 @@ fn main() { ), ) .add( - DefaultTheme::primary_button("Reset") + LightTheme::primary_button("Reset") .bind(&sliders) .on_clicked(|data| { data.0.update(|data| *data = 0); @@ -430,7 +427,7 @@ fn main() { let scrolling_page = Column::new() .add(FillParent::horizontal( - Label::new("Scroll down") + LightTheme::label("Scroll down") .bind(&scroll_data) .on_data_changed(|label, data| { label.text = if data.offset == data.maximum_offset { @@ -444,35 +441,35 @@ fn main() { )) .add( Row::new() - .add(Border::new( + .add(Border::with_style( Scroll::vertical( Spacing::new( Column::new() - .add(Label::new("S")) - .add(Label::new("c")) - .add(Label::new("r")) - .add(Label::new("o")) - .add(Label::new("l")) - .add(Label::new("o")) - .add(Label::new("l")) - .add(Label::new("o")) - .add(Label::new("l")) - .add(Label::new("o")) - .add(Label::new("l")) - .add(Label::new("o")) - .add(Label::new("l")) - .add(Label::new("o")) - .add(Label::new("l")) - .add(Label::new("o")) - .add(Label::new("l")) - .add(Label::new("o")) - .add(Label::new("l")) - .add(Label::new("o")) - .add(Label::new("l")) - .add(Label::new("o")) - .add(Label::new("Scrollolo :)")) + .add(LightTheme::label("S")) + .add(LightTheme::label("c")) + .add(LightTheme::label("r")) + .add(LightTheme::label("o")) + .add(LightTheme::label("l")) + .add(LightTheme::label("o")) + .add(LightTheme::label("l")) + .add(LightTheme::label("o")) + .add(LightTheme::label("l")) + .add(LightTheme::label("o")) + .add(LightTheme::label("l")) + .add(LightTheme::label("o")) + .add(LightTheme::label("l")) + .add(LightTheme::label("o")) + .add(LightTheme::label("l")) + .add(LightTheme::label("o")) + .add(LightTheme::label("l")) + .add(LightTheme::label("o")) + .add(LightTheme::label("l")) + .add(LightTheme::label("o")) + .add(LightTheme::label("l")) + .add(LightTheme::label("o")) + .add(LightTheme::label("Scrollolo :)")) .add( - DefaultTheme::primary_button("Back to top") + LightTheme::primary_button("Back to top") .bind(&scroll_data) .on_clicked(|data| data.scroll_to(0)), ), @@ -484,10 +481,11 @@ fn main() { .bind(&scroll_data) .on_scroll_changed(ScrollbarConnector::on_scroll_widget_scroll_changed) .on_data_changed(ScrollbarConnector::on_scroll_widget_data_changed), + BorderStyle::new(Rgb888::CSS_LIGHT_GRAY, 1), )) .weight(8) .add( - DefaultTheme::vertical_scrollbar() + LightTheme::vertical_scrollbar() .bind(&scroll_data) .on_data_changed(ScrollbarConnector::on_scrollbar_data_changed) .on_value_changed(ScrollbarConnector::on_scrollbar_value_changed), @@ -497,7 +495,7 @@ fn main() { .add( Column::new() .add( - Scroll::horizontal(Label::new( + Scroll::horizontal(LightTheme::label( "Some very long text that can be used to demonstrate horizontal scrollbars", )) .set_active(false) @@ -506,7 +504,7 @@ fn main() { .on_data_changed(ScrollbarConnector::on_scroll_widget_data_changed), ) .add( - DefaultTheme::horizontal_scrollbar() + LightTheme::horizontal_scrollbar() .bind(&horizontal_scroll_data) .on_data_changed(ScrollbarConnector::on_scrollbar_data_changed) .on_value_changed(ScrollbarConnector::on_scrollbar_value_changed), @@ -556,7 +554,7 @@ fn main() { let mut window = SimWindow::new("Everything but the kitchen sink", &output_settings); loop { - gui.canvas.target.clear(Rgb888::BACKGROUND_COLOR).unwrap(); + gui.canvas.target.clear(Rgb888::WHITE).unwrap(); gui.update(); gui.measure(); diff --git a/src/widgets/background.rs b/src/widgets/background.rs index 98d61e6..381fc80 100644 --- a/src/widgets/background.rs +++ b/src/widgets/background.rs @@ -36,25 +36,17 @@ where where P: Default, { + Background::with_style(inner, P::default()) + } + + pub fn with_style(inner: W, style: P) -> Background { Background { - background_properties: P::default(), + background_properties: style, inner, on_state_changed: |_, _| (), } } -} -impl WrapperBindable for Background -where - W: Widget, - P: BackgroundProperties, -{ -} - -impl Background -where - P: BackgroundProperties, -{ pub fn background_color(mut self, color: P::Color) -> Self { self.set_background_color(color); self @@ -70,6 +62,13 @@ where } } +impl WrapperBindable for Background +where + W: Widget, + P: BackgroundProperties, +{ +} + impl Wrapper, D> where W: Widget, diff --git a/src/widgets/border.rs b/src/widgets/border.rs index e14ff14..82b49a8 100644 --- a/src/widgets/border.rs +++ b/src/widgets/border.rs @@ -39,25 +39,17 @@ where where P: Default, { + Self::with_style(inner, P::default()) + } + + pub fn with_style(inner: W, style: P) -> Border { Border { - border_properties: P::default(), + border_properties: style, inner, on_state_changed: |_, _| (), } } -} -impl WrapperBindable for Border -where - W: Widget, - P: BorderProperties, -{ -} - -impl Border -where - P: BorderProperties, -{ pub fn border_color(mut self, color: P::Color) -> Self { self.set_border_color(color); self @@ -73,6 +65,13 @@ where } } +impl WrapperBindable for Border +where + W: Widget, + P: BorderProperties, +{ +} + impl Wrapper, D> where W: Widget, diff --git a/src/widgets/graphical/checkbox.rs b/src/widgets/graphical/checkbox.rs index 58ad496..617585d 100644 --- a/src/widgets/graphical/checkbox.rs +++ b/src/widgets/graphical/checkbox.rs @@ -38,18 +38,22 @@ impl

CheckBox

where P: CheckBoxProperties, { - pub fn new() -> CheckBox

- where - P: Default, - { + pub fn with_style(style: P) -> CheckBox

{ CheckBox { parent_index: 0, - checkbox_properties: P::default(), + checkbox_properties: style, bounds: BoundingBox::default(), on_state_changed: |_, _| (), } } + pub fn new() -> CheckBox

+ where + P: Default, + { + Self::with_style(P::default()) + } + pub fn background_color(mut self, color: P::Color) -> Self { self.set_background_color(color); self diff --git a/src/widgets/graphical/radio.rs b/src/widgets/graphical/radio.rs index a4779eb..3b65f4b 100644 --- a/src/widgets/graphical/radio.rs +++ b/src/widgets/graphical/radio.rs @@ -38,18 +38,22 @@ impl

RadioButton

where P: RadioButtonProperties, { - pub fn new() -> RadioButton

- where - P: Default, - { - RadioButton { + pub fn with_style(style: P) -> Self { + Self { parent_index: 0, - radio_properties: P::default(), + radio_properties: style, bounds: BoundingBox::default(), on_state_changed: |_, _| (), } } + pub fn new() -> RadioButton

+ where + P: Default, + { + Self::with_style(P::default()) + } + pub fn background_color(mut self, color: P::Color) -> Self { self.set_background_color(color); self