Files
lvgl/src/core/lv_obj_pos.c
2021-03-16 20:36:27 +01:00

772 lines
22 KiB
C

/**
* @file lv_obj_pos.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_obj.h"
#include "lv_disp.h"
#include "lv_refr.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_obj_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static bool refr_size(lv_obj_t * obj, lv_coord_t w, lv_coord_t h);
static void calc_auto_size(lv_obj_t * obj, lv_coord_t * w_out, lv_coord_t * h_out);
static void layout_update_core(lv_obj_t * obj);
void lv_obj_move_to(lv_obj_t * obj, lv_coord_t x, lv_coord_t y, bool notify);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_obj_set_pos(lv_obj_t * obj, lv_coord_t x, lv_coord_t y)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(lv_obj_is_layout_positioned(obj)) {
return;
}
obj->x_set = x;
obj->y_set = y;
lv_obj_move_to(obj, x, y, true);
}
void lv_obj_set_x(lv_obj_t * obj, lv_coord_t x)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_set_pos(obj, x, obj->y_set);
}
void lv_obj_set_y(lv_obj_t * obj, lv_coord_t y)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_set_pos(obj, obj->x_set, y);
}
void lv_obj_set_size(lv_obj_t * obj, lv_coord_t w, lv_coord_t h)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
/*If the width or height is set by a layout do not modify them*/
if(obj->w_set == LV_SIZE_LAYOUT && obj->h_set == LV_SIZE_LAYOUT) return;
if(obj->w_set == LV_SIZE_LAYOUT) w = lv_obj_get_width(obj);
if(obj->h_set == LV_SIZE_LAYOUT) h = lv_obj_get_height(obj);
obj->w_set = w;
obj->h_set = h;
/* If the width or height is set to special layout related value save them in w_set and h_set
* but use the current size on the object width*/
if(LV_COORD_IS_LAYOUT(w)) w = lv_obj_get_width(obj);
if(LV_COORD_IS_LAYOUT(h)) h = lv_obj_get_height(obj);
/*Calculate the required auto sizes*/
bool w_content = obj->w_set == LV_SIZE_CONTENT ? true : false;
bool h_content = obj->h_set == 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);
if(h_content) lv_obj_scroll_to_y(obj, 0, LV_ANIM_OFF);
if(w_content && h_content) calc_auto_size(obj, &w, &h);
else if(w_content) calc_auto_size(obj, &w, NULL);
else if(h_content) calc_auto_size(obj, NULL, &h);
/*Calculate the required auto sizes*/
bool pct_w = LV_COORD_IS_PCT(obj->w_set) ? true : false;
bool pct_h = LV_COORD_IS_PCT(obj->h_set) ? true : false;
lv_obj_t * parent = lv_obj_get_parent(obj);
if(parent) {
lv_coord_t parent_w = lv_obj_get_width_fit(parent);
lv_coord_t parent_h = lv_obj_get_height_fit(parent);
if(pct_w) w = (LV_COORD_GET_PCT(obj->w_set) * parent_w) / 100;
if(pct_h) h = (LV_COORD_GET_PCT(obj->h_set) * parent_h) / 100;
}
refr_size(obj, w, h);
}
void lv_obj_set_width(lv_obj_t * obj, lv_coord_t w)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_set_size(obj, w, obj->h_set);
}
void lv_obj_set_height(lv_obj_t * obj, lv_coord_t h)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_set_size(obj, obj->w_set, h);
}
void lv_obj_set_content_width(lv_obj_t * obj, lv_coord_t w)
{
lv_coord_t pleft = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
lv_coord_t pright = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
lv_obj_set_width(obj, w + pleft + pright);
}
void lv_obj_set_content_height(lv_obj_t * obj, lv_coord_t h)
{
lv_coord_t ptop = lv_obj_get_style_pad_top(obj, LV_PART_MAIN);
lv_coord_t pbottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN);
lv_obj_set_height(obj, h + ptop + pbottom);
}
void lv_obj_set_layout(lv_obj_t * obj, const void * layout)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_allocate_spec_attr(obj);
obj->spec_attr->layout_dsc = layout;
lv_obj_mark_layout_as_dirty(obj);
}
bool lv_obj_is_layout_positioned(const lv_obj_t * obj)
{
if(lv_obj_has_flag_any(obj, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_IGNORE_LAYOUT | LV_OBJ_FLAG_FLOATING)) return false;
lv_obj_t * parent = lv_obj_get_parent(obj);
if(parent == NULL) return false;
if(parent->spec_attr && parent->spec_attr->layout_dsc) return true;
else return false;
}
void lv_obj_mark_layout_as_dirty(lv_obj_t * obj)
{
obj->layout_inv = 1;
/*Mark the screen as dirty too to mark that there is an something to do on this screen*/
lv_obj_t * scr = lv_obj_get_screen(obj);
scr->layout_inv = 1;
/*Make the display refreshing*/
lv_disp_t * disp = lv_obj_get_disp(scr);
lv_timer_pause(disp->refr_timer, false);
}
void lv_obj_update_layout(lv_obj_t * obj)
{
lv_obj_t * scr = lv_obj_get_screen(obj);
/*There are no dirty layouts on this screen*/
if(scr->layout_inv == 0) return;
do {
scr->layout_inv = 0;
layout_update_core(obj);
}while(scr->layout_inv); /*Repeat until there where layout invalidations*/
/* Restore the global state because other calls of this function needs this info too.
* Other calls might use different start object, but they need to know if there is dirty layout somewhere.
* However if the screen was updated it's sure that all layouts are ready. */
if(obj != scr) scr->layout_inv = 1;
}
void lv_obj_align(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, lv_coord_t x_ofs, lv_coord_t y_ofs)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(base == NULL) base = lv_obj_get_parent(obj);
LV_ASSERT_OBJ(base, MY_CLASS);
lv_coord_t x = 0;
lv_coord_t y = 0;
lv_obj_t * parent = lv_obj_get_parent(obj);
lv_coord_t pleft = lv_obj_get_style_pad_left(parent, LV_PART_MAIN);
lv_coord_t ptop = lv_obj_get_style_pad_top(parent, LV_PART_MAIN);
switch(align) {
case LV_ALIGN_CENTER:
x = lv_obj_get_width_fit(base) / 2 - lv_obj_get_width(obj) / 2;
y = lv_obj_get_height_fit(base) / 2 - lv_obj_get_height(obj) / 2;
break;
case LV_ALIGN_IN_TOP_LEFT:
x = 0;
y = 0;
break;
case LV_ALIGN_IN_TOP_MID:
x = lv_obj_get_width_fit(base) / 2 - lv_obj_get_width(obj) / 2;
y = 0;
break;
case LV_ALIGN_IN_TOP_RIGHT:
x = lv_obj_get_width_fit(base) - lv_obj_get_width(obj);
y = 0;
break;
case LV_ALIGN_IN_BOTTOM_LEFT:
x = 0;
y = lv_obj_get_height_fit(base) - lv_obj_get_height(obj);
break;
case LV_ALIGN_IN_BOTTOM_MID:
x = lv_obj_get_width_fit(base) / 2 - lv_obj_get_width(obj) / 2;
y = lv_obj_get_height_fit(base) - lv_obj_get_height(obj);
break;
case LV_ALIGN_IN_BOTTOM_RIGHT:
x = lv_obj_get_width_fit(base) - lv_obj_get_width(obj);
y = lv_obj_get_height_fit(base) - lv_obj_get_height(obj);
break;
case LV_ALIGN_IN_LEFT_MID:
x = 0;
y = lv_obj_get_height_fit(base) / 2 - lv_obj_get_height(obj) / 2;
break;
case LV_ALIGN_IN_RIGHT_MID:
x = lv_obj_get_width_fit(base) - lv_obj_get_width(obj);
y = lv_obj_get_height_fit(base) / 2 - lv_obj_get_height(obj) / 2;
break;
case LV_ALIGN_OUT_TOP_LEFT:
x = -pleft;
y = -lv_obj_get_height(obj) - ptop;
break;
case LV_ALIGN_OUT_TOP_MID:
x = lv_obj_get_width(base) / 2 - lv_obj_get_width(obj) / 2 - pleft;
y = -lv_obj_get_height(obj) - ptop;
break;
case LV_ALIGN_OUT_TOP_RIGHT:
x = lv_obj_get_width(base) - lv_obj_get_width(obj) - pleft;
y = -lv_obj_get_height(obj) - ptop;
break;
case LV_ALIGN_OUT_BOTTOM_LEFT:
x = - pleft;
y = lv_obj_get_height(base) - ptop;
break;
case LV_ALIGN_OUT_BOTTOM_MID:
x = lv_obj_get_width(base) / 2 - lv_obj_get_width(obj) / 2 - pleft;
y = lv_obj_get_height(base) - ptop;
break;
case LV_ALIGN_OUT_BOTTOM_RIGHT:
x = lv_obj_get_width(base) - lv_obj_get_width(obj) - pleft;
y = lv_obj_get_height(base) - ptop;
break;
case LV_ALIGN_OUT_LEFT_TOP:
x = -lv_obj_get_width(obj) - pleft;
y = - ptop;
break;
case LV_ALIGN_OUT_LEFT_MID:
x = -lv_obj_get_width(obj) - pleft;
y = lv_obj_get_height(base) / 2 - lv_obj_get_height(obj) / 2 - ptop;
break;
case LV_ALIGN_OUT_LEFT_BOTTOM:
x = -lv_obj_get_width(obj) - pleft;
y = lv_obj_get_height(base) - lv_obj_get_height(obj) - ptop;
break;
case LV_ALIGN_OUT_RIGHT_TOP:
x = lv_obj_get_width(base) - pleft;
y = - ptop;
break;
case LV_ALIGN_OUT_RIGHT_MID:
x = lv_obj_get_width(base) - pleft;
y = lv_obj_get_height(base) / 2 - lv_obj_get_height(obj) / 2 - ptop;
break;
case LV_ALIGN_OUT_RIGHT_BOTTOM:
x = lv_obj_get_width(base) - pleft;
y = lv_obj_get_height(base) - lv_obj_get_height(obj) - ptop;
break;
}
x += x_ofs + base->coords.x1 - parent->coords.x1;
y += y_ofs + base->coords.y1 - parent->coords.y1;
lv_obj_set_pos(obj, x, y);
}
void lv_obj_get_coords(const lv_obj_t * obj, lv_area_t * coords)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_area_copy(coords, &obj->coords);
}
lv_coord_t lv_obj_get_x(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_coord_t rel_x;
lv_obj_t * parent = lv_obj_get_parent(obj);
if(parent) {
rel_x = obj->coords.x1 - parent->coords.x1;
rel_x += lv_obj_get_scroll_x(parent);
rel_x -= lv_obj_get_style_pad_left(parent, LV_PART_MAIN);
}
else {
rel_x = obj->coords.x1;
}
return rel_x;
}
lv_coord_t lv_obj_get_y(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_coord_t rel_y;
lv_obj_t * parent = lv_obj_get_parent(obj);
if(parent) {
rel_y = obj->coords.y1 - parent->coords.y1;
rel_y += lv_obj_get_scroll_y(parent);
rel_y -= lv_obj_get_style_pad_top(parent, LV_PART_MAIN);
}
else {
rel_y = obj->coords.y1;
}
return rel_y;
}
lv_coord_t lv_obj_get_width(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
return lv_area_get_width(&obj->coords);
}
lv_coord_t lv_obj_get_height(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
return lv_area_get_height(&obj->coords);
}
lv_coord_t lv_obj_get_width_fit(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_coord_t left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
lv_coord_t right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
return lv_obj_get_width(obj) - left - right;
}
lv_coord_t lv_obj_get_height_fit(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_coord_t top = lv_obj_get_style_pad_top((lv_obj_t *)obj, LV_PART_MAIN);
lv_coord_t bottom = lv_obj_get_style_pad_bottom((lv_obj_t *)obj, LV_PART_MAIN);
return lv_obj_get_height(obj) - top - bottom;
}
void lv_obj_get_coords_fit(const lv_obj_t * obj, lv_area_t * area)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_get_coords(obj, area);
area->x1 += lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
area->x2 -= lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
area->y1 += lv_obj_get_style_pad_top(obj, LV_PART_MAIN);
area->y2 -= lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN);
}
lv_coord_t lv_obj_get_height_visible(const lv_obj_t * obj)
{
lv_obj_update_layout(lv_obj_get_screen(obj));
lv_coord_t h = LV_COORD_MAX;
lv_obj_t * parent = lv_obj_get_parent(obj);
while(parent) {
h = LV_MIN(lv_obj_get_height_fit(parent), h);
parent = lv_obj_get_parent(parent);
}
return h == LV_COORD_MAX ? LV_DPI_DEF : h;
}
lv_coord_t lv_obj_get_width_visible(const lv_obj_t * obj)
{
lv_obj_update_layout(lv_obj_get_screen(obj));
lv_coord_t w = LV_COORD_MAX;
lv_obj_t * parent = lv_obj_get_parent(obj);
while(parent) {
w = LV_MIN(lv_obj_get_width_fit(parent), w);
parent = lv_obj_get_parent(parent);
}
return w == LV_COORD_MAX ? LV_DPI_DEF : w;
}
lv_coord_t lv_obj_get_self_width(struct _lv_obj_t * obj)
{
lv_point_t p = {0, LV_COORD_MIN};
lv_signal_send((lv_obj_t * )obj, LV_SIGNAL_GET_SELF_SIZE, &p);
return p.x;
}
lv_coord_t lv_obj_get_self_height(struct _lv_obj_t * obj)
{
lv_point_t p = {LV_COORD_MIN, 0};
lv_signal_send((lv_obj_t * )obj, LV_SIGNAL_GET_SELF_SIZE, &p);
return p.y;
}
bool lv_obj_handle_self_size_chg(struct _lv_obj_t * obj)
{
if(obj->w_set != LV_SIZE_CONTENT && obj->h_set == LV_SIZE_CONTENT) return false;
lv_obj_set_size(obj, obj->w_set, obj->h_set);
return true;
}
void lv_obj_move_to(lv_obj_t * obj, lv_coord_t x, lv_coord_t y, bool notify)
{
/*Convert x and y to absolute coordinates*/
lv_obj_t * parent = obj->parent;
if(parent) {
lv_coord_t pad_left = lv_obj_get_style_pad_left(parent, LV_PART_MAIN);
lv_coord_t pad_top = lv_obj_get_style_pad_top(parent, LV_PART_MAIN);
x += pad_left + parent->coords.x1 - lv_obj_get_scroll_x(parent);
y += pad_top + parent->coords.y1 - lv_obj_get_scroll_y(parent);
}
/*Calculate and set the movement*/
lv_point_t diff;
diff.x = x - obj->coords.x1;
diff.y = y - obj->coords.y1;
/* Do nothing if the position is not changed */
/* It is very important else recursive positioning can
* occur without position change*/
if(diff.x == 0 && diff.y == 0) return;
/*Invalidate the original area*/
lv_obj_invalidate(obj);
/*Save the original coordinates*/
lv_area_t ori;
lv_obj_get_coords(obj, &ori);
/*Check if the object inside the parent or not*/
lv_area_t parent_fit_area;
bool on1 = false;
if(parent) {
lv_obj_get_coords_fit(parent, &parent_fit_area);
/* If the object is already out of the parent and its position is changes
* surely the scrollbars also changes so invalidate them*/
on1 = _lv_area_is_in(&ori, &parent_fit_area, 0);
if(!on1) lv_obj_scrollbar_invalidate(parent);
}
obj->coords.x1 += diff.x;
obj->coords.y1 += diff.y;
obj->coords.x2 += diff.x;
obj->coords.y2 += diff.y;
lv_obj_move_children_by(obj, diff.x, diff.y, false);
/*Inform the object about its new coordinates*/
lv_signal_send(obj, LV_SIGNAL_COORD_CHG, &ori);
/*Send a signal to the parent too*/
if(parent && notify) lv_signal_send(parent, LV_SIGNAL_CHILD_CHG, obj);
/*Invalidate the new area*/
lv_obj_invalidate(obj);
/* If the object was out of the parent invalidate the new scrollbar area too.
* If it wasn't out of the parent but out now, also invalidate the srollbars*/
if(parent) {
bool on2 = _lv_area_is_in(&obj->coords, &parent_fit_area, 0);
if(on1 || (!on1 && on2)) lv_obj_scrollbar_invalidate(parent);
}
}
void lv_obj_move_children_by(lv_obj_t * obj, lv_coord_t x_diff, lv_coord_t y_diff, bool ignore_floating)
{
uint32_t i;
for(i = 0; i < lv_obj_get_child_cnt(obj); i++) {
lv_obj_t * child = lv_obj_get_child(obj, i);
if(ignore_floating && lv_obj_has_flag(child, LV_OBJ_FLAG_FLOATING)) continue;
child->coords.x1 += x_diff;
child->coords.y1 += y_diff;
child->coords.x2 += x_diff;
child->coords.y2 += y_diff;
lv_obj_move_children_by(child, x_diff, y_diff, false);
}
}
void lv_obj_invalidate_area(const lv_obj_t * obj, const lv_area_t * area)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_area_t area_tmp;
lv_area_copy(&area_tmp, area);
bool visible = lv_obj_area_is_visible(obj, &area_tmp);
if(visible) _lv_inv_area(lv_obj_get_disp(obj), &area_tmp);
}
void lv_obj_invalidate(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
/*Truncate the area to the object*/
lv_area_t obj_coords;
lv_coord_t ext_size = _lv_obj_get_ext_draw_size(obj);
lv_area_copy(&obj_coords, &obj->coords);
obj_coords.x1 -= ext_size;
obj_coords.y1 -= ext_size;
obj_coords.x2 += ext_size;
obj_coords.y2 += ext_size;
lv_obj_invalidate_area(obj, &obj_coords);
}
bool lv_obj_area_is_visible(const lv_obj_t * obj, lv_area_t * area)
{
if(lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN)) return false;
/*Invalidate the object only if it belongs to the current or previous'*/
lv_obj_t * obj_scr = lv_obj_get_screen(obj);
lv_disp_t * disp = lv_obj_get_disp(obj_scr);
if(obj_scr != lv_disp_get_scr_act(disp) &&
obj_scr != lv_disp_get_scr_prev(disp) &&
obj_scr != lv_disp_get_layer_top(disp) &&
obj_scr != lv_disp_get_layer_sys(disp))
{
return false;
}
/*Truncate the area to the object*/
lv_area_t obj_coords;
lv_coord_t ext_size = _lv_obj_get_ext_draw_size(obj);
lv_area_copy(&obj_coords, &obj->coords);
obj_coords.x1 -= ext_size;
obj_coords.y1 -= ext_size;
obj_coords.x2 += ext_size;
obj_coords.y2 += ext_size;
bool is_common;
is_common = _lv_area_intersect(area, area, &obj_coords);
if(is_common == false) return false; /*The area is not on the object*/
/*Truncate recursively to the parents*/
lv_obj_t * par = lv_obj_get_parent(obj);
while(par != NULL) {
is_common = _lv_area_intersect(area, area, &par->coords);
if(is_common == false) return false; /*If no common parts with parent break;*/
if(lv_obj_has_flag(par, LV_OBJ_FLAG_HIDDEN)) return false; /*If the parent is hidden then the child is hidden and won't be drawn*/
par = lv_obj_get_parent(par);
}
return true;
}
bool lv_obj_is_visible(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_area_t obj_coords;
lv_coord_t ext_size = _lv_obj_get_ext_draw_size(obj);
lv_area_copy(&obj_coords, &obj->coords);
obj_coords.x1 -= ext_size;
obj_coords.y1 -= ext_size;
obj_coords.x2 += ext_size;
obj_coords.y2 += ext_size;
return lv_obj_area_is_visible(obj, &obj_coords);
}
void lv_obj_set_ext_click_area(lv_obj_t * obj, lv_coord_t size)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_allocate_spec_attr(obj);
obj->spec_attr->ext_click_pad = size;
}
void lv_obj_get_click_area(const lv_obj_t * obj, lv_area_t * area)
{
lv_area_copy(area, &obj->coords);
if(obj->spec_attr) {
area->x1 -= obj->spec_attr->ext_click_pad;
area->x2 += obj->spec_attr->ext_click_pad;
area->y1 -= obj->spec_attr->ext_click_pad;
area->y2 += obj->spec_attr->ext_click_pad;
}
}
bool lv_obj_hit_test(lv_obj_t * obj, const lv_point_t * point)
{
lv_area_t a;
lv_obj_get_click_area(obj, &a);
bool res = _lv_area_is_point_on(&a, point, 0);
if(res == false) return false;
if(lv_obj_has_flag(obj, LV_OBJ_FLAG_ADV_HITTEST)) {
lv_hit_test_info_t hit_info;
hit_info.point = point;
hit_info.result = true;
lv_signal_send(obj, LV_SIGNAL_HIT_TEST, &hit_info);
return hit_info.result;
}
return res;
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Set the size of an object.
* It's the core function to set the size of objects but user should use `lv_obj_set_size/width/height/..` etc.
* @param obj pointer to an object
* @param w the new width in pixels
* @param h the new height in pixels
* @return true: the size was changed; false: `w` and `h` was equal to the current width and height so nothing happened.
*/
static bool refr_size(lv_obj_t * obj, lv_coord_t w, lv_coord_t h)
{
/* If the size is managed by the layout don't let to overwrite it.*/
if(obj->w_set == LV_SIZE_LAYOUT) w = lv_obj_get_width(obj);
if(obj->h_set == LV_SIZE_LAYOUT) h = lv_obj_get_height(obj);
/* Do nothing if the size is not changed */
/* It is very important else recursive resizing can
* occur without size change*/
if(lv_obj_get_width(obj) == w && lv_obj_get_height(obj) == h) {
return false;
}
/*Invalidate the original area*/
lv_obj_invalidate(obj);
/*Save the original coordinates*/
lv_area_t ori;
lv_obj_get_coords(obj, &ori);
lv_obj_t * parent = lv_obj_get_parent(obj);
/*Check if the object inside the parent or not*/
lv_area_t parent_fit_area;
lv_obj_get_coords_fit(parent, &parent_fit_area);
/* If the object is already out of the parent and its position is changes
* surely the scrollbars also changes so invalidate them*/
bool on1 = _lv_area_is_in(&ori, &parent_fit_area, 0);
if(!on1) lv_obj_scrollbar_invalidate(parent);
/* Set the length and height
* Be sure the content is not scrolled in an invalid position on the new size*/
obj->coords.y2 = obj->coords.y1 + h - 1;
if(lv_obj_get_base_dir(obj) == LV_BIDI_DIR_RTL) {
obj->coords.x1 = obj->coords.x2 - w + 1;
}
else {
obj->coords.x2 = obj->coords.x1 + w - 1;
}
/*Send a signal to the object with its new coordinates*/
lv_signal_send(obj, LV_SIGNAL_COORD_CHG, &ori);
/*Send a signal to the parent too*/
if(parent != NULL) lv_signal_send(parent, LV_SIGNAL_CHILD_CHG, obj);
/*Invalidate the new area*/
lv_obj_invalidate(obj);
/* If the object was out of the parent invalidate the new scrollbar area too.
* If it wasn't out of the parent but out now, also invalidate the srollbars*/
bool on2 = _lv_area_is_in(&obj->coords, &parent_fit_area, 0);
if(on1 || (!on1 && on2)) lv_obj_scrollbar_invalidate(parent);
return true;
}
/**
* 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)
{
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;
}
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;
}
}
static void layout_update_core(lv_obj_t * obj)
{
uint32_t i;
for(i = 0; i < lv_obj_get_child_cnt(obj); i++) {
lv_obj_t * child = lv_obj_get_child(obj, i);
layout_update_core(child);
}
if(!obj->layout_inv) return;
const lv_layout_dsc_t * layout = obj->spec_attr ? obj->spec_attr->layout_dsc : NULL;
const lv_layout_update_cb_t update_cp = layout ? layout->update_cb : NULL;
if(update_cp != NULL && lv_obj_get_child_cnt(obj) > 0) {
obj->layout_inv = 0;
update_cp(obj);
}
}