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