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