1 /**
2  * @file lv_obj.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_obj_private.h"
10 #include "../misc/lv_event_private.h"
11 #include "../misc/lv_area_private.h"
12 #include "lv_obj_style_private.h"
13 #include "lv_obj_event_private.h"
14 #include "lv_obj_class_private.h"
15 #include "../indev/lv_indev.h"
16 #include "../indev/lv_indev_private.h"
17 #include "lv_refr.h"
18 #include "lv_group.h"
19 #include "../display/lv_display.h"
20 #include "../display/lv_display_private.h"
21 #include "../themes/lv_theme.h"
22 #include "../misc/lv_assert.h"
23 #include "../misc/lv_math.h"
24 #include "../misc/lv_log.h"
25 #include "../misc/lv_types.h"
26 #include "../tick/lv_tick.h"
27 #include "../stdlib/lv_string.h"
28 #include "lv_obj_draw_private.h"
29 
30 /*********************
31  *      DEFINES
32  *********************/
33 #define MY_CLASS (&lv_obj_class)
34 #define LV_OBJ_DEF_WIDTH    (LV_DPX(100))
35 #define LV_OBJ_DEF_HEIGHT   (LV_DPX(50))
36 #define STYLE_TRANSITION_MAX 32
37 
38 /**********************
39  *      TYPEDEFS
40  **********************/
41 
42 /**********************
43  *  STATIC PROTOTYPES
44  **********************/
45 static void lv_obj_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
46 static void lv_obj_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
47 static void lv_obj_draw(lv_event_t * e);
48 static void lv_obj_event(const lv_obj_class_t * class_p, lv_event_t * e);
49 static void draw_scrollbar(lv_obj_t * obj, lv_layer_t * layer);
50 static lv_result_t scrollbar_init_draw_dsc(lv_obj_t * obj, lv_draw_rect_dsc_t * dsc);
51 static bool obj_valid_child(const lv_obj_t * parent, const lv_obj_t * obj_to_find);
52 static void update_obj_state(lv_obj_t * obj, lv_state_t new_state);
53 static void null_on_delete_cb(lv_event_t * e);
54 
55 #if LV_USE_OBJ_PROPERTY
56     static lv_result_t lv_obj_set_any(lv_obj_t *, lv_prop_id_t, const lv_property_t *);
57     static lv_result_t lv_obj_get_any(const lv_obj_t *, lv_prop_id_t, lv_property_t *);
58 #endif
59 
60 /**********************
61  *  STATIC VARIABLES
62  **********************/
63 #if LV_USE_OBJ_PROPERTY
64 static const lv_property_ops_t properties[] = {
65     {
66         .id = LV_PROPERTY_OBJ_PARENT,
67         .setter = lv_obj_set_parent,
68         .getter = lv_obj_get_parent,
69     },
70     {
71         .id = LV_PROPERTY_OBJ_X,
72         .setter = lv_obj_set_x,
73         .getter = lv_obj_get_x,
74     },
75     {
76         .id = LV_PROPERTY_OBJ_Y,
77         .setter = lv_obj_set_y,
78         .getter = lv_obj_get_y,
79     },
80     {
81         .id = LV_PROPERTY_OBJ_W,
82         .setter = lv_obj_set_width,
83         .getter = lv_obj_get_width,
84     },
85     {
86         .id = LV_PROPERTY_OBJ_H,
87         .setter = lv_obj_set_height,
88         .getter = lv_obj_get_height,
89     },
90     {
91         .id = LV_PROPERTY_OBJ_CONTENT_WIDTH,
92         .setter = lv_obj_set_content_width,
93         .getter = lv_obj_get_content_width,
94     },
95     {
96         .id = LV_PROPERTY_OBJ_CONTENT_HEIGHT,
97         .setter = lv_obj_set_content_height,
98         .getter = lv_obj_get_content_height,
99     },
100     {
101         .id = LV_PROPERTY_OBJ_LAYOUT,
102         .setter = lv_obj_set_layout,
103     },
104     {
105         .id = LV_PROPERTY_OBJ_ALIGN,
106         .setter = lv_obj_set_align,
107     },
108     {
109         .id = LV_PROPERTY_OBJ_SCROLLBAR_MODE,
110         .setter = lv_obj_set_scrollbar_mode,
111         .getter = lv_obj_get_scrollbar_mode,
112     },
113     {
114         .id = LV_PROPERTY_OBJ_SCROLL_DIR,
115         .setter = lv_obj_set_scroll_dir,
116         .getter = lv_obj_get_scroll_dir,
117     },
118     {
119         .id = LV_PROPERTY_OBJ_SCROLL_SNAP_X,
120         .setter = lv_obj_set_scroll_snap_x,
121         .getter = lv_obj_get_scroll_snap_x,
122     },
123     {
124         .id = LV_PROPERTY_OBJ_SCROLL_SNAP_Y,
125         .setter = lv_obj_set_scroll_snap_y,
126         .getter = lv_obj_get_scroll_snap_y,
127     },
128     {
129         .id = LV_PROPERTY_OBJ_SCROLL_TOP,
130         .getter = lv_obj_get_scroll_top,
131     },
132     {
133         .id = LV_PROPERTY_OBJ_SCROLL_BOTTOM,
134         .getter = lv_obj_get_scroll_bottom,
135     },
136     {
137         .id = LV_PROPERTY_OBJ_SCROLL_LEFT,
138         .getter = lv_obj_get_scroll_left,
139     },
140     {
141         .id = LV_PROPERTY_OBJ_SCROLL_RIGHT,
142         .getter = lv_obj_get_scroll_right,
143     },
144     {
145         .id = LV_PROPERTY_OBJ_SCROLL_END,
146         .getter = lv_obj_get_scroll_end,
147     },
148     {
149         .id = LV_PROPERTY_OBJ_EXT_DRAW_SIZE,
150         .getter = lv_obj_get_ext_draw_size,
151     },
152     {
153         .id = LV_PROPERTY_OBJ_EVENT_COUNT,
154         .getter = lv_obj_get_event_count,
155     },
156     {
157         .id = LV_PROPERTY_OBJ_SCREEN,
158         .getter = lv_obj_get_screen,
159     },
160     {
161         .id = LV_PROPERTY_OBJ_DISPLAY,
162         .getter = lv_obj_get_display,
163     },
164     {
165         .id = LV_PROPERTY_OBJ_CHILD_COUNT,
166         .getter = lv_obj_get_child_count,
167     },
168     {
169         .id = LV_PROPERTY_OBJ_INDEX,
170         .getter = lv_obj_get_index,
171     },
172     {
173         .id = LV_PROPERTY_ID_ANY,
174         .setter = lv_obj_set_any,
175         .getter = lv_obj_get_any,
176     }
177 };
178 #endif
179 
180 const lv_obj_class_t lv_obj_class = {
181     .constructor_cb = lv_obj_constructor,
182     .destructor_cb = lv_obj_destructor,
183     .event_cb = lv_obj_event,
184     .width_def = LV_DPI_DEF,
185     .height_def = LV_DPI_DEF,
186     .editable = LV_OBJ_CLASS_EDITABLE_FALSE,
187     .group_def = LV_OBJ_CLASS_GROUP_DEF_FALSE,
188     .instance_size = (sizeof(lv_obj_t)),
189     .base_class = NULL,
190     .name = "obj",
191 #if LV_USE_OBJ_PROPERTY
192     .prop_index_start = LV_PROPERTY_OBJ_START,
193     .prop_index_end = LV_PROPERTY_OBJ_END,
194     .properties = properties,
195     .properties_count = sizeof(properties) / sizeof(properties[0]),
196 
197 #if LV_USE_OBJ_PROPERTY_NAME
198     .property_names = lv_obj_property_names,
199     .names_count = sizeof(lv_obj_property_names) / sizeof(lv_property_name_t),
200 #endif
201 
202 #endif
203 };
204 
205 /**********************
206  *      MACROS
207  **********************/
208 
209 /**********************
210  *   GLOBAL FUNCTIONS
211  **********************/
212 
lv_obj_create(lv_obj_t * parent)213 lv_obj_t * lv_obj_create(lv_obj_t * parent)
214 {
215     LV_LOG_INFO("begin");
216     lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
217     LV_ASSERT_NULL(obj);
218     if(obj == NULL) return NULL;
219     lv_obj_class_init_obj(obj);
220     return obj;
221 }
222 
223 /*=====================
224  * Setter functions
225  *====================*/
226 
227 /*-----------------
228  * Attribute set
229  *----------------*/
230 
lv_obj_add_flag(lv_obj_t * obj,lv_obj_flag_t f)231 void lv_obj_add_flag(lv_obj_t * obj, lv_obj_flag_t f)
232 {
233     LV_ASSERT_OBJ(obj, MY_CLASS);
234     if(lv_obj_has_flag(obj, f)) /*Check if all flags are set*/
235         return;
236 
237     bool was_on_layout = lv_obj_is_layout_positioned(obj);
238 
239     /* We must invalidate the area occupied by the object before we hide it as calls to invalidate hidden objects are ignored */
240     if(f & LV_OBJ_FLAG_HIDDEN) lv_obj_invalidate(obj);
241 
242     obj->flags |= f;
243 
244     if(f & LV_OBJ_FLAG_HIDDEN) {
245         if(lv_obj_has_state(obj, LV_STATE_FOCUSED)) {
246             lv_group_t * group = lv_obj_get_group(obj);
247             if(group != NULL) {
248                 lv_group_focus_next(group);
249                 lv_obj_t * next_obj = lv_group_get_focused(group);
250                 if(next_obj != NULL) {
251                     lv_obj_invalidate(next_obj);
252                 }
253             }
254         }
255     }
256 
257     if((was_on_layout != lv_obj_is_layout_positioned(obj)) || (f & (LV_OBJ_FLAG_LAYOUT_1 |  LV_OBJ_FLAG_LAYOUT_2))) {
258         lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj));
259         lv_obj_mark_layout_as_dirty(obj);
260     }
261 
262     if(f & LV_OBJ_FLAG_SCROLLABLE) {
263         lv_area_t hor_area, ver_area;
264         lv_obj_get_scrollbar_area(obj, &hor_area, &ver_area);
265         lv_obj_invalidate_area(obj, &hor_area);
266         lv_obj_invalidate_area(obj, &ver_area);
267     }
268 }
269 
lv_obj_remove_flag(lv_obj_t * obj,lv_obj_flag_t f)270 void lv_obj_remove_flag(lv_obj_t * obj, lv_obj_flag_t f)
271 {
272     LV_ASSERT_OBJ(obj, MY_CLASS);
273     if(!lv_obj_has_flag_any(obj, f))
274         return;
275 
276     bool was_on_layout = lv_obj_is_layout_positioned(obj);
277     if(f & LV_OBJ_FLAG_SCROLLABLE) {
278         lv_area_t hor_area, ver_area;
279         lv_obj_get_scrollbar_area(obj, &hor_area, &ver_area);
280         lv_obj_invalidate_area(obj, &hor_area);
281         lv_obj_invalidate_area(obj, &ver_area);
282     }
283 
284     obj->flags &= (~f);
285 
286     if(f & LV_OBJ_FLAG_HIDDEN) {
287         lv_obj_invalidate(obj);
288         lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj));
289         lv_obj_mark_layout_as_dirty(obj);
290     }
291 
292     if((was_on_layout != lv_obj_is_layout_positioned(obj)) || (f & (LV_OBJ_FLAG_LAYOUT_1 |  LV_OBJ_FLAG_LAYOUT_2))) {
293         lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj));
294     }
295 
296 }
297 
lv_obj_update_flag(lv_obj_t * obj,lv_obj_flag_t f,bool v)298 void lv_obj_update_flag(lv_obj_t * obj, lv_obj_flag_t f, bool v)
299 {
300     if(v) lv_obj_add_flag(obj, f);
301     else lv_obj_remove_flag(obj, f);
302 }
303 
lv_obj_add_state(lv_obj_t * obj,lv_state_t state)304 void lv_obj_add_state(lv_obj_t * obj, lv_state_t state)
305 {
306     LV_ASSERT_OBJ(obj, MY_CLASS);
307 
308     lv_state_t new_state = obj->state | state;
309     if(obj->state != new_state) {
310         update_obj_state(obj, new_state);
311     }
312 }
313 
lv_obj_remove_state(lv_obj_t * obj,lv_state_t state)314 void lv_obj_remove_state(lv_obj_t * obj, lv_state_t state)
315 {
316     LV_ASSERT_OBJ(obj, MY_CLASS);
317 
318     lv_state_t new_state = obj->state & (~state);
319     if(obj->state != new_state) {
320         update_obj_state(obj, new_state);
321     }
322 }
323 
lv_obj_set_state(lv_obj_t * obj,lv_state_t state,bool v)324 void lv_obj_set_state(lv_obj_t * obj, lv_state_t state, bool v)
325 {
326     if(v) lv_obj_add_state(obj, state);
327     else lv_obj_remove_state(obj, state);
328 }
329 
330 /*=======================
331  * Getter functions
332  *======================*/
333 
lv_obj_has_flag(const lv_obj_t * obj,lv_obj_flag_t f)334 bool lv_obj_has_flag(const lv_obj_t * obj, lv_obj_flag_t f)
335 {
336     LV_ASSERT_OBJ(obj, MY_CLASS);
337 
338     return (obj->flags & f)  == f;
339 }
340 
lv_obj_has_flag_any(const lv_obj_t * obj,lv_obj_flag_t f)341 bool lv_obj_has_flag_any(const lv_obj_t * obj, lv_obj_flag_t f)
342 {
343     LV_ASSERT_OBJ(obj, MY_CLASS);
344 
345     return !!(obj->flags & f);
346 }
347 
lv_obj_get_state(const lv_obj_t * obj)348 lv_state_t lv_obj_get_state(const lv_obj_t * obj)
349 {
350     LV_ASSERT_OBJ(obj, MY_CLASS);
351 
352     return obj->state;
353 }
354 
lv_obj_has_state(const lv_obj_t * obj,lv_state_t state)355 bool lv_obj_has_state(const lv_obj_t * obj, lv_state_t state)
356 {
357     LV_ASSERT_OBJ(obj, MY_CLASS);
358 
359     return !!(obj->state & state);
360 }
361 
lv_obj_get_group(const lv_obj_t * obj)362 lv_group_t * lv_obj_get_group(const lv_obj_t * obj)
363 {
364     LV_ASSERT_OBJ(obj, MY_CLASS);
365 
366     if(obj->spec_attr) return obj->spec_attr->group_p;
367     else return NULL;
368 }
369 
370 /*-------------------
371  * OTHER FUNCTIONS
372  *------------------*/
373 
lv_obj_allocate_spec_attr(lv_obj_t * obj)374 void lv_obj_allocate_spec_attr(lv_obj_t * obj)
375 {
376     LV_ASSERT_OBJ(obj, MY_CLASS);
377 
378     if(obj->spec_attr == NULL) {
379         obj->spec_attr = lv_malloc_zeroed(sizeof(lv_obj_spec_attr_t));
380         LV_ASSERT_MALLOC(obj->spec_attr);
381         if(obj->spec_attr == NULL) return;
382 
383         obj->spec_attr->scroll_dir = LV_DIR_ALL;
384         obj->spec_attr->scrollbar_mode = LV_SCROLLBAR_MODE_AUTO;
385     }
386 }
387 
lv_obj_check_type(const lv_obj_t * obj,const lv_obj_class_t * class_p)388 bool lv_obj_check_type(const lv_obj_t * obj, const lv_obj_class_t * class_p)
389 {
390     if(obj == NULL) return false;
391     return obj->class_p == class_p;
392 }
393 
lv_obj_has_class(const lv_obj_t * obj,const lv_obj_class_t * class_p)394 bool lv_obj_has_class(const lv_obj_t * obj, const lv_obj_class_t * class_p)
395 {
396     const lv_obj_class_t * obj_class = obj->class_p;
397     while(obj_class) {
398         if(obj_class == class_p) return true;
399         obj_class = obj_class->base_class;
400     }
401 
402     return false;
403 }
404 
lv_obj_get_class(const lv_obj_t * obj)405 const lv_obj_class_t * lv_obj_get_class(const lv_obj_t * obj)
406 {
407     return obj->class_p;
408 }
409 
lv_obj_is_valid(const lv_obj_t * obj)410 bool lv_obj_is_valid(const lv_obj_t * obj)
411 {
412     lv_display_t * disp = lv_display_get_next(NULL);
413     while(disp) {
414         uint32_t i;
415         for(i = 0; i < disp->screen_cnt; i++) {
416             if(disp->screens[i] == obj) return true;
417             bool found = obj_valid_child(disp->screens[i], obj);
418             if(found) return true;
419         }
420 
421         disp = lv_display_get_next(disp);
422     }
423 
424     return false;
425 }
426 
lv_obj_null_on_delete(lv_obj_t ** obj_ptr)427 void lv_obj_null_on_delete(lv_obj_t ** obj_ptr)
428 {
429     lv_obj_add_event_cb(*obj_ptr, null_on_delete_cb, LV_EVENT_DELETE, obj_ptr);
430 }
431 
432 #if LV_USE_OBJ_ID
lv_obj_get_id(const lv_obj_t * obj)433 void * lv_obj_get_id(const lv_obj_t * obj)
434 {
435     LV_ASSERT_NULL(obj);
436     return obj->id;
437 }
438 
lv_obj_get_child_by_id(const lv_obj_t * obj,const void * id)439 lv_obj_t * lv_obj_get_child_by_id(const lv_obj_t * obj, const void * id)
440 {
441     if(obj == NULL) obj = lv_display_get_screen_active(NULL);
442     if(obj == NULL) return NULL;
443 
444     uint32_t i;
445     uint32_t child_cnt = lv_obj_get_child_count(obj);
446     for(i = 0; i < child_cnt; i++) {
447         lv_obj_t * child = obj->spec_attr->children[i];
448         if(lv_obj_id_compare(child->id, id) == 0) return child;
449     }
450 
451     /*Search children*/
452     for(i = 0; i < child_cnt; i++) {
453         lv_obj_t * child = obj->spec_attr->children[i];
454         lv_obj_t * found = lv_obj_get_child_by_id(child, id);
455         if(found != NULL) return found;
456     }
457 
458     return NULL;
459 }
460 #endif
461 
lv_obj_set_user_data(lv_obj_t * obj,void * user_data)462 void lv_obj_set_user_data(lv_obj_t * obj, void * user_data)
463 {
464     obj->user_data = user_data;
465 }
466 
lv_obj_get_user_data(lv_obj_t * obj)467 void * lv_obj_get_user_data(lv_obj_t * obj)
468 {
469     return obj->user_data;
470 }
471 
472 /**********************
473  *   STATIC FUNCTIONS
474  **********************/
475 
lv_obj_constructor(const lv_obj_class_t * class_p,lv_obj_t * obj)476 static void lv_obj_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
477 {
478     LV_UNUSED(class_p);
479     LV_TRACE_OBJ_CREATE("begin");
480 
481     lv_obj_t * parent = obj->parent;
482     if(parent) {
483         int32_t sl = lv_obj_get_scroll_left(parent);
484         int32_t st = lv_obj_get_scroll_top(parent);
485 
486         obj->coords.y1 = parent->coords.y1 + lv_obj_get_style_pad_top(parent, LV_PART_MAIN) - st;
487         obj->coords.y2 = obj->coords.y1 - 1;
488         obj->coords.x1  = parent->coords.x1 + lv_obj_get_style_pad_left(parent, LV_PART_MAIN) - sl;
489         obj->coords.x2  = obj->coords.x1 - 1;
490     }
491 
492     /*Set attributes*/
493     obj->flags = LV_OBJ_FLAG_CLICKABLE;
494     obj->flags |= LV_OBJ_FLAG_SNAPPABLE;
495     if(parent) obj->flags |= LV_OBJ_FLAG_PRESS_LOCK;
496     if(parent) obj->flags |= LV_OBJ_FLAG_SCROLL_CHAIN;
497     obj->flags |= LV_OBJ_FLAG_CLICK_FOCUSABLE;
498     obj->flags |= LV_OBJ_FLAG_SCROLLABLE;
499     obj->flags |= LV_OBJ_FLAG_SCROLL_ELASTIC;
500     obj->flags |= LV_OBJ_FLAG_SCROLL_MOMENTUM;
501     obj->flags |= LV_OBJ_FLAG_SCROLL_WITH_ARROW;
502     if(parent) obj->flags |= LV_OBJ_FLAG_GESTURE_BUBBLE;
503 
504 #if LV_OBJ_ID_AUTO_ASSIGN
505     lv_obj_assign_id(class_p, obj);
506 #endif
507 
508     LV_TRACE_OBJ_CREATE("finished");
509 }
510 
lv_obj_destructor(const lv_obj_class_t * class_p,lv_obj_t * obj)511 static void lv_obj_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
512 {
513     LV_UNUSED(class_p);
514 
515     lv_event_mark_deleted(obj);
516 
517     /*Remove all style*/
518     lv_obj_enable_style_refresh(false); /*No need to refresh the style because the object will be deleted*/
519     lv_obj_remove_style_all(obj);
520     lv_obj_enable_style_refresh(true);
521 
522     /*Remove the animations from this object*/
523     lv_anim_delete(obj, NULL);
524 
525     /*Delete from the group*/
526     lv_group_t * group = lv_obj_get_group(obj);
527     if(group) lv_group_remove_obj(obj);
528 
529     if(obj->spec_attr) {
530         if(obj->spec_attr->children) {
531             lv_free(obj->spec_attr->children);
532             obj->spec_attr->children = NULL;
533         }
534 
535         lv_event_remove_all(&obj->spec_attr->event_list);
536 
537 #if LV_DRAW_TRANSFORM_USE_MATRIX
538         if(obj->spec_attr->matrix) {
539             lv_free(obj->spec_attr->matrix);
540             obj->spec_attr->matrix = NULL;
541         }
542 #endif
543 
544         lv_free(obj->spec_attr);
545         obj->spec_attr = NULL;
546     }
547 
548 #if LV_OBJ_ID_AUTO_ASSIGN
549     lv_obj_free_id(obj);
550 #endif
551 }
552 
lv_obj_draw(lv_event_t * e)553 static void lv_obj_draw(lv_event_t * e)
554 {
555     lv_event_code_t code = lv_event_get_code(e);
556     lv_obj_t * obj = lv_event_get_current_target(e);
557     if(code == LV_EVENT_COVER_CHECK) {
558         lv_cover_check_info_t * info = lv_event_get_param(e);
559         if(info->res == LV_COVER_RES_MASKED) return;
560         if(lv_obj_get_style_clip_corner(obj, LV_PART_MAIN)) {
561             info->res = LV_COVER_RES_MASKED;
562             return;
563         }
564 
565         /*Most trivial test. Is the mask fully IN the object? If no it surely doesn't cover it*/
566         int32_t r = lv_obj_get_style_radius(obj, LV_PART_MAIN);
567         int32_t w = lv_obj_get_style_transform_width(obj, LV_PART_MAIN);
568         int32_t h = lv_obj_get_style_transform_height(obj, LV_PART_MAIN);
569         lv_area_t coords;
570         lv_area_copy(&coords, &obj->coords);
571         lv_area_increase(&coords, w, h);
572 
573         if(lv_area_is_in(info->area, &coords, r) == false) {
574             info->res = LV_COVER_RES_NOT_COVER;
575             return;
576         }
577 
578         if(lv_obj_get_style_bg_opa(obj, LV_PART_MAIN) < LV_OPA_MAX) {
579             info->res = LV_COVER_RES_NOT_COVER;
580             return;
581         }
582 
583         if(lv_obj_get_style_opa(obj, LV_PART_MAIN) < LV_OPA_MAX) {
584             info->res = LV_COVER_RES_NOT_COVER;
585             return;
586         }
587 
588         if(lv_obj_get_style_bg_grad_dir(obj, 0) != LV_GRAD_DIR_NONE) {
589             if(lv_obj_get_style_bg_grad_opa(obj, 0) < LV_OPA_MAX) {
590                 info->res = LV_COVER_RES_NOT_COVER;
591                 return;
592             }
593         }
594         const lv_grad_dsc_t * grad_dsc = lv_obj_get_style_bg_grad(obj, 0);
595         if(grad_dsc) {
596             uint32_t i;
597             for(i = 0; i < grad_dsc->stops_count; i++) {
598                 if(grad_dsc->stops[i].opa < LV_OPA_MAX) {
599                     info->res = LV_COVER_RES_NOT_COVER;
600                     return;
601                 }
602             }
603         }
604         info->res = LV_COVER_RES_COVER;
605     }
606     else if(code == LV_EVENT_DRAW_MAIN) {
607         lv_layer_t * layer = lv_event_get_layer(e);
608         lv_draw_rect_dsc_t draw_dsc;
609         lv_draw_rect_dsc_init(&draw_dsc);
610         draw_dsc.base.layer = layer;
611 
612         lv_obj_init_draw_rect_dsc(obj, LV_PART_MAIN, &draw_dsc);
613         /*If the border is drawn later disable loading its properties*/
614         if(lv_obj_get_style_border_post(obj, LV_PART_MAIN)) {
615             draw_dsc.border_post = 1;
616         }
617 
618         int32_t w = lv_obj_get_style_transform_width(obj, LV_PART_MAIN);
619         int32_t h = lv_obj_get_style_transform_height(obj, LV_PART_MAIN);
620         lv_area_t coords;
621         lv_area_copy(&coords, &obj->coords);
622         lv_area_increase(&coords, w, h);
623 
624         lv_draw_rect(layer, &draw_dsc, &coords);
625     }
626     else if(code == LV_EVENT_DRAW_POST) {
627         lv_layer_t * layer = lv_event_get_layer(e);
628         draw_scrollbar(obj, layer);
629 
630         /*If the border is drawn later disable loading other properties*/
631         if(lv_obj_get_style_border_width(obj, LV_PART_MAIN) &&
632            lv_obj_get_style_border_post(obj, LV_PART_MAIN)) {
633             lv_draw_rect_dsc_t draw_dsc;
634             lv_draw_rect_dsc_init(&draw_dsc);
635             draw_dsc.bg_opa = LV_OPA_TRANSP;
636             draw_dsc.bg_image_opa = LV_OPA_TRANSP;
637             draw_dsc.outline_opa = LV_OPA_TRANSP;
638             draw_dsc.shadow_opa = LV_OPA_TRANSP;
639             draw_dsc.base.layer = layer;
640             lv_obj_init_draw_rect_dsc(obj, LV_PART_MAIN, &draw_dsc);
641 
642             int32_t w = lv_obj_get_style_transform_width(obj, LV_PART_MAIN);
643             int32_t h = lv_obj_get_style_transform_height(obj, LV_PART_MAIN);
644             lv_area_t coords;
645             lv_area_copy(&coords, &obj->coords);
646             lv_area_increase(&coords, w, h);
647 
648             lv_draw_rect(layer, &draw_dsc, &coords);
649         }
650     }
651 }
652 
draw_scrollbar(lv_obj_t * obj,lv_layer_t * layer)653 static void draw_scrollbar(lv_obj_t * obj, lv_layer_t * layer)
654 {
655 
656     lv_area_t hor_area;
657     lv_area_t ver_area;
658     lv_obj_get_scrollbar_area(obj, &hor_area, &ver_area);
659 
660     if(lv_area_get_size(&hor_area) <= 0 && lv_area_get_size(&ver_area) <= 0) return;
661 
662     lv_draw_rect_dsc_t draw_dsc;
663     lv_result_t sb_res = scrollbar_init_draw_dsc(obj, &draw_dsc);
664     if(sb_res != LV_RESULT_OK) return;
665 
666     if(lv_area_get_size(&hor_area) > 0) {
667         draw_dsc.base.id1 = 0;
668         lv_draw_rect(layer, &draw_dsc, &hor_area);
669     }
670     if(lv_area_get_size(&ver_area) > 0) {
671         draw_dsc.base.id1 = 1;
672         lv_draw_rect(layer, &draw_dsc, &ver_area);
673     }
674 }
675 
676 /**
677  * Initialize the draw descriptor for the scrollbar
678  * @param obj pointer to an object
679  * @param dsc the draw descriptor to initialize
680  * @return LV_RESULT_OK: the scrollbar is visible; LV_RESULT_INVALID: the scrollbar is not visible
681  */
scrollbar_init_draw_dsc(lv_obj_t * obj,lv_draw_rect_dsc_t * dsc)682 static lv_result_t scrollbar_init_draw_dsc(lv_obj_t * obj, lv_draw_rect_dsc_t * dsc)
683 {
684     lv_draw_rect_dsc_init(dsc);
685     dsc->bg_opa = lv_obj_get_style_bg_opa(obj, LV_PART_SCROLLBAR);
686     if(dsc->bg_opa > LV_OPA_MIN) {
687         dsc->bg_color = lv_obj_get_style_bg_color(obj, LV_PART_SCROLLBAR);
688     }
689 
690     dsc->border_opa = lv_obj_get_style_border_opa(obj, LV_PART_SCROLLBAR);
691     if(dsc->border_opa > LV_OPA_MIN) {
692         dsc->border_width = lv_obj_get_style_border_width(obj, LV_PART_SCROLLBAR);
693         if(dsc->border_width > 0) {
694             dsc->border_color = lv_obj_get_style_border_color(obj, LV_PART_SCROLLBAR);
695         }
696         else {
697             dsc->border_opa = LV_OPA_TRANSP;
698         }
699     }
700 
701     dsc->shadow_opa = lv_obj_get_style_shadow_opa(obj, LV_PART_SCROLLBAR);
702     if(dsc->shadow_opa > LV_OPA_MIN) {
703         dsc->shadow_width = lv_obj_get_style_shadow_width(obj, LV_PART_SCROLLBAR);
704         if(dsc->shadow_width > 0) {
705             dsc->shadow_spread = lv_obj_get_style_shadow_spread(obj, LV_PART_SCROLLBAR);
706             dsc->shadow_color = lv_obj_get_style_shadow_color(obj, LV_PART_SCROLLBAR);
707         }
708         else {
709             dsc->shadow_opa = LV_OPA_TRANSP;
710         }
711     }
712 
713     lv_opa_t opa = lv_obj_get_style_opa_recursive(obj, LV_PART_SCROLLBAR);
714     if(opa < LV_OPA_MAX) {
715         lv_opa_t v = LV_OPA_MIX2(dsc->bg_opa, opa);
716         dsc->bg_opa = v;
717         dsc->border_opa = v;
718         dsc->shadow_opa = v;
719     }
720 
721     if(dsc->bg_opa != LV_OPA_TRANSP || dsc->border_opa != LV_OPA_TRANSP || dsc->shadow_opa != LV_OPA_TRANSP) {
722         dsc->radius = lv_obj_get_style_radius(obj, LV_PART_SCROLLBAR);
723         return LV_RESULT_OK;
724     }
725     else {
726         return LV_RESULT_INVALID;
727     }
728 }
729 
lv_obj_event(const lv_obj_class_t * class_p,lv_event_t * e)730 static void lv_obj_event(const lv_obj_class_t * class_p, lv_event_t * e)
731 {
732     LV_UNUSED(class_p);
733 
734     lv_event_code_t code = lv_event_get_code(e);
735     lv_obj_t * obj = lv_event_get_current_target(e);
736     if(code == LV_EVENT_PRESSED) {
737         lv_obj_add_state(obj, LV_STATE_PRESSED);
738     }
739     else if(code == LV_EVENT_RELEASED) {
740         lv_obj_remove_state(obj, LV_STATE_PRESSED);
741         void * param = lv_event_get_param(e);
742         /*Go the checked state if enabled*/
743         if(lv_indev_get_scroll_obj(param) == NULL && lv_obj_has_flag(obj, LV_OBJ_FLAG_CHECKABLE)) {
744             if(!(lv_obj_get_state(obj) & LV_STATE_CHECKED)) lv_obj_add_state(obj, LV_STATE_CHECKED);
745             else lv_obj_remove_state(obj, LV_STATE_CHECKED);
746 
747             lv_result_t res = lv_obj_send_event(obj, LV_EVENT_VALUE_CHANGED, NULL);
748             if(res != LV_RESULT_OK) return;
749         }
750     }
751     else if(code == LV_EVENT_PRESS_LOST) {
752         lv_obj_remove_state(obj, LV_STATE_PRESSED);
753     }
754     else if(code == LV_EVENT_STYLE_CHANGED) {
755         uint32_t child_cnt = lv_obj_get_child_count(obj);
756         for(uint32_t i = 0; i < child_cnt; i++) {
757             lv_obj_t * child = obj->spec_attr->children[i];
758             lv_obj_mark_layout_as_dirty(child);
759         }
760     }
761     else if(code == LV_EVENT_KEY) {
762         if(lv_obj_has_flag(obj, LV_OBJ_FLAG_CHECKABLE)) {
763             uint32_t c = lv_event_get_key(e);
764             if(c == LV_KEY_RIGHT || c == LV_KEY_UP) {
765                 lv_obj_add_state(obj, LV_STATE_CHECKED);
766             }
767             else if(c == LV_KEY_LEFT || c == LV_KEY_DOWN) {
768                 lv_obj_remove_state(obj, LV_STATE_CHECKED);
769             }
770 
771             /*With Enter LV_EVENT_RELEASED will send VALUE_CHANGE event*/
772             if(c != LV_KEY_ENTER) {
773                 lv_result_t res = lv_obj_send_event(obj, LV_EVENT_VALUE_CHANGED, NULL);
774                 if(res != LV_RESULT_OK) return;
775             }
776         }
777         else if(lv_obj_has_flag(obj, LV_OBJ_FLAG_SCROLLABLE | LV_OBJ_FLAG_SCROLL_WITH_ARROW) && !lv_obj_is_editable(obj)) {
778             /*scroll by keypad or encoder*/
779             lv_anim_enable_t anim_enable = LV_ANIM_OFF;
780             int32_t sl = lv_obj_get_scroll_left(obj);
781             int32_t sr = lv_obj_get_scroll_right(obj);
782             uint32_t c = lv_event_get_key(e);
783             if(c == LV_KEY_DOWN) {
784                 /*use scroll_to_x/y functions to enforce scroll limits*/
785                 lv_obj_scroll_to_y(obj, lv_obj_get_scroll_y(obj) + lv_obj_get_height(obj) / 4, anim_enable);
786             }
787             else if(c == LV_KEY_UP) {
788                 lv_obj_scroll_to_y(obj, lv_obj_get_scroll_y(obj) - lv_obj_get_height(obj) / 4, anim_enable);
789             }
790             else if(c == LV_KEY_RIGHT) {
791                 /*If the object can't be scrolled horizontally then scroll it vertically*/
792                 if(!((lv_obj_get_scroll_dir(obj) & LV_DIR_HOR) && (sl > 0 || sr > 0)))
793                     lv_obj_scroll_to_y(obj, lv_obj_get_scroll_y(obj) + lv_obj_get_height(obj) / 4, anim_enable);
794                 else
795                     lv_obj_scroll_to_x(obj, lv_obj_get_scroll_x(obj) + lv_obj_get_width(obj) / 4, anim_enable);
796             }
797             else if(c == LV_KEY_LEFT) {
798                 /*If the object can't be scrolled horizontally then scroll it vertically*/
799                 if(!((lv_obj_get_scroll_dir(obj) & LV_DIR_HOR) && (sl > 0 || sr > 0)))
800                     lv_obj_scroll_to_y(obj, lv_obj_get_scroll_y(obj) - lv_obj_get_height(obj) / 4, anim_enable);
801                 else
802                     lv_obj_scroll_to_x(obj, lv_obj_get_scroll_x(obj) - lv_obj_get_width(obj) / 4, anim_enable);
803             }
804         }
805     }
806     else if(code == LV_EVENT_FOCUSED) {
807         if(lv_obj_has_flag(obj, LV_OBJ_FLAG_SCROLL_ON_FOCUS)) {
808             lv_obj_scroll_to_view_recursive(obj, LV_ANIM_ON);
809         }
810 
811         bool editing = false;
812         editing = lv_group_get_editing(lv_obj_get_group(obj));
813         lv_state_t state = LV_STATE_FOCUSED;
814 
815         /* Use the indev for then indev handler.
816          * But if the obj was focused manually it returns NULL so try to
817          * use the indev from the event*/
818         lv_indev_t * indev = lv_indev_active();
819         if(indev == NULL) indev = lv_event_get_indev(e);
820 
821         lv_indev_type_t indev_type = lv_indev_get_type(indev);
822         if(indev_type == LV_INDEV_TYPE_KEYPAD || indev_type == LV_INDEV_TYPE_ENCODER) state |= LV_STATE_FOCUS_KEY;
823         if(editing) {
824             state |= LV_STATE_EDITED;
825             lv_obj_add_state(obj, state);
826         }
827         else {
828             lv_obj_add_state(obj, state);
829             lv_obj_remove_state(obj, LV_STATE_EDITED);
830         }
831     }
832     else if(code == LV_EVENT_SCROLL_BEGIN) {
833         lv_obj_add_state(obj, LV_STATE_SCROLLED);
834     }
835     else if(code == LV_EVENT_SCROLL_END) {
836         lv_obj_remove_state(obj, LV_STATE_SCROLLED);
837         if(lv_obj_get_scrollbar_mode(obj) == LV_SCROLLBAR_MODE_ACTIVE) {
838             lv_area_t hor_area, ver_area;
839             lv_obj_get_scrollbar_area(obj, &hor_area, &ver_area);
840             lv_obj_invalidate_area(obj, &hor_area);
841             lv_obj_invalidate_area(obj, &ver_area);
842         }
843     }
844     else if(code == LV_EVENT_DEFOCUSED) {
845         lv_obj_remove_state(obj, LV_STATE_FOCUSED | LV_STATE_EDITED | LV_STATE_FOCUS_KEY);
846     }
847     else if(code == LV_EVENT_SIZE_CHANGED) {
848         int32_t align = lv_obj_get_style_align(obj, LV_PART_MAIN);
849         uint16_t layout = lv_obj_get_style_layout(obj, LV_PART_MAIN);
850         if(layout || align) {
851             lv_obj_mark_layout_as_dirty(obj);
852         }
853 
854         uint32_t i;
855         uint32_t child_cnt = lv_obj_get_child_count(obj);
856         for(i = 0; i < child_cnt; i++) {
857             lv_obj_t * child = obj->spec_attr->children[i];
858             lv_obj_mark_layout_as_dirty(child);
859         }
860     }
861     else if(code == LV_EVENT_CHILD_CHANGED) {
862         int32_t w = lv_obj_get_style_width(obj, LV_PART_MAIN);
863         int32_t h = lv_obj_get_style_height(obj, LV_PART_MAIN);
864         int32_t align = lv_obj_get_style_align(obj, LV_PART_MAIN);
865         uint16_t layout = lv_obj_get_style_layout(obj, LV_PART_MAIN);
866         if(layout || align || w == LV_SIZE_CONTENT || h == LV_SIZE_CONTENT) {
867             lv_obj_mark_layout_as_dirty(obj);
868         }
869     }
870     else if(code == LV_EVENT_CHILD_DELETED) {
871         obj->readjust_scroll_after_layout = 1;
872         lv_obj_mark_layout_as_dirty(obj);
873     }
874     else if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
875         int32_t d = lv_obj_calculate_ext_draw_size(obj, LV_PART_MAIN);
876         lv_event_set_ext_draw_size(e, d);
877     }
878     else if(code == LV_EVENT_DRAW_MAIN || code == LV_EVENT_DRAW_POST || code == LV_EVENT_COVER_CHECK) {
879         lv_obj_draw(e);
880     }
881     else if(code == LV_EVENT_INDEV_RESET) {
882         lv_obj_remove_state(obj, LV_STATE_PRESSED);
883         lv_obj_remove_state(obj, LV_STATE_SCROLLED);
884     }
885     else if(code == LV_EVENT_HOVER_OVER) {
886         lv_obj_add_state(obj, LV_STATE_HOVERED);
887     }
888     else if(code == LV_EVENT_HOVER_LEAVE) {
889         lv_obj_remove_state(obj, LV_STATE_HOVERED);
890     }
891 }
892 
893 /**
894  * Set the state (fully overwrite) of an object.
895  * If specified in the styles, transition animations will be started from the previous state to the current.
896  * @param obj       pointer to an object
897  * @param state     the new state
898  */
update_obj_state(lv_obj_t * obj,lv_state_t new_state)899 static void update_obj_state(lv_obj_t * obj, lv_state_t new_state)
900 {
901     if(obj->state == new_state) return;
902 
903     LV_ASSERT_OBJ(obj, MY_CLASS);
904 
905     lv_state_t prev_state = obj->state;
906 
907     lv_style_state_cmp_t cmp_res = lv_obj_style_state_compare(obj, prev_state, new_state);
908     /*If there is no difference in styles there is nothing else to do*/
909     if(cmp_res == LV_STYLE_STATE_CMP_SAME) {
910         obj->state = new_state;
911         return;
912     }
913 
914     /*Invalidate the object in their current state*/
915     lv_obj_invalidate(obj);
916 
917     obj->state = new_state;
918     lv_obj_update_layer_type(obj);
919     lv_obj_style_transition_dsc_t * ts = lv_malloc_zeroed(sizeof(lv_obj_style_transition_dsc_t) * STYLE_TRANSITION_MAX);
920     uint32_t tsi = 0;
921     uint32_t i;
922     for(i = 0; i < obj->style_cnt && tsi < STYLE_TRANSITION_MAX; i++) {
923         lv_obj_style_t * obj_style = &obj->styles[i];
924         lv_state_t state_act = lv_obj_style_get_selector_state(obj->styles[i].selector);
925         lv_part_t part_act = lv_obj_style_get_selector_part(obj->styles[i].selector);
926         if(state_act & (~new_state)) continue; /*Skip unrelated styles*/
927         if(obj_style->is_trans) continue;
928 
929         lv_style_value_t v;
930         if(lv_style_get_prop_inlined(obj_style->style, LV_STYLE_TRANSITION, &v) != LV_STYLE_RES_FOUND) continue;
931         const lv_style_transition_dsc_t * tr = v.ptr;
932 
933         /*Add the props to the set if not added yet or added but with smaller weight*/
934         uint32_t j;
935         for(j = 0; tr->props[j] != 0 && tsi < STYLE_TRANSITION_MAX; j++) {
936             uint32_t t;
937             for(t = 0; t < tsi; t++) {
938                 lv_style_selector_t selector = ts[t].selector;
939                 lv_state_t state_ts = lv_obj_style_get_selector_state(selector);
940                 lv_part_t part_ts = lv_obj_style_get_selector_part(selector);
941                 if(ts[t].prop == tr->props[j] && part_ts == part_act && state_ts >= state_act) break;
942             }
943 
944             /*If not found  add it*/
945             if(t == tsi) {
946                 ts[tsi].time = tr->time;
947                 ts[tsi].delay = tr->delay;
948                 ts[tsi].path_cb = tr->path_xcb;
949                 ts[tsi].prop = tr->props[j];
950                 ts[tsi].user_data = tr->user_data;
951                 ts[tsi].selector = obj_style->selector;
952                 tsi++;
953             }
954         }
955     }
956 
957     for(i = 0; i < tsi; i++) {
958         lv_part_t part_act = lv_obj_style_get_selector_part(ts[i].selector);
959         lv_obj_style_create_transition(obj, part_act, prev_state, new_state, &ts[i]);
960     }
961 
962     lv_free(ts);
963 
964     if(cmp_res == LV_STYLE_STATE_CMP_DIFF_REDRAW) {
965         /*Invalidation is not enough, e.g. layer type needs to be updated too*/
966         lv_obj_refresh_style(obj, LV_PART_ANY, LV_STYLE_PROP_ANY);
967     }
968     else if(cmp_res == LV_STYLE_STATE_CMP_DIFF_LAYOUT) {
969         lv_obj_refresh_style(obj, LV_PART_ANY, LV_STYLE_PROP_ANY);
970     }
971     else if(cmp_res == LV_STYLE_STATE_CMP_DIFF_DRAW_PAD) {
972         lv_obj_invalidate(obj);
973         lv_obj_refresh_ext_draw_size(obj);
974     }
975 }
976 
obj_valid_child(const lv_obj_t * parent,const lv_obj_t * obj_to_find)977 static bool obj_valid_child(const lv_obj_t * parent, const lv_obj_t * obj_to_find)
978 {
979     /*Check all children of `parent`*/
980     uint32_t child_cnt = 0;
981     if(parent->spec_attr) child_cnt = parent->spec_attr->child_cnt;
982     uint32_t i;
983     for(i = 0; i < child_cnt; i++) {
984         lv_obj_t * child = parent->spec_attr->children[i];
985         if(child == obj_to_find) {
986             return true;
987         }
988 
989         /*Check the children*/
990         bool found = obj_valid_child(child, obj_to_find);
991         if(found) {
992             return true;
993         }
994     }
995     return false;
996 }
997 
null_on_delete_cb(lv_event_t * e)998 static void null_on_delete_cb(lv_event_t * e)
999 {
1000     lv_obj_t ** obj_ptr = lv_event_get_user_data(e);
1001     *obj_ptr = NULL;
1002 }
1003 
1004 #if LV_USE_OBJ_PROPERTY
lv_obj_set_any(lv_obj_t * obj,lv_prop_id_t id,const lv_property_t * prop)1005 static lv_result_t lv_obj_set_any(lv_obj_t * obj, lv_prop_id_t id, const lv_property_t * prop)
1006 {
1007     LV_ASSERT_OBJ(obj, MY_CLASS);
1008 
1009     if(id >= LV_PROPERTY_OBJ_FLAG_START && id <= LV_PROPERTY_OBJ_FLAG_END) {
1010         lv_obj_flag_t flag = 1L << (id - LV_PROPERTY_OBJ_FLAG_START);
1011         if(prop->num) lv_obj_add_flag(obj, flag);
1012         else lv_obj_remove_flag(obj, flag);
1013         return LV_RESULT_OK;
1014     }
1015     else if(id >= LV_PROPERTY_OBJ_STATE_START && id <= LV_PROPERTY_OBJ_STATE_END) {
1016         lv_state_t state = 1L << (id - LV_PROPERTY_OBJ_STATE_START);
1017         if(id == LV_PROPERTY_OBJ_STATE_ANY) {
1018             state = LV_STATE_ANY;
1019         }
1020 
1021         if(prop->num) lv_obj_add_state(obj, state);
1022         else lv_obj_remove_state(obj, state);
1023         return LV_RESULT_OK;
1024     }
1025     else {
1026         return LV_RESULT_INVALID;
1027     }
1028 }
1029 
lv_obj_get_any(const lv_obj_t * obj,lv_prop_id_t id,lv_property_t * prop)1030 static lv_result_t lv_obj_get_any(const lv_obj_t * obj, lv_prop_id_t id, lv_property_t * prop)
1031 {
1032     LV_ASSERT_OBJ(obj, MY_CLASS);
1033     if(id >= LV_PROPERTY_OBJ_FLAG_START && id <= LV_PROPERTY_OBJ_FLAG_END) {
1034         lv_obj_flag_t flag = 1L << (id - LV_PROPERTY_OBJ_FLAG_START);
1035         prop->id = id;
1036         prop->num = obj->flags & flag;
1037         return LV_RESULT_OK;
1038     }
1039     else if(id >= LV_PROPERTY_OBJ_STATE_START && id <= LV_PROPERTY_OBJ_STATE_END) {
1040         prop->id = id;
1041         if(id == LV_PROPERTY_OBJ_STATE_ANY) {
1042             prop->num = obj->state;
1043         }
1044         else {
1045             lv_obj_flag_t flag = 1L << (id - LV_PROPERTY_OBJ_STATE_START);
1046             prop->num = obj->state & flag;
1047         }
1048         return LV_RESULT_OK;
1049     }
1050     else {
1051         return LV_RESULT_INVALID;
1052     }
1053 }
1054 #endif
1055