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