diff --git a/src/draw/lv_draw_label.c b/src/draw/lv_draw_label.c index d2e5f2a11..d072b449d 100644 --- a/src/draw/lv_draw_label.c +++ b/src/draw/lv_draw_label.c @@ -67,6 +67,7 @@ void lv_draw_label_dsc_init(lv_draw_label_dsc_t * dsc) lv_memzero(dsc, sizeof(lv_draw_label_dsc_t)); dsc->opa = LV_OPA_COVER; dsc->color = lv_color_black(); + dsc->text_length = LV_TEXT_LEN_MAX; dsc->font = LV_FONT_DEFAULT; dsc->sel_start = LV_DRAW_LABEL_NO_TXT_SEL; dsc->sel_end = LV_DRAW_LABEL_NO_TXT_SEL; @@ -214,14 +215,16 @@ void lv_draw_label_iterate_characters(lv_draw_unit_t * draw_unit, const lv_draw_ pos.y += dsc->hint->y; } - uint32_t line_end = line_start + lv_text_get_next_line(&dsc->text[line_start], font, dsc->letter_space, w, NULL, - dsc->flag); + uint32_t remaining_len = dsc->text_length; + + uint32_t line_end = line_start + lv_text_get_next_line(&dsc->text[line_start], remaining_len, font, dsc->letter_space, + w, NULL, dsc->flag); /*Go the first visible line*/ while(pos.y + line_height_font < draw_unit->clip_area->y1) { /*Go to next line*/ line_start = line_end; - line_end += lv_text_get_next_line(&dsc->text[line_start], font, dsc->letter_space, w, NULL, dsc->flag); + line_end += lv_text_get_next_line(&dsc->text[line_start], remaining_len, font, dsc->letter_space, w, NULL, dsc->flag); pos.y += line_height; /*Save at the threshold coordinate*/ @@ -277,7 +280,7 @@ void lv_draw_label_iterate_characters(lv_draw_unit_t * draw_unit, const lv_draw_ uint8_t is_first_space_after_cmd = 0; /*Write out all lines*/ - while(dsc->text[line_start] != '\0') { + while(remaining_len && dsc->text[line_start] != '\0') { pos.x += x_ofs; line_start_x = pos.x; @@ -292,7 +295,7 @@ void lv_draw_label_iterate_characters(lv_draw_unit_t * draw_unit, const lv_draw_ const char * bidi_txt = dsc->text + line_start; #endif - while(next_char_offset < line_end - line_start) { + while(next_char_offset < remaining_len && next_char_offset < line_end - line_start) { uint32_t logical_char_pos = 0; /* Check if the text selection is enabled */ @@ -440,8 +443,11 @@ void lv_draw_label_iterate_characters(lv_draw_unit_t * draw_unit, const lv_draw_ bidi_txt = NULL; #endif /*Go to next line*/ + remaining_len -= line_end - line_start; line_start = line_end; - line_end += lv_text_get_next_line(&dsc->text[line_start], font, dsc->letter_space, w, NULL, dsc->flag); + if(remaining_len) { + line_end += lv_text_get_next_line(&dsc->text[line_start], remaining_len, font, dsc->letter_space, w, NULL, dsc->flag); + } pos.x = coords->x1; /*Align to middle*/ diff --git a/src/draw/lv_draw_label.h b/src/draw/lv_draw_label.h index e0e462900..f71399b3d 100644 --- a/src/draw/lv_draw_label.h +++ b/src/draw/lv_draw_label.h @@ -33,6 +33,7 @@ typedef struct { lv_draw_dsc_base_t base; const char * text; + uint32_t text_length; const lv_font_t * font; uint32_t sel_start; uint32_t sel_end; diff --git a/src/misc/lv_text.c b/src/misc/lv_text.c index 5b33ada40..52033210e 100644 --- a/src/misc/lv_text.c +++ b/src/misc/lv_text.c @@ -106,7 +106,7 @@ void lv_text_get_size(lv_point_t * size_res, const char * text, const lv_font_t /*Calc. the height and longest line*/ while(text[line_start] != '\0') { - new_line_start += lv_text_get_next_line(&text[line_start], font, letter_space, max_width, NULL, flag); + new_line_start += lv_text_get_next_line(&text[line_start], LV_TEXT_LEN_MAX, font, letter_space, max_width, NULL, flag); if((unsigned long)size_res->y + (unsigned long)letter_height + (unsigned long)line_space > LV_MAX_OF(int32_t)) { LV_LOG_WARN("integer overflow while calculating text height"); @@ -246,6 +246,9 @@ static uint32_t lv_text_get_next_word(const char * txt, const lv_font_t * font, if(break_index == NO_BREAK_FOUND && (cur_w - letter_space) > max_width) { break_index = i; break_letter_count = word_len - 1; + if(flag & LV_TEXT_FLAG_BREAK_ALL) { + break; + } /*break_index is now pointing at the character that doesn't fit*/ } @@ -315,9 +318,9 @@ static uint32_t lv_text_get_next_word(const char * txt, const lv_font_t * font, #endif } -uint32_t lv_text_get_next_line(const char * txt, const lv_font_t * font, - int32_t letter_space, int32_t max_width, - int32_t * used_width, lv_text_flag_t flag) +uint32_t lv_text_get_next_line(const char * txt, uint32_t len, + const lv_font_t * font, int32_t letter_space, + int32_t max_width, int32_t * used_width, lv_text_flag_t flag) { if(used_width) *used_width = 0; @@ -331,10 +334,10 @@ uint32_t lv_text_get_next_line(const char * txt, const lv_font_t * font, *without thinking about word wrapping*/ if((flag & LV_TEXT_FLAG_EXPAND) || (flag & LV_TEXT_FLAG_FIT)) { uint32_t i; - for(i = 0; txt[i] != '\n' && txt[i] != '\r' && txt[i] != '\0'; i++) { + for(i = 0; i < len && txt[i] != '\n' && txt[i] != '\r' && txt[i] != '\0'; i++) { /*Just find the new line chars or string ends by incrementing `i`*/ } - if(txt[i] != '\0') i++; /*To go beyond `\n`*/ + if(i < len && txt[i] != '\0') i++; /*To go beyond `\n`*/ if(used_width) *used_width = -1; return i; } @@ -344,7 +347,7 @@ uint32_t lv_text_get_next_line(const char * txt, const lv_font_t * font, uint32_t i = 0; /*Iterating index into txt*/ - while(txt[i] != '\0' && max_width > 0) { + while(i < len && txt[i] != '\0' && max_width > 0) { lv_text_flag_t word_flag = flag; if(i == 0) word_flag |= LV_TEXT_FLAG_BREAK_ALL; diff --git a/src/misc/lv_text.h b/src/misc/lv_text.h index 1381c36b5..77b176d3e 100644 --- a/src/misc/lv_text.h +++ b/src/misc/lv_text.h @@ -30,6 +30,8 @@ extern "C" { #define LV_TXT_ENC_UTF8 1 #define LV_TXT_ENC_ASCII 2 +#define LV_TEXT_LEN_MAX UINT32_MAX + /********************** * TYPEDEFS **********************/ diff --git a/src/misc/lv_text_private.h b/src/misc/lv_text_private.h index 858f59383..3814d8a14 100644 --- a/src/misc/lv_text_private.h +++ b/src/misc/lv_text_private.h @@ -31,6 +31,7 @@ extern "C" { /** * Get the next line of text. Check line length and break chars too. * @param txt a '\0' terminated string + * @param len length of 'txt' in bytes * @param font pointer to a font * @param letter_space letter space * @param max_width max width of the text (break the lines to fit this size). Set COORD_MAX to avoid @@ -41,7 +42,7 @@ extern "C" { * @return the index of the first char of the new line (in byte index not letter index. With UTF-8 * they are different) */ -uint32_t lv_text_get_next_line(const char * txt, const lv_font_t * font, int32_t letter_space, +uint32_t lv_text_get_next_line(const char * txt, uint32_t len, const lv_font_t * font, int32_t letter_space, int32_t max_width, int32_t * used_width, lv_text_flag_t flag); /** diff --git a/src/widgets/label/lv_label.c b/src/widgets/label/lv_label.c index 0907b02b6..f1ad439d6 100644 --- a/src/widgets/label/lv_label.c +++ b/src/widgets/label/lv_label.c @@ -346,7 +346,7 @@ void lv_label_get_letter_pos(const lv_obj_t * obj, uint32_t char_id, lv_point_t bool last_line = y + letter_height + line_space + letter_height > max_h; if(last_line && label->long_mode == LV_LABEL_LONG_DOT) flag |= LV_TEXT_FLAG_BREAK_ALL; - new_line_start += lv_text_get_next_line(&txt[line_start], font, letter_space, max_w, NULL, flag); + new_line_start += lv_text_get_next_line(&txt[line_start], LV_TEXT_LEN_MAX, font, letter_space, max_w, NULL, flag); if(byte_id < new_line_start || txt[new_line_start] == '\0') break; /*The line of 'index' letter begins at 'line_start'*/ @@ -437,7 +437,7 @@ uint32_t lv_label_get_letter_on(const lv_obj_t * obj, lv_point_t * pos_in, bool bool last_line = y + letter_height + line_space + letter_height > max_h; if(last_line && label->long_mode == LV_LABEL_LONG_DOT) flag |= LV_TEXT_FLAG_BREAK_ALL; - new_line_start += lv_text_get_next_line(&txt[line_start], font, letter_space, max_w, NULL, flag); + new_line_start += lv_text_get_next_line(&txt[line_start], LV_TEXT_LEN_MAX, font, letter_space, max_w, NULL, flag); if(pos.y <= y + letter_height) { /*The line is found (stored in 'line_start')*/ @@ -558,7 +558,7 @@ bool lv_label_is_char_under_pos(const lv_obj_t * obj, lv_point_t * pos) bool last_line = y + letter_height + line_space + letter_height > max_h; if(last_line && label->long_mode == LV_LABEL_LONG_DOT) flag |= LV_TEXT_FLAG_BREAK_ALL; - new_line_start += lv_text_get_next_line(&txt[line_start], font, letter_space, max_w, NULL, flag); + new_line_start += lv_text_get_next_line(&txt[line_start], LV_TEXT_LEN_MAX, font, letter_space, max_w, NULL, flag); if(pos->y <= y + letter_height) break; /*The line is found (stored in 'line_start')*/ y += letter_height + line_space; diff --git a/src/widgets/span/lv_span.c b/src/widgets/span/lv_span.c index 3561c2e04..e7ece397c 100644 --- a/src/widgets/span/lv_span.c +++ b/src/widgets/span/lv_span.c @@ -738,7 +738,7 @@ static bool lv_text_get_snippet(const char * txt, const lv_font_t * font, real_max_width++; #endif - uint32_t ofs = lv_text_get_next_line(txt, font, letter_space, real_max_width, use_width, flag); + uint32_t ofs = lv_text_get_next_line(txt, LV_TEXT_LEN_MAX, font, letter_space, real_max_width, use_width, flag); *end_ofs = ofs; if(txt[ofs] == '\0' && *use_width < max_width && !(ofs && (txt[ofs - 1] == '\n' || txt[ofs - 1] == '\r'))) { @@ -1018,16 +1018,11 @@ static void lv_draw_span(lv_obj_t * obj, lv_layer_t * layer) if(last_snippet->txt[last_snippet->bytes] == '\0') { next_line_h = 0; lv_span_t * next_span = lv_ll_get_next(&spans->child_ll, last_snippet->span); - if(next_span) { /* have the next line */ + if(next_span && next_span->txt && next_span->txt[0]) { /* have the next line */ next_line_h = lv_font_get_line_height(lv_span_get_style_text_font(obj, next_span)) + line_space; } } if(txt_pos.y + max_line_h + next_line_h - line_space > coords.y2 + 1) { /* for overflow if is end line. */ - if(last_snippet->txt[last_snippet->bytes] != '\0') { - last_snippet->bytes = lv_strlen(last_snippet->txt); - last_snippet->txt_w = lv_text_get_width_with_flags(last_snippet->txt, last_snippet->bytes, last_snippet->font, - last_snippet->letter_space, label_draw_dsc.flag); - } ellipsis_valid = spans->overflow == LV_SPAN_OVERFLOW_ELLIPSIS; is_end_line = true; } @@ -1076,76 +1071,45 @@ static void lv_draw_span(lv_obj_t * obj, lv_layer_t * layer) } uint32_t txt_bytes = pinfo->bytes; - /* overflow */ - uint32_t dot_letter_w = 0; + if(pos.x > clip_area.x2) { + continue; + } + + label_draw_dsc.text = bidi_txt; + label_draw_dsc.text_length = txt_bytes; + label_draw_dsc.letter_space = pinfo->letter_space; + label_draw_dsc.decor = lv_span_get_style_text_decor(obj, pinfo->span); + lv_area_t a; + a.x1 = pos.x; + a.y1 = pos.y; + a.x2 = a.x1 + pinfo->txt_w; + a.y2 = a.y1 + pinfo->line_h; + + bool need_draw_ellipsis = false; uint32_t dot_width = 0; + /* deal overflow */ if(ellipsis_valid) { - dot_letter_w = lv_font_get_glyph_width(pinfo->font, '.', '.'); + uint32_t dot_letter_w = lv_font_get_glyph_width(pinfo->font, '.', '.'); dot_width = dot_letter_w * 3; - } - int32_t ellipsis_width = coords.x1 + max_width - dot_width; - uint32_t j = 0; - while(j < txt_bytes) { - /* skip invalid fields */ - if(pos.x > clip_area.x2) { - break; - } - uint32_t letter = lv_text_encoded_next(bidi_txt, &j); - uint32_t letter_next = lv_text_encoded_next(&bidi_txt[j], NULL); - int32_t letter_w = lv_font_get_glyph_width(pinfo->font, letter, letter_next); - - /* skip invalid fields */ - if(pos.x + letter_w + pinfo->letter_space < clip_area.x1) { - if(letter_w > 0) { - pos.x = pos.x + letter_w + pinfo->letter_space; - } - continue; - } - - if(ellipsis_valid && pos.x + letter_w + pinfo->letter_space > ellipsis_width) { - for(int ell = 0; ell < 3; ell++) { - lv_draw_character(layer, &label_draw_dsc, &pos, '.'); - pos.x = pos.x + dot_letter_w + pinfo->letter_space; - } - if(pos.x <= ellipsis_width) { - pos.x = ellipsis_width + 1; - } - break; - } - else { - lv_draw_character(layer, &label_draw_dsc, &pos, letter); - if(letter_w > 0) { - pos.x = pos.x + letter_w + pinfo->letter_space; - } - } + label_draw_dsc.flag = LV_TEXT_FLAG_BREAK_ALL; + uint32_t next_ofs; + need_draw_ellipsis = lv_text_get_snippet(pinfo->txt, pinfo->font, pinfo->letter_space, coords.x2 - a.x1 - dot_width, + label_draw_dsc.flag, &pinfo->txt_w, &next_ofs); + a.x2 = a.x1 + pinfo->txt_w; + label_draw_dsc.text_length = next_ofs + 1; } - /* draw decor */ - lv_text_decor_t decor = lv_span_get_style_text_decor(obj, pinfo->span); - if(decor != LV_TEXT_DECOR_NONE) { - lv_draw_line_dsc_t line_dsc; - lv_draw_line_dsc_init(&line_dsc); - line_dsc.color = label_draw_dsc.color; - line_dsc.width = label_draw_dsc.font->underline_thickness ? pinfo->font->underline_thickness : 1; - line_dsc.opa = label_draw_dsc.opa; - line_dsc.blend_mode = label_draw_dsc.blend_mode; + lv_draw_label(layer, &label_draw_dsc, &a); - if(decor & LV_TEXT_DECOR_STRIKETHROUGH) { - int32_t y = pos.y + ((pinfo->line_h - line_space) >> 1) + (line_dsc.width >> 1); - lv_point_precise_set(&line_dsc.p1, txt_pos.x, y); - lv_point_precise_set(&line_dsc.p2, pos.x, y); - lv_draw_line(layer, &line_dsc); - } - - if(decor & LV_TEXT_DECOR_UNDERLINE) { - int32_t y = pos.y + pinfo->line_h - line_space - pinfo->font->base_line - pinfo->font->underline_position; - lv_point_precise_set(&line_dsc.p1, txt_pos.x, y); - lv_point_precise_set(&line_dsc.p2, pos.x, y); - lv_draw_line(layer, &line_dsc); - } + if(need_draw_ellipsis) { + label_draw_dsc.text = "..."; + a.x1 = a.x2; + a.x2 = a.x1 + dot_width; + lv_draw_label(layer, &label_draw_dsc, &a); } - txt_pos.x = pos.x; + + txt_pos.x = a.x2; } Next_line_init: diff --git a/tests/ref_imgs/widgets/span_02.png b/tests/ref_imgs/widgets/span_02.png index ffcf4b09f..e79d52a99 100644 Binary files a/tests/ref_imgs/widgets/span_02.png and b/tests/ref_imgs/widgets/span_02.png differ diff --git a/tests/ref_imgs/widgets/span_03.png b/tests/ref_imgs/widgets/span_03.png index 09ce4a2fd..4cb61285f 100644 Binary files a/tests/ref_imgs/widgets/span_03.png and b/tests/ref_imgs/widgets/span_03.png differ diff --git a/tests/ref_imgs/widgets/span_04.png b/tests/ref_imgs/widgets/span_04.png index 21edcf63d..bf5727728 100644 Binary files a/tests/ref_imgs/widgets/span_04.png and b/tests/ref_imgs/widgets/span_04.png differ diff --git a/tests/ref_imgs/widgets/span_05.png b/tests/ref_imgs/widgets/span_05.png index 09ce4a2fd..4cb61285f 100644 Binary files a/tests/ref_imgs/widgets/span_05.png and b/tests/ref_imgs/widgets/span_05.png differ diff --git a/tests/ref_imgs_vg_lite/widgets/span_02.png b/tests/ref_imgs_vg_lite/widgets/span_02.png index e7a636846..ead766f39 100644 Binary files a/tests/ref_imgs_vg_lite/widgets/span_02.png and b/tests/ref_imgs_vg_lite/widgets/span_02.png differ diff --git a/tests/ref_imgs_vg_lite/widgets/span_03.png b/tests/ref_imgs_vg_lite/widgets/span_03.png index 7c72384cd..922a11000 100644 Binary files a/tests/ref_imgs_vg_lite/widgets/span_03.png and b/tests/ref_imgs_vg_lite/widgets/span_03.png differ diff --git a/tests/ref_imgs_vg_lite/widgets/span_04.png b/tests/ref_imgs_vg_lite/widgets/span_04.png index 805cecff8..fd679294a 100644 Binary files a/tests/ref_imgs_vg_lite/widgets/span_04.png and b/tests/ref_imgs_vg_lite/widgets/span_04.png differ diff --git a/tests/ref_imgs_vg_lite/widgets/span_05.png b/tests/ref_imgs_vg_lite/widgets/span_05.png index 7c72384cd..922a11000 100644 Binary files a/tests/ref_imgs_vg_lite/widgets/span_05.png and b/tests/ref_imgs_vg_lite/widgets/span_05.png differ diff --git a/tests/ref_imgs_vg_lite/widgets/span_09.png b/tests/ref_imgs_vg_lite/widgets/span_09.png index 2736848f7..ae5a14b67 100644 Binary files a/tests/ref_imgs_vg_lite/widgets/span_09.png and b/tests/ref_imgs_vg_lite/widgets/span_09.png differ diff --git a/tests/src/test_cases/test_txt.c b/tests/src/test_cases/test_txt.c index 2d36a3984..687faa462 100644 --- a/tests/src/test_cases/test_txt.c +++ b/tests/src/test_cases/test_txt.c @@ -134,7 +134,7 @@ void test_txt_next_line_should_handle_empty_string(void) int32_t max_width = 0; lv_text_flag_t flag = LV_TEXT_FLAG_NONE; - uint32_t next_line = lv_text_get_next_line("", font_ptr, letter_space, max_width, NULL, flag); + uint32_t next_line = lv_text_get_next_line("", 0, font_ptr, letter_space, max_width, NULL, flag); TEST_ASSERT_EQUAL_UINT32(0, next_line); }