Skip to content

Commit

Permalink
fix(align) avoid circular references with LV_SIZE_CONTENT
Browse files Browse the repository at this point in the history
If a child has pct width and the parent has LV_SIZE_CONTENT width, it results in a circular reference.
With fix zero content width is assumed for children in such case.

Besides if a child is center or right aligned the calculation of LV_SIZE_CONTENT might give in conter intuitive result.
To solve this center and right aligned children are not considered in LV_SIZE_CONTENT calculations.

The same applies for height.
  • Loading branch information
kisvegabor committed Oct 8, 2021
1 parent acf915b commit 038b781
Showing 1 changed file with 152 additions and 48 deletions.
200 changes: 152 additions & 48 deletions src/core/lv_obj_pos.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
/**********************
* STATIC PROTOTYPES
**********************/
static void calc_auto_size(lv_obj_t * obj, lv_coord_t * w_out, lv_coord_t * h_out);
static lv_coord_t calc_content_width(lv_obj_t * obj);
static lv_coord_t calc_content_height(lv_obj_t * obj);
static void layout_update_core(lv_obj_t * obj);

/**********************
Expand Down Expand Up @@ -91,61 +92,77 @@ bool lv_obj_refr_size(lv_obj_t * obj)
lv_obj_t * parent = lv_obj_get_parent(obj);
if(parent == NULL) return false;

lv_coord_t w;
lv_coord_t sl_ori = lv_obj_get_scroll_left(obj);
bool w_content = false;
bool w_is_content = false;
bool w_is_pct = false;

lv_coord_t w;
if(obj->w_layout) {
w = lv_obj_get_width(obj);
}
else {
w = lv_obj_get_style_width(obj, LV_PART_MAIN);
w_content = w == LV_SIZE_CONTENT ? true : false;
w_is_content = w == LV_SIZE_CONTENT ? true : false;
w_is_pct = LV_COORD_IS_PCT(w) ? true : false;
lv_coord_t parent_w = lv_obj_get_content_width(parent);

/*Be sure the object is not scrolled when it has auto size*/
if(w_content) {
lv_obj_scroll_to_x(obj, 0, LV_ANIM_OFF);
calc_auto_size(obj, &w, NULL);
if(w_is_content) {
w = calc_content_width(obj);
}
else if(w_is_pct) {
/*If parent has content size and the child has pct size
*a circular dependency will occur. To solve it keep child size at zero */
if(parent->w_layout == 0 && lv_obj_get_style_width(parent, 0) == LV_SIZE_CONTENT) {
lv_coord_t border_w = lv_obj_get_style_border_width(obj, 0);
w = lv_obj_get_style_pad_left(obj, 0) + border_w;
w += lv_obj_get_style_pad_right(obj, 0) + border_w;
}
else {
w = (LV_COORD_GET_PCT(w) * parent_w) / 100;
}
}

/*Calculate the sizes in percentage*/
bool pct_w = LV_COORD_IS_PCT(w) ? true : false;

lv_coord_t parent_w = lv_obj_get_content_width(parent);
if(pct_w) w = (LV_COORD_GET_PCT(w) * parent_w) / 100;

lv_coord_t minw = lv_obj_get_style_min_width(obj, LV_PART_MAIN);
lv_coord_t maxw = lv_obj_get_style_max_width(obj, LV_PART_MAIN);
w = lv_clamp_width(w, minw, maxw, parent_w);
}

lv_coord_t h;
lv_coord_t st_ori = lv_obj_get_scroll_top(obj);
bool h_content = false;
lv_coord_t h;
bool h_is_content = false;
bool h_is_pct = false;
if(obj->h_layout) {
h = lv_obj_get_height(obj);
}
else {
h = lv_obj_get_style_height(obj, LV_PART_MAIN);
h_content = h == LV_SIZE_CONTENT ? true : false;
h_is_content = h == LV_SIZE_CONTENT ? true : false;
h_is_pct = LV_COORD_IS_PCT(h) ? true : false;
lv_coord_t parent_h = lv_obj_get_content_height(parent);

/*Be sure the object is not scrolled when it has auto size*/
if(h_content) {
lv_obj_scroll_to_y(obj, 0, LV_ANIM_OFF);
calc_auto_size(obj, NULL, &h);
if(h_is_content) {
h = calc_content_height(obj);
}
else if(h_is_pct) {
/*If parent has content size and the child has pct size
*a circular dependency will occur. To solve it keep child size at zero */
if(parent->h_layout == 0 && lv_obj_get_style_height(parent, 0) == LV_SIZE_CONTENT) {
lv_coord_t border_w = lv_obj_get_style_border_width(obj, 0);
h = lv_obj_get_style_pad_top(obj, 0) + border_w;
h += lv_obj_get_style_pad_bottom(obj, 0) + border_w;
}
else {
h = (LV_COORD_GET_PCT(h) * parent_h) / 100;
}
}

/*Calculate the sizes in percentage*/
bool pct_h = LV_COORD_IS_PCT(h) ? true : false;
lv_coord_t parent_h = lv_obj_get_content_height(parent);
if(pct_h) h = (LV_COORD_GET_PCT(h) * parent_h) / 100;

lv_coord_t minh = lv_obj_get_style_min_height(obj, LV_PART_MAIN);
lv_coord_t maxh = lv_obj_get_style_max_height(obj, LV_PART_MAIN);
h = lv_clamp_height(h, minh, maxh, parent_h);
}

/*calc_auto_size set the scroll x/y to 0 so revert the original value*/
if(w_content || h_content) {
if(w_is_content || h_is_content) {
lv_obj_scroll_to(obj, sl_ori, st_ori, LV_ANIM_OFF);
}

Expand Down Expand Up @@ -195,10 +212,6 @@ bool lv_obj_refr_size(lv_obj_t * obj)
bool on2 = _lv_area_is_in(&obj->coords, &parent_fit_area, 0);
if(on1 || (!on1 && on2)) lv_obj_scrollbar_invalidate(parent);





return true;
}

Expand Down Expand Up @@ -917,27 +930,118 @@ lv_coord_t lv_clamp_height(lv_coord_t height, lv_coord_t min_height, lv_coord_t
* STATIC FUNCTIONS
**********************/

/**
* Calculate the "auto size". It's `auto_size = max(children_size, self_size)`
* @param obj pointer to an object
* @param w_out store the width here. NULL to not calculate width
* @param h_out store the height here. NULL to not calculate height
*/
static void calc_auto_size(lv_obj_t * obj, lv_coord_t * w_out, lv_coord_t * h_out)
static lv_coord_t calc_content_width(lv_obj_t * obj)
{
if(!w_out && !h_out) return;
/*Get the bounding box of the children*/
if(w_out) {
lv_coord_t scroll_right = lv_obj_get_scroll_right(obj);
lv_coord_t scroll_left = lv_obj_get_scroll_left(obj);
*w_out = lv_obj_get_width(obj) + scroll_right + scroll_left;
lv_obj_scroll_to_x(obj, 0, LV_ANIM_OFF);

lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
lv_coord_t pad_right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN) + border_width;
lv_coord_t pad_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN) + border_width;

lv_coord_t self_w;
self_w = lv_obj_get_self_width(obj) + pad_left + pad_right;

lv_coord_t child_res = LV_COORD_MIN;
uint32_t i;
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
/*With RTL find the left most coordinate*/
if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) == LV_BASE_DIR_RTL) {
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = obj->spec_attr->children[i];
if(lv_obj_has_flag_any(child, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;

if(!lv_obj_is_layout_positioned(child)) {
lv_align_t align = lv_obj_get_style_align(child, 0);
switch(align) {
case LV_ALIGN_DEFAULT:
case LV_ALIGN_TOP_RIGHT:
case LV_ALIGN_BOTTOM_RIGHT:
case LV_ALIGN_RIGHT_MID:
/*Normal right aligns. Other are ignored due to possible circular dependencies*/
child_res = LV_MAX(child_res, obj->coords.x2 - child->coords.x1 + 1);
break;
}
} else {
child_res = LV_MAX(child_res, obj->coords.x2 - child->coords.x1 + 1);
}
}
if(child_res != LV_COORD_MIN) {
child_res += pad_left;
}
}
/*Else find the right most coordinate*/
else {
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = obj->spec_attr->children[i];
if(lv_obj_has_flag_any(child, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;

if(!lv_obj_is_layout_positioned(child)) {
lv_align_t align = lv_obj_get_style_align(child, 0);
switch(align) {
case LV_ALIGN_DEFAULT:
case LV_ALIGN_TOP_LEFT:
case LV_ALIGN_BOTTOM_LEFT:
case LV_ALIGN_LEFT_MID:
/*Normal left aligns. Other are ignored due to possible circular dependencies*/
child_res = LV_MAX(child_res, child->coords.x2 - obj->coords.x1 + 1);
break;
}
} else {
child_res = LV_MAX(child_res, child->coords.x2 - obj->coords.x1 + 1);
}
}

if(child_res != LV_COORD_MIN) {
child_res += pad_right;
}
}

if(child_res == LV_COORD_MIN) return self_w;
else return LV_MAX(child_res, self_w);
}

static lv_coord_t calc_content_height(lv_obj_t * obj)
{
lv_obj_scroll_to_y(obj, 0, LV_ANIM_OFF);

lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
lv_coord_t pad_top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN) + border_width;
lv_coord_t pad_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN) + border_width;

lv_coord_t self_h;
self_h = lv_obj_get_self_height(obj) + pad_top + pad_bottom;

lv_coord_t child_res = LV_COORD_MIN;
uint32_t i;
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = obj->spec_attr->children[i];
if(lv_obj_has_flag_any(child, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;


if(!lv_obj_is_layout_positioned(child)) {
lv_align_t align = lv_obj_get_style_align(child, 0);
switch(align) {
case LV_ALIGN_DEFAULT:
case LV_ALIGN_TOP_RIGHT:
case LV_ALIGN_TOP_MID:
case LV_ALIGN_TOP_LEFT:
/*Normal top aligns. Other are ignored due to possible circular dependencies*/
child_res = LV_MAX(child_res, child->coords.y2 - obj->coords.y1 + 1);
break;
}
} else {
child_res = LV_MAX(child_res, child->coords.y2 - obj->coords.y1 + 1);
}
}

if(h_out) {
lv_coord_t scroll_bottom = lv_obj_get_scroll_bottom(obj);
lv_coord_t scroll_top = lv_obj_get_scroll_top(obj);
*h_out = lv_obj_get_height(obj) + scroll_bottom + scroll_top;
if(child_res != LV_COORD_MIN) {
child_res += pad_bottom;
return LV_MAX(child_res, self_h);
} else {
return self_h;
}

}

static void layout_update_core(lv_obj_t * obj)
Expand Down

0 comments on commit 038b781

Please sign in to comment.