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