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