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
walk_core(lv_obj_t * obj,lv_obj_tree_walk_cb_t cb,void * user_data)419 static lv_obj_tree_walk_res_t walk_core(lv_obj_t * obj, lv_obj_tree_walk_cb_t cb, void * user_data)
420 {
421 lv_obj_tree_walk_res_t res = LV_OBJ_TREE_WALK_NEXT;
422
423 if(obj == NULL) {
424 lv_disp_t * disp = lv_disp_get_next(NULL);
425 while(disp) {
426 uint32_t i;
427 for(i = 0; i < disp->screen_cnt; i++) {
428 walk_core(disp->screens[i], cb, user_data);
429 }
430 disp = lv_disp_get_next(disp);
431 }
432 return LV_OBJ_TREE_WALK_END; /*The value doesn't matter as it wasn't called recursively*/
433 }
434
435 res = cb(obj, user_data);
436
437 if(res == LV_OBJ_TREE_WALK_END) return LV_OBJ_TREE_WALK_END;
438
439 if(res != LV_OBJ_TREE_WALK_SKIP_CHILDREN) {
440 uint32_t i;
441 for(i = 0; i < lv_obj_get_child_cnt(obj); i++) {
442 res = walk_core(lv_obj_get_child(obj, i), cb, user_data);
443 if(res == LV_OBJ_TREE_WALK_END) return LV_OBJ_TREE_WALK_END;
444 }
445 }
446 return LV_OBJ_TREE_WALK_NEXT;
447 }
448