1 /**
2 * @file lv_obj_tree.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9 #include "lv_obj_private.h"
10 #include "lv_obj_class_private.h"
11 #include "../indev/lv_indev.h"
12 #include "../indev/lv_indev_private.h"
13 #include "../display/lv_display.h"
14 #include "../display/lv_display_private.h"
15 #include "../misc/lv_anim_private.h"
16 #include "../misc/lv_async.h"
17 #include "../core/lv_global.h"
18
19 /*********************
20 * DEFINES
21 *********************/
22 #define MY_CLASS (&lv_obj_class)
23 #define disp_ll_p &(LV_GLOBAL_DEFAULT()->disp_ll)
24
25 #define OBJ_DUMP_STRING_LEN 128
26
27 /**********************
28 * TYPEDEFS
29 **********************/
30
31 /**********************
32 * STATIC PROTOTYPES
33 **********************/
34 static void lv_obj_delete_async_cb(void * obj);
35 static void obj_delete_core(lv_obj_t * obj);
36 static lv_obj_tree_walk_res_t walk_core(lv_obj_t * obj, lv_obj_tree_walk_cb_t cb, void * user_data);
37 static void dump_tree_core(lv_obj_t * obj, int32_t depth);
38 static lv_obj_t * lv_obj_get_first_not_deleting_child(lv_obj_t * obj);
39
40 /**********************
41 * STATIC VARIABLES
42 **********************/
43
44 /**********************
45 * MACROS
46 **********************/
47
48 /**********************
49 * GLOBAL FUNCTIONS
50 **********************/
51
lv_obj_delete(lv_obj_t * obj)52 void lv_obj_delete(lv_obj_t * obj)
53 {
54 if(obj->is_deleting)
55 return;
56
57 LV_LOG_TRACE("begin (delete %p)", (void *)obj);
58 LV_ASSERT_OBJ(obj, MY_CLASS);
59 lv_obj_invalidate(obj);
60
61 lv_obj_t * par = lv_obj_get_parent(obj);
62
63 lv_display_t * disp = NULL;
64 bool act_screen_del = false;
65 if(par == NULL) {
66 disp = lv_obj_get_display(obj);
67 if(!disp) return; /*Shouldn't happen*/
68 if(disp->act_scr == obj) act_screen_del = true;
69 }
70
71 obj_delete_core(obj);
72
73 /*Call the ancestor's event handler to the parent to notify it about the child delete*/
74 if(par && !par->is_deleting) {
75 lv_obj_scrollbar_invalidate(par);
76 lv_obj_send_event(par, LV_EVENT_CHILD_CHANGED, NULL);
77 lv_obj_send_event(par, LV_EVENT_CHILD_DELETED, NULL);
78 }
79
80 /*Handle if the active screen was deleted*/
81 if(act_screen_del) {
82 LV_LOG_WARN("the active screen was deleted");
83 disp->act_scr = NULL;
84 }
85
86 LV_ASSERT_MEM_INTEGRITY();
87 LV_LOG_TRACE("finished (delete %p)", (void *)obj);
88 }
89
lv_obj_clean(lv_obj_t * obj)90 void lv_obj_clean(lv_obj_t * obj)
91 {
92 LV_LOG_TRACE("begin (clean %p)", (void *)obj);
93 LV_ASSERT_OBJ(obj, MY_CLASS);
94
95 lv_obj_invalidate(obj);
96
97 uint32_t cnt = lv_obj_get_child_count(obj);
98 lv_obj_t * child = lv_obj_get_first_not_deleting_child(obj);
99 while(child) {
100 obj_delete_core(child);
101 child = lv_obj_get_first_not_deleting_child(obj);
102 }
103 /*Just to remove scroll animations if any*/
104 lv_obj_scroll_to(obj, 0, 0, LV_ANIM_OFF);
105 if(obj->spec_attr) {
106 obj->spec_attr->scroll.x = 0;
107 obj->spec_attr->scroll.y = 0;
108 }
109
110 if(lv_obj_get_child_count(obj) < cnt) {
111 lv_obj_send_event(obj, LV_EVENT_CHILD_CHANGED, NULL);
112 lv_obj_send_event(obj, LV_EVENT_CHILD_DELETED, NULL);
113 }
114
115 LV_ASSERT_MEM_INTEGRITY();
116
117 LV_LOG_TRACE("finished (clean %p)", (void *)obj);
118 }
119
lv_obj_delete_delayed(lv_obj_t * obj,uint32_t delay_ms)120 void lv_obj_delete_delayed(lv_obj_t * obj, uint32_t delay_ms)
121 {
122 lv_anim_t a;
123 lv_anim_init(&a);
124 lv_anim_set_var(&a, obj);
125 lv_anim_set_exec_cb(&a, NULL);
126 lv_anim_set_duration(&a, 1);
127 lv_anim_set_delay(&a, delay_ms);
128 lv_anim_set_completed_cb(&a, lv_obj_delete_anim_completed_cb);
129 lv_anim_start(&a);
130 }
131
lv_obj_delete_anim_completed_cb(lv_anim_t * a)132 void lv_obj_delete_anim_completed_cb(lv_anim_t * a)
133 {
134 lv_obj_delete(a->var);
135 }
136
lv_obj_delete_async(lv_obj_t * obj)137 void lv_obj_delete_async(lv_obj_t * obj)
138 {
139 LV_ASSERT_OBJ(obj, MY_CLASS);
140 lv_async_call(lv_obj_delete_async_cb, obj);
141 }
142
lv_obj_set_parent(lv_obj_t * obj,lv_obj_t * parent)143 void lv_obj_set_parent(lv_obj_t * obj, lv_obj_t * parent)
144 {
145 LV_ASSERT_OBJ(obj, MY_CLASS);
146 LV_ASSERT_OBJ(parent, MY_CLASS);
147
148 if(obj->parent == NULL) {
149 LV_LOG_WARN("Can't set the parent of a screen");
150 return;
151 }
152
153 if(parent == NULL) {
154 LV_LOG_WARN("Can't set parent == NULL to an object");
155 return;
156 }
157
158 if(parent == obj->parent) {
159 return;
160 }
161
162 lv_obj_invalidate(obj);
163
164 lv_obj_allocate_spec_attr(parent);
165
166 lv_obj_t * old_parent = obj->parent;
167 /*Remove the object from the old parent's child list*/
168 int32_t i;
169 for(i = lv_obj_get_index(obj); i <= (int32_t)lv_obj_get_child_count(old_parent) - 2; i++) {
170 old_parent->spec_attr->children[i] = old_parent->spec_attr->children[i + 1];
171 }
172 old_parent->spec_attr->child_cnt--;
173 if(old_parent->spec_attr->child_cnt) {
174 old_parent->spec_attr->children = lv_realloc(old_parent->spec_attr->children,
175 old_parent->spec_attr->child_cnt * (sizeof(lv_obj_t *)));
176 }
177 else {
178 lv_free(old_parent->spec_attr->children);
179 old_parent->spec_attr->children = NULL;
180 }
181
182 /*Add the child to the new parent as the last (newest child)*/
183 parent->spec_attr->child_cnt++;
184 parent->spec_attr->children = lv_realloc(parent->spec_attr->children,
185 parent->spec_attr->child_cnt * (sizeof(lv_obj_t *)));
186 parent->spec_attr->children[lv_obj_get_child_count(parent) - 1] = obj;
187
188 obj->parent = parent;
189
190 /*Notify the original parent because one of its children is lost*/
191 lv_obj_scrollbar_invalidate(old_parent);
192 lv_obj_send_event(old_parent, LV_EVENT_CHILD_CHANGED, obj);
193 lv_obj_send_event(old_parent, LV_EVENT_CHILD_DELETED, NULL);
194
195 /*Notify the new parent about the child*/
196 lv_obj_send_event(parent, LV_EVENT_CHILD_CHANGED, obj);
197 lv_obj_send_event(parent, LV_EVENT_CHILD_CREATED, NULL);
198
199 lv_obj_mark_layout_as_dirty(obj);
200
201 lv_obj_invalidate(obj);
202 }
203
lv_obj_move_to_index(lv_obj_t * obj,int32_t index)204 void lv_obj_move_to_index(lv_obj_t * obj, int32_t index)
205 {
206 LV_ASSERT_OBJ(obj, MY_CLASS);
207
208 /* Check parent validity */
209 lv_obj_t * parent = lv_obj_get_parent(obj);
210 if(!parent) {
211 LV_LOG_WARN("parent is NULL");
212 return;
213 }
214
215 const uint32_t parent_child_count = lv_obj_get_child_count(parent);
216 /* old_index only can be 0 or greater, this point cannot be reached if the parent is not null */
217 const int32_t old_index = lv_obj_get_index(obj);
218 LV_ASSERT(0 <= old_index);
219
220 if(index < 0) {
221 index += parent_child_count;
222 }
223
224 /* Index was negative and the absolute value is greater than parent child count */
225 if((index < 0)
226 /* Index is same or bigger than parent child count */
227 || (index >= (int32_t) parent_child_count)
228 /* If both previous and new index are the same */
229 || (index == old_index)) {
230
231 return;
232 }
233
234 int32_t i = old_index;
235 if(index < old_index) {
236 while(i > index) {
237 parent->spec_attr->children[i] = parent->spec_attr->children[i - 1];
238 i--;
239 }
240 }
241 else {
242 while(i < index) {
243 parent->spec_attr->children[i] = parent->spec_attr->children[i + 1];
244 i++;
245 }
246 }
247
248 parent->spec_attr->children[index] = obj;
249 lv_obj_send_event(parent, LV_EVENT_CHILD_CHANGED, NULL);
250 lv_obj_invalidate(parent);
251 }
252
lv_obj_swap(lv_obj_t * obj1,lv_obj_t * obj2)253 void lv_obj_swap(lv_obj_t * obj1, lv_obj_t * obj2)
254 {
255 LV_ASSERT_OBJ(obj1, MY_CLASS);
256 LV_ASSERT_OBJ(obj2, MY_CLASS);
257
258 lv_obj_t * parent = lv_obj_get_parent(obj1);
259 lv_obj_t * parent2 = lv_obj_get_parent(obj2);
260
261 uint_fast32_t index1 = lv_obj_get_index(obj1);
262 uint_fast32_t index2 = lv_obj_get_index(obj2);
263
264 lv_obj_send_event(parent2, LV_EVENT_CHILD_DELETED, obj2);
265 lv_obj_send_event(parent, LV_EVENT_CHILD_DELETED, obj1);
266
267 parent->spec_attr->children[index1] = obj2;
268 obj2->parent = parent;
269
270 parent2->spec_attr->children[index2] = obj1;
271 obj1->parent = parent2;
272
273 lv_obj_send_event(parent, LV_EVENT_CHILD_CHANGED, obj2);
274 lv_obj_send_event(parent, LV_EVENT_CHILD_CREATED, obj2);
275 lv_obj_send_event(parent2, LV_EVENT_CHILD_CHANGED, obj1);
276 lv_obj_send_event(parent2, LV_EVENT_CHILD_CREATED, obj1);
277
278 lv_obj_invalidate(parent);
279
280 if(parent != parent2) {
281 lv_obj_invalidate(parent2);
282 }
283 lv_group_swap_obj(obj1, obj2);
284 }
285
lv_obj_get_screen(const lv_obj_t * obj)286 lv_obj_t * lv_obj_get_screen(const lv_obj_t * obj)
287 {
288 LV_ASSERT_OBJ(obj, MY_CLASS);
289
290 const lv_obj_t * par = obj;
291 const lv_obj_t * act_par;
292
293 do {
294 act_par = par;
295 par = lv_obj_get_parent(act_par);
296 } while(par != NULL);
297
298 return (lv_obj_t *)act_par;
299 }
300
lv_obj_get_display(const lv_obj_t * obj)301 lv_display_t * lv_obj_get_display(const lv_obj_t * obj)
302 {
303 LV_ASSERT_OBJ(obj, MY_CLASS);
304
305 const lv_obj_t * scr;
306
307 if(obj->parent == NULL) scr = obj; /*`obj` is a screen*/
308 else scr = lv_obj_get_screen(obj); /*get the screen of `obj`*/
309
310 lv_display_t * d;
311 lv_ll_t * disp_head = disp_ll_p;
312 LV_LL_READ(disp_head, d) {
313 uint32_t i;
314 for(i = 0; i < d->screen_cnt; i++) {
315 if(d->screens[i] == scr) return d;
316 }
317 }
318
319 LV_LOG_WARN("No screen found");
320 return NULL;
321 }
322
lv_obj_get_parent(const lv_obj_t * obj)323 lv_obj_t * lv_obj_get_parent(const lv_obj_t * obj)
324 {
325 if(obj == NULL) return NULL;
326 LV_ASSERT_OBJ(obj, MY_CLASS);
327
328 return obj->parent;
329 }
330
lv_obj_get_child(const lv_obj_t * obj,int32_t idx)331 lv_obj_t * lv_obj_get_child(const lv_obj_t * obj, int32_t idx)
332 {
333 LV_ASSERT_OBJ(obj, MY_CLASS);
334
335 if(obj->spec_attr == NULL) return NULL;
336
337 uint32_t idu;
338 if(idx < 0) {
339 idx = obj->spec_attr->child_cnt + idx;
340 if(idx < 0) return NULL;
341 idu = (uint32_t) idx;
342 }
343 else {
344 idu = idx;
345 }
346
347 if(idu >= obj->spec_attr->child_cnt) return NULL;
348 else return obj->spec_attr->children[idx];
349 }
350
lv_obj_get_child_by_type(const lv_obj_t * obj,int32_t idx,const lv_obj_class_t * class_p)351 lv_obj_t * lv_obj_get_child_by_type(const lv_obj_t * obj, int32_t idx, const lv_obj_class_t * class_p)
352 {
353 LV_ASSERT_OBJ(obj, MY_CLASS);
354
355 if(obj->spec_attr == NULL) return NULL;
356
357 int32_t i;
358 int32_t cnt = (int32_t)obj->spec_attr->child_cnt;
359 if(idx >= 0) {
360 for(i = 0; i < cnt; i++) {
361 if(obj->spec_attr->children[i]->class_p == class_p) {
362 if(idx == 0) return obj->spec_attr->children[i];
363 else idx--;
364 }
365 }
366 }
367 else {
368 idx++; /*-1 means the first child*/
369 for(i = cnt - 1; i >= 0; i--) {
370 if(obj->spec_attr->children[i]->class_p == class_p) {
371 if(idx == 0) return obj->spec_attr->children[i];
372 else idx++;
373 }
374 }
375 }
376 return NULL;
377 }
378
lv_obj_get_sibling(const lv_obj_t * obj,int32_t idx)379 lv_obj_t * lv_obj_get_sibling(const lv_obj_t * obj, int32_t idx)
380 {
381 lv_obj_t * parent = lv_obj_get_parent(obj);
382 int32_t sibling_idx = (int32_t)lv_obj_get_index(obj) + idx;
383 if(sibling_idx < 0) return NULL;
384
385 return lv_obj_get_child(parent, sibling_idx);
386 }
387
lv_obj_get_sibling_by_type(const lv_obj_t * obj,int32_t idx,const lv_obj_class_t * class_p)388 lv_obj_t * lv_obj_get_sibling_by_type(const lv_obj_t * obj, int32_t idx, const lv_obj_class_t * class_p)
389 {
390 LV_ASSERT_OBJ(obj, MY_CLASS);
391
392 lv_obj_t * parent = lv_obj_get_parent(obj);
393 int32_t sibling_idx = (int32_t)lv_obj_get_index_by_type(obj, class_p) + idx;
394 if(sibling_idx < 0) return NULL;
395
396 return lv_obj_get_child_by_type(parent, sibling_idx, class_p);
397 }
398
lv_obj_get_child_count(const lv_obj_t * obj)399 uint32_t lv_obj_get_child_count(const lv_obj_t * obj)
400 {
401 LV_ASSERT_OBJ(obj, MY_CLASS);
402 if(obj->spec_attr == NULL) return 0;
403 return obj->spec_attr->child_cnt;
404 }
405
lv_obj_get_child_count_by_type(const lv_obj_t * obj,const lv_obj_class_t * class_p)406 uint32_t lv_obj_get_child_count_by_type(const lv_obj_t * obj, const lv_obj_class_t * class_p)
407 {
408 LV_ASSERT_OBJ(obj, MY_CLASS);
409 if(obj->spec_attr == NULL) return 0;
410
411 uint32_t i;
412 uint32_t cnt = 0;
413 for(i = 0; i < obj->spec_attr->child_cnt; i++) {
414 if(obj->spec_attr->children[i]->class_p == class_p) cnt++;
415 }
416 return cnt;
417 }
418
lv_obj_get_index(const lv_obj_t * obj)419 int32_t lv_obj_get_index(const lv_obj_t * obj)
420 {
421 LV_ASSERT_OBJ(obj, MY_CLASS);
422
423 lv_obj_t * parent = lv_obj_get_parent(obj);
424 if(parent == NULL) return -1;
425
426 int32_t i = 0;
427 for(i = 0; i < parent->spec_attr->child_cnt; i++) {
428 if(parent->spec_attr->children[i] == obj) return i;
429 }
430
431 /*Shouldn't reach this point*/
432 LV_ASSERT(0);
433 return -1;
434 }
435
lv_obj_get_index_by_type(const lv_obj_t * obj,const lv_obj_class_t * class_p)436 int32_t lv_obj_get_index_by_type(const lv_obj_t * obj, const lv_obj_class_t * class_p)
437 {
438 LV_ASSERT_OBJ(obj, MY_CLASS);
439
440 lv_obj_t * parent = lv_obj_get_parent(obj);
441 if(parent == NULL) return 0xFFFFFFFF;
442
443 uint32_t i = 0;
444 uint32_t idx = 0;
445 for(i = 0; i < parent->spec_attr->child_cnt; i++) {
446 lv_obj_t * child = parent->spec_attr->children[i];
447 if(child->class_p == class_p) {
448 if(child == obj) return idx;
449 idx++;
450 }
451 }
452
453 /*Can happen if there was no children with the given type*/
454 return -1;
455 }
456
lv_obj_tree_walk(lv_obj_t * start_obj,lv_obj_tree_walk_cb_t cb,void * user_data)457 void lv_obj_tree_walk(lv_obj_t * start_obj, lv_obj_tree_walk_cb_t cb, void * user_data)
458 {
459 walk_core(start_obj, cb, user_data);
460 }
461
lv_obj_dump_tree(lv_obj_t * start_obj)462 void lv_obj_dump_tree(lv_obj_t * start_obj)
463 {
464 if(start_obj == NULL) {
465 lv_display_t * disp = lv_display_get_next(NULL);
466 while(disp) {
467 uint32_t i;
468 for(i = 0; i < disp->screen_cnt; i++) {
469 dump_tree_core(disp->screens[i], 0);
470 }
471 disp = lv_display_get_next(disp);
472 }
473 }
474 else {
475 dump_tree_core(start_obj, 0);
476 }
477 }
478
479 /**********************
480 * STATIC FUNCTIONS
481 **********************/
482
lv_obj_delete_async_cb(void * obj)483 static void lv_obj_delete_async_cb(void * obj)
484 {
485 LV_ASSERT_OBJ(obj, MY_CLASS);
486
487 lv_obj_delete(obj);
488 }
489
obj_indev_reset(lv_indev_t * indev,lv_obj_t * obj)490 static void obj_indev_reset(lv_indev_t * indev, lv_obj_t * obj)
491 {
492 /* If the input device is already in the release state,
493 * there is no need to wait for the input device to be released
494 */
495 if(lv_indev_get_state(indev) != LV_INDEV_STATE_RELEASED) {
496 /*Wait for release to avoid accidentally triggering other obj to be clicked*/
497 lv_indev_wait_release(indev);
498 }
499
500 /*Reset the input device*/
501 lv_indev_reset(indev, obj);
502 }
503
obj_delete_core(lv_obj_t * obj)504 static void obj_delete_core(lv_obj_t * obj)
505 {
506 if(obj->is_deleting)
507 return;
508
509 obj->is_deleting = true;
510
511 /*Let the user free the resources used in `LV_EVENT_DELETE`*/
512 lv_result_t res = lv_obj_send_event(obj, LV_EVENT_DELETE, NULL);
513 if(res == LV_RESULT_INVALID) {
514 obj->is_deleting = false;
515 return;
516 }
517
518 /*Clean registered event_cb*/
519 if(obj->spec_attr) lv_event_remove_all(&(obj->spec_attr->event_list));
520
521 /*Recursively delete the children*/
522 lv_obj_t * child = lv_obj_get_child(obj, 0);
523 while(child) {
524 obj_delete_core(child);
525 child = lv_obj_get_child(obj, 0);
526 }
527
528 lv_group_t * group = lv_obj_get_group(obj);
529
530 /*Reset all input devices if the object to delete is used*/
531 lv_indev_t * indev = lv_indev_get_next(NULL);
532 while(indev) {
533 lv_indev_type_t indev_type = lv_indev_get_type(indev);
534 if(indev_type == LV_INDEV_TYPE_POINTER || indev_type == LV_INDEV_TYPE_BUTTON) {
535 if(indev->pointer.act_obj == obj || indev->pointer.last_obj == obj || indev->pointer.scroll_obj == obj) {
536 obj_indev_reset(indev, obj);
537 }
538 if(indev->pointer.last_pressed == obj) {
539 indev->pointer.last_pressed = NULL;
540 }
541 if(indev->pointer.last_hovered == obj) {
542 indev->pointer.last_hovered = NULL;
543 }
544 }
545
546 if(indev->group == group && obj == lv_indev_get_active_obj()) {
547 obj_indev_reset(indev, obj);
548 }
549 indev = lv_indev_get_next(indev);
550 }
551
552 /*Delete all pending async del-s*/
553 lv_result_t async_cancel_res = LV_RESULT_OK;
554 while(async_cancel_res == LV_RESULT_OK) {
555 async_cancel_res = lv_async_call_cancel(lv_obj_delete_async_cb, obj);
556 }
557
558 /*All children deleted. Now clean up the object specific data*/
559 lv_obj_destruct(obj);
560
561 /*Remove the screen for the screen list*/
562 if(obj->parent == NULL) {
563 lv_display_t * disp = lv_obj_get_display(obj);
564 uint32_t i;
565 /*Find the screen in the list*/
566 for(i = 0; i < disp->screen_cnt; i++) {
567 if(disp->screens[i] == obj) break;
568 }
569
570 uint32_t id = i;
571 for(i = id; i < disp->screen_cnt - 1; i++) {
572 disp->screens[i] = disp->screens[i + 1];
573 }
574 disp->screen_cnt--;
575 disp->screens = lv_realloc(disp->screens, disp->screen_cnt * sizeof(lv_obj_t *));
576 }
577 /*Remove the object from the child list of its parent*/
578 else {
579 int32_t id = lv_obj_get_index(obj);
580 uint16_t i;
581 for(i = id; i < obj->parent->spec_attr->child_cnt - 1; i++) {
582 obj->parent->spec_attr->children[i] = obj->parent->spec_attr->children[i + 1];
583 }
584 obj->parent->spec_attr->child_cnt--;
585 obj->parent->spec_attr->children = lv_realloc(obj->parent->spec_attr->children,
586 obj->parent->spec_attr->child_cnt * sizeof(lv_obj_t *));
587 }
588
589 /*Free the object itself*/
590 lv_free(obj);
591 }
592
walk_core(lv_obj_t * obj,lv_obj_tree_walk_cb_t cb,void * user_data)593 static lv_obj_tree_walk_res_t walk_core(lv_obj_t * obj, lv_obj_tree_walk_cb_t cb, void * user_data)
594 {
595 lv_obj_tree_walk_res_t res = LV_OBJ_TREE_WALK_NEXT;
596
597 if(obj == NULL) {
598 lv_display_t * disp = lv_display_get_next(NULL);
599 while(disp) {
600 uint32_t i;
601 for(i = 0; i < disp->screen_cnt; i++) {
602 walk_core(disp->screens[i], cb, user_data);
603 }
604 disp = lv_display_get_next(disp);
605 }
606 return LV_OBJ_TREE_WALK_END; /*The value doesn't matter as it wasn't called recursively*/
607 }
608
609 res = cb(obj, user_data);
610
611 if(res == LV_OBJ_TREE_WALK_END) return LV_OBJ_TREE_WALK_END;
612
613 if(res != LV_OBJ_TREE_WALK_SKIP_CHILDREN) {
614 uint32_t i;
615 for(i = 0; i < lv_obj_get_child_count(obj); i++) {
616 res = walk_core(lv_obj_get_child(obj, i), cb, user_data);
617 if(res == LV_OBJ_TREE_WALK_END) return LV_OBJ_TREE_WALK_END;
618 }
619 }
620 return LV_OBJ_TREE_WALK_NEXT;
621 }
622
dump_tree_core(lv_obj_t * obj,int32_t depth)623 static void dump_tree_core(lv_obj_t * obj, int32_t depth)
624 {
625 #if LV_USE_LOG
626 const char * id;
627
628 #if LV_USE_OBJ_ID
629 char buf[OBJ_DUMP_STRING_LEN];
630 id = lv_obj_stringify_id(obj, buf, sizeof(buf));
631 if(id == NULL) id = "obj0";
632 #else
633 id = "obj0";
634 #endif
635
636 /*id of `obj0` is an invalid id for builtin id*/
637 LV_LOG_USER("parent:%p, obj:%p, id:%s;", (void *)(obj ? obj->parent : NULL), (void *)obj, id);
638 #endif /*LV_USE_LOG*/
639
640 if(obj && obj->spec_attr && obj->spec_attr->child_cnt) {
641 for(uint32_t i = 0; i < obj->spec_attr->child_cnt; i++) {
642 dump_tree_core(lv_obj_get_child(obj, i), depth + 1);
643 }
644 }
645 }
646
lv_obj_get_first_not_deleting_child(lv_obj_t * obj)647 static lv_obj_t * lv_obj_get_first_not_deleting_child(lv_obj_t * obj)
648 {
649 LV_ASSERT_OBJ(obj, MY_CLASS);
650
651 if(obj->spec_attr == NULL) return NULL;
652
653 int32_t i;
654 int32_t cnt = (int32_t)obj->spec_attr->child_cnt;
655 for(i = 0; i < cnt; i++) {
656 if(!obj->spec_attr->children[i]->is_deleting) {
657 return obj->spec_attr->children[i];
658 }
659 }
660
661 return NULL;
662 }
663