1 /**
2  * @file lv_obj.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_obj.h"
10 #include "lv_indev.h"
11 #include "lv_refr.h"
12 #include "lv_group.h"
13 #include "lv_disp.h"
14 #include "lv_theme.h"
15 #include "../misc/lv_assert.h"
16 #include "../draw/lv_draw.h"
17 #include "../misc/lv_anim.h"
18 #include "../misc/lv_timer.h"
19 #include "../misc/lv_async.h"
20 #include "../misc/lv_fs.h"
21 #include "../misc/lv_gc.h"
22 #include "../misc/lv_math.h"
23 #include "../misc/lv_log.h"
24 #include "../hal/lv_hal.h"
25 #include "../extra/lv_extra.h"
26 #include <stdint.h>
27 #include <string.h>
28 
29 #if LV_USE_GPU_STM32_DMA2D
30     #include "../draw/stm32_dma2d/lv_gpu_stm32_dma2d.h"
31 #endif
32 
33 #if LV_USE_GPU_SWM341_DMA2D
34     #include "../draw/swm341_dma2d/lv_gpu_swm341_dma2d.h"
35 #endif
36 
37 #if LV_USE_GPU_NXP_PXP && LV_USE_GPU_NXP_PXP_AUTO_INIT
38     #include "../draw/nxp/pxp/lv_gpu_nxp_pxp.h"
39 #endif
40 
41 /*********************
42  *      DEFINES
43  *********************/
44 #define MY_CLASS &lv_obj_class
45 #define LV_OBJ_DEF_WIDTH    (LV_DPX(100))
46 #define LV_OBJ_DEF_HEIGHT   (LV_DPX(50))
47 #define STYLE_TRANSITION_MAX 32
48 
49 /**********************
50  *      TYPEDEFS
51  **********************/
52 
53 /**********************
54  *  STATIC PROTOTYPES
55  **********************/
56 static void lv_obj_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
57 static void lv_obj_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
58 static void lv_obj_draw(lv_event_t * e);
59 static void lv_obj_event(const lv_obj_class_t * class_p, lv_event_t * e);
60 static void draw_scrollbar(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx);
61 static lv_res_t scrollbar_init_draw_dsc(lv_obj_t * obj, lv_draw_rect_dsc_t * dsc);
62 static bool obj_valid_child(const lv_obj_t * parent, const lv_obj_t * obj_to_find);
63 static void lv_obj_set_state(lv_obj_t * obj, lv_state_t new_state);
64 
65 /**********************
66  *  STATIC VARIABLES
67  **********************/
68 static bool lv_initialized = false;
69 const lv_obj_class_t lv_obj_class = {
70     .constructor_cb = lv_obj_constructor,
71     .destructor_cb = lv_obj_destructor,
72     .event_cb = lv_obj_event,
73     .width_def = LV_DPI_DEF,
74     .height_def = LV_DPI_DEF,
75     .editable = LV_OBJ_CLASS_EDITABLE_FALSE,
76     .group_def = LV_OBJ_CLASS_GROUP_DEF_FALSE,
77     .instance_size = (sizeof(lv_obj_t)),
78     .base_class = NULL,
79 };
80 
81 /**********************
82  *      MACROS
83  **********************/
84 
85 /**********************
86  *   GLOBAL FUNCTIONS
87  **********************/
88 
lv_is_initialized(void)89 bool lv_is_initialized(void)
90 {
91     return lv_initialized;
92 }
93 
lv_init(void)94 void lv_init(void)
95 {
96     /*Do nothing if already initialized*/
97     if(lv_initialized) {
98         LV_LOG_WARN("lv_init: already inited");
99         return;
100     }
101 
102     LV_LOG_INFO("begin");
103 
104     /*Initialize the misc modules*/
105     lv_mem_init();
106 
107     _lv_timer_core_init();
108 
109     _lv_fs_init();
110 
111     _lv_anim_core_init();
112 
113     _lv_group_init();
114 
115     lv_draw_init();
116 
117 #if LV_USE_GPU_STM32_DMA2D
118     /*Initialize DMA2D GPU*/
119     lv_draw_stm32_dma2d_init();
120 #endif
121 
122 #if LV_USE_GPU_SWM341_DMA2D
123     /*Initialize DMA2D GPU*/
124     lv_draw_swm341_dma2d_init();
125 #endif
126 
127 #if LV_USE_GPU_NXP_PXP && LV_USE_GPU_NXP_PXP_AUTO_INIT
128     PXP_COND_STOP(!lv_gpu_nxp_pxp_init(), "PXP init failed.");
129 #endif
130 
131     _lv_obj_style_init();
132     _lv_ll_init(&LV_GC_ROOT(_lv_disp_ll), sizeof(lv_disp_t));
133     _lv_ll_init(&LV_GC_ROOT(_lv_indev_ll), sizeof(lv_indev_t));
134 
135     /*Initialize the screen refresh system*/
136     _lv_refr_init();
137 
138     _lv_img_decoder_init();
139 #if LV_IMG_CACHE_DEF_SIZE
140     lv_img_cache_set_size(LV_IMG_CACHE_DEF_SIZE);
141 #endif
142     /*Test if the IDE has UTF-8 encoding*/
143     char * txt = "Á";
144 
145     uint8_t * txt_u8 = (uint8_t *)txt;
146     if(txt_u8[0] != 0xc3 || txt_u8[1] != 0x81 || txt_u8[2] != 0x00) {
147         LV_LOG_WARN("The strings have no UTF-8 encoding. Non-ASCII characters won't be displayed.");
148     }
149 
150     uint32_t endianess_test = 0x11223344;
151     uint8_t * endianess_test_p = (uint8_t *) &endianess_test;
152     bool big_endian = endianess_test_p[0] == 0x11 ? true : false;
153 
154     if(big_endian) {
155         LV_ASSERT_MSG(LV_BIG_ENDIAN_SYSTEM == 1,
156                       "It's a big endian system but LV_BIG_ENDIAN_SYSTEM is not enabled in lv_conf.h");
157     }
158     else {
159         LV_ASSERT_MSG(LV_BIG_ENDIAN_SYSTEM == 0,
160                       "It's a little endian system but LV_BIG_ENDIAN_SYSTEM is enabled in lv_conf.h");
161     }
162 
163 #if LV_USE_ASSERT_MEM_INTEGRITY
164     LV_LOG_WARN("Memory integrity checks are enabled via LV_USE_ASSERT_MEM_INTEGRITY which makes LVGL much slower");
165 #endif
166 
167 #if LV_USE_ASSERT_OBJ
168     LV_LOG_WARN("Object sanity checks are enabled via LV_USE_ASSERT_OBJ which makes LVGL much slower");
169 #endif
170 
171 #if LV_USE_ASSERT_STYLE
172     LV_LOG_WARN("Style sanity checks are enabled that uses more RAM");
173 #endif
174 
175 #if LV_LOG_LEVEL == LV_LOG_LEVEL_TRACE
176     LV_LOG_WARN("Log level is set to 'Trace' which makes LVGL much slower");
177 #endif
178 
179     lv_extra_init();
180 
181     lv_initialized = true;
182 
183     LV_LOG_TRACE("finished");
184 }
185 
186 #if LV_ENABLE_GC || !LV_MEM_CUSTOM
187 
lv_deinit(void)188 void lv_deinit(void)
189 {
190     _lv_gc_clear_roots();
191 
192     lv_disp_set_default(NULL);
193     lv_mem_deinit();
194     lv_initialized = false;
195 
196     LV_LOG_INFO("lv_deinit done");
197 
198 #if LV_USE_LOG
199     lv_log_register_print_cb(NULL);
200 #endif
201 }
202 #endif
203 
lv_obj_create(lv_obj_t * parent)204 lv_obj_t * lv_obj_create(lv_obj_t * parent)
205 {
206     LV_LOG_INFO("begin");
207     lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
208     lv_obj_class_init_obj(obj);
209     return obj;
210 }
211 
212 /*=====================
213  * Setter functions
214  *====================*/
215 
216 /*-----------------
217  * Attribute set
218  *----------------*/
219 
lv_obj_add_flag(lv_obj_t * obj,lv_obj_flag_t f)220 void lv_obj_add_flag(lv_obj_t * obj, lv_obj_flag_t f)
221 {
222     LV_ASSERT_OBJ(obj, MY_CLASS);
223 
224     bool was_on_layout = lv_obj_is_layout_positioned(obj);
225 
226     if(f & LV_OBJ_FLAG_HIDDEN) lv_obj_invalidate(obj);
227 
228     obj->flags |= f;
229 
230     if(f & LV_OBJ_FLAG_HIDDEN) {
231         lv_obj_invalidate(obj);
232     }
233 
234     if((was_on_layout != lv_obj_is_layout_positioned(obj)) || (f & (LV_OBJ_FLAG_LAYOUT_1 |  LV_OBJ_FLAG_LAYOUT_2))) {
235         lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj));
236         lv_obj_mark_layout_as_dirty(obj);
237     }
238 
239     if(f & LV_OBJ_FLAG_SCROLLABLE) {
240         lv_area_t hor_area, ver_area;
241         lv_obj_get_scrollbar_area(obj, &hor_area, &ver_area);
242         lv_obj_invalidate_area(obj, &hor_area);
243         lv_obj_invalidate_area(obj, &ver_area);
244     }
245 }
246 
lv_obj_clear_flag(lv_obj_t * obj,lv_obj_flag_t f)247 void lv_obj_clear_flag(lv_obj_t * obj, lv_obj_flag_t f)
248 {
249     LV_ASSERT_OBJ(obj, MY_CLASS);
250 
251     bool was_on_layout = lv_obj_is_layout_positioned(obj);
252     if(f & LV_OBJ_FLAG_SCROLLABLE) {
253         lv_area_t hor_area, ver_area;
254         lv_obj_get_scrollbar_area(obj, &hor_area, &ver_area);
255         lv_obj_invalidate_area(obj, &hor_area);
256         lv_obj_invalidate_area(obj, &ver_area);
257     }
258 
259     obj->flags &= (~f);
260 
261     if(f & LV_OBJ_FLAG_HIDDEN) {
262         lv_obj_invalidate(obj);
263         if(lv_obj_is_layout_positioned(obj)) {
264             lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj));
265             lv_obj_mark_layout_as_dirty(obj);
266         }
267     }
268 
269     if((was_on_layout != lv_obj_is_layout_positioned(obj)) || (f & (LV_OBJ_FLAG_LAYOUT_1 |  LV_OBJ_FLAG_LAYOUT_2))) {
270         lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj));
271     }
272 
273 }
274 
lv_obj_add_state(lv_obj_t * obj,lv_state_t state)275 void lv_obj_add_state(lv_obj_t * obj, lv_state_t state)
276 {
277     LV_ASSERT_OBJ(obj, MY_CLASS);
278 
279     lv_state_t new_state = obj->state | state;
280     if(obj->state != new_state) {
281         lv_obj_set_state(obj, new_state);
282     }
283 }
284 
lv_obj_clear_state(lv_obj_t * obj,lv_state_t state)285 void lv_obj_clear_state(lv_obj_t * obj, lv_state_t state)
286 {
287     LV_ASSERT_OBJ(obj, MY_CLASS);
288 
289     lv_state_t new_state = obj->state & (~state);
290     if(obj->state != new_state) {
291         lv_obj_set_state(obj, new_state);
292     }
293 }
294 
295 /*=======================
296  * Getter functions
297  *======================*/
298 
lv_obj_has_flag(const lv_obj_t * obj,lv_obj_flag_t f)299 bool lv_obj_has_flag(const lv_obj_t * obj, lv_obj_flag_t f)
300 {
301     LV_ASSERT_OBJ(obj, MY_CLASS);
302 
303     return (obj->flags & f)  == f ? true : false;
304 }
305 
lv_obj_has_flag_any(const lv_obj_t * obj,lv_obj_flag_t f)306 bool lv_obj_has_flag_any(const lv_obj_t * obj, lv_obj_flag_t f)
307 {
308     LV_ASSERT_OBJ(obj, MY_CLASS);
309 
310     return (obj->flags & f) ? true : false;
311 }
312 
lv_obj_get_state(const lv_obj_t * obj)313 lv_state_t lv_obj_get_state(const lv_obj_t * obj)
314 {
315     LV_ASSERT_OBJ(obj, MY_CLASS);
316 
317     return obj->state;
318 }
319 
lv_obj_has_state(const lv_obj_t * obj,lv_state_t state)320 bool lv_obj_has_state(const lv_obj_t * obj, lv_state_t state)
321 {
322     LV_ASSERT_OBJ(obj, MY_CLASS);
323 
324     return obj->state & state ? true : false;
325 }
326 
lv_obj_get_group(const lv_obj_t * obj)327 void * lv_obj_get_group(const lv_obj_t * obj)
328 {
329     LV_ASSERT_OBJ(obj, MY_CLASS);
330 
331     if(obj->spec_attr) return obj->spec_attr->group_p;
332     else return NULL;
333 }
334 
335 /*-------------------
336  * OTHER FUNCTIONS
337  *------------------*/
338 
lv_obj_allocate_spec_attr(lv_obj_t * obj)339 void lv_obj_allocate_spec_attr(lv_obj_t * obj)
340 {
341     LV_ASSERT_OBJ(obj, MY_CLASS);
342 
343     if(obj->spec_attr == NULL) {
344         static uint32_t x = 0;
345         x++;
346         obj->spec_attr = lv_mem_alloc(sizeof(_lv_obj_spec_attr_t));
347         LV_ASSERT_MALLOC(obj->spec_attr);
348         if(obj->spec_attr == NULL) return;
349 
350         lv_memset_00(obj->spec_attr, sizeof(_lv_obj_spec_attr_t));
351 
352         obj->spec_attr->scroll_dir = LV_DIR_ALL;
353         obj->spec_attr->scrollbar_mode = LV_SCROLLBAR_MODE_AUTO;
354     }
355 }
356 
lv_obj_check_type(const lv_obj_t * obj,const lv_obj_class_t * class_p)357 bool lv_obj_check_type(const lv_obj_t * obj, const lv_obj_class_t * class_p)
358 {
359     if(obj == NULL) return false;
360     return obj->class_p == class_p ? true : false;
361 }
362 
lv_obj_has_class(const lv_obj_t * obj,const lv_obj_class_t * class_p)363 bool lv_obj_has_class(const lv_obj_t * obj, const lv_obj_class_t * class_p)
364 {
365     const lv_obj_class_t * obj_class = obj->class_p;
366     while(obj_class) {
367         if(obj_class == class_p) return true;
368         obj_class = obj_class->base_class;
369     }
370 
371     return false;
372 }
373 
lv_obj_get_class(const lv_obj_t * obj)374 const lv_obj_class_t * lv_obj_get_class(const lv_obj_t * obj)
375 {
376     return obj->class_p;
377 }
378 
lv_obj_is_valid(const lv_obj_t * obj)379 bool lv_obj_is_valid(const lv_obj_t * obj)
380 {
381     lv_disp_t * disp = lv_disp_get_next(NULL);
382     while(disp) {
383         uint32_t i;
384         for(i = 0; i < disp->screen_cnt; i++) {
385             if(disp->screens[i] == obj) return true;
386             bool found = obj_valid_child(disp->screens[i], obj);
387             if(found) return true;
388         }
389 
390         disp = lv_disp_get_next(disp);
391     }
392 
393     return false;
394 }
395 
396 /**********************
397  *   STATIC FUNCTIONS
398  **********************/
399 
lv_obj_constructor(const lv_obj_class_t * class_p,lv_obj_t * obj)400 static void lv_obj_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
401 {
402     LV_UNUSED(class_p);
403     LV_TRACE_OBJ_CREATE("begin");
404 
405     lv_obj_t * parent = obj->parent;
406     if(parent) {
407         lv_coord_t sl = lv_obj_get_scroll_left(parent);
408         lv_coord_t st = lv_obj_get_scroll_top(parent);
409 
410         obj->coords.y1 = parent->coords.y1 + lv_obj_get_style_pad_top(parent, LV_PART_MAIN) - st;
411         obj->coords.y2 = obj->coords.y1 - 1;
412         obj->coords.x1  = parent->coords.x1 + lv_obj_get_style_pad_left(parent, LV_PART_MAIN) - sl;
413         obj->coords.x2  = obj->coords.x1 - 1;
414     }
415 
416     /*Set attributes*/
417     obj->flags = LV_OBJ_FLAG_CLICKABLE;
418     obj->flags |= LV_OBJ_FLAG_SNAPPABLE;
419     if(parent) obj->flags |= LV_OBJ_FLAG_PRESS_LOCK;
420     if(parent) obj->flags |= LV_OBJ_FLAG_SCROLL_CHAIN;
421     obj->flags |= LV_OBJ_FLAG_CLICK_FOCUSABLE;
422     obj->flags |= LV_OBJ_FLAG_SCROLLABLE;
423     obj->flags |= LV_OBJ_FLAG_SCROLL_ELASTIC;
424     obj->flags |= LV_OBJ_FLAG_SCROLL_MOMENTUM;
425     obj->flags |= LV_OBJ_FLAG_SCROLL_WITH_ARROW;
426     if(parent) obj->flags |= LV_OBJ_FLAG_GESTURE_BUBBLE;
427 
428     LV_TRACE_OBJ_CREATE("finished");
429 }
430 
lv_obj_destructor(const lv_obj_class_t * class_p,lv_obj_t * obj)431 static void lv_obj_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
432 {
433     LV_UNUSED(class_p);
434 
435     _lv_event_mark_deleted(obj);
436 
437     /*Remove all style*/
438     lv_obj_enable_style_refresh(false); /*No need to refresh the style because the object will be deleted*/
439     lv_obj_remove_style_all(obj);
440     lv_obj_enable_style_refresh(true);
441 
442     /*Remove the animations from this object*/
443     lv_anim_del(obj, NULL);
444 
445     /*Delete from the group*/
446     lv_group_t * group = lv_obj_get_group(obj);
447     if(group) lv_group_remove_obj(obj);
448 
449     if(obj->spec_attr) {
450         if(obj->spec_attr->children) {
451             lv_mem_free(obj->spec_attr->children);
452             obj->spec_attr->children = NULL;
453         }
454         if(obj->spec_attr->event_dsc) {
455             lv_mem_free(obj->spec_attr->event_dsc);
456             obj->spec_attr->event_dsc = NULL;
457         }
458 
459         lv_mem_free(obj->spec_attr);
460         obj->spec_attr = NULL;
461     }
462 }
463 
lv_obj_draw(lv_event_t * e)464 static void lv_obj_draw(lv_event_t * e)
465 {
466     lv_event_code_t code = lv_event_get_code(e);
467     lv_obj_t * obj = lv_event_get_target(e);
468     if(code == LV_EVENT_COVER_CHECK) {
469         lv_cover_check_info_t * info = lv_event_get_param(e);
470         if(info->res == LV_COVER_RES_MASKED) return;
471         if(lv_obj_get_style_clip_corner(obj, LV_PART_MAIN)) {
472             info->res = LV_COVER_RES_MASKED;
473             return;
474         }
475 
476         /*Most trivial test. Is the mask fully IN the object? If no it surely doesn't cover it*/
477         lv_coord_t r = lv_obj_get_style_radius(obj, LV_PART_MAIN);
478         lv_coord_t w = lv_obj_get_style_transform_width(obj, LV_PART_MAIN);
479         lv_coord_t h = lv_obj_get_style_transform_height(obj, LV_PART_MAIN);
480         lv_area_t coords;
481         lv_area_copy(&coords, &obj->coords);
482         coords.x1 -= w;
483         coords.x2 += w;
484         coords.y1 -= h;
485         coords.y2 += h;
486 
487         if(_lv_area_is_in(info->area, &coords, r) == false) {
488             info->res = LV_COVER_RES_NOT_COVER;
489             return;
490         }
491 
492         if(lv_obj_get_style_bg_opa(obj, LV_PART_MAIN) < LV_OPA_MAX) {
493             info->res = LV_COVER_RES_NOT_COVER;
494             return;
495         }
496 
497         info->res = LV_COVER_RES_COVER;
498 
499     }
500     else if(code == LV_EVENT_DRAW_MAIN) {
501         lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
502         lv_draw_rect_dsc_t draw_dsc;
503         lv_draw_rect_dsc_init(&draw_dsc);
504         /*If the border is drawn later disable loading its properties*/
505         if(lv_obj_get_style_border_post(obj, LV_PART_MAIN)) {
506             draw_dsc.border_post = 1;
507         }
508 
509         lv_obj_init_draw_rect_dsc(obj, LV_PART_MAIN, &draw_dsc);
510         lv_coord_t w = lv_obj_get_style_transform_width(obj, LV_PART_MAIN);
511         lv_coord_t h = lv_obj_get_style_transform_height(obj, LV_PART_MAIN);
512         lv_area_t coords;
513         lv_area_copy(&coords, &obj->coords);
514         coords.x1 -= w;
515         coords.x2 += w;
516         coords.y1 -= h;
517         coords.y2 += h;
518 
519         lv_obj_draw_part_dsc_t part_dsc;
520         lv_obj_draw_dsc_init(&part_dsc, draw_ctx);
521         part_dsc.class_p = MY_CLASS;
522         part_dsc.type = LV_OBJ_DRAW_PART_RECTANGLE;
523         part_dsc.rect_dsc = &draw_dsc;
524         part_dsc.draw_area = &coords;
525         part_dsc.part = LV_PART_MAIN;
526         lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_dsc);
527 
528 #if LV_DRAW_COMPLEX
529         /*With clip corner enabled draw the bg img separately to make it clipped*/
530         bool clip_corner = (lv_obj_get_style_clip_corner(obj, LV_PART_MAIN) && draw_dsc.radius != 0) ? true : false;
531         const void * bg_img_src = draw_dsc.bg_img_src;
532         if(clip_corner) {
533             draw_dsc.bg_img_src = NULL;
534         }
535 #endif
536 
537         lv_draw_rect(draw_ctx, &draw_dsc, &coords);
538 
539 
540 #if LV_DRAW_COMPLEX
541         if(clip_corner) {
542             lv_draw_mask_radius_param_t * mp = lv_mem_buf_get(sizeof(lv_draw_mask_radius_param_t));
543             lv_draw_mask_radius_init(mp, &obj->coords, draw_dsc.radius, false);
544             /*Add the mask and use `obj+8` as custom id. Don't use `obj` directly because it might be used by the user*/
545             lv_draw_mask_add(mp, obj + 8);
546 
547             if(bg_img_src) {
548                 draw_dsc.bg_opa = LV_OPA_TRANSP;
549                 draw_dsc.border_opa = LV_OPA_TRANSP;
550                 draw_dsc.outline_opa = LV_OPA_TRANSP;
551                 draw_dsc.shadow_opa = LV_OPA_TRANSP;
552                 draw_dsc.bg_img_src = bg_img_src;
553                 lv_draw_rect(draw_ctx, &draw_dsc, &coords);
554             }
555 
556         }
557 #endif
558         lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_dsc);
559     }
560     else if(code == LV_EVENT_DRAW_POST) {
561         lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
562         draw_scrollbar(obj, draw_ctx);
563 
564 #if LV_DRAW_COMPLEX
565         if(lv_obj_get_style_clip_corner(obj, LV_PART_MAIN)) {
566             lv_draw_mask_radius_param_t * param = lv_draw_mask_remove_custom(obj + 8);
567             if(param) {
568                 lv_draw_mask_free_param(param);
569                 lv_mem_buf_release(param);
570             }
571         }
572 #endif
573 
574         /*If the border is drawn later disable loading other properties*/
575         if(lv_obj_get_style_border_post(obj, LV_PART_MAIN)) {
576             lv_draw_rect_dsc_t draw_dsc;
577             lv_draw_rect_dsc_init(&draw_dsc);
578             draw_dsc.bg_opa = LV_OPA_TRANSP;
579             draw_dsc.bg_img_opa = LV_OPA_TRANSP;
580             draw_dsc.outline_opa = LV_OPA_TRANSP;
581             draw_dsc.shadow_opa = LV_OPA_TRANSP;
582             lv_obj_init_draw_rect_dsc(obj, LV_PART_MAIN, &draw_dsc);
583 
584             lv_coord_t w = lv_obj_get_style_transform_width(obj, LV_PART_MAIN);
585             lv_coord_t h = lv_obj_get_style_transform_height(obj, LV_PART_MAIN);
586             lv_area_t coords;
587             lv_area_copy(&coords, &obj->coords);
588             coords.x1 -= w;
589             coords.x2 += w;
590             coords.y1 -= h;
591             coords.y2 += h;
592 
593             lv_obj_draw_part_dsc_t part_dsc;
594             lv_obj_draw_dsc_init(&part_dsc, draw_ctx);
595             part_dsc.class_p = MY_CLASS;
596             part_dsc.type = LV_OBJ_DRAW_PART_BORDER_POST;
597             part_dsc.rect_dsc = &draw_dsc;
598             part_dsc.draw_area = &coords;
599             part_dsc.part = LV_PART_MAIN;
600             lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_dsc);
601 
602             lv_draw_rect(draw_ctx, &draw_dsc, &coords);
603             lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_dsc);
604         }
605     }
606 }
607 
draw_scrollbar(lv_obj_t * obj,lv_draw_ctx_t * draw_ctx)608 static void draw_scrollbar(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx)
609 {
610 
611     lv_area_t hor_area;
612     lv_area_t ver_area;
613     lv_obj_get_scrollbar_area(obj, &hor_area, &ver_area);
614 
615     if(lv_area_get_size(&hor_area) <= 0 && lv_area_get_size(&ver_area) <= 0) return;
616 
617     lv_draw_rect_dsc_t draw_dsc;
618     lv_res_t sb_res = scrollbar_init_draw_dsc(obj, &draw_dsc);
619     if(sb_res != LV_RES_OK) return;
620 
621     lv_obj_draw_part_dsc_t part_dsc;
622     lv_obj_draw_dsc_init(&part_dsc, draw_ctx);
623     part_dsc.class_p = MY_CLASS;
624     part_dsc.type = LV_OBJ_DRAW_PART_SCROLLBAR;
625     part_dsc.rect_dsc = &draw_dsc;
626     part_dsc.part = LV_PART_SCROLLBAR;
627 
628     if(lv_area_get_size(&hor_area) > 0) {
629         part_dsc.draw_area = &hor_area;
630         lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_dsc);
631         lv_draw_rect(draw_ctx, &draw_dsc, &hor_area);
632         lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_dsc);
633     }
634     if(lv_area_get_size(&ver_area) > 0) {
635         part_dsc.draw_area = &ver_area;
636         lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_dsc);
637         part_dsc.draw_area = &ver_area;
638         lv_draw_rect(draw_ctx, &draw_dsc, &ver_area);
639         lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_dsc);
640     }
641 }
642 
643 /**
644  * Initialize the draw descriptor for the scrollbar
645  * @param obj pointer to an object
646  * @param dsc the draw descriptor to initialize
647  * @return LV_RES_OK: the scrollbar is visible; LV_RES_INV: the scrollbar is not visible
648  */
scrollbar_init_draw_dsc(lv_obj_t * obj,lv_draw_rect_dsc_t * dsc)649 static lv_res_t scrollbar_init_draw_dsc(lv_obj_t * obj, lv_draw_rect_dsc_t * dsc)
650 {
651     lv_draw_rect_dsc_init(dsc);
652     dsc->bg_opa = lv_obj_get_style_bg_opa(obj, LV_PART_SCROLLBAR);
653     if(dsc->bg_opa > LV_OPA_MIN) {
654         dsc->bg_color = lv_obj_get_style_bg_color(obj, LV_PART_SCROLLBAR);
655     }
656 
657     dsc->border_opa = lv_obj_get_style_border_opa(obj, LV_PART_SCROLLBAR);
658     if(dsc->border_opa > LV_OPA_MIN) {
659         dsc->border_width = lv_obj_get_style_border_width(obj, LV_PART_SCROLLBAR);
660         if(dsc->border_width > 0) {
661             dsc->border_color = lv_obj_get_style_border_color(obj, LV_PART_SCROLLBAR);
662         }
663         else {
664             dsc->border_opa = LV_OPA_TRANSP;
665         }
666     }
667 
668 #if LV_DRAW_COMPLEX
669     dsc->shadow_opa = lv_obj_get_style_shadow_opa(obj, LV_PART_SCROLLBAR);
670     if(dsc->shadow_opa > LV_OPA_MIN) {
671         dsc->shadow_width = lv_obj_get_style_shadow_width(obj, LV_PART_SCROLLBAR);
672         if(dsc->shadow_width > 0) {
673             dsc->shadow_spread = lv_obj_get_style_shadow_spread(obj, LV_PART_SCROLLBAR);
674             dsc->shadow_color = lv_obj_get_style_shadow_color(obj, LV_PART_SCROLLBAR);
675         }
676         else {
677             dsc->shadow_opa = LV_OPA_TRANSP;
678         }
679     }
680 
681     lv_opa_t opa = lv_obj_get_style_opa(obj, LV_PART_SCROLLBAR);
682     if(opa < LV_OPA_MAX) {
683         dsc->bg_opa = (dsc->bg_opa * opa) >> 8;
684         dsc->border_opa = (dsc->bg_opa * opa) >> 8;
685         dsc->shadow_opa = (dsc->bg_opa * opa) >> 8;
686     }
687 
688     if(dsc->bg_opa != LV_OPA_TRANSP || dsc->border_opa != LV_OPA_TRANSP || dsc->shadow_opa != LV_OPA_TRANSP) {
689         dsc->radius = lv_obj_get_style_radius(obj, LV_PART_SCROLLBAR);
690         return LV_RES_OK;
691     }
692     else {
693         return LV_RES_INV;
694     }
695 #else
696     if(dsc->bg_opa != LV_OPA_TRANSP || dsc->border_opa != LV_OPA_TRANSP) return LV_RES_OK;
697     else return LV_RES_INV;
698 #endif
699 }
700 
lv_obj_event(const lv_obj_class_t * class_p,lv_event_t * e)701 static void lv_obj_event(const lv_obj_class_t * class_p, lv_event_t * e)
702 {
703     LV_UNUSED(class_p);
704 
705     lv_event_code_t code = lv_event_get_code(e);
706     lv_obj_t * obj = lv_event_get_current_target(e);
707     if(code == LV_EVENT_PRESSED) {
708         lv_obj_add_state(obj, LV_STATE_PRESSED);
709     }
710     else if(code == LV_EVENT_RELEASED) {
711         lv_obj_clear_state(obj, LV_STATE_PRESSED);
712         void * param = lv_event_get_param(e);
713         /*Go the checked state if enabled*/
714         if(lv_indev_get_scroll_obj(param) == NULL && lv_obj_has_flag(obj, LV_OBJ_FLAG_CHECKABLE)) {
715             if(!(lv_obj_get_state(obj) & LV_STATE_CHECKED)) lv_obj_add_state(obj, LV_STATE_CHECKED);
716             else lv_obj_clear_state(obj, LV_STATE_CHECKED);
717 
718             lv_res_t res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
719             if(res != LV_RES_OK) return;
720         }
721     }
722     else if(code == LV_EVENT_PRESS_LOST) {
723         lv_obj_clear_state(obj, LV_STATE_PRESSED);
724     }
725     else if(code == LV_EVENT_STYLE_CHANGED) {
726         uint32_t child_cnt = lv_obj_get_child_cnt(obj);
727         for(uint32_t i = 0; i < child_cnt; i++) {
728             lv_obj_t * child = obj->spec_attr->children[i];
729             lv_obj_mark_layout_as_dirty(child);
730         }
731     }
732     else if(code == LV_EVENT_KEY) {
733         if(lv_obj_has_flag(obj, LV_OBJ_FLAG_CHECKABLE)) {
734             char c = *((char *)lv_event_get_param(e));
735             if(c == LV_KEY_RIGHT || c == LV_KEY_UP) {
736                 lv_obj_add_state(obj, LV_STATE_CHECKED);
737             }
738             else if(c == LV_KEY_LEFT || c == LV_KEY_DOWN) {
739                 lv_obj_clear_state(obj, LV_STATE_CHECKED);
740             }
741 
742             /*With Enter LV_EVENT_RELEASED will send VALUE_CHANGE event*/
743             if(c != LV_KEY_ENTER) {
744                 lv_res_t res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
745                 if(res != LV_RES_OK) return;
746             }
747         }
748         else if(lv_obj_has_flag(obj, LV_OBJ_FLAG_SCROLLABLE | LV_OBJ_FLAG_SCROLL_WITH_ARROW) && !lv_obj_is_editable(obj)) {
749             /*scroll by keypad or encoder*/
750             lv_anim_enable_t anim_enable = LV_ANIM_OFF;
751             lv_coord_t sl = lv_obj_get_scroll_left(obj);
752             lv_coord_t sr = lv_obj_get_scroll_right(obj);
753             char c = *((char *)lv_event_get_param(e));
754             if(c == LV_KEY_DOWN) {
755                 /*use scroll_to_x/y functions to enforce scroll limits*/
756                 lv_obj_scroll_to_y(obj, lv_obj_get_scroll_y(obj) + lv_obj_get_height(obj) / 4, anim_enable);
757             }
758             else if(c == LV_KEY_UP) {
759                 lv_obj_scroll_to_y(obj, lv_obj_get_scroll_y(obj) - lv_obj_get_height(obj) / 4, anim_enable);
760             }
761             else if(c == LV_KEY_RIGHT) {
762                 /*If the object can't be scrolled horizontally then scroll it vertically*/
763                 if(!((lv_obj_get_scroll_dir(obj) & LV_DIR_HOR) && (sl > 0 || sr > 0)))
764                     lv_obj_scroll_to_y(obj, lv_obj_get_scroll_y(obj) + lv_obj_get_height(obj) / 4, anim_enable);
765                 else
766                     lv_obj_scroll_to_x(obj, lv_obj_get_scroll_x(obj) + lv_obj_get_width(obj) / 4, anim_enable);
767             }
768             else if(c == LV_KEY_LEFT) {
769                 /*If the object can't be scrolled horizontally then scroll it vertically*/
770                 if(!((lv_obj_get_scroll_dir(obj) & LV_DIR_HOR) && (sl > 0 || sr > 0)))
771                     lv_obj_scroll_to_y(obj, lv_obj_get_scroll_y(obj) - lv_obj_get_height(obj) / 4, anim_enable);
772                 else
773                     lv_obj_scroll_to_x(obj, lv_obj_get_scroll_x(obj) - lv_obj_get_width(obj) / 4, anim_enable);
774             }
775         }
776     }
777     else if(code == LV_EVENT_FOCUSED) {
778         if(lv_obj_has_flag(obj, LV_OBJ_FLAG_SCROLL_ON_FOCUS)) {
779             lv_obj_scroll_to_view_recursive(obj, LV_ANIM_ON);
780         }
781 
782         bool editing = false;
783         editing = lv_group_get_editing(lv_obj_get_group(obj));
784         lv_state_t state = LV_STATE_FOCUSED;
785 
786         /* Use the indev for then indev handler.
787          * But if the obj was focused manually it returns NULL so try to
788          * use the indev from the event*/
789         lv_indev_t * indev = lv_indev_get_act();
790         if(indev == NULL) indev = lv_event_get_indev(e);
791 
792         lv_indev_type_t indev_type = lv_indev_get_type(indev);
793         if(indev_type == LV_INDEV_TYPE_KEYPAD || indev_type == LV_INDEV_TYPE_ENCODER) state |= LV_STATE_FOCUS_KEY;
794         if(editing) {
795             state |= LV_STATE_EDITED;
796             lv_obj_add_state(obj, state);
797         }
798         else {
799             lv_obj_add_state(obj, state);
800             lv_obj_clear_state(obj, LV_STATE_EDITED);
801         }
802     }
803     else if(code == LV_EVENT_SCROLL_BEGIN) {
804         lv_obj_add_state(obj, LV_STATE_SCROLLED);
805     }
806     else if(code == LV_EVENT_SCROLL_END) {
807         lv_obj_clear_state(obj, LV_STATE_SCROLLED);
808         if(lv_obj_get_scrollbar_mode(obj) == LV_SCROLLBAR_MODE_ACTIVE) {
809             lv_area_t hor_area, ver_area;
810             lv_obj_get_scrollbar_area(obj, &hor_area, &ver_area);
811             lv_obj_invalidate_area(obj, &hor_area);
812             lv_obj_invalidate_area(obj, &ver_area);
813         }
814     }
815     else if(code == LV_EVENT_DEFOCUSED) {
816         lv_obj_clear_state(obj, LV_STATE_FOCUSED | LV_STATE_EDITED | LV_STATE_FOCUS_KEY);
817     }
818     else if(code == LV_EVENT_SIZE_CHANGED) {
819         lv_coord_t align = lv_obj_get_style_align(obj, LV_PART_MAIN);
820         uint16_t layout = lv_obj_get_style_layout(obj, LV_PART_MAIN);
821         if(layout || align) {
822             lv_obj_mark_layout_as_dirty(obj);
823         }
824 
825         uint32_t i;
826         uint32_t child_cnt = lv_obj_get_child_cnt(obj);
827         for(i = 0; i < child_cnt; i++) {
828             lv_obj_t * child = obj->spec_attr->children[i];
829             lv_obj_mark_layout_as_dirty(child);
830         }
831     }
832     else if(code == LV_EVENT_CHILD_CHANGED) {
833         lv_coord_t w = lv_obj_get_style_width(obj, LV_PART_MAIN);
834         lv_coord_t h = lv_obj_get_style_height(obj, LV_PART_MAIN);
835         lv_coord_t align = lv_obj_get_style_align(obj, LV_PART_MAIN);
836         uint16_t layout = lv_obj_get_style_layout(obj, LV_PART_MAIN);
837         if(layout || align || w == LV_SIZE_CONTENT || h == LV_SIZE_CONTENT) {
838             lv_obj_mark_layout_as_dirty(obj);
839         }
840     }
841     else if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
842         lv_coord_t d = lv_obj_calculate_ext_draw_size(obj, LV_PART_MAIN);
843         lv_event_set_ext_draw_size(e, d);
844     }
845     else if(code == LV_EVENT_DRAW_MAIN || code == LV_EVENT_DRAW_POST || code == LV_EVENT_COVER_CHECK) {
846         lv_obj_draw(e);
847     }
848 }
849 
850 /**
851  * Set the state (fully overwrite) of an object.
852  * If specified in the styles, transition animations will be started from the previous state to the current.
853  * @param obj       pointer to an object
854  * @param state     the new state
855  */
lv_obj_set_state(lv_obj_t * obj,lv_state_t new_state)856 static void lv_obj_set_state(lv_obj_t * obj, lv_state_t new_state)
857 {
858     if(obj->state == new_state) return;
859 
860     LV_ASSERT_OBJ(obj, MY_CLASS);
861 
862     lv_state_t prev_state = obj->state;
863     obj->state = new_state;
864 
865     _lv_style_state_cmp_t cmp_res = _lv_obj_style_state_compare(obj, prev_state, new_state);
866     /*If there is no difference in styles there is nothing else to do*/
867     if(cmp_res == _LV_STYLE_STATE_CMP_SAME) return;
868 
869     _lv_obj_style_transition_dsc_t * ts = lv_mem_buf_get(sizeof(_lv_obj_style_transition_dsc_t) * STYLE_TRANSITION_MAX);
870     lv_memset_00(ts, sizeof(_lv_obj_style_transition_dsc_t) * STYLE_TRANSITION_MAX);
871     uint32_t tsi = 0;
872     uint32_t i;
873     for(i = 0; i < obj->style_cnt && tsi < STYLE_TRANSITION_MAX; i++) {
874         _lv_obj_style_t * obj_style = &obj->styles[i];
875         lv_state_t state_act = lv_obj_style_get_selector_state(obj->styles[i].selector);
876         lv_part_t part_act = lv_obj_style_get_selector_part(obj->styles[i].selector);
877         if(state_act & (~new_state)) continue; /*Skip unrelated styles*/
878         if(obj_style->is_trans) continue;
879 
880         lv_style_value_t v;
881         if(lv_style_get_prop_inlined(obj_style->style, LV_STYLE_TRANSITION, &v) != LV_STYLE_RES_FOUND) continue;
882         const lv_style_transition_dsc_t * tr = v.ptr;
883 
884         /*Add the props to the set if not added yet or added but with smaller weight*/
885         uint32_t j;
886         for(j = 0; tr->props[j] != 0 && tsi < STYLE_TRANSITION_MAX; j++) {
887             uint32_t t;
888             for(t = 0; t < tsi; t++) {
889                 lv_style_selector_t selector = ts[t].selector;
890                 lv_state_t state_ts = lv_obj_style_get_selector_state(selector);
891                 lv_part_t part_ts = lv_obj_style_get_selector_part(selector);
892                 if(ts[t].prop == tr->props[j] && part_ts == part_act && state_ts >= state_act) break;
893             }
894 
895             /*If not found  add it*/
896             if(t == tsi) {
897                 ts[tsi].time = tr->time;
898                 ts[tsi].delay = tr->delay;
899                 ts[tsi].path_cb = tr->path_xcb;
900                 ts[tsi].prop = tr->props[j];
901 #if LV_USE_USER_DATA
902                 ts[tsi].user_data = tr->user_data;
903 #endif
904                 ts[tsi].selector = obj_style->selector;
905                 tsi++;
906             }
907         }
908     }
909 
910     for(i = 0; i < tsi; i++) {
911         lv_part_t part_act = lv_obj_style_get_selector_part(ts[i].selector);
912         _lv_obj_style_create_transition(obj, part_act, prev_state, new_state, &ts[i]);
913     }
914 
915     lv_mem_buf_release(ts);
916 
917     if(cmp_res == _LV_STYLE_STATE_CMP_DIFF_REDRAW) {
918         lv_obj_invalidate(obj);
919     }
920     else if(cmp_res == _LV_STYLE_STATE_CMP_DIFF_LAYOUT) {
921         lv_obj_refresh_style(obj, LV_PART_ANY, LV_STYLE_PROP_ANY);
922     }
923     else if(cmp_res == _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD) {
924         lv_obj_invalidate(obj);
925         lv_obj_refresh_ext_draw_size(obj);
926     }
927 }
928 
obj_valid_child(const lv_obj_t * parent,const lv_obj_t * obj_to_find)929 static bool obj_valid_child(const lv_obj_t * parent, const lv_obj_t * obj_to_find)
930 {
931     /*Check all children of `parent`*/
932     uint32_t child_cnt = 0;
933     if(parent->spec_attr) child_cnt = parent->spec_attr->child_cnt;
934     uint32_t i;
935     for(i = 0; i < child_cnt; i++) {
936         lv_obj_t * child = parent->spec_attr->children[i];
937         if(child == obj_to_find) {
938             return true;
939         }
940 
941         /*Check the children*/
942         bool found = obj_valid_child(child, obj_to_find);
943         if(found) {
944             return true;
945         }
946     }
947     return false;
948 }
949