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