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_misc/lv_debug.h"
15 #include "../lv_themes/lv_theme.h"
16 #include "../lv_draw/lv_draw.h"
17 #include "../lv_misc/lv_anim.h"
18 #include "../lv_misc/lv_task.h"
19 #include "../lv_misc/lv_async.h"
20 #include "../lv_misc/lv_fs.h"
21 #include "../lv_misc/lv_gc.h"
22 #include "../lv_misc/lv_math.h"
23 #include "../lv_misc/lv_gc.h"
24 #include "../lv_misc/lv_math.h"
25 #include "../lv_misc/lv_log.h"
26 #include "../lv_hal/lv_hal.h"
27 #include <stdint.h>
28 #include <string.h>
29
30 #if defined(LV_GC_INCLUDE)
31 #include LV_GC_INCLUDE
32 #endif /* LV_ENABLE_GC */
33
34
35 #if defined(LV_USER_DATA_FREE_INCLUDE)
36 #include LV_USER_DATA_FREE_INCLUDE
37 #endif /* LV_USE_USER_DATA_FREE */
38
39 #include LV_THEME_DEFAULT_INCLUDE
40
41 #if LV_USE_GPU_STM32_DMA2D
42 #include "../lv_gpu/lv_gpu_stm32_dma2d.h"
43 #endif
44
45 /*********************
46 * DEFINES
47 *********************/
48 #define LV_OBJX_NAME "lv_obj"
49 #define LV_OBJ_DEF_WIDTH (LV_DPX(100))
50 #define LV_OBJ_DEF_HEIGHT (LV_DPX(50))
51
52 /**********************
53 * TYPEDEFS
54 **********************/
55 typedef struct _lv_event_temp_data {
56 lv_obj_t * obj;
57 bool deleted;
58 struct _lv_event_temp_data * prev;
59 } lv_event_temp_data_t;
60
61 typedef struct {
62 lv_obj_t * obj;
63 lv_style_property_t prop;
64 uint8_t part;
65 union {
66 lv_color_t _color;
67 lv_style_int_t _int;
68 lv_opa_t _opa;
69 const void * _ptr;
70 } start_value;
71 union {
72 lv_color_t _color;
73 lv_style_int_t _int;
74 lv_opa_t _opa;
75 const void * _ptr;
76 } end_value;
77 } lv_style_trans_t;
78
79 typedef struct {
80 lv_draw_rect_dsc_t rect;
81 lv_draw_label_dsc_t label;
82 lv_draw_line_dsc_t line;
83 lv_draw_img_dsc_t img;
84 lv_style_int_t pad_top;
85 lv_style_int_t pad_bottom;
86 lv_style_int_t pad_right;
87 lv_style_int_t pad_left;
88 lv_style_int_t pad_inner;
89 lv_style_int_t margin_top;
90 lv_style_int_t margin_bottom;
91 lv_style_int_t margin_left;
92 lv_style_int_t margin_right;
93 lv_style_int_t size;
94 lv_style_int_t transform_width;
95 lv_style_int_t transform_height;
96 lv_style_int_t transform_angle;
97 lv_style_int_t transform_zoom;
98 lv_style_int_t scale_width;
99 lv_style_int_t scale_border_width;
100 lv_style_int_t scale_end_border_width;
101 lv_style_int_t scale_end_line_width;
102 lv_color_t scale_grad_color;
103 lv_color_t scale_end_color;
104 lv_opa_t opa_scale;
105 uint32_t clip_corder : 1;
106 uint32_t border_post : 1;
107 } style_snapshot_t;
108
109 typedef enum {
110 STYLE_COMPARE_SAME,
111 STYLE_COMPARE_VISUAL_DIFF,
112 STYLE_COMPARE_DIFF,
113 } style_snapshot_res_t;
114
115 /**********************
116 * STATIC PROTOTYPES
117 **********************/
118 static lv_design_res_t lv_obj_design(lv_obj_t * obj, const lv_area_t * clip_area, lv_design_mode_t mode);
119 static lv_res_t lv_obj_signal(lv_obj_t * obj, lv_signal_t sign, void * param);
120 static void refresh_children_position(lv_obj_t * obj, lv_coord_t x_diff, lv_coord_t y_diff);
121 static void report_style_mod_core(void * style_p, lv_obj_t * obj);
122 static void refresh_children_style(lv_obj_t * obj);
123 static void base_dir_refr_children(lv_obj_t * obj);
124 static void obj_align_core(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, bool x_set, bool y_set,
125 lv_coord_t x_ofs, lv_coord_t y_ofs);
126 static void obj_align_mid_core(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, bool x_set, bool y_set,
127 lv_coord_t x_ofs, lv_coord_t y_ofs);
128 #if LV_USE_ANIMATION
129 static lv_style_trans_t * trans_create(lv_obj_t * obj, lv_style_property_t prop, uint8_t part, lv_state_t prev_state,
130 lv_state_t new_state);
131 static void trans_del(lv_obj_t * obj, uint8_t part, lv_style_property_t prop, lv_style_trans_t * tr_limit);
132 static void trans_anim_cb(lv_style_trans_t * tr, lv_anim_value_t v);
133 static void trans_anim_start_cb(lv_anim_t * a);
134 static void trans_anim_ready_cb(lv_anim_t * a);
135 static void opa_scale_anim(lv_obj_t * obj, lv_anim_value_t v);
136 static void fade_in_anim_ready(lv_anim_t * a);
137 #endif
138 static void lv_event_mark_deleted(lv_obj_t * obj);
139 static bool obj_valid_child(const lv_obj_t * parent, const lv_obj_t * obj_to_find);
140 static void lv_obj_del_async_cb(void * obj);
141 static void obj_del_core(lv_obj_t * obj);
142 static void update_style_cache(lv_obj_t * obj, uint8_t part, uint16_t prop);
143 static void update_style_cache_children(lv_obj_t * obj);
144 static void invalidate_style_cache(lv_obj_t * obj, uint8_t part, lv_style_property_t prop);
145 static void style_snapshot(lv_obj_t * obj, uint8_t part, style_snapshot_t * shot);
146 static style_snapshot_res_t style_snapshot_compare(style_snapshot_t * shot1, style_snapshot_t * shot2);
147
148 /**********************
149 * STATIC VARIABLES
150 **********************/
151 static bool lv_initialized = false;
152 static lv_event_temp_data_t * event_temp_data_head;
153 static const void * event_act_data;
154
155 /**********************
156 * MACROS
157 **********************/
158
159 /**********************
160 * GLOBAL FUNCTIONS
161 **********************/
162
163 /**
164 * Init. the 'lv' library.
165 */
lv_init(void)166 void lv_init(void)
167 {
168 /* Do nothing if already initialized */
169 if(lv_initialized) {
170 LV_LOG_WARN("lv_init: already inited");
171 return;
172 }
173
174 LV_LOG_TRACE("lv_init started");
175
176 /*Initialize the lv_misc modules*/
177 _lv_mem_init();
178 _lv_task_core_init();
179
180 #if LV_USE_FILESYSTEM
181 _lv_fs_init();
182 #endif
183
184 #if LV_USE_ANIMATION
185 _lv_anim_core_init();
186 #endif
187
188 #if LV_USE_GROUP
189 _lv_group_init();
190 #endif
191
192 #if LV_USE_GPU_STM32_DMA2D
193 /*Initialize DMA2D GPU*/
194 lv_gpu_stm32_dma2d_init();
195 #endif
196
197 _lv_ll_init(&LV_GC_ROOT(_lv_obj_style_trans_ll), sizeof(lv_style_trans_t));
198
199 _lv_ll_init(&LV_GC_ROOT(_lv_disp_ll), sizeof(lv_disp_t));
200 _lv_ll_init(&LV_GC_ROOT(_lv_indev_ll), sizeof(lv_indev_t));
201
202 lv_theme_t * th = LV_THEME_DEFAULT_INIT(LV_THEME_DEFAULT_COLOR_PRIMARY, LV_THEME_DEFAULT_COLOR_SECONDARY,
203 LV_THEME_DEFAULT_FLAG,
204 LV_THEME_DEFAULT_FONT_SMALL, LV_THEME_DEFAULT_FONT_NORMAL, LV_THEME_DEFAULT_FONT_SUBTITLE, LV_THEME_DEFAULT_FONT_TITLE);
205 lv_theme_set_act(th);
206
207 /*Initialize the screen refresh system*/
208 _lv_refr_init();
209
210 /*Init the input device handling*/
211 _lv_indev_init();
212
213 _lv_img_decoder_init();
214 lv_img_cache_set_size(LV_IMG_CACHE_DEF_SIZE);
215
216 /*Test if the IDE has UTF-8 encoding*/
217 char * txt = "Á";
218
219 uint8_t * txt_u8 = (uint8_t *) txt;
220 if(txt_u8[0] != 0xc3 || txt_u8[1] != 0x81 || txt_u8[2] != 0x00) {
221 LV_LOG_WARN("The strings has no UTF-8 encoding. Some characters won't be displayed.")
222 }
223
224 lv_initialized = true;
225 LV_LOG_INFO("lv_init ready");
226 }
227
228 #if LV_ENABLE_GC || !LV_MEM_CUSTOM
229
230 /**
231 * Deinit the 'lv' library
232 * Currently only implemented when not using custom allocators, or GC is enabled.
233 */
lv_deinit(void)234 void lv_deinit(void)
235 {
236 _lv_gc_clear_roots();
237
238 lv_disp_set_default(NULL);
239 _lv_mem_deinit();
240 lv_initialized = false;
241
242 LV_LOG_INFO("lv_deinit done");
243
244 #if LV_USE_LOG
245 lv_log_register_print_cb(NULL);
246 #endif
247 }
248 #endif
249
250 /*--------------------
251 * Create and delete
252 *-------------------*/
253
254 /**
255 * Create a basic object
256 * @param parent pointer to a parent object.
257 * If NULL then a screen will be created
258 * @param copy pointer to a base object, if not NULL then the new object will be copied from it
259 * @return pointer to the new object
260 */
lv_obj_create(lv_obj_t * parent,const lv_obj_t * copy)261 lv_obj_t * lv_obj_create(lv_obj_t * parent, const lv_obj_t * copy)
262 {
263 lv_obj_t * new_obj = NULL;
264
265 /*Create a screen*/
266 if(parent == NULL) {
267 LV_LOG_TRACE("Screen create started");
268 lv_disp_t * disp = lv_disp_get_default();
269 if(!disp) {
270 LV_LOG_WARN("lv_obj_create: not display created to so far. No place to assign the new screen");
271 return NULL;
272 }
273
274 new_obj = _lv_ll_ins_head(&disp->scr_ll);
275 LV_ASSERT_MEM(new_obj);
276 if(new_obj == NULL) return NULL;
277
278 _lv_memset_00(new_obj, sizeof(lv_obj_t));
279
280 #if LV_USE_BIDI
281 new_obj->base_dir = LV_BIDI_BASE_DIR_DEF;
282 #else
283 new_obj->base_dir = LV_BIDI_DIR_LTR;
284 #endif
285
286 /*Set the callbacks*/
287 new_obj->signal_cb = lv_obj_signal;
288 new_obj->design_cb = lv_obj_design;
289 new_obj->event_cb = NULL;
290
291 /*Set coordinates to full screen size*/
292 new_obj->coords.x1 = 0;
293 new_obj->coords.y1 = 0;
294 new_obj->coords.x2 = lv_disp_get_hor_res(NULL) - 1;
295 new_obj->coords.y2 = lv_disp_get_ver_res(NULL) - 1;
296 }
297 /*Create a normal object*/
298 else {
299 LV_LOG_TRACE("Object create started");
300 LV_ASSERT_OBJ(parent, LV_OBJX_NAME);
301
302 new_obj = _lv_ll_ins_head(&parent->child_ll);
303 LV_ASSERT_MEM(new_obj);
304 if(new_obj == NULL) return NULL;
305
306 _lv_memset_00(new_obj, sizeof(lv_obj_t));
307
308 new_obj->parent = parent;
309
310 #if LV_USE_BIDI
311 new_obj->base_dir = LV_BIDI_DIR_INHERIT;
312 #else
313 new_obj->base_dir = LV_BIDI_DIR_LTR;
314 #endif
315
316 /*Set the callbacks (signal:cb is required in `lv_obj_get_base_dir` if `LV_USE_ASSERT_OBJ` is enabled)*/
317 new_obj->signal_cb = lv_obj_signal;
318 new_obj->design_cb = lv_obj_design;
319 new_obj->event_cb = NULL;
320
321 new_obj->coords.y1 = parent->coords.y1;
322 new_obj->coords.y2 = parent->coords.y1 + LV_OBJ_DEF_HEIGHT;
323 if(lv_obj_get_base_dir(new_obj) == LV_BIDI_DIR_RTL) {
324 new_obj->coords.x2 = parent->coords.x2;
325 new_obj->coords.x1 = parent->coords.x2 - LV_OBJ_DEF_WIDTH;
326 }
327 else {
328 new_obj->coords.x1 = parent->coords.x1;
329 new_obj->coords.x2 = parent->coords.x1 + LV_OBJ_DEF_WIDTH;
330 }
331 }
332
333
334 _lv_ll_init(&(new_obj->child_ll), sizeof(lv_obj_t));
335
336
337 new_obj->ext_draw_pad = 0;
338
339 #if LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_FULL
340 _lv_memset_00(&new_obj->ext_click_pad, sizeof(new_obj->ext_click_pad));
341 #elif LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_TINY
342 new_obj->ext_click_pad_hor = 0;
343 new_obj->ext_click_pad_ver = 0;
344 #endif
345
346 /*Init realign*/
347 #if LV_USE_OBJ_REALIGN
348 new_obj->realign.align = LV_ALIGN_CENTER;
349 new_obj->realign.xofs = 0;
350 new_obj->realign.yofs = 0;
351 new_obj->realign.base = NULL;
352 new_obj->realign.auto_realign = 0;
353 #endif
354
355 /*Init. user date*/
356 #if LV_USE_USER_DATA
357 _lv_memset_00(&new_obj->user_data, sizeof(lv_obj_user_data_t));
358 #endif
359
360
361 #if LV_USE_GROUP
362 new_obj->group_p = NULL;
363 #endif
364
365 /*Set attributes*/
366 new_obj->adv_hittest = 0;
367 new_obj->click = 1;
368 new_obj->drag = 0;
369 new_obj->drag_throw = 0;
370 new_obj->drag_parent = 0;
371 new_obj->drag_dir = LV_DRAG_DIR_BOTH;
372 new_obj->hidden = 0;
373 new_obj->top = 0;
374 new_obj->protect = LV_PROTECT_NONE;
375 new_obj->parent_event = 0;
376 new_obj->gesture_parent = parent ? 1 : 0;
377 new_obj->focus_parent = 0;
378 new_obj->state = LV_STATE_DEFAULT;
379
380 new_obj->ext_attr = NULL;
381
382 lv_style_list_init(&new_obj->style_list);
383 if(copy == NULL) {
384 if(parent != NULL) lv_theme_apply(new_obj, LV_THEME_OBJ);
385 else lv_theme_apply(new_obj, LV_THEME_SCR);
386 }
387 else {
388 lv_style_list_copy(&new_obj->style_list, ©->style_list);
389 }
390 /*Copy the attributes if required*/
391 if(copy != NULL) {
392 lv_area_copy(&new_obj->coords, ©->coords);
393 new_obj->ext_draw_pad = copy->ext_draw_pad;
394
395 #if LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_FULL
396 lv_area_copy(&new_obj->ext_click_pad, ©->ext_click_pad);
397 #elif LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_TINY
398 new_obj->ext_click_pad_hor = copy->ext_click_pad_hor;
399 new_obj->ext_click_pad_ver = copy->ext_click_pad_ver;
400 #endif
401
402 /*Set user data*/
403 #if LV_USE_USER_DATA
404 _lv_memcpy(&new_obj->user_data, ©->user_data, sizeof(lv_obj_user_data_t));
405 #endif
406
407 new_obj->base_dir = copy->base_dir;
408
409 /*Copy realign*/
410 #if LV_USE_OBJ_REALIGN
411 new_obj->realign.align = copy->realign.align;
412 new_obj->realign.xofs = copy->realign.xofs;
413 new_obj->realign.yofs = copy->realign.yofs;
414 new_obj->realign.base = copy->realign.base;
415 new_obj->realign.auto_realign = copy->realign.auto_realign;
416 #endif
417
418 /*Only copy the `event_cb`. `signal_cb` and `design_cb` will be copied in the derived
419 * object type (e.g. `lv_btn`)*/
420 new_obj->event_cb = copy->event_cb;
421
422 /*Copy attributes*/
423 new_obj->adv_hittest = copy->adv_hittest;
424 new_obj->click = copy->click;
425 new_obj->drag = copy->drag;
426 new_obj->drag_dir = copy->drag_dir;
427 new_obj->drag_throw = copy->drag_throw;
428 new_obj->drag_parent = copy->drag_parent;
429 new_obj->hidden = copy->hidden;
430 new_obj->top = copy->top;
431 new_obj->parent_event = copy->parent_event;
432
433 new_obj->protect = copy->protect;
434 new_obj->gesture_parent = copy->gesture_parent;
435 new_obj->focus_parent = copy->focus_parent;
436
437 #if LV_USE_GROUP
438 /*Add to the same group*/
439 if(copy->group_p != NULL) {
440 lv_group_add_obj(copy->group_p, new_obj);
441 }
442 #endif
443
444 /*Set the same coordinates for non screen objects*/
445 if(lv_obj_get_parent(copy) != NULL && parent != NULL) {
446 lv_obj_set_pos(new_obj, lv_obj_get_x(copy), lv_obj_get_y(copy));
447 }
448 }
449
450 /*Send a signal to the parent to notify it about the new child*/
451 if(parent != NULL) {
452 parent->signal_cb(parent, LV_SIGNAL_CHILD_CHG, new_obj);
453
454 /*Invalidate the area if not screen created*/
455 lv_obj_invalidate(new_obj);
456 }
457
458 LV_LOG_INFO("Object create ready");
459
460 return new_obj;
461 }
462
463 /**
464 * Delete 'obj' and all of its children
465 * @param obj pointer to an object to delete
466 * @return LV_RES_INV because the object is deleted
467 */
lv_obj_del(lv_obj_t * obj)468 lv_res_t lv_obj_del(lv_obj_t * obj)
469 {
470 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
471 lv_obj_invalidate(obj);
472
473 lv_disp_t * disp = NULL;
474 bool act_scr_del = false;
475 lv_obj_t * par = lv_obj_get_parent(obj);
476 if(par == NULL) {
477 disp = lv_obj_get_disp(obj);
478 if(!disp) return LV_RES_INV; /*Shouldn't happen*/
479 if(disp->act_scr == obj) act_scr_del = true;
480 }
481
482
483 obj_del_core(obj);
484
485 /*Send a signal to the parent to notify it about the child delete*/
486 if(par) {
487 par->signal_cb(par, LV_SIGNAL_CHILD_CHG, NULL);
488 }
489
490 /*Handle if the active screen was deleted*/
491 if(act_scr_del) {
492 disp->act_scr = NULL;
493 }
494
495 return LV_RES_INV;
496 }
497
498 #if LV_USE_ANIMATION
499 /**
500 * A function to be easily used in animation ready callback to delete an object when the animation is ready
501 * @param a pointer to the animation
502 */
lv_obj_del_anim_ready_cb(lv_anim_t * a)503 void lv_obj_del_anim_ready_cb(lv_anim_t * a)
504 {
505 lv_obj_del(a->var);
506 }
507 #endif
508
509 /**
510 * Helper function for asynchronously deleting objects.
511 * Useful for cases where you can't delete an object directly in an `LV_EVENT_DELETE` handler (i.e. parent).
512 * @param obj object to delete
513 * @see lv_async_call
514 */
lv_obj_del_async(lv_obj_t * obj)515 void lv_obj_del_async(lv_obj_t * obj)
516 {
517 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
518 lv_async_call(lv_obj_del_async_cb, obj);
519 }
520
521 /**
522 * Delete all children of an object
523 * @param obj pointer to an object
524 */
lv_obj_clean(lv_obj_t * obj)525 void lv_obj_clean(lv_obj_t * obj)
526 {
527 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
528 lv_obj_t * child = lv_obj_get_child(obj, NULL);
529 while(child) {
530 lv_obj_del(child);
531 child = lv_obj_get_child(obj, NULL); /*Get the new first child*/
532 }
533 }
534
535 /**
536 * Mark an area of an object as invalid.
537 * This area will be redrawn by 'lv_refr_task'
538 * @param obj pointer to an object
539 * @param area the area to redraw
540 */
lv_obj_invalidate_area(const lv_obj_t * obj,const lv_area_t * area)541 void lv_obj_invalidate_area(const lv_obj_t * obj, const lv_area_t * area)
542 {
543 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
544
545 lv_area_t area_tmp;
546 lv_area_copy(&area_tmp, area);
547 bool visible = lv_obj_area_is_visible(obj, &area_tmp);
548
549 if(visible) _lv_inv_area(lv_obj_get_disp(obj), &area_tmp);
550 }
551
552 /**
553 * Mark the object as invalid therefore its current position will be redrawn by 'lv_refr_task'
554 * @param obj pointer to an object
555 */
lv_obj_invalidate(const lv_obj_t * obj)556 void lv_obj_invalidate(const lv_obj_t * obj)
557 {
558 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
559
560 /*Truncate the area to the object*/
561 lv_area_t obj_coords;
562 lv_coord_t ext_size = obj->ext_draw_pad;
563 lv_area_copy(&obj_coords, &obj->coords);
564 obj_coords.x1 -= ext_size;
565 obj_coords.y1 -= ext_size;
566 obj_coords.x2 += ext_size;
567 obj_coords.y2 += ext_size;
568
569 lv_obj_invalidate_area(obj, &obj_coords);
570
571 }
572
573 /**
574 * Tell whether an area of an object is visible (even partially) now or not
575 * @param obj pointer to an object
576 * @param area the are to check. The visible part of the area will be written back here.
577 * @return true: visible; false: not visible (hidden, out of parent, on other screen, etc)
578 */
lv_obj_area_is_visible(const lv_obj_t * obj,lv_area_t * area)579 bool lv_obj_area_is_visible(const lv_obj_t * obj, lv_area_t * area)
580 {
581 if(lv_obj_get_hidden(obj)) return false;
582
583 /*Invalidate the object only if it belongs to the curent or previous'*/
584 lv_obj_t * obj_scr = lv_obj_get_screen(obj);
585 lv_disp_t * disp = lv_obj_get_disp(obj_scr);
586 if(obj_scr == lv_disp_get_scr_act(disp) ||
587 obj_scr == lv_disp_get_scr_prev(disp) ||
588 obj_scr == lv_disp_get_layer_top(disp) ||
589 obj_scr == lv_disp_get_layer_sys(disp)) {
590
591 /*Truncate the area to the object*/
592 lv_area_t obj_coords;
593 lv_coord_t ext_size = obj->ext_draw_pad;
594 lv_area_copy(&obj_coords, &obj->coords);
595 obj_coords.x1 -= ext_size;
596 obj_coords.y1 -= ext_size;
597 obj_coords.x2 += ext_size;
598 obj_coords.y2 += ext_size;
599
600 bool is_common;
601
602 is_common = _lv_area_intersect(area, area, &obj_coords);
603 if(is_common == false) return false; /*The area is not on the object*/
604
605 /*Truncate recursively to the parents*/
606 lv_obj_t * par = lv_obj_get_parent(obj);
607 while(par != NULL) {
608 is_common = _lv_area_intersect(area, area, &par->coords);
609 if(is_common == false) return false; /*If no common parts with parent break;*/
610 if(lv_obj_get_hidden(par)) return false; /*If the parent is hidden then the child is hidden and won't be drawn*/
611
612 par = lv_obj_get_parent(par);
613 }
614 }
615
616 return true;
617 }
618
619 /**
620 * Tell whether an object is visible (even partially) now or not
621 * @param obj pointer to an object
622 * @return true: visible; false: not visible (hidden, out of parent, on other screen, etc)
623 */
lv_obj_is_visible(const lv_obj_t * obj)624 bool lv_obj_is_visible(const lv_obj_t * obj)
625 {
626 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
627
628 lv_area_t obj_coords;
629 lv_coord_t ext_size = obj->ext_draw_pad;
630 lv_area_copy(&obj_coords, &obj->coords);
631 obj_coords.x1 -= ext_size;
632 obj_coords.y1 -= ext_size;
633 obj_coords.x2 += ext_size;
634 obj_coords.y2 += ext_size;
635
636 return lv_obj_area_is_visible(obj, &obj_coords);
637
638 }
639
640 /*=====================
641 * Setter functions
642 *====================*/
643
644 /*--------------------
645 * Parent/children set
646 *--------------------*/
647
648 /**
649 * Set a new parent for an object. Its relative position will be the same.
650 * @param obj pointer to an object. Can't be a screen.
651 * @param parent pointer to the new parent object. (Can't be NULL)
652 */
lv_obj_set_parent(lv_obj_t * obj,lv_obj_t * parent)653 void lv_obj_set_parent(lv_obj_t * obj, lv_obj_t * parent)
654 {
655 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
656 LV_ASSERT_OBJ(parent, LV_OBJX_NAME);
657
658 if(obj->parent == NULL) {
659 LV_LOG_WARN("Can't set the parent of a screen");
660 return;
661 }
662
663 if(parent == NULL) {
664 LV_LOG_WARN("Can't set parent == NULL to an object");
665 return;
666 }
667
668 lv_obj_invalidate(obj);
669
670 lv_obj_t * old_par = obj->parent;
671 lv_point_t old_pos;
672 old_pos.y = lv_obj_get_y(obj);
673
674 lv_bidi_dir_t new_base_dir = lv_obj_get_base_dir(parent);
675
676 if(new_base_dir != LV_BIDI_DIR_RTL) {
677 old_pos.x = lv_obj_get_x(obj);
678 }
679 else {
680 old_pos.x = old_par->coords.x2 - obj->coords.x2;
681 }
682
683 _lv_ll_chg_list(&obj->parent->child_ll, &parent->child_ll, obj, true);
684 obj->parent = parent;
685
686
687 if(new_base_dir != LV_BIDI_DIR_RTL) {
688 lv_obj_set_pos(obj, old_pos.x, old_pos.y);
689 }
690 else {
691 /*Align to the right in case of RTL base dir*/
692 lv_coord_t new_x = lv_obj_get_width(parent) - old_pos.x - lv_obj_get_width(obj);
693 lv_obj_set_pos(obj, new_x, old_pos.y);
694 }
695
696 /*Notify the original parent because one of its children is lost*/
697 old_par->signal_cb(old_par, LV_SIGNAL_CHILD_CHG, NULL);
698
699 /*Notify the new parent about the child*/
700 parent->signal_cb(parent, LV_SIGNAL_CHILD_CHG, obj);
701
702 lv_obj_invalidate(obj);
703 }
704
705 /**
706 * Move and object to the foreground
707 * @param obj pointer to an object
708 */
lv_obj_move_foreground(lv_obj_t * obj)709 void lv_obj_move_foreground(lv_obj_t * obj)
710 {
711 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
712
713 lv_obj_t * parent = lv_obj_get_parent(obj);
714
715 /*Do nothing of already in the foreground*/
716 if(_lv_ll_get_head(&parent->child_ll) == obj) return;
717
718 lv_obj_invalidate(parent);
719
720 _lv_ll_chg_list(&parent->child_ll, &parent->child_ll, obj, true);
721
722 /*Notify the new parent about the child*/
723 parent->signal_cb(parent, LV_SIGNAL_CHILD_CHG, obj);
724
725 lv_obj_invalidate(parent);
726 }
727
728 /**
729 * Move and object to the background
730 * @param obj pointer to an object
731 */
lv_obj_move_background(lv_obj_t * obj)732 void lv_obj_move_background(lv_obj_t * obj)
733 {
734 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
735
736 lv_obj_t * parent = lv_obj_get_parent(obj);
737
738 /*Do nothing of already in the background*/
739 if(_lv_ll_get_tail(&parent->child_ll) == obj) return;
740
741 lv_obj_invalidate(parent);
742
743 _lv_ll_chg_list(&parent->child_ll, &parent->child_ll, obj, false);
744
745 /*Notify the new parent about the child*/
746 parent->signal_cb(parent, LV_SIGNAL_CHILD_CHG, obj);
747
748 lv_obj_invalidate(parent);
749 }
750
751 /*--------------------
752 * Coordinate set
753 * ------------------*/
754
755 /**
756 * Set relative the position of an object (relative to the parent)
757 * @param obj pointer to an object
758 * @param x new distance from the left side of the parent
759 * @param y new distance from the top of the parent
760 */
lv_obj_set_pos(lv_obj_t * obj,lv_coord_t x,lv_coord_t y)761 void lv_obj_set_pos(lv_obj_t * obj, lv_coord_t x, lv_coord_t y)
762 {
763 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
764
765 /*Convert x and y to absolute coordinates*/
766 lv_obj_t * par = obj->parent;
767
768 if(par) {
769 x = x + par->coords.x1;
770 y = y + par->coords.y1;
771 }
772
773
774 /*Calculate and set the movement*/
775 lv_point_t diff;
776 diff.x = x - obj->coords.x1;
777 diff.y = y - obj->coords.y1;
778
779 /* Do nothing if the position is not changed */
780 /* It is very important else recursive positioning can
781 * occur without position change*/
782 if(diff.x == 0 && diff.y == 0) return;
783
784 /*Invalidate the original area*/
785 lv_obj_invalidate(obj);
786
787 /*Save the original coordinates*/
788 lv_area_t ori;
789 lv_obj_get_coords(obj, &ori);
790
791 obj->coords.x1 += diff.x;
792 obj->coords.y1 += diff.y;
793 obj->coords.x2 += diff.x;
794 obj->coords.y2 += diff.y;
795
796 refresh_children_position(obj, diff.x, diff.y);
797
798 /*Inform the object about its new coordinates*/
799 obj->signal_cb(obj, LV_SIGNAL_COORD_CHG, &ori);
800
801 /*Send a signal to the parent too*/
802 if(par) par->signal_cb(par, LV_SIGNAL_CHILD_CHG, obj);
803
804 /*Invalidate the new area*/
805 lv_obj_invalidate(obj);
806 }
807
808 /**
809 * Set the x coordinate of a object
810 * @param obj pointer to an object
811 * @param x new distance from the left side from the parent
812 */
lv_obj_set_x(lv_obj_t * obj,lv_coord_t x)813 void lv_obj_set_x(lv_obj_t * obj, lv_coord_t x)
814 {
815 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
816
817 lv_obj_set_pos(obj, x, lv_obj_get_y(obj));
818 }
819
820 /**
821 * Set the y coordinate of a object
822 * @param obj pointer to an object
823 * @param y new distance from the top of the parent
824 */
lv_obj_set_y(lv_obj_t * obj,lv_coord_t y)825 void lv_obj_set_y(lv_obj_t * obj, lv_coord_t y)
826 {
827 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
828
829 lv_obj_set_pos(obj, lv_obj_get_x(obj), y);
830 }
831
832 /**
833 * Set the size of an object
834 * @param obj pointer to an object
835 * @param w new width
836 * @param h new height
837 */
lv_obj_set_size(lv_obj_t * obj,lv_coord_t w,lv_coord_t h)838 void lv_obj_set_size(lv_obj_t * obj, lv_coord_t w, lv_coord_t h)
839 {
840 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
841
842 /* Do nothing if the size is not changed */
843 /* It is very important else recursive resizing can
844 * occur without size change*/
845 if(lv_obj_get_width(obj) == w && lv_obj_get_height(obj) == h) {
846 return;
847 }
848
849 /*Invalidate the original area*/
850 lv_obj_invalidate(obj);
851
852 /*Save the original coordinates*/
853 lv_area_t ori;
854 lv_obj_get_coords(obj, &ori);
855
856 /*Set the length and height*/
857 obj->coords.y2 = obj->coords.y1 + h - 1;
858 if(lv_obj_get_base_dir(obj) == LV_BIDI_DIR_RTL) {
859 obj->coords.x1 = obj->coords.x2 - w + 1;
860 }
861 else {
862 obj->coords.x2 = obj->coords.x1 + w - 1;
863 }
864
865 /*Send a signal to the object with its new coordinates*/
866 obj->signal_cb(obj, LV_SIGNAL_COORD_CHG, &ori);
867
868 /*Send a signal to the parent too*/
869 lv_obj_t * par = lv_obj_get_parent(obj);
870 if(par != NULL) par->signal_cb(par, LV_SIGNAL_CHILD_CHG, obj);
871
872 /*Tell the children the parent's size has changed*/
873 lv_obj_t * i;
874 _LV_LL_READ(obj->child_ll, i) {
875 i->signal_cb(i, LV_SIGNAL_PARENT_SIZE_CHG, &ori);
876 }
877
878 /*Invalidate the new area*/
879 lv_obj_invalidate(obj);
880
881 /*Automatically realign the object if required*/
882 #if LV_USE_OBJ_REALIGN
883 if(obj->realign.auto_realign) lv_obj_realign(obj);
884 #endif
885 }
886
887 /**
888 * Set the width of an object
889 * @param obj pointer to an object
890 * @param w new width
891 */
lv_obj_set_width(lv_obj_t * obj,lv_coord_t w)892 void lv_obj_set_width(lv_obj_t * obj, lv_coord_t w)
893 {
894 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
895
896 lv_obj_set_size(obj, w, lv_obj_get_height(obj));
897 }
898
899 /**
900 * Set the height of an object
901 * @param obj pointer to an object
902 * @param h new height
903 */
lv_obj_set_height(lv_obj_t * obj,lv_coord_t h)904 void lv_obj_set_height(lv_obj_t * obj, lv_coord_t h)
905 {
906 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
907
908 lv_obj_set_size(obj, lv_obj_get_width(obj), h);
909 }
910
911 /**
912 * Set the width reduced by the left and right padding.
913 * @param obj pointer to an object
914 * @param w the width without paddings
915 */
lv_obj_set_width_fit(lv_obj_t * obj,lv_coord_t w)916 void lv_obj_set_width_fit(lv_obj_t * obj, lv_coord_t w)
917 {
918 lv_style_int_t pleft = lv_obj_get_style_pad_left(obj, LV_OBJ_PART_MAIN);
919 lv_style_int_t pright = lv_obj_get_style_pad_right(obj, LV_OBJ_PART_MAIN);
920
921 lv_obj_set_width(obj, w - pleft - pright);
922 }
923
924 /**
925 * Set the height reduced by the top and bottom padding.
926 * @param obj pointer to an object
927 * @param h the height without paddings
928 */
lv_obj_set_height_fit(lv_obj_t * obj,lv_coord_t h)929 void lv_obj_set_height_fit(lv_obj_t * obj, lv_coord_t h)
930 {
931 lv_style_int_t ptop = lv_obj_get_style_pad_top(obj, LV_OBJ_PART_MAIN);
932 lv_style_int_t pbottom = lv_obj_get_style_pad_bottom(obj, LV_OBJ_PART_MAIN);
933
934 lv_obj_set_height(obj, h - ptop - pbottom);
935 }
936
937 /**
938 * Set the width of an object by taking the left and right margin into account.
939 * The object width will be `obj_w = w - margin_left - margin_right`
940 * @param obj pointer to an object
941 * @param w new height including margins
942 */
lv_obj_set_width_margin(lv_obj_t * obj,lv_coord_t w)943 void lv_obj_set_width_margin(lv_obj_t * obj, lv_coord_t w)
944 {
945 lv_style_int_t mleft = lv_obj_get_style_margin_left(obj, LV_OBJ_PART_MAIN);
946 lv_style_int_t mright = lv_obj_get_style_margin_right(obj, LV_OBJ_PART_MAIN);
947
948 lv_obj_set_width(obj, w - mleft - mright);
949 }
950
951 /**
952 * Set the height of an object by taking the top and bottom margin into account.
953 * The object height will be `obj_h = h - margin_top - margin_bottom`
954 * @param obj pointer to an object
955 * @param h new height including margins
956 */
lv_obj_set_height_margin(lv_obj_t * obj,lv_coord_t h)957 void lv_obj_set_height_margin(lv_obj_t * obj, lv_coord_t h)
958 {
959 lv_style_int_t mtop = lv_obj_get_style_margin_top(obj, LV_OBJ_PART_MAIN);
960 lv_style_int_t mbottom = lv_obj_get_style_margin_bottom(obj, LV_OBJ_PART_MAIN);
961
962 lv_obj_set_height(obj, h - mtop - mbottom);
963 }
964
965 /**
966 * Align an object to an other object.
967 * @param obj pointer to an object to align
968 * @param base pointer to an object (if NULL the parent is used). 'obj' will be aligned to it.
969 * @param align type of alignment (see 'lv_align_t' enum)
970 * @param x_ofs x coordinate offset after alignment
971 * @param y_ofs y coordinate offset after alignment
972 */
lv_obj_align(lv_obj_t * obj,const lv_obj_t * base,lv_align_t align,lv_coord_t x_ofs,lv_coord_t y_ofs)973 void lv_obj_align(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, lv_coord_t x_ofs, lv_coord_t y_ofs)
974 {
975 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
976
977 if(base == NULL) base = lv_obj_get_parent(obj);
978
979 LV_ASSERT_OBJ(base, LV_OBJX_NAME);
980
981 obj_align_core(obj, base, align, true, true, x_ofs, y_ofs);
982
983 #if LV_USE_OBJ_REALIGN
984 /*Save the last align parameters to use them in `lv_obj_realign`*/
985 obj->realign.align = align;
986 obj->realign.xofs = x_ofs;
987 obj->realign.yofs = y_ofs;
988 obj->realign.base = base;
989 obj->realign.mid_align = 0;
990 #endif
991 }
992
993 /**
994 * Align an object to an other object horizontally.
995 * @param obj pointer to an object to align
996 * @param base pointer to an object (if NULL the parent is used). 'obj' will be aligned to it.
997 * @param align type of alignment (see 'lv_align_t' enum)
998 * @param x_ofs x coordinate offset after alignment
999 */
lv_obj_align_x(lv_obj_t * obj,const lv_obj_t * base,lv_align_t align,lv_coord_t x_ofs)1000 void lv_obj_align_x(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, lv_coord_t x_ofs)
1001 {
1002 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
1003
1004 if(base == NULL) base = lv_obj_get_parent(obj);
1005
1006 LV_ASSERT_OBJ(base, LV_OBJX_NAME);
1007
1008 obj_align_core(obj, base, align, true, false, x_ofs, 0);
1009 }
1010
1011 /**
1012 * Align an object to an other object vertically.
1013 * @param obj pointer to an object to align
1014 * @param base pointer to an object (if NULL the parent is used). 'obj' will be aligned to it.
1015 * @param align type of alignment (see 'lv_align_t' enum)
1016 * @param y_ofs y coordinate offset after alignment
1017 */
lv_obj_align_y(lv_obj_t * obj,const lv_obj_t * base,lv_align_t align,lv_coord_t y_ofs)1018 void lv_obj_align_y(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, lv_coord_t y_ofs)
1019 {
1020 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
1021
1022 if(base == NULL) base = lv_obj_get_parent(obj);
1023
1024 LV_ASSERT_OBJ(base, LV_OBJX_NAME);
1025
1026 obj_align_core(obj, base, align, false, true, 0, y_ofs);
1027 }
1028
1029 /**
1030 * Align an object's middle point to an other object.
1031 * @param obj pointer to an object to align
1032 * @param base pointer to an object (if NULL the parent is used). 'obj' will be aligned to it.
1033 * @param align type of alignment (see 'lv_align_t' enum)
1034 * @param x_ofs x coordinate offset after alignment
1035 * @param y_ofs y coordinate offset after alignment
1036 */
lv_obj_align_mid(lv_obj_t * obj,const lv_obj_t * base,lv_align_t align,lv_coord_t x_ofs,lv_coord_t y_ofs)1037 void lv_obj_align_mid(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, lv_coord_t x_ofs, lv_coord_t y_ofs)
1038 {
1039 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
1040
1041 if(base == NULL) {
1042 base = lv_obj_get_parent(obj);
1043 }
1044
1045 LV_ASSERT_OBJ(base, LV_OBJX_NAME);
1046
1047
1048 obj_align_mid_core(obj, base, align, true, true, x_ofs, y_ofs);
1049
1050 #if LV_USE_OBJ_REALIGN
1051 /*Save the last align parameters to use them in `lv_obj_realign`*/
1052 obj->realign.align = align;
1053 obj->realign.xofs = x_ofs;
1054 obj->realign.yofs = y_ofs;
1055 obj->realign.base = base;
1056 obj->realign.mid_align = 1;
1057 #endif
1058 }
1059
1060 /**
1061 * Align an object's middle point to an other object horizontally.
1062 * @param obj pointer to an object to align
1063 * @param base pointer to an object (if NULL the parent is used). 'obj' will be aligned to it.
1064 * @param align type of alignment (see 'lv_align_t' enum)
1065 * @param x_ofs x coordinate offset after alignment
1066 */
lv_obj_align_mid_x(lv_obj_t * obj,const lv_obj_t * base,lv_align_t align,lv_coord_t x_ofs)1067 void lv_obj_align_mid_x(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, lv_coord_t x_ofs)
1068 {
1069 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
1070
1071 if(base == NULL) {
1072 base = lv_obj_get_parent(obj);
1073 }
1074
1075 LV_ASSERT_OBJ(base, LV_OBJX_NAME);
1076
1077
1078 obj_align_mid_core(obj, base, align, true, false, x_ofs, 0);
1079 }
1080
1081
1082 /**
1083 * Align an object's middle point to an other object vertically.
1084 * @param obj pointer to an object to align
1085 * @param base pointer to an object (if NULL the parent is used). 'obj' will be aligned to it.
1086 * @param align type of alignment (see 'lv_align_t' enum)
1087 * @param y_ofs y coordinate offset after alignment
1088 */
lv_obj_align_mid_y(lv_obj_t * obj,const lv_obj_t * base,lv_align_t align,lv_coord_t y_ofs)1089 void lv_obj_align_mid_y(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, lv_coord_t y_ofs)
1090 {
1091 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
1092
1093 if(base == NULL) {
1094 base = lv_obj_get_parent(obj);
1095 }
1096
1097 LV_ASSERT_OBJ(base, LV_OBJX_NAME);
1098
1099
1100 obj_align_mid_core(obj, base, align, false, true, 0, y_ofs);
1101 }
1102
1103 /**
1104 * Realign the object based on the last `lv_obj_align` parameters.
1105 * @param obj pointer to an object
1106 */
lv_obj_realign(lv_obj_t * obj)1107 void lv_obj_realign(lv_obj_t * obj)
1108 {
1109 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
1110
1111 #if LV_USE_OBJ_REALIGN
1112 if(obj->realign.mid_align)
1113 lv_obj_align_mid(obj, obj->realign.base, obj->realign.align, obj->realign.xofs, obj->realign.yofs);
1114 else
1115 lv_obj_align(obj, obj->realign.base, obj->realign.align, obj->realign.xofs, obj->realign.yofs);
1116 #else
1117 (void)obj;
1118 LV_LOG_WARN("lv_obj_realign: no effect because LV_USE_OBJ_REALIGN = 0");
1119 #endif
1120 }
1121
1122 /**
1123 * Enable the automatic realign of the object when its size has changed based on the last
1124 * `lv_obj_align` parameters.
1125 * @param obj pointer to an object
1126 * @param en true: enable auto realign; false: disable auto realign
1127 */
lv_obj_set_auto_realign(lv_obj_t * obj,bool en)1128 void lv_obj_set_auto_realign(lv_obj_t * obj, bool en)
1129 {
1130 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
1131
1132 #if LV_USE_OBJ_REALIGN
1133 obj->realign.auto_realign = en ? 1 : 0;
1134 #else
1135 (void)obj;
1136 (void)en;
1137 LV_LOG_WARN("lv_obj_set_auto_realign: no effect because LV_USE_OBJ_REALIGN = 0");
1138 #endif
1139 }
1140
1141
1142 /**
1143 * Set the size of an extended clickable area
1144 * If TINY mode is used, only the largest of the horizontal and vertical padding
1145 * values are considered.
1146 * @param obj pointer to an object
1147 * @param left extended clickable are on the left [px]
1148 * @param right extended clickable are on the right [px]
1149 * @param top extended clickable are on the top [px]
1150 * @param bottom extended clickable are on the bottom [px]
1151 */
lv_obj_set_ext_click_area(lv_obj_t * obj,lv_coord_t left,lv_coord_t right,lv_coord_t top,lv_coord_t bottom)1152 void lv_obj_set_ext_click_area(lv_obj_t * obj, lv_coord_t left, lv_coord_t right, lv_coord_t top, lv_coord_t bottom)
1153 {
1154 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
1155
1156 #if LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_FULL
1157 obj->ext_click_pad.x1 = left;
1158 obj->ext_click_pad.x2 = right;
1159 obj->ext_click_pad.y1 = top;
1160 obj->ext_click_pad.y2 = bottom;
1161 #elif LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_TINY
1162 obj->ext_click_pad_hor = LV_MATH_MAX(left, right);
1163 obj->ext_click_pad_ver = LV_MATH_MAX(top, bottom);
1164 #else
1165 (void)obj; /*Unused*/
1166 (void)left; /*Unused*/
1167 (void)right; /*Unused*/
1168 (void)top; /*Unused*/
1169 (void)bottom; /*Unused*/
1170 #endif
1171 }
1172
1173 /*---------------------
1174 * Appearance set
1175 *--------------------*/
1176
1177 /**
1178 * Add a new style to the style list of an object.
1179 * @param obj pointer to an object
1180 * @param part the part of the object which style property should be set.
1181 * E.g. `LV_OBJ_PART_MAIN`, `LV_BTN_PART_MAIN`, `LV_SLIDER_PART_KNOB`
1182 * @param style pointer to a style to add (Only its pointer will be saved)
1183 */
lv_obj_add_style(lv_obj_t * obj,uint8_t part,lv_style_t * style)1184 void lv_obj_add_style(lv_obj_t * obj, uint8_t part, lv_style_t * style)
1185 {
1186 if(style == NULL) return;
1187
1188 lv_style_list_t * style_dsc = lv_obj_get_style_list(obj, part);
1189 if(style_dsc == NULL) {
1190 LV_LOG_WARN("Can't find style with part: %d", part);
1191 return;
1192 }
1193
1194 _lv_style_list_add_style(style_dsc, style);
1195 #if LV_USE_ANIMATION
1196 trans_del(obj, part, 0xFF, NULL);
1197 #endif
1198 lv_obj_refresh_style(obj, part, LV_STYLE_PROP_ALL);
1199 }
1200
1201 /**
1202 * Remove a style from the style list of an object.
1203 * @param obj pointer to an object
1204 * @param part the part of the object which style property should be set.
1205 * E.g. `LV_OBJ_PART_MAIN`, `LV_BTN_PART_MAIN`, `LV_SLIDER_PART_KNOB`
1206 * @param style pointer to a style to remove
1207 */
lv_obj_remove_style(lv_obj_t * obj,uint8_t part,lv_style_t * style)1208 void lv_obj_remove_style(lv_obj_t * obj, uint8_t part, lv_style_t * style)
1209 {
1210 if(style == NULL) return;
1211
1212 lv_style_list_t * style_dsc = lv_obj_get_style_list(obj, part);
1213 if(style_dsc == NULL) {
1214 LV_LOG_WARN("Can't find style with part: %d", part);
1215 return;
1216 }
1217
1218 _lv_style_list_remove_style(style_dsc, style);
1219 #if LV_USE_ANIMATION
1220 trans_del(obj, part, 0xFF, NULL);
1221 #endif
1222 lv_obj_refresh_style(obj, part, LV_STYLE_PROP_ALL);
1223 }
1224
1225 /**
1226 * Reset a style to the default (empty) state.
1227 * Release all used memories and cancel pending related transitions.
1228 * Typically used in `LV_SIGN_CLEAN_UP.
1229 * @param obj pointer to an object
1230 * @param part the part of the object which style list should be reseted.
1231 * E.g. `LV_OBJ_PART_MAIN`, `LV_BTN_PART_MAIN`, `LV_SLIDER_PART_KNOB`
1232 */
lv_obj_clean_style_list(lv_obj_t * obj,uint8_t part)1233 void lv_obj_clean_style_list(lv_obj_t * obj, uint8_t part)
1234 {
1235 lv_style_list_t * style_dsc = lv_obj_get_style_list(obj, part);
1236 if(style_dsc == NULL) {
1237 LV_LOG_WARN("lv_obj_clean_style_list: can't find style with `part`");
1238 return;
1239 }
1240
1241 _lv_style_list_reset(style_dsc);
1242 #if LV_USE_ANIMATION
1243 trans_del(obj, part, 0xFF, NULL);
1244 #endif
1245 }
1246
1247 /**
1248 * Reset a style to the default (empty) state.
1249 * Release all used memories and cancel pending related transitions.
1250 * Also notifies the object about the style change.
1251 * @param obj pointer to an object
1252 * @param part the part of the object which style list should be reseted.
1253 * E.g. `LV_OBJ_PART_MAIN`, `LV_BTN_PART_MAIN`, `LV_SLIDER_PART_KNOB`
1254 */
lv_obj_reset_style_list(lv_obj_t * obj,uint8_t part)1255 void lv_obj_reset_style_list(lv_obj_t * obj, uint8_t part)
1256 {
1257 lv_obj_clean_style_list(obj, part);
1258
1259 lv_obj_refresh_style(obj, part, LV_STYLE_PROP_ALL);
1260 }
1261
1262 /**
1263 * Set a local style property of a part of an object in a given state.
1264 * @param obj pointer to an object
1265 * @param part the part of the object which style property should be set.
1266 * E.g. `LV_OBJ_PART_MAIN`, `LV_BTN_PART_MAIN`, `LV_SLIDER_PART_KNOB`
1267 * @param prop a style property ORed with a state.
1268 * E.g. `LV_STYLE_BORDER_WIDTH | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)`
1269 * @param the value to set
1270 * @note shouldn't be used directly. Use the specific property get functions instead.
1271 * For example: `lv_obj_style_get_border_opa()`
1272 * @note for performance reasons it's not checked if the property really has integer type
1273 */
_lv_obj_set_style_local_int(lv_obj_t * obj,uint8_t part,lv_style_property_t prop,lv_style_int_t value)1274 void _lv_obj_set_style_local_int(lv_obj_t * obj, uint8_t part, lv_style_property_t prop, lv_style_int_t value)
1275 {
1276 lv_style_list_t * style_dsc = lv_obj_get_style_list(obj, part);
1277 _lv_style_list_set_local_int(style_dsc, prop, value);
1278 #if LV_USE_ANIMATION
1279 trans_del(obj, part, prop, NULL);
1280 #endif
1281 lv_obj_refresh_style(obj, part, prop & (~LV_STYLE_STATE_MASK));
1282 }
1283
1284 /**
1285 * Set a local style property of a part of an object in a given state.
1286 * @param obj pointer to an object
1287 * @param part the part of the object which style property should be set.
1288 * E.g. `LV_OBJ_PART_MAIN`, `LV_BTN_PART_MAIN`, `LV_SLIDER_PART_KNOB`
1289 * @param prop a style property ORed with a state.
1290 * E.g. `LV_STYLE_BORDER_COLOR | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)`
1291 * @param the value to set
1292 * @note shouldn't be used directly. Use the specific property get functions instead.
1293 * For example: `lv_obj_style_get_border_opa()`
1294 * @note for performance reasons it's not checked if the property really has color type
1295 */
_lv_obj_set_style_local_color(lv_obj_t * obj,uint8_t part,lv_style_property_t prop,lv_color_t color)1296 void _lv_obj_set_style_local_color(lv_obj_t * obj, uint8_t part, lv_style_property_t prop, lv_color_t color)
1297 {
1298 lv_style_list_t * style_dsc = lv_obj_get_style_list(obj, part);
1299 _lv_style_list_set_local_color(style_dsc, prop, color);
1300 #if LV_USE_ANIMATION
1301 trans_del(obj, part, prop, NULL);
1302 #endif
1303 lv_obj_refresh_style(obj, part, prop & (~LV_STYLE_STATE_MASK));
1304 }
1305
1306 /**
1307 * Set a local style property of a part of an object in a given state.
1308 * @param obj pointer to an object
1309 * @param part the part of the object which style property should be set.
1310 * E.g. `LV_OBJ_PART_MAIN`, `LV_BTN_PART_MAIN`, `LV_SLIDER_PART_KNOB`
1311 * @param prop a style property ORed with a state.
1312 * E.g. `LV_STYLE_BORDER_OPA | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)`
1313 * @param the value to set
1314 * @note shouldn't be used directly. Use the specific property get functions instead.
1315 * For example: `lv_obj_style_get_border_opa()`
1316 * @note for performance reasons it's not checked if the property really has opacity type
1317 */
_lv_obj_set_style_local_opa(lv_obj_t * obj,uint8_t part,lv_style_property_t prop,lv_opa_t opa)1318 void _lv_obj_set_style_local_opa(lv_obj_t * obj, uint8_t part, lv_style_property_t prop, lv_opa_t opa)
1319 {
1320 lv_style_list_t * style_dsc = lv_obj_get_style_list(obj, part);
1321 _lv_style_list_set_local_opa(style_dsc, prop, opa);
1322 #if LV_USE_ANIMATION
1323 trans_del(obj, part, prop, NULL);
1324 #endif
1325 lv_obj_refresh_style(obj, part, prop & (~LV_STYLE_STATE_MASK));
1326 }
1327
1328 /**
1329 * Set a local style property of a part of an object in a given state.
1330 * @param obj pointer to an object
1331 * @param part the part of the object which style property should be set.
1332 * E.g. `LV_OBJ_PART_MAIN`, `LV_BTN_PART_MAIN`, `LV_SLIDER_PART_KNOB`
1333 * @param prop a style property ORed with a state.
1334 * E.g. `LV_STYLE_TEXT_FONT | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)`
1335 * @param value the value to set
1336 * @note shouldn't be used directly. Use the specific property get functions instead.
1337 * For example: `lv_obj_style_get_border_opa()`
1338 * @note for performance reasons it's not checked if the property really has pointer type
1339 */
_lv_obj_set_style_local_ptr(lv_obj_t * obj,uint8_t part,lv_style_property_t prop,const void * value)1340 void _lv_obj_set_style_local_ptr(lv_obj_t * obj, uint8_t part, lv_style_property_t prop, const void * value)
1341 {
1342 lv_style_list_t * style_dsc = lv_obj_get_style_list(obj, part);
1343 _lv_style_list_set_local_ptr(style_dsc, prop, value);
1344 #if LV_USE_ANIMATION
1345 trans_del(obj, part, prop, NULL);
1346 #endif
1347 lv_obj_refresh_style(obj, part, prop & (~LV_STYLE_STATE_MASK));
1348 }
1349
1350 /**
1351 * Remove a local style property from a part of an object with a given state.
1352 * @param obj pointer to an object
1353 * @param part the part of the object which style property should be removed.
1354 * E.g. `LV_OBJ_PART_MAIN`, `LV_BTN_PART_MAIN`, `LV_SLIDER_PART_KNOB`
1355 * @param prop a style property ORed with a state.
1356 * E.g. `LV_STYLE_TEXT_FONT | (LV_STATE_PRESSED << LV_STYLE_STATE_POS)`
1357 * @note shouldn't be used directly. Use the specific property remove functions instead.
1358 * For example: `lv_obj_style_remove_border_opa()`
1359 * @return true: the property was found and removed; false: the property was not found
1360 */
lv_obj_remove_style_local_prop(lv_obj_t * obj,uint8_t part,lv_style_property_t prop)1361 bool lv_obj_remove_style_local_prop(lv_obj_t * obj, uint8_t part, lv_style_property_t prop)
1362 {
1363 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
1364 lv_style_t * style = lv_obj_get_local_style(obj, part);
1365 if(style) return lv_style_remove_prop(style, prop);
1366 else return false;
1367 }
1368
1369 /**
1370 * Notify an object (and its children) about its style is modified
1371 * @param obj pointer to an object
1372 * @param part the part of the object which style property should be refreshed.
1373 * @param prop `LV_STYLE_PROP_ALL` or an `LV_STYLE_...` property. It is used to optimize what needs to be refreshed.
1374 */
lv_obj_refresh_style(lv_obj_t * obj,uint8_t part,lv_style_property_t prop)1375 void lv_obj_refresh_style(lv_obj_t * obj, uint8_t part, lv_style_property_t prop)
1376 {
1377 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
1378
1379 invalidate_style_cache(obj, part, prop);
1380
1381 /*If a real style refresh is required*/
1382 bool real_refr = false;
1383 switch(prop) {
1384 case LV_STYLE_PROP_ALL:
1385 case LV_STYLE_CLIP_CORNER:
1386 case LV_STYLE_SIZE:
1387 case LV_STYLE_TRANSFORM_WIDTH:
1388 case LV_STYLE_TRANSFORM_HEIGHT:
1389 case LV_STYLE_TRANSFORM_ANGLE:
1390 case LV_STYLE_TRANSFORM_ZOOM:
1391 case LV_STYLE_PAD_TOP:
1392 case LV_STYLE_PAD_BOTTOM:
1393 case LV_STYLE_PAD_LEFT:
1394 case LV_STYLE_PAD_RIGHT:
1395 case LV_STYLE_PAD_INNER:
1396 case LV_STYLE_MARGIN_TOP:
1397 case LV_STYLE_MARGIN_BOTTOM:
1398 case LV_STYLE_MARGIN_LEFT:
1399 case LV_STYLE_MARGIN_RIGHT:
1400 case LV_STYLE_OUTLINE_WIDTH:
1401 case LV_STYLE_OUTLINE_PAD:
1402 case LV_STYLE_OUTLINE_OPA:
1403 case LV_STYLE_SHADOW_WIDTH:
1404 case LV_STYLE_SHADOW_OPA:
1405 case LV_STYLE_SHADOW_OFS_X:
1406 case LV_STYLE_SHADOW_OFS_Y:
1407 case LV_STYLE_SHADOW_SPREAD:
1408 case LV_STYLE_VALUE_LETTER_SPACE:
1409 case LV_STYLE_VALUE_LINE_SPACE:
1410 case LV_STYLE_VALUE_OFS_X:
1411 case LV_STYLE_VALUE_OFS_Y:
1412 case LV_STYLE_VALUE_ALIGN:
1413 case LV_STYLE_VALUE_STR:
1414 case LV_STYLE_VALUE_FONT:
1415 case LV_STYLE_VALUE_OPA:
1416 case LV_STYLE_TEXT_LETTER_SPACE:
1417 case LV_STYLE_TEXT_LINE_SPACE:
1418 case LV_STYLE_TEXT_FONT:
1419 case LV_STYLE_LINE_WIDTH:
1420 real_refr = true;
1421 break;
1422 default:
1423 real_refr = false;
1424 }
1425
1426 if(real_refr) {
1427 lv_obj_invalidate(obj);
1428 obj->signal_cb(obj, LV_SIGNAL_STYLE_CHG, NULL);
1429
1430 switch(prop) {
1431 case LV_STYLE_PROP_ALL:
1432 case LV_STYLE_MARGIN_TOP:
1433 case LV_STYLE_MARGIN_BOTTOM:
1434 case LV_STYLE_MARGIN_LEFT:
1435 case LV_STYLE_MARGIN_RIGHT:
1436 if(obj->parent) obj->parent->signal_cb(obj->parent, LV_SIGNAL_CHILD_CHG, NULL);
1437 break;
1438 }
1439
1440 lv_obj_invalidate(obj);
1441
1442 /*Send style change signals*/
1443 if(prop == LV_STYLE_PROP_ALL || (prop & LV_STYLE_INHERIT_MASK)) refresh_children_style(obj);
1444 }
1445 else {
1446 lv_obj_invalidate(obj);
1447 }
1448 }
1449
1450 /**
1451 * Notify all object if a style is modified
1452 * @param style pointer to a style. Only the objects with this style will be notified
1453 * (NULL to notify all objects)
1454 */
lv_obj_report_style_mod(lv_style_t * style)1455 void lv_obj_report_style_mod(lv_style_t * style)
1456 {
1457 lv_disp_t * d = lv_disp_get_next(NULL);
1458
1459 while(d) {
1460 lv_obj_t * i;
1461 _LV_LL_READ(d->scr_ll, i) {
1462 report_style_mod_core(style, i);
1463 }
1464 d = lv_disp_get_next(d);
1465 }
1466 }
1467
1468 /**
1469 * Enable/disable the use of style cahche for an object
1470 * @param obj pointer to an object
1471 * @param dis true: disable; false: enable (re-enable)
1472 */
_lv_obj_disable_style_caching(lv_obj_t * obj,bool dis)1473 void _lv_obj_disable_style_caching(lv_obj_t * obj, bool dis)
1474 {
1475 uint8_t part;
1476 for(part = 0; part < _LV_OBJ_PART_REAL_FIRST; part++) {
1477 lv_style_list_t * list = lv_obj_get_style_list(obj, part);
1478 if(list == NULL) break;
1479 list->ignore_cache = dis;
1480 }
1481 for(part = _LV_OBJ_PART_REAL_FIRST; part < 0xFF; part++) {
1482 lv_style_list_t * list = lv_obj_get_style_list(obj, part);
1483 if(list == NULL) break;
1484 list->ignore_cache = dis;
1485 }
1486 }
1487
1488 /*-----------------
1489 * Attribute set
1490 *----------------*/
1491
1492 /**
1493 * Hide an object. It won't be visible and clickable.
1494 * @param obj pointer to an object
1495 * @param en true: hide the object
1496 */
lv_obj_set_hidden(lv_obj_t * obj,bool en)1497 void lv_obj_set_hidden(lv_obj_t * obj, bool en)
1498 {
1499 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
1500
1501 if(!obj->hidden) lv_obj_invalidate(obj); /*Invalidate when not hidden (hidden objects are ignored) */
1502
1503 obj->hidden = en == false ? 0 : 1;
1504
1505 if(!obj->hidden) lv_obj_invalidate(obj); /*Invalidate when not hidden (hidden objects are ignored) */
1506
1507 lv_obj_t * par = lv_obj_get_parent(obj);
1508 if(par) par->signal_cb(par, LV_SIGNAL_CHILD_CHG, obj);
1509 }
1510
1511 /**
1512 * Set whether advanced hit-testing is enabled on an object
1513 * @param obj pointer to an object
1514 * @param en true: advanced hit-testing is enabled
1515 */
lv_obj_set_adv_hittest(lv_obj_t * obj,bool en)1516 void lv_obj_set_adv_hittest(lv_obj_t * obj, bool en)
1517 {
1518 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
1519
1520 obj->adv_hittest = en == false ? 0 : 1;
1521 }
1522
1523 /**
1524 * Enable or disable the clicking of an object
1525 * @param obj pointer to an object
1526 * @param en true: make the object clickable
1527 */
lv_obj_set_click(lv_obj_t * obj,bool en)1528 void lv_obj_set_click(lv_obj_t * obj, bool en)
1529 {
1530 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
1531
1532 obj->click = (en == true ? 1 : 0);
1533 }
1534
1535 /**
1536 * Enable to bring this object to the foreground if it
1537 * or any of its children is clicked
1538 * @param obj pointer to an object
1539 * @param en true: enable the auto top feature
1540 */
lv_obj_set_top(lv_obj_t * obj,bool en)1541 void lv_obj_set_top(lv_obj_t * obj, bool en)
1542 {
1543 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
1544
1545 obj->top = (en == true ? 1 : 0);
1546 }
1547
1548 /**
1549 * Enable the dragging of an object
1550 * @param obj pointer to an object
1551 * @param en true: make the object draggable
1552 */
lv_obj_set_drag(lv_obj_t * obj,bool en)1553 void lv_obj_set_drag(lv_obj_t * obj, bool en)
1554 {
1555 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
1556
1557 if(en == true) lv_obj_set_click(obj, true); /*Drag is useless without enabled clicking*/
1558 obj->drag = (en == true ? 1 : 0);
1559 }
1560
1561 /**
1562 * Set the directions an object can be dragged in
1563 * @param obj pointer to an object
1564 * @param drag_dir bitwise OR of allowed directions an object can be dragged in
1565 */
lv_obj_set_drag_dir(lv_obj_t * obj,lv_drag_dir_t drag_dir)1566 void lv_obj_set_drag_dir(lv_obj_t * obj, lv_drag_dir_t drag_dir)
1567 {
1568 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
1569
1570 obj->drag_dir = drag_dir;
1571
1572 if(obj->drag_dir != 0) lv_obj_set_drag(obj, true); /*Drag direction requires drag*/
1573 }
1574
1575 /**
1576 * Enable the throwing of an object after is is dragged
1577 * @param obj pointer to an object
1578 * @param en true: enable the drag throw
1579 */
lv_obj_set_drag_throw(lv_obj_t * obj,bool en)1580 void lv_obj_set_drag_throw(lv_obj_t * obj, bool en)
1581 {
1582 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
1583
1584 obj->drag_throw = (en == true ? 1 : 0);
1585 }
1586
1587 /**
1588 * Enable to use parent for drag related operations.
1589 * If trying to drag the object the parent will be moved instead
1590 * @param obj pointer to an object
1591 * @param en true: enable the 'drag parent' for the object
1592 */
lv_obj_set_drag_parent(lv_obj_t * obj,bool en)1593 void lv_obj_set_drag_parent(lv_obj_t * obj, bool en)
1594 {
1595 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
1596
1597 obj->drag_parent = (en == true ? 1 : 0);
1598 }
1599
1600 /**
1601 * Enable to use parent for gesture related operations.
1602 * If trying to gesture the object the parent will be moved instead
1603 * @param obj pointer to an object
1604 * @param en true: enable the 'gesture parent' for the object
1605 */
lv_obj_set_gesture_parent(lv_obj_t * obj,bool en)1606 void lv_obj_set_gesture_parent(lv_obj_t * obj, bool en)
1607 {
1608 obj->gesture_parent = (en == true ? 1 : 0);
1609 }
1610
1611 /**
1612 * Enable to use parent for focus state.
1613 * When object is focused the parent will get the state instead (visual only)
1614 * @param obj pointer to an object
1615 * @param en true: enable the 'focus parent' for the object
1616 */
lv_obj_set_focus_parent(lv_obj_t * obj,bool en)1617 void lv_obj_set_focus_parent(lv_obj_t * obj, bool en)
1618 {
1619 if(lv_obj_is_focused(obj)) {
1620 if(en) {
1621 obj->focus_parent = 1;
1622 lv_obj_clear_state(obj, LV_STATE_FOCUSED | LV_STATE_EDITED);
1623 lv_obj_set_state(lv_obj_get_focused_obj(obj), LV_STATE_FOCUSED);
1624 }
1625 else {
1626 lv_obj_clear_state(lv_obj_get_focused_obj(obj), LV_STATE_FOCUSED | LV_STATE_EDITED);
1627 lv_obj_set_state(obj, LV_STATE_FOCUSED);
1628 obj->focus_parent = 0;
1629 }
1630 }
1631 else {
1632 obj->focus_parent = (en == true ? 1 : 0);
1633 }
1634 }
1635
1636 /**
1637 * Propagate the events to the parent too
1638 * @param obj pointer to an object
1639 * @param en true: enable the event propagation
1640 */
lv_obj_set_parent_event(lv_obj_t * obj,bool en)1641 void lv_obj_set_parent_event(lv_obj_t * obj, bool en)
1642 {
1643 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
1644
1645 obj->parent_event = (en == true ? 1 : 0);
1646 }
1647
1648 /**
1649 * Set the base direction of the object
1650 * @param obj pointer to an object
1651 * @param dir the new base direction. `LV_BIDI_DIR_LTR/RTL/AUTO/INHERIT`
1652 */
lv_obj_set_base_dir(lv_obj_t * obj,lv_bidi_dir_t dir)1653 void lv_obj_set_base_dir(lv_obj_t * obj, lv_bidi_dir_t dir)
1654 {
1655 if(dir != LV_BIDI_DIR_LTR && dir != LV_BIDI_DIR_RTL &&
1656 dir != LV_BIDI_DIR_AUTO && dir != LV_BIDI_DIR_INHERIT) {
1657
1658 LV_LOG_WARN("lv_obj_set_base_dir: invalid base dir");
1659 return;
1660 }
1661
1662 obj->base_dir = dir;
1663 lv_signal_send(obj, LV_SIGNAL_BASE_DIR_CHG, NULL);
1664
1665 /* Notify the children about the parent base dir has changed.
1666 * (The children might have `LV_BIDI_DIR_INHERIT`)*/
1667 base_dir_refr_children(obj);
1668 }
1669
1670 /**
1671 * Set a bit or bits in the protect filed
1672 * @param obj pointer to an object
1673 * @param prot 'OR'-ed values from `lv_protect_t`
1674 */
lv_obj_add_protect(lv_obj_t * obj,uint8_t prot)1675 void lv_obj_add_protect(lv_obj_t * obj, uint8_t prot)
1676 {
1677 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
1678
1679 obj->protect |= prot;
1680 }
1681
1682 /**
1683 * Clear a bit or bits in the protect filed
1684 * @param obj pointer to an object
1685 * @param prot 'OR'-ed values from `lv_protect_t`
1686 */
lv_obj_clear_protect(lv_obj_t * obj,uint8_t prot)1687 void lv_obj_clear_protect(lv_obj_t * obj, uint8_t prot)
1688 {
1689 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
1690
1691 prot = (~prot) & 0xFF;
1692 obj->protect &= prot;
1693 }
1694
1695 /**
1696 * Set the state (fully overwrite) of an object.
1697 * If specified in the styles a transition animation will be started
1698 * from the previous state to the current
1699 * @param obj pointer to an object
1700 * @param state the new state
1701 */
lv_obj_set_state(lv_obj_t * obj,lv_state_t new_state)1702 void lv_obj_set_state(lv_obj_t * obj, lv_state_t new_state)
1703 {
1704 if(obj->state == new_state) return;
1705
1706 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
1707
1708 lv_state_t prev_state = obj->state;
1709 style_snapshot_res_t cmp_res = STYLE_COMPARE_SAME;
1710 uint8_t part;
1711 for(part = 0; part < _LV_OBJ_PART_REAL_FIRST; part++) {
1712 lv_style_list_t * style_list = lv_obj_get_style_list(obj, part);
1713 if(style_list == NULL) break; /*No more style lists*/
1714 obj->state = prev_state;
1715 style_snapshot_t shot_pre;
1716 style_snapshot(obj, part, &shot_pre);
1717 obj->state = new_state;
1718 style_snapshot_t shot_post;
1719 style_snapshot(obj, part, &shot_post);
1720
1721 style_snapshot_res_t r = style_snapshot_compare(&shot_pre, &shot_post);
1722 if(r == STYLE_COMPARE_DIFF) {
1723 cmp_res = STYLE_COMPARE_DIFF;
1724 break;
1725 }
1726 if(r == STYLE_COMPARE_VISUAL_DIFF) {
1727 cmp_res = STYLE_COMPARE_VISUAL_DIFF;
1728 }
1729 }
1730
1731 obj->state = new_state;
1732
1733 if(cmp_res == STYLE_COMPARE_SAME) {
1734 return;
1735 }
1736
1737 #if LV_USE_ANIMATION == 0
1738 if(cmp_res == STYLE_COMPARE_DIFF) lv_obj_refresh_style(obj, part, LV_STYLE_PROP_ALL);
1739 else if(cmp_res == STYLE_COMPARE_VISUAL_DIFF) lv_obj_refresh_style(obj, LV_OBJ_PART_ALL, LV_STYLE_PROP_ALL);
1740 #else
1741
1742
1743 for(part = 0; part < _LV_OBJ_PART_REAL_LAST; part++) {
1744 lv_style_list_t * style_list = lv_obj_get_style_list(obj, part);
1745 if(style_list == NULL) break; /*No more style lists*/
1746 if(style_list->ignore_trans) continue;
1747
1748 lv_style_int_t time = lv_obj_get_style_transition_time(obj, part);
1749 lv_style_property_t props[LV_STYLE_TRANS_NUM_MAX];
1750 lv_style_int_t delay = lv_obj_get_style_transition_delay(obj, part);
1751 lv_anim_path_t * path = lv_obj_get_style_transition_path(obj, part);
1752 props[0] = lv_obj_get_style_transition_prop_1(obj, part);
1753 props[1] = lv_obj_get_style_transition_prop_2(obj, part);
1754 props[2] = lv_obj_get_style_transition_prop_3(obj, part);
1755 props[3] = lv_obj_get_style_transition_prop_4(obj, part);
1756 props[4] = lv_obj_get_style_transition_prop_5(obj, part);
1757 props[5] = lv_obj_get_style_transition_prop_6(obj, part);
1758
1759 uint8_t i;
1760 for(i = 0; i < LV_STYLE_TRANS_NUM_MAX; i++) {
1761 if(props[i] != 0) {
1762 _lv_style_list_add_trans_style(style_list);
1763
1764 lv_style_trans_t * tr = trans_create(obj, props[i], part, prev_state, new_state);
1765
1766 /*If there is a pending anim for this property remove it*/
1767 if(tr) {
1768 tr->obj = obj;
1769 tr->prop = props[i];
1770 tr->part = part;
1771
1772 lv_anim_t a;
1773 lv_anim_init(&a);
1774 lv_anim_set_var(&a, tr);
1775 lv_anim_set_exec_cb(&a, (lv_anim_exec_xcb_t)trans_anim_cb);
1776 lv_anim_set_start_cb(&a, trans_anim_start_cb);
1777 lv_anim_set_ready_cb(&a, trans_anim_ready_cb);
1778 lv_anim_set_values(&a, 0x00, 0xFF);
1779 lv_anim_set_time(&a, time);
1780 lv_anim_set_delay(&a, delay);
1781 lv_anim_set_path(&a, path);
1782 a.early_apply = 0;
1783 lv_anim_start(&a);
1784 }
1785
1786 }
1787 }
1788 if(cmp_res == STYLE_COMPARE_DIFF) lv_obj_refresh_style(obj, part, LV_STYLE_PROP_ALL);
1789
1790 if(cmp_res == STYLE_COMPARE_VISUAL_DIFF) {
1791 invalidate_style_cache(obj, part, LV_STYLE_PROP_ALL);
1792 }
1793 }
1794
1795 if(cmp_res == STYLE_COMPARE_VISUAL_DIFF) {
1796 lv_obj_invalidate(obj);
1797 }
1798
1799 #endif
1800
1801 }
1802
1803 /**
1804 * Add a given state or states to the object. The other state bits will remain unchanged.
1805 * If specified in the styles a transition animation will be started
1806 * from the previous state to the current
1807 * @param obj pointer to an object
1808 * @param state the state bits to add. E.g `LV_STATE_PRESSED | LV_STATE_FOCUSED`
1809 */
lv_obj_add_state(lv_obj_t * obj,lv_state_t state)1810 void lv_obj_add_state(lv_obj_t * obj, lv_state_t state)
1811 {
1812 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
1813
1814 lv_state_t new_state = obj->state | state;
1815 if(obj->state != new_state) {
1816 lv_obj_set_state(obj, new_state);
1817 }
1818 }
1819
1820 /**
1821 * Remove a given state or states to the object. The other state bits will remain unchanged.
1822 * If specified in the styles a transition animation will be started
1823 * from the previous state to the current
1824 * @param obj pointer to an object
1825 * @param state the state bits to remove. E.g `LV_STATE_PRESSED | LV_STATE_FOCUSED`
1826 */
lv_obj_clear_state(lv_obj_t * obj,lv_state_t state)1827 void lv_obj_clear_state(lv_obj_t * obj, lv_state_t state)
1828 {
1829 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
1830
1831 lv_state_t new_state = obj->state & (~state);
1832 if(obj->state != new_state) {
1833 lv_obj_set_state(obj, new_state);
1834 }
1835 }
1836
1837 #if LV_USE_ANIMATION
1838 /**
1839 * Finish all pending transitions on a part of an object
1840 * @param obj pointer to an object
1841 * @param part part of the object, e.g `LV_BRN_PART_MAIN` or `LV_OBJ_PART_ALL` for all parts
1842 */
lv_obj_finish_transitions(lv_obj_t * obj,uint8_t part)1843 void lv_obj_finish_transitions(lv_obj_t * obj, uint8_t part)
1844 {
1845 /*Animate all related transition to the end value*/
1846 lv_style_trans_t * tr;
1847 _LV_LL_READ_BACK(LV_GC_ROOT(_lv_obj_style_trans_ll), tr) {
1848 if(tr->obj == obj && (part == tr->part || part == LV_OBJ_PART_ALL)) {
1849 trans_anim_cb(tr, 255);
1850 }
1851 }
1852
1853 /*Free all related transition data*/
1854 trans_del(obj, part, 0xFF, NULL);
1855 }
1856 #endif
1857
1858 /**
1859 * Set a an event handler function for an object.
1860 * Used by the user to react on event which happens with the object.
1861 * @param obj pointer to an object
1862 * @param event_cb the new event function
1863 */
lv_obj_set_event_cb(lv_obj_t * obj,lv_event_cb_t event_cb)1864 void lv_obj_set_event_cb(lv_obj_t * obj, lv_event_cb_t event_cb)
1865 {
1866 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
1867
1868 obj->event_cb = event_cb;
1869 }
1870
1871 /**
1872 * Send an event to the object
1873 * @param obj pointer to an object
1874 * @param event the type of the event from `lv_event_t`
1875 * @param data arbitrary data depending on the object type and the event. (Usually `NULL`)
1876 * @return LV_RES_OK: `obj` was not deleted in the event; LV_RES_INV: `obj` was deleted in the event
1877 */
lv_event_send(lv_obj_t * obj,lv_event_t event,const void * data)1878 lv_res_t lv_event_send(lv_obj_t * obj, lv_event_t event, const void * data)
1879 {
1880 if(obj == NULL) return LV_RES_OK;
1881
1882 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
1883
1884 lv_res_t res;
1885 res = lv_event_send_func(obj->event_cb, obj, event, data);
1886 return res;
1887 }
1888
1889 /**
1890 * Send LV_EVENT_REFRESH event to an object
1891 * @param obj point to an obejct. (Can NOT be NULL)
1892 * @return LV_RES_OK: success, LV_RES_INV: to object become invalid (e.g. deleted) due to this event.
1893 */
lv_event_send_refresh(lv_obj_t * obj)1894 lv_res_t lv_event_send_refresh(lv_obj_t * obj)
1895 {
1896 return lv_event_send(obj, LV_EVENT_REFRESH, NULL);
1897 }
1898
1899 /**
1900 * Send LV_EVENT_REFRESH event to an object and all of its children.
1901 * @param obj pointer to an object or NULL to refresh all objects of all displays
1902 */
lv_event_send_refresh_recursive(lv_obj_t * obj)1903 void lv_event_send_refresh_recursive(lv_obj_t * obj)
1904 {
1905 if(obj == NULL) {
1906 /*If no obj specified refresh all screen of all displays */
1907 lv_disp_t * d = lv_disp_get_next(NULL);
1908 while(d) {
1909 lv_obj_t * scr = _lv_ll_get_head(&d->scr_ll);
1910 while(scr) {
1911 lv_event_send_refresh_recursive(scr);
1912 scr = _lv_ll_get_next(&d->scr_ll, scr);
1913 }
1914 lv_event_send_refresh_recursive(d->top_layer);
1915 lv_event_send_refresh_recursive(d->sys_layer);
1916
1917 d = lv_disp_get_next(d);
1918 }
1919 }
1920 else {
1921
1922 lv_res_t res = lv_event_send_refresh(obj);
1923 if(res != LV_RES_OK) return; /*If invalid returned do not check the children*/
1924
1925 lv_obj_t * child = lv_obj_get_child(obj, NULL);
1926 while(child) {
1927 lv_event_send_refresh_recursive(child);
1928
1929 child = lv_obj_get_child(obj, child);
1930 }
1931 }
1932 }
1933
1934
1935 /**
1936 * Call an event function with an object, event, and data.
1937 * @param event_xcb an event callback function. If `NULL` `LV_RES_OK` will return without any actions.
1938 * (the 'x' in the argument name indicates that its not a fully generic function because it not follows
1939 * the `func_name(object, callback, ...)` convention)
1940 * @param obj pointer to an object to associate with the event (can be `NULL` to simply call the `event_cb`)
1941 * @param event an event
1942 * @param data pointer to a custom data
1943 * @return LV_RES_OK: `obj` was not deleted in the event; LV_RES_INV: `obj` was deleted in the event
1944 */
lv_event_send_func(lv_event_cb_t event_xcb,lv_obj_t * obj,lv_event_t event,const void * data)1945 lv_res_t lv_event_send_func(lv_event_cb_t event_xcb, lv_obj_t * obj, lv_event_t event, const void * data)
1946 {
1947 if(obj != NULL) {
1948 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
1949 }
1950
1951 /* Build a simple linked list from the objects used in the events
1952 * It's important to know if an this object was deleted by a nested event
1953 * called from this `even_cb`. */
1954 lv_event_temp_data_t event_temp_data;
1955 event_temp_data.obj = obj;
1956 event_temp_data.deleted = false;
1957 event_temp_data.prev = NULL;
1958
1959 if(event_temp_data_head) {
1960 event_temp_data.prev = event_temp_data_head;
1961 }
1962 event_temp_data_head = &event_temp_data;
1963
1964 const void * event_act_data_save = event_act_data;
1965 event_act_data = data;
1966
1967 /*Call the input device's feedback callback if set*/
1968 lv_indev_t * indev_act = lv_indev_get_act();
1969 if(indev_act) {
1970 if(indev_act->driver.feedback_cb) indev_act->driver.feedback_cb(&indev_act->driver, event);
1971 }
1972
1973 /*Call the event callback itself*/
1974 if(event_xcb) event_xcb(obj, event);
1975
1976 /*Restore the event data*/
1977 event_act_data = event_act_data_save;
1978
1979 /*Remove this element from the list*/
1980 event_temp_data_head = event_temp_data_head->prev;
1981
1982 if(event_temp_data.deleted) {
1983 return LV_RES_INV;
1984 }
1985
1986 if(obj) {
1987 if(obj->parent_event && obj->parent) {
1988 lv_res_t res = lv_event_send(obj->parent, event, data);
1989 if(res != LV_RES_OK) {
1990 return LV_RES_INV;
1991 }
1992 }
1993 }
1994
1995 return LV_RES_OK;
1996 }
1997
1998 /**
1999 * Get the `data` parameter of the current event
2000 * @return the `data` parameter
2001 */
lv_event_get_data(void)2002 const void * lv_event_get_data(void)
2003 {
2004 return event_act_data;
2005 }
2006
2007 /**
2008 * Set the a signal function of an object. Used internally by the library.
2009 * Always call the previous signal function in the new.
2010 * @param obj pointer to an object
2011 * @param cb the new signal function
2012 */
lv_obj_set_signal_cb(lv_obj_t * obj,lv_signal_cb_t signal_cb)2013 void lv_obj_set_signal_cb(lv_obj_t * obj, lv_signal_cb_t signal_cb)
2014 {
2015 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2016
2017 obj->signal_cb = signal_cb;
2018 }
2019
2020 /**
2021 * Send an event to the object
2022 * @param obj pointer to an object
2023 * @param event the type of the event from `lv_event_t`.
2024 * @return LV_RES_OK or LV_RES_INV
2025 */
lv_signal_send(lv_obj_t * obj,lv_signal_t signal,void * param)2026 lv_res_t lv_signal_send(lv_obj_t * obj, lv_signal_t signal, void * param)
2027 {
2028 if(obj == NULL) return LV_RES_OK;
2029
2030 lv_res_t res = LV_RES_OK;
2031 if(obj->signal_cb) res = obj->signal_cb(obj, signal, param);
2032
2033 return res;
2034 }
2035
2036 /**
2037 * Set a new design function for an object
2038 * @param obj pointer to an object
2039 * @param design_cb the new design function
2040 */
lv_obj_set_design_cb(lv_obj_t * obj,lv_design_cb_t design_cb)2041 void lv_obj_set_design_cb(lv_obj_t * obj, lv_design_cb_t design_cb)
2042 {
2043 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2044
2045 obj->design_cb = design_cb;
2046 }
2047
2048 /*----------------
2049 * Other set
2050 *--------------*/
2051
2052 /**
2053 * Allocate a new ext. data for an object
2054 * @param obj pointer to an object
2055 * @param ext_size the size of the new ext. data
2056 * @return pointer to the allocated ext.
2057 * If out of memory NULL is returned and the original ext is preserved
2058 */
lv_obj_allocate_ext_attr(lv_obj_t * obj,uint16_t ext_size)2059 void * lv_obj_allocate_ext_attr(lv_obj_t * obj, uint16_t ext_size)
2060 {
2061 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2062
2063 void * new_ext = lv_mem_realloc(obj->ext_attr, ext_size);
2064 if(new_ext == NULL) return NULL;
2065
2066 obj->ext_attr = new_ext;
2067 return (void *)obj->ext_attr;
2068 }
2069
2070 /**
2071 * Send a 'LV_SIGNAL_REFR_EXT_SIZE' signal to the object to refresh the extended draw area.
2072 * he object needs to be invalidated by `lv_obj_invalidate(obj)` manually after this function.
2073 * @param obj pointer to an object
2074 */
lv_obj_refresh_ext_draw_pad(lv_obj_t * obj)2075 void lv_obj_refresh_ext_draw_pad(lv_obj_t * obj)
2076 {
2077 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2078
2079 obj->ext_draw_pad = 0;
2080 obj->signal_cb(obj, LV_SIGNAL_REFR_EXT_DRAW_PAD, NULL);
2081
2082 }
2083
2084 /*=======================
2085 * Getter functions
2086 *======================*/
2087
2088 /**
2089 * Return with the screen of an object
2090 * @param obj pointer to an object
2091 * @return pointer to a screen
2092 */
lv_obj_get_screen(const lv_obj_t * obj)2093 lv_obj_t * lv_obj_get_screen(const lv_obj_t * obj)
2094 {
2095 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2096
2097 const lv_obj_t * par = obj;
2098 const lv_obj_t * act_p;
2099
2100 do {
2101 act_p = par;
2102 par = lv_obj_get_parent(act_p);
2103 } while(par != NULL);
2104
2105 return (lv_obj_t *)act_p;
2106 }
2107
2108 /**
2109 * Get the display of an object
2110 * @param scr pointer to an object
2111 * @return pointer the object's display
2112 */
lv_obj_get_disp(const lv_obj_t * obj)2113 lv_disp_t * lv_obj_get_disp(const lv_obj_t * obj)
2114 {
2115 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2116
2117 const lv_obj_t * scr;
2118
2119 if(obj->parent == NULL)
2120 scr = obj; /*`obj` is a screen*/
2121 else
2122 scr = lv_obj_get_screen(obj); /*get the screen of `obj`*/
2123
2124 lv_disp_t * d;
2125 _LV_LL_READ(LV_GC_ROOT(_lv_disp_ll), d) {
2126 lv_obj_t * s;
2127 _LV_LL_READ(d->scr_ll, s) {
2128 if(s == scr) return d;
2129 }
2130 }
2131
2132 LV_LOG_WARN("lv_scr_get_disp: screen not found")
2133 return NULL;
2134 }
2135
2136 /*---------------------
2137 * Parent/children get
2138 *--------------------*/
2139
2140 /**
2141 * Returns with the parent of an object
2142 * @param obj pointer to an object
2143 * @return pointer to the parent of 'obj'
2144 */
lv_obj_get_parent(const lv_obj_t * obj)2145 lv_obj_t * lv_obj_get_parent(const lv_obj_t * obj)
2146 {
2147 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2148
2149 return obj->parent;
2150 }
2151
2152 /**
2153 * Iterate through the children of an object (start from the "youngest")
2154 * @param obj pointer to an object
2155 * @param child NULL at first call to get the next children
2156 * and the previous return value later
2157 * @return the child after 'act_child' or NULL if no more child
2158 */
lv_obj_get_child(const lv_obj_t * obj,const lv_obj_t * child)2159 lv_obj_t * lv_obj_get_child(const lv_obj_t * obj, const lv_obj_t * child)
2160 {
2161 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2162
2163 lv_obj_t * result = NULL;
2164
2165 if(child == NULL) {
2166 result = _lv_ll_get_head(&obj->child_ll);
2167 }
2168 else {
2169 result = _lv_ll_get_next(&obj->child_ll, child);
2170 }
2171
2172 return result;
2173 }
2174
2175 /**
2176 * Iterate through the children of an object (start from the "oldest")
2177 * @param obj pointer to an object
2178 * @param child NULL at first call to get the next children
2179 * and the previous return value later
2180 * @return the child after 'act_child' or NULL if no more child
2181 */
lv_obj_get_child_back(const lv_obj_t * obj,const lv_obj_t * child)2182 lv_obj_t * lv_obj_get_child_back(const lv_obj_t * obj, const lv_obj_t * child)
2183 {
2184 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2185
2186 lv_obj_t * result = NULL;
2187
2188 if(child == NULL) {
2189 result = _lv_ll_get_tail(&obj->child_ll);
2190 }
2191 else {
2192 result = _lv_ll_get_prev(&obj->child_ll, child);
2193 }
2194
2195 return result;
2196 }
2197
2198 /**
2199 * Count the children of an object (only children directly on 'obj')
2200 * @param obj pointer to an object
2201 * @return children number of 'obj'
2202 */
lv_obj_count_children(const lv_obj_t * obj)2203 uint16_t lv_obj_count_children(const lv_obj_t * obj)
2204 {
2205 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2206
2207 lv_obj_t * i;
2208 uint16_t cnt = 0;
2209
2210 _LV_LL_READ(obj->child_ll, i) cnt++;
2211
2212 return cnt;
2213 }
2214
2215 /** Recursively count the children of an object
2216 * @param obj pointer to an object
2217 * @return children number of 'obj'
2218 */
lv_obj_count_children_recursive(const lv_obj_t * obj)2219 uint16_t lv_obj_count_children_recursive(const lv_obj_t * obj)
2220 {
2221 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2222
2223 lv_obj_t * i;
2224 uint16_t cnt = 0;
2225
2226 _LV_LL_READ(obj->child_ll, i) {
2227 cnt++; /*Count the child*/
2228 cnt += lv_obj_count_children_recursive(i); /*recursively count children's children*/
2229 }
2230
2231 return cnt;
2232 }
2233
2234 /*---------------------
2235 * Coordinate get
2236 *--------------------*/
2237
2238 /**
2239 * Copy the coordinates of an object to an area
2240 * @param obj pointer to an object
2241 * @param cords_p pointer to an area to store the coordinates
2242 */
lv_obj_get_coords(const lv_obj_t * obj,lv_area_t * cords_p)2243 void lv_obj_get_coords(const lv_obj_t * obj, lv_area_t * cords_p)
2244 {
2245 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2246
2247 lv_area_copy(cords_p, &obj->coords);
2248 }
2249
2250 /**
2251 * Reduce area retried by `lv_obj_get_coords()` the get graphically usable area of an object.
2252 * (Without the size of the border or other extra graphical elements)
2253 * @param coords_p store the result area here
2254 */
lv_obj_get_inner_coords(const lv_obj_t * obj,lv_area_t * coords_p)2255 void lv_obj_get_inner_coords(const lv_obj_t * obj, lv_area_t * coords_p)
2256 {
2257 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2258
2259 lv_border_side_t part = lv_obj_get_style_border_side(obj, LV_OBJ_PART_MAIN);
2260 lv_coord_t w = lv_obj_get_style_border_width(obj, LV_OBJ_PART_MAIN);
2261
2262 if(part & LV_BORDER_SIDE_LEFT) coords_p->x1 += w;
2263
2264 if(part & LV_BORDER_SIDE_RIGHT) coords_p->x2 -= w;
2265
2266 if(part & LV_BORDER_SIDE_TOP) coords_p->y1 += w;
2267
2268 if(part & LV_BORDER_SIDE_BOTTOM) coords_p->y2 -= w;
2269 }
2270
2271 /**
2272 * Get the x coordinate of object
2273 * @param obj pointer to an object
2274 * @return distance of 'obj' from the left side of its parent
2275 */
lv_obj_get_x(const lv_obj_t * obj)2276 lv_coord_t lv_obj_get_x(const lv_obj_t * obj)
2277 {
2278 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2279
2280 lv_coord_t rel_x;
2281 lv_obj_t * parent = lv_obj_get_parent(obj);
2282 if(parent) {
2283 rel_x = obj->coords.x1 - parent->coords.x1;
2284 }
2285 else {
2286 rel_x = obj->coords.x1;
2287 }
2288 return rel_x;
2289 }
2290
2291 /**
2292 * Get the y coordinate of object
2293 * @param obj pointer to an object
2294 * @return distance of 'obj' from the top of its parent
2295 */
lv_obj_get_y(const lv_obj_t * obj)2296 lv_coord_t lv_obj_get_y(const lv_obj_t * obj)
2297 {
2298 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2299
2300 lv_coord_t rel_y;
2301 lv_obj_t * parent = lv_obj_get_parent(obj);
2302 if(parent) {
2303 rel_y = obj->coords.y1 - parent->coords.y1;
2304 }
2305 else {
2306 rel_y = obj->coords.y1;
2307 }
2308 return rel_y;
2309 }
2310
2311 /**
2312 * Get the width of an object
2313 * @param obj pointer to an object
2314 * @return the width
2315 */
lv_obj_get_width(const lv_obj_t * obj)2316 lv_coord_t lv_obj_get_width(const lv_obj_t * obj)
2317 {
2318 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2319
2320 return lv_area_get_width(&obj->coords);
2321 }
2322
2323 /**
2324 * Get the height of an object
2325 * @param obj pointer to an object
2326 * @return the height
2327 */
lv_obj_get_height(const lv_obj_t * obj)2328 lv_coord_t lv_obj_get_height(const lv_obj_t * obj)
2329 {
2330 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2331
2332 return lv_area_get_height(&obj->coords);
2333 }
2334
2335 /**
2336 * Get that width reduced by the left and right padding.
2337 * @param obj pointer to an object
2338 * @return the width which still fits into the container
2339 */
lv_obj_get_width_fit(const lv_obj_t * obj)2340 lv_coord_t lv_obj_get_width_fit(const lv_obj_t * obj)
2341 {
2342 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2343
2344 lv_style_int_t left = lv_obj_get_style_pad_left(obj, LV_OBJ_PART_MAIN);
2345 lv_style_int_t right = lv_obj_get_style_pad_right(obj, LV_OBJ_PART_MAIN);
2346
2347 return lv_obj_get_width(obj) - left - right;
2348 }
2349
2350 /**
2351 * Get that height reduced by the top an bottom padding.
2352 * @param obj pointer to an object
2353 * @return the height which still fits into the container
2354 */
lv_obj_get_height_fit(const lv_obj_t * obj)2355 lv_coord_t lv_obj_get_height_fit(const lv_obj_t * obj)
2356 {
2357 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2358
2359 lv_style_int_t top = lv_obj_get_style_pad_top((lv_obj_t *)obj, LV_OBJ_PART_MAIN);
2360 lv_style_int_t bottom = lv_obj_get_style_pad_bottom((lv_obj_t *)obj, LV_OBJ_PART_MAIN);
2361
2362 return lv_obj_get_height(obj) - top - bottom;
2363 }
2364
2365 /**
2366 * Get the height of an object by taking the top and bottom margin into account.
2367 * The returned height will be `obj_h + margin_top + margin_bottom`
2368 * @param obj pointer to an object
2369 * @return the height including thee margins
2370 */
lv_obj_get_height_margin(lv_obj_t * obj)2371 lv_coord_t lv_obj_get_height_margin(lv_obj_t * obj)
2372 {
2373 lv_style_int_t mtop = lv_obj_get_style_margin_top(obj, LV_OBJ_PART_MAIN);
2374 lv_style_int_t mbottom = lv_obj_get_style_margin_bottom(obj, LV_OBJ_PART_MAIN);
2375
2376 return lv_obj_get_height(obj) + mtop + mbottom;
2377 }
2378
2379 /**
2380 * Get the width of an object by taking the left and right margin into account.
2381 * The returned width will be `obj_w + margin_left + margin_right`
2382 * @param obj pointer to an object
2383 * @return the height including thee margins
2384 */
lv_obj_get_width_margin(lv_obj_t * obj)2385 lv_coord_t lv_obj_get_width_margin(lv_obj_t * obj)
2386 {
2387 lv_style_int_t mleft = lv_obj_get_style_margin_left(obj, LV_OBJ_PART_MAIN);
2388 lv_style_int_t mright = lv_obj_get_style_margin_right(obj, LV_OBJ_PART_MAIN);
2389
2390 return lv_obj_get_width(obj) + mleft + mright;
2391 }
2392
2393 /**
2394 * Set that width reduced by the left and right padding of the parent.
2395 * @param obj pointer to an object
2396 * @param div indicates how many columns are assumed.
2397 * If 1 the width will be set the the parent's width
2398 * If 2 only half parent width - inner padding of the parent
2399 * If 3 only third parent width - 2 * inner padding of the parent
2400 * @param span how many columns are combined
2401 * @return the width according to the given parameters
2402 */
lv_obj_get_width_grid(lv_obj_t * obj,uint8_t div,uint8_t span)2403 lv_coord_t lv_obj_get_width_grid(lv_obj_t * obj, uint8_t div, uint8_t span)
2404 {
2405 lv_coord_t obj_w = lv_obj_get_width_fit(obj);
2406 lv_style_int_t pinner = lv_obj_get_style_pad_inner(obj, LV_OBJ_PART_MAIN);
2407
2408 lv_coord_t r = (obj_w - (div - 1) * pinner) / div;
2409
2410 r = r * span + (span - 1) * pinner;
2411 return r;
2412 }
2413
2414 /**
2415 * Get that height reduced by the top and bottom padding of the parent.
2416 * @param obj pointer to an object
2417 * @param div indicates how many rows are assumed.
2418 * If 1 the height will be set the the parent's height
2419 * If 2 only half parent height - inner padding of the parent
2420 * If 3 only third parent height - 2 * inner padding of the parent
2421 * @param span how many rows are combined
2422 * @return the height according to the given parameters
2423 */
lv_obj_get_height_grid(lv_obj_t * obj,uint8_t div,uint8_t span)2424 lv_coord_t lv_obj_get_height_grid(lv_obj_t * obj, uint8_t div, uint8_t span)
2425 {
2426 lv_coord_t obj_h = lv_obj_get_height_fit(obj);
2427 lv_style_int_t pinner = lv_obj_get_style_pad_inner(obj, LV_OBJ_PART_MAIN);
2428
2429 lv_coord_t r = (obj_h - (div - 1) * pinner) / div;
2430
2431 r = r * span + (span - 1) * pinner;
2432 return r;
2433 }
2434
2435 /**
2436 * Get the automatic realign property of the object.
2437 * @param obj pointer to an object
2438 * @return true: auto realign is enabled; false: auto realign is disabled
2439 */
lv_obj_get_auto_realign(const lv_obj_t * obj)2440 bool lv_obj_get_auto_realign(const lv_obj_t * obj)
2441 {
2442 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2443
2444 #if LV_USE_OBJ_REALIGN
2445 return obj->realign.auto_realign ? true : false;
2446 #else
2447 (void)obj;
2448 return false;
2449 #endif
2450 }
2451
2452 /**
2453 * Get the left padding of extended clickable area
2454 * @param obj pointer to an object
2455 * @return the extended left padding
2456 */
lv_obj_get_ext_click_pad_left(const lv_obj_t * obj)2457 lv_coord_t lv_obj_get_ext_click_pad_left(const lv_obj_t * obj)
2458 {
2459 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2460
2461 #if LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_TINY
2462 return obj->ext_click_pad_hor;
2463 #elif LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_FULL
2464 return obj->ext_click_pad.x1;
2465 #else
2466 (void)obj; /*Unused*/
2467 return 0;
2468 #endif
2469 }
2470
2471 /**
2472 * Get the right padding of extended clickable area
2473 * @param obj pointer to an object
2474 * @return the extended right padding
2475 */
lv_obj_get_ext_click_pad_right(const lv_obj_t * obj)2476 lv_coord_t lv_obj_get_ext_click_pad_right(const lv_obj_t * obj)
2477 {
2478 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2479
2480 #if LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_TINY
2481 return obj->ext_click_pad_hor;
2482 #elif LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_FULL
2483 return obj->ext_click_pad.x2;
2484 #else
2485 (void)obj; /*Unused*/
2486 return 0;
2487 #endif
2488 }
2489
2490 /**
2491 * Get the top padding of extended clickable area
2492 * @param obj pointer to an object
2493 * @return the extended top padding
2494 */
lv_obj_get_ext_click_pad_top(const lv_obj_t * obj)2495 lv_coord_t lv_obj_get_ext_click_pad_top(const lv_obj_t * obj)
2496 {
2497 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2498
2499 #if LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_TINY
2500 return obj->ext_click_pad_ver;
2501 #elif LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_FULL
2502 return obj->ext_click_pad.y1;
2503 #else
2504 (void)obj; /*Unused*/
2505 return 0;
2506 #endif
2507 }
2508
2509 /**
2510 * Get the bottom padding of extended clickable area
2511 * @param obj pointer to an object
2512 * @return the extended bottom padding
2513 */
lv_obj_get_ext_click_pad_bottom(const lv_obj_t * obj)2514 lv_coord_t lv_obj_get_ext_click_pad_bottom(const lv_obj_t * obj)
2515 {
2516 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2517
2518 #if LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_TINY
2519 return obj->ext_click_pad_ver;
2520 #elif LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_FULL
2521 return obj->ext_click_pad.y2;
2522 #else
2523 (void)obj; /*Unused*/
2524 return 0;
2525 #endif
2526 }
2527
2528 /**
2529 * Get the extended size attribute of an object
2530 * @param obj pointer to an object
2531 * @return the extended size attribute
2532 */
lv_obj_get_ext_draw_pad(const lv_obj_t * obj)2533 lv_coord_t lv_obj_get_ext_draw_pad(const lv_obj_t * obj)
2534 {
2535 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2536
2537 return obj->ext_draw_pad;
2538 }
2539
2540 /*-----------------
2541 * Appearance get
2542 *---------------*/
2543
lv_obj_get_style_list(const lv_obj_t * obj,uint8_t part)2544 lv_style_list_t * lv_obj_get_style_list(const lv_obj_t * obj, uint8_t part)
2545 {
2546 if(part == LV_OBJ_PART_MAIN) return &((lv_obj_t *)obj)->style_list;
2547
2548 lv_get_style_info_t info;
2549 info.part = part;
2550 info.result = NULL;
2551
2552 lv_res_t res;
2553 res = lv_signal_send((lv_obj_t *)obj, LV_SIGNAL_GET_STYLE, &info);
2554
2555 if(res != LV_RES_OK) return NULL;
2556
2557 return info.result;
2558 }
2559
2560 /**
2561 * Get a style property of a part of an object in the object's current state.
2562 * If there is a running transitions it is taken into account
2563 * @param obj pointer to an object
2564 * @param part the part of the object which style property should be get.
2565 * E.g. `LV_OBJ_PART_MAIN`, `LV_BTN_PART_MAIN`, `LV_SLIDER_PART_KNOB`
2566 * @param prop the property to get. E.g. `LV_STYLE_BORDER_WIDTH`.
2567 * The state of the object will be added internally
2568 * @return the value of the property of the given part in the current state.
2569 * If the property is not found a default value will be returned.
2570 * @note shouldn't be used directly. Use the specific property get functions instead.
2571 * For example: `lv_obj_style_get_border_width()`
2572 * @note for performance reasons it's not checked if the property really has integer type
2573 */
_lv_obj_get_style_int(const lv_obj_t * obj,uint8_t part,lv_style_property_t prop)2574 lv_style_int_t _lv_obj_get_style_int(const lv_obj_t * obj, uint8_t part, lv_style_property_t prop)
2575 {
2576 lv_style_property_t prop_ori = prop;
2577
2578 lv_style_attr_t attr;
2579 attr = prop_ori >> 8;
2580
2581 lv_style_int_t value_act;
2582 lv_res_t res = LV_RES_INV;
2583 const lv_obj_t * parent = obj;
2584 while(parent) {
2585 lv_style_list_t * list = lv_obj_get_style_list(parent, part);
2586 if(!list->ignore_cache && list->style_cnt > 0) {
2587 if(!list->valid_cache) update_style_cache((lv_obj_t *)parent, part, prop & (~LV_STYLE_STATE_MASK));
2588
2589 bool def = false;
2590 switch(prop & (~LV_STYLE_STATE_MASK)) {
2591 case LV_STYLE_CLIP_CORNER:
2592 if(list->clip_corner_off) def = true;
2593 break;
2594 case LV_STYLE_TEXT_LETTER_SPACE:
2595 case LV_STYLE_TEXT_LINE_SPACE:
2596 if(list->text_space_zero) def = true;
2597 break;
2598 case LV_STYLE_TRANSFORM_ANGLE:
2599 case LV_STYLE_TRANSFORM_WIDTH:
2600 case LV_STYLE_TRANSFORM_HEIGHT:
2601 case LV_STYLE_TRANSFORM_ZOOM:
2602 if(list->transform_all_zero) def = true;
2603 break;
2604 case LV_STYLE_BORDER_WIDTH:
2605 if(list->border_width_zero) def = true;
2606 break;
2607 case LV_STYLE_BORDER_SIDE:
2608 if(list->border_side_full) def = true;
2609 break;
2610 case LV_STYLE_BORDER_POST:
2611 if(list->border_post_off) def = true;
2612 break;
2613 case LV_STYLE_OUTLINE_WIDTH:
2614 if(list->outline_width_zero) def = true;
2615 break;
2616 case LV_STYLE_RADIUS:
2617 if(list->radius_zero) def = true;
2618 break;
2619 case LV_STYLE_SHADOW_WIDTH:
2620 if(list->shadow_width_zero) def = true;
2621 break;
2622 case LV_STYLE_PAD_TOP:
2623 case LV_STYLE_PAD_BOTTOM:
2624 case LV_STYLE_PAD_LEFT:
2625 case LV_STYLE_PAD_RIGHT:
2626 if(list->pad_all_zero) def = true;
2627 break;
2628 case LV_STYLE_MARGIN_TOP:
2629 case LV_STYLE_MARGIN_BOTTOM:
2630 case LV_STYLE_MARGIN_LEFT:
2631 case LV_STYLE_MARGIN_RIGHT:
2632 if(list->margin_all_zero) def = true;
2633 break;
2634 case LV_STYLE_BG_BLEND_MODE:
2635 case LV_STYLE_BORDER_BLEND_MODE:
2636 case LV_STYLE_IMAGE_BLEND_MODE:
2637 case LV_STYLE_LINE_BLEND_MODE:
2638 case LV_STYLE_OUTLINE_BLEND_MODE:
2639 case LV_STYLE_PATTERN_BLEND_MODE:
2640 case LV_STYLE_SHADOW_BLEND_MODE:
2641 case LV_STYLE_TEXT_BLEND_MODE:
2642 case LV_STYLE_VALUE_BLEND_MODE:
2643 if(list->blend_mode_all_normal) def = true;
2644 break;
2645 case LV_STYLE_TEXT_DECOR:
2646 if(list->text_decor_none) def = true;
2647 break;
2648 }
2649
2650 if(def) {
2651 break;
2652 }
2653 }
2654
2655 lv_state_t state = lv_obj_get_state(parent, part);
2656 prop = (uint16_t)prop_ori + ((uint16_t)state << LV_STYLE_STATE_POS);
2657
2658 res = _lv_style_list_get_int(list, prop, &value_act);
2659 if(res == LV_RES_OK) return value_act;
2660
2661 if(LV_STYLE_ATTR_GET_INHERIT(attr) == 0) break;
2662
2663 /*If not found, check the `MAIN` style first*/
2664 if(part != LV_OBJ_PART_MAIN) {
2665 part = LV_OBJ_PART_MAIN;
2666 continue;
2667 }
2668
2669 /*Check the parent too.*/
2670 parent = lv_obj_get_parent(parent);
2671 }
2672
2673 /*Handle unset values*/
2674 prop = prop & (~LV_STYLE_STATE_MASK);
2675 switch(prop) {
2676 case LV_STYLE_BORDER_SIDE:
2677 return LV_BORDER_SIDE_FULL;
2678 case LV_STYLE_SIZE:
2679 return LV_DPI / 20;
2680 case LV_STYLE_SCALE_WIDTH:
2681 return LV_DPI / 8;
2682 case LV_STYLE_BG_GRAD_STOP:
2683 return 255;
2684 case LV_STYLE_TRANSFORM_ZOOM:
2685 return LV_IMG_ZOOM_NONE;
2686 }
2687
2688 return 0;
2689 }
2690
2691 /**
2692 * Get a style property of a part of an object in the object's current state.
2693 * If there is a running transitions it is taken into account
2694 * @param obj pointer to an object
2695 * @param part the part of the object which style property should be get.
2696 * E.g. `LV_OBJ_PART_MAIN`, `LV_BTN_PART_MAIN`, `LV_SLIDER_PART_KNOB`
2697 * @param prop the property to get. E.g. `LV_STYLE_BORDER_COLOR`.
2698 * The state of the object will be added internally
2699 * @return the value of the property of the given part in the current state.
2700 * If the property is not found a default value will be returned.
2701 * @note shouldn't be used directly. Use the specific property get functions instead.
2702 * For example: `lv_obj_style_get_border_color()`
2703 * @note for performance reasons it's not checked if the property really has color type
2704 */
_lv_obj_get_style_color(const lv_obj_t * obj,uint8_t part,lv_style_property_t prop)2705 lv_color_t _lv_obj_get_style_color(const lv_obj_t * obj, uint8_t part, lv_style_property_t prop)
2706 {
2707 lv_style_property_t prop_ori = prop;
2708
2709 lv_style_attr_t attr;
2710 attr = prop_ori >> 8;
2711
2712 lv_color_t value_act;
2713 lv_res_t res = LV_RES_INV;
2714 const lv_obj_t * parent = obj;
2715 while(parent) {
2716 lv_style_list_t * list = lv_obj_get_style_list(parent, part);
2717
2718 lv_state_t state = lv_obj_get_state(parent, part);
2719 prop = (uint16_t)prop_ori + ((uint16_t)state << LV_STYLE_STATE_POS);
2720
2721 res = _lv_style_list_get_color(list, prop, &value_act);
2722 if(res == LV_RES_OK) return value_act;
2723
2724 if(LV_STYLE_ATTR_GET_INHERIT(attr) == 0) break;
2725
2726 /*If not found, check the `MAIN` style first*/
2727 if(part != LV_OBJ_PART_MAIN) {
2728 part = LV_OBJ_PART_MAIN;
2729 continue;
2730 }
2731
2732 /*Check the parent too.*/
2733 parent = lv_obj_get_parent(parent);
2734 }
2735
2736 /*Handle unset values*/
2737 prop = prop & (~LV_STYLE_STATE_MASK);
2738 switch(prop) {
2739 case LV_STYLE_BG_COLOR:
2740 case LV_STYLE_BG_GRAD_COLOR:
2741 return LV_COLOR_WHITE;
2742 }
2743
2744 return LV_COLOR_BLACK;
2745 }
2746
2747 /**
2748 * Get a style property of a part of an object in the object's current state.
2749 * If there is a running transitions it is taken into account
2750 * @param obj pointer to an object
2751 * @param part the part of the object which style property should be get.
2752 * E.g. `LV_OBJ_PART_MAIN`, `LV_BTN_PART_MAIN`, `LV_SLIDER_PART_KNOB`
2753 * @param prop the property to get. E.g. `LV_STYLE_BORDER_OPA`.
2754 * The state of the object will be added internally
2755 * @return the value of the property of the given part in the current state.
2756 * If the property is not found a default value will be returned.
2757 * @note shouldn't be used directly. Use the specific property get functions instead.
2758 * For example: `lv_obj_style_get_border_opa()`
2759 * @note for performance reasons it's not checked if the property really has opacity type
2760 */
_lv_obj_get_style_opa(const lv_obj_t * obj,uint8_t part,lv_style_property_t prop)2761 lv_opa_t _lv_obj_get_style_opa(const lv_obj_t * obj, uint8_t part, lv_style_property_t prop)
2762 {
2763 lv_style_property_t prop_ori = prop;
2764
2765 lv_style_attr_t attr;
2766 attr = prop_ori >> 8;
2767
2768 lv_opa_t value_act;
2769 lv_res_t res = LV_RES_INV;
2770 const lv_obj_t * parent = obj;
2771 while(parent) {
2772 lv_style_list_t * list = lv_obj_get_style_list(parent, part);
2773
2774 if(!list->ignore_cache && list->style_cnt > 0) {
2775 if(!list->valid_cache) update_style_cache((lv_obj_t *)parent, part, prop & (~LV_STYLE_STATE_MASK));
2776 bool def = false;
2777 switch(prop & (~LV_STYLE_STATE_MASK)) {
2778 case LV_STYLE_OPA_SCALE:
2779 if(list->opa_scale_cover) def = true;
2780 break;
2781 case LV_STYLE_BG_OPA:
2782 if(list->bg_opa_cover) return LV_OPA_COVER; /*Special case, not the default value is used*/
2783 if(list->bg_opa_transp) def = true;
2784 break;
2785 case LV_STYLE_IMAGE_RECOLOR_OPA:
2786 if(list->img_recolor_opa_transp) def = true;
2787 break;
2788 }
2789
2790 if(def) {
2791 break;
2792 }
2793 }
2794
2795
2796 lv_state_t state = lv_obj_get_state(parent, part);
2797 prop = (uint16_t)prop_ori + ((uint16_t)state << LV_STYLE_STATE_POS);
2798
2799 res = _lv_style_list_get_opa(list, prop, &value_act);
2800 if(res == LV_RES_OK) return value_act;
2801
2802 if(LV_STYLE_ATTR_GET_INHERIT(attr) == 0) break;
2803
2804 /*If not found, check the `MAIN` style first*/
2805 if(part != LV_OBJ_PART_MAIN) {
2806 part = LV_OBJ_PART_MAIN;
2807 continue;
2808 }
2809
2810 /*Check the parent too.*/
2811 parent = lv_obj_get_parent(parent);
2812 }
2813
2814 /*Handle unset values*/
2815 prop = prop & (~LV_STYLE_STATE_MASK);
2816 switch(prop) {
2817 case LV_STYLE_BG_OPA:
2818 case LV_STYLE_IMAGE_RECOLOR_OPA:
2819 case LV_STYLE_PATTERN_RECOLOR_OPA:
2820 return LV_OPA_TRANSP;
2821 }
2822
2823 return LV_OPA_COVER;
2824 }
2825
2826 /**
2827 * Get a style property of a part of an object in the object's current state.
2828 * If there is a running transitions it is taken into account
2829 * @param obj pointer to an object
2830 * @param part the part of the object which style property should be get.
2831 * E.g. `LV_OBJ_PART_MAIN`, `LV_BTN_PART_MAIN`, `LV_SLIDER_PART_KNOB`
2832 * @param prop the property to get. E.g. `LV_STYLE_TEXT_FONT`.
2833 * The state of the object will be added internally
2834 * @return the value of the property of the given part in the current state.
2835 * If the property is not found a default value will be returned.
2836 * @note shouldn't be used directly. Use the specific property get functions instead.
2837 * For example: `lv_obj_style_get_border_opa()`
2838 * @note for performance reasons it's not checked if the property really has pointer type
2839 */
_lv_obj_get_style_ptr(const lv_obj_t * obj,uint8_t part,lv_style_property_t prop)2840 const void * _lv_obj_get_style_ptr(const lv_obj_t * obj, uint8_t part, lv_style_property_t prop)
2841 {
2842 lv_style_property_t prop_ori = prop;
2843
2844 lv_style_attr_t attr;
2845 attr = prop_ori >> 8;
2846
2847 const void * value_act;
2848 lv_res_t res = LV_RES_INV;
2849 const lv_obj_t * parent = obj;
2850 while(parent) {
2851 lv_style_list_t * list = lv_obj_get_style_list(parent, part);
2852
2853 if(!list->ignore_cache && list->style_cnt > 0) {
2854 if(!list->valid_cache) update_style_cache((lv_obj_t *)parent, part, prop & (~LV_STYLE_STATE_MASK));
2855 bool def = false;
2856 switch(prop & (~LV_STYLE_STATE_MASK)) {
2857 case LV_STYLE_VALUE_STR:
2858 if(list->value_txt_str) def = true;
2859 break;
2860 case LV_STYLE_PATTERN_IMAGE:
2861 if(list->pattern_img_null) def = true;
2862 break;
2863 case LV_STYLE_TEXT_FONT:
2864 if(list->text_font_normal) def = true;
2865 break;
2866 }
2867
2868 if(def) {
2869 break;
2870 }
2871 }
2872
2873 lv_state_t state = lv_obj_get_state(parent, part);
2874 prop = (uint16_t)prop_ori + ((uint16_t)state << LV_STYLE_STATE_POS);
2875
2876 res = _lv_style_list_get_ptr(list, prop, &value_act);
2877 if(res == LV_RES_OK) return value_act;
2878
2879 if(LV_STYLE_ATTR_GET_INHERIT(attr) == 0) break;
2880
2881 /*If not found, check the `MAIN` style first*/
2882 if(part != LV_OBJ_PART_MAIN) {
2883 part = LV_OBJ_PART_MAIN;
2884 continue;
2885 }
2886
2887 /*Check the parent too.*/
2888 parent = lv_obj_get_parent(parent);
2889 }
2890
2891 /*Handle unset values*/
2892 prop = prop & (~LV_STYLE_STATE_MASK);
2893 switch(prop) {
2894 case LV_STYLE_TEXT_FONT:
2895 case LV_STYLE_VALUE_FONT:
2896 return lv_theme_get_font_normal();
2897 #if LV_USE_ANIMATION
2898 case LV_STYLE_TRANSITION_PATH:
2899 return &lv_anim_path_def;
2900 #endif
2901 }
2902
2903 return NULL;
2904 }
2905
2906 /**
2907 * Get the local style of a part of an object.
2908 * @param obj pointer to an object
2909 * @param part the part of the object which style property should be set.
2910 * E.g. `LV_OBJ_PART_MAIN`, `LV_BTN_PART_MAIN`, `LV_SLIDER_PART_KNOB`
2911 * @return pointer to the local style if exists else `NULL`.
2912 */
lv_obj_get_local_style(lv_obj_t * obj,uint8_t part)2913 lv_style_t * lv_obj_get_local_style(lv_obj_t * obj, uint8_t part)
2914 {
2915 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2916 lv_style_list_t * style_list = lv_obj_get_style_list(obj, part);
2917 return lv_style_list_get_local_style(style_list);
2918 }
2919
2920 /*-----------------
2921 * Attribute get
2922 *----------------*/
2923
2924 /**
2925 * Get the hidden attribute of an object
2926 * @param obj pointer to an object
2927 * @return true: the object is hidden
2928 */
lv_obj_get_hidden(const lv_obj_t * obj)2929 bool lv_obj_get_hidden(const lv_obj_t * obj)
2930 {
2931 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2932
2933 return obj->hidden == 0 ? false : true;
2934 }
2935
2936 /**
2937 * Get whether advanced hit-testing is enabled on an object
2938 * @param obj pointer to an object
2939 * @return true: advanced hit-testing is enabled
2940 */
lv_obj_get_adv_hittest(const lv_obj_t * obj)2941 bool lv_obj_get_adv_hittest(const lv_obj_t * obj)
2942 {
2943 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2944
2945 return obj->adv_hittest == 0 ? false : true;
2946 }
2947
2948 /**
2949 * Get the click enable attribute of an object
2950 * @param obj pointer to an object
2951 * @return true: the object is clickable
2952 */
lv_obj_get_click(const lv_obj_t * obj)2953 bool lv_obj_get_click(const lv_obj_t * obj)
2954 {
2955 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2956
2957 return obj->click == 0 ? false : true;
2958 }
2959
2960 /**
2961 * Get the top enable attribute of an object
2962 * @param obj pointer to an object
2963 * @return true: the auto top feature is enabled
2964 */
lv_obj_get_top(const lv_obj_t * obj)2965 bool lv_obj_get_top(const lv_obj_t * obj)
2966 {
2967 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2968
2969 return obj->top == 0 ? false : true;
2970 }
2971
2972 /**
2973 * Get the drag enable attribute of an object
2974 * @param obj pointer to an object
2975 * @return true: the object is draggable
2976 */
lv_obj_get_drag(const lv_obj_t * obj)2977 bool lv_obj_get_drag(const lv_obj_t * obj)
2978 {
2979 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2980
2981 return obj->drag == 0 ? false : true;
2982 }
2983
2984 /**
2985 * Get the directions an object can be dragged
2986 * @param obj pointer to an object
2987 * @return bitwise OR of allowed directions an object can be dragged in
2988 */
lv_obj_get_drag_dir(const lv_obj_t * obj)2989 lv_drag_dir_t lv_obj_get_drag_dir(const lv_obj_t * obj)
2990 {
2991 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
2992
2993 return obj->drag_dir;
2994 }
2995
2996 /**
2997 * Get the drag throw enable attribute of an object
2998 * @param obj pointer to an object
2999 * @return true: drag throw is enabled
3000 */
lv_obj_get_drag_throw(const lv_obj_t * obj)3001 bool lv_obj_get_drag_throw(const lv_obj_t * obj)
3002 {
3003 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
3004
3005 return obj->drag_throw == 0 ? false : true;
3006 }
3007
3008 /**
3009 * Get the drag parent attribute of an object
3010 * @param obj pointer to an object
3011 * @return true: drag parent is enabled
3012 */
lv_obj_get_drag_parent(const lv_obj_t * obj)3013 bool lv_obj_get_drag_parent(const lv_obj_t * obj)
3014 {
3015 return obj->drag_parent == 0 ? false : true;
3016 }
3017
3018 /**
3019 * Get the gesture parent attribute of an object
3020 * @param obj pointer to an object
3021 * @return true: gesture parent is enabled
3022 */
lv_obj_get_gesture_parent(const lv_obj_t * obj)3023 bool lv_obj_get_gesture_parent(const lv_obj_t * obj)
3024 {
3025 return obj->gesture_parent == 0 ? false : true;
3026 }
3027
3028 /**
3029 * Get the focus parent attribute of an object
3030 * @param obj pointer to an object
3031 * @return true: focus parent is enabled
3032 */
lv_obj_get_focus_parent(const lv_obj_t * obj)3033 bool lv_obj_get_focus_parent(const lv_obj_t * obj)
3034 {
3035 return obj->focus_parent == 0 ? false : true;
3036 }
3037
3038 /**
3039 * Get the drag parent attribute of an object
3040 * @param obj pointer to an object
3041 * @return true: drag parent is enabled
3042 */
lv_obj_get_parent_event(const lv_obj_t * obj)3043 bool lv_obj_get_parent_event(const lv_obj_t * obj)
3044 {
3045 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
3046
3047 return obj->parent_event == 0 ? false : true;
3048 }
3049
3050
lv_obj_get_base_dir(const lv_obj_t * obj)3051 lv_bidi_dir_t lv_obj_get_base_dir(const lv_obj_t * obj)
3052 {
3053 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
3054
3055 #if LV_USE_BIDI
3056 const lv_obj_t * parent = obj;
3057
3058 while(parent) {
3059 if(parent->base_dir != LV_BIDI_DIR_INHERIT) return parent->base_dir;
3060
3061 parent = lv_obj_get_parent(parent);
3062 }
3063
3064 return LV_BIDI_BASE_DIR_DEF;
3065 #else
3066 (void) obj; /*Unused*/
3067 return LV_BIDI_DIR_LTR;
3068 #endif
3069 }
3070
3071 /**
3072 * Get the protect field of an object
3073 * @param obj pointer to an object
3074 * @return protect field ('OR'ed values of `lv_protect_t`)
3075 */
lv_obj_get_protect(const lv_obj_t * obj)3076 uint8_t lv_obj_get_protect(const lv_obj_t * obj)
3077 {
3078 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
3079
3080 return obj->protect;
3081 }
3082
3083 /**
3084 * Check at least one bit of a given protect bitfield is set
3085 * @param obj pointer to an object
3086 * @param prot protect bits to test ('OR'ed values of `lv_protect_t`)
3087 * @return false: none of the given bits are set, true: at least one bit is set
3088 */
lv_obj_is_protected(const lv_obj_t * obj,uint8_t prot)3089 bool lv_obj_is_protected(const lv_obj_t * obj, uint8_t prot)
3090 {
3091 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
3092
3093 return (obj->protect & prot) == 0 ? false : true;
3094 }
3095
lv_obj_get_state(const lv_obj_t * obj,uint8_t part)3096 lv_state_t lv_obj_get_state(const lv_obj_t * obj, uint8_t part)
3097 {
3098 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
3099
3100 if(part < _LV_OBJ_PART_REAL_LAST) return ((lv_obj_t *)obj)->state;
3101
3102 /*If a real part is asked, then use the object's signal to get its state.
3103 * A real object can be in different state then the main part
3104 * and only the object itself knows who to get it's state. */
3105 lv_get_state_info_t info;
3106 info.part = part;
3107 info.result = LV_STATE_DEFAULT;
3108 lv_signal_send((lv_obj_t *)obj, LV_SIGNAL_GET_STATE_DSC, &info);
3109
3110 return info.result;
3111
3112 }
3113
3114 /**
3115 * Get the signal function of an object
3116 * @param obj pointer to an object
3117 * @return the signal function
3118 */
lv_obj_get_signal_cb(const lv_obj_t * obj)3119 lv_signal_cb_t lv_obj_get_signal_cb(const lv_obj_t * obj)
3120 {
3121 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
3122
3123 return obj->signal_cb;
3124 }
3125
3126 /**
3127 * Get the design function of an object
3128 * @param obj pointer to an object
3129 * @return the design function
3130 */
lv_obj_get_design_cb(const lv_obj_t * obj)3131 lv_design_cb_t lv_obj_get_design_cb(const lv_obj_t * obj)
3132 {
3133 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
3134
3135 return obj->design_cb;
3136 }
3137
3138 /**
3139 * Get the event function of an object
3140 * @param obj pointer to an object
3141 * @return the event function
3142 */
lv_obj_get_event_cb(const lv_obj_t * obj)3143 lv_event_cb_t lv_obj_get_event_cb(const lv_obj_t * obj)
3144 {
3145 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
3146
3147 return obj->event_cb;
3148 }
3149
3150 /*------------------
3151 * Other get
3152 *-----------------*/
3153
3154 /**
3155 * Get the ext pointer
3156 * @param obj pointer to an object
3157 * @return the ext pointer but not the dynamic version
3158 * Use it as ext->data1, and NOT da(ext)->data1
3159 */
lv_obj_get_ext_attr(const lv_obj_t * obj)3160 void * lv_obj_get_ext_attr(const lv_obj_t * obj)
3161 {
3162 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
3163
3164 return obj->ext_attr;
3165 }
3166
3167 /**
3168 * Get object's and its ancestors type. Put their name in `type_buf` starting with the current type.
3169 * E.g. buf.type[0]="lv_btn", buf.type[1]="lv_cont", buf.type[2]="lv_obj"
3170 * @param obj pointer to an object which type should be get
3171 * @param buf pointer to an `lv_obj_type_t` buffer to store the types
3172 */
lv_obj_get_type(const lv_obj_t * obj,lv_obj_type_t * buf)3173 void lv_obj_get_type(const lv_obj_t * obj, lv_obj_type_t * buf)
3174 {
3175 LV_ASSERT_NULL(buf);
3176 LV_ASSERT_NULL(obj);
3177
3178 lv_obj_type_t tmp;
3179
3180 _lv_memset_00(buf, sizeof(lv_obj_type_t));
3181 _lv_memset_00(&tmp, sizeof(lv_obj_type_t));
3182
3183 obj->signal_cb((lv_obj_t *)obj, LV_SIGNAL_GET_TYPE, &tmp);
3184
3185 uint8_t cnt;
3186 for(cnt = 0; cnt < LV_MAX_ANCESTOR_NUM; cnt++) {
3187 if(tmp.type[cnt] == NULL) break;
3188 }
3189
3190 /*Swap the order. The real type comes first*/
3191 uint8_t i;
3192 for(i = 0; i < cnt; i++) {
3193 buf->type[i] = tmp.type[cnt - 1 - i];
3194 }
3195 }
3196
3197 #if LV_USE_USER_DATA
3198
3199 /**
3200 * Get the object's user data
3201 * @param obj pointer to an object
3202 * @return user data
3203 */
lv_obj_get_user_data(const lv_obj_t * obj)3204 lv_obj_user_data_t lv_obj_get_user_data(const lv_obj_t * obj)
3205 {
3206 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
3207
3208 return obj->user_data;
3209 }
3210
3211 /**
3212 * Get a pointer to the object's user data
3213 * @param obj pointer to an object
3214 * @return pointer to the user data
3215 */
lv_obj_get_user_data_ptr(const lv_obj_t * obj)3216 lv_obj_user_data_t * lv_obj_get_user_data_ptr(const lv_obj_t * obj)
3217 {
3218 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
3219
3220 return (lv_obj_user_data_t *)&obj->user_data;
3221 }
3222
3223 /**
3224 * Set the object's user data. The data will be copied.
3225 * @param obj pointer to an object
3226 * @param data user data
3227 */
lv_obj_set_user_data(lv_obj_t * obj,lv_obj_user_data_t data)3228 void lv_obj_set_user_data(lv_obj_t * obj, lv_obj_user_data_t data)
3229 {
3230 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
3231
3232 _lv_memcpy(&obj->user_data, &data, sizeof(lv_obj_user_data_t));
3233 }
3234 #endif
3235
3236 /**
3237 * Get the group of the object
3238 * @param obj pointer to an object
3239 * @return the pointer to group of the object
3240 */
lv_obj_get_group(const lv_obj_t * obj)3241 void * lv_obj_get_group(const lv_obj_t * obj)
3242 {
3243 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
3244
3245 #if LV_USE_GROUP
3246 return obj->group_p;
3247 #else
3248 LV_UNUSED(obj);
3249 return NULL;
3250 #endif
3251 }
3252
3253 /**
3254 * Tell whether the object is the focused object of a group or not.
3255 * @param obj pointer to an object
3256 * @return true: the object is focused, false: the object is not focused or not in a group
3257 */
lv_obj_is_focused(const lv_obj_t * obj)3258 bool lv_obj_is_focused(const lv_obj_t * obj)
3259 {
3260 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
3261
3262 #if LV_USE_GROUP
3263 if(obj->group_p) {
3264 if(lv_group_get_focused(obj->group_p) == obj) return true;
3265 }
3266 return false;
3267 #else
3268 LV_UNUSED(obj);
3269 return false;
3270 #endif
3271 }
3272
3273
3274 /*-------------------
3275 * OTHER FUNCTIONS
3276 *------------------*/
3277
3278 /**
3279 * Check if a given screen-space point is on an object's coordinates.
3280 *
3281 * This method is intended to be used mainly by advanced hit testing algorithms to check
3282 * whether the point is even within the object (as an optimization).
3283 * @param obj object to check
3284 * @param point screen-space point
3285 */
lv_obj_is_point_on_coords(lv_obj_t * obj,const lv_point_t * point)3286 bool lv_obj_is_point_on_coords(lv_obj_t * obj, const lv_point_t * point)
3287 {
3288 #if LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_TINY
3289 lv_area_t ext_area;
3290 ext_area.x1 = obj->coords.x1 - obj->ext_click_pad_hor;
3291 ext_area.x2 = obj->coords.x2 + obj->ext_click_pad_hor;
3292 ext_area.y1 = obj->coords.y1 - obj->ext_click_pad_ver;
3293 ext_area.y2 = obj->coords.y2 + obj->ext_click_pad_ver;
3294
3295 if(!_lv_area_is_point_on(&ext_area, point, 0)) {
3296 #elif LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_FULL
3297 lv_area_t ext_area;
3298 ext_area.x1 = obj->coords.x1 - obj->ext_click_pad.x1;
3299 ext_area.x2 = obj->coords.x2 + obj->ext_click_pad.x2;
3300 ext_area.y1 = obj->coords.y1 - obj->ext_click_pad.y1;
3301 ext_area.y2 = obj->coords.y2 + obj->ext_click_pad.y2;
3302
3303 if(!_lv_area_is_point_on(&ext_area, point, 0)) {
3304 #else
3305 if(!_lv_area_is_point_on(&obj->coords, point, 0)) {
3306 #endif
3307 return false;
3308 }
3309 return true;
3310 }
3311
3312 /**
3313 * Hit-test an object given a particular point in screen space.
3314 * @param obj object to hit-test
3315 * @param point screen-space point
3316 * @return true if the object is considered under the point
3317 */
3318 bool lv_obj_hittest(lv_obj_t * obj, lv_point_t * point)
3319 {
3320 if(obj->adv_hittest) {
3321 lv_hit_test_info_t hit_info;
3322 hit_info.point = point;
3323 hit_info.result = true;
3324 obj->signal_cb(obj, LV_SIGNAL_HIT_TEST, &hit_info);
3325 return hit_info.result;
3326 }
3327 else
3328 return lv_obj_is_point_on_coords(obj, point);
3329 }
3330
3331 /**
3332 * Used in the signal callback to handle `LV_SIGNAL_GET_TYPE` signal
3333 * @param obj pointer to an object
3334 * @param buf pointer to `lv_obj_type_t`. (`param` in the signal callback)
3335 * @param name name of the object. E.g. "lv_btn". (Only the pointer is saved)
3336 * @return LV_RES_OK
3337 */
3338 lv_res_t lv_obj_handle_get_type_signal(lv_obj_type_t * buf, const char * name)
3339 {
3340 uint8_t i;
3341 for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
3342 if(buf->type[i] == NULL) break;
3343 }
3344 buf->type[i] = name;
3345
3346 return LV_RES_OK;
3347 }
3348
3349 /**
3350 * Initialize a rectangle descriptor from an object's styles
3351 * @param obj pointer to an object
3352 * @param type type of style. E.g. `LV_OBJ_PART_MAIN`, `LV_BTN_STYLE_REL` or `LV_PAGE_STYLE_SCRL`
3353 * @param draw_dsc the descriptor the initialize
3354 * @note Only the relevant fields will be set.
3355 * E.g. if `border width == 0` the other border properties won't be evaluated.
3356 */
3357 void lv_obj_init_draw_rect_dsc(lv_obj_t * obj, uint8_t part, lv_draw_rect_dsc_t * draw_dsc)
3358 {
3359 draw_dsc->radius = lv_obj_get_style_radius(obj, part);
3360
3361 #if LV_USE_OPA_SCALE
3362 lv_opa_t opa_scale = lv_obj_get_style_opa_scale(obj, part);
3363 if(opa_scale <= LV_OPA_MIN) {
3364 draw_dsc->bg_opa = LV_OPA_TRANSP;
3365 draw_dsc->border_opa = LV_OPA_TRANSP;
3366 draw_dsc->shadow_opa = LV_OPA_TRANSP;
3367 draw_dsc->pattern_opa = LV_OPA_TRANSP;
3368 draw_dsc->value_opa = LV_OPA_TRANSP;
3369 return;
3370 }
3371 #endif
3372
3373 if(draw_dsc->bg_opa != LV_OPA_TRANSP) {
3374 draw_dsc->bg_opa = lv_obj_get_style_bg_opa(obj, part);
3375 if(draw_dsc->bg_opa > LV_OPA_MIN) {
3376 draw_dsc->bg_color = lv_obj_get_style_bg_color(obj, part);
3377 draw_dsc->bg_grad_dir = lv_obj_get_style_bg_grad_dir(obj, part);
3378 if(draw_dsc->bg_grad_dir != LV_GRAD_DIR_NONE) {
3379 draw_dsc->bg_grad_color = lv_obj_get_style_bg_grad_color(obj, part);
3380 draw_dsc->bg_main_color_stop = lv_obj_get_style_bg_main_stop(obj, part);
3381 draw_dsc->bg_grad_color_stop = lv_obj_get_style_bg_grad_stop(obj, part);
3382 }
3383
3384 #if LV_USE_BLEND_MODES
3385 draw_dsc->bg_blend_mode = lv_obj_get_style_bg_blend_mode(obj, part);
3386 #endif
3387 }
3388 }
3389
3390 draw_dsc->border_width = lv_obj_get_style_border_width(obj, part);
3391 if(draw_dsc->border_width) {
3392 if(draw_dsc->border_opa != LV_OPA_TRANSP) {
3393 draw_dsc->border_opa = lv_obj_get_style_border_opa(obj, part);
3394 if(draw_dsc->border_opa > LV_OPA_MIN) {
3395 draw_dsc->border_side = lv_obj_get_style_border_side(obj, part);
3396 draw_dsc->border_color = lv_obj_get_style_border_color(obj, part);
3397 }
3398 #if LV_USE_BLEND_MODES
3399 draw_dsc->border_blend_mode = lv_obj_get_style_border_blend_mode(obj, part);
3400 #endif
3401 }
3402 }
3403
3404 #if LV_USE_OUTLINE
3405 draw_dsc->outline_width = lv_obj_get_style_outline_width(obj, part);
3406 if(draw_dsc->outline_width) {
3407 if(draw_dsc->outline_opa != LV_OPA_TRANSP) {
3408 draw_dsc->outline_opa = lv_obj_get_style_outline_opa(obj, part);
3409 if(draw_dsc->outline_opa > LV_OPA_MIN) {
3410 draw_dsc->outline_pad = lv_obj_get_style_outline_pad(obj, part);
3411 draw_dsc->outline_color = lv_obj_get_style_outline_color(obj, part);
3412 }
3413 #if LV_USE_BLEND_MODES
3414 draw_dsc->outline_blend_mode = lv_obj_get_style_outline_blend_mode(obj, part);
3415 #endif
3416 }
3417 }
3418 #endif
3419
3420 #if LV_USE_PATTERN
3421 draw_dsc->pattern_image = lv_obj_get_style_pattern_image(obj, part);
3422 if(draw_dsc->pattern_image) {
3423 if(draw_dsc->pattern_opa != LV_OPA_TRANSP) {
3424 draw_dsc->pattern_opa = lv_obj_get_style_pattern_opa(obj, part);
3425 if(draw_dsc->pattern_opa > LV_OPA_MIN) {
3426 draw_dsc->pattern_recolor_opa = lv_obj_get_style_pattern_recolor_opa(obj, part);
3427 draw_dsc->pattern_repeat = lv_obj_get_style_pattern_repeat(obj, part);
3428 if(lv_img_src_get_type(draw_dsc->pattern_image) == LV_IMG_SRC_SYMBOL) {
3429 draw_dsc->pattern_recolor = lv_obj_get_style_pattern_recolor(obj, part);
3430 draw_dsc->pattern_font = lv_obj_get_style_text_font(obj, part);
3431 }
3432 else if(draw_dsc->pattern_recolor_opa > LV_OPA_MIN) {
3433 draw_dsc->pattern_recolor = lv_obj_get_style_pattern_recolor(obj, part);
3434 }
3435 #if LV_USE_BLEND_MODES
3436 draw_dsc->pattern_blend_mode = lv_obj_get_style_pattern_blend_mode(obj, part);
3437 #endif
3438 }
3439 }
3440 }
3441 #endif
3442
3443 #if LV_USE_SHADOW
3444 draw_dsc->shadow_width = lv_obj_get_style_shadow_width(obj, part);
3445 if(draw_dsc->shadow_width) {
3446 if(draw_dsc->shadow_opa > LV_OPA_MIN) {
3447 draw_dsc->shadow_opa = lv_obj_get_style_shadow_opa(obj, part);
3448 if(draw_dsc->shadow_opa > LV_OPA_MIN) {
3449 draw_dsc->shadow_ofs_x = lv_obj_get_style_shadow_ofs_x(obj, part);
3450 draw_dsc->shadow_ofs_y = lv_obj_get_style_shadow_ofs_y(obj, part);
3451 draw_dsc->shadow_spread = lv_obj_get_style_shadow_spread(obj, part);
3452 draw_dsc->shadow_color = lv_obj_get_style_shadow_color(obj, part);
3453 #if LV_USE_BLEND_MODES
3454 draw_dsc->shadow_blend_mode = lv_obj_get_style_shadow_blend_mode(obj, part);
3455 #endif
3456 }
3457 }
3458 }
3459 #endif
3460
3461 #if LV_USE_VALUE_STR
3462 draw_dsc->value_str = lv_obj_get_style_value_str(obj, part);
3463 if(draw_dsc->value_str) {
3464 if(draw_dsc->value_opa > LV_OPA_MIN) {
3465 draw_dsc->value_opa = lv_obj_get_style_value_opa(obj, part);
3466 if(draw_dsc->value_opa > LV_OPA_MIN) {
3467 draw_dsc->value_ofs_x = lv_obj_get_style_value_ofs_x(obj, part);
3468 draw_dsc->value_ofs_y = lv_obj_get_style_value_ofs_y(obj, part);
3469 draw_dsc->value_color = lv_obj_get_style_value_color(obj, part);
3470 draw_dsc->value_font = lv_obj_get_style_value_font(obj, part);
3471 draw_dsc->value_letter_space = lv_obj_get_style_value_letter_space(obj, part);
3472 draw_dsc->value_line_space = lv_obj_get_style_value_line_space(obj, part);
3473 draw_dsc->value_align = lv_obj_get_style_value_align(obj, part);
3474 #if LV_USE_BLEND_MODES
3475 draw_dsc->value_blend_mode = lv_obj_get_style_value_blend_mode(obj, part);
3476 #endif
3477 }
3478 }
3479 }
3480 #endif
3481
3482 #if LV_USE_OPA_SCALE
3483 if(opa_scale < LV_OPA_MAX) {
3484 draw_dsc->bg_opa = (uint16_t)((uint16_t)draw_dsc->bg_opa * opa_scale) >> 8;
3485 draw_dsc->border_opa = (uint16_t)((uint16_t)draw_dsc->border_opa * opa_scale) >> 8;
3486 draw_dsc->shadow_opa = (uint16_t)((uint16_t)draw_dsc->shadow_opa * opa_scale) >> 8;
3487 draw_dsc->pattern_opa = (uint16_t)((uint16_t)draw_dsc->pattern_opa * opa_scale) >> 8;
3488 draw_dsc->value_opa = (uint16_t)((uint16_t)draw_dsc->value_opa * opa_scale) >> 8;
3489 }
3490 #endif
3491 }
3492
3493 void lv_obj_init_draw_label_dsc(lv_obj_t * obj, uint8_t part, lv_draw_label_dsc_t * draw_dsc)
3494 {
3495 draw_dsc->opa = lv_obj_get_style_text_opa(obj, part);
3496 if(draw_dsc->opa <= LV_OPA_MIN) return;
3497
3498 #if LV_USE_OPA_SCALE
3499 lv_opa_t opa_scale = lv_obj_get_style_opa_scale(obj, part);
3500 if(opa_scale < LV_OPA_MAX) {
3501 draw_dsc->opa = (uint16_t)((uint16_t)draw_dsc->opa * opa_scale) >> 8;
3502 }
3503 if(draw_dsc->opa <= LV_OPA_MIN) return;
3504 #endif
3505
3506 draw_dsc->color = lv_obj_get_style_text_color(obj, part);
3507 draw_dsc->letter_space = lv_obj_get_style_text_letter_space(obj, part);
3508 draw_dsc->line_space = lv_obj_get_style_text_line_space(obj, part);
3509 draw_dsc->decor = lv_obj_get_style_text_decor(obj, part);
3510 #if LV_USE_BLEND_MODES
3511 draw_dsc->blend_mode = lv_obj_get_style_text_blend_mode(obj, part);
3512 #endif
3513
3514 draw_dsc->font = lv_obj_get_style_text_font(obj, part);
3515
3516 if(draw_dsc->sel_start != LV_DRAW_LABEL_NO_TXT_SEL && draw_dsc->sel_end != LV_DRAW_LABEL_NO_TXT_SEL) {
3517 draw_dsc->color = lv_obj_get_style_text_sel_color(obj, part);
3518 }
3519
3520 #if LV_USE_BIDI
3521 draw_dsc->bidi_dir = lv_obj_get_base_dir(obj);
3522 #endif
3523 }
3524
3525 void lv_obj_init_draw_img_dsc(lv_obj_t * obj, uint8_t part, lv_draw_img_dsc_t * draw_dsc)
3526 {
3527 draw_dsc->opa = lv_obj_get_style_image_opa(obj, part);
3528 if(draw_dsc->opa <= LV_OPA_MIN) return;
3529
3530 #if LV_USE_OPA_SCALE
3531 lv_opa_t opa_scale = lv_obj_get_style_opa_scale(obj, part);
3532 if(opa_scale < LV_OPA_MAX) {
3533 draw_dsc->opa = (uint16_t)((uint16_t)draw_dsc->opa * opa_scale) >> 8;
3534 }
3535 if(draw_dsc->opa <= LV_OPA_MIN) return;
3536 #endif
3537
3538 draw_dsc->angle = 0;
3539 draw_dsc->zoom = LV_IMG_ZOOM_NONE;
3540 draw_dsc->pivot.x = lv_area_get_width(&obj->coords) / 2;
3541 draw_dsc->pivot.y = lv_area_get_height(&obj->coords) / 2;
3542
3543 draw_dsc->recolor_opa = lv_obj_get_style_image_recolor_opa(obj, part);
3544 if(draw_dsc->recolor_opa > 0) {
3545 draw_dsc->recolor = lv_obj_get_style_image_recolor(obj, part);
3546 }
3547 #if LV_USE_BLEND_MODES
3548 draw_dsc->blend_mode = lv_obj_get_style_image_blend_mode(obj, part);
3549 #endif
3550 }
3551
3552 void lv_obj_init_draw_line_dsc(lv_obj_t * obj, uint8_t part, lv_draw_line_dsc_t * draw_dsc)
3553 {
3554 draw_dsc->width = lv_obj_get_style_line_width(obj, part);
3555 if(draw_dsc->width == 0) return;
3556
3557 draw_dsc->opa = lv_obj_get_style_line_opa(obj, part);
3558 if(draw_dsc->opa <= LV_OPA_MIN) return;
3559
3560 #if LV_USE_OPA_SCALE
3561 lv_opa_t opa_scale = lv_obj_get_style_opa_scale(obj, part);
3562 if(opa_scale < LV_OPA_MAX) {
3563 draw_dsc->opa = (uint16_t)((uint16_t)draw_dsc->opa * opa_scale) >> 8;
3564 }
3565 if(draw_dsc->opa <= LV_OPA_MIN) return;
3566 #endif
3567
3568 draw_dsc->color = lv_obj_get_style_line_color(obj, part);
3569
3570 draw_dsc->dash_width = lv_obj_get_style_line_dash_width(obj, part);
3571 if(draw_dsc->dash_width) {
3572 draw_dsc->dash_gap = lv_obj_get_style_line_dash_gap(obj, part);
3573 }
3574
3575 draw_dsc->round_start = lv_obj_get_style_line_rounded(obj, part);
3576 draw_dsc->round_end = draw_dsc->round_start;
3577
3578 #if LV_USE_BLEND_MODES
3579 draw_dsc->blend_mode = lv_obj_get_style_line_blend_mode(obj, part);
3580 #endif
3581 }
3582
3583 /**
3584 * Get the required extra size (around the object's part) to draw shadow, outline, value etc.
3585 * @param obj pointer to an object
3586 * @param part part of the object
3587 */
3588 lv_coord_t lv_obj_get_draw_rect_ext_pad_size(lv_obj_t * obj, uint8_t part)
3589 {
3590 lv_coord_t s = 0;
3591
3592 lv_coord_t sh_width = lv_obj_get_style_shadow_width(obj, part);
3593 if(sh_width) {
3594 lv_opa_t sh_opa = lv_obj_get_style_shadow_opa(obj, part);
3595 if(sh_opa > LV_OPA_MIN) {
3596 sh_width = sh_width / 2; /*THe blur adds only half width*/
3597 sh_width++;
3598 sh_width += lv_obj_get_style_shadow_spread(obj, part);
3599 lv_style_int_t sh_ofs_x = lv_obj_get_style_shadow_ofs_x(obj, part);
3600 lv_style_int_t sh_ofs_y = lv_obj_get_style_shadow_ofs_y(obj, part);
3601 sh_width += LV_MATH_MAX(LV_MATH_ABS(sh_ofs_x), LV_MATH_ABS(sh_ofs_y));
3602 s = LV_MATH_MAX(s, sh_width);
3603 }
3604 }
3605
3606 const char * value_str = lv_obj_get_style_value_str(obj, part);
3607 if(value_str) {
3608 lv_opa_t value_opa = lv_obj_get_style_value_opa(obj, part);
3609 if(value_opa > LV_OPA_MIN) {
3610 lv_style_int_t letter_space = lv_obj_get_style_value_letter_space(obj, part);
3611 lv_style_int_t line_space = lv_obj_get_style_value_letter_space(obj, part);
3612 const lv_font_t * font = lv_obj_get_style_value_font(obj, part);
3613
3614 lv_point_t txt_size;
3615 _lv_txt_get_size(&txt_size, value_str, font, letter_space, line_space, LV_COORD_MAX, LV_TXT_FLAG_NONE);
3616
3617 lv_area_t value_area;
3618 value_area.x1 = 0;
3619 value_area.y1 = 0;
3620 value_area.x2 = txt_size.x - 1;
3621 value_area.y2 = txt_size.y - 1;
3622
3623 lv_style_int_t align = lv_obj_get_style_value_align(obj, part);
3624 lv_style_int_t xofs = lv_obj_get_style_value_ofs_x(obj, part);
3625 lv_style_int_t yofs = lv_obj_get_style_value_ofs_y(obj, part);
3626 lv_point_t p_align;
3627 _lv_area_align(&obj->coords, &value_area, align, &p_align);
3628
3629 value_area.x1 += p_align.x + xofs;
3630 value_area.y1 += p_align.y + yofs;
3631 value_area.x2 += p_align.x + xofs;
3632 value_area.y2 += p_align.y + yofs;
3633
3634 s = LV_MATH_MAX(s, obj->coords.x1 - value_area.x1);
3635 s = LV_MATH_MAX(s, obj->coords.y1 - value_area.y1);
3636 s = LV_MATH_MAX(s, value_area.x2 - obj->coords.x2);
3637 s = LV_MATH_MAX(s, value_area.y2 - obj->coords.y2);
3638 }
3639 }
3640
3641 lv_style_int_t outline_width = lv_obj_get_style_outline_width(obj, part);
3642 if(outline_width) {
3643 lv_opa_t outline_opa = lv_obj_get_style_outline_opa(obj, part);
3644 if(outline_opa > LV_OPA_MIN) {
3645 lv_style_int_t outline_pad = lv_obj_get_style_outline_pad(obj, part);
3646 s = LV_MATH_MAX(s, outline_pad + outline_width);
3647 }
3648 }
3649
3650 lv_coord_t w = lv_obj_get_style_transform_width(obj, part);
3651 lv_coord_t h = lv_obj_get_style_transform_height(obj, part);
3652 lv_coord_t wh = LV_MATH_MAX(w, h);
3653 if(wh > 0) s += wh;
3654
3655 return s;
3656 }
3657
3658 /**
3659 * Fade in (from transparent to fully cover) an object and all its children using an `opa_scale` animation.
3660 * @param obj the object to fade in
3661 * @param time duration of the animation [ms]
3662 * @param delay wait before the animation starts [ms]
3663 */
3664 void lv_obj_fade_in(lv_obj_t * obj, uint32_t time, uint32_t delay)
3665 {
3666 #if LV_USE_ANIMATION
3667 lv_anim_t a;
3668 lv_anim_init(&a);
3669 lv_anim_set_var(&a, obj);
3670 lv_anim_set_values(&a, LV_OPA_TRANSP, LV_OPA_COVER);
3671 lv_anim_set_exec_cb(&a, (lv_anim_exec_xcb_t)opa_scale_anim);
3672 lv_anim_set_ready_cb(&a, fade_in_anim_ready);
3673 lv_anim_set_time(&a, time);
3674 lv_anim_set_delay(&a, delay);
3675 lv_anim_start(&a);
3676 #else
3677 (void) obj; /*Unused*/
3678 (void) time; /*Unused*/
3679 (void) delay; /*Unused*/
3680 #endif
3681 }
3682
3683 /**
3684 * Fade out (from fully cover to transparent) an object and all its children using an `opa_scale` animation.
3685 * @param obj the object to fade in
3686 * @param time duration of the animation [ms]
3687 * @param delay wait before the animation starts [ms]
3688 */
3689 void lv_obj_fade_out(lv_obj_t * obj, uint32_t time, uint32_t delay)
3690 {
3691 #if LV_USE_ANIMATION
3692 lv_anim_t a;
3693 lv_anim_init(&a);
3694 lv_anim_set_var(&a, obj);
3695 lv_anim_set_values(&a, LV_OPA_COVER, LV_OPA_TRANSP);
3696 lv_anim_set_exec_cb(&a, (lv_anim_exec_xcb_t)opa_scale_anim);
3697 lv_anim_set_time(&a, time);
3698 lv_anim_set_delay(&a, delay);
3699 lv_anim_start(&a);
3700 #else
3701 (void) obj; /*Unused*/
3702 (void) time; /*Unused*/
3703 (void) delay; /*Unused*/
3704 #endif
3705 }
3706
3707 /**
3708 * Check if any object has a given type
3709 * @param obj pointer to an object
3710 * @param obj_type type of the object. (e.g. "lv_btn")
3711 * @return true: valid
3712 */
3713 bool lv_debug_check_obj_type(const lv_obj_t * obj, const char * obj_type)
3714 {
3715 if(obj_type[0] == '\0') return true;
3716
3717 lv_obj_type_t types;
3718 lv_obj_get_type((lv_obj_t *)obj, &types);
3719
3720 uint8_t i;
3721 for(i = 0; i < LV_MAX_ANCESTOR_NUM; i++) {
3722 if(types.type[i] == NULL) break;
3723 if(strcmp(types.type[i], obj_type) == 0) return true;
3724 }
3725
3726 return false;
3727 }
3728
3729 /**
3730 * Check if any object is still "alive", and part of the hierarchy
3731 * @param obj pointer to an object
3732 * @param obj_type type of the object. (e.g. "lv_btn")
3733 * @return true: valid
3734 */
3735 bool lv_debug_check_obj_valid(const lv_obj_t * obj)
3736 {
3737 lv_disp_t * disp = lv_disp_get_next(NULL);
3738 while(disp) {
3739 lv_obj_t * scr;
3740 _LV_LL_READ(disp->scr_ll, scr) {
3741
3742 if(scr == obj) return true;
3743 bool found = obj_valid_child(scr, obj);
3744 if(found) return true;
3745 }
3746
3747 disp = lv_disp_get_next(disp);
3748 }
3749
3750 return false;
3751 }
3752
3753 /**********************
3754 * STATIC FUNCTIONS
3755 **********************/
3756
3757 static void lv_obj_del_async_cb(void * obj)
3758 {
3759 LV_ASSERT_OBJ(obj, LV_OBJX_NAME);
3760
3761 lv_obj_del(obj);
3762 }
3763
3764 static void obj_del_core(lv_obj_t * obj)
3765 {
3766 /*Let the user free the resources used in `LV_EVENT_DELETE`*/
3767 lv_event_send(obj, LV_EVENT_DELETE, NULL);
3768
3769 /*Delete from the group*/
3770 #if LV_USE_GROUP
3771 lv_group_t * group = lv_obj_get_group(obj);
3772 if(group) lv_group_remove_obj(obj);
3773 #endif
3774
3775 /*Remove the animations from this object*/
3776 #if LV_USE_ANIMATION
3777 lv_anim_del(obj, NULL);
3778 trans_del(obj, 0xFF, 0xFF, NULL);
3779 #endif
3780
3781 /*Delete the user data*/
3782 #if LV_USE_USER_DATA
3783 #if LV_USE_USER_DATA_FREE
3784 LV_USER_DATA_FREE(obj);
3785 #endif
3786 #endif
3787
3788 /*Recursively delete the children*/
3789 lv_obj_t * i;
3790 i = _lv_ll_get_head(&(obj->child_ll));
3791 while(i != NULL) {
3792 /*Call the recursive delete to the child too*/
3793 obj_del_core(i);
3794
3795 /*Set i to the new head node*/
3796 i = _lv_ll_get_head(&(obj->child_ll));
3797 }
3798
3799 lv_event_mark_deleted(obj);
3800
3801 /* Reset all input devices if the object to delete is used*/
3802 lv_indev_t * indev = lv_indev_get_next(NULL);
3803 while(indev) {
3804 if(indev->proc.types.pointer.act_obj == obj || indev->proc.types.pointer.last_obj == obj) {
3805 lv_indev_reset(indev, obj);
3806 }
3807 if(indev->proc.types.pointer.last_pressed == obj) {
3808 indev->proc.types.pointer.last_pressed = NULL;
3809 }
3810
3811 #if LV_USE_GROUP
3812 if(indev->group == group && obj == lv_indev_get_obj_act()) {
3813 lv_indev_reset(indev, obj);
3814 }
3815 #endif
3816 indev = lv_indev_get_next(indev);
3817 }
3818
3819 /* All children deleted.
3820 * Now clean up the object specific data*/
3821 obj->signal_cb(obj, LV_SIGNAL_CLEANUP, NULL);
3822
3823 /*Remove the object from parent's children list*/
3824 lv_obj_t * par = lv_obj_get_parent(obj);
3825 if(par == NULL) { /*It is a screen*/
3826 lv_disp_t * d = lv_obj_get_disp(obj);
3827 _lv_ll_remove(&d->scr_ll, obj);
3828 }
3829 else {
3830 _lv_ll_remove(&(par->child_ll), obj);
3831 }
3832
3833 /*Delete the base objects*/
3834 if(obj->ext_attr != NULL) lv_mem_free(obj->ext_attr);
3835 lv_mem_free(obj); /*Free the object itself*/
3836 }
3837
3838 /**
3839 * Handle the drawing related tasks of the base objects.
3840 * @param obj pointer to an object
3841 * @param clip_area the object will be drawn only in this area
3842 * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
3843 * (return 'true' if yes)
3844 * LV_DESIGN_DRAW: draw the object (always return 'true')
3845 * @param return an element of `lv_design_res_t`
3846 */
3847 static lv_design_res_t lv_obj_design(lv_obj_t * obj, const lv_area_t * clip_area, lv_design_mode_t mode)
3848 {
3849 if(mode == LV_DESIGN_COVER_CHK) {
3850 if(lv_obj_get_style_clip_corner(obj, LV_OBJ_PART_MAIN)) return LV_DESIGN_RES_MASKED;
3851
3852 /*Most trivial test. Is the mask fully IN the object? If no it surely doesn't cover it*/
3853 lv_coord_t r = lv_obj_get_style_radius(obj, LV_OBJ_PART_MAIN);
3854 lv_coord_t w = lv_obj_get_style_transform_width(obj, LV_OBJ_PART_MAIN);
3855 lv_coord_t h = lv_obj_get_style_transform_height(obj, LV_OBJ_PART_MAIN);
3856 lv_area_t coords;
3857 lv_area_copy(&coords, &obj->coords);
3858 coords.x1 -= w;
3859 coords.x2 += w;
3860 coords.y1 -= h;
3861 coords.y2 += h;
3862
3863 if(_lv_area_is_in(clip_area, &coords, r) == false) return LV_DESIGN_RES_NOT_COVER;
3864
3865 if(lv_obj_get_style_bg_opa(obj, LV_OBJ_PART_MAIN) < LV_OPA_MAX) return LV_DESIGN_RES_NOT_COVER;
3866
3867 if(lv_obj_get_style_bg_blend_mode(obj, LV_OBJ_PART_MAIN) != LV_BLEND_MODE_NORMAL) return LV_DESIGN_RES_NOT_COVER;
3868 if(lv_obj_get_style_border_blend_mode(obj, LV_OBJ_PART_MAIN) != LV_BLEND_MODE_NORMAL) return LV_DESIGN_RES_NOT_COVER;
3869 if(lv_obj_get_style_opa_scale(obj, LV_OBJ_PART_MAIN) < LV_OPA_MAX) return LV_DESIGN_RES_NOT_COVER;
3870
3871 return LV_DESIGN_RES_COVER;
3872
3873 }
3874 else if(mode == LV_DESIGN_DRAW_MAIN) {
3875 lv_draw_rect_dsc_t draw_dsc;
3876 lv_draw_rect_dsc_init(&draw_dsc);
3877 /*If the border is drawn later disable loading its properties*/
3878 if(lv_obj_get_style_border_post(obj, LV_OBJ_PART_MAIN)) {
3879 draw_dsc.border_post = 1;
3880 }
3881
3882 lv_obj_init_draw_rect_dsc(obj, LV_OBJ_PART_MAIN, &draw_dsc);
3883
3884 lv_coord_t w = lv_obj_get_style_transform_width(obj, LV_OBJ_PART_MAIN);
3885 lv_coord_t h = lv_obj_get_style_transform_height(obj, LV_OBJ_PART_MAIN);
3886 lv_area_t coords;
3887 lv_area_copy(&coords, &obj->coords);
3888 coords.x1 -= w;
3889 coords.x2 += w;
3890 coords.y1 -= h;
3891 coords.y2 += h;
3892
3893 lv_draw_rect(&coords, clip_area, &draw_dsc);
3894
3895 if(lv_obj_get_style_clip_corner(obj, LV_OBJ_PART_MAIN)) {
3896 lv_draw_mask_radius_param_t * mp = _lv_mem_buf_get(sizeof(lv_draw_mask_radius_param_t));
3897
3898 lv_coord_t r = lv_obj_get_style_radius(obj, LV_OBJ_PART_MAIN);
3899 /* If it has border make the clip area 1 px smaller to avoid color bleeding
3900 * The border will cover the minimal issue on the edges*/
3901 if(draw_dsc.border_post && draw_dsc.border_opa >= LV_OPA_MIN && draw_dsc.border_width > 0) {
3902 lv_area_t cc_area;
3903 cc_area.x1 = obj->coords.x1 + 1;
3904 cc_area.y1 = obj->coords.y1 + 1;
3905 cc_area.x2 = obj->coords.x2 - 1;
3906 cc_area.y2 = obj->coords.y2 - 1;
3907 lv_draw_mask_radius_init(mp, &cc_area, r, false);
3908 }
3909 /*If no border use the full size.*/
3910 else {
3911 lv_draw_mask_radius_init(mp, &obj->coords, r, false);
3912 }
3913
3914 /*Add the mask and use `obj+8` as custom id. Don't use `obj` directly because it might be used by the user*/
3915 lv_draw_mask_add(mp, obj + 8);
3916 }
3917 }
3918 else if(mode == LV_DESIGN_DRAW_POST) {
3919 if(lv_obj_get_style_clip_corner(obj, LV_OBJ_PART_MAIN)) {
3920 lv_draw_mask_radius_param_t * param = lv_draw_mask_remove_custom(obj + 8);
3921 _lv_mem_buf_release(param);
3922 }
3923
3924 /*If the border is drawn later disable loading other properties*/
3925 if(lv_obj_get_style_border_post(obj, LV_OBJ_PART_MAIN)) {
3926 lv_draw_rect_dsc_t draw_dsc;
3927 lv_draw_rect_dsc_init(&draw_dsc);
3928 draw_dsc.bg_opa = LV_OPA_TRANSP;
3929 draw_dsc.pattern_opa = LV_OPA_TRANSP;
3930 draw_dsc.shadow_opa = LV_OPA_TRANSP;
3931 draw_dsc.value_opa = LV_OPA_TRANSP;
3932 lv_obj_init_draw_rect_dsc(obj, LV_OBJ_PART_MAIN, &draw_dsc);
3933
3934 lv_coord_t w = lv_obj_get_style_transform_width(obj, LV_OBJ_PART_MAIN);
3935 lv_coord_t h = lv_obj_get_style_transform_height(obj, LV_OBJ_PART_MAIN);
3936 lv_area_t coords;
3937 lv_area_copy(&coords, &obj->coords);
3938 coords.x1 -= w;
3939 coords.x2 += w;
3940 coords.y1 -= h;
3941 coords.y2 += h;
3942 lv_draw_rect(&coords, clip_area, &draw_dsc);
3943 }
3944 }
3945
3946 return LV_DESIGN_RES_OK;
3947 }
3948
3949
3950 /**
3951 * Get the really focused object by taking `focus_parent` into account.
3952 * @param obj the start object
3953 * @return the object to really focus
3954 */
3955 lv_obj_t * lv_obj_get_focused_obj(const lv_obj_t * obj)
3956 {
3957 if(obj == NULL) return NULL;
3958 const lv_obj_t * focus_obj = obj;
3959 while(lv_obj_get_focus_parent(focus_obj) != false && focus_obj != NULL) {
3960 focus_obj = lv_obj_get_parent(focus_obj);
3961 }
3962
3963 return (lv_obj_t *)focus_obj;
3964 }
3965
3966 /**
3967 * Signal function of the basic object
3968 * @param obj pointer to an object
3969 * @param sign signal type
3970 * @param param parameter for the signal (depends on signal type)
3971 * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
3972 */
3973 static lv_res_t lv_obj_signal(lv_obj_t * obj, lv_signal_t sign, void * param)
3974 {
3975 if(sign == LV_SIGNAL_GET_STYLE) {
3976 lv_get_style_info_t * info = param;
3977 if(info->part == LV_OBJ_PART_MAIN) info->result = &obj->style_list;
3978 else info->result = NULL;
3979 return LV_RES_OK;
3980 }
3981 else if(sign == LV_SIGNAL_GET_TYPE) return lv_obj_handle_get_type_signal(param, LV_OBJX_NAME);
3982
3983 lv_res_t res = LV_RES_OK;
3984
3985 if(sign == LV_SIGNAL_CHILD_CHG) {
3986 /*Return 'invalid' if the child change signal is not enabled*/
3987 if(lv_obj_is_protected(obj, LV_PROTECT_CHILD_CHG) != false) res = LV_RES_INV;
3988 }
3989 else if(sign == LV_SIGNAL_REFR_EXT_DRAW_PAD) {
3990 lv_coord_t d = lv_obj_get_draw_rect_ext_pad_size(obj, LV_OBJ_PART_MAIN);
3991 obj->ext_draw_pad = LV_MATH_MAX(obj->ext_draw_pad, d);
3992 }
3993 #if LV_USE_OBJ_REALIGN
3994 else if(sign == LV_SIGNAL_PARENT_SIZE_CHG) {
3995 if(obj->realign.auto_realign) {
3996 lv_obj_realign(obj);
3997 }
3998 }
3999 #endif
4000 else if(sign == LV_SIGNAL_STYLE_CHG) {
4001 lv_obj_refresh_ext_draw_pad(obj);
4002 }
4003 else if(sign == LV_SIGNAL_PRESSED) {
4004 lv_obj_add_state(obj, LV_STATE_PRESSED);
4005 }
4006 else if(sign == LV_SIGNAL_RELEASED || sign == LV_SIGNAL_PRESS_LOST) {
4007 lv_obj_clear_state(obj, LV_STATE_PRESSED);
4008 }
4009 else if(sign == LV_SIGNAL_FOCUS) {
4010 bool editing = false;
4011 #if LV_USE_GROUP
4012 editing = lv_group_get_editing(lv_obj_get_group(obj));
4013 #endif
4014 if(editing) {
4015 uint8_t state = LV_STATE_FOCUSED;
4016 state |= LV_STATE_EDITED;
4017
4018 /*if using focus mode, change target to parent*/
4019 obj = lv_obj_get_focused_obj(obj);
4020
4021 lv_obj_add_state(obj, state);
4022 }
4023 else {
4024
4025 /*if using focus mode, change target to parent*/
4026 obj = lv_obj_get_focused_obj(obj);
4027
4028 lv_obj_add_state(obj, LV_STATE_FOCUSED);
4029 lv_obj_clear_state(obj, LV_STATE_EDITED);
4030 }
4031 }
4032 else if(sign == LV_SIGNAL_DEFOCUS) {
4033
4034 /*if using focus mode, change target to parent*/
4035 obj = lv_obj_get_focused_obj(obj);
4036
4037 lv_obj_clear_state(obj, LV_STATE_FOCUSED | LV_STATE_EDITED);
4038 }
4039 else if(sign == LV_SIGNAL_CLEANUP) {
4040 lv_obj_clean_style_list(obj, LV_OBJ_PART_MAIN);
4041 }
4042
4043 return res;
4044 }
4045
4046 /**
4047 * Reposition the children of an object. (Called recursively)
4048 * @param obj pointer to an object which children will be repositioned
4049 * @param x_diff x coordinate shift
4050 * @param y_diff y coordinate shift
4051 */
4052 static void refresh_children_position(lv_obj_t * obj, lv_coord_t x_diff, lv_coord_t y_diff)
4053 {
4054 lv_obj_t * i;
4055 _LV_LL_READ(obj->child_ll, i) {
4056 i->coords.x1 += x_diff;
4057 i->coords.y1 += y_diff;
4058 i->coords.x2 += x_diff;
4059 i->coords.y2 += y_diff;
4060
4061 refresh_children_position(i, x_diff, y_diff);
4062 }
4063 }
4064
4065 /**
4066 * Refresh the style of all children of an object. (Called recursively)
4067 * @param style refresh objects only with this style_list.
4068 * @param obj pointer to an object
4069 */
4070 static void report_style_mod_core(void * style, lv_obj_t * obj)
4071 {
4072 uint8_t part;
4073 for(part = 0; part != _LV_OBJ_PART_REAL_LAST; part++) {
4074 lv_style_list_t * list = lv_obj_get_style_list(obj, part);
4075 if(list == NULL) break;
4076
4077 uint8_t ci;
4078 for(ci = 0; ci < list->style_cnt; ci++) {
4079 lv_style_t * class = lv_style_list_get_style(list, ci);
4080 if(class == style || style == NULL) {
4081 lv_obj_refresh_style(obj, part, LV_STYLE_PROP_ALL);
4082 break;
4083 }
4084 }
4085 }
4086
4087 lv_obj_t * child = lv_obj_get_child(obj, NULL);
4088 while(child) {
4089 report_style_mod_core(style, child);
4090 child = lv_obj_get_child(obj, child);
4091 }
4092
4093 }
4094
4095 /**
4096 * Recursively refresh the style of the children. Go deeper until a not NULL style is found
4097 * because the NULL styles are inherited from the parent
4098 * @param obj pointer to an object
4099 */
4100 static void refresh_children_style(lv_obj_t * obj)
4101 {
4102 lv_obj_t * child = lv_obj_get_child(obj, NULL);
4103 while(child != NULL) {
4104 lv_obj_invalidate(child);
4105 child->signal_cb(child, LV_SIGNAL_STYLE_CHG, NULL);
4106 lv_obj_invalidate(child);
4107
4108 refresh_children_style(child); /*Check children too*/
4109 child = lv_obj_get_child(obj, child);
4110 }
4111 }
4112
4113 static void base_dir_refr_children(lv_obj_t * obj)
4114 {
4115 lv_obj_t * child;
4116 child = lv_obj_get_child(obj, NULL);
4117
4118 while(child) {
4119 if(child->base_dir == LV_BIDI_DIR_INHERIT) {
4120 lv_signal_send(child, LV_SIGNAL_BASE_DIR_CHG, NULL);
4121 base_dir_refr_children(child);
4122 }
4123
4124 child = lv_obj_get_child(obj, child);
4125 }
4126 }
4127
4128 static void obj_align_core(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, bool x_set, bool y_set,
4129 lv_coord_t x_ofs, lv_coord_t y_ofs)
4130 {
4131 lv_point_t new_pos;
4132 _lv_area_align(&base->coords, &obj->coords, align, &new_pos);
4133
4134 /*Bring together the coordination system of base and obj*/
4135 lv_obj_t * par = lv_obj_get_parent(obj);
4136 lv_coord_t par_abs_x = par->coords.x1;
4137 lv_coord_t par_abs_y = par->coords.y1;
4138 new_pos.x += x_ofs;
4139 new_pos.y += y_ofs;
4140 new_pos.x -= par_abs_x;
4141 new_pos.y -= par_abs_y;
4142
4143 if(x_set && y_set) lv_obj_set_pos(obj, new_pos.x, new_pos.y);
4144 else if(x_set) lv_obj_set_x(obj, new_pos.x);
4145 else if(y_set) lv_obj_set_y(obj, new_pos.y);
4146 }
4147
4148 static void obj_align_mid_core(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, bool x_set, bool y_set,
4149 lv_coord_t x_ofs, lv_coord_t y_ofs)
4150 {
4151 lv_coord_t new_x = lv_obj_get_x(obj);
4152 lv_coord_t new_y = lv_obj_get_y(obj);
4153
4154 lv_coord_t obj_w_half = lv_obj_get_width(obj) / 2;
4155 lv_coord_t obj_h_half = lv_obj_get_height(obj) / 2;
4156
4157
4158 switch(align) {
4159 case LV_ALIGN_CENTER:
4160 new_x = lv_obj_get_width(base) / 2 - obj_w_half;
4161 new_y = lv_obj_get_height(base) / 2 - obj_h_half;
4162 break;
4163
4164 case LV_ALIGN_IN_TOP_LEFT:
4165 new_x = -obj_w_half;
4166 new_y = -obj_h_half;
4167 break;
4168 case LV_ALIGN_IN_TOP_MID:
4169 new_x = lv_obj_get_width(base) / 2 - obj_w_half;
4170 new_y = -obj_h_half;
4171 break;
4172
4173 case LV_ALIGN_IN_TOP_RIGHT:
4174 new_x = lv_obj_get_width(base) - obj_w_half;
4175 new_y = -obj_h_half;
4176 break;
4177
4178 case LV_ALIGN_IN_BOTTOM_LEFT:
4179 new_x = -obj_w_half;
4180 new_y = lv_obj_get_height(base) - obj_h_half;
4181 break;
4182 case LV_ALIGN_IN_BOTTOM_MID:
4183 new_x = lv_obj_get_width(base) / 2 - obj_w_half;
4184 new_y = lv_obj_get_height(base) - obj_h_half;
4185 break;
4186
4187 case LV_ALIGN_IN_BOTTOM_RIGHT:
4188 new_x = lv_obj_get_width(base) - obj_w_half;
4189 new_y = lv_obj_get_height(base) - obj_h_half;
4190 break;
4191
4192 case LV_ALIGN_IN_LEFT_MID:
4193 new_x = -obj_w_half;
4194 new_y = lv_obj_get_height(base) / 2 - obj_h_half;
4195 break;
4196
4197 case LV_ALIGN_IN_RIGHT_MID:
4198 new_x = lv_obj_get_width(base) - obj_w_half;
4199 new_y = lv_obj_get_height(base) / 2 - obj_h_half;
4200 break;
4201
4202 case LV_ALIGN_OUT_TOP_LEFT:
4203 new_x = -obj_w_half;
4204 new_y = -obj_h_half;
4205 break;
4206
4207 case LV_ALIGN_OUT_TOP_MID:
4208 new_x = lv_obj_get_width(base) / 2 - obj_w_half;
4209 new_y = -obj_h_half;
4210 break;
4211
4212 case LV_ALIGN_OUT_TOP_RIGHT:
4213 new_x = lv_obj_get_width(base) - obj_w_half;
4214 new_y = -obj_h_half;
4215 break;
4216
4217 case LV_ALIGN_OUT_BOTTOM_LEFT:
4218 new_x = -obj_w_half;
4219 new_y = lv_obj_get_height(base) - obj_h_half;
4220 break;
4221
4222 case LV_ALIGN_OUT_BOTTOM_MID:
4223 new_x = lv_obj_get_width(base) / 2 - obj_w_half;
4224 new_y = lv_obj_get_height(base) - obj_h_half;
4225 break;
4226
4227 case LV_ALIGN_OUT_BOTTOM_RIGHT:
4228 new_x = lv_obj_get_width(base) - obj_w_half;
4229 new_y = lv_obj_get_height(base) - obj_h_half;
4230 break;
4231
4232 case LV_ALIGN_OUT_LEFT_TOP:
4233 new_x = -obj_w_half;
4234 new_y = -obj_h_half;
4235 break;
4236
4237 case LV_ALIGN_OUT_LEFT_MID:
4238 new_x = -obj_w_half;
4239 new_y = lv_obj_get_height(base) / 2 - obj_h_half;
4240 break;
4241
4242 case LV_ALIGN_OUT_LEFT_BOTTOM:
4243 new_x = -obj_w_half;
4244 new_y = lv_obj_get_height(base) - obj_h_half;
4245 break;
4246
4247 case LV_ALIGN_OUT_RIGHT_TOP:
4248 new_x = lv_obj_get_width(base) - obj_w_half;
4249 new_y = -obj_h_half;
4250 break;
4251
4252 case LV_ALIGN_OUT_RIGHT_MID:
4253 new_x = lv_obj_get_width(base) - obj_w_half;
4254 new_y = lv_obj_get_height(base) / 2 - obj_h_half;
4255 break;
4256
4257 case LV_ALIGN_OUT_RIGHT_BOTTOM:
4258 new_x = lv_obj_get_width(base) - obj_w_half;
4259 new_y = lv_obj_get_height(base) - obj_h_half;
4260 break;
4261 }
4262
4263 /*Bring together the coordination system of base and obj*/
4264 lv_obj_t * par = lv_obj_get_parent(obj);
4265 lv_coord_t base_abs_x = base->coords.x1;
4266 lv_coord_t base_abs_y = base->coords.y1;
4267 lv_coord_t par_abs_x = par->coords.x1;
4268 lv_coord_t par_abs_y = par->coords.y1;
4269 new_x += x_ofs + base_abs_x;
4270 new_y += y_ofs + base_abs_y;
4271 new_x -= par_abs_x;
4272 new_y -= par_abs_y;
4273 if(x_set && y_set) lv_obj_set_pos(obj, new_x, new_y);
4274 else if(x_set) lv_obj_set_x(obj, new_x);
4275 else if(y_set) lv_obj_set_y(obj, new_y);
4276
4277 }
4278
4279
4280
4281 #if LV_USE_ANIMATION
4282
4283 /**
4284 * Allocate and initialize a transition for a property of an object if the properties value is different in the new state.
4285 * It allocates `lv_style_trans_t` in `_lv_obj_style_trans_ll` and set only `start/end_values`. No animation will be created here.
4286 * @param obj and object to add the transition
4287 * @param prop the property to apply the transaction
4288 * @param part the part of the object to apply the transaction
4289 * @param prev_state the previous state of the objects
4290 * @param new_state the new state of the object
4291 * @return pointer to the allocated `the transaction` variable or `NULL` if no transition created
4292 */
4293 static lv_style_trans_t * trans_create(lv_obj_t * obj, lv_style_property_t prop, uint8_t part, lv_state_t prev_state,
4294 lv_state_t new_state)
4295 {
4296 lv_style_trans_t * tr;
4297 lv_style_list_t * style_list = lv_obj_get_style_list(obj, part);
4298 lv_style_t * style_trans = _lv_style_list_get_transition_style(style_list);
4299
4300 bool cahche_ori = style_list->ignore_cache;
4301
4302 /*Get the previous and current values*/
4303 if((prop & 0xF) < LV_STYLE_ID_COLOR) { /*Int*/
4304 style_list->skip_trans = 1;
4305 style_list->ignore_cache = 1;
4306 obj->state = prev_state;
4307 lv_style_int_t int1 = _lv_obj_get_style_int(obj, part, prop);
4308 obj->state = new_state;
4309 lv_style_int_t int2 = _lv_obj_get_style_int(obj, part, prop);
4310 style_list->skip_trans = 0;
4311 style_list->ignore_cache = cahche_ori;
4312
4313 if(int1 == int2) return NULL;
4314 obj->state = prev_state;
4315 int1 = _lv_obj_get_style_int(obj, part, prop);
4316 obj->state = new_state;
4317 _lv_style_set_int(style_trans, prop, int1); /*Be sure `trans_style` has a valid value */
4318
4319 if(prop == LV_STYLE_RADIUS) {
4320 if(int1 == LV_RADIUS_CIRCLE || int2 == LV_RADIUS_CIRCLE) {
4321 lv_coord_t whalf = lv_obj_get_width(obj) / 2;
4322 lv_coord_t hhalf = lv_obj_get_width(obj) / 2;
4323 if(int1 == LV_RADIUS_CIRCLE) int1 = LV_MATH_MIN(whalf + 1, hhalf + 1);
4324 if(int2 == LV_RADIUS_CIRCLE) int2 = LV_MATH_MIN(whalf + 1, hhalf + 1);
4325 }
4326 }
4327
4328 tr = _lv_ll_ins_head(&LV_GC_ROOT(_lv_obj_style_trans_ll));
4329 LV_ASSERT_MEM(tr);
4330 if(tr == NULL) return NULL;
4331 tr->start_value._int = int1;
4332 tr->end_value._int = int2;
4333 }
4334 else if((prop & 0xF) < LV_STYLE_ID_OPA) { /*Color*/
4335 style_list->skip_trans = 1;
4336 style_list->ignore_cache = 1;
4337 obj->state = prev_state;
4338 lv_color_t c1 = _lv_obj_get_style_color(obj, part, prop);
4339 obj->state = new_state;
4340 lv_color_t c2 = _lv_obj_get_style_color(obj, part, prop);
4341 style_list->skip_trans = 0;
4342 style_list->ignore_cache = cahche_ori;
4343
4344 if(c1.full == c2.full) return NULL;
4345 obj->state = prev_state;
4346 c1 = _lv_obj_get_style_color(obj, part, prop);
4347 obj->state = new_state;
4348 _lv_style_set_color(style_trans, prop, c1); /*Be sure `trans_style` has a valid value */
4349
4350 tr = _lv_ll_ins_head(&LV_GC_ROOT(_lv_obj_style_trans_ll));
4351 LV_ASSERT_MEM(tr);
4352 if(tr == NULL) return NULL;
4353 tr->start_value._color = c1;
4354 tr->end_value._color = c2;
4355 }
4356 else if((prop & 0xF) < LV_STYLE_ID_PTR) { /*Opa*/
4357 style_list->skip_trans = 1;
4358 style_list->ignore_cache = 1;
4359 obj->state = prev_state;
4360 lv_opa_t o1 = _lv_obj_get_style_opa(obj, part, prop);
4361 obj->state = new_state;
4362 lv_opa_t o2 = _lv_obj_get_style_opa(obj, part, prop);
4363 style_list->skip_trans = 0;
4364 style_list->ignore_cache = cahche_ori;
4365
4366 if(o1 == o2) return NULL;
4367
4368 obj->state = prev_state;
4369 o1 = _lv_obj_get_style_opa(obj, part, prop);
4370 obj->state = new_state;
4371 _lv_style_set_opa(style_trans, prop, o1); /*Be sure `trans_style` has a valid value */
4372
4373 tr = _lv_ll_ins_head(&LV_GC_ROOT(_lv_obj_style_trans_ll));
4374 LV_ASSERT_MEM(tr);
4375 if(tr == NULL) return NULL;
4376 tr->start_value._opa = o1;
4377 tr->end_value._opa = o2;
4378 }
4379 else { /*Ptr*/
4380 obj->state = prev_state;
4381 style_list->skip_trans = 1;
4382 style_list->ignore_cache = 1;
4383 const void * p1 = _lv_obj_get_style_ptr(obj, part, prop);
4384 obj->state = new_state;
4385 const void * p2 = _lv_obj_get_style_ptr(obj, part, prop);
4386 style_list->skip_trans = 0;
4387 style_list->ignore_cache = cahche_ori;
4388
4389 if(memcmp(&p1, &p2, sizeof(const void *)) == 0) return NULL;
4390 obj->state = prev_state;
4391 p1 = _lv_obj_get_style_ptr(obj, part, prop);
4392 obj->state = new_state;
4393 _lv_style_set_ptr(style_trans, prop, p1); /*Be sure `trans_style` has a valid value */
4394
4395 tr = _lv_ll_ins_head(&LV_GC_ROOT(_lv_obj_style_trans_ll));
4396 LV_ASSERT_MEM(tr);
4397 if(tr == NULL) return NULL;
4398 tr->start_value._ptr = p1;
4399 tr->end_value._ptr = p2;
4400 }
4401
4402 return tr;
4403 }
4404
4405 /**
4406 * Remove the transition from object's part's property.
4407 * - Remove the transition from `_lv_obj_style_trans_ll` and free it
4408 * - Delete pending transitions
4409 * @param obj pointer to an object which transition(s) should be removed
4410 * @param part a part of object or 0xFF to remove from all parts
4411 * @param prop a property or 0xFF to remove all properties
4412 * @param tr_limit delete transitions only "older" then this. `NULL` is not used
4413 */
4414 static void trans_del(lv_obj_t * obj, uint8_t part, lv_style_property_t prop, lv_style_trans_t * tr_limit)
4415 {
4416 lv_style_trans_t * tr;
4417 lv_style_trans_t * tr_prev;
4418 tr = _lv_ll_get_tail(&LV_GC_ROOT(_lv_obj_style_trans_ll));
4419 while(tr != NULL) {
4420 if(tr == tr_limit) break;
4421
4422 /*'tr' might be deleted, so get the next object while 'tr' is valid*/
4423 tr_prev = _lv_ll_get_prev(&LV_GC_ROOT(_lv_obj_style_trans_ll), tr);
4424
4425 if(tr->obj == obj && (part == tr->part || part == 0xFF) && (prop == tr->prop || prop == 0xFF)) {
4426 /* Remove the transitioned property from trans. style
4427 * to allow changing it by normal styles*/
4428 lv_style_list_t * list = lv_obj_get_style_list(tr->obj, tr->part);
4429 lv_style_t * style_trans = _lv_style_list_get_transition_style(list);
4430 lv_style_remove_prop(style_trans, tr->prop);
4431
4432 lv_anim_del(tr, NULL);
4433 _lv_ll_remove(&LV_GC_ROOT(_lv_obj_style_trans_ll), tr);
4434 lv_mem_free(tr);
4435 }
4436 tr = tr_prev;
4437 }
4438 }
4439
4440 static void trans_anim_cb(lv_style_trans_t * tr, lv_anim_value_t v)
4441 {
4442 lv_style_list_t * list = lv_obj_get_style_list(tr->obj, tr->part);
4443 lv_style_t * style = _lv_style_list_get_transition_style(list);
4444
4445 if((tr->prop & 0xF) < LV_STYLE_ID_COLOR) { /*Value*/
4446 lv_style_int_t x;
4447 if(v == 0) x = tr->start_value._int;
4448 else if(v == 255) x = tr->end_value._int;
4449 else x = tr->start_value._int + ((int32_t)((int32_t)(tr->end_value._int - tr->start_value._int) * v) >> 8);
4450 _lv_style_set_int(style, tr->prop, x);
4451 }
4452 else if((tr->prop & 0xF) < LV_STYLE_ID_OPA) { /*Color*/
4453 lv_color_t x;
4454 if(v <= 0) x = tr->start_value._color;
4455 else if(v >= 255) x = tr->end_value._color;
4456 else x = lv_color_mix(tr->end_value._color, tr->start_value._color, v);
4457 _lv_style_set_color(style, tr->prop, x);
4458 }
4459 else if((tr->prop & 0xF) < LV_STYLE_ID_PTR) { /*Opa*/
4460 lv_opa_t x;
4461 if(v <= 0) x = tr->start_value._opa;
4462 else if(v >= 255) x = tr->end_value._opa;
4463 else x = tr->start_value._opa + (((tr->end_value._opa - tr->start_value._opa) * v) >> 8);
4464 _lv_style_set_opa(style, tr->prop, x);
4465 }
4466 else {
4467 const void * x;
4468 if(v < 128) x = tr->start_value._ptr;
4469 else x = tr->end_value._ptr;
4470 _lv_style_set_ptr(style, tr->prop, x);
4471 }
4472 lv_obj_refresh_style(tr->obj, tr->part, tr->prop);
4473
4474 }
4475
4476 static void trans_anim_start_cb(lv_anim_t * a)
4477 {
4478 lv_style_trans_t * tr = a->var;
4479
4480 lv_style_property_t prop_tmp = tr->prop;
4481
4482 /*Start the animation from the current value*/
4483 if((prop_tmp & 0xF) < LV_STYLE_ID_COLOR) { /*Int*/
4484 tr->start_value._int = _lv_obj_get_style_int(tr->obj, tr->part, prop_tmp);
4485 }
4486 else if((prop_tmp & 0xF) < LV_STYLE_ID_OPA) { /*Color*/
4487 tr->start_value._color = _lv_obj_get_style_color(tr->obj, tr->part, prop_tmp);
4488 }
4489 else if((prop_tmp & 0xF) < LV_STYLE_ID_PTR) { /*Opa*/
4490 tr->start_value._opa = _lv_obj_get_style_opa(tr->obj, tr->part, prop_tmp);
4491 }
4492 else { /*Ptr*/
4493 tr->start_value._ptr = _lv_obj_get_style_ptr(tr->obj, tr->part, prop_tmp);
4494 }
4495
4496 /*Init prop to an invalid values to be sure `trans_del` won't delete this added `tr`*/
4497 tr->prop = 0;
4498 /*Delete the relate transition if any*/
4499 trans_del(tr->obj, tr->part, prop_tmp, tr);
4500
4501 tr->prop = prop_tmp;
4502
4503 }
4504
4505 static void trans_anim_ready_cb(lv_anim_t * a)
4506 {
4507 lv_style_trans_t * tr = a->var;
4508
4509 /* Remove the transitioned property from trans. style
4510 * if there no more transitions for this property
4511 * It allows changing it by normal styles*/
4512
4513 bool running = false;
4514 lv_style_trans_t * tr_i;
4515 _LV_LL_READ(LV_GC_ROOT(_lv_obj_style_trans_ll), tr_i) {
4516 if(tr_i != tr && tr_i->obj == tr->obj && tr_i->part == tr->part && tr_i->prop == tr->prop) {
4517 running = true;
4518 }
4519 }
4520
4521 if(!running) {
4522 lv_style_list_t * list = lv_obj_get_style_list(tr->obj, tr->part);
4523 lv_style_t * style_trans = _lv_style_list_get_transition_style(list);
4524 lv_style_remove_prop(style_trans, tr->prop);
4525 }
4526
4527 _lv_ll_remove(&LV_GC_ROOT(_lv_obj_style_trans_ll), tr);
4528 lv_mem_free(tr);
4529 }
4530
4531 static void opa_scale_anim(lv_obj_t * obj, lv_anim_value_t v)
4532 {
4533 lv_obj_set_style_local_opa_scale(obj, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, v);
4534 }
4535
4536 static void fade_in_anim_ready(lv_anim_t * a)
4537 {
4538 lv_style_remove_prop(lv_obj_get_local_style(a->var, LV_OBJ_PART_MAIN), LV_STYLE_OPA_SCALE);
4539 }
4540
4541 #endif
4542
4543 static void lv_event_mark_deleted(lv_obj_t * obj)
4544 {
4545 lv_event_temp_data_t * t = event_temp_data_head;
4546
4547 while(t) {
4548 if(t->obj == obj) t->deleted = true;
4549 t = t->prev;
4550 }
4551 }
4552
4553 static bool obj_valid_child(const lv_obj_t * parent, const lv_obj_t * obj_to_find)
4554 {
4555 /*Check all children of `parent`*/
4556 lv_obj_t * child;
4557 _LV_LL_READ(parent->child_ll, child) {
4558 if(child == obj_to_find) return true;
4559
4560 /*Check the children*/
4561 bool found = obj_valid_child(child, obj_to_find);
4562 if(found) return true;
4563 }
4564
4565 return false;
4566 }
4567
4568 static bool style_prop_is_cacheble(lv_style_property_t prop)
4569 {
4570
4571 switch(prop) {
4572 case LV_STYLE_PROP_ALL:
4573 case LV_STYLE_CLIP_CORNER:
4574 case LV_STYLE_TEXT_LETTER_SPACE:
4575 case LV_STYLE_TEXT_LINE_SPACE:
4576 case LV_STYLE_TEXT_FONT:
4577 case LV_STYLE_TRANSFORM_ANGLE:
4578 case LV_STYLE_TRANSFORM_WIDTH:
4579 case LV_STYLE_TRANSFORM_HEIGHT:
4580 case LV_STYLE_TRANSFORM_ZOOM:
4581 case LV_STYLE_BORDER_WIDTH:
4582 case LV_STYLE_OUTLINE_WIDTH:
4583 case LV_STYLE_RADIUS:
4584 case LV_STYLE_SHADOW_WIDTH:
4585 case LV_STYLE_OPA_SCALE:
4586 case LV_STYLE_BG_OPA:
4587 case LV_STYLE_BORDER_SIDE:
4588 case LV_STYLE_BORDER_POST:
4589 case LV_STYLE_IMAGE_RECOLOR_OPA:
4590 case LV_STYLE_VALUE_STR:
4591 case LV_STYLE_PATTERN_IMAGE:
4592 case LV_STYLE_PAD_TOP:
4593 case LV_STYLE_PAD_BOTTOM:
4594 case LV_STYLE_PAD_LEFT:
4595 case LV_STYLE_PAD_RIGHT:
4596 case LV_STYLE_MARGIN_TOP:
4597 case LV_STYLE_MARGIN_BOTTOM:
4598 case LV_STYLE_MARGIN_LEFT:
4599 case LV_STYLE_MARGIN_RIGHT:
4600 case LV_STYLE_BG_BLEND_MODE:
4601 case LV_STYLE_BORDER_BLEND_MODE:
4602 case LV_STYLE_IMAGE_BLEND_MODE:
4603 case LV_STYLE_LINE_BLEND_MODE:
4604 case LV_STYLE_OUTLINE_BLEND_MODE:
4605 case LV_STYLE_PATTERN_BLEND_MODE:
4606 case LV_STYLE_SHADOW_BLEND_MODE:
4607 case LV_STYLE_TEXT_BLEND_MODE:
4608 case LV_STYLE_VALUE_BLEND_MODE:
4609 return true;
4610 break;
4611 default:
4612 return false;
4613 }
4614 }
4615
4616 /**
4617 * Update the cache of style list
4618 * @param obj pointer to an obejct
4619 * @param part the part of the object
4620 * @param prop the property which triggered the update
4621 */
4622 static void update_style_cache(lv_obj_t * obj, uint8_t part, uint16_t prop)
4623 {
4624 if(style_prop_is_cacheble(prop) == false) return;
4625
4626 lv_style_list_t * list = lv_obj_get_style_list(obj, part);
4627
4628 bool ignore_cache_ori = list->ignore_cache;
4629 list->ignore_cache = 1;
4630
4631 #if LV_USE_OPA_SCALE
4632 list->opa_scale_cover = lv_obj_get_style_opa_scale(obj, part) == LV_OPA_COVER ? 1 : 0;
4633 #else
4634 list->opa_scale_cover = 1;
4635 #endif
4636 list->text_decor_none = lv_obj_get_style_text_decor(obj, part) == LV_TEXT_DECOR_NONE ? 1 : 0;
4637 list->text_font_normal = lv_obj_get_style_text_font(obj, part) == LV_THEME_DEFAULT_FONT_NORMAL ? 1 : 0;
4638
4639 list->text_space_zero = 1;
4640 if(lv_obj_get_style_text_letter_space(obj, part) != 0 ||
4641 lv_obj_get_style_text_line_space(obj, part) != 0) {
4642 list->text_space_zero = 0;
4643 }
4644
4645
4646 lv_opa_t bg_opa = lv_obj_get_style_bg_opa(obj, part);
4647 list->bg_opa_transp = bg_opa == LV_OPA_TRANSP ? 1 : 0;
4648 list->bg_opa_cover = bg_opa == LV_OPA_COVER ? 1 : 0;
4649
4650 list->border_width_zero = lv_obj_get_style_border_width(obj, part) == 0 ? 1 : 0;
4651 list->border_side_full = lv_obj_get_style_border_side(obj, part) == LV_BORDER_SIDE_FULL ? 1 : 0;
4652 list->border_post_off = lv_obj_get_style_border_post(obj, part) == 0 ? 1 : 0;
4653 list->clip_corner_off = lv_obj_get_style_clip_corner(obj, part) == false ? 1 : 0;
4654 list->img_recolor_opa_transp = lv_obj_get_style_image_recolor_opa(obj, part) == LV_OPA_TRANSP ? 1 : 0;
4655 list->outline_width_zero = lv_obj_get_style_outline_width(obj, part) == 0 ? 1 : 0;
4656 list->pattern_img_null = lv_obj_get_style_pattern_image(obj, part) == NULL ? 1 : 0;
4657 list->radius_zero = lv_obj_get_style_radius(obj, part) == 0 ? 1 : 0;
4658 list->shadow_width_zero = lv_obj_get_style_shadow_width(obj, part) == 0 ? 1 : 0;
4659 list->value_txt_str = lv_obj_get_style_value_str(obj, part) == NULL ? 1 : 0;
4660
4661
4662 list->transform_all_zero = 1;
4663 if(lv_obj_get_style_transform_angle(obj, part) != 0 ||
4664 lv_obj_get_style_transform_width(obj, part) != 0 ||
4665 lv_obj_get_style_transform_height(obj, part) != 0 ||
4666 lv_obj_get_style_transform_zoom(obj, part) != LV_IMG_ZOOM_NONE) {
4667 list->transform_all_zero = 0;
4668 }
4669
4670 list->pad_all_zero = 1;
4671 if(lv_obj_get_style_pad_top(obj, part) != 0 ||
4672 lv_obj_get_style_pad_bottom(obj, part) != 0 ||
4673 lv_obj_get_style_pad_left(obj, part) != 0 ||
4674 lv_obj_get_style_pad_right(obj, part) != 0) {
4675 list->pad_all_zero = 0;
4676 }
4677
4678 list->margin_all_zero = 1;
4679 if(lv_obj_get_style_margin_top(obj, part) != 0 ||
4680 lv_obj_get_style_margin_bottom(obj, part) != 0 ||
4681 lv_obj_get_style_margin_left(obj, part) != 0 ||
4682 lv_obj_get_style_margin_right(obj, part) != 0) {
4683 list->margin_all_zero = 0;
4684 }
4685
4686 list->blend_mode_all_normal = 1;
4687 #if LV_USE_BLEND_MODES
4688 if(lv_obj_get_style_bg_blend_mode(obj, part) != LV_BLEND_MODE_NORMAL ||
4689 lv_obj_get_style_border_blend_mode(obj, part) != LV_BLEND_MODE_NORMAL ||
4690 lv_obj_get_style_pattern_blend_mode(obj, part) != LV_BLEND_MODE_NORMAL ||
4691 lv_obj_get_style_outline_blend_mode(obj, part) != LV_BLEND_MODE_NORMAL ||
4692 lv_obj_get_style_value_blend_mode(obj, part) != LV_BLEND_MODE_NORMAL ||
4693 lv_obj_get_style_text_blend_mode(obj, part) != LV_BLEND_MODE_NORMAL ||
4694 lv_obj_get_style_line_blend_mode(obj, part) != LV_BLEND_MODE_NORMAL ||
4695 lv_obj_get_style_image_blend_mode(obj, part) != LV_BLEND_MODE_NORMAL ||
4696 lv_obj_get_style_shadow_blend_mode(obj, part) != LV_BLEND_MODE_NORMAL) {
4697 list->blend_mode_all_normal = 0;
4698 }
4699 #endif
4700 list->ignore_cache = ignore_cache_ori;
4701 list->valid_cache = 1;
4702 }
4703
4704 /**
4705 * Update the cache of style list
4706 * @param obj pointer to an object
4707 * @param part the part of the object
4708 */
4709 static void update_style_cache_children(lv_obj_t * obj)
4710 {
4711 uint8_t part;
4712 for(part = 0; part != _LV_OBJ_PART_REAL_LAST; part++) {
4713 lv_style_list_t * list = lv_obj_get_style_list(obj, part);
4714 if(list == NULL) break;
4715
4716 bool ignore_cache_ori = list->ignore_cache;
4717 list->ignore_cache = 1;
4718
4719 list->opa_scale_cover = lv_obj_get_style_opa_scale(obj, part) == LV_OPA_COVER ? 1 : 0;
4720 list->text_decor_none = lv_obj_get_style_text_decor(obj, part) == LV_TEXT_DECOR_NONE ? 1 : 0;
4721 list->text_font_normal = lv_obj_get_style_text_font(obj, part) == lv_theme_get_font_normal() ? 1 : 0;
4722 list->img_recolor_opa_transp = lv_obj_get_style_image_recolor_opa(obj, part) == LV_OPA_TRANSP ? 1 : 0;
4723
4724 list->text_space_zero = 1;
4725 if(lv_obj_get_style_text_letter_space(obj, part) != 0 ||
4726 lv_obj_get_style_text_line_space(obj, part) != 0) {
4727 list->text_space_zero = 0;
4728 }
4729
4730 list->ignore_cache = ignore_cache_ori;
4731 }
4732
4733 lv_obj_t * child = lv_obj_get_child(obj, NULL);
4734 while(child) {
4735 update_style_cache_children(child);
4736 child = lv_obj_get_child(obj, child);
4737 }
4738
4739 }
4740
4741 /**
4742 * Mark the object and all of it's children's style lists as invalid.
4743 * The cache will be updated when a cached property asked nest time
4744 * @param obj pointer to an object
4745 */
4746 static void invalidate_style_cache(lv_obj_t * obj, uint8_t part, lv_style_property_t prop)
4747 {
4748 if(style_prop_is_cacheble(prop) == false) return;
4749
4750 if(part != LV_OBJ_PART_ALL) {
4751 lv_style_list_t * list = lv_obj_get_style_list(obj, part);
4752 if(list == NULL) return;
4753 list->valid_cache = 0;
4754 }
4755 else {
4756
4757 for(part = 0; part < _LV_OBJ_PART_REAL_FIRST; part++) {
4758 lv_style_list_t * list = lv_obj_get_style_list(obj, part);
4759 if(list == NULL) break;
4760 list->valid_cache = 0;
4761 }
4762 for(part = _LV_OBJ_PART_REAL_FIRST; part < 0xFF; part++) {
4763 lv_style_list_t * list = lv_obj_get_style_list(obj, part);
4764 if(list == NULL) break;
4765 list->valid_cache = 0;
4766 }
4767 }
4768
4769 lv_obj_t * child = lv_obj_get_child(obj, NULL);
4770 while(child) {
4771 update_style_cache_children(child);
4772 child = lv_obj_get_child(obj, child);
4773 }
4774 }
4775
4776 static void style_snapshot(lv_obj_t * obj, uint8_t part, style_snapshot_t * shot)
4777 {
4778 _lv_obj_disable_style_caching(obj, true);
4779 _lv_memset_00(shot, sizeof(style_snapshot_t));
4780 lv_draw_rect_dsc_init(&shot->rect);
4781 lv_draw_label_dsc_init(&shot->label);
4782 lv_draw_img_dsc_init(&shot->img);
4783 lv_draw_line_dsc_init(&shot->line);
4784
4785 lv_style_list_t * list = lv_obj_get_style_list(obj, part);
4786 bool trans_ori = list->skip_trans;
4787 list->skip_trans = 1;
4788
4789 lv_obj_init_draw_rect_dsc(obj, part, &shot->rect);
4790 lv_obj_init_draw_label_dsc(obj, part, &shot->label);
4791 lv_obj_init_draw_img_dsc(obj, part, &shot->img);
4792 lv_obj_init_draw_line_dsc(obj, part, &shot->line);
4793
4794
4795 shot->pad_top = lv_obj_get_style_pad_top(obj, part);
4796 shot->pad_bottom = lv_obj_get_style_pad_bottom(obj, part);
4797 shot->pad_right = lv_obj_get_style_pad_right(obj, part);
4798 shot->pad_left = lv_obj_get_style_pad_left(obj, part);
4799 shot->pad_inner = lv_obj_get_style_pad_inner(obj, part);
4800 shot->margin_top = lv_obj_get_style_margin_top(obj, part);
4801 shot->margin_bottom = lv_obj_get_style_margin_bottom(obj, part);
4802 shot->margin_left = lv_obj_get_style_margin_left(obj, part);
4803 shot->margin_right = lv_obj_get_style_margin_right(obj, part);
4804 shot->size = lv_obj_get_style_size(obj, part);
4805 shot->transform_width = lv_obj_get_style_transform_width(obj, part);
4806 shot->transform_height = lv_obj_get_style_transform_height(obj, part);
4807 shot->transform_angle = lv_obj_get_style_transform_angle(obj, part);
4808 shot->transform_zoom = lv_obj_get_style_transform_zoom(obj, part);
4809 shot->scale_width = lv_obj_get_style_scale_width(obj, part);
4810 shot->scale_border_width = lv_obj_get_style_scale_border_width(obj, part);
4811 shot->scale_end_border_width = lv_obj_get_style_scale_end_border_width(obj, part);
4812 shot->scale_end_line_width = lv_obj_get_style_scale_end_line_width(obj, part);
4813 shot->scale_grad_color = lv_obj_get_style_scale_grad_color(obj, part);
4814 shot->scale_end_color = lv_obj_get_style_scale_end_color(obj, part);
4815 shot->opa_scale = lv_obj_get_style_opa_scale(obj, part);
4816 shot->clip_corder = lv_obj_get_style_clip_corner(obj, part);
4817 shot->border_post = lv_obj_get_style_border_post(obj, part);
4818
4819 _lv_obj_disable_style_caching(obj, false);
4820 list->skip_trans = trans_ori;
4821 }
4822
4823 static style_snapshot_res_t style_snapshot_compare(style_snapshot_t * shot1, style_snapshot_t * shot2)
4824 {
4825 if(memcmp(shot1, shot2, sizeof(style_snapshot_t)) == 0) return STYLE_COMPARE_SAME;
4826
4827
4828 if(shot1->pad_top != shot2->pad_top) return STYLE_COMPARE_DIFF;
4829 if(shot1->pad_bottom != shot2->pad_bottom) return STYLE_COMPARE_DIFF;
4830 if(shot1->pad_left != shot2->pad_right) return STYLE_COMPARE_DIFF;
4831 if(shot1->pad_right != shot2->pad_right) return STYLE_COMPARE_DIFF;
4832 if(shot1->pad_top != shot2->pad_top) return STYLE_COMPARE_DIFF;
4833 if(shot1->pad_inner != shot2->pad_inner) return STYLE_COMPARE_DIFF;
4834 if(shot1->margin_top != shot2->margin_top) return STYLE_COMPARE_DIFF;
4835 if(shot1->margin_bottom != shot2->margin_bottom) return STYLE_COMPARE_DIFF;
4836 if(shot1->margin_left != shot2->margin_right) return STYLE_COMPARE_DIFF;
4837 if(shot1->margin_right != shot2->margin_right) return STYLE_COMPARE_DIFF;
4838 if(shot1->margin_top != shot2->margin_top) return STYLE_COMPARE_DIFF;
4839 if(shot1->transform_width != shot2->transform_width) return STYLE_COMPARE_DIFF;
4840 if(shot1->transform_height != shot2->transform_height) return STYLE_COMPARE_DIFF;
4841 if(shot1->transform_angle != shot2->transform_angle) return STYLE_COMPARE_DIFF;
4842 if(shot1->transform_zoom != shot2->transform_zoom) return STYLE_COMPARE_DIFF;
4843 if(shot1->rect.outline_width != shot2->rect.outline_width) return STYLE_COMPARE_DIFF;
4844 if(shot1->rect.outline_pad != shot2->rect.outline_pad) return STYLE_COMPARE_DIFF;
4845 if(shot1->rect.value_font != shot2->rect.value_font) return STYLE_COMPARE_DIFF;
4846 if(shot1->rect.value_align != shot2->rect.value_align) return STYLE_COMPARE_DIFF;
4847 if(shot1->rect.value_font != shot2->rect.value_font) return STYLE_COMPARE_DIFF;
4848 if(shot1->rect.shadow_spread != shot2->rect.shadow_spread) return STYLE_COMPARE_DIFF;
4849 if(shot1->rect.shadow_width != shot2->rect.shadow_width) return STYLE_COMPARE_DIFF;
4850 if(shot1->rect.shadow_ofs_x != shot2->rect.shadow_ofs_x) return STYLE_COMPARE_DIFF;
4851 if(shot1->rect.shadow_ofs_y != shot2->rect.shadow_ofs_y) return STYLE_COMPARE_DIFF;
4852
4853 /*If not returned earlier its just a visual difference, a simple redraw is enough*/
4854 return STYLE_COMPARE_VISUAL_DIFF;
4855 }
4856