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 uint32_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();
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 grow_unit = grow_max_size / grow_value_sum;
432 lv_coord_t size = grow_unit * t->grow_dsc[i].grow_value;
433 lv_coord_t size_clamp = LV_CLAMP(t->grow_dsc[i].min_size, size, t->grow_dsc[i].max_size);
434
435 if(size_clamp != size) {
436 t->grow_dsc[i].clamped = 1;
437 grow_reiterate = true;
438 }
439 t->grow_dsc[i].final_size = size_clamp;
440 grow_value_sum -= t->grow_dsc[i].grow_value;
441 grow_max_size -= t->grow_dsc[i].final_size;
442 }
443 }
444 }
445
446
447 bool rtl = lv_obj_get_style_base_dir(cont, LV_PART_MAIN) == LV_BASE_DIR_RTL ? true : false;
448
449 lv_coord_t main_pos = 0;
450
451 lv_coord_t place_gap = 0;
452 place_content(f->main_place, max_main_size, t->track_main_size, t->item_cnt, &main_pos, &place_gap);
453 if(f->row && rtl) main_pos += lv_obj_get_content_width(cont);
454
455 lv_obj_t * item = lv_obj_get_child(cont, item_first_id);
456 /*Reposition the children*/
457 while(item && item_first_id != item_last_id) {
458 if(lv_obj_has_flag_any(item, LV_OBJ_FLAG_IGNORE_LAYOUT | LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) {
459 item = get_next_item(cont, f->rev, &item_first_id);
460 continue;
461 }
462 lv_coord_t grow_size = lv_obj_get_style_flex_grow(item, LV_PART_MAIN);
463 if(grow_size) {
464 lv_coord_t s = 0;
465 for(i = 0; i < t->grow_item_cnt; i++) {
466 if(t->grow_dsc[i].item == item) {
467 s = t->grow_dsc[i].final_size;
468 break;
469 }
470 }
471
472 if(f->row) item->w_layout = 1;
473 else item->h_layout = 1;
474
475 if(s != area_get_main_size(&item->coords)) {
476 lv_obj_invalidate(item);
477
478 lv_area_t old_coords;
479 lv_area_copy(&old_coords, &item->coords);
480 area_set_main_size(&item->coords, s);
481 lv_event_send(item, LV_EVENT_SIZE_CHANGED, &old_coords);
482 lv_event_send(lv_obj_get_parent(item), LV_EVENT_CHILD_CHANGED, item);
483 lv_obj_invalidate(item);
484 }
485 }
486 else {
487 item->w_layout = 0;
488 item->h_layout = 0;
489 }
490
491 lv_coord_t cross_pos = 0;
492 switch(f->cross_place) {
493 case LV_FLEX_ALIGN_CENTER:
494 /*Round up the cross size to avoid rounding error when dividing by 2
495 *The issue comes up e,g, with column direction with center cross direction if an element's width changes*/
496 cross_pos = (((t->track_cross_size + 1) & (~1)) - area_get_cross_size(&item->coords)) / 2;
497 break;
498 case LV_FLEX_ALIGN_END:
499 cross_pos = t->track_cross_size - area_get_cross_size(&item->coords);
500 break;
501 default:
502 break;
503 }
504
505 if(f->row && rtl) main_pos -= area_get_main_size(&item->coords);
506
507
508 /*Handle percentage value of translate*/
509 lv_coord_t tr_x = lv_obj_get_style_translate_x(item, LV_PART_MAIN);
510 lv_coord_t tr_y = lv_obj_get_style_translate_y(item, LV_PART_MAIN);
511 lv_coord_t w = lv_obj_get_width(item);
512 lv_coord_t h = lv_obj_get_height(item);
513 if(LV_COORD_IS_PCT(tr_x)) tr_x = (w * LV_COORD_GET_PCT(tr_x)) / 100;
514 if(LV_COORD_IS_PCT(tr_y)) tr_y = (h * LV_COORD_GET_PCT(tr_y)) / 100;
515
516 lv_coord_t diff_x = abs_x - item->coords.x1 + tr_x;
517 lv_coord_t diff_y = abs_y - item->coords.y1 + tr_y;
518 diff_x += f->row ? main_pos : cross_pos;
519 diff_y += f->row ? cross_pos : main_pos;
520
521 if(diff_x || diff_y) {
522 lv_obj_invalidate(item);
523 item->coords.x1 += diff_x;
524 item->coords.x2 += diff_x;
525 item->coords.y1 += diff_y;
526 item->coords.y2 += diff_y;
527 lv_obj_invalidate(item);
528 lv_obj_move_children_by(item, diff_x, diff_y, true);
529 }
530
531 if(!(f->row && rtl)) main_pos += area_get_main_size(&item->coords) + item_gap + place_gap;
532 else main_pos -= item_gap + place_gap;
533
534 item = get_next_item(cont, f->rev, &item_first_id);
535 }
536 }
537
538 /**
539 * Tell a start coordinate and gap for a placement type.
540 */
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)541 static void place_content(lv_flex_align_t place, lv_coord_t max_size, lv_coord_t content_size, lv_coord_t item_cnt,
542 lv_coord_t * start_pos, lv_coord_t * gap)
543 {
544 if(item_cnt <= 1) {
545 switch(place) {
546 case LV_FLEX_ALIGN_SPACE_BETWEEN:
547 case LV_FLEX_ALIGN_SPACE_AROUND:
548 case LV_FLEX_ALIGN_SPACE_EVENLY:
549 place = LV_FLEX_ALIGN_CENTER;
550 break;
551 default:
552 break;
553 }
554 }
555
556 switch(place) {
557 case LV_FLEX_ALIGN_CENTER:
558 *gap = 0;
559 *start_pos += (max_size - content_size) / 2;
560 break;
561 case LV_FLEX_ALIGN_END:
562 *gap = 0;
563 *start_pos += max_size - content_size;
564 break;
565 case LV_FLEX_ALIGN_SPACE_BETWEEN:
566 *gap = (lv_coord_t)(max_size - content_size) / (lv_coord_t)(item_cnt - 1);
567 break;
568 case LV_FLEX_ALIGN_SPACE_AROUND:
569 *gap += (lv_coord_t)(max_size - content_size) / (lv_coord_t)(item_cnt);
570 *start_pos += *gap / 2;
571 break;
572 case LV_FLEX_ALIGN_SPACE_EVENLY:
573 *gap = (lv_coord_t)(max_size - content_size) / (lv_coord_t)(item_cnt + 1);
574 *start_pos += *gap;
575 break;
576 default:
577 *gap = 0;
578 }
579 }
580
get_next_item(lv_obj_t * cont,bool rev,int32_t * item_id)581 static lv_obj_t * get_next_item(lv_obj_t * cont, bool rev, int32_t * item_id)
582 {
583 if(rev) {
584 (*item_id)--;
585 if(*item_id >= 0) return cont->spec_attr->children[*item_id];
586 else return NULL;
587 }
588 else {
589 (*item_id)++;
590 if((*item_id) < (int32_t)cont->spec_attr->child_cnt) return cont->spec_attr->children[*item_id];
591 else return NULL;
592 }
593 }
594
595 #endif /*LV_USE_FLEX*/
596