mirror of
https://github.com/lvgl/lvgl.git
synced 2026-05-24 08:16:29 +08:00
fix(align) avoid circular references with LV_SIZE_CONTENT
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.
This commit is contained in:
+154
-50
@@ -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);
|
||||
|
||||
/**********************
|
||||
@@ -91,53 +92,69 @@ 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;
|
||||
|
||||
/*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);
|
||||
}
|
||||
|
||||
/*Calculate the sizes in percentage*/
|
||||
bool pct_w = LV_COORD_IS_PCT(w) ? 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);
|
||||
if(pct_w) w = (LV_COORD_GET_PCT(w) * parent_w) / 100;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/*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);
|
||||
}
|
||||
|
||||
/*Calculate the sizes in percentage*/
|
||||
bool pct_h = LV_COORD_IS_PCT(h) ? 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);
|
||||
if(pct_h) h = (LV_COORD_GET_PCT(h) * parent_h) / 100;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -145,7 +162,7 @@ bool lv_obj_refr_size(lv_obj_t * obj)
|
||||
}
|
||||
|
||||
/*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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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(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) 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(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)
|
||||
|
||||
Reference in New Issue
Block a user