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