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