1 /**
2  * @file lv_label.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_label.h"
10 #if LV_USE_LABEL != 0
11 #include "../core/lv_obj.h"
12 #include "../misc/lv_assert.h"
13 #include "../core/lv_group.h"
14 #include "../draw/lv_draw.h"
15 #include "../misc/lv_color.h"
16 #include "../misc/lv_math.h"
17 #include "../misc/lv_bidi.h"
18 #include "../misc/lv_txt_ap.h"
19 #include "../misc/lv_printf.h"
20 
21 /*********************
22  *      DEFINES
23  *********************/
24 #define MY_CLASS &lv_label_class
25 
26 #define LV_LABEL_DEF_SCROLL_SPEED   (lv_disp_get_dpi(lv_obj_get_disp(obj)) / 3)
27 #define LV_LABEL_SCROLL_DELAY       300
28 #define LV_LABEL_DOT_END_INV 0xFFFFFFFF
29 #define LV_LABEL_HINT_HEIGHT_LIMIT 1024 /*Enable "hint" to buffer info about labels larger than this. (Speed up drawing)*/
30 
31 /**********************
32  *      TYPEDEFS
33  **********************/
34 
35 /**********************
36  *  STATIC PROTOTYPES
37  **********************/
38 static void lv_label_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
39 static void lv_label_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
40 static void lv_label_event(const lv_obj_class_t * class_p, lv_event_t * e);
41 static void draw_main(lv_event_t * e);
42 
43 static void lv_label_refr_text(lv_obj_t * obj);
44 static void lv_label_revert_dots(lv_obj_t * label);
45 
46 static bool lv_label_set_dot_tmp(lv_obj_t * label, char * data, uint32_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 static void set_ofs_x_anim(void * obj, int32_t v);
50 static void set_ofs_y_anim(void * obj, int32_t v);
51 
52 /**********************
53  *  STATIC VARIABLES
54  **********************/
55 const lv_obj_class_t lv_label_class = {
56     .constructor_cb = lv_label_constructor,
57     .destructor_cb = lv_label_destructor,
58     .event_cb = lv_label_event,
59     .width_def = LV_SIZE_CONTENT,
60     .height_def = LV_SIZE_CONTENT,
61     .instance_size = sizeof(lv_label_t),
62     .base_class = &lv_obj_class
63 };
64 
65 /**********************
66  *      MACROS
67  **********************/
68 
69 /**********************
70  *   GLOBAL FUNCTIONS
71  **********************/
72 
lv_label_create(lv_obj_t * parent)73 lv_obj_t * lv_label_create(lv_obj_t * parent)
74 {
75     LV_LOG_INFO("begin");
76     lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
77     lv_obj_class_init_obj(obj);
78     return obj;
79 }
80 
81 /*=====================
82  * Setter functions
83  *====================*/
84 
lv_label_set_text(lv_obj_t * obj,const char * text)85 void lv_label_set_text(lv_obj_t * obj, const char * text)
86 {
87     LV_ASSERT_OBJ(obj, MY_CLASS);
88     lv_label_t * label = (lv_label_t *)obj;
89 
90     lv_obj_invalidate(obj);
91 
92     /*If text is NULL then just refresh with the current text*/
93     if(text == NULL) text = label->text;
94 
95     if(label->text == text && label->static_txt == 0) {
96         /*If set its own text then reallocate it (maybe its size changed)*/
97 #if LV_USE_ARABIC_PERSIAN_CHARS
98         /*Get the size of the text and process it*/
99         size_t len = _lv_txt_ap_calc_bytes_cnt(text);
100 
101         label->text = lv_mem_realloc(label->text, len);
102         LV_ASSERT_MALLOC(label->text);
103         if(label->text == NULL) return;
104 
105         _lv_txt_ap_proc(label->text, label->text);
106 #else
107         label->text = lv_mem_realloc(label->text, strlen(label->text) + 1);
108 #endif
109 
110         LV_ASSERT_MALLOC(label->text);
111         if(label->text == NULL) return;
112     }
113     else {
114         /*Free the old text*/
115         if(label->text != NULL && label->static_txt == 0) {
116             lv_mem_free(label->text);
117             label->text = NULL;
118         }
119 
120 #if LV_USE_ARABIC_PERSIAN_CHARS
121         /*Get the size of the text and process it*/
122         size_t len = _lv_txt_ap_calc_bytes_cnt(text);
123 
124         label->text = lv_mem_alloc(len);
125         LV_ASSERT_MALLOC(label->text);
126         if(label->text == NULL) return;
127 
128         _lv_txt_ap_proc(text, label->text);
129 #else
130         /*Get the size of the text*/
131         size_t len = strlen(text) + 1;
132 
133         /*Allocate space for the new text*/
134         label->text = lv_mem_alloc(len);
135         LV_ASSERT_MALLOC(label->text);
136         if(label->text == NULL) return;
137         strcpy(label->text, text);
138 #endif
139 
140         /*Now the text is dynamically allocated*/
141         label->static_txt = 0;
142     }
143 
144     lv_label_refr_text(obj);
145 }
146 
lv_label_set_text_fmt(lv_obj_t * obj,const char * fmt,...)147 void lv_label_set_text_fmt(lv_obj_t * obj, const char * fmt, ...)
148 {
149     LV_ASSERT_OBJ(obj, MY_CLASS);
150     LV_ASSERT_NULL(fmt);
151 
152     lv_obj_invalidate(obj);
153     lv_label_t * label = (lv_label_t *)obj;
154 
155     /*If text is NULL then refresh*/
156     if(fmt == NULL) {
157         lv_label_refr_text(obj);
158         return;
159     }
160 
161     if(label->text != NULL && label->static_txt == 0) {
162         lv_mem_free(label->text);
163         label->text = NULL;
164     }
165 
166     va_list args;
167     va_start(args, fmt);
168     label->text = _lv_txt_set_text_vfmt(fmt, args);
169     va_end(args);
170     label->static_txt = 0; /*Now the text is dynamically allocated*/
171 
172     lv_label_refr_text(obj);
173 }
174 
lv_label_set_text_static(lv_obj_t * obj,const char * text)175 void lv_label_set_text_static(lv_obj_t * obj, const char * text)
176 {
177     LV_ASSERT_OBJ(obj, MY_CLASS);
178     lv_label_t * label = (lv_label_t *)obj;
179 
180     if(label->static_txt == 0 && label->text != NULL) {
181         lv_mem_free(label->text);
182         label->text = NULL;
183     }
184 
185     if(text != NULL) {
186         label->static_txt = 1;
187         label->text       = (char *)text;
188     }
189 
190     lv_label_refr_text(obj);
191 }
192 
lv_label_set_long_mode(lv_obj_t * obj,lv_label_long_mode_t long_mode)193 void lv_label_set_long_mode(lv_obj_t * obj, lv_label_long_mode_t long_mode)
194 {
195     LV_ASSERT_OBJ(obj, MY_CLASS);
196 
197     lv_label_t * label = (lv_label_t *)obj;
198 
199     /*Delete the old animation (if exists)*/
200     lv_anim_del(obj, set_ofs_x_anim);
201     lv_anim_del(obj, set_ofs_y_anim);
202     label->offset.x = 0;
203     label->offset.y = 0;
204 
205     if(long_mode == LV_LABEL_LONG_SCROLL || long_mode == LV_LABEL_LONG_SCROLL_CIRCULAR || long_mode == LV_LABEL_LONG_CLIP)
206         label->expand = 1;
207     else
208         label->expand = 0;
209 
210     /*Restore the character under the dots*/
211     if(label->long_mode == LV_LABEL_LONG_DOT && label->dot_end != LV_LABEL_DOT_END_INV) {
212         lv_label_revert_dots(obj);
213     }
214 
215     label->long_mode = long_mode;
216     lv_label_refr_text(obj);
217 }
218 
lv_label_set_recolor(lv_obj_t * obj,bool en)219 void lv_label_set_recolor(lv_obj_t * obj, bool en)
220 {
221     LV_ASSERT_OBJ(obj, MY_CLASS);
222 
223     lv_label_t * label = (lv_label_t *)obj;
224     if(label->recolor == en) return;
225 
226     label->recolor = en == false ? 0 : 1;
227 
228     /*Refresh the text because the potential color codes in text needs to be hidden or revealed*/
229     lv_label_refr_text(obj);
230 }
231 
lv_label_set_text_sel_start(lv_obj_t * obj,uint32_t index)232 void lv_label_set_text_sel_start(lv_obj_t * obj, uint32_t index)
233 {
234     LV_ASSERT_OBJ(obj, MY_CLASS);
235 
236 #if LV_LABEL_TEXT_SELECTION
237     lv_label_t * label = (lv_label_t *)obj;
238     label->sel_start   = index;
239     lv_obj_invalidate(obj);
240 #else
241     LV_UNUSED(obj);    /*Unused*/
242     LV_UNUSED(index);  /*Unused*/
243 #endif
244 }
245 
lv_label_set_text_sel_end(lv_obj_t * obj,uint32_t index)246 void lv_label_set_text_sel_end(lv_obj_t * obj, uint32_t index)
247 {
248     LV_ASSERT_OBJ(obj, MY_CLASS);
249 
250 #if LV_LABEL_TEXT_SELECTION
251     lv_label_t * label = (lv_label_t *)obj;
252     label->sel_end     = index;
253     lv_obj_invalidate(obj);
254 #else
255     LV_UNUSED(obj);   /*Unused*/
256     LV_UNUSED(index); /*Unused*/
257 #endif
258 }
259 
260 /*=====================
261  * Getter functions
262  *====================*/
263 
lv_label_get_text(const lv_obj_t * obj)264 char * lv_label_get_text(const lv_obj_t * obj)
265 {
266     LV_ASSERT_OBJ(obj, MY_CLASS);
267     lv_label_t * label = (lv_label_t *)obj;
268     return label->text;
269 }
270 
lv_label_get_long_mode(const lv_obj_t * obj)271 lv_label_long_mode_t lv_label_get_long_mode(const lv_obj_t * obj)
272 {
273     LV_ASSERT_OBJ(obj, MY_CLASS);
274     lv_label_t * label = (lv_label_t *)obj;
275     return label->long_mode;
276 }
277 
lv_label_get_recolor(const lv_obj_t * obj)278 bool lv_label_get_recolor(const lv_obj_t * obj)
279 {
280     LV_ASSERT_OBJ(obj, MY_CLASS);
281 
282     lv_label_t * label = (lv_label_t *)obj;
283     return label->recolor == 0 ? false : true;
284 }
285 
lv_label_get_letter_pos(const lv_obj_t * obj,uint32_t char_id,lv_point_t * pos)286 void lv_label_get_letter_pos(const lv_obj_t * obj, uint32_t char_id, lv_point_t * pos)
287 {
288     LV_ASSERT_OBJ(obj, MY_CLASS);
289     LV_ASSERT_NULL(pos);
290 
291     lv_label_t * label = (lv_label_t *)obj;
292     const char * txt         = lv_label_get_text(obj);
293     lv_text_align_t align = lv_obj_calculate_style_text_align(obj, LV_PART_MAIN, txt);
294 
295     if(txt[0] == '\0') {
296         pos->y = 0;
297         switch(align) {
298             case LV_TEXT_ALIGN_LEFT:
299                 pos->x = 0;
300                 break;
301             case LV_TEXT_ALIGN_RIGHT:
302                 pos->x = lv_obj_get_content_width(obj);
303                 break;
304             case LV_TEXT_ALIGN_CENTER:
305                 pos->x = lv_obj_get_content_width(obj) / 2;
306                 break;
307         }
308         return;
309     }
310 
311     lv_area_t txt_coords;
312     lv_obj_get_content_coords(obj, &txt_coords);
313 
314     uint32_t line_start      = 0;
315     uint32_t new_line_start  = 0;
316     lv_coord_t max_w         = lv_area_get_width(&txt_coords);
317     const lv_font_t * font   = lv_obj_get_style_text_font(obj, LV_PART_MAIN);
318     lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN);
319     lv_coord_t letter_space = lv_obj_get_style_text_letter_space(obj, LV_PART_MAIN);
320     lv_coord_t letter_height    = lv_font_get_line_height(font);
321     lv_coord_t y             = 0;
322     lv_text_flag_t flag       = LV_TEXT_FLAG_NONE;
323 
324     if(label->recolor != 0) flag |= LV_TEXT_FLAG_RECOLOR;
325     if(label->expand != 0) flag |= LV_TEXT_FLAG_EXPAND;
326     if(lv_obj_get_style_width(obj, LV_PART_MAIN) == LV_SIZE_CONTENT && !obj->w_layout) flag |= LV_TEXT_FLAG_FIT;
327 
328     uint32_t byte_id = _lv_txt_encoded_get_byte_id(txt, char_id);
329 
330     /*Search the line of the index letter*/;
331     while(txt[new_line_start] != '\0') {
332         new_line_start += _lv_txt_get_next_line(&txt[line_start], font, letter_space, max_w, NULL, flag);
333         if(byte_id < new_line_start || txt[new_line_start] == '\0')
334             break; /*The line of 'index' letter begins at 'line_start'*/
335 
336         y += letter_height + line_space;
337         line_start = new_line_start;
338     }
339 
340     /*If the last character is line break then go to the next line*/
341     if(byte_id > 0) {
342         if((txt[byte_id - 1] == '\n' || txt[byte_id - 1] == '\r') && txt[byte_id] == '\0') {
343             y += letter_height + line_space;
344             line_start = byte_id;
345         }
346     }
347 
348     const char * bidi_txt;
349     uint32_t visual_byte_pos;
350 #if LV_USE_BIDI
351     lv_base_dir_t base_dir = lv_obj_get_style_base_dir(obj, LV_PART_MAIN);
352     if(base_dir == LV_BASE_DIR_AUTO) base_dir = _lv_bidi_detect_base_dir(txt);
353 
354     char * mutable_bidi_txt = NULL;
355     /*Handle Bidi*/
356     if(new_line_start == byte_id) {
357         visual_byte_pos = base_dir == LV_BASE_DIR_RTL ? 0 : byte_id - line_start;
358         bidi_txt = &txt[line_start];
359     }
360     else {
361         uint32_t line_char_id = _lv_txt_encoded_get_char_id(&txt[line_start], byte_id - line_start);
362 
363         bool is_rtl;
364         uint32_t visual_char_pos = _lv_bidi_get_visual_pos(&txt[line_start], &mutable_bidi_txt, new_line_start - line_start,
365                                                            base_dir, line_char_id, &is_rtl);
366         bidi_txt = mutable_bidi_txt;
367         if(is_rtl) visual_char_pos++;
368 
369         visual_byte_pos = _lv_txt_encoded_get_byte_id(bidi_txt, visual_char_pos);
370     }
371 #else
372     bidi_txt = &txt[line_start];
373     visual_byte_pos = byte_id - line_start;
374 #endif
375 
376     /*Calculate the x coordinate*/
377     lv_coord_t x = lv_txt_get_width(bidi_txt, visual_byte_pos, font, letter_space, flag);
378     if(char_id != line_start) x += letter_space;
379 
380     if(align == LV_TEXT_ALIGN_CENTER) {
381         lv_coord_t line_w;
382         line_w = lv_txt_get_width(bidi_txt, new_line_start - line_start, font, letter_space, flag);
383         x += lv_area_get_width(&txt_coords) / 2 - line_w / 2;
384 
385     }
386     else if(align == LV_TEXT_ALIGN_RIGHT) {
387         lv_coord_t line_w;
388         line_w = lv_txt_get_width(bidi_txt, new_line_start - line_start, font, letter_space, flag);
389 
390         x += lv_area_get_width(&txt_coords) - line_w;
391     }
392     pos->x = x;
393     pos->y = y;
394 
395 #if LV_USE_BIDI
396     if(mutable_bidi_txt) lv_mem_buf_release(mutable_bidi_txt);
397 #endif
398 }
399 
lv_label_get_letter_on(const lv_obj_t * obj,lv_point_t * pos_in)400 uint32_t lv_label_get_letter_on(const lv_obj_t * obj, lv_point_t * pos_in)
401 {
402     LV_ASSERT_OBJ(obj, MY_CLASS);
403     LV_ASSERT_NULL(pos_in);
404     lv_label_t * label = (lv_label_t *)obj;
405 
406     lv_point_t pos;
407     pos.x = pos_in->x - lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
408     pos.y = pos_in->y - lv_obj_get_style_pad_top(obj, LV_PART_MAIN);
409 
410     lv_area_t txt_coords;
411     lv_obj_get_content_coords(obj, &txt_coords);
412     const char * txt         = lv_label_get_text(obj);
413     uint32_t line_start      = 0;
414     uint32_t new_line_start  = 0;
415     lv_coord_t max_w         = lv_area_get_width(&txt_coords);
416     const lv_font_t * font   = lv_obj_get_style_text_font(obj, LV_PART_MAIN);
417     lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN);
418     lv_coord_t letter_space = lv_obj_get_style_text_letter_space(obj, LV_PART_MAIN);
419     lv_coord_t letter_height    = lv_font_get_line_height(font);
420     lv_coord_t y             = 0;
421     lv_text_flag_t flag       = LV_TEXT_FLAG_NONE;
422     uint32_t logical_pos;
423     char * bidi_txt;
424 
425     if(label->recolor != 0) flag |= LV_TEXT_FLAG_RECOLOR;
426     if(label->expand != 0) flag |= LV_TEXT_FLAG_EXPAND;
427     if(lv_obj_get_style_width(obj, LV_PART_MAIN) == LV_SIZE_CONTENT && !obj->w_layout) flag |= LV_TEXT_FLAG_FIT;
428 
429     lv_text_align_t align = lv_obj_calculate_style_text_align(obj, LV_PART_MAIN, label->text);
430 
431     /*Search the line of the index letter*/;
432     while(txt[line_start] != '\0') {
433         new_line_start += _lv_txt_get_next_line(&txt[line_start], font, letter_space, max_w, NULL, flag);
434 
435         if(pos.y <= y + letter_height) {
436             /*The line is found (stored in 'line_start')*/
437             /*Include the NULL terminator in the last line*/
438             uint32_t tmp = new_line_start;
439             uint32_t letter;
440             letter = _lv_txt_encoded_prev(txt, &tmp);
441             if(letter != '\n' && txt[new_line_start] == '\0') new_line_start++;
442             break;
443         }
444         y += letter_height + line_space;
445 
446         line_start = new_line_start;
447     }
448 
449 #if LV_USE_BIDI
450     bidi_txt = lv_mem_buf_get(new_line_start - line_start + 1);
451     uint32_t txt_len = new_line_start - line_start;
452     if(new_line_start > 0 && txt[new_line_start - 1] == '\0' && txt_len > 0) txt_len--;
453     _lv_bidi_process_paragraph(txt + line_start, bidi_txt, txt_len, lv_obj_get_style_base_dir(obj, LV_PART_MAIN), NULL, 0);
454 #else
455     bidi_txt = (char *)txt + line_start;
456 #endif
457 
458     /*Calculate the x coordinate*/
459     lv_coord_t x = 0;
460     if(align == LV_TEXT_ALIGN_CENTER) {
461         lv_coord_t line_w;
462         line_w = lv_txt_get_width(bidi_txt, new_line_start - line_start, font, letter_space, flag);
463         x += lv_area_get_width(&txt_coords) / 2 - line_w / 2;
464     }
465     else if(align == LV_TEXT_ALIGN_RIGHT) {
466         lv_coord_t line_w;
467         line_w = lv_txt_get_width(bidi_txt, new_line_start - line_start, font, letter_space, flag);
468         x += lv_area_get_width(&txt_coords) - line_w;
469     }
470 
471     lv_text_cmd_state_t cmd_state = LV_TEXT_CMD_STATE_WAIT;
472 
473     uint32_t i = 0;
474     uint32_t i_act = i;
475 
476     if(new_line_start > 0) {
477         while(i + line_start < new_line_start) {
478             /*Get the current letter and the next letter for kerning*/
479             /*Be careful 'i' already points to the next character*/
480             uint32_t letter;
481             uint32_t letter_next;
482             _lv_txt_encoded_letter_next_2(bidi_txt, &letter, &letter_next, &i);
483 
484             /*Handle the recolor command*/
485             if((flag & LV_TEXT_FLAG_RECOLOR) != 0) {
486                 if(_lv_txt_is_cmd(&cmd_state, bidi_txt[i]) != false) {
487                     continue; /*Skip the letter if it is part of a command*/
488                 }
489             }
490 
491             lv_coord_t gw = lv_font_get_glyph_width(font, letter, letter_next);
492 
493             /*Finish if the x position or the last char of the next line is reached*/
494             if(pos.x < x + gw || i + line_start == new_line_start ||  txt[i_act + line_start] == '\0') {
495                 i = i_act;
496                 break;
497             }
498             x += gw;
499             x += letter_space;
500             i_act = i;
501         }
502     }
503 
504 #if LV_USE_BIDI
505     /*Handle Bidi*/
506     uint32_t cid = _lv_txt_encoded_get_char_id(bidi_txt, i);
507     if(txt[line_start + i] == '\0') {
508         logical_pos = i;
509     }
510     else {
511         bool is_rtl;
512         logical_pos = _lv_bidi_get_logical_pos(&txt[line_start], NULL,
513                                                txt_len, lv_obj_get_style_base_dir(obj, LV_PART_MAIN), cid, &is_rtl);
514         if(is_rtl) logical_pos++;
515     }
516     lv_mem_buf_release(bidi_txt);
517 #else
518     logical_pos = _lv_txt_encoded_get_char_id(bidi_txt, i);
519 #endif
520 
521     return  logical_pos + _lv_txt_encoded_get_char_id(txt, line_start);
522 }
523 
lv_label_is_char_under_pos(const lv_obj_t * obj,lv_point_t * pos)524 bool lv_label_is_char_under_pos(const lv_obj_t * obj, lv_point_t * pos)
525 {
526     LV_ASSERT_OBJ(obj, MY_CLASS);
527     LV_ASSERT_NULL(pos);
528 
529     lv_area_t txt_coords;
530     lv_obj_get_content_coords(obj, &txt_coords);
531     const char * txt         = lv_label_get_text(obj);
532     lv_label_t * label     = (lv_label_t *)obj;
533     uint32_t line_start      = 0;
534     uint32_t new_line_start  = 0;
535     lv_coord_t max_w         = lv_area_get_width(&txt_coords);
536     const lv_font_t * font   = lv_obj_get_style_text_font(obj, LV_PART_MAIN);
537     lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN);
538     lv_coord_t letter_space = lv_obj_get_style_text_letter_space(obj, LV_PART_MAIN);
539     lv_coord_t letter_height    = lv_font_get_line_height(font);
540     lv_text_align_t align = lv_obj_calculate_style_text_align(obj, LV_PART_MAIN, label->text);
541 
542     lv_coord_t y             = 0;
543     lv_text_flag_t flag       = LV_TEXT_FLAG_NONE;
544 
545     if(label->recolor != 0) flag |= LV_TEXT_FLAG_RECOLOR;
546     if(label->expand != 0) flag |= LV_TEXT_FLAG_EXPAND;
547     if(lv_obj_get_style_width(obj, LV_PART_MAIN) == LV_SIZE_CONTENT && !obj->w_layout) flag |= LV_TEXT_FLAG_FIT;
548 
549     /*Search the line of the index letter*/;
550     while(txt[line_start] != '\0') {
551         new_line_start += _lv_txt_get_next_line(&txt[line_start], font, letter_space, max_w, NULL, flag);
552 
553         if(pos->y <= y + letter_height) break; /*The line is found (stored in 'line_start')*/
554         y += letter_height + line_space;
555 
556         line_start = new_line_start;
557     }
558 
559     /*Calculate the x coordinate*/
560     lv_coord_t x      = 0;
561     lv_coord_t last_x = 0;
562     if(align == LV_TEXT_ALIGN_CENTER) {
563         lv_coord_t line_w;
564         line_w = lv_txt_get_width(&txt[line_start], new_line_start - line_start, font, letter_space, flag);
565         x += lv_area_get_width(&txt_coords) / 2 - line_w / 2;
566     }
567     else if(align == LV_TEXT_ALIGN_RIGHT) {
568         lv_coord_t line_w;
569         line_w = lv_txt_get_width(&txt[line_start], new_line_start - line_start, font, letter_space, flag);
570         x += lv_area_get_width(&txt_coords) - line_w;
571     }
572 
573     lv_text_cmd_state_t cmd_state = LV_TEXT_CMD_STATE_WAIT;
574 
575     uint32_t i           = line_start;
576     uint32_t i_current   = i;
577     uint32_t letter      = '\0';
578     uint32_t letter_next = '\0';
579 
580     if(new_line_start > 0) {
581         while(i <= new_line_start - 1) {
582             /*Get the current letter and the next letter for kerning*/
583             /*Be careful 'i' already points to the next character*/
584             _lv_txt_encoded_letter_next_2(txt, &letter, &letter_next, &i);
585 
586             /*Handle the recolor command*/
587             if((flag & LV_TEXT_FLAG_RECOLOR) != 0) {
588                 if(_lv_txt_is_cmd(&cmd_state, txt[i]) != false) {
589                     continue; /*Skip the letter if it is part of a command*/
590                 }
591             }
592             last_x = x;
593             x += lv_font_get_glyph_width(font, letter, letter_next);
594             if(pos->x < x) {
595                 i = i_current;
596                 break;
597             }
598             x += letter_space;
599             i_current = i;
600         }
601     }
602 
603     int32_t max_diff = lv_font_get_glyph_width(font, letter, letter_next) + letter_space + 1;
604     return (pos->x >= (last_x - letter_space) && pos->x <= (last_x + max_diff));
605 }
606 
lv_label_get_text_selection_start(const lv_obj_t * obj)607 uint32_t lv_label_get_text_selection_start(const lv_obj_t * obj)
608 {
609     LV_ASSERT_OBJ(obj, MY_CLASS);
610 
611 #if LV_LABEL_TEXT_SELECTION
612     lv_label_t * label = (lv_label_t *)obj;
613     return label->sel_start;
614 
615 #else
616     LV_UNUSED(obj); /*Unused*/
617     return LV_LABEL_TEXT_SELECTION_OFF;
618 #endif
619 }
620 
lv_label_get_text_selection_end(const lv_obj_t * obj)621 uint32_t lv_label_get_text_selection_end(const lv_obj_t * obj)
622 {
623     LV_ASSERT_OBJ(obj, MY_CLASS);
624 
625 #if LV_LABEL_TEXT_SELECTION
626     lv_label_t * label = (lv_label_t *)obj;
627     return label->sel_end;
628 #else
629     LV_UNUSED(obj); /*Unused*/
630     return LV_LABEL_TEXT_SELECTION_OFF;
631 #endif
632 }
633 
634 /*=====================
635  * Other functions
636  *====================*/
637 
lv_label_ins_text(lv_obj_t * obj,uint32_t pos,const char * txt)638 void lv_label_ins_text(lv_obj_t * obj, uint32_t pos, const char * txt)
639 {
640     LV_ASSERT_OBJ(obj, MY_CLASS);
641     LV_ASSERT_NULL(txt);
642 
643     lv_label_t * label = (lv_label_t *)obj;
644 
645     /*Can not append to static text*/
646     if(label->static_txt != 0) return;
647 
648     lv_obj_invalidate(obj);
649 
650     /*Allocate space for the new text*/
651     size_t old_len = strlen(label->text);
652     size_t ins_len = strlen(txt);
653     size_t new_len = ins_len + old_len;
654     label->text        = lv_mem_realloc(label->text, new_len + 1);
655     LV_ASSERT_MALLOC(label->text);
656     if(label->text == NULL) return;
657 
658     if(pos == LV_LABEL_POS_LAST) {
659         pos = _lv_txt_get_encoded_length(label->text);
660     }
661 
662     _lv_txt_ins(label->text, pos, txt);
663     lv_label_set_text(obj, NULL);
664 }
665 
lv_label_cut_text(lv_obj_t * obj,uint32_t pos,uint32_t cnt)666 void lv_label_cut_text(lv_obj_t * obj, uint32_t pos, uint32_t cnt)
667 {
668     LV_ASSERT_OBJ(obj, MY_CLASS);
669     lv_label_t * label = (lv_label_t *)obj;
670 
671     /*Can not append to static text*/
672     if(label->static_txt != 0) return;
673 
674     lv_obj_invalidate(obj);
675 
676     char * label_txt = lv_label_get_text(obj);
677     /*Delete the characters*/
678     _lv_txt_cut(label_txt, pos, cnt);
679 
680     /*Refresh the label*/
681     lv_label_refr_text(obj);
682 }
683 
684 /**********************
685  *   STATIC FUNCTIONS
686  **********************/
687 
lv_label_constructor(const lv_obj_class_t * class_p,lv_obj_t * obj)688 static void lv_label_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
689 {
690     LV_UNUSED(class_p);
691     LV_TRACE_OBJ_CREATE("begin");
692 
693     lv_label_t * label = (lv_label_t *)obj;
694 
695     label->text       = NULL;
696     label->static_txt = 0;
697     label->recolor    = 0;
698     label->dot_end    = LV_LABEL_DOT_END_INV;
699     label->long_mode  = LV_LABEL_LONG_WRAP;
700     label->offset.x = 0;
701     label->offset.y = 0;
702 
703 #if LV_LABEL_LONG_TXT_HINT
704     label->hint.line_start = -1;
705     label->hint.coord_y    = 0;
706     label->hint.y          = 0;
707 #endif
708 
709 #if LV_LABEL_TEXT_SELECTION
710     label->sel_start = LV_DRAW_LABEL_NO_TXT_SEL;
711     label->sel_end   = LV_DRAW_LABEL_NO_TXT_SEL;
712 #endif
713     label->dot.tmp_ptr   = NULL;
714     label->dot_tmp_alloc = 0;
715 
716     lv_obj_clear_flag(obj, LV_OBJ_FLAG_CLICKABLE);
717     lv_label_set_long_mode(obj, LV_LABEL_LONG_WRAP);
718     lv_label_set_text(obj, "Text");
719 
720 
721     LV_TRACE_OBJ_CREATE("finished");
722 }
723 
lv_label_destructor(const lv_obj_class_t * class_p,lv_obj_t * obj)724 static void lv_label_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
725 {
726     LV_UNUSED(class_p);
727     lv_label_t * label = (lv_label_t *)obj;
728 
729     lv_label_dot_tmp_free(obj);
730     if(!label->static_txt) lv_mem_free(label->text);
731     label->text = NULL;
732 }
733 
lv_label_event(const lv_obj_class_t * class_p,lv_event_t * e)734 static void lv_label_event(const lv_obj_class_t * class_p, lv_event_t * e)
735 {
736     LV_UNUSED(class_p);
737 
738     lv_res_t res;
739 
740     /*Call the ancestor's event handler*/
741     res = lv_obj_event_base(MY_CLASS, e);
742     if(res != LV_RES_OK) return;
743 
744     lv_event_code_t code = lv_event_get_code(e);
745     lv_obj_t * obj = lv_event_get_target(e);
746 
747     if(code == LV_EVENT_STYLE_CHANGED) {
748         /*Revert dots for proper refresh*/
749         lv_label_revert_dots(obj);
750         lv_label_refr_text(obj);
751     }
752     else if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
753         /* Italic or other non-typical letters can be drawn of out of the object.
754          * It happens if box_w + ofs_x > adw_w in the glyph.
755          * To avoid this add some extra draw area.
756          * font_h / 4 is an empirical value. */
757         const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_MAIN);
758         lv_coord_t font_h = lv_font_get_line_height(font);
759         lv_event_set_ext_draw_size(e, font_h / 4);
760     }
761     else if(code == LV_EVENT_SIZE_CHANGED) {
762         lv_label_revert_dots(obj);
763         lv_label_refr_text(obj);
764     }
765     else if(code == LV_EVENT_GET_SELF_SIZE) {
766         lv_point_t size;
767         lv_label_t * label = (lv_label_t *)obj;
768         const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_MAIN);
769         lv_coord_t letter_space = lv_obj_get_style_text_letter_space(obj, LV_PART_MAIN);
770         lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN);
771         lv_text_flag_t flag = LV_TEXT_FLAG_NONE;
772         if(label->recolor != 0) flag |= LV_TEXT_FLAG_RECOLOR;
773         if(label->expand != 0) flag |= LV_TEXT_FLAG_EXPAND;
774 
775         lv_coord_t w = lv_obj_get_content_width(obj);
776         if(lv_obj_get_style_width(obj, LV_PART_MAIN) == LV_SIZE_CONTENT && !obj->w_layout) w = LV_COORD_MAX;
777         else w = lv_obj_get_content_width(obj);
778 
779         lv_txt_get_size(&size, label->text, font, letter_space, line_space, w, flag);
780 
781         lv_point_t * self_size = lv_event_get_param(e);
782         self_size->x = LV_MAX(self_size->x, size.x);
783         self_size->y = LV_MAX(self_size->y, size.y);
784     }
785     else if(code == LV_EVENT_DRAW_MAIN) {
786         draw_main(e);
787     }
788 }
789 
790 
draw_main(lv_event_t * e)791 static void draw_main(lv_event_t * e)
792 {
793     lv_obj_t * obj = lv_event_get_target(e);
794     lv_label_t * label = (lv_label_t *)obj;
795     lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
796 
797     lv_area_t txt_coords;
798     lv_obj_get_content_coords(obj, &txt_coords);
799 
800     lv_text_flag_t flag = LV_TEXT_FLAG_NONE;
801     if(label->recolor != 0) flag |= LV_TEXT_FLAG_RECOLOR;
802     if(label->expand != 0) flag |= LV_TEXT_FLAG_EXPAND;
803     if(lv_obj_get_style_width(obj, LV_PART_MAIN) == LV_SIZE_CONTENT && !obj->w_layout) flag |= LV_TEXT_FLAG_FIT;
804 
805     lv_draw_label_dsc_t label_draw_dsc;
806     lv_draw_label_dsc_init(&label_draw_dsc);
807 
808     label_draw_dsc.ofs_x = label->offset.x;
809     label_draw_dsc.ofs_y = label->offset.y;
810 
811     label_draw_dsc.flag = flag;
812     lv_obj_init_draw_label_dsc(obj, LV_PART_MAIN, &label_draw_dsc);
813     lv_bidi_calculate_align(&label_draw_dsc.align, &label_draw_dsc.bidi_dir, label->text);
814 
815     label_draw_dsc.sel_start = lv_label_get_text_selection_start(obj);
816     label_draw_dsc.sel_end = lv_label_get_text_selection_end(obj);
817     if(label_draw_dsc.sel_start != LV_DRAW_LABEL_NO_TXT_SEL && label_draw_dsc.sel_end != LV_DRAW_LABEL_NO_TXT_SEL) {
818         label_draw_dsc.sel_color = lv_obj_get_style_text_color_filtered(obj, LV_PART_SELECTED);
819         label_draw_dsc.sel_bg_color = lv_obj_get_style_bg_color(obj, LV_PART_SELECTED);
820     }
821 
822     /* In SCROLL and SCROLL_CIRCULAR mode the CENTER and RIGHT are pointless, so remove them.
823      * (In addition, they will create misalignment in this situation)*/
824     if((label->long_mode == LV_LABEL_LONG_SCROLL || label->long_mode == LV_LABEL_LONG_SCROLL_CIRCULAR) &&
825        (label_draw_dsc.align == LV_TEXT_ALIGN_CENTER || label_draw_dsc.align == LV_TEXT_ALIGN_RIGHT)) {
826         lv_point_t size;
827         lv_txt_get_size(&size, label->text, label_draw_dsc.font, label_draw_dsc.letter_space, label_draw_dsc.line_space,
828                         LV_COORD_MAX, flag);
829         if(size.x > lv_area_get_width(&txt_coords)) {
830             label_draw_dsc.align = LV_TEXT_ALIGN_LEFT;
831         }
832     }
833 #if LV_LABEL_LONG_TXT_HINT
834     lv_draw_label_hint_t * hint = &label->hint;
835     if(label->long_mode == LV_LABEL_LONG_SCROLL_CIRCULAR || lv_area_get_height(&txt_coords) < LV_LABEL_HINT_HEIGHT_LIMIT)
836         hint = NULL;
837 
838 #else
839     /*Just for compatibility*/
840     lv_draw_label_hint_t * hint = NULL;
841 #endif
842 
843     lv_area_t txt_clip;
844     bool is_common = _lv_area_intersect(&txt_clip, &txt_coords, draw_ctx->clip_area);
845     if(!is_common) return;
846 
847     if(label->long_mode == LV_LABEL_LONG_WRAP) {
848         lv_coord_t s = lv_obj_get_scroll_top(obj);
849         lv_area_move(&txt_coords, 0, -s);
850         txt_coords.y2 = obj->coords.y2;
851     }
852     if(label->long_mode == LV_LABEL_LONG_SCROLL || label->long_mode == LV_LABEL_LONG_SCROLL_CIRCULAR) {
853         const lv_area_t * clip_area_ori = draw_ctx->clip_area;
854         draw_ctx->clip_area = &txt_clip;
855         lv_draw_label(draw_ctx, &label_draw_dsc, &txt_coords, label->text, hint);
856         draw_ctx->clip_area = clip_area_ori;
857     }
858     else {
859         lv_draw_label(draw_ctx, &label_draw_dsc, &txt_coords, label->text, hint);
860     }
861 
862     const lv_area_t * clip_area_ori = draw_ctx->clip_area;
863     draw_ctx->clip_area = &txt_clip;
864 
865     if(label->long_mode == LV_LABEL_LONG_SCROLL_CIRCULAR) {
866         lv_point_t size;
867         lv_txt_get_size(&size, label->text, label_draw_dsc.font, label_draw_dsc.letter_space, label_draw_dsc.line_space,
868                         LV_COORD_MAX, flag);
869 
870         /*Draw the text again on label to the original to make a circular effect */
871         if(size.x > lv_area_get_width(&txt_coords)) {
872             label_draw_dsc.ofs_x = label->offset.x + size.x +
873                                    lv_font_get_glyph_width(label_draw_dsc.font, ' ', ' ') * LV_LABEL_WAIT_CHAR_COUNT;
874             label_draw_dsc.ofs_y = label->offset.y;
875 
876             lv_draw_label(draw_ctx, &label_draw_dsc, &txt_coords, label->text, hint);
877         }
878 
879         /*Draw the text again below the original to make a circular effect */
880         if(size.y > lv_area_get_height(&txt_coords)) {
881             label_draw_dsc.ofs_x = label->offset.x;
882             label_draw_dsc.ofs_y = label->offset.y + size.y + lv_font_get_line_height(label_draw_dsc.font);
883 
884             lv_draw_label(draw_ctx, &label_draw_dsc, &txt_coords, label->text, hint);
885         }
886     }
887 
888     draw_ctx->clip_area = clip_area_ori;
889 }
890 
891 /**
892  * Refresh the label with its text stored in its extended data
893  * @param label pointer to a label object
894  */
lv_label_refr_text(lv_obj_t * obj)895 static void lv_label_refr_text(lv_obj_t * obj)
896 {
897     lv_label_t * label = (lv_label_t *)obj;
898     if(label->text == NULL) return;
899 #if LV_LABEL_LONG_TXT_HINT
900     label->hint.line_start = -1; /*The hint is invalid if the text changes*/
901 #endif
902 
903     lv_area_t txt_coords;
904     lv_obj_get_content_coords(obj, &txt_coords);
905     lv_coord_t max_w         = lv_area_get_width(&txt_coords);
906     const lv_font_t * font   = lv_obj_get_style_text_font(obj, LV_PART_MAIN);
907     lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN);
908     lv_coord_t letter_space = lv_obj_get_style_text_letter_space(obj, LV_PART_MAIN);
909 
910     /*Calc. the height and longest line*/
911     lv_point_t size;
912     lv_text_flag_t flag = LV_TEXT_FLAG_NONE;
913     if(label->recolor != 0) flag |= LV_TEXT_FLAG_RECOLOR;
914     if(label->expand != 0) flag |= LV_TEXT_FLAG_EXPAND;
915     if(lv_obj_get_style_width(obj, LV_PART_MAIN) == LV_SIZE_CONTENT && !obj->w_layout) flag |= LV_TEXT_FLAG_FIT;
916 
917     lv_txt_get_size(&size, label->text, font, letter_space, line_space, max_w, flag);
918 
919     lv_obj_refresh_self_size(obj);
920 
921     /*In scroll mode start an offset animation*/
922     if(label->long_mode == LV_LABEL_LONG_SCROLL) {
923         uint16_t anim_speed = lv_obj_get_style_anim_speed(obj, LV_PART_MAIN);
924         if(anim_speed == 0) anim_speed = LV_LABEL_DEF_SCROLL_SPEED;
925         lv_anim_t a;
926         lv_anim_init(&a);
927         lv_anim_set_var(&a, obj);
928         lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
929         lv_anim_set_playback_delay(&a, LV_LABEL_SCROLL_DELAY);
930         lv_anim_set_repeat_delay(&a, a.playback_delay);
931 
932         bool hor_anim = false;
933         if(size.x > lv_area_get_width(&txt_coords)) {
934 #if LV_USE_BIDI
935             int32_t start, end;
936             lv_base_dir_t base_dir = lv_obj_get_style_base_dir(obj, LV_PART_MAIN);
937 
938             if(base_dir == LV_BASE_DIR_AUTO)
939                 base_dir = _lv_bidi_detect_base_dir(label->text);
940 
941             if(base_dir == LV_BASE_DIR_RTL) {
942                 start = lv_area_get_width(&txt_coords) - size.x;
943                 end = 0;
944             }
945             else {
946                 start = 0;
947                 end = lv_area_get_width(&txt_coords) - size.x;
948             }
949 
950             lv_anim_set_values(&a, start, end);
951 #else
952             lv_anim_set_values(&a, 0, lv_area_get_width(&txt_coords) - size.x);
953             lv_anim_set_exec_cb(&a, set_ofs_x_anim);
954 #endif
955             lv_anim_set_exec_cb(&a, set_ofs_x_anim);
956 
957             lv_anim_t * anim_cur = lv_anim_get(obj, set_ofs_x_anim);
958             int32_t act_time = 0;
959             bool playback_now = false;
960             if(anim_cur) {
961                 act_time = anim_cur->act_time;
962                 playback_now = anim_cur->playback_now;
963             }
964             if(act_time < a.time) {
965                 a.act_time = act_time;      /*To keep the old position*/
966                 a.early_apply = 0;
967                 if(playback_now) {
968                     a.playback_now = 1;
969                     /*Swap the start and end values*/
970                     int32_t tmp;
971                     tmp      = a.start_value;
972                     a.start_value = a.end_value;
973                     a.end_value   = tmp;
974                 }
975             }
976 
977             lv_anim_set_time(&a, lv_anim_speed_to_time(anim_speed, a.start_value, a.end_value));
978             lv_anim_set_playback_time(&a, a.time);
979             lv_anim_start(&a);
980             hor_anim = true;
981         }
982         else {
983             /*Delete the offset animation if not required*/
984             lv_anim_del(obj, set_ofs_x_anim);
985             label->offset.x = 0;
986         }
987 
988         if(size.y > lv_area_get_height(&txt_coords) && hor_anim == false) {
989             lv_anim_set_values(&a, 0, lv_area_get_height(&txt_coords) - size.y - (lv_font_get_line_height(font)));
990             lv_anim_set_exec_cb(&a, set_ofs_y_anim);
991 
992             lv_anim_t * anim_cur = lv_anim_get(obj, set_ofs_y_anim);
993             int32_t act_time = 0;
994             bool playback_now = false;
995             if(anim_cur) {
996                 act_time = anim_cur->act_time;
997                 playback_now = anim_cur->playback_now;
998             }
999             if(act_time < a.time) {
1000                 a.act_time = act_time;      /*To keep the old position*/
1001                 a.early_apply = 0;
1002                 if(playback_now) {
1003                     a.playback_now = 1;
1004                     /*Swap the start and end values*/
1005                     int32_t tmp;
1006                     tmp      = a.start_value;
1007                     a.start_value = a.end_value;
1008                     a.end_value   = tmp;
1009                 }
1010             }
1011 
1012             lv_anim_set_time(&a, lv_anim_speed_to_time(anim_speed, a.start_value, a.end_value));
1013             lv_anim_set_playback_time(&a, a.time);
1014             lv_anim_start(&a);
1015         }
1016         else {
1017             /*Delete the offset animation if not required*/
1018             lv_anim_del(obj, set_ofs_y_anim);
1019             label->offset.y = 0;
1020         }
1021     }
1022     /*In roll inf. mode keep the size but start offset animations*/
1023     else if(label->long_mode == LV_LABEL_LONG_SCROLL_CIRCULAR) {
1024         const lv_anim_t * anim_template = lv_obj_get_style_anim(obj, LV_PART_MAIN);
1025         uint16_t anim_speed = lv_obj_get_style_anim_speed(obj, LV_PART_MAIN);
1026         if(anim_speed == 0) anim_speed = LV_LABEL_DEF_SCROLL_SPEED;
1027         lv_anim_t a;
1028         lv_anim_init(&a);
1029         lv_anim_set_var(&a, obj);
1030         lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
1031 
1032         bool hor_anim = false;
1033         if(size.x > lv_area_get_width(&txt_coords)) {
1034 #if LV_USE_BIDI
1035             int32_t start, end;
1036             lv_base_dir_t base_dir = lv_obj_get_style_base_dir(obj, LV_PART_MAIN);
1037 
1038             if(base_dir == LV_BASE_DIR_AUTO)
1039                 base_dir = _lv_bidi_detect_base_dir(label->text);
1040 
1041             if(base_dir == LV_BASE_DIR_RTL) {
1042                 start = -size.x - lv_font_get_glyph_width(font, ' ', ' ') * LV_LABEL_WAIT_CHAR_COUNT;
1043                 end = 0;
1044             }
1045             else {
1046                 start = 0;
1047                 end = -size.x - lv_font_get_glyph_width(font, ' ', ' ') * LV_LABEL_WAIT_CHAR_COUNT;
1048             }
1049 
1050             lv_anim_set_values(&a, start, end);
1051 #else
1052             lv_anim_set_values(&a, 0, -size.x - lv_font_get_glyph_width(font, ' ', ' ') * LV_LABEL_WAIT_CHAR_COUNT);
1053 #endif
1054             lv_anim_set_exec_cb(&a, set_ofs_x_anim);
1055             lv_anim_set_time(&a, lv_anim_speed_to_time(anim_speed, a.start_value, a.end_value));
1056 
1057             lv_anim_t * anim_cur = lv_anim_get(obj, set_ofs_x_anim);
1058             int32_t act_time = anim_cur ? anim_cur->act_time : 0;
1059 
1060             /*If a template animation exists, consider it's start delay and repeat delay*/
1061             if(anim_template) {
1062                 a.act_time = anim_template->act_time;
1063                 a.repeat_delay = anim_template->repeat_delay;
1064             }
1065             else if(act_time < a.time) {
1066                 a.act_time = act_time;      /*To keep the old position when the label text is updated mid-scrolling*/
1067                 a.early_apply = 0;
1068             }
1069 
1070             lv_anim_start(&a);
1071             hor_anim = true;
1072         }
1073         else {
1074             /*Delete the offset animation if not required*/
1075             lv_anim_del(obj, set_ofs_x_anim);
1076             label->offset.x = 0;
1077         }
1078 
1079         if(size.y > lv_area_get_height(&txt_coords) && hor_anim == false) {
1080             lv_anim_set_values(&a, 0, -size.y - (lv_font_get_line_height(font)));
1081             lv_anim_set_exec_cb(&a, set_ofs_y_anim);
1082             lv_anim_set_time(&a, lv_anim_speed_to_time(anim_speed, a.start_value, a.end_value));
1083 
1084             lv_anim_t * anim_cur = lv_anim_get(obj, set_ofs_y_anim);
1085             int32_t act_time = anim_cur ? anim_cur->act_time : 0;
1086 
1087             /*If a template animation exists, consider it's start delay and repeat delay*/
1088             if(anim_template) {
1089                 a.act_time = anim_template->act_time;
1090                 a.repeat_delay = anim_template->repeat_delay;
1091             }
1092             else if(act_time < a.time) {
1093                 a.act_time = act_time;      /*To keep the old position when the label text is updated mid-scrolling*/
1094                 a.early_apply = 0;
1095             }
1096 
1097             lv_anim_start(&a);
1098         }
1099         else {
1100             /*Delete the offset animation if not required*/
1101             lv_anim_del(obj, set_ofs_y_anim);
1102             label->offset.y = 0;
1103         }
1104     }
1105     else if(label->long_mode == LV_LABEL_LONG_DOT) {
1106         if(size.y <= lv_area_get_height(&txt_coords)) { /*No dots are required, the text is short enough*/
1107             label->dot_end = LV_LABEL_DOT_END_INV;
1108         }
1109         else if(size.y <= lv_font_get_line_height(font)) { /*No dots are required for one-line texts*/
1110             label->dot_end = LV_LABEL_DOT_END_INV;
1111         }
1112         else if(_lv_txt_get_encoded_length(label->text) <= LV_LABEL_DOT_NUM) {   /*Don't turn to dots all the characters*/
1113             label->dot_end = LV_LABEL_DOT_END_INV;
1114         }
1115         else {
1116             lv_point_t p;
1117             lv_coord_t y_overed;
1118             p.x = lv_area_get_width(&txt_coords) -
1119                   (lv_font_get_glyph_width(font, '.', '.') + letter_space) *
1120                   LV_LABEL_DOT_NUM; /*Shrink with dots*/
1121             p.y = lv_area_get_height(&txt_coords);
1122             y_overed = p.y %
1123                        (lv_font_get_line_height(font) + line_space); /*Round down to the last line*/
1124             if(y_overed >= lv_font_get_line_height(font)) {
1125                 p.y -= y_overed;
1126                 p.y += lv_font_get_line_height(font);
1127             }
1128             else {
1129                 p.y -= y_overed;
1130                 p.y -= line_space;
1131             }
1132 
1133             uint32_t letter_id = lv_label_get_letter_on(obj, &p);
1134 
1135             /*Be sure there is space for the dots*/
1136             size_t txt_len = strlen(label->text);
1137             uint32_t byte_id     = _lv_txt_encoded_get_byte_id(label->text, letter_id);
1138             while(byte_id + LV_LABEL_DOT_NUM > txt_len) {
1139                 _lv_txt_encoded_prev(label->text, &byte_id);
1140                 letter_id--;
1141             }
1142 
1143             /*Save letters under the dots and replace them with dots*/
1144             uint32_t byte_id_ori = byte_id;
1145             uint32_t i;
1146             uint8_t len = 0;
1147             for(i = 0; i <= LV_LABEL_DOT_NUM; i++) {
1148                 len += _lv_txt_encoded_size(&label->text[byte_id]);
1149                 _lv_txt_encoded_next(label->text, &byte_id);
1150                 if(len > LV_LABEL_DOT_NUM || byte_id > txt_len) {
1151                     break;
1152                 }
1153             }
1154 
1155             if(lv_label_set_dot_tmp(obj, &label->text[byte_id_ori], len)) {
1156                 for(i = 0; i < LV_LABEL_DOT_NUM; i++) {
1157                     label->text[byte_id_ori + i] = '.';
1158                 }
1159                 label->text[byte_id_ori + LV_LABEL_DOT_NUM] = '\0';
1160                 label->dot_end                              = letter_id + LV_LABEL_DOT_NUM;
1161             }
1162         }
1163     }
1164     else if(label->long_mode == LV_LABEL_LONG_CLIP) {
1165         /*Do nothing*/
1166     }
1167 
1168     lv_obj_invalidate(obj);
1169 }
1170 
1171 
lv_label_revert_dots(lv_obj_t * obj)1172 static void lv_label_revert_dots(lv_obj_t * obj)
1173 {
1174 
1175     lv_label_t * label = (lv_label_t *)obj;
1176 
1177     if(label->long_mode != LV_LABEL_LONG_DOT) return;
1178     if(label->dot_end == LV_LABEL_DOT_END_INV) return;
1179     uint32_t letter_i = label->dot_end - LV_LABEL_DOT_NUM;
1180     uint32_t byte_i   = _lv_txt_encoded_get_byte_id(label->text, letter_i);
1181 
1182     /*Restore the characters*/
1183     uint8_t i      = 0;
1184     char * dot_tmp = lv_label_get_dot_tmp(obj);
1185     while(label->text[byte_i + i] != '\0') {
1186         label->text[byte_i + i] = dot_tmp[i];
1187         i++;
1188     }
1189     label->text[byte_i + i] = dot_tmp[i];
1190     lv_label_dot_tmp_free(obj);
1191 
1192     label->dot_end = LV_LABEL_DOT_END_INV;
1193 }
1194 
1195 /**
1196  * Store `len` characters from `data`. Allocates space if necessary.
1197  *
1198  * @param label pointer to label object
1199  * @param len Number of characters to store.
1200  * @return true on success.
1201  */
lv_label_set_dot_tmp(lv_obj_t * obj,char * data,uint32_t len)1202 static bool lv_label_set_dot_tmp(lv_obj_t * obj, char * data, uint32_t len)
1203 {
1204 
1205     lv_label_t * label = (lv_label_t *)obj;
1206     lv_label_dot_tmp_free(obj); /*Deallocate any existing space*/
1207     if(len > sizeof(char *)) {
1208         /*Memory needs to be allocated. Allocates an additional byte
1209          *for a NULL-terminator so it can be copied.*/
1210         label->dot.tmp_ptr = lv_mem_alloc(len + 1);
1211         if(label->dot.tmp_ptr == NULL) {
1212             LV_LOG_ERROR("Failed to allocate memory for dot_tmp_ptr");
1213             return false;
1214         }
1215         lv_memcpy(label->dot.tmp_ptr, data, len);
1216         label->dot.tmp_ptr[len] = '\0';
1217         label->dot_tmp_alloc    = true;
1218     }
1219     else {
1220         /*Characters can be directly stored in object*/
1221         label->dot_tmp_alloc = false;
1222         lv_memcpy(label->dot.tmp, data, len);
1223     }
1224     return true;
1225 }
1226 
1227 /**
1228  * Get the stored dot_tmp characters
1229  * @param label pointer to label object
1230  * @return char pointer to a stored characters. Is *not* necessarily NULL-terminated.
1231  */
lv_label_get_dot_tmp(lv_obj_t * obj)1232 static char * lv_label_get_dot_tmp(lv_obj_t * obj)
1233 {
1234     lv_label_t * label = (lv_label_t *)obj;
1235     if(label->dot_tmp_alloc) {
1236         return label->dot.tmp_ptr;
1237     }
1238     else {
1239         return label->dot.tmp;
1240     }
1241 }
1242 
1243 /**
1244  * Free the dot_tmp_ptr field if it was previously allocated.
1245  * Always clears the field
1246  * @param label pointer to label object.
1247  */
lv_label_dot_tmp_free(lv_obj_t * obj)1248 static void lv_label_dot_tmp_free(lv_obj_t * obj)
1249 {
1250     lv_label_t * label = (lv_label_t *)obj;
1251     if(label->dot_tmp_alloc && label->dot.tmp_ptr) {
1252         lv_mem_free(label->dot.tmp_ptr);
1253     }
1254     label->dot_tmp_alloc = false;
1255     label->dot.tmp_ptr   = NULL;
1256 }
1257 
1258 
set_ofs_x_anim(void * obj,int32_t v)1259 static void set_ofs_x_anim(void * obj, int32_t v)
1260 {
1261     lv_label_t * label = (lv_label_t *)obj;
1262     label->offset.x    = v;
1263     lv_obj_invalidate(obj);
1264 }
1265 
set_ofs_y_anim(void * obj,int32_t v)1266 static void set_ofs_y_anim(void * obj, int32_t v)
1267 {
1268     lv_label_t * label = (lv_label_t *)obj;
1269     label->offset.y    = v;
1270     lv_obj_invalidate(obj);
1271 }
1272 
1273 
1274 #endif
1275