acid-drop

- Hacking the planet from a LilyGo T-Deck using custom firmware
git clone git://git.acid.vegas/acid-drop.git
Log | Files | Refs | Archive | README | LICENSE

lv_label.c (43466B)

      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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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  */
    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 
   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  */
   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  */
   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  */
   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 
   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 
   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