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