1 /**
2 * @file lv_obj_style.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9 #include "lv_obj.h"
10 #include "lv_disp.h"
11 #include "../misc/lv_gc.h"
12
13 /*********************
14 * DEFINES
15 *********************/
16 #define MY_CLASS &lv_obj_class
17
18 /**********************
19 * TYPEDEFS
20 **********************/
21
22 typedef struct {
23 lv_obj_t * obj;
24 lv_style_prop_t prop;
25 lv_style_selector_t selector;
26 lv_style_value_t start_value;
27 lv_style_value_t end_value;
28 } trans_t;
29
30 typedef enum {
31 CACHE_ZERO = 0,
32 CACHE_TRUE = 1,
33 CACHE_UNSET = 2,
34 CACHE_255 = 3,
35 CACHE_NEED_CHECK = 4,
36 } cache_t;
37
38 /**********************
39 * GLOBAL PROTOTYPES
40 **********************/
41
42 /**********************
43 * STATIC PROTOTYPES
44 **********************/
45 static lv_style_t * get_local_style(lv_obj_t * obj, lv_style_selector_t selector);
46 static _lv_obj_style_t * get_trans_style(lv_obj_t * obj, uint32_t part);
47 static lv_style_res_t get_prop_core(const lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop, lv_style_value_t * v);
48 static void report_style_change_core(void * style, lv_obj_t * obj);
49 static void refresh_children_style(lv_obj_t * obj);
50 static bool trans_del(lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop, trans_t * tr_limit);
51 static void trans_anim_cb(void * _tr, int32_t v);
52 static void trans_anim_start_cb(lv_anim_t * a);
53 static void trans_anim_ready_cb(lv_anim_t * a);
54 static lv_layer_type_t calculate_layer_type(lv_obj_t * obj);
55 static void fade_anim_cb(void * obj, int32_t v);
56 static void fade_in_anim_ready(lv_anim_t * a);
57
58 /**********************
59 * STATIC VARIABLES
60 **********************/
61 static bool style_refr = true;
62
63 /**********************
64 * MACROS
65 **********************/
66
67 /**********************
68 * GLOBAL FUNCTIONS
69 **********************/
70
_lv_obj_style_init(void)71 void _lv_obj_style_init(void)
72 {
73 _lv_ll_init(&LV_GC_ROOT(_lv_obj_style_trans_ll), sizeof(trans_t));
74 }
75
lv_obj_add_style(lv_obj_t * obj,lv_style_t * style,lv_style_selector_t selector)76 void lv_obj_add_style(lv_obj_t * obj, lv_style_t * style, lv_style_selector_t selector)
77 {
78 trans_del(obj, selector, LV_STYLE_PROP_ANY, NULL);
79
80 uint32_t i;
81 /*Go after the transition and local styles*/
82 for(i = 0; i < obj->style_cnt; i++) {
83 if(obj->styles[i].is_trans) continue;
84 if(obj->styles[i].is_local) continue;
85 break;
86 }
87
88 /*Now `i` is at the first normal style. Insert the new style before this*/
89
90 /*Allocate space for the new style and shift the rest of the style to the end*/
91 obj->style_cnt++;
92 obj->styles = lv_mem_realloc(obj->styles, obj->style_cnt * sizeof(_lv_obj_style_t));
93
94 uint32_t j;
95 for(j = obj->style_cnt - 1; j > i ; j--) {
96 obj->styles[j] = obj->styles[j - 1];
97 }
98
99 lv_memset_00(&obj->styles[i], sizeof(_lv_obj_style_t));
100 obj->styles[i].style = style;
101 obj->styles[i].selector = selector;
102
103 lv_obj_refresh_style(obj, selector, LV_STYLE_PROP_ANY);
104 }
105
lv_obj_remove_style(lv_obj_t * obj,lv_style_t * style,lv_style_selector_t selector)106 void lv_obj_remove_style(lv_obj_t * obj, lv_style_t * style, lv_style_selector_t selector)
107 {
108 lv_state_t state = lv_obj_style_get_selector_state(selector);
109 lv_part_t part = lv_obj_style_get_selector_part(selector);
110 lv_style_prop_t prop = LV_STYLE_PROP_ANY;
111 if(style && style->prop_cnt == 0) prop = LV_STYLE_PROP_INV;
112
113 uint32_t i = 0;
114 bool deleted = false;
115 while(i < obj->style_cnt) {
116 lv_state_t state_act = lv_obj_style_get_selector_state(obj->styles[i].selector);
117 lv_part_t part_act = lv_obj_style_get_selector_part(obj->styles[i].selector);
118 if((state != LV_STATE_ANY && state_act != state) ||
119 (part != LV_PART_ANY && part_act != part) ||
120 (style != NULL && style != obj->styles[i].style)) {
121 i++;
122 continue;
123 }
124
125 if(obj->styles[i].is_trans) {
126 trans_del(obj, part, LV_STYLE_PROP_ANY, NULL);
127 }
128
129 if(obj->styles[i].is_local || obj->styles[i].is_trans) {
130 lv_style_reset(obj->styles[i].style);
131 lv_mem_free(obj->styles[i].style);
132 obj->styles[i].style = NULL;
133 }
134
135 /*Shift the styles after `i` by one*/
136 uint32_t j;
137 for(j = i; j < (uint32_t)obj->style_cnt - 1 ; j++) {
138 obj->styles[j] = obj->styles[j + 1];
139 }
140
141 obj->style_cnt--;
142 obj->styles = lv_mem_realloc(obj->styles, obj->style_cnt * sizeof(_lv_obj_style_t));
143
144 deleted = true;
145 /*The style from the current `i` index is removed, so `i` points to the next style.
146 *Therefore it doesn't needs to be incremented*/
147 }
148 if(deleted && prop != LV_STYLE_PROP_INV) {
149 lv_obj_refresh_style(obj, part, prop);
150 }
151 }
152
lv_obj_report_style_change(lv_style_t * style)153 void lv_obj_report_style_change(lv_style_t * style)
154 {
155 if(!style_refr) return;
156 lv_disp_t * d = lv_disp_get_next(NULL);
157
158 while(d) {
159 uint32_t i;
160 for(i = 0; i < d->screen_cnt; i++) {
161 report_style_change_core(style, d->screens[i]);
162 }
163 d = lv_disp_get_next(d);
164 }
165 }
166
lv_obj_refresh_style(lv_obj_t * obj,lv_style_selector_t selector,lv_style_prop_t prop)167 void lv_obj_refresh_style(lv_obj_t * obj, lv_style_selector_t selector, lv_style_prop_t prop)
168 {
169 LV_ASSERT_OBJ(obj, MY_CLASS);
170
171 if(!style_refr) return;
172
173 lv_obj_invalidate(obj);
174
175 lv_part_t part = lv_obj_style_get_selector_part(selector);
176
177 bool is_layout_refr = lv_style_prop_has_flag(prop, LV_STYLE_PROP_LAYOUT_REFR);
178 bool is_ext_draw = lv_style_prop_has_flag(prop, LV_STYLE_PROP_EXT_DRAW);
179 bool is_inheritable = lv_style_prop_has_flag(prop, LV_STYLE_PROP_INHERIT);
180 bool is_layer_refr = lv_style_prop_has_flag(prop, LV_STYLE_PROP_LAYER_REFR);
181
182 if(is_layout_refr) {
183 if(part == LV_PART_ANY ||
184 part == LV_PART_MAIN ||
185 lv_obj_get_style_height(obj, 0) == LV_SIZE_CONTENT ||
186 lv_obj_get_style_width(obj, 0) == LV_SIZE_CONTENT) {
187 lv_event_send(obj, LV_EVENT_STYLE_CHANGED, NULL);
188 lv_obj_mark_layout_as_dirty(obj);
189 }
190 }
191 if((part == LV_PART_ANY || part == LV_PART_MAIN) && (prop == LV_STYLE_PROP_ANY || is_layout_refr)) {
192 lv_obj_t * parent = lv_obj_get_parent(obj);
193 if(parent) lv_obj_mark_layout_as_dirty(parent);
194 }
195
196 /*Cache the layer type*/
197 if((part == LV_PART_ANY || part == LV_PART_MAIN) && is_layer_refr) {
198 lv_layer_type_t layer_type = calculate_layer_type(obj);
199 if(obj->spec_attr) obj->spec_attr->layer_type = layer_type;
200 else if(layer_type != LV_LAYER_TYPE_NONE) {
201 lv_obj_allocate_spec_attr(obj);
202 obj->spec_attr->layer_type = layer_type;
203 }
204 }
205
206 if(prop == LV_STYLE_PROP_ANY || is_ext_draw) {
207 lv_obj_refresh_ext_draw_size(obj);
208 }
209 lv_obj_invalidate(obj);
210
211 if(prop == LV_STYLE_PROP_ANY || (is_inheritable && (is_ext_draw || is_layout_refr))) {
212 if(part != LV_PART_SCROLLBAR) {
213 refresh_children_style(obj);
214 }
215 }
216 }
217
lv_obj_enable_style_refresh(bool en)218 void lv_obj_enable_style_refresh(bool en)
219 {
220 style_refr = en;
221 }
222
lv_obj_get_style_prop(const lv_obj_t * obj,lv_part_t part,lv_style_prop_t prop)223 lv_style_value_t lv_obj_get_style_prop(const lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop)
224 {
225 lv_style_value_t value_act;
226 bool inheritable = lv_style_prop_has_flag(prop, LV_STYLE_PROP_INHERIT);
227 lv_style_res_t found = LV_STYLE_RES_NOT_FOUND;
228 while(obj) {
229 found = get_prop_core(obj, part, prop, &value_act);
230 if(found == LV_STYLE_RES_FOUND) break;
231 if(!inheritable) break;
232
233 /*If not found, check the `MAIN` style first*/
234 if(found != LV_STYLE_RES_INHERIT && part != LV_PART_MAIN) {
235 part = LV_PART_MAIN;
236 continue;
237 }
238
239 /*Check the parent too.*/
240 obj = lv_obj_get_parent(obj);
241 }
242
243 if(found != LV_STYLE_RES_FOUND) {
244 if(part == LV_PART_MAIN && (prop == LV_STYLE_WIDTH || prop == LV_STYLE_HEIGHT)) {
245 const lv_obj_class_t * cls = obj->class_p;
246 while(cls) {
247 if(prop == LV_STYLE_WIDTH) {
248 if(cls->width_def != 0) break;
249 }
250 else {
251 if(cls->height_def != 0) break;
252 }
253 cls = cls->base_class;
254 }
255
256 if(cls) {
257 value_act.num = prop == LV_STYLE_WIDTH ? cls->width_def : cls->height_def;
258 }
259 else {
260 value_act.num = 0;
261 }
262 }
263 else {
264 value_act = lv_style_prop_get_default(prop);
265 }
266 }
267 return value_act;
268 }
269
lv_obj_set_local_style_prop(lv_obj_t * obj,lv_style_prop_t prop,lv_style_value_t value,lv_style_selector_t selector)270 void lv_obj_set_local_style_prop(lv_obj_t * obj, lv_style_prop_t prop, lv_style_value_t value,
271 lv_style_selector_t selector)
272 {
273 lv_style_t * style = get_local_style(obj, selector);
274 lv_style_set_prop(style, prop, value);
275 lv_obj_refresh_style(obj, selector, prop);
276 }
277
lv_obj_set_local_style_prop_meta(lv_obj_t * obj,lv_style_prop_t prop,uint16_t meta,lv_style_selector_t selector)278 void lv_obj_set_local_style_prop_meta(lv_obj_t * obj, lv_style_prop_t prop, uint16_t meta,
279 lv_style_selector_t selector)
280 {
281 lv_style_t * style = get_local_style(obj, selector);
282 lv_style_set_prop_meta(style, prop, meta);
283 lv_obj_refresh_style(obj, selector, prop);
284 }
285
lv_obj_get_local_style_prop(lv_obj_t * obj,lv_style_prop_t prop,lv_style_value_t * value,lv_style_selector_t selector)286 lv_style_res_t lv_obj_get_local_style_prop(lv_obj_t * obj, lv_style_prop_t prop, lv_style_value_t * value,
287 lv_style_selector_t selector)
288 {
289 uint32_t i;
290 for(i = 0; i < obj->style_cnt; i++) {
291 if(obj->styles[i].is_local &&
292 obj->styles[i].selector == selector) {
293 return lv_style_get_prop(obj->styles[i].style, prop, value);
294 }
295 }
296
297 return LV_STYLE_RES_NOT_FOUND;
298 }
299
lv_obj_remove_local_style_prop(lv_obj_t * obj,lv_style_prop_t prop,lv_style_selector_t selector)300 bool lv_obj_remove_local_style_prop(lv_obj_t * obj, lv_style_prop_t prop, lv_style_selector_t selector)
301 {
302 LV_ASSERT_OBJ(obj, MY_CLASS);
303
304 uint32_t i;
305 /*Find the style*/
306 for(i = 0; i < obj->style_cnt; i++) {
307 if(obj->styles[i].is_local &&
308 obj->styles[i].selector == selector) {
309 break;
310 }
311 }
312
313 /*The style is not found*/
314 if(i == obj->style_cnt) return false;
315
316 lv_res_t res = lv_style_remove_prop(obj->styles[i].style, prop);
317 if(res == LV_RES_OK) {
318 lv_obj_refresh_style(obj, selector, prop);
319 }
320
321 return res;
322 }
323
_lv_obj_style_create_transition(lv_obj_t * obj,lv_part_t part,lv_state_t prev_state,lv_state_t new_state,const _lv_obj_style_transition_dsc_t * tr_dsc)324 void _lv_obj_style_create_transition(lv_obj_t * obj, lv_part_t part, lv_state_t prev_state, lv_state_t new_state,
325 const _lv_obj_style_transition_dsc_t * tr_dsc)
326 {
327 trans_t * tr;
328
329 /*Get the previous and current values*/
330 obj->skip_trans = 1;
331 obj->state = prev_state;
332 lv_style_value_t v1 = lv_obj_get_style_prop(obj, part, tr_dsc->prop);
333 obj->state = new_state;
334 lv_style_value_t v2 = lv_obj_get_style_prop(obj, part, tr_dsc->prop);
335 obj->skip_trans = 0;
336
337 if(v1.ptr == v2.ptr && v1.num == v2.num && v1.color.full == v2.color.full) return;
338 obj->state = prev_state;
339 v1 = lv_obj_get_style_prop(obj, part, tr_dsc->prop);
340 obj->state = new_state;
341
342 _lv_obj_style_t * style_trans = get_trans_style(obj, part);
343 lv_style_set_prop(style_trans->style, tr_dsc->prop, v1); /*Be sure `trans_style` has a valid value*/
344
345 if(tr_dsc->prop == LV_STYLE_RADIUS) {
346 if(v1.num == LV_RADIUS_CIRCLE || v2.num == LV_RADIUS_CIRCLE) {
347 lv_coord_t whalf = lv_obj_get_width(obj) / 2;
348 lv_coord_t hhalf = lv_obj_get_height(obj) / 2;
349 if(v1.num == LV_RADIUS_CIRCLE) v1.num = LV_MIN(whalf + 1, hhalf + 1);
350 if(v2.num == LV_RADIUS_CIRCLE) v2.num = LV_MIN(whalf + 1, hhalf + 1);
351 }
352 }
353
354 tr = _lv_ll_ins_head(&LV_GC_ROOT(_lv_obj_style_trans_ll));
355 LV_ASSERT_MALLOC(tr);
356 if(tr == NULL) return;
357 tr->start_value = v1;
358 tr->end_value = v2;
359 tr->obj = obj;
360 tr->prop = tr_dsc->prop;
361 tr->selector = part;
362
363 lv_anim_t a;
364 lv_anim_init(&a);
365 lv_anim_set_var(&a, tr);
366 lv_anim_set_exec_cb(&a, trans_anim_cb);
367 lv_anim_set_start_cb(&a, trans_anim_start_cb);
368 lv_anim_set_ready_cb(&a, trans_anim_ready_cb);
369 lv_anim_set_values(&a, 0x00, 0xFF);
370 lv_anim_set_time(&a, tr_dsc->time);
371 lv_anim_set_delay(&a, tr_dsc->delay);
372 lv_anim_set_path_cb(&a, tr_dsc->path_cb);
373 lv_anim_set_early_apply(&a, false);
374 #if LV_USE_USER_DATA
375 a.user_data = tr_dsc->user_data;
376 #endif
377 lv_anim_start(&a);
378 }
379
_lv_obj_style_apply_color_filter(const lv_obj_t * obj,uint32_t part,lv_style_value_t v)380 lv_style_value_t _lv_obj_style_apply_color_filter(const lv_obj_t * obj, uint32_t part, lv_style_value_t v)
381 {
382 if(obj == NULL) return v;
383 const lv_color_filter_dsc_t * f = lv_obj_get_style_color_filter_dsc(obj, part);
384 if(f && f->filter_cb) {
385 lv_opa_t f_opa = lv_obj_get_style_color_filter_opa(obj, part);
386 if(f_opa != 0) v.color = f->filter_cb(f, v.color, f_opa);
387 }
388 return v;
389 }
390
_lv_obj_style_state_compare(lv_obj_t * obj,lv_state_t state1,lv_state_t state2)391 _lv_style_state_cmp_t _lv_obj_style_state_compare(lv_obj_t * obj, lv_state_t state1, lv_state_t state2)
392 {
393 _lv_style_state_cmp_t res = _LV_STYLE_STATE_CMP_SAME;
394
395 /*Are there any new styles for the new state?*/
396 uint32_t i;
397 for(i = 0; i < obj->style_cnt; i++) {
398 if(obj->styles[i].is_trans) continue;
399
400 lv_state_t state_act = lv_obj_style_get_selector_state(obj->styles[i].selector);
401 /*The style is valid for a state but not the other*/
402 bool valid1 = state_act & (~state1) ? false : true;
403 bool valid2 = state_act & (~state2) ? false : true;
404 if(valid1 != valid2) {
405 lv_style_t * style = obj->styles[i].style;
406 lv_style_value_t v;
407 /*If there is layout difference on the main part, return immediately. There is no more serious difference*/
408 bool layout_diff = false;
409 if(lv_style_get_prop(style, LV_STYLE_PAD_TOP, &v))layout_diff = true;
410 else if(lv_style_get_prop(style, LV_STYLE_PAD_BOTTOM, &v)) layout_diff = true;
411 else if(lv_style_get_prop(style, LV_STYLE_PAD_LEFT, &v)) layout_diff = true;
412 else if(lv_style_get_prop(style, LV_STYLE_PAD_RIGHT, &v)) layout_diff = true;
413 else if(lv_style_get_prop(style, LV_STYLE_PAD_COLUMN, &v)) layout_diff = true;
414 else if(lv_style_get_prop(style, LV_STYLE_PAD_ROW, &v)) layout_diff = true;
415 else if(lv_style_get_prop(style, LV_STYLE_LAYOUT, &v)) layout_diff = true;
416 else if(lv_style_get_prop(style, LV_STYLE_TRANSLATE_X, &v)) layout_diff = true;
417 else if(lv_style_get_prop(style, LV_STYLE_TRANSLATE_Y, &v)) layout_diff = true;
418 else if(lv_style_get_prop(style, LV_STYLE_WIDTH, &v)) layout_diff = true;
419 else if(lv_style_get_prop(style, LV_STYLE_HEIGHT, &v)) layout_diff = true;
420 else if(lv_style_get_prop(style, LV_STYLE_MIN_WIDTH, &v)) layout_diff = true;
421 else if(lv_style_get_prop(style, LV_STYLE_MAX_WIDTH, &v)) layout_diff = true;
422 else if(lv_style_get_prop(style, LV_STYLE_MIN_HEIGHT, &v)) layout_diff = true;
423 else if(lv_style_get_prop(style, LV_STYLE_MAX_HEIGHT, &v)) layout_diff = true;
424 else if(lv_style_get_prop(style, LV_STYLE_BORDER_WIDTH, &v)) layout_diff = true;
425 else if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_ANGLE, &v)) layout_diff = true;
426 else if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_ZOOM, &v)) layout_diff = true;
427
428 if(layout_diff) {
429 return _LV_STYLE_STATE_CMP_DIFF_LAYOUT;
430 }
431
432 /*Check for draw pad changes*/
433 if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_WIDTH, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
434 else if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_HEIGHT, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
435 else if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_ANGLE, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
436 else if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_ZOOM, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
437 else if(lv_style_get_prop(style, LV_STYLE_OUTLINE_OPA, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
438 else if(lv_style_get_prop(style, LV_STYLE_OUTLINE_PAD, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
439 else if(lv_style_get_prop(style, LV_STYLE_OUTLINE_WIDTH, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
440 else if(lv_style_get_prop(style, LV_STYLE_SHADOW_WIDTH, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
441 else if(lv_style_get_prop(style, LV_STYLE_SHADOW_OPA, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
442 else if(lv_style_get_prop(style, LV_STYLE_SHADOW_OFS_X, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
443 else if(lv_style_get_prop(style, LV_STYLE_SHADOW_OFS_Y, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
444 else if(lv_style_get_prop(style, LV_STYLE_SHADOW_SPREAD, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
445 else if(lv_style_get_prop(style, LV_STYLE_LINE_WIDTH, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
446 else if(res == _LV_STYLE_STATE_CMP_SAME) res = _LV_STYLE_STATE_CMP_DIFF_REDRAW;
447 }
448 }
449
450 return res;
451 }
452
lv_obj_fade_in(lv_obj_t * obj,uint32_t time,uint32_t delay)453 void lv_obj_fade_in(lv_obj_t * obj, uint32_t time, uint32_t delay)
454 {
455 lv_anim_t a;
456 lv_anim_init(&a);
457 lv_anim_set_var(&a, obj);
458 lv_anim_set_values(&a, 0, LV_OPA_COVER);
459 lv_anim_set_exec_cb(&a, fade_anim_cb);
460 lv_anim_set_ready_cb(&a, fade_in_anim_ready);
461 lv_anim_set_time(&a, time);
462 lv_anim_set_delay(&a, delay);
463 lv_anim_start(&a);
464 }
465
lv_obj_fade_out(lv_obj_t * obj,uint32_t time,uint32_t delay)466 void lv_obj_fade_out(lv_obj_t * obj, uint32_t time, uint32_t delay)
467 {
468 lv_anim_t a;
469 lv_anim_init(&a);
470 lv_anim_set_var(&a, obj);
471 lv_anim_set_values(&a, lv_obj_get_style_opa(obj, 0), LV_OPA_TRANSP);
472 lv_anim_set_exec_cb(&a, fade_anim_cb);
473 lv_anim_set_time(&a, time);
474 lv_anim_set_delay(&a, delay);
475 lv_anim_start(&a);
476 }
477
lv_obj_style_get_selector_state(lv_style_selector_t selector)478 lv_state_t lv_obj_style_get_selector_state(lv_style_selector_t selector)
479 {
480 return selector & 0xFFFF;
481 }
482
lv_obj_style_get_selector_part(lv_style_selector_t selector)483 lv_part_t lv_obj_style_get_selector_part(lv_style_selector_t selector)
484 {
485 return selector & 0xFF0000;
486 }
487
lv_obj_calculate_style_text_align(const struct _lv_obj_t * obj,lv_part_t part,const char * txt)488 lv_text_align_t lv_obj_calculate_style_text_align(const struct _lv_obj_t * obj, lv_part_t part, const char * txt)
489 {
490 lv_text_align_t align = lv_obj_get_style_text_align(obj, part);
491 lv_base_dir_t base_dir = lv_obj_get_style_base_dir(obj, part);
492 lv_bidi_calculate_align(&align, &base_dir, txt);
493 return align;
494 }
495
lv_obj_get_style_opa_recursive(const lv_obj_t * obj,lv_part_t part)496 lv_opa_t lv_obj_get_style_opa_recursive(const lv_obj_t * obj, lv_part_t part)
497 {
498
499 lv_opa_t opa_obj = lv_obj_get_style_opa(obj, part);
500 if(opa_obj <= LV_OPA_MIN) return LV_OPA_TRANSP;
501
502 lv_opa_t opa_final = LV_OPA_COVER;
503 if(opa_obj < LV_OPA_MAX) {
504 opa_final = ((uint32_t)opa_final * opa_obj) >> 8;
505 }
506
507 if(part != LV_PART_MAIN) {
508 part = LV_PART_MAIN;
509 }
510 else {
511 obj = lv_obj_get_parent(obj);
512 }
513
514 while(obj) {
515 opa_obj = lv_obj_get_style_opa(obj, part);
516 if(opa_obj <= LV_OPA_MIN) return LV_OPA_TRANSP;
517 if(opa_obj < LV_OPA_MAX) {
518 opa_final = ((uint32_t)opa_final * opa_obj) >> 8;
519 }
520
521 obj = lv_obj_get_parent(obj);
522 }
523
524 if(opa_final <= LV_OPA_MIN) return LV_OPA_TRANSP;
525 if(opa_final >= LV_OPA_MAX) return LV_OPA_COVER;
526 return opa_final;
527 }
528
529 /**********************
530 * STATIC FUNCTIONS
531 **********************/
532
533 /**
534 * Get the local style of an object for a given part and for a given state.
535 * If the local style for the part-state pair doesn't exist allocate and return it.
536 * @param obj pointer to an object
537 * @param selector OR-ed value of parts and state for which the style should be get
538 * @return pointer to the local style
539 */
get_local_style(lv_obj_t * obj,lv_style_selector_t selector)540 static lv_style_t * get_local_style(lv_obj_t * obj, lv_style_selector_t selector)
541 {
542 uint32_t i;
543 for(i = 0; i < obj->style_cnt; i++) {
544 if(obj->styles[i].is_local &&
545 obj->styles[i].selector == selector) {
546 return obj->styles[i].style;
547 }
548 }
549
550 obj->style_cnt++;
551 obj->styles = lv_mem_realloc(obj->styles, obj->style_cnt * sizeof(_lv_obj_style_t));
552 LV_ASSERT_MALLOC(obj->styles);
553
554 for(i = obj->style_cnt - 1; i > 0 ; i--) {
555 /*Copy only normal styles (not local and transition).
556 *The new local style will be added as the last local style*/
557 if(obj->styles[i - 1].is_local || obj->styles[i - 1].is_trans) break;
558 obj->styles[i] = obj->styles[i - 1];
559 }
560
561 lv_memset_00(&obj->styles[i], sizeof(_lv_obj_style_t));
562 obj->styles[i].style = lv_mem_alloc(sizeof(lv_style_t));
563 lv_style_init(obj->styles[i].style);
564 obj->styles[i].is_local = 1;
565 obj->styles[i].selector = selector;
566 return obj->styles[i].style;
567 }
568
569 /**
570 * Get the transition style of an object for a given part and for a given state.
571 * If the transition style for the part-state pair doesn't exist allocate and return it.
572 * @param obj pointer to an object
573 * @param selector OR-ed value of parts and state for which the style should be get
574 * @return pointer to the transition style
575 */
get_trans_style(lv_obj_t * obj,lv_style_selector_t selector)576 static _lv_obj_style_t * get_trans_style(lv_obj_t * obj, lv_style_selector_t selector)
577 {
578 uint32_t i;
579 for(i = 0; i < obj->style_cnt; i++) {
580 if(obj->styles[i].is_trans && obj->styles[i].selector == selector) break;
581 }
582
583 /*Already have a transition style for it*/
584 if(i != obj->style_cnt) return &obj->styles[i];
585
586 obj->style_cnt++;
587 obj->styles = lv_mem_realloc(obj->styles, obj->style_cnt * sizeof(_lv_obj_style_t));
588
589 for(i = obj->style_cnt - 1; i > 0 ; i--) {
590 obj->styles[i] = obj->styles[i - 1];
591 }
592
593 lv_memset_00(&obj->styles[0], sizeof(_lv_obj_style_t));
594 obj->styles[0].style = lv_mem_alloc(sizeof(lv_style_t));
595 lv_style_init(obj->styles[0].style);
596 obj->styles[0].is_trans = 1;
597 obj->styles[0].selector = selector;
598 return &obj->styles[0];
599 }
600
get_prop_core(const lv_obj_t * obj,lv_part_t part,lv_style_prop_t prop,lv_style_value_t * v)601 static lv_style_res_t get_prop_core(const lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop, lv_style_value_t * v)
602 {
603 uint8_t group = 1 << _lv_style_get_prop_group(prop);
604 int32_t weight = -1;
605 lv_state_t state = obj->state;
606 lv_state_t state_inv = ~state;
607 lv_style_value_t value_tmp;
608 bool skip_trans = obj->skip_trans;
609 uint32_t i;
610 lv_style_res_t found;
611 for(i = 0; i < obj->style_cnt; i++) {
612 _lv_obj_style_t * obj_style = &obj->styles[i];
613 if(obj_style->is_trans == false) break;
614 if(skip_trans) continue;
615
616 lv_part_t part_act = lv_obj_style_get_selector_part(obj->styles[i].selector);
617
618 if(part_act != part) continue;
619 if((obj_style->style->has_group & group) == 0) continue;
620 found = lv_style_get_prop(obj_style->style, prop, &value_tmp);
621 if(found == LV_STYLE_RES_FOUND) {
622 *v = value_tmp;
623 return LV_STYLE_RES_FOUND;
624 }
625 else if(found == LV_STYLE_RES_INHERIT) {
626 return LV_STYLE_RES_INHERIT;
627 }
628 }
629
630 for(; i < obj->style_cnt; i++) {
631 if((obj->styles[i].style->has_group & group) == 0) continue;
632 _lv_obj_style_t * obj_style = &obj->styles[i];
633 lv_part_t part_act = lv_obj_style_get_selector_part(obj->styles[i].selector);
634 lv_state_t state_act = lv_obj_style_get_selector_state(obj->styles[i].selector);
635 if(part_act != part) continue;
636
637 /*Be sure the style not specifies other state than the requested.
638 *E.g. For HOVER+PRESS object state, HOVER style only is OK, but HOVER+FOCUS style is not*/
639 if((state_act & state_inv)) continue;
640
641 /*Check only better candidates*/
642 if(state_act <= weight) continue;
643
644 found = lv_style_get_prop(obj_style->style, prop, &value_tmp);
645
646 if(found == LV_STYLE_RES_FOUND) {
647 if(state_act == state) {
648 *v = value_tmp;
649 return LV_STYLE_RES_FOUND;
650 }
651 if(weight < state_act) {
652 weight = state_act;
653 *v = value_tmp;
654 }
655 }
656 else if(found == LV_STYLE_RES_INHERIT) {
657 return LV_STYLE_RES_INHERIT;
658 }
659 }
660
661 if(weight >= 0) {
662 *v = value_tmp;
663 return LV_STYLE_RES_FOUND;
664 }
665 else return LV_STYLE_RES_NOT_FOUND;
666 }
667
668 /**
669 * Refresh the style of all children of an object. (Called recursively)
670 * @param style refresh objects only with this
671 * @param obj pointer to an object
672 */
report_style_change_core(void * style,lv_obj_t * obj)673 static void report_style_change_core(void * style, lv_obj_t * obj)
674 {
675 uint32_t i;
676 for(i = 0; i < obj->style_cnt; i++) {
677 if(style == NULL || obj->styles[i].style == style) {
678 lv_obj_refresh_style(obj, LV_PART_ANY, LV_STYLE_PROP_ANY);
679 break;
680 }
681 }
682
683 uint32_t child_cnt = lv_obj_get_child_cnt(obj);
684 for(i = 0; i < child_cnt; i++) {
685 report_style_change_core(style, obj->spec_attr->children[i]);
686 }
687 }
688
689 /**
690 * Recursively refresh the style of the children. Go deeper until a not NULL style is found
691 * because the NULL styles are inherited from the parent
692 * @param obj pointer to an object
693 */
refresh_children_style(lv_obj_t * obj)694 static void refresh_children_style(lv_obj_t * obj)
695 {
696 uint32_t i;
697 uint32_t child_cnt = lv_obj_get_child_cnt(obj);
698 for(i = 0; i < child_cnt; i++) {
699 lv_obj_t * child = obj->spec_attr->children[i];
700 lv_obj_invalidate(child);
701 lv_event_send(child, LV_EVENT_STYLE_CHANGED, NULL);
702 lv_obj_invalidate(child);
703
704 refresh_children_style(child); /*Check children too*/
705 }
706 }
707
708 /**
709 * Remove the transition from object's part's property.
710 * - Remove the transition from `_lv_obj_style_trans_ll` and free it
711 * - Delete pending transitions
712 * @param obj pointer to an object which transition(s) should be removed
713 * @param part a part of object or 0xFF to remove from all parts
714 * @param prop a property or 0xFF to remove all properties
715 * @param tr_limit delete transitions only "older" than this. `NULL` if not used
716 */
trans_del(lv_obj_t * obj,lv_part_t part,lv_style_prop_t prop,trans_t * tr_limit)717 static bool trans_del(lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop, trans_t * tr_limit)
718 {
719 trans_t * tr;
720 trans_t * tr_prev;
721 bool removed = false;
722 tr = _lv_ll_get_tail(&LV_GC_ROOT(_lv_obj_style_trans_ll));
723 while(tr != NULL) {
724 if(tr == tr_limit) break;
725
726 /*'tr' might be deleted, so get the next object while 'tr' is valid*/
727 tr_prev = _lv_ll_get_prev(&LV_GC_ROOT(_lv_obj_style_trans_ll), tr);
728
729 if(tr->obj == obj && (part == tr->selector || part == LV_PART_ANY) && (prop == tr->prop || prop == LV_STYLE_PROP_ANY)) {
730 /*Remove any transitioned properties from the trans. style
731 *to allow changing it by normal styles*/
732 uint32_t i;
733 for(i = 0; i < obj->style_cnt; i++) {
734 if(obj->styles[i].is_trans && (part == LV_PART_ANY || obj->styles[i].selector == part)) {
735 lv_style_remove_prop(obj->styles[i].style, tr->prop);
736 }
737 }
738
739 /*Free the transition descriptor too*/
740 lv_anim_del(tr, NULL);
741 _lv_ll_remove(&LV_GC_ROOT(_lv_obj_style_trans_ll), tr);
742 lv_mem_free(tr);
743 removed = true;
744
745 }
746 tr = tr_prev;
747 }
748 return removed;
749 }
750
trans_anim_cb(void * _tr,int32_t v)751 static void trans_anim_cb(void * _tr, int32_t v)
752 {
753 trans_t * tr = _tr;
754 lv_obj_t * obj = tr->obj;
755
756 uint32_t i;
757 for(i = 0; i < obj->style_cnt; i++) {
758 if(obj->styles[i].is_trans == 0 || obj->styles[i].selector != tr->selector) continue;
759
760 lv_style_value_t value_final;
761 switch(tr->prop) {
762
763 case LV_STYLE_BORDER_SIDE:
764 case LV_STYLE_BORDER_POST:
765 case LV_STYLE_BLEND_MODE:
766 if(v < 255) value_final.num = tr->start_value.num;
767 else value_final.num = tr->end_value.num;
768 break;
769 case LV_STYLE_TRANSITION:
770 case LV_STYLE_TEXT_FONT:
771 if(v < 255) value_final.ptr = tr->start_value.ptr;
772 else value_final.ptr = tr->end_value.ptr;
773 break;
774 case LV_STYLE_COLOR_FILTER_DSC:
775 if(tr->start_value.ptr == NULL) value_final.ptr = tr->end_value.ptr;
776 else if(tr->end_value.ptr == NULL) value_final.ptr = tr->start_value.ptr;
777 else if(v < 128) value_final.ptr = tr->start_value.ptr;
778 else value_final.ptr = tr->end_value.ptr;
779 break;
780 case LV_STYLE_BG_COLOR:
781 case LV_STYLE_BG_GRAD_COLOR:
782 case LV_STYLE_BORDER_COLOR:
783 case LV_STYLE_TEXT_COLOR:
784 case LV_STYLE_SHADOW_COLOR:
785 case LV_STYLE_OUTLINE_COLOR:
786 case LV_STYLE_IMG_RECOLOR:
787 if(v <= 0) value_final.color = tr->start_value.color;
788 else if(v >= 255) value_final.color = tr->end_value.color;
789 else value_final.color = lv_color_mix(tr->end_value.color, tr->start_value.color, v);
790 break;
791
792 default:
793 if(v == 0) value_final.num = tr->start_value.num;
794 else if(v == 255) value_final.num = tr->end_value.num;
795 else value_final.num = tr->start_value.num + ((int32_t)((int32_t)(tr->end_value.num - tr->start_value.num) * v) >> 8);
796 break;
797 }
798
799 lv_style_value_t old_value;
800 bool refr = true;
801 if(lv_style_get_prop(obj->styles[i].style, tr->prop, &old_value)) {
802 if(value_final.ptr == old_value.ptr && value_final.color.full == old_value.color.full &&
803 value_final.num == old_value.num) {
804 refr = false;
805 }
806 }
807 lv_style_set_prop(obj->styles[i].style, tr->prop, value_final);
808 if(refr) lv_obj_refresh_style(tr->obj, tr->selector, tr->prop);
809 break;
810
811 }
812
813 }
814
trans_anim_start_cb(lv_anim_t * a)815 static void trans_anim_start_cb(lv_anim_t * a)
816 {
817 trans_t * tr = a->var;
818
819 lv_part_t part = lv_obj_style_get_selector_part(tr->selector);
820 tr->start_value = lv_obj_get_style_prop(tr->obj, part, tr->prop);
821
822 /*Init prop to an invalid values to be sure `trans_del` won't delete this added `tr`*/
823 lv_style_prop_t prop_tmp = tr->prop;
824 tr->prop = LV_STYLE_PROP_INV;
825
826 /*Delete the related transitions if any*/
827 trans_del(tr->obj, part, prop_tmp, tr);
828
829 tr->prop = prop_tmp;
830
831 _lv_obj_style_t * style_trans = get_trans_style(tr->obj, tr->selector);
832 lv_style_set_prop(style_trans->style, tr->prop, tr->start_value); /*Be sure `trans_style` has a valid value*/
833
834 }
835
trans_anim_ready_cb(lv_anim_t * a)836 static void trans_anim_ready_cb(lv_anim_t * a)
837 {
838 trans_t * tr = a->var;
839 lv_obj_t * obj = tr->obj;
840 lv_style_prop_t prop = tr->prop;
841
842 /*Remove the transitioned property from trans. style
843 *if there no more transitions for this property
844 *It allows changing it by normal styles*/
845 bool running = false;
846 trans_t * tr_i;
847 _LV_LL_READ(&LV_GC_ROOT(_lv_obj_style_trans_ll), tr_i) {
848 if(tr_i != tr && tr_i->obj == tr->obj && tr_i->selector == tr->selector && tr_i->prop == tr->prop) {
849 running = true;
850 break;
851 }
852 }
853
854 if(!running) {
855 uint32_t i;
856 for(i = 0; i < obj->style_cnt; i++) {
857 if(obj->styles[i].is_trans && obj->styles[i].selector == tr->selector) {
858 _lv_ll_remove(&LV_GC_ROOT(_lv_obj_style_trans_ll), tr);
859 lv_mem_free(tr);
860
861 _lv_obj_style_t * obj_style = &obj->styles[i];
862 lv_style_remove_prop(obj_style->style, prop);
863
864 if(lv_style_is_empty(obj->styles[i].style)) {
865 lv_obj_remove_style(obj, obj_style->style, obj_style->selector);
866
867 }
868 break;
869 }
870 }
871 }
872 }
873
calculate_layer_type(lv_obj_t * obj)874 static lv_layer_type_t calculate_layer_type(lv_obj_t * obj)
875 {
876 if(lv_obj_get_style_transform_angle(obj, 0) != 0) return LV_LAYER_TYPE_TRANSFORM;
877 if(lv_obj_get_style_transform_zoom(obj, 0) != 256) return LV_LAYER_TYPE_TRANSFORM;
878 if(lv_obj_get_style_opa_layered(obj, 0) != LV_OPA_COVER) return LV_LAYER_TYPE_SIMPLE;
879 #if LV_DRAW_COMPLEX
880 if(lv_obj_get_style_blend_mode(obj, 0) != LV_BLEND_MODE_NORMAL) return LV_LAYER_TYPE_SIMPLE;
881 #endif
882 return LV_LAYER_TYPE_NONE;
883 }
884
fade_anim_cb(void * obj,int32_t v)885 static void fade_anim_cb(void * obj, int32_t v)
886 {
887 lv_obj_set_style_opa(obj, v, 0);
888 }
889
fade_in_anim_ready(lv_anim_t * a)890 static void fade_in_anim_ready(lv_anim_t * a)
891 {
892 lv_obj_remove_local_style_prop(a->var, LV_STYLE_OPA, 0);
893 }
894