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_debug.h"
13 #include "../lv_core/lv_indev.h"
14 #include "../lv_misc/lv_txt.h"
15 #include "../lv_misc/lv_math.h"
16 #include "../lv_draw/lv_draw_label.h"
17 #include "../lv_themes/lv_theme.h"
18
19 /*********************
20 * DEFINES
21 *********************/
22 #define LV_OBJX_NAME "lv_table"
23
24 /**********************
25 * TYPEDEFS
26 **********************/
27
28 /**********************
29 * STATIC PROTOTYPES
30 **********************/
31 static lv_design_res_t lv_table_design(lv_obj_t * table, const lv_area_t * clip_area, lv_design_mode_t mode);
32 static lv_res_t lv_table_signal(lv_obj_t * table, lv_signal_t sign, void * param);
33 static lv_style_list_t * lv_table_get_style(lv_obj_t * table, uint8_t part);
34 static lv_coord_t get_row_height(lv_obj_t * table, uint16_t row_id, const lv_font_t ** font,
35 lv_style_int_t * letter_space, lv_style_int_t * line_space,
36 lv_style_int_t * cell_left, lv_style_int_t * cell_right, lv_style_int_t * cell_top, lv_style_int_t * cell_bottom);
37 static void refr_size(lv_obj_t * table);
38
39 /**********************
40 * STATIC VARIABLES
41 **********************/
42 static lv_signal_cb_t ancestor_signal;
43 static lv_design_cb_t ancestor_design;
44
45 /**********************
46 * MACROS
47 **********************/
48
49 /**********************
50 * GLOBAL FUNCTIONS
51 **********************/
52
53 /**
54 * Create a table object
55 * @param par pointer to an object, it will be the parent of the new table
56 * @param copy pointer to a table object, if not NULL then the new object will be copied from it
57 * @return pointer to the created table
58 */
lv_table_create(lv_obj_t * par,const lv_obj_t * copy)59 lv_obj_t * lv_table_create(lv_obj_t * par, const lv_obj_t * copy)
60 {
61 LV_LOG_TRACE("table create started");
62
63 /*Create the ancestor of table*/
64 lv_obj_t * table = lv_obj_create(par, copy);
65 LV_ASSERT_MEM(table);
66 if(table == NULL) return NULL;
67
68 /*Allocate the table type specific extended data*/
69 lv_table_ext_t * ext = lv_obj_allocate_ext_attr(table, sizeof(lv_table_ext_t));
70 LV_ASSERT_MEM(ext);
71 if(ext == NULL) {
72 lv_obj_del(table);
73 return NULL;
74 }
75
76 if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_cb(table);
77 if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_cb(table);
78
79 /*Initialize the allocated 'ext' */
80 ext->cell_data = NULL;
81 ext->col_cnt = 0;
82 ext->row_cnt = 0;
83 ext->row_h = NULL;
84 ext->cell_types = 1;
85
86 uint16_t i;
87 for(i = 0; i < LV_TABLE_CELL_STYLE_CNT; i++) {
88 lv_style_list_init(&ext->cell_style[i]);
89 }
90
91 for(i = 0; i < LV_TABLE_COL_MAX; i++) {
92 ext->col_w[i] = LV_DPI;
93 }
94
95 /*The signal and design functions are not copied so set them here*/
96 lv_obj_set_signal_cb(table, lv_table_signal);
97 lv_obj_set_design_cb(table, lv_table_design);
98
99 /*Init the new table table*/
100 if(copy == NULL) {
101 lv_theme_apply(table, LV_THEME_TABLE);
102 }
103 /*Copy an existing table*/
104 else {
105 lv_table_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
106 for(i = 0; i < LV_TABLE_CELL_STYLE_CNT; i++) {
107 lv_style_list_copy(&ext->cell_style[i], ©_ext->cell_style[i]);
108 }
109 lv_table_set_row_cnt(table, copy_ext->row_cnt);
110 lv_table_set_col_cnt(table, copy_ext->col_cnt);
111
112 /*Refresh the style with new signal function*/
113 lv_obj_refresh_style(table, LV_OBJ_PART_ALL, LV_STYLE_PROP_ALL);
114 }
115
116 LV_LOG_INFO("table created");
117
118 return 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_ASSERT_OBJ(table, LV_OBJX_NAME);
136 LV_ASSERT_NULL(txt);
137
138 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
139 if(col >= ext->col_cnt) {
140 LV_LOG_WARN("lv_table_set_cell_value: invalid column");
141 return;
142 }
143
144 /*Auto expand*/
145 if(row >= ext->row_cnt) {
146 lv_table_set_row_cnt(table, row + 1);
147 }
148
149 uint32_t cell = row * ext->col_cnt + col;
150 lv_table_cell_format_t format;
151
152 /*Save the format byte*/
153 if(ext->cell_data[cell]) {
154 format.format_byte = ext->cell_data[cell][0];
155 }
156 /*Initialize the format byte*/
157 else {
158 lv_bidi_dir_t base_dir = lv_obj_get_base_dir(table);
159 if(base_dir == LV_BIDI_DIR_LTR) format.s.align = LV_LABEL_ALIGN_LEFT;
160 else if(base_dir == LV_BIDI_DIR_RTL) format.s.align = LV_LABEL_ALIGN_RIGHT;
161 else if(base_dir == LV_BIDI_DIR_AUTO)
162 #if LV_USE_BIDI
163 format.s.align = _lv_bidi_detect_base_dir(txt);
164 #else
165 format.s.align = LV_LABEL_ALIGN_LEFT;
166 #endif
167 format.s.right_merge = 0;
168 format.s.type = 0;
169 format.s.crop = 0;
170 }
171
172 ext->cell_data[cell] = lv_mem_realloc(ext->cell_data[cell], strlen(txt) + 2); /*+1: trailing '\0; +1: format byte*/
173 LV_ASSERT_MEM(ext->cell_data[cell]);
174 if(ext->cell_data[cell] == NULL) return;
175
176 strcpy(ext->cell_data[cell] + 1, txt); /*+1 to skip the format byte*/
177
178 ext->cell_data[cell][0] = format.format_byte;
179 refr_size(table);
180 }
181
182 /**
183 * Set the number of rows
184 * @param table table pointer to a Table object
185 * @param row_cnt number of rows
186 */
lv_table_set_row_cnt(lv_obj_t * table,uint16_t row_cnt)187 void lv_table_set_row_cnt(lv_obj_t * table, uint16_t row_cnt)
188 {
189 LV_ASSERT_OBJ(table, LV_OBJX_NAME);
190
191 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
192 uint16_t old_row_cnt = ext->row_cnt;
193 ext->row_cnt = row_cnt;
194
195 if(ext->row_cnt > 0) {
196 ext->row_h = lv_mem_realloc(ext->row_h, ext->row_cnt * sizeof(ext->row_h[0]));
197 LV_ASSERT_MEM(ext->row_h);
198 if(ext->row_h == NULL) return;
199 }
200 else {
201 lv_mem_free(ext->row_h);
202 ext->row_h = NULL;
203 }
204
205 if(ext->row_cnt > 0 && ext->col_cnt > 0) {
206 ext->cell_data = lv_mem_realloc(ext->cell_data, ext->row_cnt * ext->col_cnt * sizeof(char *));
207 LV_ASSERT_MEM(ext->cell_data);
208 if(ext->cell_data == NULL) return;
209
210 /*Initialize the new fields*/
211 if(old_row_cnt < row_cnt) {
212 uint16_t old_cell_cnt = old_row_cnt * ext->col_cnt;
213 uint32_t new_cell_cnt = ext->col_cnt * ext->row_cnt;
214 _lv_memset_00(&ext->cell_data[old_cell_cnt], (new_cell_cnt - old_cell_cnt) * sizeof(ext->cell_data[0]));
215 }
216 }
217 else {
218 lv_mem_free(ext->cell_data);
219 ext->cell_data = NULL;
220 }
221
222 refr_size(table);
223 }
224
225 /**
226 * Set the number of columns
227 * @param table table pointer to a Table object
228 * @param col_cnt number of columns. Must be < LV_TABLE_COL_MAX
229 */
lv_table_set_col_cnt(lv_obj_t * table,uint16_t col_cnt)230 void lv_table_set_col_cnt(lv_obj_t * table, uint16_t col_cnt)
231 {
232 LV_ASSERT_OBJ(table, LV_OBJX_NAME);
233
234 if(col_cnt >= LV_TABLE_COL_MAX) {
235 LV_LOG_WARN("lv_table_set_col_cnt: too many columns. Must be < LV_TABLE_COL_MAX.");
236 return;
237 }
238
239 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
240 uint16_t old_col_cnt = ext->col_cnt;
241 ext->col_cnt = col_cnt;
242
243 if(ext->row_cnt > 0 && ext->col_cnt > 0) {
244 ext->cell_data = lv_mem_realloc(ext->cell_data, ext->row_cnt * ext->col_cnt * sizeof(char *));
245 LV_ASSERT_MEM(ext->cell_data);
246 if(ext->cell_data == NULL) return;
247
248 /*Initialize the new fields*/
249 if(old_col_cnt < col_cnt) {
250 uint16_t old_cell_cnt = old_col_cnt * ext->row_cnt;
251 uint32_t new_cell_cnt = ext->col_cnt * ext->row_cnt;
252 _lv_memset_00(&ext->cell_data[old_cell_cnt], (new_cell_cnt - old_cell_cnt) * sizeof(ext->cell_data[0]));
253 }
254
255 }
256 else {
257 lv_mem_free(ext->cell_data);
258 ext->cell_data = NULL;
259 }
260 refr_size(table);
261 }
262
263 /**
264 * Set the width of a column
265 * @param table table pointer to a Table object
266 * @param col_id id of the column [0 .. LV_TABLE_COL_MAX -1]
267 * @param w width of the column
268 */
lv_table_set_col_width(lv_obj_t * table,uint16_t col_id,lv_coord_t w)269 void lv_table_set_col_width(lv_obj_t * table, uint16_t col_id, lv_coord_t w)
270 {
271 LV_ASSERT_OBJ(table, LV_OBJX_NAME);
272
273 if(col_id >= LV_TABLE_COL_MAX) {
274 LV_LOG_WARN("lv_table_set_col_width: too big 'col_id'. Must be < LV_TABLE_COL_MAX.");
275 return;
276 }
277
278 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
279 ext->col_w[col_id] = w;
280 refr_size(table);
281 }
282
283 /**
284 * Set the text align in a cell
285 * @param table pointer to a Table object
286 * @param row id of the row [0 .. row_cnt -1]
287 * @param col id of the column [0 .. col_cnt -1]
288 * @param align LV_LABEL_ALIGN_LEFT or LV_LABEL_ALIGN_CENTER or LV_LABEL_ALIGN_RIGHT
289 */
lv_table_set_cell_align(lv_obj_t * table,uint16_t row,uint16_t col,lv_label_align_t align)290 void lv_table_set_cell_align(lv_obj_t * table, uint16_t row, uint16_t col, lv_label_align_t align)
291 {
292 LV_ASSERT_OBJ(table, LV_OBJX_NAME);
293
294 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
295 if(col >= ext->col_cnt) {
296 LV_LOG_WARN("lv_table_set_cell_align: invalid column");
297 return;
298 }
299
300 /*Auto expand*/
301 if(row >= ext->row_cnt) {
302 lv_table_set_row_cnt(table, row + 1);
303 }
304 uint32_t cell = row * ext->col_cnt + col;
305
306 if(ext->cell_data[cell] == NULL) {
307 ext->cell_data[cell] = lv_mem_alloc(2); /*+1: trailing '\0; +1: format byte*/
308 LV_ASSERT_MEM(ext->cell_data[cell]);
309 if(ext->cell_data[cell] == NULL) return;
310
311 ext->cell_data[cell][0] = 0;
312 ext->cell_data[cell][1] = '\0';
313 }
314
315 lv_table_cell_format_t format;
316 format.format_byte = ext->cell_data[cell][0];
317 format.s.align = align;
318 ext->cell_data[cell][0] = format.format_byte;
319 }
320
321 /**
322 * Set the type of a cell.
323 * @param table pointer to a Table object
324 * @param row id of the row [0 .. row_cnt -1]
325 * @param col id of the column [0 .. col_cnt -1]
326 * @param type 1,2,3 or 4. The cell style will be chosen accordingly.
327 */
lv_table_set_cell_type(lv_obj_t * table,uint16_t row,uint16_t col,uint8_t type)328 void lv_table_set_cell_type(lv_obj_t * table, uint16_t row, uint16_t col, uint8_t type)
329 {
330 LV_ASSERT_OBJ(table, LV_OBJX_NAME);
331
332 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
333 if(col >= ext->col_cnt) {
334 LV_LOG_WARN("lv_table_set_cell_type: invalid column");
335 return;
336 }
337
338 /*Auto expand*/
339 if(row >= ext->row_cnt) {
340 lv_table_set_row_cnt(table, row + 1);
341 }
342
343 uint32_t cell = row * ext->col_cnt + col;
344
345 if(ext->cell_data[cell] == NULL) {
346 ext->cell_data[cell] = lv_mem_alloc(2); /*+1: trailing '\0; +1: format byte*/
347 LV_ASSERT_MEM(ext->cell_data[cell]);
348 if(ext->cell_data[cell] == NULL) return;
349
350 ext->cell_data[cell][0] = 0;
351 ext->cell_data[cell][1] = '\0';
352 }
353
354 if(type > 0) type--; /*User gives 1,2,3,4 but easier to handle 0, 1, 2, 3*/
355 if(type >= LV_TABLE_CELL_STYLE_CNT) type = LV_TABLE_CELL_STYLE_CNT - 1;
356
357 lv_table_cell_format_t format;
358 format.format_byte = ext->cell_data[cell][0];
359 format.s.type = type;
360 ext->cell_data[cell][0] = format.format_byte;
361
362 ext->cell_types |= 1 << type;
363 }
364
365 /**
366 * Set the cell crop. (Don't adjust the height of the cell according to its content)
367 * @param table pointer to a Table object
368 * @param row id of the row [0 .. row_cnt -1]
369 * @param col id of the column [0 .. col_cnt -1]
370 * @param crop true: crop the cell content; false: set the cell height to the content.
371 */
lv_table_set_cell_crop(lv_obj_t * table,uint16_t row,uint16_t col,bool crop)372 void lv_table_set_cell_crop(lv_obj_t * table, uint16_t row, uint16_t col, bool crop)
373 {
374 LV_ASSERT_OBJ(table, LV_OBJX_NAME);
375
376 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
377 if(col >= ext->col_cnt) {
378 LV_LOG_WARN("lv_table_set_cell_crop: invalid column");
379 return;
380 }
381
382 /*Auto expand*/
383 if(row >= ext->row_cnt) {
384 lv_table_set_row_cnt(table, row + 1);
385 }
386
387 uint32_t cell = row * ext->col_cnt + col;
388
389 if(ext->cell_data[cell] == NULL) {
390 ext->cell_data[cell] = lv_mem_alloc(2); /*+1: trailing '\0; +1: format byte*/
391 LV_ASSERT_MEM(ext->cell_data[cell]);
392 if(ext->cell_data[cell] == NULL) return;
393
394 ext->cell_data[cell][0] = 0;
395 ext->cell_data[cell][1] = '\0';
396 }
397
398 lv_table_cell_format_t format;
399 format.format_byte = ext->cell_data[cell][0];
400 format.s.crop = crop;
401 ext->cell_data[cell][0] = format.format_byte;
402 }
403
404 /**
405 * Merge a cell with the right neighbor. The value of the cell to the right won't be displayed.
406 * @param table table pointer to a Table object
407 * @param row id of the row [0 .. row_cnt -1]
408 * @param col id of the column [0 .. col_cnt -1]
409 * @param en true: merge right; false: don't merge right
410 */
lv_table_set_cell_merge_right(lv_obj_t * table,uint16_t row,uint16_t col,bool en)411 void lv_table_set_cell_merge_right(lv_obj_t * table, uint16_t row, uint16_t col, bool en)
412 {
413 LV_ASSERT_OBJ(table, LV_OBJX_NAME);
414
415 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
416 if(col >= ext->col_cnt) {
417 LV_LOG_WARN("lv_table_set_cell_merge_right: invalid column");
418 return;
419 }
420
421 /*Auto expand*/
422 if(row >= ext->row_cnt) {
423 lv_table_set_row_cnt(table, row + 1);
424 }
425
426 uint32_t cell = row * ext->col_cnt + col;
427
428 if(ext->cell_data[cell] == NULL) {
429 ext->cell_data[cell] = lv_mem_alloc(2); /*+1: trailing '\0; +1: format byte*/
430 LV_ASSERT_MEM(ext->cell_data[cell]);
431 if(ext->cell_data[cell] == NULL) return;
432
433 ext->cell_data[cell][0] = 0;
434 ext->cell_data[cell][1] = '\0';
435 }
436
437 lv_table_cell_format_t format;
438 format.format_byte = ext->cell_data[cell][0];
439 format.s.right_merge = en ? 1 : 0;
440 ext->cell_data[cell][0] = format.format_byte;
441 refr_size(table);
442 }
443
444 /*=====================
445 * Getter functions
446 *====================*/
447
448 /**
449 * Get the value of a cell.
450 * @param table pointer to a Table object
451 * @param row id of the row [0 .. row_cnt -1]
452 * @param col id of the column [0 .. col_cnt -1]
453 * @return text in the cell
454 */
lv_table_get_cell_value(lv_obj_t * table,uint16_t row,uint16_t col)455 const char * lv_table_get_cell_value(lv_obj_t * table, uint16_t row, uint16_t col)
456 {
457 LV_ASSERT_OBJ(table, LV_OBJX_NAME);
458
459 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
460 if(row >= ext->row_cnt || col >= ext->col_cnt) {
461 LV_LOG_WARN("lv_table_set_cell_value: invalid row or column");
462 return "";
463 }
464 uint32_t cell = row * ext->col_cnt + col;
465
466 if(ext->cell_data[cell] == NULL) return "";
467
468 return &ext->cell_data[cell][1]; /*Skip the format byte*/
469 }
470
471 /**
472 * Get the number of rows.
473 * @param table table pointer to a Table object
474 * @return number of rows.
475 */
lv_table_get_row_cnt(lv_obj_t * table)476 uint16_t lv_table_get_row_cnt(lv_obj_t * table)
477 {
478 LV_ASSERT_OBJ(table, LV_OBJX_NAME);
479
480 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
481 return ext->row_cnt;
482 }
483
484 /**
485 * Get the number of columns.
486 * @param table table pointer to a Table object
487 * @return number of columns.
488 */
lv_table_get_col_cnt(lv_obj_t * table)489 uint16_t lv_table_get_col_cnt(lv_obj_t * table)
490 {
491 LV_ASSERT_OBJ(table, LV_OBJX_NAME);
492
493 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
494 return ext->col_cnt;
495 }
496
497 /**
498 * Get the width of a column
499 * @param table table pointer to a Table object
500 * @param col_id id of the column [0 .. LV_TABLE_COL_MAX -1]
501 * @return width of the column
502 */
lv_table_get_col_width(lv_obj_t * table,uint16_t col_id)503 lv_coord_t lv_table_get_col_width(lv_obj_t * table, uint16_t col_id)
504 {
505 LV_ASSERT_OBJ(table, LV_OBJX_NAME);
506
507 if(col_id >= LV_TABLE_COL_MAX) {
508 LV_LOG_WARN("lv_table_set_col_width: too big 'col_id'. Must be < LV_TABLE_COL_MAX.");
509 return 0;
510 }
511
512 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
513 return ext->col_w[col_id];
514 }
515
516 /**
517 * Get the text align of a cell
518 * @param table pointer to a Table object
519 * @param row id of the row [0 .. row_cnt -1]
520 * @param col id of the column [0 .. col_cnt -1]
521 * @return LV_LABEL_ALIGN_LEFT (default in case of error) or LV_LABEL_ALIGN_CENTER or
522 * LV_LABEL_ALIGN_RIGHT
523 */
lv_table_get_cell_align(lv_obj_t * table,uint16_t row,uint16_t col)524 lv_label_align_t lv_table_get_cell_align(lv_obj_t * table, uint16_t row, uint16_t col)
525 {
526 LV_ASSERT_OBJ(table, LV_OBJX_NAME);
527
528 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
529 if(row >= ext->row_cnt || col >= ext->col_cnt) {
530 LV_LOG_WARN("lv_table_set_cell_align: invalid row or column");
531 return LV_LABEL_ALIGN_LEFT; /*Just return with something*/
532 }
533 uint32_t cell = row * ext->col_cnt + col;
534
535 if(ext->cell_data[cell] == NULL)
536 return LV_LABEL_ALIGN_LEFT; /*Just return with something*/
537 else {
538 lv_table_cell_format_t format;
539 format.format_byte = ext->cell_data[cell][0];
540 return format.s.align;
541 }
542 }
543
544 /**
545 * Get the type of a cell
546 * @param table pointer to a Table object
547 * @param row id of the row [0 .. row_cnt -1]
548 * @param col id of the column [0 .. col_cnt -1]
549 * @return 1,2,3 or 4
550 */
lv_table_get_cell_type(lv_obj_t * table,uint16_t row,uint16_t col)551 lv_label_align_t lv_table_get_cell_type(lv_obj_t * table, uint16_t row, uint16_t col)
552 {
553 LV_ASSERT_OBJ(table, LV_OBJX_NAME);
554
555 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
556 if(row >= ext->row_cnt || col >= ext->col_cnt) {
557 LV_LOG_WARN("lv_table_get_cell_type: invalid row or column");
558 return 1; /*Just return with something*/
559 }
560 uint32_t cell = row * ext->col_cnt + col;
561
562 if(ext->cell_data[cell] == NULL)
563 return 1; /*Just return with something*/
564 else {
565 lv_table_cell_format_t format;
566 format.format_byte = ext->cell_data[cell][0];
567 return format.s.type + 1; /*0,1,2,3 is stored but user sees 1,2,3,4*/
568 }
569 }
570
571 /**
572 * Get the crop property of a cell
573 * @param table pointer to a Table object
574 * @param row id of the row [0 .. row_cnt -1]
575 * @param col id of the column [0 .. col_cnt -1]
576 * @return true: text crop enabled; false: disabled
577 */
lv_table_get_cell_crop(lv_obj_t * table,uint16_t row,uint16_t col)578 lv_label_align_t lv_table_get_cell_crop(lv_obj_t * table, uint16_t row, uint16_t col)
579 {
580 LV_ASSERT_OBJ(table, LV_OBJX_NAME);
581
582 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
583 if(row >= ext->row_cnt || col >= ext->col_cnt) {
584 LV_LOG_WARN("lv_table_get_cell_crop: invalid row or column");
585 return false; /*Just return with something*/
586 }
587 uint32_t cell = row * ext->col_cnt + col;
588
589 if(ext->cell_data[cell] == NULL)
590 return false; /*Just return with something*/
591 else {
592 lv_table_cell_format_t format;
593 format.format_byte = ext->cell_data[cell][0];
594 return format.s.crop;
595 }
596 }
597
598 /**
599 * Get the cell merge attribute.
600 * @param table table pointer to a Table object
601 * @param row id of the row [0 .. row_cnt -1]
602 * @param col id of the column [0 .. col_cnt -1]
603 * @return true: merge right; false: don't merge right
604 */
lv_table_get_cell_merge_right(lv_obj_t * table,uint16_t row,uint16_t col)605 bool lv_table_get_cell_merge_right(lv_obj_t * table, uint16_t row, uint16_t col)
606 {
607 LV_ASSERT_OBJ(table, LV_OBJX_NAME);
608
609 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
610 if(row >= ext->row_cnt || col >= ext->col_cnt) {
611 LV_LOG_WARN("lv_table_get_cell_merge_right: invalid row or column");
612 return false;
613 }
614
615 uint32_t cell = row * ext->col_cnt + col;
616
617 if(ext->cell_data[cell] == NULL)
618 return false;
619 else {
620 lv_table_cell_format_t format;
621 format.format_byte = ext->cell_data[cell][0];
622 return format.s.right_merge ? true : false;
623 }
624 }
625
626 /**
627 * Get the last pressed or being pressed cell
628 * @param table pointer to a table object
629 * @param row pointer to variable to store the pressed row
630 * @param col pointer to variable to store the pressed column
631 * @return LV_RES_OK: a valid pressed cell was found, LV_RES_INV: no valid cell is pressed
632 */
lv_table_get_pressed_cell(lv_obj_t * table,uint16_t * row,uint16_t * col)633 lv_res_t lv_table_get_pressed_cell(lv_obj_t * table, uint16_t * row, uint16_t * col)
634 {
635 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
636
637 lv_indev_type_t type = lv_indev_get_type(lv_indev_get_act());
638 if(type != LV_INDEV_TYPE_POINTER && type != LV_INDEV_TYPE_BUTTON) {
639 if(col) *col = 0xFFFF;
640 if(row) *row = 0xFFFF;
641 return LV_RES_INV;
642 }
643
644 lv_point_t p;
645 lv_indev_get_point(lv_indev_get_act(), &p);
646
647 lv_coord_t tmp;
648 if(col) {
649 lv_coord_t x = p.x;
650 x -= table->coords.x1;
651 x -= lv_obj_get_style_pad_left(table, LV_TABLE_PART_BG);
652 *col = 0;
653 tmp = 0;
654 for(*col = 0; *col < ext->col_cnt; (*col)++) {
655 tmp += ext->col_w[*col];
656 if(x < tmp) break;
657 }
658 }
659
660 if(row) {
661 lv_coord_t y = p.y;
662 y -= table->coords.y1;
663 y -= lv_obj_get_style_pad_top(table, LV_TABLE_PART_BG);
664
665 *row = 0;
666 tmp = 0;
667
668 for(*row = 0; *row < ext->row_cnt; (*row)++) {
669 tmp += ext->row_h[*row];
670 if(y < tmp) break;
671 }
672 }
673
674 return LV_RES_OK;
675 }
676
677 /**********************
678 * STATIC FUNCTIONS
679 **********************/
680
681 /**
682 * Handle the drawing related tasks of the tables
683 * @param table pointer to an object
684 * @param clip_area the object will be drawn only in this area
685 * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
686 * (return 'true' if yes)
687 * LV_DESIGN_DRAW: draw the object (always return 'true')
688 * LV_DESIGN_DRAW_POST: drawing after every children are drawn
689 * @param return an element of `lv_design_res_t`
690 */
lv_table_design(lv_obj_t * table,const lv_area_t * clip_area,lv_design_mode_t mode)691 static lv_design_res_t lv_table_design(lv_obj_t * table, const lv_area_t * clip_area, lv_design_mode_t mode)
692 {
693 /*Return false if the object is not covers the mask_p area*/
694 if(mode == LV_DESIGN_COVER_CHK) {
695 return ancestor_design(table, clip_area, mode);
696 }
697 /*Draw the object*/
698 else if(mode == LV_DESIGN_DRAW_MAIN) {
699 /*Draw the background*/
700 ancestor_design(table, clip_area, mode);
701
702 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
703
704 lv_point_t txt_size;
705 lv_area_t cell_area;
706 lv_area_t txt_area;
707 lv_txt_flag_t txt_flags;
708
709 lv_style_int_t bg_top = lv_obj_get_style_pad_top(table, LV_TABLE_PART_BG);
710 lv_style_int_t bg_bottom = lv_obj_get_style_pad_bottom(table, LV_TABLE_PART_BG);
711 lv_style_int_t bg_left = lv_obj_get_style_pad_left(table, LV_TABLE_PART_BG);
712 lv_style_int_t bg_right = lv_obj_get_style_pad_right(table, LV_TABLE_PART_BG);
713
714 lv_draw_rect_dsc_t rect_dsc[LV_TABLE_CELL_STYLE_CNT];
715 lv_draw_label_dsc_t label_dsc[LV_TABLE_CELL_STYLE_CNT];
716 lv_draw_line_dsc_t line_dsc[LV_TABLE_CELL_STYLE_CNT];
717 lv_style_int_t cell_left[LV_TABLE_CELL_STYLE_CNT];
718 lv_style_int_t cell_right[LV_TABLE_CELL_STYLE_CNT];
719 lv_style_int_t cell_top[LV_TABLE_CELL_STYLE_CNT];
720 lv_style_int_t cell_bottom[LV_TABLE_CELL_STYLE_CNT];
721
722 uint16_t i;
723 for(i = 0; i < LV_TABLE_CELL_STYLE_CNT; i++) {
724 if((ext->cell_types & (1 << i)) == 0) continue; /*Skip unused cell types*/
725 lv_draw_rect_dsc_init(&rect_dsc[i]);
726 lv_obj_init_draw_rect_dsc(table, LV_TABLE_PART_CELL1 + i, &rect_dsc[i]);
727
728 lv_draw_label_dsc_init(&label_dsc[i]);
729 lv_obj_init_draw_label_dsc(table, LV_TABLE_PART_CELL1 + i, &label_dsc[i]);
730
731 lv_draw_line_dsc_init(&line_dsc[i]);
732 lv_obj_init_draw_line_dsc(table, LV_TABLE_PART_CELL1 + i, &line_dsc[i]);
733
734 cell_left[i] = lv_obj_get_style_pad_left(table, LV_TABLE_PART_CELL1 + i);
735 cell_right[i] = lv_obj_get_style_pad_right(table, LV_TABLE_PART_CELL1 + i);
736 cell_top[i] = lv_obj_get_style_pad_top(table, LV_TABLE_PART_CELL1 + i);
737 cell_bottom[i] = lv_obj_get_style_pad_bottom(table, LV_TABLE_PART_CELL1 + i);
738 }
739
740 uint16_t col;
741 uint16_t row;
742 uint16_t cell = 0;
743
744 cell_area.y2 = table->coords.y1 + bg_top - 1;
745 for(row = 0; row < ext->row_cnt; row++) {
746 lv_coord_t h_row = ext->row_h[row];
747
748 cell_area.y1 = cell_area.y2 + 1;
749 cell_area.y2 = cell_area.y1 + h_row - 1;
750
751 if(cell_area.y1 > clip_area->y2) return LV_DESIGN_RES_OK;
752
753 cell_area.x2 = table->coords.x1 + bg_left - 1;
754
755 for(col = 0; col < ext->col_cnt; col++) {
756
757 lv_table_cell_format_t format;
758 if(ext->cell_data[cell]) {
759 format.format_byte = ext->cell_data[cell][0];
760 }
761 else {
762 format.s.right_merge = 0;
763 format.s.align = LV_LABEL_ALIGN_LEFT;
764 format.s.type = 0;
765 format.s.crop = 1;
766 }
767
768 cell_area.x1 = cell_area.x2 + 1;
769 cell_area.x2 = cell_area.x1 + ext->col_w[col] - 1;
770
771 uint16_t col_merge = 0;
772 for(col_merge = 0; col_merge + col < ext->col_cnt - 1; col_merge++) {
773 if(ext->cell_data[cell + col_merge] != NULL) {
774 format.format_byte = ext->cell_data[cell + col_merge][0];
775 if(format.s.right_merge)
776 cell_area.x2 += ext->col_w[col + col_merge + 1];
777 else
778 break;
779 }
780 else {
781 break;
782 }
783 }
784
785 if(cell_area.y2 < clip_area->y1) {
786 cell += col_merge + 1;
787 col += col_merge;
788 continue;
789 }
790
791 uint8_t cell_type = format.s.type;
792
793 /*Expand the cell area with a half border to avoid drawing 2 borders next to each other*/
794 lv_area_t cell_area_border;
795 lv_area_copy(&cell_area_border, &cell_area);
796 if((rect_dsc[cell_type].border_side & LV_BORDER_SIDE_LEFT) && cell_area_border.x1 > table->coords.x1 + bg_left) {
797 cell_area_border.x1 -= rect_dsc[cell_type].border_width / 2;
798 }
799 if((rect_dsc[cell_type].border_side & LV_BORDER_SIDE_TOP) && cell_area_border.y1 > table->coords.y1 + bg_top) {
800 cell_area_border.y1 -= rect_dsc[cell_type].border_width / 2;
801 }
802 if((rect_dsc[cell_type].border_side & LV_BORDER_SIDE_RIGHT) && cell_area_border.x2 < table->coords.x2 - bg_right - 1) {
803 cell_area_border.x2 += rect_dsc[cell_type].border_width / 2 + (rect_dsc[cell_type].border_width & 0x1);
804 }
805 if((rect_dsc[cell_type].border_side & LV_BORDER_SIDE_BOTTOM) &&
806 cell_area_border.y2 < table->coords.y2 - bg_bottom - 1) {
807 cell_area_border.y2 += rect_dsc[cell_type].border_width / 2 + (rect_dsc[cell_type].border_width & 0x1);
808 }
809
810 lv_draw_rect(&cell_area_border, clip_area, &rect_dsc[cell_type]);
811
812 if(ext->cell_data[cell]) {
813 txt_area.x1 = cell_area.x1 + cell_left[cell_type];
814 txt_area.x2 = cell_area.x2 - cell_right[cell_type];
815 txt_area.y1 = cell_area.y1 + cell_top[cell_type];
816 txt_area.y2 = cell_area.y2 - cell_bottom[cell_type];
817
818 /*Align the content to the middle if not cropped*/
819 if(format.s.crop == 0) {
820 txt_flags = LV_TXT_FLAG_NONE;
821 }
822 else {
823 txt_flags = LV_TXT_FLAG_EXPAND;
824 }
825
826 _lv_txt_get_size(&txt_size, ext->cell_data[cell] + 1, label_dsc[cell_type].font,
827 label_dsc[cell_type].letter_space, label_dsc[cell_type].line_space,
828 lv_area_get_width(&txt_area), txt_flags);
829
830 label_dsc[cell_type].flag = 0;
831 /*Align the content to the middle if not cropped*/
832 if(format.s.crop == 0) {
833 txt_area.y1 = cell_area.y1 + h_row / 2 - txt_size.y / 2;
834 txt_area.y2 = cell_area.y1 + h_row / 2 + txt_size.y / 2;
835 }
836
837 switch(format.s.align) {
838 default:
839 case LV_LABEL_ALIGN_LEFT:
840 label_dsc[cell_type].flag |= LV_TXT_FLAG_NONE;
841 break;
842 case LV_LABEL_ALIGN_RIGHT:
843 label_dsc[cell_type].flag |= LV_TXT_FLAG_RIGHT;
844 break;
845 case LV_LABEL_ALIGN_CENTER:
846 label_dsc[cell_type].flag |= LV_TXT_FLAG_CENTER;
847 break;
848 }
849
850 lv_area_t label_mask;
851 bool label_mask_ok;
852 label_mask_ok = _lv_area_intersect(&label_mask, clip_area, &cell_area);
853 if(label_mask_ok) {
854 lv_draw_label(&txt_area, &label_mask, &label_dsc[cell_type], ext->cell_data[cell] + 1, NULL);
855 }
856
857 /*Draw lines after '\n's*/
858 lv_point_t p1;
859 lv_point_t p2;
860 p1.x = cell_area.x1;
861 p2.x = cell_area.x2;
862 for(i = 1; ext->cell_data[cell][i] != '\0'; i++) {
863 if(ext->cell_data[cell][i] == '\n') {
864 ext->cell_data[cell][i] = '\0';
865 _lv_txt_get_size(&txt_size, ext->cell_data[cell] + 1, label_dsc[cell_type].font,
866 label_dsc[cell_type].letter_space, label_dsc[cell_type].line_space,
867 lv_area_get_width(&txt_area), txt_flags);
868
869 p1.y = txt_area.y1 + txt_size.y + label_dsc[cell_type].line_space / 2;
870 p2.y = txt_area.y1 + txt_size.y + label_dsc[cell_type].line_space / 2;
871 lv_draw_line(&p1, &p2, clip_area, &line_dsc[cell_type]);
872
873 ext->cell_data[cell][i] = '\n';
874 }
875 }
876 }
877
878 cell += col_merge + 1;
879 col += col_merge;
880 }
881 }
882 }
883 /*Post draw when the children are drawn*/
884 else if(mode == LV_DESIGN_DRAW_POST) {
885 ancestor_design(table, clip_area, mode);
886 }
887
888 return LV_DESIGN_RES_OK;
889 }
890
891 /**
892 * Signal function of the table
893 * @param table pointer to a table object
894 * @param sign a signal type from lv_signal_t enum
895 * @param param pointer to a signal specific variable
896 * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
897 */
lv_table_signal(lv_obj_t * table,lv_signal_t sign,void * param)898 static lv_res_t lv_table_signal(lv_obj_t * table, lv_signal_t sign, void * param)
899 {
900 lv_res_t res;
901 if(sign == LV_SIGNAL_GET_STYLE) {
902 lv_get_style_info_t * info = param;
903 info->result = lv_table_get_style(table, info->part);
904 if(info->result != NULL) return LV_RES_OK;
905 else return ancestor_signal(table, sign, param);
906 }
907
908 /* Include the ancient signal function */
909 res = ancestor_signal(table, sign, param);
910 if(res != LV_RES_OK) return res;
911 if(sign == LV_SIGNAL_GET_TYPE) return lv_obj_handle_get_type_signal(param, LV_OBJX_NAME);
912
913 if(sign == LV_SIGNAL_CLEANUP) {
914 /*Free the cell texts*/
915 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
916 uint16_t i;
917 for(i = 0; i < ext->col_cnt * ext->row_cnt; i++) {
918 if(ext->cell_data[i]) {
919 lv_mem_free(ext->cell_data[i]);
920 ext->cell_data[i] = NULL;
921 }
922 }
923
924 if(ext->cell_data) lv_mem_free(ext->cell_data);
925 if(ext->row_h) lv_mem_free(ext->row_h);
926
927 for(i = 0; i < LV_TABLE_CELL_STYLE_CNT; i++) {
928 lv_obj_clean_style_list(table, LV_TABLE_PART_CELL1 + i);
929 }
930 }
931 else if(sign == LV_SIGNAL_STYLE_CHG) {
932 refr_size(table);
933 }
934
935 return res;
936 }
937
938
939 /**
940 * Get the style descriptor of a part of the object
941 * @param table pointer the object
942 * @param part the part from. (LV_TABLE_PART_...)
943 * @return pointer to the style descriptor of the specified part
944 */
lv_table_get_style(lv_obj_t * table,uint8_t part)945 static lv_style_list_t * lv_table_get_style(lv_obj_t * table, uint8_t part)
946 {
947 LV_ASSERT_OBJ(table, LV_OBJX_NAME);
948
949 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
950 lv_style_list_t * style_dsc_p;
951
952 switch(part) {
953 case LV_TABLE_PART_BG:
954 style_dsc_p = &table->style_list;
955 break;
956 case LV_TABLE_PART_CELL1:
957 style_dsc_p = &ext->cell_style[0];
958 break;
959 case LV_TABLE_PART_CELL2:
960 style_dsc_p = &ext->cell_style[1];
961 break;
962 case LV_TABLE_PART_CELL3:
963 style_dsc_p = &ext->cell_style[2];
964 break;
965 case LV_TABLE_PART_CELL4:
966 style_dsc_p = &ext->cell_style[3];
967 break;
968 default:
969 style_dsc_p = NULL;
970 }
971
972 return style_dsc_p;
973 }
974
refr_size(lv_obj_t * table)975 static void refr_size(lv_obj_t * table)
976 {
977 lv_coord_t h = 0;
978 lv_coord_t w = 0;
979
980 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
981 if(ext->row_cnt == 0 || ext->col_cnt == 0) {
982 lv_obj_set_size(table, w, h);
983 return;
984 }
985
986 uint16_t i;
987 for(i = 0; i < ext->col_cnt; i++) {
988 w += ext->col_w[i];
989 }
990
991 lv_style_int_t cell_left[LV_TABLE_CELL_STYLE_CNT];
992 lv_style_int_t cell_right[LV_TABLE_CELL_STYLE_CNT];
993 lv_style_int_t cell_top[LV_TABLE_CELL_STYLE_CNT];
994 lv_style_int_t cell_bottom[LV_TABLE_CELL_STYLE_CNT];
995 lv_style_int_t letter_space[LV_TABLE_CELL_STYLE_CNT];
996 lv_style_int_t line_space[LV_TABLE_CELL_STYLE_CNT];
997 const lv_font_t * font[LV_TABLE_CELL_STYLE_CNT];
998
999 for(i = 0; i < LV_TABLE_CELL_STYLE_CNT; i++) {
1000 if((ext->cell_types & (1 << i)) == 0) continue; /*Skip unused cell types*/
1001 cell_left[i] = lv_obj_get_style_pad_left(table, LV_TABLE_PART_CELL1 + i);
1002 cell_right[i] = lv_obj_get_style_pad_right(table, LV_TABLE_PART_CELL1 + i);
1003 cell_top[i] = lv_obj_get_style_pad_top(table, LV_TABLE_PART_CELL1 + i);
1004 cell_bottom[i] = lv_obj_get_style_pad_bottom(table, LV_TABLE_PART_CELL1 + i);
1005 letter_space[i] = lv_obj_get_style_text_letter_space(table, LV_TABLE_PART_CELL1 + i);
1006 line_space[i] = lv_obj_get_style_text_line_space(table, LV_TABLE_PART_CELL1 + i);
1007 font[i] = lv_obj_get_style_text_font(table, LV_TABLE_PART_CELL1 + i);
1008 }
1009
1010
1011 for(i = 0; i < ext->row_cnt; i++) {
1012 ext->row_h[i] = get_row_height(table, i, font, letter_space, line_space,
1013 cell_left, cell_right, cell_top, cell_bottom);
1014 h += ext->row_h[i];
1015 }
1016
1017 lv_style_int_t bg_top = lv_obj_get_style_pad_top(table, LV_TABLE_PART_BG);
1018 lv_style_int_t bg_bottom = lv_obj_get_style_pad_bottom(table, LV_TABLE_PART_BG);
1019 lv_style_int_t bg_left = lv_obj_get_style_pad_left(table, LV_TABLE_PART_BG);
1020 lv_style_int_t bg_right = lv_obj_get_style_pad_right(table, LV_TABLE_PART_BG);
1021 w += bg_left + bg_right;
1022 h += bg_top + bg_bottom;
1023
1024 lv_obj_set_size(table, w + 1, h + 1);
1025 lv_obj_invalidate(table); /*Always invalidate even if the size hasn't changed*/
1026 }
1027
get_row_height(lv_obj_t * table,uint16_t row_id,const lv_font_t ** font,lv_style_int_t * letter_space,lv_style_int_t * line_space,lv_style_int_t * cell_left,lv_style_int_t * cell_right,lv_style_int_t * cell_top,lv_style_int_t * cell_bottom)1028 static lv_coord_t get_row_height(lv_obj_t * table, uint16_t row_id, const lv_font_t ** font,
1029 lv_style_int_t * letter_space, lv_style_int_t * line_space,
1030 lv_style_int_t * cell_left, lv_style_int_t * cell_right, lv_style_int_t * cell_top, lv_style_int_t * cell_bottom)
1031 {
1032 lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
1033 lv_point_t txt_size;
1034 lv_coord_t txt_w;
1035
1036 uint16_t row_start = row_id * ext->col_cnt;
1037 uint16_t cell;
1038 uint16_t col;
1039 lv_coord_t h_max = lv_font_get_line_height(font[0]) + cell_top[0] + cell_bottom[0];
1040
1041 for(cell = row_start, col = 0; cell < row_start + ext->col_cnt; cell++, col++) {
1042 if(ext->cell_data[cell] != NULL) {
1043 txt_w = ext->col_w[col];
1044 uint16_t col_merge = 0;
1045 for(col_merge = 0; col_merge + col < ext->col_cnt - 1; col_merge++) {
1046
1047 if(ext->cell_data[cell + col_merge] != NULL) {
1048 lv_table_cell_format_t format;
1049 format.format_byte = ext->cell_data[cell + col_merge][0];
1050 if(format.s.right_merge)
1051 txt_w += ext->col_w[col + col_merge + 1];
1052 else
1053 break;
1054 }
1055 else {
1056 break;
1057 }
1058 }
1059
1060 lv_table_cell_format_t format;
1061 format.format_byte = ext->cell_data[cell][0];
1062 uint8_t cell_type = format.s.type;
1063
1064 /*With text crop assume 1 line*/
1065 if(format.s.crop) {
1066 h_max = LV_MATH_MAX(lv_font_get_line_height(font[cell_type]) + cell_top[cell_type] + cell_bottom[cell_type],
1067 h_max);
1068 }
1069 /*Without text crop calculate the height of the text in the cell*/
1070 else {
1071 txt_w -= cell_left[cell_type] + cell_right[cell_type];
1072
1073 _lv_txt_get_size(&txt_size, ext->cell_data[cell] + 1, font[cell_type],
1074 letter_space[cell_type], line_space[cell_type], txt_w, LV_TXT_FLAG_NONE);
1075
1076 h_max = LV_MATH_MAX(txt_size.y + cell_top[cell_type] + cell_bottom[cell_type], h_max);
1077 cell += col_merge;
1078 col += col_merge;
1079 }
1080 }
1081 }
1082
1083 return h_max;
1084 }
1085
1086 #endif
1087