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