1use alloc::boxed::Box;
2
3use crate::{Color, Display, fonts, lvgl, palette};
4
5pub const PRIMARY_COLOR: Color = Color::new(0xFA, 0xFA, 0xFA);
6pub const BACKGROUND_COLOR_PRIMARY: Color = Color::new(0x09, 0x09, 0x0b);
7pub const BACKGROUND_COLOR_PRIMARY_MUTED: Color = Color::new(0x18, 0x18, 0x1b);
8pub const BORDER_COLOR_PRIMARY: Color = Color::new(0x27, 0x27, 0x2a);
9pub const SECONDARY_COLOR: Color = palette::get(palette::Hue::Red, palette::Tone::MAIN);
10pub const IS_DARK: bool = true;
11
12pub const BORDER_WIDTH: i32 = 2;
13pub const RADIUS: i32 = BORDER_WIDTH * 4;
14pub const PADDING: i32 = 16;
15
16#[derive(Clone, Copy)]
20#[repr(C)]
21#[allow(dead_code)]
22pub(crate) struct Theme {
23 pub apply_cb: Option<lvgl::lv_theme_apply_cb_t>,
24 pub parent: *mut Theme,
26 pub user_data: *mut core::ffi::c_void,
27 pub disp: *mut lvgl::lv_display_t,
28 pub color_primary: lvgl::lv_color_t,
29 pub color_secondary: lvgl::lv_color_t,
30 pub font_small: *const lvgl::lv_font_t,
31 pub font_normal: *const lvgl::lv_font_t,
32 pub font_large: *const lvgl::lv_font_t,
33 pub flags: u32,
35}
36
37pub(crate) fn initialize(display: &Display) {
38 update(display, PRIMARY_COLOR, SECONDARY_COLOR, IS_DARK);
39
40 unsafe {
41 let default_theme = lvgl::lv_theme_default_get() as *const Theme;
42
43 let theme = Box::new(*default_theme);
44 let theme = Box::into_raw(theme) as *mut lvgl::lv_theme_t;
45
46 lvgl::lv_theme_set_parent(theme, default_theme as *mut lvgl::lv_theme_t);
47 lvgl::lv_theme_set_apply_cb(theme, Some(theme_apply));
48 lvgl::lv_display_set_theme(display.get_lvgl_display(), theme);
49 lvgl::lv_obj_set_style_bg_color(
50 display.get_object(),
51 BACKGROUND_COLOR_PRIMARY.into_lvgl_color(),
52 lvgl::LV_PART_MAIN,
53 );
54 }
55}
56
57pub(crate) fn update(
58 display: &Display,
59 primary_color: Color,
60 secondary_color: Color,
61 is_dark: bool,
62) {
63 unsafe {
64 lvgl::lv_theme_default_init(
65 display.get_lvgl_display(),
66 primary_color.into_lvgl_color(),
67 secondary_color.into_lvgl_color(),
68 is_dark,
69 fonts::get_font_medium(),
70 );
71 }
72}
73
74pub fn get_background_color_primary() -> Color {
75 BACKGROUND_COLOR_PRIMARY
76}
77
78pub fn get_background_color_primary_muted() -> Color {
79 BACKGROUND_COLOR_PRIMARY_MUTED
80}
81
82pub fn get_primary_color() -> Color {
83 PRIMARY_COLOR
84}
85
86pub fn get_secondary_color() -> Color {
87 SECONDARY_COLOR
88}
89
90pub fn get_border_color_primary() -> Color {
91 BORDER_COLOR_PRIMARY
92}
93
94pub unsafe fn apply_default_style(
100 object: *mut lvgl::lv_obj_t,
101 selector: lvgl::lv_style_selector_t,
102) {
103 unsafe {
104 lvgl::lv_obj_set_style_bg_color(
105 object,
106 BACKGROUND_COLOR_PRIMARY.into_lvgl_color(),
107 selector,
108 );
109 lvgl::lv_obj_set_style_border_color(
110 object,
111 BORDER_COLOR_PRIMARY.into_lvgl_color(),
112 selector,
113 );
114 }
115}
116
117pub unsafe extern "C" fn theme_apply(_: *mut lvgl::lv_theme_t, object: *mut lvgl::lv_obj_t) {
123 unsafe {
124 let class = lvgl::lv_obj_get_class(object);
125 let parent = lvgl::lv_obj_get_parent(object);
126
127 if class == &lvgl::lv_button_class {
128 lvgl::lv_obj_set_style_bg_color(
129 object,
130 PRIMARY_COLOR.into_lvgl_color(),
131 lvgl::LV_PART_MAIN,
132 );
133 lvgl::lv_obj_set_style_text_color(
134 object,
135 BACKGROUND_COLOR_PRIMARY.into_lvgl_color(),
136 lvgl::LV_PART_MAIN,
137 );
138
139 let tab_view = lvgl::lv_obj_get_parent(parent);
140
141 if !tab_view.is_null()
142 && lvgl::lv_obj_get_child(tab_view, 0) == parent
143 && lvgl::lv_obj_check_type(tab_view, &lvgl::lv_tabview_class)
144 {
145 lvgl::lv_obj_set_style_pad_all(object, BORDER_WIDTH * 4, lvgl::LV_PART_MAIN);
146 lvgl::lv_obj_set_style_radius(object, RADIUS, lvgl::LV_PART_MAIN);
147
148 lvgl::lv_obj_set_style_border_side(
149 object,
150 lvgl::lv_border_side_t_LV_BORDER_SIDE_FULL,
151 lvgl::LV_STATE_CHECKED,
152 );
153 lvgl::lv_obj_set_style_border_side(
154 object,
155 lvgl::lv_border_side_t_LV_BORDER_SIDE_FULL,
156 lvgl::LV_PART_MAIN,
157 );
158 lvgl::lv_obj_set_style_border_color(
159 object,
160 PRIMARY_COLOR.into_lvgl_color(),
161 lvgl::LV_STATE_CHECKED,
162 );
163 lvgl::lv_obj_set_style_border_color(
164 object,
165 PRIMARY_COLOR.into_lvgl_color(),
166 lvgl::LV_PART_MAIN,
167 );
168
169 lvgl::lv_obj_set_style_border_width(object, BORDER_WIDTH, lvgl::LV_PART_MAIN);
170 lvgl::lv_obj_set_style_border_width(object, BORDER_WIDTH, lvgl::LV_STATE_CHECKED);
171 lvgl::lv_obj_set_style_border_opa(
172 object,
173 lvgl::LV_OPA_TRANSP as _,
174 lvgl::LV_PART_MAIN,
175 );
176 lvgl::lv_obj_set_style_border_opa(
177 object,
178 lvgl::LV_OPA_COVER as _,
179 lvgl::LV_STATE_CHECKED,
180 );
181
182 lvgl::lv_obj_set_style_bg_opa(object, lvgl::LV_OPA_TRANSP as _, lvgl::LV_PART_MAIN);
183
184 lvgl::lv_obj_set_style_text_color(
185 object,
186 PRIMARY_COLOR.into_lvgl_color(),
187 lvgl::LV_PART_MAIN,
188 );
189
190 lvgl::lv_obj_set_style_bg_color(
191 parent,
192 BACKGROUND_COLOR_PRIMARY_MUTED.into_lvgl_color(),
193 lvgl::LV_PART_MAIN,
194 );
195 lvgl::lv_obj_set_style_radius(parent, RADIUS, lvgl::LV_PART_MAIN);
196 lvgl::lv_obj_set_style_pad_all(parent, BORDER_WIDTH * 2, lvgl::LV_PART_MAIN);
197 lvgl::lv_obj_set_flex_align(
198 parent,
199 lvgl::lv_flex_align_t_LV_FLEX_ALIGN_CENTER,
200 lvgl::lv_flex_align_t_LV_FLEX_ALIGN_CENTER,
201 lvgl::lv_flex_align_t_LV_FLEX_ALIGN_CENTER,
202 );
203 }
204 } else if class == &lvgl::lv_buttonmatrix_class {
205 apply_default_style(object, lvgl::LV_PART_MAIN);
206 apply_default_style(object, lvgl::LV_PART_ITEMS);
207 lvgl::lv_obj_set_style_text_color(
208 object,
209 PRIMARY_COLOR.into_lvgl_color(),
210 lvgl::LV_PART_MAIN,
211 );
212
213 lvgl::lv_obj_set_style_border_width(object, BORDER_WIDTH, lvgl::LV_PART_ITEMS);
214 lvgl::lv_obj_set_style_text_color(
215 object,
216 PRIMARY_COLOR.into_lvgl_color(),
217 lvgl::LV_PART_ITEMS,
218 );
219 } else if class == &lvgl::lv_checkbox_class {
220 apply_default_style(object, lvgl::LV_PART_MAIN);
221 apply_default_style(object, lvgl::LV_PART_INDICATOR);
222 lvgl::lv_obj_set_style_border_color(
223 object,
224 PRIMARY_COLOR.into_lvgl_color(),
225 lvgl::LV_PART_INDICATOR | lvgl::LV_STATE_CHECKED,
226 );
227 lvgl::lv_obj_set_style_text_color(
228 object,
229 BACKGROUND_COLOR_PRIMARY.into_lvgl_color(),
230 lvgl::LV_PART_INDICATOR | lvgl::LV_STATE_CHECKED,
231 );
232 } else if class == &lvgl::lv_list_text_class {
233 apply_default_style(object, lvgl::LV_PART_MAIN);
234 lvgl::lv_obj_set_style_pad_left(object, PADDING, lvgl::LV_PART_MAIN);
235 lvgl::lv_obj_set_style_bg_color(
236 object,
237 BACKGROUND_COLOR_PRIMARY_MUTED.into_lvgl_color(),
238 lvgl::LV_PART_MAIN,
239 );
240 } else if class == &lvgl::lv_list_button_class {
241 apply_default_style(object, lvgl::LV_PART_MAIN);
242 lvgl::lv_obj_set_style_pad_left(object, 2 * PADDING, lvgl::LV_PART_MAIN);
243 } else if class == &lvgl::lv_switch_class {
244 apply_default_style(object, lvgl::LV_PART_MAIN);
245 apply_default_style(object, lvgl::LV_PART_INDICATOR);
246 lvgl::lv_obj_set_style_bg_color(
247 object,
248 BACKGROUND_COLOR_PRIMARY.into_lvgl_color(),
249 lvgl::LV_PART_KNOB | lvgl::LV_STATE_CHECKED,
250 );
251 } else {
252 apply_default_style(object, lvgl::LV_PART_MAIN);
253 }
254 }
255}