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