use rlvgl_core::{
style::Style,
widget::{Color, Rect, Widget},
};
use crate::theme::{ColorScheme, ComponentSize, Theme, Variant};
pub trait StyleProps: Sized {
fn style_mut(&mut self) -> &mut Style;
fn with_style(mut self, style: Style) -> Self {
*self.style_mut() = style;
self
}
fn bg(mut self, color: Color) -> Self {
self.style_mut().bg_color = color;
self
}
fn bg_color(self, color: Color) -> Self {
self.bg(color)
}
fn border_color(mut self, color: Color) -> Self {
self.style_mut().border_color = color;
self
}
fn border_width(mut self, width: u8) -> Self {
self.style_mut().border_width = width;
self
}
fn radius(mut self, radius: u8) -> Self {
self.style_mut().radius = radius;
self
}
fn rounded(self, radius: u8) -> Self {
self.radius(radius)
}
fn opacity(mut self, alpha: u8) -> Self {
self.style_mut().alpha = alpha;
self
}
fn alpha(self, alpha: u8) -> Self {
self.opacity(alpha)
}
}
pub trait BoundsProps: Widget + Sized {
fn bounds(mut self, bounds: Rect) -> Self {
self.set_bounds(bounds);
self
}
}
impl<T> BoundsProps for T where T: Widget + Sized {}
pub trait ThemeProps: StyleProps {
fn themed(
mut self,
theme: &Theme,
scheme: ColorScheme,
variant: Variant,
size: ComponentSize,
) -> Self {
let resolved = theme.component_style(scheme, variant, size);
*self.style_mut() = resolved.style;
self
}
fn variant(self, theme: &Theme, scheme: ColorScheme, variant: Variant) -> Self {
self.themed(theme, scheme, variant, ComponentSize::Md)
}
fn component_size(mut self, theme: &Theme, size: ComponentSize) -> Self {
let resolved = theme.component_size(size);
self.style_mut().radius = resolved.radius;
self.style_mut().border_width = resolved.border_width;
self
}
}
impl<T> ThemeProps for T where T: StyleProps {}
macro_rules! impl_style_props_via_method {
($($ty:path),+ $(,)?) => {
$(
impl StyleProps for $ty {
fn style_mut(&mut self) -> &mut Style {
<$ty>::style_mut(self)
}
}
)+
};
}
macro_rules! impl_style_props_via_field {
($($ty:path),+ $(,)?) => {
$(
impl StyleProps for $ty {
fn style_mut(&mut self) -> &mut Style {
&mut self.style
}
}
)+
};
}
impl_style_props_via_method!(
crate::alert::Alert,
crate::badge::Badge,
crate::bar::Bar,
crate::button::IconButton,
crate::button_matrix::ButtonMatrix,
crate::checkbox::Checkbox,
crate::divider::Divider,
crate::drawer::Drawer,
crate::event::Slider,
crate::input::Input,
crate::input::Textarea,
crate::layout::BoxLayout,
crate::led::Led,
crate::list::List,
crate::modal::Modal,
crate::progress::Progress,
crate::radio::Radio,
crate::select::Select,
crate::spinner::Spinner,
crate::switch::Switch,
crate::tabs::Tabs,
crate::tag::Tag,
crate::text::Heading,
crate::text::Text,
crate::toast::Toast,
rlvgl_widgets::button::Button,
);
impl_style_props_via_field!(
rlvgl_widgets::calendar::Calendar,
rlvgl_widgets::message_box::MessageBox,
rlvgl_widgets::spinbox::Spinbox,
rlvgl_widgets::table::Table,
rlvgl_widgets::window::Window,
);
#[cfg(test)]
mod tests {
use super::*;
use crate::Progress;
use crate::theme::{ColorScheme, ComponentSize, Variant};
#[test]
fn style_props_update_main_style() {
let progress = Progress::new(
Rect {
x: 0,
y: 0,
width: 80,
height: 8,
},
0,
100,
)
.bg(Color(1, 2, 3, 255))
.border_color(Color(4, 5, 6, 255))
.border_width(2)
.rounded(4)
.opacity(128);
assert_eq!(progress.style().bg_color, Color(1, 2, 3, 255));
assert_eq!(progress.style().border_color, Color(4, 5, 6, 255));
assert_eq!(progress.style().border_width, 2);
assert_eq!(progress.style().radius, 4);
assert_eq!(progress.style().alpha, 128);
}
#[test]
fn theme_props_apply_variant_style() {
let theme = Theme::material_light();
let progress = Progress::new(
Rect {
x: 0,
y: 0,
width: 80,
height: 8,
},
0,
100,
)
.themed(
&theme,
ColorScheme::Danger,
Variant::Outline,
ComponentSize::Lg,
);
assert_eq!(progress.style().bg_color.3, 0);
assert_eq!(progress.style().border_width, 2);
assert_eq!(progress.style().radius, theme.tokens.radii.lg);
}
}