Files
lvgl/src/lv_core/lv_flex.c

411 lines
13 KiB
C

/**
* @file lv_flex.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_flex.h"
#include "lv_obj.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_obj_t * find_track_end(lv_obj_t * cont, lv_obj_t * item_start, lv_coord_t max_size, lv_coord_t * grow_unit, lv_coord_t * track_cross_size);
static void children_repos(lv_obj_t * cont, lv_obj_t * item_first, lv_obj_t * item_last, lv_coord_t abs_x, lv_coord_t abs_y, lv_coord_t track_size, lv_coord_t grow_unit);
static void place_content(lv_coord_t place, lv_coord_t max_size, lv_coord_t track_size, lv_coord_t track_cnt, lv_coord_t * start_pos, lv_coord_t * gap);
static lv_flex_dir_t get_dir(const lv_obj_t * obj);
static bool get_rev(const lv_obj_t * obj);
static bool get_wrap(const lv_obj_t * obj);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/*=====================
* Setter functions
*====================*/
void lv_obj_set_flex_dir(lv_obj_t * obj, lv_flex_dir_t flex_dir)
{
lv_obj_allocate_rare_attr(obj);
if(obj->spec_attr->flex_cont.dir == flex_dir) return;
obj->spec_attr->flex_cont.dir = flex_dir & 0x3;
obj->spec_attr->flex_cont.wrap = flex_dir & _LV_FLEX_WRAP ? 1 : 0;
obj->spec_attr->flex_cont.rev = flex_dir & _LV_FLEX_REVERSE ? 1 : 0;
_lv_flex_refresh(obj);
}
void lv_obj_set_flex_track_place(lv_obj_t * obj, lv_flex_place_t place)
{
lv_obj_allocate_rare_attr(obj);
if(obj->spec_attr->flex_cont.place == place) return;
obj->spec_attr->flex_cont.place = place;
_lv_flex_refresh(obj);
}
void lv_obj_set_flex_gap(lv_obj_t * obj, lv_coord_t gap)
{
if(obj->spec_attr == NULL) lv_obj_allocate_rare_attr(obj);
if(obj->spec_attr->flex_cont.gap == gap) return;
obj->spec_attr->flex_cont.gap = gap;
_lv_flex_refresh(obj);
}
void lv_obj_set_flex_item(lv_obj_t * obj, bool en)
{
if(en) {
lv_coord_t f = _LV_COORD_FELX(LV_FLEX_PLACE_START);
lv_obj_set_pos(obj, f, f);
} else {
lv_obj_set_pos(obj, lv_obj_get_x(obj), lv_obj_get_y(obj));
}
}
void lv_obj_set_flex_item_place(lv_obj_t * obj, lv_flex_place_t place)
{
if(place == LV_FLEX_PLACE_NONE) {
lv_obj_set_pos(obj, lv_obj_get_x(obj), lv_obj_get_x(obj));
} else {
lv_coord_t f = _LV_COORD_FELX(place);
lv_obj_set_pos(obj, f, f);
}
}
/*=====================
* Getter functions
*====================*/
lv_flex_dir_t lv_obj_get_flex_dir(const lv_obj_t * obj)
{
if(obj->spec_attr) return obj->spec_attr->flex_cont.dir;
else return LV_FLEX_DIR_NONE;
}
lv_flex_place_t lv_obj_get_flex_track_place(const lv_obj_t * obj)
{
if(obj->spec_attr) return obj->spec_attr->flex_cont.place;
else return LV_FLEX_PLACE_START;
}
lv_coord_t lv_obj_get_flex_gap(const lv_obj_t * obj)
{
if(obj->spec_attr) return obj->spec_attr->flex_cont.gap;
else return 0;
}
lv_flex_place_t lv_obj_get_flex_item_place(lv_obj_t * obj)
{
lv_coord_t x = lv_obj_get_x(obj);
if(LV_COORD_IS_FLEX(x)) return LV_COORD_GET_FLEX(x);
else return LV_FLEX_PLACE_NONE;
}
void _lv_flex_refresh(lv_obj_t * cont)
{
lv_flex_dir_t dir = get_dir(cont);
if(dir == LV_FLEX_DIR_NONE) return;
bool rtl = lv_obj_get_base_dir(cont) == LV_BIDI_DIR_RTL ? true : false;
bool row = dir == LV_FLEX_DIR_ROW ? true : false;
/*Count the grow units and free space*/
lv_coord_t max_main_size = (row ? lv_obj_get_width_fit(cont) : lv_obj_get_height_fit(cont));
lv_coord_t abs_y = cont->coords.y1 + lv_obj_get_style_pad_top(cont, LV_OBJ_PART_MAIN) - lv_obj_get_scroll_y(cont);
lv_coord_t abs_x = cont->coords.x1 + lv_obj_get_style_pad_left(cont, LV_OBJ_PART_MAIN) - lv_obj_get_scroll_x(cont);
lv_coord_t place = lv_obj_get_flex_track_place(cont);
lv_ll_t * ll = _lv_obj_get_child_ll(cont);
lv_coord_t * cross_pos = (row ? &abs_y : &abs_x);
if((row && cont->h_set == LV_SIZE_AUTO) ||
(!row && cont->w_set == LV_SIZE_AUTO))
{
place = LV_FLEX_PLACE_START;
}
if(rtl && !row) {
if(place == LV_FLEX_PLACE_START) place = LV_FLEX_PLACE_END;
else if(place == LV_FLEX_PLACE_END) place = LV_FLEX_PLACE_START;
}
lv_coord_t all_track_size = 0;
lv_coord_t gap = 0;
uint32_t row_cnt = 0;
lv_coord_t grow_unit;
lv_coord_t track_size;
lv_obj_t * track_first_item;
lv_obj_t * next_track_first_item;
bool rev = get_rev(cont);
if(place != LV_FLEX_PLACE_START) {
track_first_item = rev ? _lv_ll_get_head(ll) : _lv_ll_get_tail(ll);
while(track_first_item) {
/*Search the first item of the next row */
next_track_first_item = find_track_end(cont, track_first_item, max_main_size, &grow_unit, &track_size);
all_track_size += track_size;
row_cnt++;
track_first_item = next_track_first_item;
}
lv_coord_t max_cross_size = (row ? lv_obj_get_height_fit(cont) : lv_obj_get_width_fit(cont));
place_content(place, max_cross_size, all_track_size,row_cnt, cross_pos, &gap);
}
track_first_item = rev ? _lv_ll_get_head(ll) : _lv_ll_get_tail(ll);
if(rtl && !row) {
*cross_pos += all_track_size;
}
while(track_first_item) {
/*Search the first item of the next row */
next_track_first_item = find_track_end(cont, track_first_item, max_main_size, &grow_unit, &track_size);
if(rtl && !row) {
*cross_pos -= track_size;
}
children_repos(cont, track_first_item, next_track_first_item, abs_x, abs_y, track_size, grow_unit);
track_first_item = next_track_first_item;
if(rtl && !row) {
*cross_pos -= gap;
} else {
*cross_pos += track_size + gap;
}
}
LV_ASSERT_MEM_INTEGRITY();
}
/**********************
* STATIC FUNCTIONS
**********************/
static lv_obj_t * find_track_end(lv_obj_t * cont, lv_obj_t * item_start, lv_coord_t max_size, lv_coord_t * grow_unit, lv_coord_t * track_cross_size)
{
bool wrap = get_wrap(cont);
bool rev = get_rev(cont);
lv_coord_t gap = lv_obj_get_flex_gap(cont);
bool row = get_dir(cont) == LV_FLEX_DIR_ROW ? true : false;
lv_coord_t(*get_main_size)(const lv_obj_t *) = (row ? lv_obj_get_width_margin : lv_obj_get_height_margin);
lv_coord_t(*get_cross_size)(const lv_obj_t *) = (!row ? lv_obj_get_width_margin : lv_obj_get_height_margin);
void * (*ll_iter)(const lv_ll_t * , const void *) = rev ? _lv_ll_get_next : _lv_ll_get_prev;
lv_ll_t * ll = _lv_obj_get_child_ll(cont);
lv_coord_t grow_sum = 0;
lv_coord_t used_size = 0;
uint32_t grow_item_cnt = 0;
*track_cross_size = 0;
*grow_unit = 0;
lv_obj_t * item = item_start;
while(item) {
/*Ignore non-flex items*/
lv_coord_t main_set = (row ? item->x_set : item->y_set);
if(LV_COORD_IS_FLEX(main_set) == false) {
item = ll_iter(ll, item);
continue;
}
lv_coord_t main_size = (row ? item->w_set : item->h_set);
if(_LV_FLEX_GET_GROW(main_size)) {
grow_sum += _LV_FLEX_GET_GROW(main_size);
grow_item_cnt++;
} else {
lv_coord_t item_size = get_main_size(item) + gap;
if(wrap && used_size + item_size > max_size) break;
used_size += item_size;
}
*track_cross_size = LV_MATH_MAX(get_cross_size(item), *track_cross_size);
item = ll_iter(ll, item);
}
if(used_size > 0) used_size -= gap; /*There is no gap after the last item*/
if(grow_item_cnt && grow_sum) {
lv_coord_t s = max_size - used_size;
s -= grow_item_cnt * gap;
*grow_unit = s / grow_sum;
} else {
*grow_unit = 0;
}
/*Have at least one item in a row*/
if(item && item == item_start) {
item = ll_iter(ll, item);
if(item) *track_cross_size = get_cross_size(item);
}
return item;
}
static void children_repos(lv_obj_t * cont, lv_obj_t * item_first, lv_obj_t * item_last, lv_coord_t abs_x, lv_coord_t abs_y, lv_coord_t track_size, lv_coord_t grow_unit)
{
bool rev = get_rev(cont);
lv_coord_t gap = lv_obj_get_flex_gap(cont);
bool row = get_dir(cont) == LV_FLEX_DIR_ROW ? true : false;
lv_coord_t(*obj_get_main_size)(const lv_obj_t *) = (row ? lv_obj_get_width_margin : lv_obj_get_height_margin);
lv_coord_t(*obj_get_cross_size)(const lv_obj_t *) = (!row ? lv_obj_get_width_margin : lv_obj_get_height_margin);
void (*area_set_main_size)(lv_area_t *, lv_coord_t) = (row ? lv_area_set_width : lv_area_set_height);
void (*area_set_cross_size)(lv_area_t *, lv_coord_t) = (!row ? lv_area_set_width : lv_area_set_height);
lv_coord_t (*area_get_main_size)(const lv_area_t *) = (!row ? lv_area_get_width : lv_area_get_height);
lv_style_int_t (*get_margin_start)(const lv_obj_t *, uint8_t part) = (row ? lv_obj_get_style_margin_left : lv_obj_get_style_margin_top);
lv_style_int_t (*get_margin_end)(const lv_obj_t *, uint8_t part) = (row ? lv_obj_get_style_margin_right : lv_obj_get_style_margin_bottom);
void * (*ll_iter)(const lv_ll_t * , const void *) = rev ? _lv_ll_get_next : _lv_ll_get_prev;
bool rtl = lv_obj_get_base_dir(cont) == LV_BIDI_DIR_RTL ? true : false;
if(row && rtl) abs_x += lv_obj_get_width_fit(cont);
lv_ll_t * ll = _lv_obj_get_child_ll(cont);
lv_coord_t main_pos = 0;
/*Reposition the children*/
lv_obj_t * item = item_first; /*Just to use a shorter name*/
while(item != item_last) {
/*Ignore non-flex items*/
lv_coord_t main_set = (row ? item->x_set : item->y_set);
if(LV_COORD_IS_FLEX(main_set) == false) {
item = ll_iter(ll, item);
continue;
}
lv_coord_t main_size = (row ? item->w_set : item->h_set);
if(_LV_FLEX_GET_GROW(main_size) || LV_COORD_GET_FLEX(main_set) == LV_FLEX_PLACE_STRETCH) {
lv_area_t old_coords;
lv_area_copy(&old_coords, &item->coords);
if(_LV_FLEX_GET_GROW(main_size)) {
lv_coord_t s = _LV_FLEX_GET_GROW(main_size) * grow_unit;
s -= get_margin_start(item, LV_OBJ_PART_MAIN) + get_margin_end(item, LV_OBJ_PART_MAIN);
area_set_main_size(&item->coords, s);
}
if(LV_COORD_GET_FLEX(main_set) == LV_FLEX_PLACE_STRETCH) {
area_set_cross_size(&item->coords, track_size);
}
if(lv_area_get_height(&old_coords) != area_get_main_size(&item->coords)) {
lv_obj_invalidate(item);
item->signal_cb(item, LV_SIGNAL_COORD_CHG, &old_coords);
lv_obj_invalidate(item);
}
}
lv_coord_t cross_pos = 0;
lv_coord_t cross_set = (row ? item->y_set : item->x_set);
switch(LV_COORD_GET_FLEX(cross_set)) {
case LV_FLEX_PLACE_CENTER:
cross_pos = (track_size - obj_get_cross_size(item)) / 2;
break;
case LV_FLEX_PLACE_END:
cross_pos = track_size - obj_get_cross_size(item);
break;
}
if(row && rtl) {
main_pos -= obj_get_main_size(item) + gap;
}
lv_coord_t diff_x = abs_x - item->coords.x1 + lv_obj_get_style_margin_left(item, LV_OBJ_PART_MAIN);
lv_coord_t diff_y = abs_y - item->coords.y1 + lv_obj_get_style_margin_top(item, LV_OBJ_PART_MAIN);
diff_x += row ? main_pos : cross_pos;
diff_y += row ? cross_pos : main_pos;
if(diff_x || diff_y) {
item->coords.x1 += diff_x;
item->coords.x2 += diff_x;
item->coords.y1 += diff_y;
item->coords.y2 += diff_y;
_lv_obj_move_children_by(item, diff_x, diff_y);
}
if(!(row && rtl)) {
main_pos += obj_get_main_size(item) + gap;
}
item = ll_iter(ll, item);
}
}
static void place_content(lv_coord_t place, lv_coord_t max_size, lv_coord_t track_size, lv_coord_t track_cnt, lv_coord_t * start_pos, lv_coord_t * gap)
{
if(track_cnt <= 1) {
switch(place) {
case LV_FLEX_PLACE_SPACE_BETWEEN:
case LV_FLEX_PLACE_SPACE_AROUND:
case LV_FLEX_PLACE_SPACE_EVENLY:
place = LV_FLEX_PLACE_CENTER;
break;
}
}
switch(place) {
case LV_FLEX_PLACE_CENTER:
*start_pos += (max_size - track_size) / 2;
break;
case LV_FLEX_PLACE_END:
*start_pos += max_size - track_size;
break;
case LV_FLEX_PLACE_SPACE_BETWEEN:
*gap = (lv_coord_t)(max_size - track_size) / (lv_coord_t)(track_cnt - 1);
break;
case LV_FLEX_PLACE_SPACE_AROUND:
*gap += (lv_coord_t)(max_size - track_size) / (lv_coord_t)(track_cnt);
*start_pos += *gap / 2;
break;
case LV_FLEX_PLACE_SPACE_EVENLY:
*gap = (lv_coord_t)(max_size - track_size) / (lv_coord_t)(track_cnt + 1);
*start_pos += *gap;
break;
default:
*gap = 0;
}
}
static lv_flex_dir_t get_dir(const lv_obj_t * obj)
{
if(obj->spec_attr) return obj->spec_attr->flex_cont.dir;
else return false;
}
static bool get_rev(const lv_obj_t * obj)
{
if(obj->spec_attr) return obj->spec_attr->flex_cont.rev;
else return false;
}
static bool get_wrap(const lv_obj_t * obj)
{
if(obj->spec_attr) return obj->spec_attr->flex_cont.wrap;
else return false;
}