1 /**
2 * @file lv_table.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9 #include "lv_table.h"
10 #if LV_USE_TABLE != 0
11
12 #include "../lv_misc/lv_txt.h"
13 #include "../lv_misc/lv_math.h"
14 #include "../lv_draw/lv_draw_label.h"
15 #include "../lv_themes/lv_theme.h"
16
17 /*********************
18 * DEFINES
19 *********************/
20
21 /**********************
22 * TYPEDEFS
23 **********************/
24
25 /**********************
26 * STATIC PROTOTYPES
27 **********************/
28 static bool lv_table_design(lv_obj_t * table, const lv_area_t * mask, lv_design_mode_t mode);
29 static lv_res_t lv_table_signal(lv_obj_t * table, lv_signal_t sign, void * param);
30 static lv_coord_t get_row_height(lv_obj_t * table, uint16_t row_id);
31 static void refr_size(lv_obj_t * table);
32
33 /**********************
34 * STATIC VARIABLES
35 **********************/
36 static lv_signal_cb_t ancestor_signal;
37 static lv_design_cb_t ancestor_scrl_design;
38
39 /**********************
40 * MACROS
41 **********************/
42
43 /**********************
44 * GLOBAL FUNCTIONS
45 **********************/
46
47 /**
48 * Create a table object
49 * @param par pointer to an object, it will be the parent of the new table
50 * @param copy pointer to a table object, if not NULL then the new object will be copied from it
51 * @return pointer to the created table
52 */
lv_table_create(lv_obj_t * par,const lv_obj_t * copy)53 lv_obj_t * lv_table_create(lv_obj_t * par, const lv_obj_t * copy)
54 {
55 LV_LOG_TRACE("table create started");
56
57 /*Create the ancestor of table*/
58 lv_obj_t * new_table = lv_obj_create(par, copy);
59 lv_mem_assert(new_table);
60 if(new_table == NULL) return NULL;
61
62 /*Allocate the table type specific extended data*/
63 lv_table_ext_t * ext = lv_obj_allocate_ext_attr(new_table, sizeof(lv_table_ext_t));
64 lv_mem_assert(ext);
65 if(ext == NULL) return NULL;
66 if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_cb(new_table);
67 if(ancestor_scrl_design == NULL) ancestor_scrl_design = lv_obj_get_design_cb(new_table);
68
69 /*Initialize the allocated 'ext' */
70 ext->cell_data = NULL;
71 ext->cell_style[0] = &lv_style_plain;
72 ext->cell_style[1] = &lv_style_plain;
73 ext->cell_style[2] = &lv_style_plain;
74 ext->cell_style[3] = &lv_style_plain;
75 ext->col_cnt = 0;
76 ext->row_cnt = 0;
77
78 uint16_t i;
79 for(i = 0; i < LV_TABLE_COL_MAX; i++) {
80 ext->col_w[i] = LV_DPI;
81 }
82
83 /*The signal and design functions are not copied so set them here*/
84 lv_obj_set_signal_cb(new_table, lv_table_signal);
85 lv_obj_set_design_cb(new_table, lv_table_design);
86
87 /*Init the new table table*/
88 if(copy == NULL) {
89 /*Set the default styles*/
90 lv_theme_t * th = lv_theme_get_current();
91 if(th) {
92 lv_table_set_style(new_table, LV_TABLE_STYLE_BG, th->style.table.bg);
93 lv_table_set_style(new_table, LV_TABLE_STYLE_CELL1, th->style.table.cell);
94 lv_table_set_style(new_table, LV_TABLE_STYLE_CELL2, th->style.table.cell);
95 lv_table_set_style(new_table, LV_TABLE_STYLE_CELL3, th->style.table.cell);
96 lv_table_set_style(new_table, LV_TABLE_STYLE_CELL4, th->style.table.cell);
97 } else {
98 lv_table_set_style(new_table, LV_TABLE_STYLE_BG, &lv_style_plain_color);
99 }
100 lv_obj_set_click(new_table, false); /*Can be removed if click support is added*/
101 }
102 /*Copy an existing table*/
103 else {
104 lv_table_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
105 ext->cell_style[0] = copy_ext->cell_style[0];
106 ext->cell_style[1] = copy_ext->cell_style[1];
107 ext->cell_style[2] = copy_ext->cell_style[2];
108 ext->cell_style[3] = copy_ext->cell_style[3];
109 ext->col_cnt = copy_ext->col_cnt;
110 ext->row_cnt = copy_ext->row_cnt;
111
112 /*Refresh the style with new signal function*/
113 lv_obj_refresh_style(new_table);
114 }
115
116 LV_LOG_INFO("table created");
117
118 return new_table;
119 }
120
121 /*=====================
122 * Setter functions
123 *====================*/
124
125 /**
126 * Set the value of a cell.
127 * @param table pointer to a Table object
128 * @param row id of the row [0 .. row_cnt -1]
129 * @param col id of the column [0 .. col_cnt -1]
130 * @param txt text to display in the cell. It will be copied and saved so this variable is not
131 * required after this function call.
132 */
lv_table_set_cell_value(lv_obj_t * table,uint16_t row,uint16_t col,const char * txt)133 void lv_table_set_cell_value(lv_obj_t * table, uint16_t row, uint16_t col, const char * txt)
134 {
135 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
136 if(row >= ext->row_cnt || col >= ext->col_cnt) {
137 LV_LOG_WARN("lv_table_set_cell_value: invalid row or column");
138 return;
139 }
140 uint32_t cell = row * ext->col_cnt + col;
141 lv_table_cell_format_t format;
142
143 /*Save the format byte*/
144 if(ext->cell_data[cell]) {
145 format.format_byte = ext->cell_data[cell][0];
146 }
147 /*Initialize the format byte*/
148 else {
149 format.s.align = LV_LABEL_ALIGN_LEFT;
150 format.s.right_merge = 0;
151 format.s.type = 0;
152 format.s.crop = 0;
153 }
154
155 ext->cell_data[cell] = lv_mem_realloc(ext->cell_data[cell], strlen(txt) + 2); /*+1: trailing '\0; +1: format byte*/
156 strcpy(ext->cell_data[cell] + 1, txt); /*Leave the format byte*/
157 ext->cell_data[cell][0] = format.format_byte;
158 refr_size(table);
159 }
160
161 /**
162 * Set the number of rows
163 * @param table table pointer to a Table object
164 * @param row_cnt number of rows
165 */
lv_table_set_row_cnt(lv_obj_t * table,uint16_t row_cnt)166 void lv_table_set_row_cnt(lv_obj_t * table, uint16_t row_cnt)
167 {
168 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
169 uint16_t old_row_cnt = ext->row_cnt;
170 ext->row_cnt = row_cnt;
171
172 if(ext->row_cnt > 0 && ext->col_cnt > 0) {
173 ext->cell_data = lv_mem_realloc(ext->cell_data, ext->row_cnt * ext->col_cnt * sizeof(char *));
174
175 /*Initilize the new fields*/
176 if(old_row_cnt < row_cnt) {
177 uint16_t old_cell_cnt = old_row_cnt * ext->col_cnt;
178 uint32_t new_cell_cnt = ext->col_cnt * ext->row_cnt;
179 memset(&ext->cell_data[old_cell_cnt], 0, (new_cell_cnt - old_cell_cnt) * sizeof(ext->cell_data[0]));
180 }
181 } else {
182 lv_mem_free(ext->cell_data);
183 ext->cell_data = NULL;
184 }
185
186 refr_size(table);
187 }
188
189 /**
190 * Set the number of columns
191 * @param table table pointer to a Table object
192 * @param col_cnt number of columns. Must be < LV_TABLE_COL_MAX
193 */
lv_table_set_col_cnt(lv_obj_t * table,uint16_t col_cnt)194 void lv_table_set_col_cnt(lv_obj_t * table, uint16_t col_cnt)
195 {
196
197 if(col_cnt >= LV_TABLE_COL_MAX) {
198 LV_LOG_WARN("lv_table_set_col_cnt: too many columns. Must be < LV_TABLE_COL_MAX.");
199 return;
200 }
201
202 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
203 uint16_t old_col_cnt = ext->col_cnt;
204 ext->col_cnt = col_cnt;
205
206 if(ext->row_cnt > 0 && ext->col_cnt > 0) {
207 ext->cell_data = lv_mem_realloc(ext->cell_data, ext->row_cnt * ext->col_cnt * sizeof(char *));
208 /*Initilize the new fields*/
209 if(old_col_cnt < col_cnt) {
210 uint16_t old_cell_cnt = old_col_cnt * ext->row_cnt;
211 uint32_t new_cell_cnt = ext->col_cnt * ext->row_cnt;
212 memset(&ext->cell_data[old_cell_cnt], 0, (new_cell_cnt - old_cell_cnt) * sizeof(ext->cell_data[0]));
213 }
214
215 } else {
216 lv_mem_free(ext->cell_data);
217 ext->cell_data = NULL;
218 }
219 refr_size(table);
220 }
221
222 /**
223 * Set the width of a column
224 * @param table table pointer to a Table object
225 * @param col_id id of the column [0 .. LV_TABLE_COL_MAX -1]
226 * @param w width of the column
227 */
lv_table_set_col_width(lv_obj_t * table,uint16_t col_id,lv_coord_t w)228 void lv_table_set_col_width(lv_obj_t * table, uint16_t col_id, lv_coord_t w)
229 {
230 if(col_id >= LV_TABLE_COL_MAX) {
231 LV_LOG_WARN("lv_table_set_col_width: too big 'col_id'. Must be < LV_TABLE_COL_MAX.");
232 return;
233 }
234
235 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
236 ext->col_w[col_id] = w;
237 refr_size(table);
238 }
239
240 /**
241 * Set the text align in a cell
242 * @param table pointer to a Table object
243 * @param row id of the row [0 .. row_cnt -1]
244 * @param col id of the column [0 .. col_cnt -1]
245 * @param align LV_LABEL_ALIGN_LEFT or LV_LABEL_ALIGN_CENTER or LV_LABEL_ALIGN_RIGHT
246 */
lv_table_set_cell_align(lv_obj_t * table,uint16_t row,uint16_t col,lv_label_align_t align)247 void lv_table_set_cell_align(lv_obj_t * table, uint16_t row, uint16_t col, lv_label_align_t align)
248 {
249 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
250 if(row >= ext->row_cnt || col >= ext->col_cnt) {
251 LV_LOG_WARN("lv_table_set_cell_align: invalid row or column");
252 return;
253 }
254 uint32_t cell = row * ext->col_cnt + col;
255
256 if(ext->cell_data[cell] == NULL) {
257 ext->cell_data[cell] = lv_mem_alloc(2); /*+1: trailing '\0; +1: format byte*/
258 ext->cell_data[cell][0] = 0;
259 ext->cell_data[cell][1] = '\0';
260 }
261
262 lv_table_cell_format_t format;
263 format.format_byte = ext->cell_data[cell][0];
264 format.s.align = align;
265 ext->cell_data[cell][0] = format.format_byte;
266 }
267
268 /**
269 * Set the type of a cell.
270 * @param table pointer to a Table object
271 * @param row id of the row [0 .. row_cnt -1]
272 * @param col id of the column [0 .. col_cnt -1]
273 * @param type 1,2,3 or 4. The cell style will be chosen accordingly.
274 */
lv_table_set_cell_type(lv_obj_t * table,uint16_t row,uint16_t col,uint8_t type)275 void lv_table_set_cell_type(lv_obj_t * table, uint16_t row, uint16_t col, uint8_t type)
276 {
277 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
278 if(row >= ext->row_cnt || col >= ext->col_cnt) {
279 LV_LOG_WARN("lv_table_set_cell_type: invalid row or column");
280 return;
281 }
282 uint32_t cell = row * ext->col_cnt + col;
283
284 if(ext->cell_data[cell] == NULL) {
285 ext->cell_data[cell] = lv_mem_alloc(2); /*+1: trailing '\0; +1: format byte*/
286 ext->cell_data[cell][0] = 0;
287 ext->cell_data[cell][1] = '\0';
288 }
289
290 if(type > 0) type--; /*User gives 1,2,3,4 but easier to handle 0, 1, 2, 3*/
291 if(type >= LV_TABLE_CELL_STYLE_CNT) type = LV_TABLE_CELL_STYLE_CNT - 1;
292
293 lv_table_cell_format_t format;
294 format.format_byte = ext->cell_data[cell][0];
295 format.s.type = type;
296 ext->cell_data[cell][0] = format.format_byte;
297 }
298
299 /**
300 * Set the cell crop. (Don't adjust the height of the cell according to its content)
301 * @param table pointer to a Table object
302 * @param row id of the row [0 .. row_cnt -1]
303 * @param col id of the column [0 .. col_cnt -1]
304 * @param crop true: crop the cell content; false: set the cell height to the content.
305 */
lv_table_set_cell_crop(lv_obj_t * table,uint16_t row,uint16_t col,bool crop)306 void lv_table_set_cell_crop(lv_obj_t * table, uint16_t row, uint16_t col, bool crop)
307 {
308 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
309 if(row >= ext->row_cnt || col >= ext->col_cnt) {
310 LV_LOG_WARN("lv_table_set_cell_crop: invalid row or column");
311 return;
312 }
313 uint32_t cell = row * ext->col_cnt + col;
314
315 if(ext->cell_data[cell] == NULL) {
316 ext->cell_data[cell] = lv_mem_alloc(2); /*+1: trailing '\0; +1: format byte*/
317 ext->cell_data[cell][0] = 0;
318 ext->cell_data[cell][1] = '\0';
319 }
320
321 lv_table_cell_format_t format;
322 format.format_byte = ext->cell_data[cell][0];
323 format.s.crop = crop;
324 ext->cell_data[cell][0] = format.format_byte;
325 }
326
327 /**
328 * Merge a cell with the right neighbor. The value of the cell to the right won't be displayed.
329 * @param table table pointer to a Table object
330 * @param row id of the row [0 .. row_cnt -1]
331 * @param col id of the column [0 .. col_cnt -1]
332 * @param en true: merge right; false: don't merge right
333 */
lv_table_set_cell_merge_right(lv_obj_t * table,uint16_t row,uint16_t col,bool en)334 void lv_table_set_cell_merge_right(lv_obj_t * table, uint16_t row, uint16_t col, bool en)
335 {
336 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
337 if(row >= ext->row_cnt || col >= ext->col_cnt) {
338 LV_LOG_WARN("lv_table_set_cell_merge_right: invalid row or column");
339 return;
340 }
341
342 uint32_t cell = row * ext->col_cnt + col;
343
344 if(ext->cell_data[cell] == NULL) {
345 ext->cell_data[cell] = lv_mem_alloc(2); /*+1: trailing '\0; +1: format byte*/
346 ext->cell_data[cell][0] = 0;
347 ext->cell_data[cell][1] = '\0';
348 }
349
350 lv_table_cell_format_t format;
351 format.format_byte = ext->cell_data[cell][0];
352 format.s.right_merge = en ? 1 : 0;
353 ext->cell_data[cell][0] = format.format_byte;
354 refr_size(table);
355 }
356
357 /**
358 * Set a style of a table.
359 * @param table pointer to table object
360 * @param type which style should be set
361 * @param style pointer to a style
362 */
lv_table_set_style(lv_obj_t * table,lv_table_style_t type,const lv_style_t * style)363 void lv_table_set_style(lv_obj_t * table, lv_table_style_t type, const lv_style_t * style)
364 {
365 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
366
367 switch(type) {
368 case LV_TABLE_STYLE_BG:
369 lv_obj_set_style(table, style);
370 refr_size(table);
371 break;
372 case LV_TABLE_STYLE_CELL1:
373 ext->cell_style[0] = style;
374 refr_size(table);
375 break;
376 case LV_TABLE_STYLE_CELL2:
377 ext->cell_style[1] = style;
378 refr_size(table);
379 break;
380 case LV_TABLE_STYLE_CELL3:
381 ext->cell_style[2] = style;
382 refr_size(table);
383 break;
384 case LV_TABLE_STYLE_CELL4:
385 ext->cell_style[3] = style;
386 refr_size(table);
387 break;
388 }
389 }
390
391 /*=====================
392 * Getter functions
393 *====================*/
394
395 /**
396 * Get the value of a cell.
397 * @param table pointer to a Table object
398 * @param row id of the row [0 .. row_cnt -1]
399 * @param col id of the column [0 .. col_cnt -1]
400 * @return text in the cell
401 */
lv_table_get_cell_value(lv_obj_t * table,uint16_t row,uint16_t col)402 const char * lv_table_get_cell_value(lv_obj_t * table, uint16_t row, uint16_t col)
403 {
404 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
405 if(row >= ext->row_cnt || col >= ext->col_cnt) {
406 LV_LOG_WARN("lv_table_set_cell_value: invalid row or column");
407 return "";
408 }
409 uint32_t cell = row * ext->col_cnt + col;
410
411 if(ext->cell_data[cell] == NULL) return "";
412
413 return &ext->cell_data[cell][1]; /*Skip the format byte*/
414 }
415
416 /**
417 * Get the number of rows.
418 * @param table table pointer to a Table object
419 * @return number of rows.
420 */
lv_table_get_row_cnt(lv_obj_t * table)421 uint16_t lv_table_get_row_cnt(lv_obj_t * table)
422 {
423 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
424 return ext->row_cnt;
425 }
426
427 /**
428 * Get the number of columns.
429 * @param table table pointer to a Table object
430 * @return number of columns.
431 */
lv_table_get_col_cnt(lv_obj_t * table)432 uint16_t lv_table_get_col_cnt(lv_obj_t * table)
433 {
434 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
435 return ext->col_cnt;
436 }
437
438 /**
439 * Get the width of a column
440 * @param table table pointer to a Table object
441 * @param col_id id of the column [0 .. LV_TABLE_COL_MAX -1]
442 * @return width of the column
443 */
lv_table_get_col_width(lv_obj_t * table,uint16_t col_id)444 lv_coord_t lv_table_get_col_width(lv_obj_t * table, uint16_t col_id)
445 {
446 if(col_id >= LV_TABLE_COL_MAX) {
447 LV_LOG_WARN("lv_table_set_col_width: too big 'col_id'. Must be < LV_TABLE_COL_MAX.");
448 return 0;
449 }
450
451 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
452 return ext->col_w[col_id];
453 }
454
455 /**
456 * Get the text align of a cell
457 * @param table pointer to a Table object
458 * @param row id of the row [0 .. row_cnt -1]
459 * @param col id of the column [0 .. col_cnt -1]
460 * @return LV_LABEL_ALIGN_LEFT (default in case of error) or LV_LABEL_ALIGN_CENTER or
461 * LV_LABEL_ALIGN_RIGHT
462 */
lv_table_get_cell_align(lv_obj_t * table,uint16_t row,uint16_t col)463 lv_label_align_t lv_table_get_cell_align(lv_obj_t * table, uint16_t row, uint16_t col)
464 {
465 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
466 if(row >= ext->row_cnt || col >= ext->col_cnt) {
467 LV_LOG_WARN("lv_table_set_cell_align: invalid row or column");
468 return LV_LABEL_ALIGN_LEFT; /*Just return with something*/
469 }
470 uint32_t cell = row * ext->col_cnt + col;
471
472 if(ext->cell_data[cell] == NULL)
473 return LV_LABEL_ALIGN_LEFT; /*Just return with something*/
474 else {
475 lv_table_cell_format_t format;
476 format.format_byte = ext->cell_data[cell][0];
477 return format.s.align;
478 }
479 }
480
481 /**
482 * Get the type of a cell
483 * @param table pointer to a Table object
484 * @param row id of the row [0 .. row_cnt -1]
485 * @param col id of the column [0 .. col_cnt -1]
486 * @return 1,2,3 or 4
487 */
lv_table_get_cell_type(lv_obj_t * table,uint16_t row,uint16_t col)488 lv_label_align_t lv_table_get_cell_type(lv_obj_t * table, uint16_t row, uint16_t col)
489 {
490 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
491 if(row >= ext->row_cnt || col >= ext->col_cnt) {
492 LV_LOG_WARN("lv_table_get_cell_type: invalid row or column");
493 return 1; /*Just return with something*/
494 }
495 uint32_t cell = row * ext->col_cnt + col;
496
497 if(ext->cell_data[cell] == NULL)
498 return 1; /*Just return with something*/
499 else {
500 lv_table_cell_format_t format;
501 format.format_byte = ext->cell_data[cell][0];
502 return format.s.type + 1; /*0,1,2,3 is stored but user sees 1,2,3,4*/
503 }
504 }
505
506 /**
507 * Get the crop property of a cell
508 * @param table pointer to a Table object
509 * @param row id of the row [0 .. row_cnt -1]
510 * @param col id of the column [0 .. col_cnt -1]
511 * @return true: text crop enabled; false: disabled
512 */
lv_table_get_cell_crop(lv_obj_t * table,uint16_t row,uint16_t col)513 lv_label_align_t lv_table_get_cell_crop(lv_obj_t * table, uint16_t row, uint16_t col)
514 {
515 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
516 if(row >= ext->row_cnt || col >= ext->col_cnt) {
517 LV_LOG_WARN("lv_table_get_cell_crop: invalid row or column");
518 return false; /*Just return with something*/
519 }
520 uint32_t cell = row * ext->col_cnt + col;
521
522 if(ext->cell_data[cell] == NULL)
523 return false; /*Just return with something*/
524 else {
525 lv_table_cell_format_t format;
526 format.format_byte = ext->cell_data[cell][0];
527 return format.s.crop;
528 }
529 }
530
531 /**
532 * Get the cell merge attribute.
533 * @param table table pointer to a Table object
534 * @param row id of the row [0 .. row_cnt -1]
535 * @param col id of the column [0 .. col_cnt -1]
536 * @return true: merge right; false: don't merge right
537 */
lv_table_get_cell_merge_right(lv_obj_t * table,uint16_t row,uint16_t col)538 bool lv_table_get_cell_merge_right(lv_obj_t * table, uint16_t row, uint16_t col)
539 {
540 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
541 if(row >= ext->row_cnt || col >= ext->col_cnt) {
542 LV_LOG_WARN("lv_table_get_cell_merge_right: invalid row or column");
543 return false;
544 }
545
546 uint32_t cell = row * ext->col_cnt + col;
547
548 if(ext->cell_data[cell] == NULL)
549 return false;
550 else {
551 lv_table_cell_format_t format;
552 format.format_byte = ext->cell_data[cell][0];
553 return format.s.right_merge ? true : false;
554 }
555 }
556
557 /**
558 * Get style of a table.
559 * @param table pointer to table object
560 * @param type which style should be get
561 * @return style pointer to the style
562 */
lv_table_get_style(const lv_obj_t * table,lv_table_style_t type)563 const lv_style_t * lv_table_get_style(const lv_obj_t * table, lv_table_style_t type)
564 {
565 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
566 const lv_style_t * style = NULL;
567
568 switch(type) {
569 case LV_TABLE_STYLE_BG: style = lv_obj_get_style(table); break;
570 case LV_TABLE_STYLE_CELL1: style = ext->cell_style[0]; break;
571 case LV_TABLE_STYLE_CELL2: style = ext->cell_style[1]; break;
572 case LV_TABLE_STYLE_CELL3: style = ext->cell_style[2]; break;
573 case LV_TABLE_STYLE_CELL4: style = ext->cell_style[3]; break;
574 default: return NULL;
575 }
576
577 return style;
578 }
579
580 /**********************
581 * STATIC FUNCTIONS
582 **********************/
583
584 /**
585 * Handle the drawing related tasks of the tables
586 * @param table pointer to an object
587 * @param mask the object will be drawn only in this area
588 * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
589 * (return 'true' if yes)
590 * LV_DESIGN_DRAW: draw the object (always return 'true')
591 * LV_DESIGN_DRAW_POST: drawing after every children are drawn
592 * @param return true/false, depends on 'mode'
593 */
lv_table_design(lv_obj_t * table,const lv_area_t * mask,lv_design_mode_t mode)594 static bool lv_table_design(lv_obj_t * table, const lv_area_t * mask, lv_design_mode_t mode)
595 {
596 /*Return false if the object is not covers the mask_p area*/
597 if(mode == LV_DESIGN_COVER_CHK) {
598 return false;
599 }
600 /*Draw the object*/
601 else if(mode == LV_DESIGN_DRAW_MAIN) {
602 ancestor_scrl_design(table, mask, mode);
603
604 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
605 const lv_style_t * bg_style = lv_obj_get_style(table);
606 const lv_style_t * cell_style;
607 lv_coord_t h_row;
608 lv_point_t txt_size;
609 lv_area_t cell_area;
610 lv_area_t txt_area;
611 lv_txt_flag_t txt_flags;
612 lv_opa_t opa_scale = lv_obj_get_opa_scale(table);
613
614 uint16_t col;
615 uint16_t row;
616 uint16_t cell = 0;
617
618 cell_area.y2 = table->coords.y1 + bg_style->body.padding.top;
619 for(row = 0; row < ext->row_cnt; row++) {
620 h_row = get_row_height(table, row);
621
622 cell_area.y1 = cell_area.y2;
623 cell_area.y2 = cell_area.y1 + h_row;
624
625 cell_area.x2 = table->coords.x1 + bg_style->body.padding.left;
626
627 for(col = 0; col < ext->col_cnt; col++) {
628
629 lv_table_cell_format_t format;
630 if(ext->cell_data[cell]) {
631 format.format_byte = ext->cell_data[cell][0];
632 } else {
633 format.s.right_merge = 0;
634 format.s.align = LV_LABEL_ALIGN_LEFT;
635 format.s.type = 0;
636 format.s.crop = 1;
637 }
638
639 cell_style = ext->cell_style[format.s.type];
640 cell_area.x1 = cell_area.x2;
641 cell_area.x2 = cell_area.x1 + ext->col_w[col];
642
643 uint16_t col_merge = 0;
644 for(col_merge = 0; col_merge + col < ext->col_cnt - 1; col_merge++) {
645
646 if(ext->cell_data[cell + col_merge] != NULL) {
647 format.format_byte = ext->cell_data[cell + col_merge][0];
648 if(format.s.right_merge)
649 cell_area.x2 += ext->col_w[col + col_merge + 1];
650 else
651 break;
652 } else {
653 break;
654 }
655 }
656
657 lv_draw_rect(&cell_area, mask, cell_style, opa_scale);
658
659 if(ext->cell_data[cell]) {
660
661 txt_area.x1 = cell_area.x1 + cell_style->body.padding.left;
662 txt_area.x2 = cell_area.x2 - cell_style->body.padding.right;
663 txt_area.y1 = cell_area.y1 + cell_style->body.padding.top;
664 txt_area.y2 = cell_area.y2 - cell_style->body.padding.bottom;
665 /*Align the content to the middle if not cropped*/
666 if(format.s.crop == 0) {
667 txt_flags = LV_TXT_FLAG_NONE;
668 } else {
669 txt_flags = LV_TXT_FLAG_EXPAND;
670 }
671
672 lv_txt_get_size(&txt_size, ext->cell_data[cell] + 1, cell_style->text.font,
673 cell_style->text.letter_space, cell_style->text.line_space,
674 lv_area_get_width(&txt_area), txt_flags);
675
676 /*Align the content to the middle if not cropped*/
677 if(format.s.crop == 0) {
678 txt_area.y1 = cell_area.y1 + h_row / 2 - txt_size.y / 2;
679 txt_area.y2 = cell_area.y1 + h_row / 2 + txt_size.y / 2;
680 }
681
682 switch(format.s.align) {
683 default:
684 case LV_LABEL_ALIGN_LEFT: txt_flags |= LV_TXT_FLAG_NONE; break;
685 case LV_LABEL_ALIGN_RIGHT: txt_flags |= LV_TXT_FLAG_RIGHT; break;
686 case LV_LABEL_ALIGN_CENTER: txt_flags |= LV_TXT_FLAG_CENTER; break;
687 }
688
689 lv_area_t label_mask;
690 bool label_mask_ok;
691 label_mask_ok = lv_area_intersect(&label_mask, mask, &cell_area);
692 if(label_mask_ok) {
693 lv_draw_label(&txt_area, &label_mask, cell_style, opa_scale, ext->cell_data[cell] + 1,
694 txt_flags, NULL, -1, -1, NULL);
695 }
696 /*Draw lines after '\n's*/
697 lv_point_t p1;
698 lv_point_t p2;
699 p1.x = cell_area.x1;
700 p2.x = cell_area.x2;
701 uint16_t i;
702 for(i = 1; ext->cell_data[cell][i] != '\0'; i++) {
703 if(ext->cell_data[cell][i] == '\n') {
704 ext->cell_data[cell][i] = '\0';
705 lv_txt_get_size(&txt_size, ext->cell_data[cell] + 1, cell_style->text.font,
706 cell_style->text.letter_space, cell_style->text.line_space,
707 lv_area_get_width(&txt_area), txt_flags);
708
709 p1.y = txt_area.y1 + txt_size.y + cell_style->text.line_space / 2;
710 p2.y = txt_area.y1 + txt_size.y + cell_style->text.line_space / 2;
711 lv_draw_line(&p1, &p2, mask, cell_style, opa_scale);
712
713 ext->cell_data[cell][i] = '\n';
714 }
715 }
716 }
717
718 cell += col_merge + 1;
719 col += col_merge;
720 }
721 }
722 }
723 /*Post draw when the children are drawn*/
724 else if(mode == LV_DESIGN_DRAW_POST) {
725 }
726
727 return true;
728 }
729
730 /**
731 * Signal function of the table
732 * @param table pointer to a table object
733 * @param sign a signal type from lv_signal_t enum
734 * @param param pointer to a signal specific variable
735 * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
736 */
lv_table_signal(lv_obj_t * table,lv_signal_t sign,void * param)737 static lv_res_t lv_table_signal(lv_obj_t * table, lv_signal_t sign, void * param)
738 {
739 lv_res_t res;
740
741 /* Include the ancient signal function */
742 res = ancestor_signal(table, sign, param);
743 if(res != LV_RES_OK) return res;
744
745 if(sign == LV_SIGNAL_CLEANUP) {
746 /*Free the cell texts*/
747 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
748 uint16_t cell;
749 for(cell = 0; cell < ext->col_cnt * ext->row_cnt; cell++) {
750 if(ext->cell_data[cell]) {
751 lv_mem_free(ext->cell_data[cell]);
752 ext->cell_data[cell] = NULL;
753 }
754 }
755 } else if(sign == LV_SIGNAL_GET_TYPE) {
756 lv_obj_type_t * buf = param;
757 uint8_t i;
758 for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
759 if(buf->type[i] == NULL) break;
760 }
761 buf->type[i] = "lv_table";
762 }
763
764 return res;
765 }
766
refr_size(lv_obj_t * table)767 static void refr_size(lv_obj_t * table)
768 {
769 lv_coord_t h = 0;
770 lv_coord_t w = 0;
771
772 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
773
774 uint16_t i;
775 for(i = 0; i < ext->col_cnt; i++) {
776 w += ext->col_w[i];
777 }
778 for(i = 0; i < ext->row_cnt; i++) {
779 h += get_row_height(table, i);
780 }
781
782 const lv_style_t * bg_style = lv_obj_get_style(table);
783
784 w += bg_style->body.padding.left + bg_style->body.padding.right;
785 h += bg_style->body.padding.top + bg_style->body.padding.bottom;
786
787 lv_obj_set_size(table, w + 1, h + 1);
788 lv_obj_invalidate(table);
789 }
790
get_row_height(lv_obj_t * table,uint16_t row_id)791 static lv_coord_t get_row_height(lv_obj_t * table, uint16_t row_id)
792 {
793 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
794 lv_point_t txt_size;
795 lv_coord_t txt_w;
796 const lv_style_t * cell_style;
797
798 uint16_t row_start = row_id * ext->col_cnt;
799 uint16_t cell;
800 uint16_t col;
801 lv_coord_t h_max = lv_font_get_line_height(ext->cell_style[0]->text.font) + ext->cell_style[0]->body.padding.top +
802 ext->cell_style[0]->body.padding.bottom;
803
804 for(cell = row_start, col = 0; cell < row_start + ext->col_cnt; cell++, col++) {
805 if(ext->cell_data[cell] != NULL) {
806
807 txt_w = ext->col_w[col];
808 uint16_t col_merge = 0;
809 for(col_merge = 0; col_merge + col < ext->col_cnt - 1; col_merge++) {
810
811 if(ext->cell_data[cell + col_merge] != NULL) {
812 lv_table_cell_format_t format;
813 format.format_byte = ext->cell_data[cell + col_merge][0];
814 if(format.s.right_merge)
815 txt_w += ext->col_w[col + col_merge + 1];
816 else
817 break;
818 } else {
819 break;
820 }
821 }
822
823 lv_table_cell_format_t format;
824 format.format_byte = ext->cell_data[cell][0];
825 cell_style = ext->cell_style[format.s.type];
826
827 /*With text crop assume 1 line*/
828 if(format.s.crop) {
829 h_max = LV_MATH_MAX(lv_font_get_line_height(cell_style->text.font) + cell_style->body.padding.top +
830 cell_style->body.padding.bottom,
831 h_max);
832 }
833 /*Without text crop calculate the height of the text in the cell*/
834 else {
835 txt_w -= cell_style->body.padding.left + cell_style->body.padding.right;
836
837 lv_txt_get_size(&txt_size, ext->cell_data[cell] + 1, cell_style->text.font,
838 cell_style->text.letter_space, cell_style->text.line_space, txt_w, LV_TXT_FLAG_NONE);
839
840 h_max = LV_MATH_MAX(txt_size.y + cell_style->body.padding.top + cell_style->body.padding.bottom, h_max);
841 cell += col_merge;
842 col += col_merge;
843 }
844 }
845 }
846
847 return h_max;
848 }
849
850 #endif
851