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