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_span.c (34209B)

      1 /**
      2  * @file lv_span.c
      3  *
      4  */
      5 
      6 /*********************
      7  *      INCLUDES
      8  *********************/
      9 #include "lv_span.h"
     10 
     11 #if LV_USE_SPAN != 0
     12 
     13 #include "../../../misc/lv_assert.h"
     14 
     15 /*********************
     16  *      DEFINES
     17  *********************/
     18 #define MY_CLASS &lv_spangroup_class
     19 
     20 /**********************
     21  *      TYPEDEFS
     22  **********************/
     23 typedef struct {
     24     lv_span_t * span;
     25     const char * txt;
     26     const lv_font_t * font;
     27     uint16_t   bytes;
     28     lv_coord_t txt_w;
     29     lv_coord_t line_h;
     30     lv_coord_t letter_space;
     31 } lv_snippet_t;
     32 
     33 struct _snippet_stack {
     34     lv_snippet_t    stack[LV_SPAN_SNIPPET_STACK_SIZE];
     35     uint16_t        index;
     36 };
     37 
     38 /**********************
     39  *  STATIC PROTOTYPES
     40  **********************/
     41 static void lv_spangroup_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
     42 static void lv_spangroup_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
     43 static void lv_spangroup_event(const lv_obj_class_t * class_p, lv_event_t * e);
     44 static void draw_main(lv_event_t * e);
     45 static void refresh_self_size(lv_obj_t * obj);
     46 
     47 static const lv_font_t * lv_span_get_style_text_font(lv_obj_t * par, lv_span_t * span);
     48 static lv_coord_t lv_span_get_style_text_letter_space(lv_obj_t * par, lv_span_t * span);
     49 static lv_color_t lv_span_get_style_text_color(lv_obj_t * par, lv_span_t * span);
     50 static lv_opa_t lv_span_get_style_text_opa(lv_obj_t * par, lv_span_t * span);
     51 static lv_opa_t lv_span_get_style_text_blend_mode(lv_obj_t * par, lv_span_t * span);
     52 static int32_t lv_span_get_style_text_decor(lv_obj_t * par, lv_span_t * span);
     53 
     54 static inline void span_text_check(const char ** text);
     55 static void lv_draw_span(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx);
     56 static bool lv_txt_get_snippet(const char * txt, const lv_font_t * font, lv_coord_t letter_space,
     57                                lv_coord_t max_width, lv_text_flag_t flag, lv_coord_t * use_width,
     58                                uint32_t * end_ofs);
     59 
     60 static void lv_snippet_clear(void);
     61 static uint16_t lv_get_snippet_cnt(void);
     62 static void lv_snippet_push(lv_snippet_t * item);
     63 static lv_snippet_t * lv_get_snippet(uint16_t index);
     64 static lv_coord_t convert_indent_pct(lv_obj_t * spans, lv_coord_t width);
     65 
     66 /**********************
     67  *  STATIC VARIABLES
     68  **********************/
     69 static struct _snippet_stack snippet_stack;
     70 
     71 const lv_obj_class_t lv_spangroup_class  = {
     72     .base_class = &lv_obj_class,
     73     .constructor_cb = lv_spangroup_constructor,
     74     .destructor_cb = lv_spangroup_destructor,
     75     .event_cb = lv_spangroup_event,
     76     .instance_size = sizeof(lv_spangroup_t),
     77     .width_def = LV_SIZE_CONTENT,
     78     .height_def = LV_SIZE_CONTENT,
     79 };
     80 
     81 /**********************
     82  *      MACROS
     83  **********************/
     84 
     85 /**********************
     86  *   GLOBAL FUNCTIONS
     87  **********************/
     88 
     89 lv_obj_t * lv_spangroup_create(lv_obj_t * par)
     90 {
     91     lv_obj_t * obj = lv_obj_class_create_obj(&lv_spangroup_class, par);
     92     lv_obj_class_init_obj(obj);
     93     return obj;
     94 }
     95 
     96 lv_span_t * lv_spangroup_new_span(lv_obj_t * obj)
     97 {
     98     if(obj == NULL) {
     99         return NULL;
    100     }
    101 
    102     LV_ASSERT_OBJ(obj, MY_CLASS);
    103     lv_spangroup_t * spans = (lv_spangroup_t *)obj;
    104     lv_span_t * span = _lv_ll_ins_tail(&spans->child_ll);
    105     LV_ASSERT_MALLOC(span);
    106 
    107     lv_style_init(&span->style);
    108     span->txt = (char *)"";
    109     span->static_flag = 1;
    110     span->spangroup = obj;
    111 
    112     refresh_self_size(obj);
    113 
    114     return span;
    115 }
    116 
    117 void lv_spangroup_del_span(lv_obj_t * obj, lv_span_t * span)
    118 {
    119     if(obj == NULL || span == NULL) {
    120         return;
    121     }
    122 
    123     LV_ASSERT_OBJ(obj, MY_CLASS);
    124     lv_spangroup_t * spans = (lv_spangroup_t *)obj;
    125     lv_span_t * cur_span;
    126     _LV_LL_READ(&spans->child_ll, cur_span) {
    127         if(cur_span == span) {
    128             _lv_ll_remove(&spans->child_ll, cur_span);
    129             if(cur_span->txt && cur_span->static_flag == 0) {
    130                 lv_mem_free(cur_span->txt);
    131             }
    132             lv_style_reset(&cur_span->style);
    133             lv_mem_free(cur_span);
    134             break;
    135         }
    136     }
    137 
    138     refresh_self_size(obj);
    139 }
    140 
    141 /*=====================
    142  * Setter functions
    143  *====================*/
    144 
    145 void lv_span_set_text(lv_span_t * span, const char * text)
    146 {
    147     if(span == NULL || text == NULL) {
    148         return;
    149     }
    150 
    151     if(span->txt == NULL || span->static_flag == 1) {
    152         span->txt = lv_mem_alloc(strlen(text) + 1);
    153     }
    154     else {
    155         span->txt = lv_mem_realloc(span->txt, strlen(text) + 1);
    156     }
    157     span->static_flag = 0;
    158     strcpy(span->txt, text);
    159 
    160     refresh_self_size(span->spangroup);
    161 }
    162 
    163 void lv_span_set_text_static(lv_span_t * span, const char * text)
    164 {
    165     if(span == NULL || text == NULL) {
    166         return;
    167     }
    168 
    169     if(span->txt && span->static_flag == 0) {
    170         lv_mem_free(span->txt);
    171     }
    172     span->static_flag = 1;
    173     span->txt = (char *)text;
    174 
    175     refresh_self_size(span->spangroup);
    176 }
    177 
    178 void lv_spangroup_set_align(lv_obj_t * obj, lv_text_align_t align)
    179 {
    180     lv_obj_set_style_text_align(obj, align, LV_PART_MAIN);
    181 }
    182 
    183 void lv_spangroup_set_overflow(lv_obj_t * obj, lv_span_overflow_t overflow)
    184 {
    185     LV_ASSERT_OBJ(obj, MY_CLASS);
    186     lv_spangroup_t * spans = (lv_spangroup_t *)obj;
    187     if(spans->overflow == overflow) return;
    188 
    189     spans->overflow = overflow;
    190     lv_obj_invalidate(obj);
    191 }
    192 
    193 void lv_spangroup_set_indent(lv_obj_t * obj, lv_coord_t indent)
    194 {
    195     LV_ASSERT_OBJ(obj, MY_CLASS);
    196     lv_spangroup_t * spans = (lv_spangroup_t *)obj;
    197     if(spans->indent == indent) return;
    198 
    199     spans->indent = indent;
    200 
    201     refresh_self_size(obj);
    202 }
    203 
    204 void lv_spangroup_set_mode(lv_obj_t * obj, lv_span_mode_t mode)
    205 {
    206     LV_ASSERT_OBJ(obj, MY_CLASS);
    207     lv_spangroup_t * spans = (lv_spangroup_t *)obj;
    208     spans->mode = mode;
    209     lv_spangroup_refr_mode(obj);
    210 }
    211 
    212 void lv_spangroup_set_lines(lv_obj_t * obj, int32_t lines)
    213 {
    214     LV_ASSERT_OBJ(obj, MY_CLASS);
    215     lv_spangroup_t * spans = (lv_spangroup_t *)obj;
    216     spans->lines = lines;
    217     lv_spangroup_refr_mode(obj);
    218 }
    219 
    220 /*=====================
    221  * Getter functions
    222  *====================*/
    223 
    224 lv_span_t * lv_spangroup_get_child(const lv_obj_t * obj, int32_t id)
    225 {
    226     if(obj == NULL) {
    227         return NULL;
    228     }
    229 
    230     LV_ASSERT_OBJ(obj, MY_CLASS);
    231     lv_spangroup_t * spans = (lv_spangroup_t *)obj;
    232     lv_ll_t * linked_list = &spans->child_ll;
    233 
    234     bool traverse_forwards = (id >= 0);
    235     int32_t cur_idx = 0;
    236     lv_ll_node_t * cur_node = linked_list->head;
    237 
    238     /*If using a negative index, start from the tail and use cur -1 to indicate the end*/
    239     if(!traverse_forwards) {
    240         cur_idx = -1;
    241         cur_node = linked_list->tail;
    242     }
    243 
    244     while(cur_node != NULL) {
    245         if(cur_idx == id) {
    246             return (lv_span_t *) cur_node;
    247         }
    248         if(traverse_forwards) {
    249             cur_node = (lv_ll_node_t *) _lv_ll_get_next(linked_list, cur_node);
    250             cur_idx++;
    251         }
    252         else {
    253             cur_node = (lv_ll_node_t *) _lv_ll_get_prev(linked_list, cur_node);
    254             cur_idx--;
    255         }
    256     }
    257 
    258     return NULL;
    259 }
    260 
    261 uint32_t lv_spangroup_get_child_cnt(const lv_obj_t * obj)
    262 {
    263     LV_ASSERT_OBJ(obj, MY_CLASS);
    264 
    265     if(obj == NULL) {
    266         return 0;
    267     }
    268 
    269     LV_ASSERT_OBJ(obj, MY_CLASS);
    270     lv_spangroup_t * spans = (lv_spangroup_t *)obj;
    271     return _lv_ll_get_len(&(spans->child_ll));
    272 }
    273 
    274 lv_text_align_t lv_spangroup_get_align(lv_obj_t * obj)
    275 {
    276     return lv_obj_get_style_text_align(obj, LV_PART_MAIN);
    277 }
    278 
    279 lv_span_overflow_t lv_spangroup_get_overflow(lv_obj_t * obj)
    280 {
    281     LV_ASSERT_OBJ(obj, MY_CLASS);
    282     lv_spangroup_t * spans = (lv_spangroup_t *)obj;
    283     return spans->overflow;
    284 }
    285 
    286 lv_coord_t lv_spangroup_get_indent(lv_obj_t * obj)
    287 {
    288     LV_ASSERT_OBJ(obj, MY_CLASS);
    289     lv_spangroup_t * spans = (lv_spangroup_t *)obj;
    290     return spans->indent;
    291 }
    292 
    293 lv_span_mode_t lv_spangroup_get_mode(lv_obj_t * obj)
    294 {
    295     LV_ASSERT_OBJ(obj, MY_CLASS);
    296     lv_spangroup_t * spans = (lv_spangroup_t *)obj;
    297     return spans->mode;
    298 }
    299 
    300 int32_t lv_spangroup_get_lines(lv_obj_t * obj)
    301 {
    302     LV_ASSERT_OBJ(obj, MY_CLASS);
    303     lv_spangroup_t * spans = (lv_spangroup_t *)obj;
    304     return spans->lines;
    305 }
    306 
    307 void lv_spangroup_refr_mode(lv_obj_t * obj)
    308 {
    309     LV_ASSERT_OBJ(obj, MY_CLASS);
    310     lv_spangroup_t * spans = (lv_spangroup_t *)obj;
    311 
    312     if(spans->mode == LV_SPAN_MODE_EXPAND) {
    313         lv_obj_set_width(obj, LV_SIZE_CONTENT);
    314         lv_obj_set_height(obj, LV_SIZE_CONTENT);
    315     }
    316     else if(spans->mode == LV_SPAN_MODE_BREAK) {
    317         if(lv_obj_get_style_width(obj, LV_PART_MAIN) == LV_SIZE_CONTENT) {
    318             lv_obj_set_width(obj, 100);
    319         }
    320         lv_obj_set_height(obj, LV_SIZE_CONTENT);
    321     }
    322     else if(spans->mode == LV_SPAN_MODE_FIXED) {
    323         /* use this mode, The user needs to set the size. */
    324         /* This is just to prevent an infinite loop. */
    325         if(lv_obj_get_style_width(obj, LV_PART_MAIN) == LV_SIZE_CONTENT) {
    326             lv_obj_set_width(obj, 100);
    327         }
    328         if(lv_obj_get_style_height(obj, LV_PART_MAIN) == LV_SIZE_CONTENT) {
    329             lv_coord_t width = lv_obj_get_style_width(obj, LV_PART_MAIN);
    330             if(LV_COORD_IS_PCT(width)) {
    331                 width = 100;
    332             }
    333             lv_coord_t height = lv_spangroup_get_expand_height(obj, width);
    334             lv_obj_set_content_height(obj, height);
    335         }
    336     }
    337 
    338     refresh_self_size(obj);
    339 }
    340 
    341 lv_coord_t lv_spangroup_get_max_line_h(lv_obj_t * obj)
    342 {
    343     LV_ASSERT_OBJ(obj, MY_CLASS);
    344     lv_spangroup_t * spans = (lv_spangroup_t *)obj;
    345 
    346     lv_coord_t max_line_h = 0;
    347     lv_span_t * cur_span;
    348     _LV_LL_READ(&spans->child_ll, cur_span) {
    349         const lv_font_t * font = lv_span_get_style_text_font(obj, cur_span);
    350         lv_coord_t line_h = lv_font_get_line_height(font);
    351         if(line_h > max_line_h) {
    352             max_line_h = line_h;
    353         }
    354     }
    355 
    356     return max_line_h;
    357 }
    358 
    359 uint32_t lv_spangroup_get_expand_width(lv_obj_t * obj, uint32_t max_width)
    360 {
    361     LV_ASSERT_OBJ(obj, MY_CLASS);
    362     lv_spangroup_t * spans = (lv_spangroup_t *)obj;
    363 
    364     if(_lv_ll_get_head(&spans->child_ll) == NULL) {
    365         return 0;
    366     }
    367 
    368     uint32_t width = LV_COORD_IS_PCT(spans->indent) ? 0 : spans->indent;
    369     lv_span_t * cur_span;
    370     lv_coord_t letter_space = 0;
    371     _LV_LL_READ(&spans->child_ll, cur_span) {
    372         const lv_font_t * font = lv_span_get_style_text_font(obj, cur_span);
    373         letter_space = lv_span_get_style_text_letter_space(obj, cur_span);
    374         uint32_t j = 0;
    375         const char * cur_txt = cur_span->txt;
    376         span_text_check(&cur_txt);
    377         while(cur_txt[j] != '\0') {
    378             if(max_width > 0 && width >= max_width) {
    379                 return max_width;
    380             }
    381             uint32_t letter      = _lv_txt_encoded_next(cur_txt, &j);
    382             uint32_t letter_next = _lv_txt_encoded_next(&cur_txt[j], NULL);
    383             uint16_t letter_w = lv_font_get_glyph_width(font, letter, letter_next);
    384             width = width + letter_w + letter_space;
    385         }
    386     }
    387 
    388     return width - letter_space;
    389 }
    390 
    391 lv_coord_t lv_spangroup_get_expand_height(lv_obj_t * obj, lv_coord_t width)
    392 {
    393     LV_ASSERT_OBJ(obj, MY_CLASS);
    394     lv_spangroup_t * spans = (lv_spangroup_t *)obj;
    395     if(_lv_ll_get_head(&spans->child_ll) == NULL || width <= 0) {
    396         return 0;
    397     }
    398 
    399     /* init draw variable */
    400     lv_text_flag_t txt_flag = LV_TEXT_FLAG_NONE;
    401     lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN);
    402     lv_coord_t max_width = width;
    403     lv_coord_t indent = convert_indent_pct(obj, max_width);
    404     lv_coord_t max_w  = max_width - indent; /* first line need minus indent */
    405 
    406     /* coords of draw span-txt */
    407     lv_point_t txt_pos;
    408     txt_pos.y = 0;
    409     txt_pos.x = 0 + indent; /* first line need add indent */
    410 
    411     lv_span_t * cur_span = _lv_ll_get_head(&spans->child_ll);
    412     const char * cur_txt = cur_span->txt;
    413     span_text_check(&cur_txt);
    414     uint32_t cur_txt_ofs = 0;
    415     lv_snippet_t snippet;   /* use to save cur_span info and push it to stack */
    416     memset(&snippet, 0, sizeof(snippet));
    417 
    418     int32_t line_cnt = 0;
    419     int32_t lines = spans->lines < 0 ? INT32_MAX : spans->lines;
    420     /* the loop control how many lines need to draw */
    421     while(cur_span) {
    422         int snippet_cnt = 0;
    423         lv_coord_t max_line_h = 0;  /* the max height of span-font when a line have a lot of span */
    424 
    425         /* the loop control to find a line and push the relevant span info into stack  */
    426         while(1) {
    427             /* switch to the next span when current is end */
    428             if(cur_txt[cur_txt_ofs] == '\0') {
    429                 cur_span = _lv_ll_get_next(&spans->child_ll, cur_span);
    430                 if(cur_span == NULL) break;
    431                 cur_txt = cur_span->txt;
    432                 span_text_check(&cur_txt);
    433                 cur_txt_ofs = 0;
    434                 /* maybe also cur_txt[cur_txt_ofs] == '\0' */
    435                 continue;
    436             }
    437 
    438             /* init span info to snippet. */
    439             if(cur_txt_ofs == 0) {
    440                 snippet.span = cur_span;
    441                 snippet.font = lv_span_get_style_text_font(obj, cur_span);
    442                 snippet.letter_space = lv_span_get_style_text_letter_space(obj, cur_span);
    443                 snippet.line_h = lv_font_get_line_height(snippet.font) + line_space;
    444             }
    445 
    446             /* get current span text line info */
    447             uint32_t next_ofs = 0;
    448             lv_coord_t use_width = 0;
    449             bool isfill = lv_txt_get_snippet(&cur_txt[cur_txt_ofs], snippet.font, snippet.letter_space,
    450                                              max_w, txt_flag, &use_width, &next_ofs);
    451 
    452             /* break word deal width */
    453             if(isfill && next_ofs > 0 && snippet_cnt > 0) {
    454                 if(max_w < use_width) {
    455                     break;
    456                 }
    457 
    458                 uint32_t tmp_ofs = next_ofs;
    459                 uint32_t letter = _lv_txt_encoded_prev(&cur_txt[cur_txt_ofs], &tmp_ofs);
    460                 if(!(letter == '\0' || letter == '\n' || letter == '\r' || _lv_txt_is_break_char(letter))) {
    461                     tmp_ofs = 0;
    462                     letter = _lv_txt_encoded_next(&cur_txt[cur_txt_ofs + next_ofs], &tmp_ofs);
    463                     if(!(letter == '\0' || letter == '\n'  || letter == '\r' || _lv_txt_is_break_char(letter))) {
    464                         break;
    465                     }
    466                 }
    467             }
    468 
    469             snippet.txt = &cur_txt[cur_txt_ofs];
    470             snippet.bytes = next_ofs;
    471             snippet.txt_w = use_width;
    472             cur_txt_ofs += next_ofs;
    473             if(max_line_h < snippet.line_h) {
    474                 max_line_h = snippet.line_h;
    475             }
    476             snippet_cnt ++;
    477             max_w = max_w - use_width - snippet.letter_space;
    478             if(isfill  || max_w <= 0) {
    479                 break;
    480             }
    481         }
    482 
    483         /* next line init */
    484         txt_pos.x = 0;
    485         txt_pos.y += max_line_h;
    486         max_w = max_width;
    487         line_cnt += 1;
    488         if(line_cnt >= lines) {
    489             break;
    490         }
    491     }
    492     txt_pos.y -= line_space;
    493 
    494     return txt_pos.y;
    495 }
    496 
    497 /**********************
    498  *   STATIC FUNCTIONS
    499  **********************/
    500 
    501 static void lv_spangroup_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
    502 {
    503     LV_UNUSED(class_p);
    504     lv_spangroup_t * spans = (lv_spangroup_t *)obj;
    505     _lv_ll_init(&spans->child_ll, sizeof(lv_span_t));
    506     spans->indent = 0;
    507     spans->lines = -1;
    508     spans->mode = LV_SPAN_MODE_EXPAND;
    509     spans->overflow = LV_SPAN_OVERFLOW_CLIP;
    510     spans->cache_w = 0;
    511     spans->cache_h = 0;
    512     spans->refresh = 1;
    513 }
    514 
    515 static void lv_spangroup_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
    516 {
    517     LV_UNUSED(class_p);
    518     lv_spangroup_t * spans = (lv_spangroup_t *)obj;
    519     lv_span_t * cur_span = _lv_ll_get_head(&spans->child_ll);
    520     while(cur_span) {
    521         _lv_ll_remove(&spans->child_ll, cur_span);
    522         if(cur_span->txt && cur_span->static_flag == 0) {
    523             lv_mem_free(cur_span->txt);
    524         }
    525         lv_style_reset(&cur_span->style);
    526         lv_mem_free(cur_span);
    527         cur_span = _lv_ll_get_head(&spans->child_ll);
    528     }
    529 }
    530 
    531 static void lv_spangroup_event(const lv_obj_class_t * class_p, lv_event_t * e)
    532 {
    533     LV_UNUSED(class_p);
    534 
    535     /* Call the ancestor's event handler */
    536     if(lv_obj_event_base(MY_CLASS, e) != LV_RES_OK) return;
    537 
    538     lv_event_code_t code = lv_event_get_code(e);
    539     lv_obj_t * obj = lv_event_get_target(e);
    540     lv_spangroup_t * spans = (lv_spangroup_t *)obj;
    541 
    542     if(code == LV_EVENT_DRAW_MAIN) {
    543         draw_main(e);
    544     }
    545     else if(code == LV_EVENT_STYLE_CHANGED) {
    546         refresh_self_size(obj);
    547     }
    548     else if(code == LV_EVENT_SIZE_CHANGED) {
    549         refresh_self_size(obj);
    550     }
    551     else if(code == LV_EVENT_GET_SELF_SIZE) {
    552         lv_coord_t width = 0;
    553         lv_coord_t height = 0;
    554         lv_point_t * self_size = lv_event_get_param(e);
    555 
    556         if(spans->mode == LV_SPAN_MODE_EXPAND) {
    557             if(spans->refresh) {
    558                 spans->cache_w = (lv_coord_t)lv_spangroup_get_expand_width(obj, 0);
    559                 spans->cache_h = lv_spangroup_get_max_line_h(obj);
    560                 spans->refresh = 0;
    561             }
    562             width = spans->cache_w;
    563             height = spans->cache_h;
    564         }
    565         else if(spans->mode == LV_SPAN_MODE_BREAK) {
    566             width = lv_obj_get_content_width(obj);
    567             if(self_size->y >= 0) {
    568                 if(width != spans->cache_w || spans->refresh) {
    569                     height = lv_spangroup_get_expand_height(obj, width);
    570                     spans->cache_w = width;
    571                     spans->cache_h = height;
    572                     spans->refresh = 0;
    573                 }
    574                 else {
    575                     height = spans->cache_h;
    576                 }
    577             }
    578         }
    579         else if(spans->mode == LV_SPAN_MODE_FIXED) {
    580             width =  self_size->x >= 0 ? lv_obj_get_content_width(obj) : 0;
    581             height = self_size->y >= 0 ? lv_obj_get_content_height(obj) : 0;
    582         }
    583         self_size->x = LV_MAX(self_size->x, width);
    584         self_size->y = LV_MAX(self_size->y, height);
    585     }
    586 }
    587 
    588 static void draw_main(lv_event_t * e)
    589 {
    590     lv_obj_t * obj = lv_event_get_target(e);
    591     lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
    592 
    593     lv_draw_span(obj, draw_ctx);
    594 }
    595 
    596 /**
    597  * @return true for txt fill the max_width.
    598  */
    599 static bool lv_txt_get_snippet(const char * txt, const lv_font_t * font,
    600                                lv_coord_t letter_space, lv_coord_t max_width, lv_text_flag_t flag,
    601                                lv_coord_t * use_width, uint32_t * end_ofs)
    602 {
    603     if(txt == NULL || txt[0] == '\0') {
    604         *end_ofs = 0;
    605         *use_width = 0;
    606         return false;
    607     }
    608 
    609     uint32_t ofs = _lv_txt_get_next_line(txt, font, letter_space, max_width, use_width, flag);
    610     *end_ofs = ofs;
    611 
    612     if(txt[ofs] == '\0' && *use_width < max_width) {
    613         return false;
    614     }
    615     else {
    616         return true;
    617     }
    618 }
    619 
    620 static void lv_snippet_push(lv_snippet_t * item)
    621 {
    622     if(snippet_stack.index < LV_SPAN_SNIPPET_STACK_SIZE) {
    623         memcpy(&snippet_stack.stack[snippet_stack.index], item, sizeof(lv_snippet_t));
    624         snippet_stack.index++;
    625     }
    626     else {
    627         LV_LOG_ERROR("span draw stack overflow, please set LV_SPAN_SNIPPET_STACK_SIZE too larger");
    628     }
    629 }
    630 
    631 static uint16_t lv_get_snippet_cnt(void)
    632 {
    633     return snippet_stack.index;
    634 }
    635 
    636 static lv_snippet_t * lv_get_snippet(uint16_t index)
    637 {
    638     return &snippet_stack.stack[index];
    639 }
    640 
    641 static void lv_snippet_clear(void)
    642 {
    643     snippet_stack.index = 0;
    644 }
    645 
    646 static const lv_font_t * lv_span_get_style_text_font(lv_obj_t * par, lv_span_t * span)
    647 {
    648     const lv_font_t * font;
    649     lv_style_value_t value;
    650     lv_res_t res = lv_style_get_prop(&span->style, LV_STYLE_TEXT_FONT, &value);
    651     if(res != LV_RES_OK) {
    652         font = lv_obj_get_style_text_font(par, LV_PART_MAIN);
    653     }
    654     else {
    655         font = (const lv_font_t *)value.ptr;
    656     }
    657     return font;
    658 }
    659 
    660 static lv_coord_t lv_span_get_style_text_letter_space(lv_obj_t * par, lv_span_t * span)
    661 {
    662     lv_coord_t letter_space;
    663     lv_style_value_t value;
    664     lv_res_t res = lv_style_get_prop(&span->style, LV_STYLE_TEXT_LETTER_SPACE, &value);
    665     if(res != LV_RES_OK) {
    666         letter_space = lv_obj_get_style_text_letter_space(par, LV_PART_MAIN);
    667     }
    668     else {
    669         letter_space = (lv_coord_t)value.num;
    670     }
    671     return letter_space;
    672 }
    673 
    674 static lv_color_t lv_span_get_style_text_color(lv_obj_t * par, lv_span_t * span)
    675 {
    676     lv_style_value_t value;
    677     lv_res_t res = lv_style_get_prop(&span->style, LV_STYLE_TEXT_COLOR, &value);
    678     if(res != LV_RES_OK) {
    679         value.color = lv_obj_get_style_text_color(par, LV_PART_MAIN);
    680     }
    681     return value.color;
    682 }
    683 
    684 static lv_opa_t lv_span_get_style_text_opa(lv_obj_t * par, lv_span_t * span)
    685 {
    686     lv_opa_t opa;
    687     lv_style_value_t value;
    688     lv_res_t res = lv_style_get_prop(&span->style, LV_STYLE_TEXT_OPA, &value);
    689     if(res != LV_RES_OK) {
    690         opa = (lv_opa_t)lv_obj_get_style_text_opa(par, LV_PART_MAIN);
    691     }
    692     else {
    693         opa = (lv_opa_t)value.num;
    694     }
    695     return opa;
    696 }
    697 
    698 static lv_blend_mode_t lv_span_get_style_text_blend_mode(lv_obj_t * par, lv_span_t * span)
    699 {
    700     lv_blend_mode_t mode;
    701     lv_style_value_t value;
    702     lv_res_t res = lv_style_get_prop(&span->style, LV_STYLE_BLEND_MODE, &value);
    703     if(res != LV_RES_OK) {
    704         mode = (lv_blend_mode_t)lv_obj_get_style_blend_mode(par, LV_PART_MAIN);
    705     }
    706     else {
    707         mode = (lv_blend_mode_t)value.num;
    708     }
    709     return mode;
    710 }
    711 
    712 static int32_t lv_span_get_style_text_decor(lv_obj_t * par, lv_span_t * span)
    713 {
    714     int32_t decor;
    715     lv_style_value_t value;
    716     lv_res_t res = lv_style_get_prop(&span->style, LV_STYLE_TEXT_DECOR, &value);
    717     if(res != LV_RES_OK) {
    718         decor = (lv_text_decor_t)lv_obj_get_style_text_decor(par, LV_PART_MAIN);;
    719     }
    720     else {
    721         decor = (int32_t)value.num;
    722     }
    723     return decor;
    724 }
    725 
    726 static inline void span_text_check(const char ** text)
    727 {
    728     if(*text == NULL) {
    729         *text = "";
    730         LV_LOG_ERROR("occur an error that span text == NULL");
    731     }
    732 }
    733 
    734 static lv_coord_t convert_indent_pct(lv_obj_t * obj, lv_coord_t width)
    735 {
    736     lv_spangroup_t * spans = (lv_spangroup_t *)obj;
    737 
    738     lv_coord_t indent = spans->indent;
    739     if(LV_COORD_IS_PCT(spans->indent)) {
    740         if(spans->mode == LV_SPAN_MODE_EXPAND) {
    741             indent = 0;
    742         }
    743         else {
    744             indent = (width * LV_COORD_GET_PCT(spans->indent)) / 100;
    745         }
    746     }
    747 
    748     return indent;
    749 }
    750 
    751 /**
    752  * draw span group
    753  * @param spans obj handle
    754  * @param coords coordinates of the label
    755  * @param mask the label will be drawn only in this area
    756  */
    757 static void lv_draw_span(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx)
    758 {
    759 
    760     lv_area_t coords;
    761     lv_obj_get_content_coords(obj, &coords);
    762 
    763     lv_spangroup_t * spans = (lv_spangroup_t *)obj;
    764 
    765     /* return if not span */
    766     if(_lv_ll_get_head(&spans->child_ll) == NULL) {
    767         return;
    768     }
    769 
    770     /* return if no draw area */
    771     lv_area_t clip_area;
    772     if(!_lv_area_intersect(&clip_area, &coords, draw_ctx->clip_area))  return;
    773     const lv_area_t * clip_area_ori = draw_ctx->clip_area;
    774     draw_ctx->clip_area = &clip_area;
    775 
    776     /* init draw variable */
    777     lv_text_flag_t txt_flag = LV_TEXT_FLAG_NONE;
    778     lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN);;
    779     lv_coord_t max_width = lv_area_get_width(&coords);
    780     lv_coord_t indent = convert_indent_pct(obj, max_width);
    781     lv_coord_t max_w  = max_width - indent; /* first line need minus indent */
    782     lv_opa_t obj_opa = lv_obj_get_style_opa(obj, LV_PART_MAIN);
    783 
    784     /* coords of draw span-txt */
    785     lv_point_t txt_pos;
    786     txt_pos.y = coords.y1;
    787     txt_pos.x = coords.x1 + indent; /* first line need add indent */
    788 
    789     lv_span_t * cur_span = _lv_ll_get_head(&spans->child_ll);
    790     const char * cur_txt = cur_span->txt;
    791     span_text_check(&cur_txt);
    792     uint32_t cur_txt_ofs = 0;
    793     lv_snippet_t snippet;   /* use to save cur_span info and push it to stack */
    794     lv_memset_00(&snippet, sizeof(snippet));
    795 
    796     lv_draw_label_dsc_t label_draw_dsc;
    797     lv_draw_label_dsc_init(&label_draw_dsc);
    798 
    799     bool is_first_line = true;
    800     /* the loop control how many lines need to draw */
    801     while(cur_span) {
    802         bool is_end_line = false;
    803         bool ellipsis_valid = false;
    804         lv_coord_t max_line_h = 0;  /* the max height of span-font when a line have a lot of span */
    805         lv_coord_t max_baseline = 0; /*baseline of the highest span*/
    806         lv_snippet_clear();
    807 
    808         /* the loop control to find a line and push the relevant span info into stack  */
    809         while(1) {
    810             /* switch to the next span when current is end */
    811             if(cur_txt[cur_txt_ofs] == '\0') {
    812                 cur_span = _lv_ll_get_next(&spans->child_ll, cur_span);
    813                 if(cur_span == NULL) break;
    814                 cur_txt = cur_span->txt;
    815                 span_text_check(&cur_txt);
    816                 cur_txt_ofs = 0;
    817                 /* maybe also cur_txt[cur_txt_ofs] == '\0' */
    818                 continue;
    819             }
    820 
    821             /* init span info to snippet. */
    822             if(cur_txt_ofs == 0) {
    823                 snippet.span = cur_span;
    824                 snippet.font = lv_span_get_style_text_font(obj, cur_span);
    825                 snippet.letter_space = lv_span_get_style_text_letter_space(obj, cur_span);
    826                 snippet.line_h = lv_font_get_line_height(snippet.font) + line_space;
    827             }
    828 
    829             /* get current span text line info */
    830             uint32_t next_ofs = 0;
    831             lv_coord_t use_width = 0;
    832             bool isfill = lv_txt_get_snippet(&cur_txt[cur_txt_ofs], snippet.font, snippet.letter_space,
    833                                              max_w, txt_flag, &use_width, &next_ofs);
    834 
    835             if(isfill) {
    836                 if(next_ofs > 0 && lv_get_snippet_cnt() > 0) {
    837                     /* To prevent infinite loops, the _lv_txt_get_next_line() may return incomplete words, */
    838                     /* This phenomenon should be avoided when lv_get_snippet_cnt() > 0 */
    839                     if(max_w < use_width) {
    840                         break;
    841                     }
    842                     uint32_t tmp_ofs = next_ofs;
    843                     uint32_t letter = _lv_txt_encoded_prev(&cur_txt[cur_txt_ofs], &tmp_ofs);
    844                     if(!(letter == '\0' || letter == '\n' || letter == '\r' || _lv_txt_is_break_char(letter))) {
    845                         tmp_ofs = 0;
    846                         letter = _lv_txt_encoded_next(&cur_txt[cur_txt_ofs + next_ofs], &tmp_ofs);
    847                         if(!(letter == '\0' || letter == '\n'  || letter == '\r' || _lv_txt_is_break_char(letter))) {
    848                             break;
    849                         }
    850                     }
    851                 }
    852             }
    853 
    854             snippet.txt = &cur_txt[cur_txt_ofs];
    855             snippet.bytes = next_ofs;
    856             snippet.txt_w = use_width;
    857             cur_txt_ofs += next_ofs;
    858             if(max_line_h < snippet.line_h) {
    859                 max_line_h = snippet.line_h;
    860                 max_baseline = snippet.font->base_line;
    861             }
    862 
    863             lv_snippet_push(&snippet);
    864             max_w = max_w - use_width - snippet.letter_space;
    865             if(isfill || max_w <= 0) {
    866                 break;
    867             }
    868         }
    869 
    870         /* start current line deal with */
    871 
    872         uint16_t item_cnt = lv_get_snippet_cnt();
    873         if(item_cnt == 0) {     /* break if stack is empty */
    874             break;
    875         }
    876 
    877         /* Whether the current line is the end line and does overflow processing */
    878         {
    879             lv_snippet_t * last_snippet = lv_get_snippet(item_cnt - 1);
    880             lv_coord_t next_line_h = last_snippet->line_h;
    881             if(last_snippet->txt[last_snippet->bytes] == '\0') {
    882                 next_line_h = 0;
    883                 lv_span_t * next_span = _lv_ll_get_next(&spans->child_ll, last_snippet->span);
    884                 if(next_span) { /* have the next line */
    885                     next_line_h = lv_font_get_line_height(lv_span_get_style_text_font(obj, next_span)) + line_space;
    886                 }
    887             }
    888             if(txt_pos.y + max_line_h + next_line_h - line_space > coords.y2 + 1) { /* for overflow if is end line. */
    889                 if(last_snippet->txt[last_snippet->bytes] != '\0') {
    890                     last_snippet->bytes = strlen(last_snippet->txt);
    891                     last_snippet->txt_w = lv_txt_get_width(last_snippet->txt, last_snippet->bytes, last_snippet->font,
    892                                                            last_snippet->letter_space, txt_flag);
    893                 }
    894                 ellipsis_valid = spans->overflow == LV_SPAN_OVERFLOW_ELLIPSIS ? true : false;
    895                 is_end_line = true;
    896             }
    897         }
    898 
    899         /*Go the first visible line*/
    900         if(txt_pos.y + max_line_h < clip_area.y1) {
    901             goto Next_line_init;
    902         }
    903 
    904         /* align deal with */
    905         lv_text_align_t align = lv_obj_get_style_text_align(obj, LV_PART_MAIN);
    906         if(align == LV_TEXT_ALIGN_CENTER || align == LV_TEXT_ALIGN_RIGHT) {
    907             lv_coord_t align_ofs = 0;
    908             lv_coord_t txts_w = is_first_line ? indent : 0;
    909             for(int i = 0; i < item_cnt; i++) {
    910                 lv_snippet_t * pinfo = lv_get_snippet(i);
    911                 txts_w = txts_w + pinfo->txt_w + pinfo->letter_space;
    912             }
    913             txts_w -= lv_get_snippet(item_cnt - 1)->letter_space;
    914             align_ofs = max_width > txts_w ? max_width - txts_w : 0;
    915             if(align == LV_TEXT_ALIGN_CENTER) {
    916                 align_ofs = align_ofs >> 1;
    917             }
    918             txt_pos.x += align_ofs;
    919         }
    920 
    921         /* draw line letters */
    922         int i;
    923         for(i = 0; i < item_cnt; i++) {
    924             lv_snippet_t * pinfo = lv_get_snippet(i);
    925 
    926             /* bidi deal with:todo */
    927             const char * bidi_txt = pinfo->txt;
    928 
    929             lv_point_t pos;
    930             pos.x = txt_pos.x;
    931             pos.y = txt_pos.y + max_line_h - pinfo->line_h - (max_baseline - pinfo->font->base_line);
    932             label_draw_dsc.color = lv_span_get_style_text_color(obj, pinfo->span);
    933             label_draw_dsc.opa = lv_span_get_style_text_opa(obj, pinfo->span);
    934             label_draw_dsc.font = lv_span_get_style_text_font(obj, pinfo->span);
    935             label_draw_dsc.blend_mode = lv_span_get_style_text_blend_mode(obj, pinfo->span);
    936             if(obj_opa < LV_OPA_MAX) {
    937                 label_draw_dsc.opa = (uint16_t)((uint16_t)label_draw_dsc.opa * obj_opa) >> 8;
    938             }
    939             uint32_t txt_bytes = pinfo->bytes;
    940 
    941             /* overflow */
    942             uint16_t dot_letter_w = 0;
    943             uint16_t dot_width = 0;
    944             if(ellipsis_valid) {
    945                 dot_letter_w = lv_font_get_glyph_width(pinfo->font, '.', '.');
    946                 dot_width = dot_letter_w * 3;
    947             }
    948             lv_coord_t ellipsis_width = coords.x1 + max_width - dot_width;
    949 
    950             uint32_t j = 0;
    951             while(j < txt_bytes) {
    952                 /* skip invalid fields */
    953                 if(pos.x > clip_area.x2) {
    954                     break;
    955                 }
    956                 uint32_t letter      = _lv_txt_encoded_next(bidi_txt, &j);
    957                 uint32_t letter_next = _lv_txt_encoded_next(&bidi_txt[j], NULL);
    958                 int32_t letter_w = lv_font_get_glyph_width(pinfo->font, letter, letter_next);
    959 
    960                 /* skip invalid fields */
    961                 if(pos.x + letter_w + pinfo->letter_space < clip_area.x1) {
    962                     if(letter_w > 0) {
    963                         pos.x = pos.x + letter_w + pinfo->letter_space;
    964                     }
    965                     continue;
    966                 }
    967 
    968                 if(ellipsis_valid && pos.x + letter_w + pinfo->letter_space > ellipsis_width) {
    969                     for(int ell = 0; ell < 3; ell++) {
    970                         lv_draw_letter(draw_ctx, &label_draw_dsc, &pos, '.');
    971                         pos.x = pos.x + dot_letter_w + pinfo->letter_space;
    972                     }
    973                     if(pos.x <= ellipsis_width) {
    974                         pos.x = ellipsis_width + 1;
    975                     }
    976                     break;
    977                 }
    978                 else {
    979                     lv_draw_letter(draw_ctx, &label_draw_dsc, &pos, letter);
    980                     if(letter_w > 0) {
    981                         pos.x = pos.x + letter_w + pinfo->letter_space;
    982                     }
    983                 }
    984             }
    985 
    986             /* draw decor */
    987             lv_text_decor_t decor = lv_span_get_style_text_decor(obj, pinfo->span);
    988             if(decor != LV_TEXT_DECOR_NONE) {
    989                 lv_draw_line_dsc_t line_dsc;
    990                 lv_draw_line_dsc_init(&line_dsc);
    991                 line_dsc.color = label_draw_dsc.color;
    992                 line_dsc.width = label_draw_dsc.font->underline_thickness ? pinfo->font->underline_thickness : 1;
    993                 line_dsc.opa = label_draw_dsc.opa;
    994                 line_dsc.blend_mode = label_draw_dsc.blend_mode;
    995 
    996                 if(decor & LV_TEXT_DECOR_STRIKETHROUGH) {
    997                     lv_point_t p1;
    998                     lv_point_t p2;
    999                     p1.x = txt_pos.x;
   1000                     p1.y = pos.y + ((pinfo->line_h - line_space) >> 1)  + (line_dsc.width >> 1);
   1001                     p2.x = pos.x;
   1002                     p2.y = p1.y;
   1003                     lv_draw_line(draw_ctx, &line_dsc, &p1, &p2);
   1004                 }
   1005 
   1006                 if(decor & LV_TEXT_DECOR_UNDERLINE) {
   1007                     lv_point_t p1;
   1008                     lv_point_t p2;
   1009                     p1.x = txt_pos.x;
   1010                     p1.y = pos.y + pinfo->line_h - line_space - pinfo->font->base_line - pinfo->font->underline_position;
   1011                     p2.x = pos.x;
   1012                     p2.y = p1.y;
   1013                     lv_draw_line(draw_ctx, &line_dsc, &p1, &p2);
   1014                 }
   1015             }
   1016             txt_pos.x = pos.x;
   1017         }
   1018 
   1019 Next_line_init:
   1020         /* next line init */
   1021         is_first_line = false;
   1022         txt_pos.x = coords.x1;
   1023         txt_pos.y += max_line_h;
   1024         if(is_end_line || txt_pos.y > clip_area.y2 + 1) {
   1025             draw_ctx->clip_area = clip_area_ori;
   1026             return;
   1027         }
   1028         max_w = max_width;
   1029     }
   1030     draw_ctx->clip_area = clip_area_ori;
   1031 }
   1032 
   1033 static void refresh_self_size(lv_obj_t * obj)
   1034 {
   1035     lv_spangroup_t * spans = (lv_spangroup_t *)obj;
   1036     spans->refresh = 1;
   1037     lv_obj_invalidate(obj);
   1038     lv_obj_refresh_self_size(obj);
   1039 }
   1040 
   1041 #endif