1 /**
2 * @file lv_rect.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9 #include "lv_label.h"
10 #if LV_USE_LABEL != 0
11
12 #include "../lv_core/lv_obj.h"
13 #include "../lv_core/lv_group.h"
14 #include "../lv_misc/lv_color.h"
15 #include "../lv_misc/lv_math.h"
16
17 /*********************
18 * DEFINES
19 *********************/
20 /*Test configurations*/
21 #ifndef LV_LABEL_DEF_SCROLL_SPEED
22 #define LV_LABEL_DEF_SCROLL_SPEED (25)
23 #endif
24
25 #define LV_LABEL_DOT_END_INV 0xFFFF
26 #define LV_LABEL_HINT_HEIGHT_LIMIT \
27 1024 /*Enable "hint" to buffer info about labels larger than this. (Speed up their drawing)*/
28
29 /**********************
30 * TYPEDEFS
31 **********************/
32
33 /**********************
34 * STATIC PROTOTYPES
35 **********************/
36 static lv_res_t lv_label_signal(lv_obj_t * label, lv_signal_t sign, void * param);
37 static bool lv_label_design(lv_obj_t * label, const lv_area_t * mask, lv_design_mode_t mode);
38 static void lv_label_refr_text(lv_obj_t * label);
39 static void lv_label_revert_dots(lv_obj_t * label);
40
41 #if LV_USE_ANIMATION
42 static void lv_label_set_offset_x(lv_obj_t * label, lv_coord_t x);
43 static void lv_label_set_offset_y(lv_obj_t * label, lv_coord_t y);
44 #endif
45
46 static bool lv_label_set_dot_tmp(lv_obj_t * label, char * data, uint16_t len);
47 static char * lv_label_get_dot_tmp(lv_obj_t * label);
48 static void lv_label_dot_tmp_free(lv_obj_t * label);
49
50 /**********************
51 * STATIC VARIABLES
52 **********************/
53 static lv_signal_cb_t ancestor_signal;
54
55 /**********************
56 * MACROS
57 **********************/
58
59 /**********************
60 * GLOBAL FUNCTIONS
61 **********************/
62
63 /**
64 * Create a label objects
65 * @param par pointer to an object, it will be the parent of the new label
66 * @param copy pointer to a button object, if not NULL then the new object will be copied from it
67 * @return pointer to the created button
68 */
lv_label_create(lv_obj_t * par,const lv_obj_t * copy)69 lv_obj_t * lv_label_create(lv_obj_t * par, const lv_obj_t * copy)
70 {
71 LV_LOG_TRACE("label create started");
72
73 /*Create a basic object*/
74 lv_obj_t * new_label = lv_obj_create(par, copy);
75 lv_mem_assert(new_label);
76 if(new_label == NULL) return NULL;
77
78 if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_cb(new_label);
79
80 /*Extend the basic object to a label object*/
81 lv_obj_allocate_ext_attr(new_label, sizeof(lv_label_ext_t));
82
83 lv_label_ext_t * ext = lv_obj_get_ext_attr(new_label);
84 lv_mem_assert(ext);
85 if(ext == NULL) return NULL;
86
87 ext->text = NULL;
88 ext->static_txt = 0;
89 ext->recolor = 0;
90 ext->body_draw = 0;
91 ext->align = LV_LABEL_ALIGN_LEFT;
92 ext->dot_end = LV_LABEL_DOT_END_INV;
93 ext->long_mode = LV_LABEL_LONG_EXPAND;
94 #if LV_USE_ANIMATION
95 ext->anim_speed = LV_LABEL_DEF_SCROLL_SPEED;
96 #endif
97 ext->offset.x = 0;
98 ext->offset.y = 0;
99
100 #if LV_LABEL_LONG_TXT_HINT
101 ext->hint.line_start = -1;
102 ext->hint.coord_y = 0;
103 ext->hint.y = 0;
104 #endif
105
106 #if LV_LABEL_TEXT_SEL
107 ext->txt_sel_start = LV_LABEL_TEXT_SEL_OFF;
108 ext->txt_sel_end = LV_LABEL_TEXT_SEL_OFF;
109 #endif
110 ext->dot.tmp_ptr = NULL;
111 ext->dot_tmp_alloc = 0;
112
113 lv_obj_set_design_cb(new_label, lv_label_design);
114 lv_obj_set_signal_cb(new_label, lv_label_signal);
115
116 /*Init the new label*/
117 if(copy == NULL) {
118 lv_obj_set_click(new_label, false);
119 lv_label_set_long_mode(new_label, LV_LABEL_LONG_EXPAND);
120 lv_label_set_text(new_label, "Text");
121 lv_label_set_style(new_label, LV_LABEL_STYLE_MAIN, NULL); /*Inherit parent's style*/
122 }
123 /*Copy 'copy' if not NULL*/
124 else {
125 lv_label_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
126 lv_label_set_long_mode(new_label, lv_label_get_long_mode(copy));
127 lv_label_set_recolor(new_label, lv_label_get_recolor(copy));
128 lv_label_set_body_draw(new_label, lv_label_get_body_draw(copy));
129 lv_label_set_align(new_label, lv_label_get_align(copy));
130 if(copy_ext->static_txt == 0)
131 lv_label_set_text(new_label, lv_label_get_text(copy));
132 else
133 lv_label_set_static_text(new_label, lv_label_get_text(copy));
134
135 /*In DOT mode save the text byte-to-byte because a '\0' can be in the middle*/
136 if(copy_ext->long_mode == LV_LABEL_LONG_DOT) {
137 ext->text = lv_mem_realloc(ext->text, lv_mem_get_size(copy_ext->text));
138 lv_mem_assert(ext->text);
139 if(ext->text == NULL) return NULL;
140 memcpy(ext->text, copy_ext->text, lv_mem_get_size(copy_ext->text));
141 }
142
143 if(copy_ext->dot_tmp_alloc && copy_ext->dot.tmp_ptr) {
144 int len = strlen(copy_ext->dot.tmp_ptr);
145 lv_label_set_dot_tmp(new_label, ext->dot.tmp_ptr, len);
146 } else {
147 memcpy(ext->dot.tmp, copy_ext->dot.tmp, sizeof(ext->dot.tmp));
148 }
149 ext->dot_tmp_alloc = copy_ext->dot_tmp_alloc;
150 ext->dot_end = copy_ext->dot_end;
151
152 /*Refresh the style with new signal function*/
153 lv_obj_refresh_style(new_label);
154 }
155
156 LV_LOG_INFO("label created");
157
158 return new_label;
159 }
160
161 /*=====================
162 * Setter functions
163 *====================*/
164
165 /**
166 * Set a new text for a label. Memory will be allocated to store the text by the label.
167 * @param label pointer to a label object
168 * @param text '\0' terminated character string. NULL to refresh with the current text.
169 */
lv_label_set_text(lv_obj_t * label,const char * text)170 void lv_label_set_text(lv_obj_t * label, const char * text)
171 {
172 lv_obj_invalidate(label);
173
174 lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
175
176 /*If text is NULL then refresh */
177 if(text == NULL) {
178 lv_label_refr_text(label);
179 return;
180 }
181
182 if(ext->text == text) {
183 /*If set its own text then reallocate it (maybe its size changed)*/
184 ext->text = lv_mem_realloc(ext->text, strlen(ext->text) + 1);
185 lv_mem_assert(ext->text);
186 if(ext->text == NULL) return;
187 } else {
188 /*Allocate space for the new text*/
189 uint32_t len = strlen(text) + 1;
190 if(ext->text != NULL && ext->static_txt == 0) {
191 lv_mem_free(ext->text);
192 ext->text = NULL;
193 }
194
195 ext->text = lv_mem_alloc(len);
196 lv_mem_assert(ext->text);
197 if(ext->text == NULL) return;
198
199 strcpy(ext->text, text);
200 ext->static_txt = 0; /*Now the text is dynamically allocated*/
201 }
202
203 lv_label_refr_text(label);
204 }
205
206 /**
207 * Set a new text for a label from a character array. The array don't has to be '\0' terminated.
208 * Memory will be allocated to store the array by the label.
209 * @param label pointer to a label object
210 * @param array array of characters or NULL to refresh the label
211 * @param size the size of 'array' in bytes
212 */
lv_label_set_array_text(lv_obj_t * label,const char * array,uint16_t size)213 void lv_label_set_array_text(lv_obj_t * label, const char * array, uint16_t size)
214 {
215 lv_obj_invalidate(label);
216
217 lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
218
219 /*If trying to set its own text or the array is NULL then refresh */
220 if(array == ext->text || array == NULL) {
221 lv_label_refr_text(label);
222 return;
223 }
224
225 /*Allocate space for the new text*/
226 if(ext->text != NULL && ext->static_txt == 0) {
227 lv_mem_free(ext->text);
228 ext->text = NULL;
229 }
230 ext->text = lv_mem_alloc(size + 1);
231 lv_mem_assert(ext->text);
232 if(ext->text == NULL) return;
233
234 memcpy(ext->text, array, size);
235 ext->text[size] = '\0';
236 ext->static_txt = 0; /*Now the text is dynamically allocated*/
237
238 lv_label_refr_text(label);
239 }
240
241 /**
242 * Set a static text. It will not be saved by the label so the 'text' variable
243 * has to be 'alive' while the label exist.
244 * @param label pointer to a label object
245 * @param text pointer to a text. NULL to refresh with the current text.
246 */
lv_label_set_static_text(lv_obj_t * label,const char * text)247 void lv_label_set_static_text(lv_obj_t * label, const char * text)
248 {
249 lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
250 if(ext->static_txt == 0 && ext->text != NULL) {
251 lv_mem_free(ext->text);
252 ext->text = NULL;
253 }
254
255 if(text != NULL) {
256 ext->static_txt = 1;
257 ext->text = (char *)text;
258 }
259
260 lv_label_refr_text(label);
261 }
262
263 /**
264 * Set the behavior of the label with longer text then the object size
265 * @param label pointer to a label object
266 * @param long_mode the new mode from 'lv_label_long_mode' enum.
267 * In LV_LONG_BREAK/LONG/ROLL the size of the label should be set AFTER this
268 * function
269 */
lv_label_set_long_mode(lv_obj_t * label,lv_label_long_mode_t long_mode)270 void lv_label_set_long_mode(lv_obj_t * label, lv_label_long_mode_t long_mode)
271 {
272 lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
273
274 #if LV_USE_ANIMATION
275 /*Delete the old animation (if exists)*/
276 lv_anim_del(label, (lv_anim_exec_xcb_t)lv_obj_set_x);
277 lv_anim_del(label, (lv_anim_exec_xcb_t)lv_obj_set_y);
278 lv_anim_del(label, (lv_anim_exec_xcb_t)lv_label_set_offset_x);
279 lv_anim_del(label, (lv_anim_exec_xcb_t)lv_label_set_offset_y);
280 #endif
281 ext->offset.x = 0;
282 ext->offset.y = 0;
283
284 if(long_mode == LV_LABEL_LONG_SROLL || long_mode == LV_LABEL_LONG_SROLL_CIRC || long_mode == LV_LABEL_LONG_CROP)
285 ext->expand = 1;
286 else
287 ext->expand = 0;
288
289 /*Restore the character under the dots*/
290 if(ext->long_mode == LV_LABEL_LONG_DOT && ext->dot_end != LV_LABEL_DOT_END_INV) {
291 lv_label_revert_dots(label);
292 }
293
294 ext->long_mode = long_mode;
295 lv_label_refr_text(label);
296 }
297
298 /**
299 * Set the align of the label (left or center)
300 * @param label pointer to a label object
301 * @param align 'LV_LABEL_ALIGN_LEFT' or 'LV_LABEL_ALIGN_LEFT'
302 */
lv_label_set_align(lv_obj_t * label,lv_label_align_t align)303 void lv_label_set_align(lv_obj_t * label, lv_label_align_t align)
304 {
305 lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
306 if(ext->align == align) return;
307
308 ext->align = align;
309
310 lv_obj_invalidate(label); /*Enough to invalidate because alignment is only drawing related
311 (lv_refr_label_text() not required)*/
312 }
313
314 /**
315 * Enable the recoloring by in-line commands
316 * @param label pointer to a label object
317 * @param en true: enable recoloring, false: disable
318 */
lv_label_set_recolor(lv_obj_t * label,bool en)319 void lv_label_set_recolor(lv_obj_t * label, bool en)
320 {
321 lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
322 if(ext->recolor == en) return;
323
324 ext->recolor = en == false ? 0 : 1;
325
326 lv_label_refr_text(label); /*Refresh the text because the potential colo codes in text needs to
327 be hided or revealed*/
328 }
329
330 /**
331 * Set the label to draw (or not draw) background specified in its style's body
332 * @param label pointer to a label object
333 * @param en true: draw body; false: don't draw body
334 */
lv_label_set_body_draw(lv_obj_t * label,bool en)335 void lv_label_set_body_draw(lv_obj_t * label, bool en)
336 {
337 lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
338 if(ext->body_draw == en) return;
339
340 ext->body_draw = en == false ? 0 : 1;
341
342 lv_obj_refresh_ext_draw_pad(label);
343
344 lv_obj_invalidate(label);
345 }
346
347 /**
348 * Set the label's animation speed in LV_LABEL_LONG_SROLL/SCROLL_CIRC modes
349 * @param label pointer to a label object
350 * @param anim_speed speed of animation in px/sec unit
351 */
lv_label_set_anim_speed(lv_obj_t * label,uint16_t anim_speed)352 void lv_label_set_anim_speed(lv_obj_t * label, uint16_t anim_speed)
353 {
354 #if LV_USE_ANIMATION
355 lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
356 if(ext->anim_speed == anim_speed) return;
357
358 ext->anim_speed = anim_speed;
359
360 if(ext->long_mode == LV_LABEL_LONG_SROLL || ext->long_mode == LV_LABEL_LONG_SROLL_CIRC) {
361 lv_label_refr_text(label);
362 }
363 #else
364 (void)label; /*Unused*/
365 (void)anim_speed; /*Unused*/
366 #endif
367 }
368
lv_label_set_text_sel_start(lv_obj_t * label,uint16_t index)369 void lv_label_set_text_sel_start(lv_obj_t * label, uint16_t index)
370 {
371 #if LV_LABEL_TEXT_SEL
372 lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
373 ext->txt_sel_start = index;
374 lv_obj_invalidate(label);
375 #else
376 (void)label; /*Unused*/
377 (void)index; /*Unused*/
378 #endif
379 }
380
lv_label_set_text_sel_end(lv_obj_t * label,uint16_t index)381 void lv_label_set_text_sel_end(lv_obj_t * label, uint16_t index)
382 {
383 #if LV_LABEL_TEXT_SEL
384 lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
385 ext->txt_sel_end = index;
386 lv_obj_invalidate(label);
387 #else
388 (void)label; /*Unused*/
389 (void)index; /*Unused*/
390 #endif
391 }
392
393 /*=====================
394 * Getter functions
395 *====================*/
396
397 /**
398 * Get the text of a label
399 * @param label pointer to a label object
400 * @return the text of the label
401 */
lv_label_get_text(const lv_obj_t * label)402 char * lv_label_get_text(const lv_obj_t * label)
403 {
404 lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
405
406 return ext->text;
407 }
408
409 /**
410 * Get the long mode of a label
411 * @param label pointer to a label object
412 * @return the long mode
413 */
lv_label_get_long_mode(const lv_obj_t * label)414 lv_label_long_mode_t lv_label_get_long_mode(const lv_obj_t * label)
415 {
416 lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
417 return ext->long_mode;
418 }
419
420 /**
421 * Get the align attribute
422 * @param label pointer to a label object
423 * @return LV_LABEL_ALIGN_LEFT or LV_LABEL_ALIGN_CENTER
424 */
lv_label_get_align(const lv_obj_t * label)425 lv_label_align_t lv_label_get_align(const lv_obj_t * label)
426 {
427 lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
428 return ext->align;
429 }
430
431 /**
432 * Get the recoloring attribute
433 * @param label pointer to a label object
434 * @return true: recoloring is enabled, false: disable
435 */
lv_label_get_recolor(const lv_obj_t * label)436 bool lv_label_get_recolor(const lv_obj_t * label)
437 {
438 lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
439 return ext->recolor == 0 ? false : true;
440 }
441
442 /**
443 * Get the body draw attribute
444 * @param label pointer to a label object
445 * @return true: draw body; false: don't draw body
446 */
lv_label_get_body_draw(const lv_obj_t * label)447 bool lv_label_get_body_draw(const lv_obj_t * label)
448 {
449 lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
450 return ext->body_draw == 0 ? false : true;
451 }
452
453 /**
454 * Get the label's animation speed in LV_LABEL_LONG_ROLL and SCROLL modes
455 * @param label pointer to a label object
456 * @return speed of animation in px/sec unit
457 */
lv_label_get_anim_speed(const lv_obj_t * label)458 uint16_t lv_label_get_anim_speed(const lv_obj_t * label)
459 {
460 #if LV_USE_ANIMATION
461 lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
462 return ext->anim_speed;
463 #else
464 (void)label; /*Unused*/
465 return 0;
466 #endif
467 }
468
469 /**
470 * Get the relative x and y coordinates of a letter
471 * @param label pointer to a label object
472 * @param index index of the letter [0 ... text length]. Expressed in character index, not byte
473 * index (different in UTF-8)
474 * @param pos store the result here (E.g. index = 0 gives 0;0 coordinates)
475 */
lv_label_get_letter_pos(const lv_obj_t * label,uint16_t index,lv_point_t * pos)476 void lv_label_get_letter_pos(const lv_obj_t * label, uint16_t index, lv_point_t * pos)
477 {
478 const char * txt = lv_label_get_text(label);
479 lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
480 uint32_t line_start = 0;
481 uint32_t new_line_start = 0;
482 lv_coord_t max_w = lv_obj_get_width(label);
483 const lv_style_t * style = lv_obj_get_style(label);
484 const lv_font_t * font = style->text.font;
485 uint8_t letter_height = lv_font_get_line_height(font);
486 lv_coord_t y = 0;
487 lv_txt_flag_t flag = LV_TXT_FLAG_NONE;
488
489 if(ext->recolor != 0) flag |= LV_TXT_FLAG_RECOLOR;
490 if(ext->expand != 0) flag |= LV_TXT_FLAG_EXPAND;
491 if(ext->align == LV_LABEL_ALIGN_CENTER) flag |= LV_TXT_FLAG_CENTER;
492
493 /*If the width will be expanded the set the max length to very big */
494 if(ext->long_mode == LV_LABEL_LONG_EXPAND) {
495 max_w = LV_COORD_MAX;
496 }
497
498 index = lv_txt_encoded_get_byte_id(txt, index);
499
500 /*Search the line of the index letter */;
501 while(txt[new_line_start] != '\0') {
502 new_line_start += lv_txt_get_next_line(&txt[line_start], font, style->text.letter_space, max_w, flag);
503 if(index < new_line_start || txt[new_line_start] == '\0')
504 break; /*The line of 'index' letter begins at 'line_start'*/
505
506 y += letter_height + style->text.line_space;
507 line_start = new_line_start;
508 }
509
510 /*If the last character is line break then go to the next line*/
511 if(index > 0) {
512 if((txt[index - 1] == '\n' || txt[index - 1] == '\r') && txt[index] == '\0') {
513 y += letter_height + style->text.line_space;
514 line_start = index;
515 }
516 }
517
518 /*Calculate the x coordinate*/
519 lv_coord_t x = lv_txt_get_width(&txt[line_start], index - line_start, font, style->text.letter_space, flag);
520
521 if(index != line_start) x += style->text.letter_space;
522
523 if(ext->align == LV_LABEL_ALIGN_CENTER) {
524 lv_coord_t line_w;
525 line_w = lv_txt_get_width(&txt[line_start], new_line_start - line_start, font, style->text.letter_space, flag);
526 x += lv_obj_get_width(label) / 2 - line_w / 2;
527
528 } else if(ext->align == LV_LABEL_ALIGN_RIGHT) {
529 lv_coord_t line_w;
530 line_w = lv_txt_get_width(&txt[line_start], new_line_start - line_start, font, style->text.letter_space, flag);
531
532 x += lv_obj_get_width(label) - line_w;
533 }
534 pos->x = x;
535 pos->y = y;
536 }
537
538 /**
539 * Get the index of letter on a relative point of a label
540 * @param label pointer to label object
541 * @param pos pointer to point with coordinates on a the label
542 * @return the index of the letter on the 'pos_p' point (E.g. on 0;0 is the 0. letter)
543 * Expressed in character index and not byte index (different in UTF-8)
544 */
lv_label_get_letter_on(const lv_obj_t * label,lv_point_t * pos)545 uint16_t lv_label_get_letter_on(const lv_obj_t * label, lv_point_t * pos)
546 {
547 const char * txt = lv_label_get_text(label);
548 lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
549 uint32_t line_start = 0;
550 uint32_t new_line_start = 0;
551 lv_coord_t max_w = lv_obj_get_width(label);
552 const lv_style_t * style = lv_obj_get_style(label);
553 const lv_font_t * font = style->text.font;
554 uint8_t letter_height = lv_font_get_line_height(font);
555 lv_coord_t y = 0;
556 lv_txt_flag_t flag = LV_TXT_FLAG_NONE;
557
558 if(ext->recolor != 0) flag |= LV_TXT_FLAG_RECOLOR;
559 if(ext->expand != 0) flag |= LV_TXT_FLAG_EXPAND;
560 if(ext->align == LV_LABEL_ALIGN_CENTER) flag |= LV_TXT_FLAG_CENTER;
561
562 /*If the width will be expanded set the max length to very big */
563 if(ext->long_mode == LV_LABEL_LONG_EXPAND) {
564 max_w = LV_COORD_MAX;
565 }
566
567 /*Search the line of the index letter */;
568 while(txt[line_start] != '\0') {
569 new_line_start += lv_txt_get_next_line(&txt[line_start], font, style->text.letter_space, max_w, flag);
570
571 if(pos->y <= y + letter_height) break; /*The line is found (stored in 'line_start')*/
572 y += letter_height + style->text.line_space;
573
574 line_start = new_line_start;
575 }
576
577 /*Calculate the x coordinate*/
578 lv_coord_t x = 0;
579 if(ext->align == LV_LABEL_ALIGN_CENTER) {
580 lv_coord_t line_w;
581 line_w = lv_txt_get_width(&txt[line_start], new_line_start - line_start, font, style->text.letter_space, flag);
582 x += lv_obj_get_width(label) / 2 - line_w / 2;
583 }
584
585 lv_txt_cmd_state_t cmd_state = LV_TXT_CMD_STATE_WAIT;
586
587 uint32_t i = line_start;
588 uint32_t i_current = i;
589 uint32_t letter;
590 uint32_t letter_next;
591
592 if(new_line_start > 0) {
593 while(i <= new_line_start - 1) {
594 /* Get the current letter.
595 * Be careful 'i' already points to the next character*/
596 letter = lv_txt_encoded_next(txt, &i);
597
598 /*Get the next letter too for kerning*/
599 letter_next = lv_txt_encoded_next(&txt[i], NULL);
600
601 /*Handle the recolor command*/
602 if((flag & LV_TXT_FLAG_RECOLOR) != 0) {
603 if(lv_txt_is_cmd(&cmd_state, txt[i]) != false) {
604 continue; /*Skip the letter is it is part of a command*/
605 }
606 }
607
608 x += lv_font_get_glyph_width(font, letter, letter_next);
609 if(pos->x < x) {
610 i = i_current;
611 break;
612 }
613 x += style->text.letter_space;
614 i_current = i;
615 }
616 }
617
618 return lv_encoded_get_char_id(txt, i);
619 }
620
621 /**
622 * @brief Get the selection start index.
623 * @param label pointer to a label object.
624 * @return selection start index. `LV_LABEL_TXT_SEL_OFF` if nothing is selected.
625 */
lv_label_get_text_sel_start(const lv_obj_t * label)626 uint16_t lv_label_get_text_sel_start(const lv_obj_t * label)
627 {
628 #if LV_LABEL_TEXT_SEL
629 lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
630 return ext->txt_sel_start;
631
632 #else
633 (void)label; /*Unused*/
634 return LV_LABEL_TEXT_SEL_OFF;
635 #endif
636 }
637
638 /**
639 * @brief Get the selection end index.
640 * @param label pointer to a label object.
641 * @return selection end index. `LV_LABEL_TXT_SEL_OFF` if nothing is selected.
642 */
lv_label_get_text_sel_end(const lv_obj_t * label)643 uint16_t lv_label_get_text_sel_end(const lv_obj_t * label)
644 {
645 #if LV_LABEL_TEXT_SEL
646 lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
647 return ext->txt_sel_end;
648 #else
649 (void)label; /*Unused*/
650 return LV_LABEL_TEXT_SEL_OFF;
651 #endif
652 }
653
654 /**
655 * Check if a character is drawn under a point.
656 * @param label Label object
657 * @param pos Point to check for characte under
658 * @return whether a character is drawn under the point
659 */
lv_label_is_char_under_pos(const lv_obj_t * label,lv_point_t * pos)660 bool lv_label_is_char_under_pos(const lv_obj_t * label, lv_point_t * pos)
661 {
662 const char * txt = lv_label_get_text(label);
663 lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
664 uint32_t line_start = 0;
665 uint32_t new_line_start = 0;
666 lv_coord_t max_w = lv_obj_get_width(label);
667 const lv_style_t * style = lv_obj_get_style(label);
668 const lv_font_t * font = style->text.font;
669 uint8_t letter_height = lv_font_get_line_height(font);
670 lv_coord_t y = 0;
671 lv_txt_flag_t flag = LV_TXT_FLAG_NONE;
672
673 if(ext->recolor != 0) flag |= LV_TXT_FLAG_RECOLOR;
674 if(ext->expand != 0) flag |= LV_TXT_FLAG_EXPAND;
675 if(ext->align == LV_LABEL_ALIGN_CENTER) flag |= LV_TXT_FLAG_CENTER;
676
677 /*If the width will be expanded set the max length to very big */
678 if(ext->long_mode == LV_LABEL_LONG_EXPAND) {
679 max_w = LV_COORD_MAX;
680 }
681
682 /*Search the line of the index letter */;
683 while(txt[line_start] != '\0') {
684 new_line_start += lv_txt_get_next_line(&txt[line_start], font, style->text.letter_space, max_w, flag);
685
686 if(pos->y <= y + letter_height) break; /*The line is found (stored in 'line_start')*/
687 y += letter_height + style->text.line_space;
688
689 line_start = new_line_start;
690 }
691
692 /*Calculate the x coordinate*/
693 lv_coord_t x = 0;
694 lv_coord_t last_x = 0;
695 if(ext->align == LV_LABEL_ALIGN_CENTER) {
696 lv_coord_t line_w;
697 line_w = lv_txt_get_width(&txt[line_start], new_line_start - line_start, font, style->text.letter_space, flag);
698 x += lv_obj_get_width(label) / 2 - line_w / 2;
699 }
700
701 lv_txt_cmd_state_t cmd_state = LV_TXT_CMD_STATE_WAIT;
702
703 uint32_t i = line_start;
704 uint32_t i_current = i;
705 uint32_t letter = '\0';
706 uint32_t letter_next = '\0';
707
708 if(new_line_start > 0) {
709 while(i <= new_line_start - 1) {
710 /* Get the current letter
711 * Be careful 'i' already points to the next character */
712 letter = lv_txt_encoded_next(txt, &i);
713
714 /*Get the next letter for kerning*/
715 letter_next = lv_txt_encoded_next(&txt[i], NULL);
716
717 /*Handle the recolor command*/
718 if((flag & LV_TXT_FLAG_RECOLOR) != 0) {
719 if(lv_txt_is_cmd(&cmd_state, txt[i]) != false) {
720 continue; /*Skip the letter is it is part of a command*/
721 }
722 }
723 last_x = x;
724 x += lv_font_get_glyph_width(font, letter, letter_next);
725 if(pos->x < x) {
726 i = i_current;
727 break;
728 }
729 x += style->text.letter_space;
730 i_current = i;
731 }
732 }
733
734 int32_t max_diff = lv_font_get_glyph_width(font, letter, letter_next) + style->text.letter_space + 1;
735 return (pos->x >= (last_x - style->text.letter_space) && pos->x <= (last_x + max_diff));
736 }
737
738 /*=====================
739 * Other functions
740 *====================*/
741
742 /**
743 * Insert a text to the label. The label text can not be static.
744 * @param label pointer to a label object
745 * @param pos character index to insert. Expressed in character index and not byte index (Different
746 * in UTF-8) 0: before first char. LV_LABEL_POS_LAST: after last char.
747 * @param txt pointer to the text to insert
748 */
lv_label_ins_text(lv_obj_t * label,uint32_t pos,const char * txt)749 void lv_label_ins_text(lv_obj_t * label, uint32_t pos, const char * txt)
750 {
751 lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
752
753 /*Can not append to static text*/
754 if(ext->static_txt != 0) return;
755
756 lv_obj_invalidate(label);
757
758 /*Allocate space for the new text*/
759 uint32_t old_len = strlen(ext->text);
760 uint32_t ins_len = strlen(txt);
761 uint32_t new_len = ins_len + old_len;
762 ext->text = lv_mem_realloc(ext->text, new_len + 1);
763 lv_mem_assert(ext->text);
764 if(ext->text == NULL) return;
765
766 if(pos == LV_LABEL_POS_LAST) {
767 pos = lv_txt_get_encoded_length(ext->text);
768 }
769
770 lv_txt_ins(ext->text, pos, txt);
771
772 lv_label_refr_text(label);
773 }
774
775 /**
776 * Delete characters from a label. The label text can not be static.
777 * @param label pointer to a label object
778 * @param pos character index to insert. Expressed in character index and not byte index (Different
779 * in UTF-8) 0: before first char.
780 * @param cnt number of characters to cut
781 */
lv_label_cut_text(lv_obj_t * label,uint32_t pos,uint32_t cnt)782 void lv_label_cut_text(lv_obj_t * label, uint32_t pos, uint32_t cnt)
783 {
784 lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
785
786 /*Can not append to static text*/
787 if(ext->static_txt != 0) return;
788
789 lv_obj_invalidate(label);
790
791 char * label_txt = lv_label_get_text(label);
792 /*Delete the characters*/
793 lv_txt_cut(label_txt, pos, cnt);
794
795 /*Refresh the label*/
796 lv_label_refr_text(label);
797 }
798
799 /**********************
800 * STATIC FUNCTIONS
801 **********************/
802
803 /**
804 * Handle the drawing related tasks of the labels
805 * @param label pointer to a label object
806 * @param mask the object will be drawn only in this area
807 * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
808 * (return 'true' if yes)
809 * LV_DESIGN_DRAW: draw the object (always return 'true')
810 * LV_DESIGN_DRAW_POST: drawing after every children are drawn
811 * @param return true/false, depends on 'mode'
812 */
lv_label_design(lv_obj_t * label,const lv_area_t * mask,lv_design_mode_t mode)813 static bool lv_label_design(lv_obj_t * label, const lv_area_t * mask, lv_design_mode_t mode)
814 {
815 /* A label never covers an area */
816 if(mode == LV_DESIGN_COVER_CHK)
817 return false;
818 else if(mode == LV_DESIGN_DRAW_MAIN) {
819 lv_area_t coords;
820 const lv_style_t * style = lv_obj_get_style(label);
821 lv_opa_t opa_scale = lv_obj_get_opa_scale(label);
822 lv_obj_get_coords(label, &coords);
823
824 #if LV_USE_GROUP
825 lv_group_t * g = lv_obj_get_group(label);
826 if(lv_group_get_focused(g) == label) {
827 lv_draw_rect(&coords, mask, style, opa_scale);
828 }
829 #endif
830
831 lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
832
833 if(ext->body_draw) {
834 lv_area_t bg;
835 lv_obj_get_coords(label, &bg);
836 bg.x1 -= style->body.padding.left;
837 bg.x2 += style->body.padding.right;
838 bg.y1 -= style->body.padding.top;
839 bg.y2 += style->body.padding.bottom;
840
841 lv_draw_rect(&bg, mask, style, lv_obj_get_opa_scale(label));
842 }
843
844 /*TEST: draw a background for the label*/
845 // lv_draw_rect(&label->coords, mask, &lv_style_plain_color, LV_OPA_COVER);
846
847 lv_txt_flag_t flag = LV_TXT_FLAG_NONE;
848 if(ext->recolor != 0) flag |= LV_TXT_FLAG_RECOLOR;
849 if(ext->expand != 0) flag |= LV_TXT_FLAG_EXPAND;
850 if(ext->align == LV_LABEL_ALIGN_CENTER) flag |= LV_TXT_FLAG_CENTER;
851 if(ext->align == LV_LABEL_ALIGN_RIGHT) flag |= LV_TXT_FLAG_RIGHT;
852
853 /* In ROLL mode the CENTER and RIGHT are pointless so remove them.
854 * (In addition they will result mis-alignment is this case)*/
855 if((ext->long_mode == LV_LABEL_LONG_SROLL || ext->long_mode == LV_LABEL_LONG_SROLL_CIRC) &&
856 (ext->align == LV_LABEL_ALIGN_CENTER || ext->align == LV_LABEL_ALIGN_RIGHT)) {
857 lv_point_t size;
858 lv_txt_get_size(&size, ext->text, style->text.font, style->text.letter_space, style->text.line_space,
859 LV_COORD_MAX, flag);
860 if(size.x > lv_obj_get_width(label)) {
861 flag &= ~LV_TXT_FLAG_RIGHT;
862 flag &= ~LV_TXT_FLAG_CENTER;
863 }
864 }
865 #if LV_LABEL_LONG_TXT_HINT
866 lv_draw_label_hint_t * hint = &ext->hint;
867 if(ext->long_mode == LV_LABEL_LONG_SROLL_CIRC || lv_obj_get_height(label) < LV_LABEL_HINT_HEIGHT_LIMIT)
868 hint = NULL;
869
870 #else
871 /*Just for compatibility*/
872 lv_draw_label_hint_t * hint = NULL;
873 #endif
874 lv_draw_label(&coords, mask, style, opa_scale, ext->text, flag, &ext->offset,
875 lv_label_get_text_sel_start(label), lv_label_get_text_sel_end(label), hint);
876
877
878 if(ext->long_mode == LV_LABEL_LONG_SROLL_CIRC) {
879 lv_point_t size;
880 lv_txt_get_size(&size, ext->text, style->text.font, style->text.letter_space, style->text.line_space,
881 LV_COORD_MAX, flag);
882
883 lv_point_t ofs;
884
885 /*Draw the text again next to the original to make an circular effect */
886 if(size.x > lv_obj_get_width(label)) {
887 ofs.x = ext->offset.x + size.x +
888 lv_font_get_glyph_width(style->text.font, ' ', ' ') * LV_LABEL_WAIT_CHAR_COUNT;
889 ofs.y = ext->offset.y;
890
891 lv_draw_label(&coords, mask, style, opa_scale, ext->text, flag, &ofs,
892 lv_label_get_text_sel_start(label), lv_label_get_text_sel_end(label), NULL);
893 }
894
895 /*Draw the text again below the original to make an circular effect */
896 if(size.y > lv_obj_get_height(label)) {
897 ofs.x = ext->offset.x;
898 ofs.y = ext->offset.y + size.y + lv_font_get_line_height(style->text.font);
899 lv_draw_label(&coords, mask, style, opa_scale, ext->text, flag, &ofs,
900 lv_label_get_text_sel_start(label), lv_label_get_text_sel_end(label), NULL);
901 }
902 }
903 }
904 return true;
905 }
906
907 /**
908 * Signal function of the label
909 * @param label pointer to a label object
910 * @param sign a signal type from lv_signal_t enum
911 * @param param pointer to a signal specific variable
912 * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
913 */
lv_label_signal(lv_obj_t * label,lv_signal_t sign,void * param)914 static lv_res_t lv_label_signal(lv_obj_t * label, lv_signal_t sign, void * param)
915 {
916 lv_res_t res;
917
918 /* Include the ancient signal function */
919 res = ancestor_signal(label, sign, param);
920 if(res != LV_RES_OK) return res;
921
922 lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
923 if(sign == LV_SIGNAL_CLEANUP) {
924 if(ext->static_txt == 0) {
925 lv_mem_free(ext->text);
926 ext->text = NULL;
927 }
928 lv_label_dot_tmp_free(label);
929 } else if(sign == LV_SIGNAL_STYLE_CHG) {
930 /*Revert dots for proper refresh*/
931 lv_label_revert_dots(label);
932
933 lv_label_refr_text(label);
934 } else if(sign == LV_SIGNAL_CORD_CHG) {
935 if(lv_area_get_width(&label->coords) != lv_area_get_width(param) ||
936 lv_area_get_height(&label->coords) != lv_area_get_height(param)) {
937 lv_label_revert_dots(label);
938 lv_label_refr_text(label);
939 }
940 } else if(sign == LV_SIGNAL_REFR_EXT_DRAW_PAD) {
941 if(ext->body_draw) {
942 const lv_style_t * style = lv_label_get_style(label, LV_LABEL_STYLE_MAIN);
943
944 label->ext_draw_pad = LV_MATH_MAX(label->ext_draw_pad, style->body.padding.left);
945 label->ext_draw_pad = LV_MATH_MAX(label->ext_draw_pad, style->body.padding.right);
946 label->ext_draw_pad = LV_MATH_MAX(label->ext_draw_pad, style->body.padding.top);
947 label->ext_draw_pad = LV_MATH_MAX(label->ext_draw_pad, style->body.padding.bottom);
948 }
949 } else if(sign == LV_SIGNAL_GET_TYPE) {
950 lv_obj_type_t * buf = param;
951 uint8_t i;
952 for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
953 if(buf->type[i] == NULL) break;
954 }
955 buf->type[i] = "lv_label";
956 }
957
958 return res;
959 }
960
961 /**
962 * Refresh the label with its text stored in its extended data
963 * @param label pointer to a label object
964 */
lv_label_refr_text(lv_obj_t * label)965 static void lv_label_refr_text(lv_obj_t * label)
966 {
967 lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
968
969 if(ext->text == NULL) return;
970 #if LV_LABEL_LONG_TXT_HINT
971 ext->hint.line_start = -1; /*The hint is invalid if the text changes*/
972 #endif
973
974 lv_coord_t max_w = lv_obj_get_width(label);
975 const lv_style_t * style = lv_obj_get_style(label);
976 const lv_font_t * font = style->text.font;
977
978 /*If the width will be expanded set the max length to very big */
979 if(ext->long_mode == LV_LABEL_LONG_EXPAND) {
980 max_w = LV_COORD_MAX;
981 }
982
983 /*Calc. the height and longest line*/
984 lv_point_t size;
985 lv_txt_flag_t flag = LV_TXT_FLAG_NONE;
986 if(ext->recolor != 0) flag |= LV_TXT_FLAG_RECOLOR;
987 if(ext->expand != 0) flag |= LV_TXT_FLAG_EXPAND;
988 lv_txt_get_size(&size, ext->text, font, style->text.letter_space, style->text.line_space, max_w, flag);
989
990 /*Set the full size in expand mode*/
991 if(ext->long_mode == LV_LABEL_LONG_EXPAND) {
992 lv_obj_set_size(label, size.x, size.y);
993 }
994 /*In roll mode keep the size but start offset animations*/
995 else if(ext->long_mode == LV_LABEL_LONG_SROLL) {
996 #if LV_USE_ANIMATION
997 lv_anim_t anim;
998 anim.var = label;
999 anim.repeat = 1;
1000 anim.playback = 1;
1001 anim.start = 0;
1002 anim.ready_cb = NULL;
1003 anim.path_cb = lv_anim_path_linear;
1004 anim.playback_pause =
1005 (((lv_font_get_glyph_width(style->text.font, ' ', ' ') + style->text.letter_space) * 1000) /
1006 ext->anim_speed) *
1007 LV_LABEL_WAIT_CHAR_COUNT;
1008 anim.repeat_pause = anim.playback_pause;
1009 anim.act_time = -anim.playback_pause;
1010
1011 bool hor_anim = false;
1012 if(size.x > lv_obj_get_width(label)) {
1013 anim.end = lv_obj_get_width(label) - size.x;
1014 anim.exec_cb = (lv_anim_exec_xcb_t)lv_label_set_offset_x;
1015 anim.time = lv_anim_speed_to_time(ext->anim_speed, anim.start, anim.end);
1016 lv_anim_create(&anim);
1017 hor_anim = true;
1018 } else {
1019 /*Delete the offset animation if not required*/
1020 lv_anim_del(label, (lv_anim_exec_xcb_t)lv_label_set_offset_x);
1021 ext->offset.x = 0;
1022 }
1023
1024 if(size.y > lv_obj_get_height(label) && hor_anim == false) {
1025 anim.end = lv_obj_get_height(label) - size.y - (lv_font_get_line_height(font));
1026 anim.exec_cb = (lv_anim_exec_xcb_t)lv_label_set_offset_y;
1027
1028 anim.time = lv_anim_speed_to_time(ext->anim_speed, anim.start, anim.end);
1029 lv_anim_create(&anim);
1030 } else {
1031 /*Delete the offset animation if not required*/
1032 lv_anim_del(label, (lv_anim_exec_xcb_t)lv_label_set_offset_y);
1033 ext->offset.y = 0;
1034 }
1035 #endif
1036 }
1037 /*In roll inf. mode keep the size but start offset animations*/
1038 else if(ext->long_mode == LV_LABEL_LONG_SROLL_CIRC) {
1039 #if LV_USE_ANIMATION
1040 lv_anim_t anim;
1041 anim.var = label;
1042 anim.repeat = 1;
1043 anim.playback = 0;
1044 anim.start = 0;
1045 anim.act_time = -(((lv_font_get_glyph_width(style->text.font, ' ', ' ') + style->text.letter_space) * 1000) /
1046 ext->anim_speed) *
1047 LV_LABEL_WAIT_CHAR_COUNT;
1048 anim.ready_cb = NULL;
1049 anim.path_cb = lv_anim_path_linear;
1050 anim.playback_pause = 0;
1051 anim.repeat_pause = 0;
1052
1053 bool hor_anim = false;
1054 if(size.x > lv_obj_get_width(label)) {
1055 anim.end = -size.x - lv_font_get_glyph_width(font, ' ', ' ') * LV_LABEL_WAIT_CHAR_COUNT;
1056 anim.exec_cb = (lv_anim_exec_xcb_t)lv_label_set_offset_x;
1057 anim.time = lv_anim_speed_to_time(ext->anim_speed, anim.start, anim.end);
1058 lv_anim_create(&anim);
1059 hor_anim = true;
1060 } else {
1061 /*Delete the offset animation if not required*/
1062 lv_anim_del(label, (lv_anim_exec_xcb_t)lv_label_set_offset_x);
1063 ext->offset.x = 0;
1064 }
1065
1066 if(size.y > lv_obj_get_height(label) && hor_anim == false) {
1067 anim.end = -size.y - (lv_font_get_line_height(font));
1068 anim.exec_cb = (lv_anim_exec_xcb_t)lv_label_set_offset_y;
1069 anim.time = lv_anim_speed_to_time(ext->anim_speed, anim.start, anim.end);
1070 lv_anim_create(&anim);
1071 } else {
1072 /*Delete the offset animation if not required*/
1073 lv_anim_del(label, (lv_anim_exec_xcb_t)lv_label_set_offset_y);
1074 ext->offset.y = 0;
1075 }
1076 #endif
1077 } else if(ext->long_mode == LV_LABEL_LONG_DOT) {
1078 if(size.y <= lv_obj_get_height(label)) { /*No dots are required, the text is short enough*/
1079 ext->dot_end = LV_LABEL_DOT_END_INV;
1080 } else if(lv_txt_get_encoded_length(ext->text) <= LV_LABEL_DOT_NUM) { /*Don't turn to dots all the characters*/
1081 ext->dot_end = LV_LABEL_DOT_END_INV;
1082 } else {
1083 lv_point_t p;
1084 p.x = lv_obj_get_width(label) -
1085 (lv_font_get_glyph_width(style->text.font, '.', '.') + style->text.letter_space) *
1086 LV_LABEL_DOT_NUM; /*Shrink with dots*/
1087 p.y = lv_obj_get_height(label);
1088 p.y -= p.y %
1089 (lv_font_get_line_height(style->text.font) + style->text.line_space); /*Round down to the last line*/
1090 p.y -= style->text.line_space; /*Trim the last line space*/
1091 uint32_t letter_id = lv_label_get_letter_on(label, &p);
1092
1093 /*Save letters under the dots and replace them with dots*/
1094 uint32_t i;
1095 uint32_t byte_id = lv_txt_encoded_get_byte_id(ext->text, letter_id);
1096 uint32_t byte_id_ori = byte_id;
1097 uint8_t len = 0;
1098 for(i = 0; i <= LV_LABEL_DOT_NUM; i++) {
1099 len += lv_txt_encoded_size(&ext->text[byte_id]);
1100 lv_txt_encoded_next(ext->text, &byte_id);
1101 }
1102
1103 if(lv_label_set_dot_tmp(label, &ext->text[byte_id_ori], len)) {
1104 for(i = 0; i < LV_LABEL_DOT_NUM; i++) {
1105 ext->text[byte_id_ori + i] = '.';
1106 }
1107 ext->text[byte_id_ori + LV_LABEL_DOT_NUM] = '\0';
1108 ext->dot_end = letter_id + LV_LABEL_DOT_NUM;
1109 }
1110 }
1111 }
1112 /*In break mode only the height can change*/
1113 else if(ext->long_mode == LV_LABEL_LONG_BREAK) {
1114 lv_obj_set_height(label, size.y);
1115 }
1116 /*Do not set the size in Clip mode*/
1117 else if(ext->long_mode == LV_LABEL_LONG_CROP) {
1118 /*Do nothing*/
1119 }
1120
1121 lv_obj_invalidate(label);
1122 }
1123
lv_label_revert_dots(lv_obj_t * label)1124 static void lv_label_revert_dots(lv_obj_t * label)
1125 {
1126 lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
1127 if(ext->long_mode != LV_LABEL_LONG_DOT) return;
1128 if(ext->dot_end == LV_LABEL_DOT_END_INV) return;
1129 uint32_t letter_i = ext->dot_end - LV_LABEL_DOT_NUM;
1130 uint32_t byte_i = lv_txt_encoded_get_byte_id(ext->text, letter_i);
1131
1132 /*Restore the characters*/
1133 uint8_t i = 0;
1134 char * dot_tmp = lv_label_get_dot_tmp(label);
1135 while(ext->text[byte_i + i] != '\0') {
1136 ext->text[byte_i + i] = dot_tmp[i];
1137 i++;
1138 }
1139 ext->text[byte_i + i] = dot_tmp[i];
1140 lv_label_dot_tmp_free(label);
1141
1142 ext->dot_end = LV_LABEL_DOT_END_INV;
1143 }
1144
1145 #if LV_USE_ANIMATION
lv_label_set_offset_x(lv_obj_t * label,lv_coord_t x)1146 static void lv_label_set_offset_x(lv_obj_t * label, lv_coord_t x)
1147 {
1148 lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
1149 ext->offset.x = x;
1150 lv_obj_invalidate(label);
1151 }
1152
lv_label_set_offset_y(lv_obj_t * label,lv_coord_t y)1153 static void lv_label_set_offset_y(lv_obj_t * label, lv_coord_t y)
1154 {
1155 lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
1156 ext->offset.y = y;
1157 lv_obj_invalidate(label);
1158 }
1159 #endif
1160
1161 /**
1162 * Store `len` characters from `data`. Allocates space if necessary.
1163 *
1164 * @param label pointer to label object
1165 * @param len Number of characters to store.
1166 * @return true on success.
1167 */
lv_label_set_dot_tmp(lv_obj_t * label,char * data,uint16_t len)1168 static bool lv_label_set_dot_tmp(lv_obj_t * label, char * data, uint16_t len)
1169 {
1170 lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
1171 lv_label_dot_tmp_free(label); /* Deallocate any existing space */
1172 if(len > sizeof(char *)) {
1173 /* Memory needs to be allocated. Allocates an additional byte
1174 * for a NULL-terminator so it can be copied. */
1175 ext->dot.tmp_ptr = lv_mem_alloc(len + 1);
1176 if(ext->dot.tmp_ptr == NULL) {
1177 LV_LOG_ERROR("Failed to allocate memory for dot_tmp_ptr");
1178 return false;
1179 }
1180 memcpy(ext->dot.tmp_ptr, data, len);
1181 ext->dot.tmp_ptr[len] = '\0';
1182 ext->dot_tmp_alloc = true;
1183 } else {
1184 /* Characters can be directly stored in object */
1185 ext->dot_tmp_alloc = false;
1186 memcpy(ext->dot.tmp, data, len);
1187 }
1188 return true;
1189 }
1190
1191 /**
1192 * Get the stored dot_tmp characters
1193 * @param label pointer to label object
1194 * @return char pointer to a stored characters. Is *not* necessarily NULL-terminated.
1195 */
lv_label_get_dot_tmp(lv_obj_t * label)1196 static char * lv_label_get_dot_tmp(lv_obj_t * label)
1197 {
1198 lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
1199 if(ext->dot_tmp_alloc) {
1200 return ext->dot.tmp_ptr;
1201 } else {
1202 return ext->dot.tmp;
1203 }
1204 }
1205
1206 /**
1207 * Free the dot_tmp_ptr field if it was previously allocated.
1208 * Always clears the field
1209 * @param label pointer to label object.
1210 */
lv_label_dot_tmp_free(lv_obj_t * label)1211 static void lv_label_dot_tmp_free(lv_obj_t * label)
1212 {
1213 lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
1214 if(ext->dot_tmp_alloc && ext->dot.tmp_ptr) {
1215 lv_mem_free(ext->dot.tmp_ptr);
1216 }
1217 ext->dot_tmp_alloc = false;
1218 ext->dot.tmp_ptr = NULL;
1219 }
1220
1221 #endif
1222