feat(layout): add deferred layout recalculation
This commit is contained in:
@@ -11,8 +11,13 @@ static void float_btn_event_cb(lv_obj_t * float_btn, lv_event_t e)
|
||||
lv_snprintf(buf, sizeof(buf), "Track %d", btn_cnt);
|
||||
lv_obj_t * list_btn = lv_list_add_btn(list, LV_SYMBOL_AUDIO, buf, NULL);
|
||||
btn_cnt++;
|
||||
lv_obj_scroll_to_view(list_btn, LV_ANIM_ON);
|
||||
|
||||
lv_obj_move_foreground(float_btn);
|
||||
|
||||
/* Layouts are only recalculated later but we need to know the updated position of the new button
|
||||
* to scroll to it. */
|
||||
lv_obj_update_layout(list);
|
||||
lv_obj_scroll_to_view(list_btn, LV_ANIM_ON);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ typedef struct {
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
static void flex_update(lv_obj_t * cont, lv_obj_t * item);
|
||||
static void flex_update(lv_obj_t * cont);
|
||||
static int32_t find_track_end(lv_obj_t * cont, int32_t item_start_id, lv_coord_t item_gap, lv_coord_t max_main_size, track_t * t);
|
||||
static void children_repos(lv_obj_t * cont, int32_t item_first_id, int32_t item_last_id, lv_coord_t abs_x, lv_coord_t abs_y, lv_coord_t max_main_size, lv_coord_t item_gap, track_t * t);
|
||||
static void place_content(lv_flex_place_t place, lv_coord_t max_size, lv_coord_t content_size, lv_coord_t item_cnt, lv_coord_t * start_pos, lv_coord_t * gap);
|
||||
@@ -145,17 +145,15 @@ void lv_obj_set_flex_grow(struct _lv_obj_t * obj, uint8_t grow)
|
||||
if(f->dir == LV_FLEX_FLOW_ROW) lv_obj_set_width(obj, (LV_COORD_SET_LAYOUT(grow)));
|
||||
else lv_obj_set_height(obj, (LV_COORD_SET_LAYOUT(grow)));
|
||||
|
||||
lv_obj_update_layout(parent, obj);
|
||||
lv_obj_mark_layout_as_dirty(parent);
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
static void flex_update(lv_obj_t * cont, lv_obj_t * item)
|
||||
static void flex_update(lv_obj_t * cont)
|
||||
{
|
||||
LV_UNUSED(item);
|
||||
|
||||
if(cont->spec_attr == NULL) return;
|
||||
const lv_flex_t * f = (const lv_flex_t *)cont->spec_attr->layout_dsc;
|
||||
|
||||
|
||||
@@ -43,9 +43,8 @@ void lv_obj_move_to(lv_obj_t * obj, lv_coord_t x, lv_coord_t y, bool notify_pare
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
static void grid_update(lv_obj_t * cont, lv_obj_t * item);
|
||||
static void grid_update(lv_obj_t * cont);
|
||||
static void full_refresh(lv_obj_t * cont);
|
||||
static void item_refr(lv_obj_t * item);
|
||||
static void calc(struct _lv_obj_t * obj, _lv_grid_calc_t * calc);
|
||||
static void calc_free(_lv_grid_calc_t * calc);
|
||||
static void calc_cols(lv_obj_t * cont, _lv_grid_calc_t * c);
|
||||
@@ -109,7 +108,7 @@ void lv_obj_set_grid_cell(lv_obj_t * obj, lv_grid_place_t hor_place, uint8_t col
|
||||
obj->x_set = LV_COORD_SET_LAYOUT(col_pos | (col_span << CELL_SHIFT) | (hor_place << (CELL_SHIFT * 2)));
|
||||
obj->y_set = LV_COORD_SET_LAYOUT(row_pos | (row_span << CELL_SHIFT) | (ver_place << (CELL_SHIFT * 2)));
|
||||
|
||||
lv_obj_update_layout(parent, obj);
|
||||
lv_obj_mark_layout_as_dirty(parent);
|
||||
|
||||
}
|
||||
|
||||
@@ -118,15 +117,14 @@ void lv_obj_set_grid_cell(lv_obj_t * obj, lv_grid_place_t hor_place, uint8_t col
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
static void grid_update(lv_obj_t * cont, lv_obj_t * item)
|
||||
static void grid_update(lv_obj_t * cont)
|
||||
{
|
||||
if(cont->spec_attr == NULL) return;
|
||||
if(cont->spec_attr->layout_dsc == NULL) return;
|
||||
|
||||
LV_LOG_INFO("update 0x%p container, triggered by 0x%p", cont, item);
|
||||
|
||||
if(item) item_refr(item);
|
||||
else full_refresh(cont);
|
||||
full_refresh(cont);
|
||||
|
||||
LV_TRACE_LAYOUT("finished");
|
||||
}
|
||||
@@ -168,27 +166,6 @@ static void full_refresh(lv_obj_t * cont)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the position of a grid item
|
||||
* @param item pointer to a grid item
|
||||
*/
|
||||
static void item_refr(lv_obj_t * item)
|
||||
{
|
||||
/*Calculate the grid*/
|
||||
lv_obj_t * cont = lv_obj_get_parent(item);
|
||||
if(cont == NULL) return;
|
||||
_lv_grid_calc_t c;
|
||||
calc(cont, &c);
|
||||
|
||||
item_repos(item, &c, NULL);
|
||||
|
||||
calc_free(&c);
|
||||
|
||||
if(cont->w_set == LV_SIZE_CONTENT || cont->h_set == LV_SIZE_CONTENT) {
|
||||
lv_obj_set_size(cont, cont->w_set, cont->h_set);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the grid cells coordinates
|
||||
* @param cont an object that has a grid
|
||||
|
||||
@@ -336,7 +336,7 @@ void lv_obj_add_flag(lv_obj_t * obj, lv_obj_flag_t f)
|
||||
}
|
||||
|
||||
if((was_on_layout != lv_obj_is_layout_positioned(obj)) || (f & (LV_OBJ_FLAG_LAYOUT_1 | LV_OBJ_FLAG_LAYOUT_2))) {
|
||||
lv_obj_update_layout(lv_obj_get_parent(obj), obj);
|
||||
lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,12 +351,12 @@ void lv_obj_clear_flag(lv_obj_t * obj, lv_obj_flag_t f)
|
||||
if(f & LV_OBJ_FLAG_HIDDEN) {
|
||||
lv_obj_invalidate(obj);
|
||||
if(lv_obj_is_layout_positioned(obj)) {
|
||||
lv_obj_update_layout(lv_obj_get_parent(obj), obj);
|
||||
lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj));
|
||||
}
|
||||
}
|
||||
|
||||
if((was_on_layout != lv_obj_is_layout_positioned(obj)) || (f & (LV_OBJ_FLAG_LAYOUT_1 | LV_OBJ_FLAG_LAYOUT_2))) {
|
||||
lv_obj_update_layout(lv_obj_get_parent(obj), obj);
|
||||
lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -933,7 +933,7 @@ static lv_res_t lv_obj_signal(lv_obj_t * obj, lv_signal_t sign, void * param)
|
||||
lv_obj_set_size(child, child->w_set, child->h_set);
|
||||
}
|
||||
}
|
||||
lv_obj_update_layout(obj, NULL);
|
||||
lv_obj_mark_layout_as_dirty(obj);
|
||||
}
|
||||
|
||||
|
||||
@@ -966,7 +966,7 @@ static lv_res_t lv_obj_signal(lv_obj_t * obj, lv_signal_t sign, void * param)
|
||||
}
|
||||
}
|
||||
else if(sign == LV_SIGNAL_CHILD_CHG) {
|
||||
lv_obj_update_layout(obj, param);
|
||||
lv_obj_mark_layout_as_dirty(obj);
|
||||
|
||||
if(obj->w_set == LV_SIZE_CONTENT || obj->h_set == LV_SIZE_CONTENT) {
|
||||
lv_obj_set_size(obj, obj->w_set, obj->h_set);
|
||||
@@ -975,7 +975,7 @@ static lv_res_t lv_obj_signal(lv_obj_t * obj, lv_signal_t sign, void * param)
|
||||
else if(sign == LV_SIGNAL_BASE_DIR_CHG) {
|
||||
/* The layout might depend on the base dir.
|
||||
* E.g. the first is element is on the left or right*/
|
||||
lv_obj_update_layout(obj, NULL);
|
||||
lv_obj_mark_layout_as_dirty(obj);
|
||||
}
|
||||
else if(sign == LV_SIGNAL_SCROLL) {
|
||||
res = lv_event_send(obj, LV_EVENT_SCROLL, NULL);
|
||||
@@ -993,7 +993,7 @@ static lv_res_t lv_obj_signal(lv_obj_t * obj, lv_signal_t sign, void * param)
|
||||
}
|
||||
else if(sign == LV_SIGNAL_STYLE_CHG) {
|
||||
/* Padding might have changed so the layout should be recalculated*/
|
||||
lv_obj_update_layout(obj, NULL);
|
||||
lv_obj_mark_layout_as_dirty(obj);
|
||||
|
||||
/*Reposition non grid objects on by one*/
|
||||
uint32_t i;
|
||||
|
||||
@@ -268,6 +268,7 @@ typedef struct _lv_obj_t{
|
||||
lv_coord_t h_set;
|
||||
lv_obj_flag_t flags;
|
||||
lv_state_t state;
|
||||
uint8_t layout_inv:1;
|
||||
}lv_obj_t;
|
||||
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
**********************/
|
||||
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);
|
||||
|
||||
@@ -148,7 +149,7 @@ void lv_obj_set_layout(lv_obj_t * obj, const void * layout)
|
||||
lv_obj_allocate_spec_attr(obj);
|
||||
obj->spec_attr->layout_dsc = layout;
|
||||
|
||||
lv_obj_update_layout(obj, NULL);
|
||||
lv_obj_mark_layout_as_dirty(obj);
|
||||
}
|
||||
|
||||
bool lv_obj_is_layout_positioned(const lv_obj_t * obj)
|
||||
@@ -161,15 +162,35 @@ bool lv_obj_is_layout_positioned(const lv_obj_t * obj)
|
||||
else return false;
|
||||
}
|
||||
|
||||
void lv_obj_update_layout(lv_obj_t * obj, lv_obj_t * item)
|
||||
void lv_obj_mark_layout_as_dirty(lv_obj_t * obj)
|
||||
{
|
||||
if(obj->spec_attr == NULL) return;
|
||||
if(obj->spec_attr->layout_dsc == NULL) return;
|
||||
if(obj->spec_attr->child_cnt == 0) return;
|
||||
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.*/
|
||||
scr->layout_inv = 1;
|
||||
|
||||
const lv_layout_dsc_t * layout = obj->spec_attr->layout_dsc;
|
||||
if(layout->update_cb == NULL) return;
|
||||
layout->update_cb(obj, item);
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -696,3 +717,21 @@ static void calc_auto_size(lv_obj_t * obj, lv_coord_t * w_out, lv_coord_t * h_ou
|
||||
*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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ extern "C" {
|
||||
**********************/
|
||||
struct _lv_obj_t;
|
||||
|
||||
typedef void (*lv_layout_update_cb_t)(struct _lv_obj_t * cont, struct _lv_obj_t * item);
|
||||
typedef void (*lv_layout_update_cb_t)(struct _lv_obj_t * cont);
|
||||
|
||||
/**
|
||||
* The base of all layouts descriptor.
|
||||
@@ -126,14 +126,17 @@ void lv_obj_set_layout(struct _lv_obj_t * obj, const void * layout);
|
||||
*/
|
||||
bool lv_obj_is_layout_positioned(const struct _lv_obj_t * obj);
|
||||
|
||||
/**
|
||||
* Mark the object for layout update.
|
||||
* @param obj pointer to an object whose children needs to be updated
|
||||
*/
|
||||
void lv_obj_mark_layout_as_dirty(struct _lv_obj_t * obj);
|
||||
|
||||
/**
|
||||
* Update the layout of an object.
|
||||
* @param obj pointer to an object whose children needs to be updated
|
||||
* @param item pointer to a child object that triggered the update. Set to `NULL` is not known.
|
||||
* If not `NULL` the update process can make some optimization
|
||||
* to update only the required parts of the layout
|
||||
*/
|
||||
void lv_obj_update_layout(struct _lv_obj_t * obj, struct _lv_obj_t * item);
|
||||
void lv_obj_update_layout(struct _lv_obj_t * obj);
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -199,10 +199,25 @@ void _lv_disp_refr_timer(lv_timer_t * tmr)
|
||||
lv_timer_pause(tmr, true);
|
||||
#endif
|
||||
|
||||
/*Refresh the screen's layout if required*/
|
||||
uint32_t i;
|
||||
for(i = 0; i < disp_refr->screen_cnt; i++) {
|
||||
lv_obj_update_layout(disp_refr->screens[i]);
|
||||
disp_refr->screens[i]->layout_inv = 0;
|
||||
}
|
||||
|
||||
lv_obj_update_layout(disp_refr->top_layer);
|
||||
disp_refr->top_layer->layout_inv = 0;
|
||||
|
||||
lv_obj_update_layout(disp_refr->sys_layer);
|
||||
disp_refr->sys_layer->layout_inv = 0;
|
||||
|
||||
|
||||
/*Do nothing if there is no active screen*/
|
||||
if(disp_refr->act_scr == NULL) {
|
||||
disp_refr->inv_p = 0;
|
||||
TRACE_REFR("finished (there were no invalid areas to redraw)");
|
||||
LV_LOG_WARN("there is no active screen");
|
||||
TRACE_REFR("finished");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -175,7 +175,7 @@ typedef struct _lv_disp_t {
|
||||
uint32_t inv_p : 10;
|
||||
|
||||
/*Miscellaneous data*/
|
||||
uint32_t last_activity_time; /**< Last time there was activity on this display */
|
||||
uint32_t last_activity_time; /**< Last time when there was activity on this display */
|
||||
} lv_disp_t;
|
||||
|
||||
typedef enum {
|
||||
|
||||
Reference in New Issue
Block a user