acid-drop

- Hacking the planet from a LilyGo T-Deck using custom firmware
git clone git://git.acid.vegas/acid-drop.git
Log | Files | Refs | Archive | README | LICENSE

lv_flex.c (21866B)

      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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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  */
    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  */
    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) item->w_layout = 1;
    474             else item->h_layout = 1;
    475 
    476             if(s != area_get_main_size(&item->coords)) {
    477                 lv_obj_invalidate(item);
    478 
    479                 lv_area_t old_coords;
    480                 lv_area_copy(&old_coords, &item->coords);
    481                 area_set_main_size(&item->coords, s);
    482                 lv_event_send(item, LV_EVENT_SIZE_CHANGED, &old_coords);
    483                 lv_event_send(lv_obj_get_parent(item), LV_EVENT_CHILD_CHANGED, item);
    484                 lv_obj_invalidate(item);
    485             }
    486         }
    487         else {
    488             item->w_layout = 0;
    489             item->h_layout = 0;
    490         }
    491 
    492         lv_coord_t cross_pos = 0;
    493         switch(f->cross_place) {
    494             case LV_FLEX_ALIGN_CENTER:
    495                 /*Round up the cross size to avoid rounding error when dividing by 2
    496                  *The issue comes up e,g, with column direction with center cross direction if an element's width changes*/
    497                 cross_pos = (((t->track_cross_size + 1) & (~1)) - area_get_cross_size(&item->coords)) / 2;
    498                 break;
    499             case LV_FLEX_ALIGN_END:
    500                 cross_pos = t->track_cross_size - area_get_cross_size(&item->coords);
    501                 break;
    502             default:
    503                 break;
    504         }
    505 
    506         if(f->row && rtl) main_pos -= area_get_main_size(&item->coords);
    507 
    508 
    509         /*Handle percentage value of translate*/
    510         lv_coord_t tr_x = lv_obj_get_style_translate_x(item, LV_PART_MAIN);
    511         lv_coord_t tr_y = lv_obj_get_style_translate_y(item, LV_PART_MAIN);
    512         lv_coord_t w = lv_obj_get_width(item);
    513         lv_coord_t h = lv_obj_get_height(item);
    514         if(LV_COORD_IS_PCT(tr_x)) tr_x = (w * LV_COORD_GET_PCT(tr_x)) / 100;
    515         if(LV_COORD_IS_PCT(tr_y)) tr_y = (h * LV_COORD_GET_PCT(tr_y)) / 100;
    516 
    517         lv_coord_t diff_x = abs_x - item->coords.x1 + tr_x;
    518         lv_coord_t diff_y = abs_y - item->coords.y1 + tr_y;
    519         diff_x += f->row ? main_pos : cross_pos;
    520         diff_y += f->row ? cross_pos : main_pos;
    521 
    522         if(diff_x || diff_y) {
    523             lv_obj_invalidate(item);
    524             item->coords.x1 += diff_x;
    525             item->coords.x2 += diff_x;
    526             item->coords.y1 += diff_y;
    527             item->coords.y2 += diff_y;
    528             lv_obj_invalidate(item);
    529             lv_obj_move_children_by(item, diff_x, diff_y, false);
    530         }
    531 
    532         if(!(f->row && rtl)) main_pos += area_get_main_size(&item->coords) + item_gap + place_gap;
    533         else main_pos -= item_gap + place_gap;
    534 
    535         item = get_next_item(cont, f->rev, &item_first_id);
    536     }
    537 }
    538 
    539 /**
    540  * Tell a start coordinate and gap for a placement type.
    541  */
    542 static void place_content(lv_flex_align_t place, lv_coord_t max_size, lv_coord_t content_size, lv_coord_t item_cnt,
    543                           lv_coord_t * start_pos, lv_coord_t * gap)
    544 {
    545     if(item_cnt <= 1) {
    546         switch(place) {
    547             case LV_FLEX_ALIGN_SPACE_BETWEEN:
    548             case LV_FLEX_ALIGN_SPACE_AROUND:
    549             case LV_FLEX_ALIGN_SPACE_EVENLY:
    550                 place = LV_FLEX_ALIGN_CENTER;
    551                 break;
    552             default:
    553                 break;
    554         }
    555     }
    556 
    557     switch(place) {
    558         case LV_FLEX_ALIGN_CENTER:
    559             *gap = 0;
    560             *start_pos += (max_size - content_size) / 2;
    561             break;
    562         case LV_FLEX_ALIGN_END:
    563             *gap = 0;
    564             *start_pos += max_size - content_size;
    565             break;
    566         case LV_FLEX_ALIGN_SPACE_BETWEEN:
    567             *gap = (lv_coord_t)(max_size - content_size) / (lv_coord_t)(item_cnt - 1);
    568             break;
    569         case LV_FLEX_ALIGN_SPACE_AROUND:
    570             *gap += (lv_coord_t)(max_size - content_size) / (lv_coord_t)(item_cnt);
    571             *start_pos += *gap / 2;
    572             break;
    573         case LV_FLEX_ALIGN_SPACE_EVENLY:
    574             *gap = (lv_coord_t)(max_size - content_size) / (lv_coord_t)(item_cnt + 1);
    575             *start_pos += *gap;
    576             break;
    577         default:
    578             *gap = 0;
    579     }
    580 }
    581 
    582 static lv_obj_t * get_next_item(lv_obj_t * cont, bool rev, int32_t * item_id)
    583 {
    584     if(rev) {
    585         (*item_id)--;
    586         if(*item_id >= 0) return cont->spec_attr->children[*item_id];
    587         else return NULL;
    588     }
    589     else {
    590         (*item_id)++;
    591         if((*item_id) < (int32_t)cont->spec_attr->child_cnt) return cont->spec_attr->children[*item_id];
    592         else return NULL;
    593     }
    594 }
    595 
    596 #endif /*LV_USE_FLEX*/