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