feat(layout): add deferred layout recalculation

This commit is contained in:
Gabor Kiss-Vamosi
2021-03-03 20:15:21 +01:00
parent 3ecf550bcd
commit 0d38d100c2
9 changed files with 93 additions and 55 deletions

View File

@@ -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_snprintf(buf, sizeof(buf), "Track %d", btn_cnt);
lv_obj_t * list_btn = lv_list_add_btn(list, LV_SYMBOL_AUDIO, buf, NULL); lv_obj_t * list_btn = lv_list_add_btn(list, LV_SYMBOL_AUDIO, buf, NULL);
btn_cnt++; btn_cnt++;
lv_obj_scroll_to_view(list_btn, LV_ANIM_ON);
lv_obj_move_foreground(float_btn); 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);
} }
} }

View File

@@ -32,7 +32,7 @@ typedef struct {
/********************** /**********************
* STATIC PROTOTYPES * 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 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 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); 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))); 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))); 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 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; if(cont->spec_attr == NULL) return;
const lv_flex_t * f = (const lv_flex_t *)cont->spec_attr->layout_dsc; const lv_flex_t * f = (const lv_flex_t *)cont->spec_attr->layout_dsc;

View File

@@ -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 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 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(struct _lv_obj_t * obj, _lv_grid_calc_t * calc);
static void calc_free(_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); 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->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))); 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 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 == NULL) return;
if(cont->spec_attr->layout_dsc == NULL) return; if(cont->spec_attr->layout_dsc == NULL) return;
LV_LOG_INFO("update 0x%p container, triggered by 0x%p", cont, item); LV_LOG_INFO("update 0x%p container, triggered by 0x%p", cont, item);
if(item) item_refr(item); full_refresh(cont);
else full_refresh(cont);
LV_TRACE_LAYOUT("finished"); 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 * Calculate the grid cells coordinates
* @param cont an object that has a grid * @param cont an object that has a grid

View File

@@ -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))) { 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) { if(f & LV_OBJ_FLAG_HIDDEN) {
lv_obj_invalidate(obj); lv_obj_invalidate(obj);
if(lv_obj_is_layout_positioned(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))) { 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_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) { 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) { if(obj->w_set == LV_SIZE_CONTENT || obj->h_set == LV_SIZE_CONTENT) {
lv_obj_set_size(obj, obj->w_set, obj->h_set); 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) { else if(sign == LV_SIGNAL_BASE_DIR_CHG) {
/* The layout might depend on the base dir. /* The layout might depend on the base dir.
* E.g. the first is element is on the left or right*/ * 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) { else if(sign == LV_SIGNAL_SCROLL) {
res = lv_event_send(obj, LV_EVENT_SCROLL, NULL); 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) { else if(sign == LV_SIGNAL_STYLE_CHG) {
/* Padding might have changed so the layout should be recalculated*/ /* 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*/ /*Reposition non grid objects on by one*/
uint32_t i; uint32_t i;

View File

@@ -268,6 +268,7 @@ typedef struct _lv_obj_t{
lv_coord_t h_set; lv_coord_t h_set;
lv_obj_flag_t flags; lv_obj_flag_t flags;
lv_state_t state; lv_state_t state;
uint8_t layout_inv:1;
}lv_obj_t; }lv_obj_t;

View File

@@ -24,6 +24,7 @@
**********************/ **********************/
static bool refr_size(lv_obj_t * obj, lv_coord_t w, lv_coord_t h); 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 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); 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); lv_obj_allocate_spec_attr(obj);
obj->spec_attr->layout_dsc = layout; 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) 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; 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; obj->layout_inv = 1;
if(obj->spec_attr->layout_dsc == NULL) return;
if(obj->spec_attr->child_cnt == 0) return; /*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) 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; *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);
}
}

View File

@@ -24,7 +24,7 @@ extern "C" {
**********************/ **********************/
struct _lv_obj_t; 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. * 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); 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. * Update the layout of an object.
* @param obj pointer to an object whose children needs to be updated * @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);
/** /**

View File

@@ -199,10 +199,25 @@ void _lv_disp_refr_timer(lv_timer_t * tmr)
lv_timer_pause(tmr, true); lv_timer_pause(tmr, true);
#endif #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*/ /*Do nothing if there is no active screen*/
if(disp_refr->act_scr == NULL) { if(disp_refr->act_scr == NULL) {
disp_refr->inv_p = 0; 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; return;
} }

View File

@@ -175,7 +175,7 @@ typedef struct _lv_disp_t {
uint32_t inv_p : 10; uint32_t inv_p : 10;
/*Miscellaneous data*/ /*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; } lv_disp_t;
typedef enum { typedef enum {