diff --git a/src/widgets/table/lv_table.c b/src/widgets/table/lv_table.c index 5bf161ddb..5f36fa972 100644 --- a/src/widgets/table/lv_table.c +++ b/src/widgets/table/lv_table.c @@ -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 } diff --git a/src/widgets/table/lv_table.h b/src/widgets/table/lv_table.h index 6a4784cba..7e38ab28e 100644 --- a/src/widgets/table/lv_table.h +++ b/src/widgets/table/lv_table.h @@ -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 **********************/