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 bool get_prop_core(const lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop, lv_style_value_t * v);
48 static lv_style_value_t apply_color_filter(const lv_obj_t * obj, uint32_t part, lv_style_value_t v);
49 static void report_style_change_core(void * style, lv_obj_t * obj);
50 static void refresh_children_style(lv_obj_t * obj);
51 static bool trans_del(lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop, trans_t * tr_limit);
52 static void trans_anim_cb(void * _tr, int32_t v);
53 static void trans_anim_start_cb(lv_anim_t * a);
54 static void trans_anim_ready_cb(lv_anim_t * a);
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     if(prop & LV_STYLE_PROP_LAYOUT_REFR) {
178         if(part == LV_PART_ANY ||
179            part == LV_PART_MAIN ||
180            lv_obj_get_style_height(obj, 0) == LV_SIZE_CONTENT ||
181            lv_obj_get_style_width(obj, 0) == LV_SIZE_CONTENT) {
182             lv_event_send(obj, LV_EVENT_STYLE_CHANGED, NULL);
183             lv_obj_mark_layout_as_dirty(obj);
184         }
185     }
186     if((part == LV_PART_ANY || part == LV_PART_MAIN) && (prop == LV_STYLE_PROP_ANY ||
187                                                          (prop & LV_STYLE_PROP_PARENT_LAYOUT_REFR))) {
188         lv_obj_t * parent = lv_obj_get_parent(obj);
189         if(parent) lv_obj_mark_layout_as_dirty(parent);
190     }
191 
192     if(prop == LV_STYLE_PROP_ANY || (prop & LV_STYLE_PROP_EXT_DRAW)) {
193         lv_obj_refresh_ext_draw_size(obj);
194     }
195     lv_obj_invalidate(obj);
196 
197     if(prop == LV_STYLE_PROP_ANY ||
198        ((prop & LV_STYLE_PROP_INHERIT) && ((prop & LV_STYLE_PROP_EXT_DRAW) || (prop & LV_STYLE_PROP_LAYOUT_REFR)))) {
199         if(part != LV_PART_SCROLLBAR) {
200             refresh_children_style(obj);
201         }
202     }
203 }
204 
lv_obj_enable_style_refresh(bool en)205 void lv_obj_enable_style_refresh(bool en)
206 {
207     style_refr = en;
208 }
209 
lv_obj_get_style_prop(const lv_obj_t * obj,lv_part_t part,lv_style_prop_t prop)210 lv_style_value_t lv_obj_get_style_prop(const lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop)
211 {
212     lv_style_value_t value_act;
213     bool inherit = prop & LV_STYLE_PROP_INHERIT ? true : false;
214     bool filter = prop & LV_STYLE_PROP_FILTER ? true : false;
215     if(filter) {
216         prop &= ~LV_STYLE_PROP_FILTER;
217     }
218     bool found = false;
219     while(obj) {
220         found = get_prop_core(obj, part, prop, &value_act);
221         if(found) break;
222         if(!inherit) break;
223 
224         /*If not found, check the `MAIN` style first*/
225         if(part != LV_PART_MAIN) {
226             part = LV_PART_MAIN;
227             continue;
228         }
229 
230         /*Check the parent too.*/
231         obj = lv_obj_get_parent(obj);
232     }
233 
234     if(!found) {
235         if(part == LV_PART_MAIN && (prop == LV_STYLE_WIDTH || prop == LV_STYLE_HEIGHT)) {
236             const lv_obj_class_t * cls = obj->class_p;
237             while(cls) {
238                 if(prop == LV_STYLE_WIDTH) {
239                     if(cls->width_def != 0) break;
240                 }
241                 else {
242                     if(cls->height_def != 0) break;
243                 }
244                 cls = cls->base_class;
245             }
246 
247             value_act.num = prop == LV_STYLE_WIDTH ? cls->width_def : cls->height_def;
248         }
249         else {
250             value_act = lv_style_prop_get_default(prop);
251         }
252     }
253     if(filter) value_act = apply_color_filter(obj, part, value_act);
254     return value_act;
255 }
256 
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)257 void lv_obj_set_local_style_prop(lv_obj_t * obj, lv_style_prop_t prop, lv_style_value_t value,
258                                  lv_style_selector_t selector)
259 {
260     lv_style_t * style = get_local_style(obj, selector);
261     lv_style_set_prop(style, prop, value);
262     lv_obj_refresh_style(obj, selector, prop);
263 }
264 
265 
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)266 lv_res_t lv_obj_get_local_style_prop(lv_obj_t * obj, lv_style_prop_t prop, lv_style_value_t * value,
267                                      lv_style_selector_t selector)
268 {
269     uint32_t i;
270     for(i = 0; i < obj->style_cnt; i++) {
271         if(obj->styles[i].is_local &&
272            obj->styles[i].selector == selector) {
273             return lv_style_get_prop(obj->styles[i].style, prop, value);
274         }
275     }
276 
277     return LV_RES_INV;
278 }
279 
lv_obj_remove_local_style_prop(lv_obj_t * obj,lv_style_prop_t prop,lv_style_selector_t selector)280 bool lv_obj_remove_local_style_prop(lv_obj_t * obj, lv_style_prop_t prop, lv_style_selector_t selector)
281 {
282     LV_ASSERT_OBJ(obj, MY_CLASS);
283 
284     uint32_t i;
285     /*Find the style*/
286     for(i = 0; i < obj->style_cnt; i++) {
287         if(obj->styles[i].is_local &&
288            obj->styles[i].selector == selector) {
289             break;
290         }
291     }
292 
293     /*The style is not found*/
294     if(i == obj->style_cnt) return false;
295 
296     return lv_style_remove_prop(obj->styles[i].style, prop);
297 }
298 
_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)299 void _lv_obj_style_create_transition(lv_obj_t * obj, lv_part_t part, lv_state_t prev_state, lv_state_t new_state,
300                                      const _lv_obj_style_transition_dsc_t * tr_dsc)
301 {
302     trans_t * tr;
303 
304     /*Get the previous and current values*/
305     obj->skip_trans = 1;
306     obj->state = prev_state;
307     lv_style_value_t v1 = lv_obj_get_style_prop(obj, part, tr_dsc->prop);
308     obj->state = new_state;
309     lv_style_value_t v2 = lv_obj_get_style_prop(obj, part, tr_dsc->prop);
310     obj->skip_trans = 0;
311 
312     if(v1.ptr == v2.ptr && v1.num == v2.num && v1.color.full == v2.color.full)  return;
313     obj->state = prev_state;
314     v1 = lv_obj_get_style_prop(obj, part, tr_dsc->prop);
315     obj->state = new_state;
316 
317     _lv_obj_style_t * style_trans = get_trans_style(obj, part);
318     lv_style_set_prop(style_trans->style, tr_dsc->prop, v1);   /*Be sure `trans_style` has a valid value*/
319 
320     if(tr_dsc->prop == LV_STYLE_RADIUS) {
321         if(v1.num == LV_RADIUS_CIRCLE || v2.num == LV_RADIUS_CIRCLE) {
322             lv_coord_t whalf = lv_obj_get_width(obj) / 2;
323             lv_coord_t hhalf = lv_obj_get_width(obj) / 2;
324             if(v1.num == LV_RADIUS_CIRCLE) v1.num = LV_MIN(whalf + 1, hhalf + 1);
325             if(v2.num == LV_RADIUS_CIRCLE) v2.num = LV_MIN(whalf + 1, hhalf + 1);
326         }
327     }
328 
329     tr = _lv_ll_ins_head(&LV_GC_ROOT(_lv_obj_style_trans_ll));
330     LV_ASSERT_MALLOC(tr);
331     if(tr == NULL) return;
332     tr->start_value = v1;
333     tr->end_value = v2;
334 
335     if(tr) {
336         tr->obj = obj;
337         tr->prop = tr_dsc->prop;
338         tr->selector = part;
339 
340         lv_anim_t a;
341         lv_anim_init(&a);
342         lv_anim_set_var(&a, tr);
343         lv_anim_set_exec_cb(&a, trans_anim_cb);
344         lv_anim_set_start_cb(&a, trans_anim_start_cb);
345         lv_anim_set_ready_cb(&a, trans_anim_ready_cb);
346         lv_anim_set_values(&a, 0x00, 0xFF);
347         lv_anim_set_time(&a, tr_dsc->time);
348         lv_anim_set_delay(&a, tr_dsc->delay);
349         lv_anim_set_path_cb(&a, tr_dsc->path_cb);
350         lv_anim_set_early_apply(&a, false);
351 #if LV_USE_USER_DATA
352         a.user_data = tr_dsc->user_data;
353 #endif
354         lv_anim_start(&a);
355     }
356 }
357 
_lv_obj_style_state_compare(lv_obj_t * obj,lv_state_t state1,lv_state_t state2)358 _lv_style_state_cmp_t _lv_obj_style_state_compare(lv_obj_t * obj, lv_state_t state1, lv_state_t state2)
359 {
360     _lv_style_state_cmp_t res = _LV_STYLE_STATE_CMP_SAME;
361 
362     /*Are there any new styles for the new state?*/
363     uint32_t i;
364     for(i = 0; i < obj->style_cnt; i++) {
365         if(obj->styles[i].is_trans) continue;
366 
367         lv_state_t state_act = lv_obj_style_get_selector_state(obj->styles[i].selector);
368         /*The style is valid for a state but not the other*/
369         bool valid1 = state_act & (~state1) ? false : true;
370         bool valid2 = state_act & (~state2) ? false : true;
371         if(valid1 != valid2) {
372             lv_style_t * style = obj->styles[i].style;
373             lv_style_value_t v;
374             /*If there is layout difference on the main part, return immediately. There is no more serious difference*/
375             bool layout_diff = false;
376             if(lv_style_get_prop(style, LV_STYLE_PAD_TOP, &v))layout_diff = true;
377             else if(lv_style_get_prop(style, LV_STYLE_PAD_BOTTOM, &v)) layout_diff = true;
378             else if(lv_style_get_prop(style, LV_STYLE_PAD_LEFT, &v)) layout_diff = true;
379             else if(lv_style_get_prop(style, LV_STYLE_PAD_RIGHT, &v)) layout_diff = true;
380             else if(lv_style_get_prop(style, LV_STYLE_PAD_COLUMN, &v)) layout_diff = true;
381             else if(lv_style_get_prop(style, LV_STYLE_PAD_ROW, &v)) layout_diff = true;
382             else if(lv_style_get_prop(style, LV_STYLE_LAYOUT, &v)) layout_diff = true;
383             else if(lv_style_get_prop(style, LV_STYLE_TRANSLATE_X, &v)) layout_diff = true;
384             else if(lv_style_get_prop(style, LV_STYLE_TRANSLATE_Y, &v)) layout_diff = true;
385             else if(lv_style_get_prop(style, LV_STYLE_WIDTH, &v)) layout_diff = true;
386             else if(lv_style_get_prop(style, LV_STYLE_HEIGHT, &v)) layout_diff = true;
387             else if(lv_style_get_prop(style, LV_STYLE_MIN_WIDTH, &v)) layout_diff = true;
388             else if(lv_style_get_prop(style, LV_STYLE_MAX_WIDTH, &v)) layout_diff = true;
389             else if(lv_style_get_prop(style, LV_STYLE_MIN_HEIGHT, &v)) layout_diff = true;
390             else if(lv_style_get_prop(style, LV_STYLE_MAX_HEIGHT, &v)) layout_diff = true;
391             else if(lv_style_get_prop(style, LV_STYLE_BORDER_WIDTH, &v)) layout_diff = true;
392             else if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_ANGLE, &v)) layout_diff = true;
393             else if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_ZOOM, &v)) layout_diff = true;
394 
395             if(layout_diff) {
396                 return _LV_STYLE_STATE_CMP_DIFF_LAYOUT;
397             }
398 
399             /*Check for draw pad changes*/
400             if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_WIDTH, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
401             else if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_HEIGHT, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
402             else if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_ANGLE, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
403             else if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_ZOOM, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
404             else if(lv_style_get_prop(style, LV_STYLE_OUTLINE_OPA, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
405             else if(lv_style_get_prop(style, LV_STYLE_OUTLINE_PAD, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
406             else if(lv_style_get_prop(style, LV_STYLE_OUTLINE_WIDTH, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
407             else if(lv_style_get_prop(style, LV_STYLE_SHADOW_WIDTH, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
408             else if(lv_style_get_prop(style, LV_STYLE_SHADOW_OPA, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
409             else if(lv_style_get_prop(style, LV_STYLE_SHADOW_OFS_X, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
410             else if(lv_style_get_prop(style, LV_STYLE_SHADOW_OFS_Y, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
411             else if(lv_style_get_prop(style, LV_STYLE_SHADOW_SPREAD, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
412             else if(lv_style_get_prop(style, LV_STYLE_LINE_WIDTH, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
413             else if(res == _LV_STYLE_STATE_CMP_SAME) res = _LV_STYLE_STATE_CMP_DIFF_REDRAW;
414         }
415     }
416 
417     return res;
418 }
419 
lv_obj_fade_in(lv_obj_t * obj,uint32_t time,uint32_t delay)420 void lv_obj_fade_in(lv_obj_t * obj, uint32_t time, uint32_t delay)
421 {
422     lv_anim_t a;
423     lv_anim_init(&a);
424     lv_anim_set_var(&a, obj);
425     lv_anim_set_values(&a, 0, LV_OPA_COVER);
426     lv_anim_set_exec_cb(&a, fade_anim_cb);
427     lv_anim_set_ready_cb(&a, fade_in_anim_ready);
428     lv_anim_set_time(&a, time);
429     lv_anim_set_delay(&a, delay);
430     lv_anim_start(&a);
431 }
432 
lv_obj_fade_out(lv_obj_t * obj,uint32_t time,uint32_t delay)433 void lv_obj_fade_out(lv_obj_t * obj, uint32_t time, uint32_t delay)
434 {
435     lv_anim_t a;
436     lv_anim_init(&a);
437     lv_anim_set_var(&a, obj);
438     lv_anim_set_values(&a, lv_obj_get_style_opa(obj, 0), LV_OPA_TRANSP);
439     lv_anim_set_exec_cb(&a, fade_anim_cb);
440     lv_anim_set_time(&a, time);
441     lv_anim_set_delay(&a, delay);
442     lv_anim_start(&a);
443 }
444 
lv_obj_style_get_selector_state(lv_style_selector_t selector)445 lv_state_t lv_obj_style_get_selector_state(lv_style_selector_t selector)
446 {
447     return selector & 0xFFFF;
448 }
449 
lv_obj_style_get_selector_part(lv_style_selector_t selector)450 lv_part_t lv_obj_style_get_selector_part(lv_style_selector_t selector)
451 {
452     return selector & 0xFF0000;
453 }
454 
455 
lv_obj_calculate_style_text_align(const struct _lv_obj_t * obj,lv_part_t part,const char * txt)456 lv_text_align_t lv_obj_calculate_style_text_align(const struct _lv_obj_t * obj, lv_part_t part, const char * txt)
457 {
458     lv_text_align_t align = lv_obj_get_style_text_align(obj, part);
459     lv_base_dir_t base_dir = lv_obj_get_style_base_dir(obj, part);
460     lv_bidi_calculate_align(&align, &base_dir, txt);
461     return align;
462 }
463 
464 /**********************
465  *   STATIC FUNCTIONS
466  **********************/
467 
468 /**
469  * Get the local style of an object for a given part and for a given state.
470  * If the local style for the part-state pair doesn't exist allocate and return it.
471  * @param obj pointer to an object
472  * @param selector OR-ed value of parts and state for which the style should be get
473  * @return pointer to the local style
474  */
get_local_style(lv_obj_t * obj,lv_style_selector_t selector)475 static lv_style_t * get_local_style(lv_obj_t * obj, lv_style_selector_t selector)
476 {
477     uint32_t i;
478     for(i = 0; i < obj->style_cnt; i++) {
479         if(obj->styles[i].is_local &&
480            obj->styles[i].selector == selector) {
481             return obj->styles[i].style;
482         }
483     }
484 
485     obj->style_cnt++;
486     obj->styles = lv_mem_realloc(obj->styles, obj->style_cnt * sizeof(_lv_obj_style_t));
487     LV_ASSERT_MALLOC(obj->styles);
488 
489     for(i = obj->style_cnt - 1; i > 0 ; i--) {
490         /*Copy only normal styles (not local and transition).
491          *The new local style will be added as the last local style*/
492         if(obj->styles[i - 1].is_local || obj->styles[i - 1].is_trans) break;
493         obj->styles[i] = obj->styles[i - 1];
494     }
495 
496     lv_memset_00(&obj->styles[i], sizeof(_lv_obj_style_t));
497     obj->styles[i].style = lv_mem_alloc(sizeof(lv_style_t));
498     lv_style_init(obj->styles[i].style);
499     obj->styles[i].is_local = 1;
500     obj->styles[i].selector = selector;
501     return obj->styles[i].style;
502 }
503 
504 /**
505  * Get the transition style of an object for a given part and for a given state.
506  * If the transition style for the part-state pair doesn't exist allocate and return it.
507  * @param obj   pointer to an object
508  * @param selector OR-ed value of parts and state for which the style should be get
509  * @return pointer to the transition style
510  */
get_trans_style(lv_obj_t * obj,lv_style_selector_t selector)511 static _lv_obj_style_t * get_trans_style(lv_obj_t * obj,  lv_style_selector_t selector)
512 {
513     uint32_t i;
514     for(i = 0; i < obj->style_cnt; i++) {
515         if(obj->styles[i].is_trans && obj->styles[i].selector == selector) break;
516     }
517 
518     /*Already have a transition style for it*/
519     if(i != obj->style_cnt) return &obj->styles[i];
520 
521     obj->style_cnt++;
522     obj->styles = lv_mem_realloc(obj->styles, obj->style_cnt * sizeof(_lv_obj_style_t));
523 
524     for(i = obj->style_cnt - 1; i > 0 ; i--) {
525         obj->styles[i] = obj->styles[i - 1];
526     }
527 
528     lv_memset_00(&obj->styles[0], sizeof(_lv_obj_style_t));
529     obj->styles[0].style = lv_mem_alloc(sizeof(lv_style_t));
530     lv_style_init(obj->styles[0].style);
531     obj->styles[0].is_trans = 1;
532     obj->styles[0].selector = selector;
533     return &obj->styles[0];
534 }
535 
536 
get_prop_core(const lv_obj_t * obj,lv_part_t part,lv_style_prop_t prop,lv_style_value_t * v)537 static bool get_prop_core(const lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop, lv_style_value_t * v)
538 {
539     uint8_t group = 1 << _lv_style_get_prop_group(prop);
540     int32_t weight = -1;
541     lv_state_t state = obj->state;
542     lv_state_t state_inv = ~state;
543     lv_style_value_t value_tmp;
544     bool skip_trans = obj->skip_trans;
545     uint32_t i;
546     bool found;
547     for(i = 0; i < obj->style_cnt; i++) {
548         _lv_obj_style_t * obj_style = &obj->styles[i];
549         if(obj_style->is_trans == false) break;
550         if(skip_trans) continue;
551 
552         lv_part_t part_act = lv_obj_style_get_selector_part(obj->styles[i].selector);
553 
554         if(part_act != part) continue;
555         if((obj_style->style->has_group & group) == 0) continue;
556         found = lv_style_get_prop(obj_style->style, prop, &value_tmp);
557         if(found) {
558             *v = value_tmp;
559             return true;
560         }
561     }
562 
563     for(; i < obj->style_cnt; i++) {
564         _lv_obj_style_t * obj_style = &obj->styles[i];
565         lv_part_t part_act = lv_obj_style_get_selector_part(obj->styles[i].selector);
566         lv_state_t state_act = lv_obj_style_get_selector_state(obj->styles[i].selector);
567         if(part_act != part) continue;
568 
569         if((obj_style->style->has_group & group) == 0) continue;
570 
571         /*Be sure the style not specifies other state than the requested.
572          *E.g. For HOVER+PRESS object state, HOVER style only is OK, but HOVER+FOCUS style is not*/
573         if((state_act & state_inv)) continue;
574 
575         /*Check only better candidates*/
576         if(state_act <= weight) continue;
577 
578         found = lv_style_get_prop(obj_style->style, prop, &value_tmp);
579 
580         if(found) {
581             if(state_act == state) {
582                 *v = value_tmp;
583                 return true;
584             }
585             if(weight < state_act) {
586                 weight = state_act;
587                 *v = value_tmp;
588             }
589         }
590     }
591 
592     if(weight >= 0) {
593         *v = value_tmp;
594         return true;
595     }
596     else return false;
597 }
598 
apply_color_filter(const lv_obj_t * obj,uint32_t part,lv_style_value_t v)599 static lv_style_value_t apply_color_filter(const lv_obj_t * obj, uint32_t part, lv_style_value_t v)
600 {
601     if(obj == NULL) return v;
602     const lv_color_filter_dsc_t * f = lv_obj_get_style_color_filter_dsc(obj, part);
603     if(f && f->filter_cb) {
604         lv_opa_t f_opa = lv_obj_get_style_color_filter_opa(obj, part);
605         if(f_opa != 0) v.color = f->filter_cb(f, v.color, f_opa);
606     }
607     return v;
608 }
609 
610 /**
611  * Refresh the style of all children of an object. (Called recursively)
612  * @param style refresh objects only with this
613  * @param obj pointer to an object
614  */
report_style_change_core(void * style,lv_obj_t * obj)615 static void report_style_change_core(void * style, lv_obj_t * obj)
616 {
617     uint32_t i;
618     for(i = 0; i < obj->style_cnt; i++) {
619         if(style == NULL || obj->styles[i].style == style) {
620             lv_obj_refresh_style(obj, LV_PART_ANY, LV_STYLE_PROP_ANY);
621             break;
622         }
623     }
624 
625     uint32_t child_cnt = lv_obj_get_child_cnt(obj);
626     for(i = 0; i < child_cnt; i++) {
627         report_style_change_core(style, obj->spec_attr->children[i]);
628     }
629 }
630 
631 /**
632  * Recursively refresh the style of the children. Go deeper until a not NULL style is found
633  * because the NULL styles are inherited from the parent
634  * @param obj pointer to an object
635  */
refresh_children_style(lv_obj_t * obj)636 static void refresh_children_style(lv_obj_t * obj)
637 {
638     uint32_t i;
639     uint32_t child_cnt = lv_obj_get_child_cnt(obj);
640     for(i = 0; i < child_cnt; i++) {
641         lv_obj_t * child = obj->spec_attr->children[i];
642         lv_obj_invalidate(child);
643         lv_event_send(child, LV_EVENT_STYLE_CHANGED, NULL);
644         lv_obj_invalidate(child);
645 
646         refresh_children_style(child); /*Check children too*/
647     }
648 }
649 
650 /**
651  * Remove the transition from object's part's property.
652  * - Remove the transition from `_lv_obj_style_trans_ll` and free it
653  * - Delete pending transitions
654  * @param obj pointer to an object which transition(s) should be removed
655  * @param part a part of object or 0xFF to remove from all parts
656  * @param prop a property or 0xFF to remove all properties
657  * @param tr_limit delete transitions only "older" than this. `NULL` if not used
658  */
trans_del(lv_obj_t * obj,lv_part_t part,lv_style_prop_t prop,trans_t * tr_limit)659 static bool trans_del(lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop, trans_t * tr_limit)
660 {
661     trans_t * tr;
662     trans_t * tr_prev;
663     bool removed = false;
664     tr = _lv_ll_get_tail(&LV_GC_ROOT(_lv_obj_style_trans_ll));
665     while(tr != NULL) {
666         if(tr == tr_limit) break;
667 
668         /*'tr' might be deleted, so get the next object while 'tr' is valid*/
669         tr_prev = _lv_ll_get_prev(&LV_GC_ROOT(_lv_obj_style_trans_ll), tr);
670 
671         if(tr->obj == obj && (part == tr->selector || part == LV_PART_ANY) && (prop == tr->prop || prop == LV_STYLE_PROP_ANY)) {
672             /*Remove the transitioned property from trans. style
673              *to allow changing it by normal styles*/
674             uint32_t i;
675             for(i = 0; i < obj->style_cnt; i++) {
676                 if(obj->styles[i].is_trans && (part == LV_PART_ANY || obj->styles[i].selector == part)) {
677                     lv_style_remove_prop(obj->styles[i].style, tr->prop);
678                     lv_anim_del(tr, NULL);
679                     _lv_ll_remove(&LV_GC_ROOT(_lv_obj_style_trans_ll), tr);
680                     lv_mem_free(tr);
681                     removed = true;
682                 }
683             }
684 
685         }
686         tr = tr_prev;
687     }
688     return removed;
689 }
690 
trans_anim_cb(void * _tr,int32_t v)691 static void trans_anim_cb(void * _tr, int32_t v)
692 {
693     trans_t * tr = _tr;
694     lv_obj_t * obj = tr->obj;
695 
696     uint32_t i;
697     for(i = 0; i < obj->style_cnt; i++) {
698         if(obj->styles[i].is_trans == 0 || obj->styles[i].selector != tr->selector) continue;
699 
700         lv_style_value_t value_final;
701         switch(tr->prop) {
702 
703             case LV_STYLE_BORDER_SIDE:
704             case LV_STYLE_BORDER_POST:
705             case LV_STYLE_BLEND_MODE:
706                 if(v < 255) value_final.num = tr->start_value.num;
707                 else value_final.num = tr->end_value.num;
708                 break;
709             case LV_STYLE_TRANSITION:
710             case LV_STYLE_TEXT_FONT:
711                 if(v < 255) value_final.ptr = tr->start_value.ptr;
712                 else value_final.ptr = tr->end_value.ptr;
713                 break;
714             case LV_STYLE_COLOR_FILTER_DSC:
715                 if(tr->start_value.ptr == NULL) value_final.ptr = tr->end_value.ptr;
716                 else if(tr->end_value.ptr == NULL) value_final.ptr = tr->start_value.ptr;
717                 else if(v < 128) value_final.ptr = tr->start_value.ptr;
718                 else value_final.ptr = tr->end_value.ptr;
719                 break;
720             case LV_STYLE_BG_COLOR:
721             case LV_STYLE_BORDER_COLOR:
722             case LV_STYLE_TEXT_COLOR:
723             case LV_STYLE_SHADOW_COLOR:
724             case LV_STYLE_OUTLINE_COLOR:
725             case LV_STYLE_IMG_RECOLOR:
726                 if(v <= 0) value_final.color = tr->start_value.color;
727                 else if(v >= 255) value_final.color = tr->end_value.color;
728                 else value_final.color = lv_color_mix(tr->end_value.color, tr->start_value.color, v);
729                 break;
730 
731             default:
732                 if(v == 0) value_final.num = tr->start_value.num;
733                 else if(v == 255) value_final.num = tr->end_value.num;
734                 else value_final.num = tr->start_value.num + ((int32_t)((int32_t)(tr->end_value.num - tr->start_value.num) * v) >> 8);
735                 break;
736         }
737 
738         lv_style_value_t old_value;
739         bool refr = true;
740         if(lv_style_get_prop(obj->styles[i].style, tr->prop, &old_value)) {
741             if(value_final.ptr == old_value.ptr && value_final.color.full == old_value.color.full &&
742                value_final.num == old_value.num) {
743                 refr = false;
744             }
745         }
746         lv_style_set_prop(obj->styles[i].style, tr->prop, value_final);
747         if(refr) lv_obj_refresh_style(tr->obj, tr->selector, tr->prop);
748         break;
749 
750     }
751 
752 }
753 
trans_anim_start_cb(lv_anim_t * a)754 static void trans_anim_start_cb(lv_anim_t * a)
755 {
756     trans_t * tr = a->var;
757 
758     lv_part_t part = lv_obj_style_get_selector_part(tr->selector);
759     tr->start_value = lv_obj_get_style_prop(tr->obj, part, tr->prop);
760 
761     /*Init prop to an invalid values to be sure `trans_del` won't delete this added `tr`*/
762     lv_style_prop_t prop_tmp = tr->prop;
763     tr->prop = LV_STYLE_PROP_INV;
764 
765     /*Delete the related transitions if any*/
766     trans_del(tr->obj, part, prop_tmp, tr);
767 
768     tr->prop = prop_tmp;
769 
770     _lv_obj_style_t * style_trans = get_trans_style(tr->obj, tr->selector);
771     lv_style_set_prop(style_trans->style, tr->prop, tr->start_value);   /*Be sure `trans_style` has a valid value*/
772 
773 }
774 
trans_anim_ready_cb(lv_anim_t * a)775 static void trans_anim_ready_cb(lv_anim_t * a)
776 {
777     trans_t * tr = a->var;
778     lv_obj_t * obj = tr->obj;
779     lv_style_prop_t prop = tr->prop;
780 
781     /*Remove the transitioned property from trans. style
782      *if there no more transitions for this property
783      *It allows changing it by normal styles*/
784     bool running = false;
785     trans_t * tr_i;
786     _LV_LL_READ(&LV_GC_ROOT(_lv_obj_style_trans_ll), tr_i) {
787         if(tr_i != tr && tr_i->obj == tr->obj && tr_i->selector == tr->selector && tr_i->prop == tr->prop) {
788             running = true;
789             break;
790         }
791     }
792 
793     if(!running) {
794         uint32_t i;
795         for(i = 0; i < obj->style_cnt; i++) {
796             if(obj->styles[i].is_trans && obj->styles[i].selector == tr->selector) {
797                 _lv_ll_remove(&LV_GC_ROOT(_lv_obj_style_trans_ll), tr);
798                 lv_mem_free(tr);
799 
800                 _lv_obj_style_t * obj_style = &obj->styles[i];
801                 lv_style_remove_prop(obj_style->style, prop);
802 
803                 if(lv_style_is_empty(obj->styles[i].style)) {
804                     lv_obj_remove_style(obj, obj_style->style, obj_style->selector);
805 
806                 }
807                 break;
808             }
809         }
810     }
811 }
812 
fade_anim_cb(void * obj,int32_t v)813 static void fade_anim_cb(void * obj, int32_t v)
814 {
815     lv_obj_set_style_opa(obj, v, 0);
816 }
817 
fade_in_anim_ready(lv_anim_t * a)818 static void fade_in_anim_ready(lv_anim_t * a)
819 {
820     lv_obj_remove_local_style_prop(a->var, LV_STYLE_OPA, 0);
821 }
822 
823 
824