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 uint16_t anim_speed = lv_obj_get_style_anim_speed(obj, LV_PART_MAIN);
1025 if(anim_speed == 0) anim_speed = LV_LABEL_DEF_SCROLL_SPEED;
1026 lv_anim_t a;
1027 lv_anim_init(&a);
1028 lv_anim_set_var(&a, obj);
1029 lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
1030
1031 bool hor_anim = false;
1032 if(size.x > lv_area_get_width(&txt_coords)) {
1033 #if LV_USE_BIDI
1034 int32_t start, end;
1035 lv_base_dir_t base_dir = lv_obj_get_style_base_dir(obj, LV_PART_MAIN);
1036
1037 if(base_dir == LV_BASE_DIR_AUTO)
1038 base_dir = _lv_bidi_detect_base_dir(label->text);
1039
1040 if(base_dir == LV_BASE_DIR_RTL) {
1041 start = -size.x - lv_font_get_glyph_width(font, ' ', ' ') * LV_LABEL_WAIT_CHAR_COUNT;
1042 end = 0;
1043 }
1044 else {
1045 start = 0;
1046 end = -size.x - lv_font_get_glyph_width(font, ' ', ' ') * LV_LABEL_WAIT_CHAR_COUNT;
1047 }
1048
1049 lv_anim_set_values(&a, start, end);
1050 #else
1051 lv_anim_set_values(&a, 0, -size.x - lv_font_get_glyph_width(font, ' ', ' ') * LV_LABEL_WAIT_CHAR_COUNT);
1052 #endif
1053 lv_anim_set_exec_cb(&a, set_ofs_x_anim);
1054 lv_anim_set_time(&a, lv_anim_speed_to_time(anim_speed, a.start_value, a.end_value));
1055
1056 lv_anim_t * anim_cur = lv_anim_get(obj, set_ofs_x_anim);
1057 int32_t act_time = anim_cur ? anim_cur->act_time : 0;
1058 if(act_time < a.time) {
1059 a.act_time = act_time; /*To keep the old position*/
1060 a.early_apply = 0;
1061 }
1062
1063 lv_anim_start(&a);
1064 hor_anim = true;
1065 }
1066 else {
1067 /*Delete the offset animation if not required*/
1068 lv_anim_del(obj, set_ofs_x_anim);
1069 label->offset.x = 0;
1070 }
1071
1072 if(size.y > lv_area_get_height(&txt_coords) && hor_anim == false) {
1073 lv_anim_set_values(&a, 0, -size.y - (lv_font_get_line_height(font)));
1074 lv_anim_set_exec_cb(&a, set_ofs_y_anim);
1075 lv_anim_set_time(&a, lv_anim_speed_to_time(anim_speed, a.start_value, a.end_value));
1076
1077 lv_anim_t * anim_cur = lv_anim_get(obj, set_ofs_y_anim);
1078 int32_t act_time = anim_cur ? anim_cur->act_time : 0;
1079 if(act_time < a.time) {
1080 a.act_time = act_time; /*To keep the old position*/
1081 a.early_apply = 0;
1082 }
1083
1084 lv_anim_start(&a);
1085 }
1086 else {
1087 /*Delete the offset animation if not required*/
1088 lv_anim_del(obj, set_ofs_y_anim);
1089 label->offset.y = 0;
1090 }
1091 }
1092 else if(label->long_mode == LV_LABEL_LONG_DOT) {
1093 if(size.y <= lv_area_get_height(&txt_coords)) { /*No dots are required, the text is short enough*/
1094 label->dot_end = LV_LABEL_DOT_END_INV;
1095 }
1096 else if(size.y <= lv_font_get_line_height(font)) { /*No dots are required for one-line texts*/
1097 label->dot_end = LV_LABEL_DOT_END_INV;
1098 }
1099 else if(_lv_txt_get_encoded_length(label->text) <= LV_LABEL_DOT_NUM) { /*Don't turn to dots all the characters*/
1100 label->dot_end = LV_LABEL_DOT_END_INV;
1101 }
1102 else {
1103 lv_point_t p;
1104 lv_coord_t y_overed;
1105 p.x = lv_area_get_width(&txt_coords) -
1106 (lv_font_get_glyph_width(font, '.', '.') + letter_space) *
1107 LV_LABEL_DOT_NUM; /*Shrink with dots*/
1108 p.y = lv_area_get_height(&txt_coords);
1109 y_overed = p.y %
1110 (lv_font_get_line_height(font) + line_space); /*Round down to the last line*/
1111 if(y_overed >= lv_font_get_line_height(font)) {
1112 p.y -= y_overed;
1113 p.y += lv_font_get_line_height(font);
1114 }
1115 else {
1116 p.y -= y_overed;
1117 p.y -= line_space;
1118 }
1119
1120 uint32_t letter_id = lv_label_get_letter_on(obj, &p);
1121
1122 /*Be sure there is space for the dots*/
1123 size_t txt_len = strlen(label->text);
1124 uint32_t byte_id = _lv_txt_encoded_get_byte_id(label->text, letter_id);
1125 while(byte_id + LV_LABEL_DOT_NUM > txt_len) {
1126 _lv_txt_encoded_prev(label->text, &byte_id);
1127 letter_id--;
1128 }
1129
1130 /*Save letters under the dots and replace them with dots*/
1131 uint32_t byte_id_ori = byte_id;
1132 uint32_t i;
1133 uint8_t len = 0;
1134 for(i = 0; i <= LV_LABEL_DOT_NUM; i++) {
1135 len += _lv_txt_encoded_size(&label->text[byte_id]);
1136 _lv_txt_encoded_next(label->text, &byte_id);
1137 if(len > LV_LABEL_DOT_NUM || byte_id > txt_len) {
1138 break;
1139 }
1140 }
1141
1142 if(lv_label_set_dot_tmp(obj, &label->text[byte_id_ori], len)) {
1143 for(i = 0; i < LV_LABEL_DOT_NUM; i++) {
1144 label->text[byte_id_ori + i] = '.';
1145 }
1146 label->text[byte_id_ori + LV_LABEL_DOT_NUM] = '\0';
1147 label->dot_end = letter_id + LV_LABEL_DOT_NUM;
1148 }
1149 }
1150 }
1151 else if(label->long_mode == LV_LABEL_LONG_CLIP) {
1152 /*Do nothing*/
1153 }
1154
1155 lv_obj_invalidate(obj);
1156 }
1157
1158
lv_label_revert_dots(lv_obj_t * obj)1159 static void lv_label_revert_dots(lv_obj_t * obj)
1160 {
1161
1162 lv_label_t * label = (lv_label_t *)obj;
1163
1164 if(label->long_mode != LV_LABEL_LONG_DOT) return;
1165 if(label->dot_end == LV_LABEL_DOT_END_INV) return;
1166 uint32_t letter_i = label->dot_end - LV_LABEL_DOT_NUM;
1167 uint32_t byte_i = _lv_txt_encoded_get_byte_id(label->text, letter_i);
1168
1169 /*Restore the characters*/
1170 uint8_t i = 0;
1171 char * dot_tmp = lv_label_get_dot_tmp(obj);
1172 while(label->text[byte_i + i] != '\0') {
1173 label->text[byte_i + i] = dot_tmp[i];
1174 i++;
1175 }
1176 label->text[byte_i + i] = dot_tmp[i];
1177 lv_label_dot_tmp_free(obj);
1178
1179 label->dot_end = LV_LABEL_DOT_END_INV;
1180 }
1181
1182 /**
1183 * Store `len` characters from `data`. Allocates space if necessary.
1184 *
1185 * @param label pointer to label object
1186 * @param len Number of characters to store.
1187 * @return true on success.
1188 */
lv_label_set_dot_tmp(lv_obj_t * obj,char * data,uint32_t len)1189 static bool lv_label_set_dot_tmp(lv_obj_t * obj, char * data, uint32_t len)
1190 {
1191
1192 lv_label_t * label = (lv_label_t *)obj;
1193 lv_label_dot_tmp_free(obj); /*Deallocate any existing space*/
1194 if(len > sizeof(char *)) {
1195 /*Memory needs to be allocated. Allocates an additional byte
1196 *for a NULL-terminator so it can be copied.*/
1197 label->dot.tmp_ptr = lv_mem_alloc(len + 1);
1198 if(label->dot.tmp_ptr == NULL) {
1199 LV_LOG_ERROR("Failed to allocate memory for dot_tmp_ptr");
1200 return false;
1201 }
1202 lv_memcpy(label->dot.tmp_ptr, data, len);
1203 label->dot.tmp_ptr[len] = '\0';
1204 label->dot_tmp_alloc = true;
1205 }
1206 else {
1207 /*Characters can be directly stored in object*/
1208 label->dot_tmp_alloc = false;
1209 lv_memcpy(label->dot.tmp, data, len);
1210 }
1211 return true;
1212 }
1213
1214 /**
1215 * Get the stored dot_tmp characters
1216 * @param label pointer to label object
1217 * @return char pointer to a stored characters. Is *not* necessarily NULL-terminated.
1218 */
lv_label_get_dot_tmp(lv_obj_t * obj)1219 static char * lv_label_get_dot_tmp(lv_obj_t * obj)
1220 {
1221 lv_label_t * label = (lv_label_t *)obj;
1222 if(label->dot_tmp_alloc) {
1223 return label->dot.tmp_ptr;
1224 }
1225 else {
1226 return label->dot.tmp;
1227 }
1228 }
1229
1230 /**
1231 * Free the dot_tmp_ptr field if it was previously allocated.
1232 * Always clears the field
1233 * @param label pointer to label object.
1234 */
lv_label_dot_tmp_free(lv_obj_t * obj)1235 static void lv_label_dot_tmp_free(lv_obj_t * obj)
1236 {
1237 lv_label_t * label = (lv_label_t *)obj;
1238 if(label->dot_tmp_alloc && label->dot.tmp_ptr) {
1239 lv_mem_free(label->dot.tmp_ptr);
1240 }
1241 label->dot_tmp_alloc = false;
1242 label->dot.tmp_ptr = NULL;
1243 }
1244
1245
set_ofs_x_anim(void * obj,int32_t v)1246 static void set_ofs_x_anim(void * obj, int32_t v)
1247 {
1248 lv_label_t * label = (lv_label_t *)obj;
1249 label->offset.x = v;
1250 lv_obj_invalidate(obj);
1251 }
1252
set_ofs_y_anim(void * obj,int32_t v)1253 static void set_ofs_y_anim(void * obj, int32_t v)
1254 {
1255 lv_label_t * label = (lv_label_t *)obj;
1256 label->offset.y = v;
1257 lv_obj_invalidate(obj);
1258 }
1259
1260
1261 #endif
1262