1 /**
2 * @file lv_flex.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9 #include "lv_flex.h"
10 #include "../lv_layout.h"
11 #include "../../core/lv_obj_private.h"
12
13 #if LV_USE_FLEX
14
15 #include "../../core/lv_global.h"
16 /*********************
17 * DEFINES
18 *********************/
19 #define layout_list_def LV_GLOBAL_DEFAULT()->layout_list
20
21 /**********************
22 * TYPEDEFS
23 **********************/
24 typedef struct {
25 lv_flex_align_t main_place;
26 lv_flex_align_t cross_place;
27 lv_flex_align_t track_place;
28 uint8_t row : 1;
29 uint8_t wrap : 1;
30 uint8_t rev : 1;
31 } flex_t;
32
33 typedef struct {
34 lv_obj_t * item;
35 int32_t min_size;
36 int32_t max_size;
37 int32_t final_size;
38 uint32_t grow_value;
39 uint32_t clamped : 1;
40 } grow_dsc_t;
41
42 typedef struct {
43 int32_t track_cross_size;
44 int32_t track_main_size; /*For all items*/
45 int32_t track_fix_main_size; /*For non grow items*/
46 uint32_t item_cnt;
47 grow_dsc_t * grow_dsc;
48 uint32_t grow_item_cnt;
49 uint32_t grow_dsc_calc : 1;
50 } track_t;
51
52 /**********************
53 * GLOBAL PROTOTYPES
54 **********************/
55
56 /**********************
57 * STATIC PROTOTYPES
58 **********************/
59 static void flex_update(lv_obj_t * cont, void * user_data);
60 static int32_t find_track_end(lv_obj_t * cont, flex_t * f, int32_t item_start_id, int32_t max_main_size,
61 int32_t item_gap, track_t * t);
62 static void children_repos(lv_obj_t * cont, flex_t * f, int32_t item_first_id, int32_t item_last_id, int32_t abs_x,
63 int32_t abs_y, int32_t max_main_size, int32_t item_gap, track_t * t);
64 static void place_content(lv_flex_align_t place, int32_t max_size, int32_t content_size, int32_t item_cnt,
65 int32_t * start_pos, int32_t * gap);
66 static lv_obj_t * get_next_item(lv_obj_t * cont, bool rev, int32_t * item_id);
67 static int32_t lv_obj_get_width_with_margin(const lv_obj_t * obj);
68 static int32_t lv_obj_get_height_with_margin(const lv_obj_t * obj);
69
70 /**********************
71 * GLOBAL VARIABLES
72 **********************/
73
74 /**********************
75 * STATIC VARIABLES
76 **********************/
77
78 /**********************
79 * MACROS
80 **********************/
81 #if LV_USE_LOG && LV_LOG_TRACE_LAYOUT
82 #define LV_TRACE_LAYOUT(...) LV_LOG_TRACE(__VA_ARGS__)
83 #else
84 #define LV_TRACE_LAYOUT(...)
85 #endif
86
87 /**********************
88 * GLOBAL FUNCTIONS
89 **********************/
90
91 /*=====================
92 * Setter functions
93 *====================*/
94
lv_flex_init(void)95 void lv_flex_init(void)
96 {
97 layout_list_def[LV_LAYOUT_FLEX].cb = flex_update;
98 layout_list_def[LV_LAYOUT_FLEX].user_data = NULL;
99 }
100
lv_obj_set_flex_flow(lv_obj_t * obj,lv_flex_flow_t flow)101 void lv_obj_set_flex_flow(lv_obj_t * obj, lv_flex_flow_t flow)
102 {
103 lv_obj_set_style_flex_flow(obj, flow, 0);
104 lv_obj_set_style_layout(obj, LV_LAYOUT_FLEX, 0);
105 }
106
lv_obj_set_flex_align(lv_obj_t * obj,lv_flex_align_t main_place,lv_flex_align_t cross_place,lv_flex_align_t track_place)107 void lv_obj_set_flex_align(lv_obj_t * obj, lv_flex_align_t main_place, lv_flex_align_t cross_place,
108 lv_flex_align_t track_place)
109 {
110 lv_obj_set_style_flex_main_place(obj, main_place, 0);
111 lv_obj_set_style_flex_cross_place(obj, cross_place, 0);
112 lv_obj_set_style_flex_track_place(obj, track_place, 0);
113 lv_obj_set_style_layout(obj, LV_LAYOUT_FLEX, 0);
114 }
115
lv_obj_set_flex_grow(lv_obj_t * obj,uint8_t grow)116 void lv_obj_set_flex_grow(lv_obj_t * obj, uint8_t grow)
117 {
118 lv_obj_set_style_flex_grow(obj, grow, 0);
119 lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj));
120 }
121
122 /**********************
123 * STATIC FUNCTIONS
124 **********************/
125
flex_update(lv_obj_t * cont,void * user_data)126 static void flex_update(lv_obj_t * cont, void * user_data)
127 {
128 LV_LOG_INFO("update %p container", (void *)cont);
129 LV_UNUSED(user_data);
130
131 flex_t f;
132 lv_flex_flow_t flow = lv_obj_get_style_flex_flow(cont, LV_PART_MAIN);
133 f.row = flow & LV_FLEX_COLUMN ? 0 : 1;
134 f.wrap = flow & LV_FLEX_WRAP ? 1 : 0;
135 f.rev = flow & LV_FLEX_REVERSE ? 1 : 0;
136 f.main_place = lv_obj_get_style_flex_main_place(cont, LV_PART_MAIN);
137 f.cross_place = lv_obj_get_style_flex_cross_place(cont, LV_PART_MAIN);
138 f.track_place = lv_obj_get_style_flex_track_place(cont, LV_PART_MAIN);
139
140 bool rtl = lv_obj_get_style_base_dir(cont, LV_PART_MAIN) == LV_BASE_DIR_RTL;
141 int32_t track_gap = !f.row ? lv_obj_get_style_pad_column(cont, LV_PART_MAIN) : lv_obj_get_style_pad_row(cont,
142 LV_PART_MAIN);
143 int32_t item_gap = f.row ? lv_obj_get_style_pad_column(cont, LV_PART_MAIN) : lv_obj_get_style_pad_row(cont,
144 LV_PART_MAIN);
145 int32_t max_main_size = (f.row ? lv_obj_get_content_width(cont) : lv_obj_get_content_height(cont));
146 int32_t abs_y = cont->coords.y1 + lv_obj_get_style_space_top(cont,
147 LV_PART_MAIN) - lv_obj_get_scroll_y(cont);
148 int32_t abs_x = cont->coords.x1 + lv_obj_get_style_space_left(cont,
149 LV_PART_MAIN) - lv_obj_get_scroll_x(cont);
150
151 lv_flex_align_t track_cross_place = f.track_place;
152 int32_t * cross_pos = (f.row ? &abs_y : &abs_x);
153
154 int32_t w_set = lv_obj_get_style_width(cont, LV_PART_MAIN);
155 int32_t h_set = lv_obj_get_style_height(cont, LV_PART_MAIN);
156
157 /*Content sized objects should squeeze the gap between the children, therefore any alignment will look like `START`*/
158 if((f.row && h_set == LV_SIZE_CONTENT && cont->h_layout == 0) ||
159 (!f.row && w_set == LV_SIZE_CONTENT && cont->w_layout == 0)) {
160 track_cross_place = LV_FLEX_ALIGN_START;
161 }
162
163 if(rtl && !f.row) {
164 if(track_cross_place == LV_FLEX_ALIGN_START) track_cross_place = LV_FLEX_ALIGN_END;
165 else if(track_cross_place == LV_FLEX_ALIGN_END) track_cross_place = LV_FLEX_ALIGN_START;
166 }
167
168 int32_t total_track_cross_size = 0;
169 int32_t gap = 0;
170 uint32_t track_cnt = 0;
171 int32_t track_first_item;
172 int32_t next_track_first_item;
173
174 if(track_cross_place != LV_FLEX_ALIGN_START) {
175 track_first_item = f.rev ? cont->spec_attr->child_cnt - 1 : 0;
176 track_t t;
177 while(track_first_item < (int32_t)cont->spec_attr->child_cnt && track_first_item >= 0) {
178 /*Search the first item of the next row*/
179 t.grow_dsc_calc = 0;
180 next_track_first_item = find_track_end(cont, &f, track_first_item, max_main_size, item_gap, &t);
181 total_track_cross_size += t.track_cross_size + track_gap;
182 track_cnt++;
183 track_first_item = next_track_first_item;
184 }
185
186 if(track_cnt) total_track_cross_size -= track_gap; /*No gap after the last track*/
187
188 /*Place the tracks to get the start position*/
189 int32_t max_cross_size = (f.row ? lv_obj_get_content_height(cont) : lv_obj_get_content_width(cont));
190 place_content(track_cross_place, max_cross_size, total_track_cross_size, track_cnt, cross_pos, &gap);
191 }
192
193 track_first_item = f.rev ? cont->spec_attr->child_cnt - 1 : 0;
194
195 if(rtl && !f.row) {
196 *cross_pos += total_track_cross_size;
197 }
198
199 while(track_first_item < (int32_t)cont->spec_attr->child_cnt && track_first_item >= 0) {
200 track_t t;
201 t.grow_dsc_calc = 1;
202 /*Search the first item of the next row*/
203 next_track_first_item = find_track_end(cont, &f, track_first_item, max_main_size, item_gap, &t);
204
205 if(rtl && !f.row) {
206 *cross_pos -= t.track_cross_size;
207 }
208 children_repos(cont, &f, track_first_item, next_track_first_item, abs_x, abs_y, max_main_size, item_gap, &t);
209 track_first_item = next_track_first_item;
210 lv_free(t.grow_dsc);
211 t.grow_dsc = NULL;
212 if(rtl && !f.row) {
213 *cross_pos -= gap + track_gap;
214 }
215 else {
216 *cross_pos += t.track_cross_size + gap + track_gap;
217 }
218 }
219 LV_ASSERT_MEM_INTEGRITY();
220
221 if(w_set == LV_SIZE_CONTENT || h_set == LV_SIZE_CONTENT) {
222 lv_obj_refr_size(cont);
223 }
224
225 lv_obj_send_event(cont, LV_EVENT_LAYOUT_CHANGED, NULL);
226
227 LV_TRACE_LAYOUT("finished");
228 }
229
230 /**
231 * Find the last item of a track
232 */
find_track_end(lv_obj_t * cont,flex_t * f,int32_t item_start_id,int32_t max_main_size,int32_t item_gap,track_t * t)233 static int32_t find_track_end(lv_obj_t * cont, flex_t * f, int32_t item_start_id, int32_t max_main_size,
234 int32_t item_gap, track_t * t)
235 {
236 int32_t w_set = lv_obj_get_style_width(cont, LV_PART_MAIN);
237 int32_t h_set = lv_obj_get_style_height(cont, LV_PART_MAIN);
238
239 /*Can't wrap if the size is auto (i.e. the size depends on the children)*/
240 if(f->wrap && ((f->row && w_set == LV_SIZE_CONTENT) || (!f->row && h_set == LV_SIZE_CONTENT))) {
241 f->wrap = false;
242 }
243 int32_t(*get_main_size)(const lv_obj_t *) = (f->row ? lv_obj_get_width_with_margin : lv_obj_get_height_with_margin);
244 int32_t(*get_cross_size)(const lv_obj_t *) = (!f->row ? lv_obj_get_width_with_margin :
245 lv_obj_get_height_with_margin);
246
247 t->track_main_size = 0;
248 t->track_fix_main_size = 0;
249 t->grow_item_cnt = 0;
250 t->track_cross_size = 0;
251 t->item_cnt = 0;
252 t->grow_dsc = NULL;
253
254 int32_t item_id = item_start_id;
255 int32_t grow_min_size_sum = 0;
256 lv_obj_t * item = lv_obj_get_child(cont, item_id);
257 while(item) {
258 if(item_id != item_start_id && lv_obj_has_flag(item, LV_OBJ_FLAG_FLEX_IN_NEW_TRACK)) break;
259
260 if(!lv_obj_has_flag_any(item, LV_OBJ_FLAG_IGNORE_LAYOUT | LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) {
261 uint8_t grow_value = lv_obj_get_style_flex_grow(item, LV_PART_MAIN);
262 if(grow_value) {
263 int32_t min_size = f->row ? lv_obj_get_style_min_width(item, LV_PART_MAIN)
264 : lv_obj_get_style_min_height(item, LV_PART_MAIN);
265
266 int32_t req_size = min_size;
267 if(item_id != item_start_id) req_size += item_gap; /*No gap before the first item*/
268
269 /*Wrap if can't fit*/
270 if(f->wrap && t->track_fix_main_size + grow_min_size_sum + req_size > max_main_size) break;
271
272 grow_min_size_sum += req_size;
273 if(item_id != item_start_id) {
274 t->track_fix_main_size += item_gap; /*The gap is always taken from the space*/
275 }
276
277 t->grow_item_cnt++;
278
279 if(t->grow_dsc_calc) {
280 grow_dsc_t * new_dsc = lv_realloc(t->grow_dsc, sizeof(grow_dsc_t) * (t->grow_item_cnt));
281 LV_ASSERT_MALLOC(new_dsc);
282 if(new_dsc == NULL) return item_id;
283
284
285 new_dsc[t->grow_item_cnt - 1].item = item;
286 new_dsc[t->grow_item_cnt - 1].min_size = f->row ? lv_obj_get_style_min_width(item, LV_PART_MAIN)
287 : lv_obj_get_style_min_height(item, LV_PART_MAIN);
288 new_dsc[t->grow_item_cnt - 1].max_size = f->row ? lv_obj_get_style_max_width(item, LV_PART_MAIN)
289 : lv_obj_get_style_max_height(item, LV_PART_MAIN);
290 new_dsc[t->grow_item_cnt - 1].grow_value = grow_value;
291 new_dsc[t->grow_item_cnt - 1].clamped = 0;
292
293 t->grow_dsc = new_dsc;
294 }
295 }
296 else {
297 int32_t item_size = get_main_size(item);
298 int32_t req_size = item_size;
299 if(item_id != item_start_id) req_size += item_gap; /*No gap before the first item*/
300 if(f->wrap && t->track_fix_main_size + grow_min_size_sum + req_size > max_main_size) break;
301 t->track_fix_main_size += req_size;
302 }
303
304 t->track_cross_size = LV_MAX(get_cross_size(item), t->track_cross_size);
305 t->item_cnt++;
306 }
307
308 item_id += f->rev ? -1 : +1;
309 if(item_id < 0) break;
310 item = lv_obj_get_child(cont, item_id);
311 }
312
313 /*If there is at least one "grow item" the track takes the full space*/
314 t->track_main_size = t->grow_item_cnt ? max_main_size : t->track_fix_main_size;
315
316 /*Have at least one item in a row*/
317 if(item && item_id == item_start_id) {
318 item = cont->spec_attr->children[item_id];
319 get_next_item(cont, f->rev, &item_id);
320 if(item) {
321 t->track_cross_size = get_cross_size(item);
322 t->track_main_size = get_main_size(item);
323 t->item_cnt = 1;
324 }
325 }
326
327 return item_id;
328 }
329
330 /**
331 * Position the children in the same track
332 */
children_repos(lv_obj_t * cont,flex_t * f,int32_t item_first_id,int32_t item_last_id,int32_t abs_x,int32_t abs_y,int32_t max_main_size,int32_t item_gap,track_t * t)333 static void children_repos(lv_obj_t * cont, flex_t * f, int32_t item_first_id, int32_t item_last_id, int32_t abs_x,
334 int32_t abs_y, int32_t max_main_size, int32_t item_gap, track_t * t)
335 {
336 void (*area_set_main_size)(lv_area_t *, int32_t) = (f->row ? lv_area_set_width : lv_area_set_height);
337 int32_t (*area_get_main_size)(const lv_area_t *) = (f->row ? lv_area_get_width : lv_area_get_height);
338 int32_t (*area_get_cross_size)(const lv_area_t *) = (!f->row ? lv_area_get_width : lv_area_get_height);
339
340 typedef int32_t (*margin_func_t)(const lv_obj_t *, uint32_t);
341 margin_func_t get_margin_main_start = (f->row ? lv_obj_get_style_margin_left : lv_obj_get_style_margin_top);
342 margin_func_t get_margin_main_end = (f->row ? lv_obj_get_style_margin_right : lv_obj_get_style_margin_bottom);
343 margin_func_t get_margin_cross_start = (!f->row ? lv_obj_get_style_margin_left : lv_obj_get_style_margin_top);
344 margin_func_t get_margin_cross_end = (!f->row ? lv_obj_get_style_margin_right : lv_obj_get_style_margin_bottom);
345
346 /*Calculate the size of grow items first*/
347 uint32_t i;
348 bool grow_reiterate = true;
349 while(grow_reiterate && t->grow_item_cnt) {
350 grow_reiterate = false;
351 int32_t grow_value_sum = 0;
352 int32_t grow_max_size = t->track_main_size - t->track_fix_main_size;
353 for(i = 0; i < t->grow_item_cnt; i++) {
354 if(t->grow_dsc[i].clamped == 0) {
355 grow_value_sum += t->grow_dsc[i].grow_value;
356 }
357 else {
358 grow_max_size -= t->grow_dsc[i].final_size;
359 }
360 }
361 int32_t grow_unit;
362
363 for(i = 0; i < t->grow_item_cnt; i++) {
364 if(t->grow_dsc[i].clamped == 0) {
365 LV_ASSERT(grow_value_sum != 0);
366 grow_unit = grow_max_size / grow_value_sum;
367 int32_t size = grow_unit * t->grow_dsc[i].grow_value;
368 int32_t size_clamp = LV_CLAMP(t->grow_dsc[i].min_size, size, t->grow_dsc[i].max_size);
369
370 if(size_clamp != size) {
371 t->grow_dsc[i].clamped = 1;
372 grow_reiterate = true;
373 }
374 t->grow_dsc[i].final_size = size_clamp;
375 grow_value_sum -= t->grow_dsc[i].grow_value;
376 grow_max_size -= t->grow_dsc[i].final_size;
377 }
378 }
379 }
380
381 bool rtl = lv_obj_get_style_base_dir(cont, LV_PART_MAIN) == LV_BASE_DIR_RTL;
382
383 int32_t main_pos = 0;
384
385 int32_t place_gap = 0;
386 place_content(f->main_place, max_main_size, t->track_main_size, t->item_cnt, &main_pos, &place_gap);
387 if(f->row && rtl) main_pos += lv_obj_get_content_width(cont);
388
389 lv_obj_t * item = lv_obj_get_child(cont, item_first_id);
390 /*Reposition the children*/
391 while(item && item_first_id != item_last_id) {
392 if(lv_obj_has_flag_any(item, LV_OBJ_FLAG_IGNORE_LAYOUT | LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) {
393 item = get_next_item(cont, f->rev, &item_first_id);
394 continue;
395 }
396 int32_t grow_size = lv_obj_get_style_flex_grow(item, LV_PART_MAIN);
397 if(grow_size) {
398 int32_t s = 0;
399 for(i = 0; i < t->grow_item_cnt; i++) {
400 if(t->grow_dsc[i].item == item) {
401 s = t->grow_dsc[i].final_size;
402 break;
403 }
404 }
405
406 if(f->row) {
407 item->w_layout = 1;
408 item->h_layout = 0;
409 }
410 else {
411 item->h_layout = 1;
412 item->w_layout = 0;
413 }
414
415 if(s != area_get_main_size(&item->coords)) {
416 lv_obj_invalidate(item);
417
418 lv_area_t old_coords;
419 lv_area_copy(&old_coords, &item->coords);
420 area_set_main_size(&item->coords, s);
421 lv_obj_send_event(item, LV_EVENT_SIZE_CHANGED, &old_coords);
422 lv_obj_send_event(lv_obj_get_parent(item), LV_EVENT_CHILD_CHANGED, item);
423 lv_obj_invalidate(item);
424 }
425 }
426 else {
427 item->w_layout = 0;
428 item->h_layout = 0;
429 }
430
431 int32_t cross_pos = 0;
432 switch(f->cross_place) {
433 case LV_FLEX_ALIGN_CENTER:
434 /*Round up the cross size to avoid rounding error when dividing by 2
435 *The issue comes up e,g, with column direction with center cross direction if an element's width changes*/
436 cross_pos = (((t->track_cross_size + 1) & (~1)) - area_get_cross_size(&item->coords)) / 2;
437 cross_pos += (get_margin_cross_start(item, LV_PART_MAIN) - get_margin_cross_end(item, LV_PART_MAIN)) / 2;
438 break;
439 case LV_FLEX_ALIGN_END:
440 cross_pos = t->track_cross_size - area_get_cross_size(&item->coords);
441 cross_pos -= get_margin_cross_end(item, LV_PART_MAIN);
442 break;
443 default:
444 cross_pos += get_margin_cross_start(item, LV_PART_MAIN);
445 break;
446 }
447
448 if(f->row && rtl) main_pos -= area_get_main_size(&item->coords);
449
450 /*Handle percentage value of translate*/
451 int32_t tr_x = lv_obj_get_style_translate_x(item, LV_PART_MAIN);
452 int32_t tr_y = lv_obj_get_style_translate_y(item, LV_PART_MAIN);
453 int32_t w = lv_obj_get_width(item);
454 int32_t h = lv_obj_get_height(item);
455 if(LV_COORD_IS_PCT(tr_x)) tr_x = (w * LV_COORD_GET_PCT(tr_x)) / 100;
456 if(LV_COORD_IS_PCT(tr_y)) tr_y = (h * LV_COORD_GET_PCT(tr_y)) / 100;
457
458 int32_t diff_x = abs_x - item->coords.x1 + tr_x;
459 int32_t diff_y = abs_y - item->coords.y1 + tr_y;
460 diff_x += f->row ? main_pos + get_margin_main_start(item, LV_PART_MAIN) : cross_pos;
461 diff_y += f->row ? cross_pos : main_pos + get_margin_main_start(item, LV_PART_MAIN);
462
463 if(diff_x || diff_y) {
464 lv_obj_invalidate(item);
465 item->coords.x1 += diff_x;
466 item->coords.x2 += diff_x;
467 item->coords.y1 += diff_y;
468 item->coords.y2 += diff_y;
469 lv_obj_invalidate(item);
470 lv_obj_move_children_by(item, diff_x, diff_y, false);
471 }
472
473 if(!(f->row && rtl)) main_pos += area_get_main_size(&item->coords) + item_gap + place_gap
474 + get_margin_main_start(item, LV_PART_MAIN)
475 + get_margin_main_end(item, LV_PART_MAIN);
476 else main_pos -= item_gap + place_gap;
477
478 item = get_next_item(cont, f->rev, &item_first_id);
479 }
480 }
481
482 /**
483 * Tell a start coordinate and gap for a placement type.
484 */
place_content(lv_flex_align_t place,int32_t max_size,int32_t content_size,int32_t item_cnt,int32_t * start_pos,int32_t * gap)485 static void place_content(lv_flex_align_t place, int32_t max_size, int32_t content_size, int32_t item_cnt,
486 int32_t * start_pos, int32_t * gap)
487 {
488 if(item_cnt <= 1) {
489 switch(place) {
490 case LV_FLEX_ALIGN_SPACE_AROUND:
491 case LV_FLEX_ALIGN_SPACE_EVENLY:
492 place = LV_FLEX_ALIGN_CENTER;
493 break;
494 default:
495 break;
496 }
497 }
498
499 switch(place) {
500 case LV_FLEX_ALIGN_CENTER:
501 *gap = 0;
502 *start_pos += (max_size - content_size) / 2;
503 break;
504 case LV_FLEX_ALIGN_END:
505 *gap = 0;
506 *start_pos += max_size - content_size;
507 break;
508 case LV_FLEX_ALIGN_SPACE_BETWEEN:
509 if(item_cnt > 1) *gap = (int32_t)(max_size - content_size) / (int32_t)(item_cnt - 1);
510 break;
511 case LV_FLEX_ALIGN_SPACE_AROUND:
512 *gap += (int32_t)(max_size - content_size) / (int32_t)(item_cnt);
513 *start_pos += *gap / 2;
514 break;
515 case LV_FLEX_ALIGN_SPACE_EVENLY:
516 *gap = (int32_t)(max_size - content_size) / (int32_t)(item_cnt + 1);
517 *start_pos += *gap;
518 break;
519 default:
520 *gap = 0;
521 }
522 }
523
get_next_item(lv_obj_t * cont,bool rev,int32_t * item_id)524 static lv_obj_t * get_next_item(lv_obj_t * cont, bool rev, int32_t * item_id)
525 {
526 if(rev) {
527 (*item_id)--;
528 if(*item_id >= 0) return cont->spec_attr->children[*item_id];
529 else return NULL;
530 }
531 else {
532 (*item_id)++;
533 if((*item_id) < (int32_t)cont->spec_attr->child_cnt) return cont->spec_attr->children[*item_id];
534 else return NULL;
535 }
536 }
537
lv_obj_get_width_with_margin(const lv_obj_t * obj)538 static int32_t lv_obj_get_width_with_margin(const lv_obj_t * obj)
539 {
540 return lv_obj_get_style_margin_left(obj, LV_PART_MAIN)
541 + lv_obj_get_width(obj)
542 + lv_obj_get_style_margin_right(obj, LV_PART_MAIN);
543 }
544
lv_obj_get_height_with_margin(const lv_obj_t * obj)545 static int32_t lv_obj_get_height_with_margin(const lv_obj_t * obj)
546 {
547 return lv_obj_get_style_margin_top(obj, LV_PART_MAIN)
548 + lv_obj_get_height(obj)
549 + lv_obj_get_style_margin_bottom(obj, LV_PART_MAIN);
550 }
551
552 #endif /*LV_USE_FLEX*/
553