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 LV_TRACE_OBJ_CREATE("finished");
721 }
722
lv_label_destructor(const lv_obj_class_t * class_p,lv_obj_t * obj)723 static void lv_label_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
724 {
725 LV_UNUSED(class_p);
726 lv_label_t * label = (lv_label_t *)obj;
727
728 lv_label_dot_tmp_free(obj);
729 if(!label->static_txt) lv_mem_free(label->text);
730 label->text = NULL;
731 }
732
lv_label_event(const lv_obj_class_t * class_p,lv_event_t * e)733 static void lv_label_event(const lv_obj_class_t * class_p, lv_event_t * e)
734 {
735 LV_UNUSED(class_p);
736
737 lv_res_t res;
738
739 /*Call the ancestor's event handler*/
740 res = lv_obj_event_base(MY_CLASS, e);
741 if(res != LV_RES_OK) return;
742
743 lv_event_code_t code = lv_event_get_code(e);
744 lv_obj_t * obj = lv_event_get_target(e);
745
746 if(code == LV_EVENT_STYLE_CHANGED) {
747 /*Revert dots for proper refresh*/
748 lv_label_revert_dots(obj);
749 lv_label_refr_text(obj);
750 }
751 else if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
752 /* Italic or other non-typical letters can be drawn of out of the object.
753 * It happens if box_w + ofs_x > adw_w in the glyph.
754 * To avoid this add some extra draw area.
755 * font_h / 4 is an empirical value. */
756 const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_MAIN);
757 lv_coord_t font_h = lv_font_get_line_height(font);
758 lv_event_set_ext_draw_size(e, font_h / 4);
759 }
760 else if(code == LV_EVENT_SIZE_CHANGED) {
761 lv_label_revert_dots(obj);
762 lv_label_refr_text(obj);
763 }
764 else if(code == LV_EVENT_GET_SELF_SIZE) {
765 lv_point_t size;
766 lv_label_t * label = (lv_label_t *)obj;
767 const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_MAIN);
768 lv_coord_t letter_space = lv_obj_get_style_text_letter_space(obj, LV_PART_MAIN);
769 lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN);
770 lv_text_flag_t flag = LV_TEXT_FLAG_NONE;
771 if(label->recolor != 0) flag |= LV_TEXT_FLAG_RECOLOR;
772 if(label->expand != 0) flag |= LV_TEXT_FLAG_EXPAND;
773
774 lv_coord_t w = lv_obj_get_content_width(obj);
775 if(lv_obj_get_style_width(obj, LV_PART_MAIN) == LV_SIZE_CONTENT && !obj->w_layout) w = LV_COORD_MAX;
776 else w = lv_obj_get_content_width(obj);
777
778 lv_txt_get_size(&size, label->text, font, letter_space, line_space, w, flag);
779
780 lv_point_t * self_size = lv_event_get_param(e);
781 self_size->x = LV_MAX(self_size->x, size.x);
782 self_size->y = LV_MAX(self_size->y, size.y);
783 }
784 else if(code == LV_EVENT_DRAW_MAIN) {
785 draw_main(e);
786 }
787 }
788
draw_main(lv_event_t * e)789 static void draw_main(lv_event_t * e)
790 {
791 lv_obj_t * obj = lv_event_get_target(e);
792 lv_label_t * label = (lv_label_t *)obj;
793 lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
794
795 lv_area_t txt_coords;
796 lv_obj_get_content_coords(obj, &txt_coords);
797
798 lv_text_flag_t flag = LV_TEXT_FLAG_NONE;
799 if(label->recolor != 0) flag |= LV_TEXT_FLAG_RECOLOR;
800 if(label->expand != 0) flag |= LV_TEXT_FLAG_EXPAND;
801 if(lv_obj_get_style_width(obj, LV_PART_MAIN) == LV_SIZE_CONTENT && !obj->w_layout) flag |= LV_TEXT_FLAG_FIT;
802
803 lv_draw_label_dsc_t label_draw_dsc;
804 lv_draw_label_dsc_init(&label_draw_dsc);
805
806 label_draw_dsc.ofs_x = label->offset.x;
807 label_draw_dsc.ofs_y = label->offset.y;
808
809 label_draw_dsc.flag = flag;
810 lv_obj_init_draw_label_dsc(obj, LV_PART_MAIN, &label_draw_dsc);
811 lv_bidi_calculate_align(&label_draw_dsc.align, &label_draw_dsc.bidi_dir, label->text);
812
813 label_draw_dsc.sel_start = lv_label_get_text_selection_start(obj);
814 label_draw_dsc.sel_end = lv_label_get_text_selection_end(obj);
815 if(label_draw_dsc.sel_start != LV_DRAW_LABEL_NO_TXT_SEL && label_draw_dsc.sel_end != LV_DRAW_LABEL_NO_TXT_SEL) {
816 label_draw_dsc.sel_color = lv_obj_get_style_text_color_filtered(obj, LV_PART_SELECTED);
817 label_draw_dsc.sel_bg_color = lv_obj_get_style_bg_color(obj, LV_PART_SELECTED);
818 }
819
820 /* In SCROLL and SCROLL_CIRCULAR mode the CENTER and RIGHT are pointless, so remove them.
821 * (In addition, they will create misalignment in this situation)*/
822 if((label->long_mode == LV_LABEL_LONG_SCROLL || label->long_mode == LV_LABEL_LONG_SCROLL_CIRCULAR) &&
823 (label_draw_dsc.align == LV_TEXT_ALIGN_CENTER || label_draw_dsc.align == LV_TEXT_ALIGN_RIGHT)) {
824 lv_point_t size;
825 lv_txt_get_size(&size, label->text, label_draw_dsc.font, label_draw_dsc.letter_space, label_draw_dsc.line_space,
826 LV_COORD_MAX, flag);
827 if(size.x > lv_area_get_width(&txt_coords)) {
828 label_draw_dsc.align = LV_TEXT_ALIGN_LEFT;
829 }
830 }
831 #if LV_LABEL_LONG_TXT_HINT
832 lv_draw_label_hint_t * hint = &label->hint;
833 if(label->long_mode == LV_LABEL_LONG_SCROLL_CIRCULAR || lv_area_get_height(&txt_coords) < LV_LABEL_HINT_HEIGHT_LIMIT)
834 hint = NULL;
835
836 #else
837 /*Just for compatibility*/
838 lv_draw_label_hint_t * hint = NULL;
839 #endif
840
841 lv_area_t txt_clip;
842 bool is_common = _lv_area_intersect(&txt_clip, &txt_coords, draw_ctx->clip_area);
843 if(!is_common) return;
844
845 if(label->long_mode == LV_LABEL_LONG_WRAP) {
846 lv_coord_t s = lv_obj_get_scroll_top(obj);
847 lv_area_move(&txt_coords, 0, -s);
848 txt_coords.y2 = obj->coords.y2;
849 }
850 if(label->long_mode == LV_LABEL_LONG_SCROLL || label->long_mode == LV_LABEL_LONG_SCROLL_CIRCULAR) {
851 const lv_area_t * clip_area_ori = draw_ctx->clip_area;
852 draw_ctx->clip_area = &txt_clip;
853 lv_draw_label(draw_ctx, &label_draw_dsc, &txt_coords, label->text, hint);
854 draw_ctx->clip_area = clip_area_ori;
855 }
856 else {
857 lv_draw_label(draw_ctx, &label_draw_dsc, &txt_coords, label->text, hint);
858 }
859
860 const lv_area_t * clip_area_ori = draw_ctx->clip_area;
861 draw_ctx->clip_area = &txt_clip;
862
863 if(label->long_mode == LV_LABEL_LONG_SCROLL_CIRCULAR) {
864 lv_point_t size;
865 lv_txt_get_size(&size, label->text, label_draw_dsc.font, label_draw_dsc.letter_space, label_draw_dsc.line_space,
866 LV_COORD_MAX, flag);
867
868 /*Draw the text again on label to the original to make a circular effect */
869 if(size.x > lv_area_get_width(&txt_coords)) {
870 label_draw_dsc.ofs_x = label->offset.x + size.x +
871 lv_font_get_glyph_width(label_draw_dsc.font, ' ', ' ') * LV_LABEL_WAIT_CHAR_COUNT;
872 label_draw_dsc.ofs_y = label->offset.y;
873
874 lv_draw_label(draw_ctx, &label_draw_dsc, &txt_coords, label->text, hint);
875 }
876
877 /*Draw the text again below the original to make a circular effect */
878 if(size.y > lv_area_get_height(&txt_coords)) {
879 label_draw_dsc.ofs_x = label->offset.x;
880 label_draw_dsc.ofs_y = label->offset.y + size.y + lv_font_get_line_height(label_draw_dsc.font);
881
882 lv_draw_label(draw_ctx, &label_draw_dsc, &txt_coords, label->text, hint);
883 }
884 }
885
886 draw_ctx->clip_area = clip_area_ori;
887 }
888
889 /**
890 * Refresh the label with its text stored in its extended data
891 * @param label pointer to a label object
892 */
lv_label_refr_text(lv_obj_t * obj)893 static void lv_label_refr_text(lv_obj_t * obj)
894 {
895 lv_label_t * label = (lv_label_t *)obj;
896 if(label->text == NULL) return;
897 #if LV_LABEL_LONG_TXT_HINT
898 label->hint.line_start = -1; /*The hint is invalid if the text changes*/
899 #endif
900
901 lv_area_t txt_coords;
902 lv_obj_get_content_coords(obj, &txt_coords);
903 lv_coord_t max_w = lv_area_get_width(&txt_coords);
904 const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_MAIN);
905 lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN);
906 lv_coord_t letter_space = lv_obj_get_style_text_letter_space(obj, LV_PART_MAIN);
907
908 /*Calc. the height and longest line*/
909 lv_point_t size;
910 lv_text_flag_t flag = LV_TEXT_FLAG_NONE;
911 if(label->recolor != 0) flag |= LV_TEXT_FLAG_RECOLOR;
912 if(label->expand != 0) flag |= LV_TEXT_FLAG_EXPAND;
913 if(lv_obj_get_style_width(obj, LV_PART_MAIN) == LV_SIZE_CONTENT && !obj->w_layout) flag |= LV_TEXT_FLAG_FIT;
914
915 lv_txt_get_size(&size, label->text, font, letter_space, line_space, max_w, flag);
916
917 lv_obj_refresh_self_size(obj);
918
919 /*In scroll mode start an offset animation*/
920 if(label->long_mode == LV_LABEL_LONG_SCROLL) {
921 uint16_t anim_speed = lv_obj_get_style_anim_speed(obj, LV_PART_MAIN);
922 if(anim_speed == 0) anim_speed = LV_LABEL_DEF_SCROLL_SPEED;
923 lv_anim_t a;
924 lv_anim_init(&a);
925 lv_anim_set_var(&a, obj);
926 lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
927 lv_anim_set_playback_delay(&a, LV_LABEL_SCROLL_DELAY);
928 lv_anim_set_repeat_delay(&a, a.playback_delay);
929
930 bool hor_anim = false;
931 if(size.x > lv_area_get_width(&txt_coords)) {
932 #if LV_USE_BIDI
933 int32_t start, end;
934 lv_base_dir_t base_dir = lv_obj_get_style_base_dir(obj, LV_PART_MAIN);
935
936 if(base_dir == LV_BASE_DIR_AUTO)
937 base_dir = _lv_bidi_detect_base_dir(label->text);
938
939 if(base_dir == LV_BASE_DIR_RTL) {
940 start = lv_area_get_width(&txt_coords) - size.x;
941 end = 0;
942 }
943 else {
944 start = 0;
945 end = lv_area_get_width(&txt_coords) - size.x;
946 }
947
948 lv_anim_set_values(&a, start, end);
949 #else
950 lv_anim_set_values(&a, 0, lv_area_get_width(&txt_coords) - size.x);
951 lv_anim_set_exec_cb(&a, set_ofs_x_anim);
952 #endif
953 lv_anim_set_exec_cb(&a, set_ofs_x_anim);
954
955 lv_anim_t * anim_cur = lv_anim_get(obj, set_ofs_x_anim);
956 int32_t act_time = 0;
957 bool playback_now = false;
958 if(anim_cur) {
959 act_time = anim_cur->act_time;
960 playback_now = anim_cur->playback_now;
961 }
962 if(act_time < a.time) {
963 a.act_time = act_time; /*To keep the old position*/
964 a.early_apply = 0;
965 if(playback_now) {
966 a.playback_now = 1;
967 /*Swap the start and end values*/
968 int32_t tmp;
969 tmp = a.start_value;
970 a.start_value = a.end_value;
971 a.end_value = tmp;
972 }
973 }
974
975 lv_anim_set_time(&a, lv_anim_speed_to_time(anim_speed, a.start_value, a.end_value));
976 lv_anim_set_playback_time(&a, a.time);
977 lv_anim_start(&a);
978 hor_anim = true;
979 }
980 else {
981 /*Delete the offset animation if not required*/
982 lv_anim_del(obj, set_ofs_x_anim);
983 label->offset.x = 0;
984 }
985
986 if(size.y > lv_area_get_height(&txt_coords) && hor_anim == false) {
987 lv_anim_set_values(&a, 0, lv_area_get_height(&txt_coords) - size.y - (lv_font_get_line_height(font)));
988 lv_anim_set_exec_cb(&a, set_ofs_y_anim);
989
990 lv_anim_t * anim_cur = lv_anim_get(obj, set_ofs_y_anim);
991 int32_t act_time = 0;
992 bool playback_now = false;
993 if(anim_cur) {
994 act_time = anim_cur->act_time;
995 playback_now = anim_cur->playback_now;
996 }
997 if(act_time < a.time) {
998 a.act_time = act_time; /*To keep the old position*/
999 a.early_apply = 0;
1000 if(playback_now) {
1001 a.playback_now = 1;
1002 /*Swap the start and end values*/
1003 int32_t tmp;
1004 tmp = a.start_value;
1005 a.start_value = a.end_value;
1006 a.end_value = tmp;
1007 }
1008 }
1009
1010 lv_anim_set_time(&a, lv_anim_speed_to_time(anim_speed, a.start_value, a.end_value));
1011 lv_anim_set_playback_time(&a, a.time);
1012 lv_anim_start(&a);
1013 }
1014 else {
1015 /*Delete the offset animation if not required*/
1016 lv_anim_del(obj, set_ofs_y_anim);
1017 label->offset.y = 0;
1018 }
1019 }
1020 /*In roll inf. mode keep the size but start offset animations*/
1021 else if(label->long_mode == LV_LABEL_LONG_SCROLL_CIRCULAR) {
1022 const lv_anim_t * anim_template = lv_obj_get_style_anim(obj, LV_PART_MAIN);
1023 uint16_t anim_speed = lv_obj_get_style_anim_speed(obj, LV_PART_MAIN);
1024 if(anim_speed == 0) anim_speed = LV_LABEL_DEF_SCROLL_SPEED;
1025 lv_anim_t a;
1026 lv_anim_init(&a);
1027 lv_anim_set_var(&a, obj);
1028 lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
1029
1030 bool hor_anim = false;
1031 if(size.x > lv_area_get_width(&txt_coords)) {
1032 #if LV_USE_BIDI
1033 int32_t start, end;
1034 lv_base_dir_t base_dir = lv_obj_get_style_base_dir(obj, LV_PART_MAIN);
1035
1036 if(base_dir == LV_BASE_DIR_AUTO)
1037 base_dir = _lv_bidi_detect_base_dir(label->text);
1038
1039 if(base_dir == LV_BASE_DIR_RTL) {
1040 start = -size.x - lv_font_get_glyph_width(font, ' ', ' ') * LV_LABEL_WAIT_CHAR_COUNT;
1041 end = 0;
1042 }
1043 else {
1044 start = 0;
1045 end = -size.x - lv_font_get_glyph_width(font, ' ', ' ') * LV_LABEL_WAIT_CHAR_COUNT;
1046 }
1047
1048 lv_anim_set_values(&a, start, end);
1049 #else
1050 lv_anim_set_values(&a, 0, -size.x - lv_font_get_glyph_width(font, ' ', ' ') * LV_LABEL_WAIT_CHAR_COUNT);
1051 #endif
1052 lv_anim_set_exec_cb(&a, set_ofs_x_anim);
1053 lv_anim_set_time(&a, lv_anim_speed_to_time(anim_speed, a.start_value, a.end_value));
1054
1055 lv_anim_t * anim_cur = lv_anim_get(obj, set_ofs_x_anim);
1056 int32_t act_time = anim_cur ? anim_cur->act_time : 0;
1057
1058 /*If a template animation exists, consider it's start delay and repeat delay*/
1059 if(anim_template) {
1060 a.act_time = anim_template->act_time;
1061 a.repeat_delay = anim_template->repeat_delay;
1062 }
1063 else if(act_time < a.time) {
1064 a.act_time = act_time; /*To keep the old position when the label text is updated mid-scrolling*/
1065 a.early_apply = 0;
1066 }
1067
1068 lv_anim_start(&a);
1069 hor_anim = true;
1070 }
1071 else {
1072 /*Delete the offset animation if not required*/
1073 lv_anim_del(obj, set_ofs_x_anim);
1074 label->offset.x = 0;
1075 }
1076
1077 if(size.y > lv_area_get_height(&txt_coords) && hor_anim == false) {
1078 lv_anim_set_values(&a, 0, -size.y - (lv_font_get_line_height(font)));
1079 lv_anim_set_exec_cb(&a, set_ofs_y_anim);
1080 lv_anim_set_time(&a, lv_anim_speed_to_time(anim_speed, a.start_value, a.end_value));
1081
1082 lv_anim_t * anim_cur = lv_anim_get(obj, set_ofs_y_anim);
1083 int32_t act_time = anim_cur ? anim_cur->act_time : 0;
1084
1085 /*If a template animation exists, consider it's start delay and repeat delay*/
1086 if(anim_template) {
1087 a.act_time = anim_template->act_time;
1088 a.repeat_delay = anim_template->repeat_delay;
1089 }
1090 else if(act_time < a.time) {
1091 a.act_time = act_time; /*To keep the old position when the label text is updated mid-scrolling*/
1092 a.early_apply = 0;
1093 }
1094
1095 lv_anim_start(&a);
1096 }
1097 else {
1098 /*Delete the offset animation if not required*/
1099 lv_anim_del(obj, set_ofs_y_anim);
1100 label->offset.y = 0;
1101 }
1102 }
1103 else if(label->long_mode == LV_LABEL_LONG_DOT) {
1104 if(size.y <= lv_area_get_height(&txt_coords)) { /*No dots are required, the text is short enough*/
1105 label->dot_end = LV_LABEL_DOT_END_INV;
1106 }
1107 else if(size.y <= lv_font_get_line_height(font)) { /*No dots are required for one-line texts*/
1108 label->dot_end = LV_LABEL_DOT_END_INV;
1109 }
1110 else if(_lv_txt_get_encoded_length(label->text) <= LV_LABEL_DOT_NUM) { /*Don't turn to dots all the characters*/
1111 label->dot_end = LV_LABEL_DOT_END_INV;
1112 }
1113 else {
1114 lv_point_t p;
1115 lv_coord_t y_overed;
1116 p.x = lv_area_get_width(&txt_coords) -
1117 (lv_font_get_glyph_width(font, '.', '.') + letter_space) *
1118 LV_LABEL_DOT_NUM; /*Shrink with dots*/
1119 p.y = lv_area_get_height(&txt_coords);
1120 y_overed = p.y %
1121 (lv_font_get_line_height(font) + line_space); /*Round down to the last line*/
1122 if(y_overed >= lv_font_get_line_height(font)) {
1123 p.y -= y_overed;
1124 p.y += lv_font_get_line_height(font);
1125 }
1126 else {
1127 p.y -= y_overed;
1128 p.y -= line_space;
1129 }
1130
1131 uint32_t letter_id = lv_label_get_letter_on(obj, &p);
1132
1133 /*Be sure there is space for the dots*/
1134 size_t txt_len = strlen(label->text);
1135 uint32_t byte_id = _lv_txt_encoded_get_byte_id(label->text, letter_id);
1136 while(byte_id + LV_LABEL_DOT_NUM > txt_len) {
1137 _lv_txt_encoded_prev(label->text, &byte_id);
1138 letter_id--;
1139 }
1140
1141 /*Save letters under the dots and replace them with dots*/
1142 uint32_t byte_id_ori = byte_id;
1143 uint32_t i;
1144 uint8_t len = 0;
1145 for(i = 0; i <= LV_LABEL_DOT_NUM; i++) {
1146 len += _lv_txt_encoded_size(&label->text[byte_id]);
1147 _lv_txt_encoded_next(label->text, &byte_id);
1148 if(len > LV_LABEL_DOT_NUM || byte_id > txt_len) {
1149 break;
1150 }
1151 }
1152
1153 if(lv_label_set_dot_tmp(obj, &label->text[byte_id_ori], len)) {
1154 for(i = 0; i < LV_LABEL_DOT_NUM; i++) {
1155 label->text[byte_id_ori + i] = '.';
1156 }
1157 label->text[byte_id_ori + LV_LABEL_DOT_NUM] = '\0';
1158 label->dot_end = letter_id + LV_LABEL_DOT_NUM;
1159 }
1160 }
1161 }
1162 else if(label->long_mode == LV_LABEL_LONG_CLIP) {
1163 /*Do nothing*/
1164 }
1165
1166 lv_obj_invalidate(obj);
1167 }
1168
lv_label_revert_dots(lv_obj_t * obj)1169 static void lv_label_revert_dots(lv_obj_t * obj)
1170 {
1171
1172 lv_label_t * label = (lv_label_t *)obj;
1173
1174 if(label->long_mode != LV_LABEL_LONG_DOT) return;
1175 if(label->dot_end == LV_LABEL_DOT_END_INV) return;
1176 uint32_t letter_i = label->dot_end - LV_LABEL_DOT_NUM;
1177 uint32_t byte_i = _lv_txt_encoded_get_byte_id(label->text, letter_i);
1178
1179 /*Restore the characters*/
1180 uint8_t i = 0;
1181 char * dot_tmp = lv_label_get_dot_tmp(obj);
1182 while(label->text[byte_i + i] != '\0') {
1183 label->text[byte_i + i] = dot_tmp[i];
1184 i++;
1185 }
1186 label->text[byte_i + i] = dot_tmp[i];
1187 lv_label_dot_tmp_free(obj);
1188
1189 label->dot_end = LV_LABEL_DOT_END_INV;
1190 }
1191
1192 /**
1193 * Store `len` characters from `data`. Allocates space if necessary.
1194 *
1195 * @param label pointer to label object
1196 * @param len Number of characters to store.
1197 * @return true on success.
1198 */
lv_label_set_dot_tmp(lv_obj_t * obj,char * data,uint32_t len)1199 static bool lv_label_set_dot_tmp(lv_obj_t * obj, char * data, uint32_t len)
1200 {
1201
1202 lv_label_t * label = (lv_label_t *)obj;
1203 lv_label_dot_tmp_free(obj); /*Deallocate any existing space*/
1204 if(len > sizeof(char *)) {
1205 /*Memory needs to be allocated. Allocates an additional byte
1206 *for a NULL-terminator so it can be copied.*/
1207 label->dot.tmp_ptr = lv_mem_alloc(len + 1);
1208 if(label->dot.tmp_ptr == NULL) {
1209 LV_LOG_ERROR("Failed to allocate memory for dot_tmp_ptr");
1210 return false;
1211 }
1212 lv_memcpy(label->dot.tmp_ptr, data, len);
1213 label->dot.tmp_ptr[len] = '\0';
1214 label->dot_tmp_alloc = true;
1215 }
1216 else {
1217 /*Characters can be directly stored in object*/
1218 label->dot_tmp_alloc = false;
1219 lv_memcpy(label->dot.tmp, data, len);
1220 }
1221 return true;
1222 }
1223
1224 /**
1225 * Get the stored dot_tmp characters
1226 * @param label pointer to label object
1227 * @return char pointer to a stored characters. Is *not* necessarily NULL-terminated.
1228 */
lv_label_get_dot_tmp(lv_obj_t * obj)1229 static char * lv_label_get_dot_tmp(lv_obj_t * obj)
1230 {
1231 lv_label_t * label = (lv_label_t *)obj;
1232 if(label->dot_tmp_alloc) {
1233 return label->dot.tmp_ptr;
1234 }
1235 else {
1236 return label->dot.tmp;
1237 }
1238 }
1239
1240 /**
1241 * Free the dot_tmp_ptr field if it was previously allocated.
1242 * Always clears the field
1243 * @param label pointer to label object.
1244 */
lv_label_dot_tmp_free(lv_obj_t * obj)1245 static void lv_label_dot_tmp_free(lv_obj_t * obj)
1246 {
1247 lv_label_t * label = (lv_label_t *)obj;
1248 if(label->dot_tmp_alloc && label->dot.tmp_ptr) {
1249 lv_mem_free(label->dot.tmp_ptr);
1250 }
1251 label->dot_tmp_alloc = false;
1252 label->dot.tmp_ptr = NULL;
1253 }
1254
set_ofs_x_anim(void * obj,int32_t v)1255 static void set_ofs_x_anim(void * obj, int32_t v)
1256 {
1257 lv_label_t * label = (lv_label_t *)obj;
1258 label->offset.x = v;
1259 lv_obj_invalidate(obj);
1260 }
1261
set_ofs_y_anim(void * obj,int32_t v)1262 static void set_ofs_y_anim(void * obj, int32_t v)
1263 {
1264 lv_label_t * label = (lv_label_t *)obj;
1265 label->offset.y = v;
1266 lv_obj_invalidate(obj);
1267 }
1268
1269 #endif
1270