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