1use rlvgl_core::{
8 style::Style,
9 widget::{Color, Rect, Widget},
10};
11
12use crate::theme::{ColorScheme, ComponentSize, Theme, Variant};
13
14pub trait StyleProps: Sized {
19 fn style_mut(&mut self) -> &mut Style;
21
22 fn with_style(mut self, style: Style) -> Self {
24 *self.style_mut() = style;
25 self
26 }
27
28 fn bg(mut self, color: Color) -> Self {
30 self.style_mut().bg_color = color;
31 self
32 }
33
34 fn bg_color(self, color: Color) -> Self {
36 self.bg(color)
37 }
38
39 fn border_color(mut self, color: Color) -> Self {
41 self.style_mut().border_color = color;
42 self
43 }
44
45 fn border_width(mut self, width: u8) -> Self {
47 self.style_mut().border_width = width;
48 self
49 }
50
51 fn radius(mut self, radius: u8) -> Self {
53 self.style_mut().radius = radius;
54 self
55 }
56
57 fn rounded(self, radius: u8) -> Self {
59 self.radius(radius)
60 }
61
62 fn opacity(mut self, alpha: u8) -> Self {
64 self.style_mut().alpha = alpha;
65 self
66 }
67
68 fn alpha(self, alpha: u8) -> Self {
70 self.opacity(alpha)
71 }
72}
73
74pub trait BoundsProps: Widget + Sized {
79 fn bounds(mut self, bounds: Rect) -> Self {
81 self.set_bounds(bounds);
82 self
83 }
84}
85
86impl<T> BoundsProps for T where T: Widget + Sized {}
87
88pub trait ThemeProps: StyleProps {
90 fn themed(
92 mut self,
93 theme: &Theme,
94 scheme: ColorScheme,
95 variant: Variant,
96 size: ComponentSize,
97 ) -> Self {
98 let resolved = theme.component_style(scheme, variant, size);
99 *self.style_mut() = resolved.style;
100 self
101 }
102
103 fn variant(self, theme: &Theme, scheme: ColorScheme, variant: Variant) -> Self {
105 self.themed(theme, scheme, variant, ComponentSize::Md)
106 }
107
108 fn component_size(mut self, theme: &Theme, size: ComponentSize) -> Self {
110 let resolved = theme.component_size(size);
111 self.style_mut().radius = resolved.radius;
112 self.style_mut().border_width = resolved.border_width;
113 self
114 }
115}
116
117impl<T> ThemeProps for T where T: StyleProps {}
118
119macro_rules! impl_style_props_via_method {
120 ($($ty:path),+ $(,)?) => {
121 $(
122 impl StyleProps for $ty {
123 fn style_mut(&mut self) -> &mut Style {
124 <$ty>::style_mut(self)
125 }
126 }
127 )+
128 };
129}
130
131macro_rules! impl_style_props_via_field {
132 ($($ty:path),+ $(,)?) => {
133 $(
134 impl StyleProps for $ty {
135 fn style_mut(&mut self) -> &mut Style {
136 &mut self.style
137 }
138 }
139 )+
140 };
141}
142
143impl_style_props_via_method!(
144 crate::alert::Alert,
145 crate::badge::Badge,
146 crate::bar::Bar,
147 crate::button::IconButton,
148 crate::button_matrix::ButtonMatrix,
149 crate::checkbox::Checkbox,
150 crate::divider::Divider,
151 crate::drawer::Drawer,
152 crate::event::Slider,
153 crate::input::Input,
154 crate::input::Textarea,
155 crate::layout::BoxLayout,
156 crate::led::Led,
157 crate::list::List,
158 crate::modal::Modal,
159 crate::progress::Progress,
160 crate::radio::Radio,
161 crate::select::Select,
162 crate::spinner::Spinner,
163 crate::switch::Switch,
164 crate::tabs::Tabs,
165 crate::tag::Tag,
166 crate::text::Heading,
167 crate::text::Text,
168 crate::toast::Toast,
169 rlvgl_widgets::button::Button,
170);
171
172impl_style_props_via_field!(
173 rlvgl_widgets::calendar::Calendar,
174 rlvgl_widgets::message_box::MessageBox,
175 rlvgl_widgets::spinbox::Spinbox,
176 rlvgl_widgets::table::Table,
177 rlvgl_widgets::window::Window,
178);
179
180#[cfg(test)]
181mod tests {
182 use super::*;
183 use crate::Progress;
184 use crate::theme::{ColorScheme, ComponentSize, Variant};
185
186 #[test]
187 fn style_props_update_main_style() {
188 let progress = Progress::new(
189 Rect {
190 x: 0,
191 y: 0,
192 width: 80,
193 height: 8,
194 },
195 0,
196 100,
197 )
198 .bg(Color(1, 2, 3, 255))
199 .border_color(Color(4, 5, 6, 255))
200 .border_width(2)
201 .rounded(4)
202 .opacity(128);
203
204 assert_eq!(progress.style().bg_color, Color(1, 2, 3, 255));
205 assert_eq!(progress.style().border_color, Color(4, 5, 6, 255));
206 assert_eq!(progress.style().border_width, 2);
207 assert_eq!(progress.style().radius, 4);
208 assert_eq!(progress.style().alpha, 128);
209 }
210
211 #[test]
212 fn theme_props_apply_variant_style() {
213 let theme = Theme::material_light();
214 let progress = Progress::new(
215 Rect {
216 x: 0,
217 y: 0,
218 width: 80,
219 height: 8,
220 },
221 0,
222 100,
223 )
224 .themed(
225 &theme,
226 ColorScheme::Danger,
227 Variant::Outline,
228 ComponentSize::Lg,
229 );
230
231 assert_eq!(progress.style().bg_color.3, 0);
232 assert_eq!(progress.style().border_width, 2);
233 assert_eq!(progress.style().radius, theme.tokens.radii.lg);
234 }
235}