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