feat(table): add user_data to table cells (#4767)

Co-authored-by: Oleg Belousov <bov@silentwings.ru>
This commit is contained in:
Oleg Belousov 2023-11-08 18:29:01 +03:00 committed by Gabor Kiss-Vamosi
parent a5f6e2761f
commit 81e498baf4
2 changed files with 138 additions and 42 deletions

View File

@ -41,7 +41,7 @@ static void refr_size_form_row(lv_obj_t * obj, uint32_t start_row);
static void refr_cell_size(lv_obj_t * obj, uint32_t row, uint32_t col);
static lv_result_t get_pressed_cell(lv_obj_t * obj, uint32_t * row, uint32_t * col);
static size_t get_cell_txt_len(const char * txt);
static void copy_cell_txt(char * dst, const char * txt);
static void copy_cell_txt(lv_table_cell_t * dst, const char * txt);
static void get_cell_area(lv_obj_t * obj, uint32_t row, uint32_t col, lv_area_t * area);
static void scroll_to_selected_cell(lv_obj_t * obj);
@ -100,7 +100,12 @@ void lv_table_set_cell_value(lv_obj_t * obj, uint32_t row, uint32_t col, const c
lv_table_cell_ctrl_t ctrl = 0;
/*Save the control byte*/
if(table->cell_data[cell]) ctrl = table->cell_data[cell][0];
if(table->cell_data[cell]) ctrl = table->cell_data[cell]->ctrl;
void * user_data = NULL;
/*Save the user data*/
if(table->cell_data[cell]) user_data = table->cell_data[cell]->user_data;
size_t to_allocate = get_cell_txt_len(txt);
@ -110,7 +115,8 @@ void lv_table_set_cell_value(lv_obj_t * obj, uint32_t row, uint32_t col, const c
copy_cell_txt(table->cell_data[cell], txt);
table->cell_data[cell][0] = ctrl;
table->cell_data[cell]->ctrl = ctrl;
table->cell_data[cell]->user_data = user_data;
refr_cell_size(obj, row, col);
}
@ -133,7 +139,12 @@ void lv_table_set_cell_value_fmt(lv_obj_t * obj, uint32_t row, uint32_t col, con
lv_table_cell_ctrl_t ctrl = 0;
/*Save the control byte*/
if(table->cell_data[cell]) ctrl = table->cell_data[cell][0];
if(table->cell_data[cell]) ctrl = table->cell_data[cell]->ctrl;
void * user_data = NULL;
/*Save the user_data*/
if(table->cell_data[cell]) user_data = table->cell_data[cell]->user_data;
va_list ap, ap2;
va_start(ap, fmt);
@ -156,32 +167,33 @@ void lv_table_set_cell_value_fmt(lv_obj_t * obj, uint32_t row, uint32_t col, con
/*Get the size of the Arabic text and process it*/
size_t len_ap = _lv_text_ap_calc_bytes_cnt(raw_txt);
table->cell_data[cell] = lv_realloc(table->cell_data[cell], len_ap + 1);
table->cell_data[cell] = lv_realloc(table->cell_data[cell], sizeof(lv_table_cell_t) + len_ap + 1);
LV_ASSERT_MALLOC(table->cell_data[cell]);
if(table->cell_data[cell] == NULL) {
va_end(ap2);
return;
}
_lv_text_ap_proc(raw_txt, &table->cell_data[cell][1]);
_lv_text_ap_proc(raw_txt, table->cell_data[cell]->txt);
lv_free(raw_txt);
#else
table->cell_data[cell] = lv_realloc(table->cell_data[cell], len + 2); /*+1: trailing '\0; +1: format byte*/
table->cell_data[cell] = lv_realloc(table->cell_data[cell],
sizeof(lv_table_cell_t) + len + 1); /*+1: trailing '\0; */
LV_ASSERT_MALLOC(table->cell_data[cell]);
if(table->cell_data[cell] == NULL) {
va_end(ap2);
return;
}
table->cell_data[cell][len + 1] = 0; /*Ensure NULL termination*/
table->cell_data[cell]->txt[len] = 0; /*Ensure NULL termination*/
lv_vsnprintf(&table->cell_data[cell][1], len + 1, fmt, ap2);
lv_vsnprintf(table->cell_data[cell]->txt, len, fmt, ap2);
#endif
va_end(ap2);
table->cell_data[cell][0] = ctrl;
table->cell_data[cell]->ctrl = ctrl;
table->cell_data[cell]->user_data = user_data;
refr_cell_size(obj, row, col);
}
@ -206,11 +218,15 @@ void lv_table_set_row_cnt(lv_obj_t * obj, uint32_t row_cnt)
uint32_t new_cell_cnt = table->col_cnt * table->row_cnt;
uint32_t i;
for(i = new_cell_cnt; i < old_cell_cnt; i++) {
if(table->cell_data[i]->user_data) {
lv_free(table->cell_data[i]->user_data);
table->cell_data[i]->user_data = NULL;
}
lv_free(table->cell_data[i]);
}
}
table->cell_data = lv_realloc(table->cell_data, table->row_cnt * table->col_cnt * sizeof(char *));
table->cell_data = lv_realloc(table->cell_data, table->row_cnt * table->col_cnt * sizeof(lv_table_cell_t *));
LV_ASSERT_MALLOC(table->cell_data);
if(table->cell_data == NULL) return;
@ -235,7 +251,7 @@ void lv_table_set_col_cnt(lv_obj_t * obj, uint32_t col_cnt)
uint32_t old_col_cnt = table->col_cnt;
table->col_cnt = col_cnt;
char ** new_cell_data = lv_malloc(table->row_cnt * table->col_cnt * sizeof(char *));
lv_table_cell_t ** new_cell_data = lv_malloc(table->row_cnt * table->col_cnt * sizeof(lv_table_cell_t *));
LV_ASSERT_MALLOC(new_cell_data);
if(new_cell_data == NULL) return;
uint32_t new_cell_cnt = table->col_cnt * table->row_cnt;
@ -258,6 +274,10 @@ void lv_table_set_col_cnt(lv_obj_t * obj, uint32_t col_cnt)
int32_t i;
for(i = 0; i < (int32_t)old_col_cnt - (int32_t)col_cnt; i++) {
uint32_t idx = old_col_start + min_col_cnt + i;
if(table->cell_data[idx]->user_data) {
lv_free(table->cell_data[idx]->user_data);
table->cell_data[idx]->user_data = NULL;
}
lv_free(table->cell_data[idx]);
table->cell_data[idx] = NULL;
}
@ -306,15 +326,16 @@ void lv_table_add_cell_ctrl(lv_obj_t * obj, uint32_t row, uint32_t col, lv_table
uint32_t cell = row * table->col_cnt + col;
if(is_cell_empty(table->cell_data[cell])) {
table->cell_data[cell] = lv_malloc(2); /*+1: trailing '\0; +1: format byte*/
table->cell_data[cell] = lv_malloc(sizeof(lv_table_cell_t) + 1); /*+1: trailing '\0 */
LV_ASSERT_MALLOC(table->cell_data[cell]);
if(table->cell_data[cell] == NULL) return;
table->cell_data[cell][0] = 0;
table->cell_data[cell][1] = '\0';
table->cell_data[cell]->ctrl = 0;
table->cell_data[cell]->user_data = NULL;
table->cell_data[cell]->txt[0] = '\0';
}
table->cell_data[cell][0] |= ctrl;
table->cell_data[cell]->ctrl |= ctrl;
}
void lv_table_clear_cell_ctrl(lv_obj_t * obj, uint32_t row, uint32_t col, lv_table_cell_ctrl_t ctrl)
@ -330,15 +351,45 @@ void lv_table_clear_cell_ctrl(lv_obj_t * obj, uint32_t row, uint32_t col, lv_tab
uint32_t cell = row * table->col_cnt + col;
if(is_cell_empty(table->cell_data[cell])) {
table->cell_data[cell] = lv_malloc(2); /*+1: trailing '\0; +1: format byte*/
table->cell_data[cell] = lv_malloc(sizeof(lv_table_cell_t) + 1); /*+1: trailing '\0 */
LV_ASSERT_MALLOC(table->cell_data[cell]);
if(table->cell_data[cell] == NULL) return;
table->cell_data[cell][0] = 0;
table->cell_data[cell][1] = '\0';
table->cell_data[cell]->ctrl = 0;
table->cell_data[cell]->user_data = NULL;
table->cell_data[cell]->txt[0] = '\0';
}
table->cell_data[cell][0] &= (~ctrl);
table->cell_data[cell]->ctrl &= (~ctrl);
}
void lv_table_set_user_data(lv_obj_t * obj, uint16_t row, uint16_t col, void * user_data)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_table_t * table = (lv_table_t *)obj;
/*Auto expand*/
if(col >= table->col_cnt) lv_table_set_col_cnt(obj, col + 1);
if(row >= table->row_cnt) lv_table_set_row_cnt(obj, row + 1);
uint32_t cell = row * table->col_cnt + col;
if(is_cell_empty(table->cell_data[cell])) {
table->cell_data[cell] = lv_malloc(sizeof(lv_table_cell_t) + 1); /*+1: trailing '\0 */
LV_ASSERT_MALLOC(table->cell_data[cell]);
if(table->cell_data[cell] == NULL) return;
table->cell_data[cell]->ctrl = 0;
table->cell_data[cell]->user_data = NULL;
table->cell_data[cell]->txt[0] = '\0';
}
if(table->cell_data[cell]->user_data) {
lv_free(table->cell_data[cell]->user_data);
}
table->cell_data[cell]->user_data = user_data;
}
/*=====================
@ -358,7 +409,7 @@ const char * lv_table_get_cell_value(lv_obj_t * obj, uint32_t row, uint32_t col)
if(is_cell_empty(table->cell_data[cell])) return "";
return &table->cell_data[cell][1]; /*Skip the format byte*/
return table->cell_data[cell]->txt;
}
uint32_t lv_table_get_row_cnt(lv_obj_t * obj)
@ -403,7 +454,7 @@ bool lv_table_has_cell_ctrl(lv_obj_t * obj, uint32_t row, uint32_t col, lv_table
uint32_t cell = row * table->col_cnt + col;
if(is_cell_empty(table->cell_data[cell])) return false;
else return (table->cell_data[cell][0] & ctrl) == ctrl;
else return (table->cell_data[cell]->ctrl & ctrl) == ctrl;
}
void lv_table_get_selected_cell(lv_obj_t * obj, uint32_t * row, uint32_t * col)
@ -413,6 +464,22 @@ void lv_table_get_selected_cell(lv_obj_t * obj, uint32_t * row, uint32_t * col)
*col = table->col_act;
}
void * lv_table_get_user_data(lv_obj_t * obj, uint16_t row, uint16_t col)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_table_t * table = (lv_table_t *)obj;
if(row >= table->row_cnt || col >= table->col_cnt) {
LV_LOG_WARN("invalid row or column");
return NULL;
}
uint32_t cell = row * table->col_cnt + col;
if(is_cell_empty(table->cell_data[cell])) return NULL;
return table->cell_data[cell]->user_data;
}
/**********************
* STATIC FUNCTIONS
**********************/
@ -430,7 +497,7 @@ static void lv_table_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
table->row_h = lv_malloc(table->row_cnt * sizeof(table->row_h[0]));
table->col_w[0] = LV_DPI_DEF;
table->row_h[0] = LV_DPI_DEF;
table->cell_data = lv_realloc(table->cell_data, table->row_cnt * table->col_cnt * sizeof(char *));
table->cell_data = lv_realloc(table->cell_data, table->row_cnt * table->col_cnt * sizeof(lv_table_cell_t *));
table->cell_data[0] = NULL;
LV_TRACE_OBJ_CREATE("finished");
@ -444,6 +511,10 @@ static void lv_table_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
uint32_t i;
for(i = 0; i < table->col_cnt * table->row_cnt; i++) {
if(table->cell_data[i]) {
if(table->cell_data[i]->user_data) {
lv_free(table->cell_data[i]->user_data);
table->cell_data[i]->user_data = NULL;
}
lv_free(table->cell_data[i]);
table->cell_data[i] = NULL;
}
@ -635,7 +706,7 @@ static void draw_main(lv_event_t * e)
for(col = 0; col < table->col_cnt; col++) {
lv_table_cell_ctrl_t ctrl = 0;
if(table->cell_data[cell]) ctrl = table->cell_data[cell][0];
if(table->cell_data[cell]) ctrl = table->cell_data[cell]->ctrl;
if(rtl) {
cell_area.x2 = cell_area.x1 - 1;
@ -648,11 +719,11 @@ static void draw_main(lv_event_t * e)
uint32_t col_merge = 0;
for(col_merge = 0; col_merge + col < table->col_cnt - 1; col_merge++) {
char * next_cell_data = table->cell_data[cell + col_merge];
lv_table_cell_t * next_cell_data = table->cell_data[cell + col_merge];
if(is_cell_empty(next_cell_data)) break;
lv_table_cell_ctrl_t merge_ctrl = (lv_table_cell_ctrl_t) next_cell_data[0];
lv_table_cell_ctrl_t merge_ctrl = (lv_table_cell_ctrl_t) next_cell_data->ctrl;
if(merge_ctrl & LV_TABLE_CELL_CTRL_MERGE_RIGHT) {
int32_t offset = table->col_w[col + col_merge + 1];
@ -736,7 +807,7 @@ static void draw_main(lv_event_t * e)
bool crop = ctrl & LV_TABLE_CELL_CTRL_TEXT_CROP;
if(crop) txt_flags = LV_TEXT_FLAG_EXPAND;
lv_text_get_size(&txt_size, table->cell_data[cell] + 1, label_dsc_def.font,
lv_text_get_size(&txt_size, table->cell_data[cell]->txt, label_dsc_def.font,
label_dsc_act.letter_space, label_dsc_act.line_space,
lv_area_get_width(&txt_area), txt_flags);
@ -751,7 +822,7 @@ static void draw_main(lv_event_t * e)
label_mask_ok = _lv_area_intersect(&label_clip_area, &clip_area, &cell_area);
if(label_mask_ok) {
layer->clip_area = label_clip_area;
label_dsc_act.text = table->cell_data[cell] + 1;
label_dsc_act.text = table->cell_data[cell]->txt;
lv_draw_label(layer, &label_dsc_act, &txt_area);
layer->clip_area = clip_area;
}
@ -841,7 +912,7 @@ static int32_t get_row_height(lv_obj_t * obj, uint32_t row_id, const lv_font_t *
uint32_t cell;
uint32_t col;
for(cell = row_start, col = 0; cell < row_start + table->col_cnt; cell++, col++) {
char * cell_data = table->cell_data[cell];
lv_table_cell_t * cell_data = table->cell_data[cell];
if(is_cell_empty(cell_data)) {
continue;
@ -854,11 +925,11 @@ static int32_t get_row_height(lv_obj_t * obj, uint32_t row_id, const lv_font_t *
* exit the traversal when the current cell control is not LV_TABLE_CELL_CTRL_MERGE_RIGHT */
uint32_t col_merge = 0;
for(col_merge = 0; col_merge + col < table->col_cnt - 1; col_merge++) {
char * next_cell_data = table->cell_data[cell + col_merge];
lv_table_cell_t * next_cell_data = table->cell_data[cell + col_merge];
if(is_cell_empty(next_cell_data)) break;
lv_table_cell_ctrl_t ctrl = (lv_table_cell_ctrl_t) next_cell_data[0];
lv_table_cell_ctrl_t ctrl = (lv_table_cell_ctrl_t) next_cell_data->ctrl;
if(ctrl & LV_TABLE_CELL_CTRL_MERGE_RIGHT) {
txt_w += table->col_w[col + col_merge + 1];
}
@ -867,7 +938,7 @@ static int32_t get_row_height(lv_obj_t * obj, uint32_t row_id, const lv_font_t *
}
}
lv_table_cell_ctrl_t ctrl = (lv_table_cell_ctrl_t) cell_data[0];
lv_table_cell_ctrl_t ctrl = (lv_table_cell_ctrl_t) cell_data->ctrl;
/*When cropping the text we can assume the row height is equal to the line height*/
if(ctrl & LV_TABLE_CELL_CTRL_TEXT_CROP) {
@ -879,7 +950,7 @@ static int32_t get_row_height(lv_obj_t * obj, uint32_t row_id, const lv_font_t *
lv_point_t txt_size;
txt_w -= cell_left + cell_right;
lv_text_get_size(&txt_size, table->cell_data[cell] + 1, font,
lv_text_get_size(&txt_size, table->cell_data[cell]->txt, font,
letter_space, line_space, txt_w, LV_TEXT_FLAG_NONE);
h_max = LV_MAX(txt_size.y + cell_top + cell_bottom, h_max);
@ -949,23 +1020,21 @@ static size_t get_cell_txt_len(const char * txt)
size_t retval = 0;
#if LV_USE_ARABIC_PERSIAN_CHARS
retval = _lv_text_ap_calc_bytes_cnt(txt) + 1;
retval = sizeof(lv_table_cell_t) + _lv_text_ap_calc_bytes_cnt(txt) + 1;
#else
/* cell_data layout: [ctrl][txt][trailing '\0' terminator]
* +2 because of the trailing '\0' and the ctrl */
retval = lv_strlen(txt) + 2;
retval = sizeof(lv_table_cell_t) + strlen(txt) + 1;
#endif
return retval;
}
/* Copy txt into dst skipping the format byte */
static void copy_cell_txt(char * dst, const char * txt)
static void copy_cell_txt(lv_table_cell_t * dst, const char * txt)
{
#if LV_USE_ARABIC_PERSIAN_CHARS
_lv_text_ap_proc(txt, &dst[1]);
_lv_text_ap_proc(txt, dst->txt);
#else
lv_strcpy(&dst[1], txt);
strcpy(dst->txt, txt);
#endif
}

View File

@ -49,12 +49,19 @@ typedef uint32_t lv_table_cell_ctrl_t;
#endif /*DOXYGEN*/
/*Data of cell*/
typedef struct {
lv_table_cell_ctrl_t ctrl;
void * user_data; /**< Custom user data*/
char txt[];
} lv_table_cell_t;
/*Data of table*/
typedef struct {
lv_obj_t obj;
uint32_t col_cnt;
uint32_t row_cnt;
char ** cell_data;
lv_table_cell_t ** cell_data;
int32_t * row_h;
int32_t * col_w;
uint32_t col_act;
@ -140,6 +147,18 @@ void lv_table_add_cell_ctrl(lv_obj_t * obj, uint32_t row, uint32_t col, lv_table
*/
void lv_table_clear_cell_ctrl(lv_obj_t * obj, uint32_t row, uint32_t col, lv_table_cell_ctrl_t ctrl);
/**
* Add custom user data to the cell.
* @param obj pointer to a Table object
* @param row id of the row [0 .. row_cnt -1]
* @param col id of the column [0 .. col_cnt -1]
* @param user_data pointer to the new user_data.
* Should be allocated by `lv_malloc`,
* and it will be freed automatically when the table is deleted or
* when the cell is dropped due to lower row or column count.
*/
void lv_table_set_user_data(lv_obj_t * obj, uint16_t row, uint16_t col, void * user_data);
/*=====================
* Getter functions
*====================*/
@ -193,6 +212,14 @@ bool lv_table_has_cell_ctrl(lv_obj_t * obj, uint32_t row, uint32_t col, lv_table
*/
void lv_table_get_selected_cell(lv_obj_t * obj, uint32_t * row, uint32_t * col);
/**
* Get custom user data to the cell.
* @param obj pointer to a Table object
* @param row id of the row [0 .. row_cnt -1]
* @param col id of the column [0 .. col_cnt -1]
*/
void * lv_table_get_user_data(lv_obj_t * obj, uint16_t row, uint16_t col);
/**********************
* MACROS
**********************/