1 /**
2  * @file lv_obj_tree.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include <stdlib.h>
10 
11 #include "lv_obj.h"
12 #include "lv_indev.h"
13 #include "../misc/lv_anim.h"
14 #include "../misc/lv_gc.h"
15 #include "../misc/lv_async.h"
16 
17 /*********************
18  *      DEFINES
19  *********************/
20 #define MY_CLASS &lv_obj_class
21 
22 /**********************
23  *      TYPEDEFS
24  **********************/
25 
26 /**********************
27  *  STATIC PROTOTYPES
28  **********************/
29 static void lv_obj_del_async_cb(void * obj);
30 static void obj_del_core(lv_obj_t * obj);
31 static lv_obj_tree_walk_res_t walk_core(lv_obj_t * obj, lv_obj_tree_walk_cb_t cb, void * user_data);
32 
33 /**********************
34  *  STATIC VARIABLES
35  **********************/
36 
37 /**********************
38  *      MACROS
39  **********************/
40 
41 /**********************
42  *   GLOBAL FUNCTIONS
43  **********************/
44 
lv_obj_del(lv_obj_t * obj)45 void lv_obj_del(lv_obj_t * obj)
46 {
47     LV_LOG_TRACE("begin (delete %p)", (void *)obj);
48     LV_ASSERT_OBJ(obj, MY_CLASS);
49     lv_obj_invalidate(obj);
50 
51     lv_obj_t * par = lv_obj_get_parent(obj);
52     if(par) {
53         lv_obj_scrollbar_invalidate(par);
54     }
55 
56     lv_disp_t * disp = NULL;
57     bool act_scr_del = false;
58     if(par == NULL) {
59         disp = lv_obj_get_disp(obj);
60         if(!disp) return;   /*Shouldn't happen*/
61         if(disp->act_scr == obj) act_scr_del = true;
62     }
63 
64     obj_del_core(obj);
65 
66     /*Call the ancestor's event handler to the parent to notify it about the child delete*/
67     if(par) {
68         lv_obj_update_layout(par);
69         lv_obj_readjust_scroll(par, LV_ANIM_OFF);
70         lv_obj_scrollbar_invalidate(par);
71         lv_event_send(par, LV_EVENT_CHILD_CHANGED, NULL);
72         lv_event_send(par, LV_EVENT_CHILD_DELETED, NULL);
73     }
74 
75     /*Handle if the active screen was deleted*/
76     if(act_scr_del) {
77         LV_LOG_WARN("the active screen was deleted");
78         disp->act_scr = NULL;
79     }
80 
81     LV_ASSERT_MEM_INTEGRITY();
82     LV_LOG_TRACE("finished (delete %p)", (void *)obj);
83 }
84 
lv_obj_clean(lv_obj_t * obj)85 void lv_obj_clean(lv_obj_t * obj)
86 {
87     LV_LOG_TRACE("begin (delete %p)", (void *)obj);
88     LV_ASSERT_OBJ(obj, MY_CLASS);
89 
90     lv_obj_invalidate(obj);
91 
92     lv_obj_t * child = lv_obj_get_child(obj, 0);
93     while(child) {
94         obj_del_core(child);
95         child = lv_obj_get_child(obj, 0);
96     }
97     /*Just to remove scroll animations if any*/
98     lv_obj_scroll_to(obj, 0, 0, LV_ANIM_OFF);
99     if(obj->spec_attr) {
100         obj->spec_attr->scroll.x = 0;
101         obj->spec_attr->scroll.y = 0;
102     }
103 
104     LV_ASSERT_MEM_INTEGRITY();
105 
106     LV_LOG_TRACE("finished (delete %p)", (void *)obj);
107 }
108 
lv_obj_del_delayed(lv_obj_t * obj,uint32_t delay_ms)109 void lv_obj_del_delayed(lv_obj_t * obj, uint32_t delay_ms)
110 {
111     lv_anim_t a;
112     lv_anim_init(&a);
113     lv_anim_set_var(&a, obj);
114     lv_anim_set_exec_cb(&a, NULL);
115     lv_anim_set_time(&a, 1);
116     lv_anim_set_delay(&a, delay_ms);
117     lv_anim_set_ready_cb(&a, lv_obj_del_anim_ready_cb);
118     lv_anim_start(&a);
119 }
120 
lv_obj_del_anim_ready_cb(lv_anim_t * a)121 void lv_obj_del_anim_ready_cb(lv_anim_t * a)
122 {
123     lv_obj_del(a->var);
124 }
125 
lv_obj_del_async(lv_obj_t * obj)126 void lv_obj_del_async(lv_obj_t * obj)
127 {
128     LV_ASSERT_OBJ(obj, MY_CLASS);
129     lv_async_call(lv_obj_del_async_cb, obj);
130 }
131 
lv_obj_set_parent(lv_obj_t * obj,lv_obj_t * parent)132 void lv_obj_set_parent(lv_obj_t * obj, lv_obj_t * parent)
133 {
134     LV_ASSERT_OBJ(obj, MY_CLASS);
135     LV_ASSERT_OBJ(parent, MY_CLASS);
136 
137     if(obj->parent == NULL) {
138         LV_LOG_WARN("Can't set the parent of a screen");
139         return;
140     }
141 
142     if(parent == NULL) {
143         LV_LOG_WARN("Can't set parent == NULL to an object");
144         return;
145     }
146 
147     lv_obj_invalidate(obj);
148 
149     lv_obj_allocate_spec_attr(parent);
150 
151     lv_obj_t * old_parent = obj->parent;
152     /*Remove the object from the old parent's child list*/
153     int32_t i;
154     for(i = lv_obj_get_index(obj); i <= (int32_t)lv_obj_get_child_cnt(old_parent) - 2; i++) {
155         old_parent->spec_attr->children[i] = old_parent->spec_attr->children[i + 1];
156     }
157     old_parent->spec_attr->child_cnt--;
158     if(old_parent->spec_attr->child_cnt) {
159         old_parent->spec_attr->children = lv_mem_realloc(old_parent->spec_attr->children,
160                                                          old_parent->spec_attr->child_cnt * (sizeof(lv_obj_t *)));
161     }
162     else {
163         lv_mem_free(old_parent->spec_attr->children);
164         old_parent->spec_attr->children = NULL;
165     }
166 
167     /*Add the child to the new parent as the last (newest child)*/
168     parent->spec_attr->child_cnt++;
169     parent->spec_attr->children = lv_mem_realloc(parent->spec_attr->children,
170                                                  parent->spec_attr->child_cnt * (sizeof(lv_obj_t *)));
171     parent->spec_attr->children[lv_obj_get_child_cnt(parent) - 1] = obj;
172 
173     obj->parent = parent;
174 
175     /*Notify the original parent because one of its children is lost*/
176     lv_obj_readjust_scroll(old_parent, LV_ANIM_OFF);
177     lv_obj_scrollbar_invalidate(old_parent);
178     lv_event_send(old_parent, LV_EVENT_CHILD_CHANGED, obj);
179     lv_event_send(old_parent, LV_EVENT_CHILD_DELETED, NULL);
180 
181     /*Notify the new parent about the child*/
182     lv_event_send(parent, LV_EVENT_CHILD_CHANGED, obj);
183     lv_event_send(parent, LV_EVENT_CHILD_CREATED, NULL);
184 
185     lv_obj_mark_layout_as_dirty(obj);
186 
187     lv_obj_invalidate(obj);
188 }
189 
lv_obj_move_to_index(lv_obj_t * obj,int32_t index)190 void lv_obj_move_to_index(lv_obj_t * obj, int32_t index)
191 {
192     LV_ASSERT_OBJ(obj, MY_CLASS);
193 
194     if(index < 0) {
195         index = lv_obj_get_child_cnt(lv_obj_get_parent(obj)) + index;
196     }
197 
198     const int32_t old_index = lv_obj_get_index(obj);
199 
200     lv_obj_t * parent = lv_obj_get_parent(obj);
201 
202     if(index < 0) return;
203     if(index >= (int32_t) lv_obj_get_child_cnt(parent)) return;
204     if(index == old_index) return;
205 
206     int32_t i = old_index;
207     if(index < old_index) {
208         while(i > index)  {
209             parent->spec_attr->children[i] = parent->spec_attr->children[i - 1];
210             i--;
211         }
212     }
213     else {
214         while(i < index) {
215             parent->spec_attr->children[i] = parent->spec_attr->children[i + 1];
216             i++;
217         }
218     }
219 
220     parent->spec_attr->children[index] = obj;
221     lv_event_send(parent, LV_EVENT_CHILD_CHANGED, NULL);
222     lv_obj_invalidate(parent);
223 }
224 
lv_obj_swap(lv_obj_t * obj1,lv_obj_t * obj2)225 void lv_obj_swap(lv_obj_t * obj1, lv_obj_t * obj2)
226 {
227     LV_ASSERT_OBJ(obj1, MY_CLASS);
228     LV_ASSERT_OBJ(obj2, MY_CLASS);
229 
230     lv_obj_t * parent = lv_obj_get_parent(obj1);
231     lv_obj_t * parent2 = lv_obj_get_parent(obj2);
232 
233     uint_fast32_t index1 = lv_obj_get_index(obj1);
234     uint_fast32_t index2 = lv_obj_get_index(obj2);
235 
236     lv_event_send(parent2, LV_EVENT_CHILD_DELETED, obj2);
237     lv_event_send(parent, LV_EVENT_CHILD_DELETED, obj1);
238 
239     parent->spec_attr->children[index1] = obj2;
240     parent2->spec_attr->children[index2] = obj1;
241 
242     lv_event_send(parent, LV_EVENT_CHILD_CHANGED, obj2);
243     lv_event_send(parent, LV_EVENT_CHILD_CREATED, obj2);
244     lv_event_send(parent2, LV_EVENT_CHILD_CHANGED, obj1);
245     lv_event_send(parent2, LV_EVENT_CHILD_CREATED, obj1);
246 
247     lv_obj_invalidate(parent);
248 
249     if(parent != parent2) {
250         lv_obj_invalidate(parent2);
251     }
252     lv_group_swap_obj(obj1, obj2);
253 }
254 
lv_obj_get_screen(const lv_obj_t * obj)255 lv_obj_t * lv_obj_get_screen(const lv_obj_t * obj)
256 {
257     LV_ASSERT_OBJ(obj, MY_CLASS);
258 
259     const lv_obj_t * par = obj;
260     const lv_obj_t * act_par;
261 
262     do {
263         act_par = par;
264         par = lv_obj_get_parent(act_par);
265     } while(par != NULL);
266 
267     return (lv_obj_t *)act_par;
268 }
269 
lv_obj_get_disp(const lv_obj_t * obj)270 lv_disp_t * lv_obj_get_disp(const lv_obj_t * obj)
271 {
272     LV_ASSERT_OBJ(obj, MY_CLASS);
273 
274     const lv_obj_t * scr;
275 
276     if(obj->parent == NULL) scr = obj;  /*`obj` is a screen*/
277     else scr = lv_obj_get_screen(obj);  /*get the screen of `obj`*/
278 
279     lv_disp_t * d;
280     _LV_LL_READ(&LV_GC_ROOT(_lv_disp_ll), d) {
281         uint32_t i;
282         for(i = 0; i < d->screen_cnt; i++) {
283             if(d->screens[i] == scr) return d;
284         }
285     }
286 
287     LV_LOG_WARN("No screen found");
288     return NULL;
289 }
290 
lv_obj_get_parent(const lv_obj_t * obj)291 lv_obj_t * lv_obj_get_parent(const lv_obj_t * obj)
292 {
293     if(obj == NULL) return NULL;
294     LV_ASSERT_OBJ(obj, MY_CLASS);
295 
296     return obj->parent;
297 }
298 
lv_obj_get_child(const lv_obj_t * obj,int32_t id)299 lv_obj_t * lv_obj_get_child(const lv_obj_t * obj, int32_t id)
300 {
301     LV_ASSERT_OBJ(obj, MY_CLASS);
302 
303     if(obj->spec_attr == NULL) return NULL;
304 
305     uint32_t idu;
306     if(id < 0) {
307         id = obj->spec_attr->child_cnt + id;
308         if(id < 0) return NULL;
309         idu = (uint32_t) id;
310     }
311     else {
312         idu = id;
313     }
314 
315     if(idu >= obj->spec_attr->child_cnt) return NULL;
316     else return obj->spec_attr->children[id];
317 }
318 
lv_obj_get_child_cnt(const lv_obj_t * obj)319 uint32_t lv_obj_get_child_cnt(const lv_obj_t * obj)
320 {
321     LV_ASSERT_OBJ(obj, MY_CLASS);
322     if(obj->spec_attr == NULL) return 0;
323     return obj->spec_attr->child_cnt;
324 }
325 
lv_obj_get_index(const lv_obj_t * obj)326 uint32_t lv_obj_get_index(const lv_obj_t * obj)
327 {
328     LV_ASSERT_OBJ(obj, MY_CLASS);
329 
330     lv_obj_t * parent = lv_obj_get_parent(obj);
331     if(parent == NULL) return 0;
332 
333     uint32_t i = 0;
334     for(i = 0; i < lv_obj_get_child_cnt(parent); i++) {
335         if(lv_obj_get_child(parent, i) == obj) return i;
336     }
337 
338     return 0xFFFFFFFF; /*Shouldn't happen*/
339 }
340 
lv_obj_tree_walk(lv_obj_t * start_obj,lv_obj_tree_walk_cb_t cb,void * user_data)341 void lv_obj_tree_walk(lv_obj_t * start_obj, lv_obj_tree_walk_cb_t cb, void * user_data)
342 {
343     walk_core(start_obj, cb, user_data);
344 }
345 
346 /**********************
347  *   STATIC FUNCTIONS
348  **********************/
349 
lv_obj_del_async_cb(void * obj)350 static void lv_obj_del_async_cb(void * obj)
351 {
352     LV_ASSERT_OBJ(obj, MY_CLASS);
353 
354     lv_obj_del(obj);
355 }
356 
obj_del_core(lv_obj_t * obj)357 static void obj_del_core(lv_obj_t * obj)
358 {
359     /*Let the user free the resources used in `LV_EVENT_DELETE`*/
360     lv_res_t res = lv_event_send(obj, LV_EVENT_DELETE, NULL);
361     if(res == LV_RES_INV) return;
362 
363     /*Recursively delete the children*/
364     lv_obj_t * child = lv_obj_get_child(obj, 0);
365     while(child) {
366         obj_del_core(child);
367         child = lv_obj_get_child(obj, 0);
368     }
369 
370     lv_group_t * group = lv_obj_get_group(obj);
371 
372     /*Reset all input devices if the object to delete is used*/
373     lv_indev_t * indev = lv_indev_get_next(NULL);
374     while(indev) {
375         if(indev->proc.types.pointer.act_obj == obj || indev->proc.types.pointer.last_obj == obj) {
376             lv_indev_reset(indev, obj);
377         }
378         if(indev->proc.types.pointer.last_pressed == obj) {
379             indev->proc.types.pointer.last_pressed = NULL;
380         }
381 
382         if(indev->group == group && obj == lv_indev_get_obj_act()) {
383             lv_indev_reset(indev, obj);
384         }
385         indev = lv_indev_get_next(indev);
386     }
387 
388     /*All children deleted. Now clean up the object specific data*/
389     _lv_obj_destruct(obj);
390 
391     /*Remove the screen for the screen list*/
392     if(obj->parent == NULL) {
393         lv_disp_t * disp = lv_obj_get_disp(obj);
394         uint32_t i;
395         /*Find the screen in the list*/
396         for(i = 0; i < disp->screen_cnt; i++) {
397             if(disp->screens[i] == obj) break;
398         }
399 
400         uint32_t id = i;
401         for(i = id; i < disp->screen_cnt - 1; i++) {
402             disp->screens[i] = disp->screens[i + 1];
403         }
404         disp->screen_cnt--;
405         disp->screens = lv_mem_realloc(disp->screens, disp->screen_cnt * sizeof(lv_obj_t *));
406     }
407     /*Remove the object from the child list of its parent*/
408     else {
409         uint32_t id = lv_obj_get_index(obj);
410         uint32_t i;
411         for(i = id; i < obj->parent->spec_attr->child_cnt - 1; i++) {
412             obj->parent->spec_attr->children[i] = obj->parent->spec_attr->children[i + 1];
413         }
414         obj->parent->spec_attr->child_cnt--;
415         obj->parent->spec_attr->children = lv_mem_realloc(obj->parent->spec_attr->children,
416                                                           obj->parent->spec_attr->child_cnt * sizeof(lv_obj_t *));
417     }
418 
419     /*Free the object itself*/
420     lv_mem_free(obj);
421 }
422 
423 
walk_core(lv_obj_t * obj,lv_obj_tree_walk_cb_t cb,void * user_data)424 static lv_obj_tree_walk_res_t walk_core(lv_obj_t * obj, lv_obj_tree_walk_cb_t cb, void * user_data)
425 {
426     lv_obj_tree_walk_res_t res = LV_OBJ_TREE_WALK_NEXT;
427 
428     if(obj == NULL) {
429         lv_disp_t * disp = lv_disp_get_next(NULL);
430         while(disp) {
431             uint32_t i;
432             for(i = 0; i < disp->screen_cnt; i++) {
433                 walk_core(disp->screens[i], cb, user_data);
434             }
435             disp = lv_disp_get_next(disp);
436         }
437         return LV_OBJ_TREE_WALK_END;    /*The value doesn't matter as it wasn't called recursively*/
438     }
439 
440     res = cb(obj, user_data);
441 
442     if(res == LV_OBJ_TREE_WALK_END) return LV_OBJ_TREE_WALK_END;
443 
444     if(res != LV_OBJ_TREE_WALK_SKIP_CHILDREN) {
445         uint32_t i;
446         for(i = 0; i < lv_obj_get_child_cnt(obj); i++) {
447             res = walk_core(lv_obj_get_child(obj, i), cb, user_data);
448             if(res == LV_OBJ_TREE_WALK_END) return LV_OBJ_TREE_WALK_END;
449         }
450     }
451     return LV_OBJ_TREE_WALK_NEXT;
452 }
453