lvgl/lvgl

Span elements not printing properly

andyrea76 opened this issue · 6 comments

LVGL version

9.1.0

What happened?

there seems to be an issue with the second span item

creating a group of 8 items in a span and assigning each one to a static buffer, something goes wrong with the second one. in this example, each element printed should` just be A -> F but the second one prints a garbage character

image

the same is true if i just set the text directly with lv_span_set_text after the loop for each element, not calling the static text method

How to reproduce?

#define MENU_MAX_STATUS_SYMBOLS	8
#define MAX_SYMBOL_TEXT_LENGTH	4

static lv_obj_t* headerBlockRightStatusSpan = NULL;
static lv_span_t* headerBlockRightStatusSpans[MENU_MAX_STATUS_SYMBOLS]   ;
static char headerBlockRightStatusSpans_Text[MENU_MAX_STATUS_SYMBOLS][MAX_SYMBOL_TEXT_LENGTH] ;

headerBlockRightStatusSpan = lv_spangroup_create(rightHeaderRegion);
ASSERT(headerBlockRightStatusSpan);

strcat(headerBlockRightStatusSpans_Text[0],"A");	//MSG_IconTemplate);
strcat(headerBlockRightStatusSpans_Text[1],"B");	//MSG_BatteryCharging);
strcat(headerBlockRightStatusSpans_Text[2],"C");	//MSG_IconMicrophone);
strcat(headerBlockRightStatusSpans_Text[3],"D");	//MSG_IconBluetooth);
strcat(headerBlockRightStatusSpans_Text[4],"E");	//MSG_IconGps);
strcat(headerBlockRightStatusSpans_Text[5],"F");	//MSG_IconUsb);
headerBlockRightStatusSpans_Text[6][0] = 0;
headerBlockRightStatusSpans_Text[7][0] = 0;

lv_spangroup_set_align(headerBlockRightStatusSpan, LV_TEXT_ALIGN_RIGHT);
lv_spangroup_set_mode(headerBlockRightStatusSpan, LV_SPAN_MODE_EXPAND);
for (int i = 0; i < sizeof(headerBlockRightStatusSpans) / sizeof(headerBlockRightStatusSpans[0]); i++)
{
	headerBlockRightStatusSpans[i] = lv_spangroup_new_span(headerBlockRightStatusSpan);
	//lv_style_set_text_font(&headerBlockRightStatusSpans[i]->style, imgfont);
	lv_span_set_text_static(headerBlockRightStatusSpans[i], headerBlockRightStatusSpans_Text[i]);
	if(headerBlockRightStatusSpans_Text[i][0])
		lv_style_set_text_letter_space(&headerBlockRightStatusSpans[i]->style, 5);
}
lv_spangroup_refr_mode(headerBlockRightStatusSpan);
        if(headerBlockRightStatusSpans_Text[i][0])
        lv_style_set_text_letter_space(&headerBlockRightStatusSpans[i]->style, 5);

I had to comment out this part or else I see something else troubling. No "E" or "F".

image

Anyways, after commenting out that part, I see this:

image

I can't reproduce the issue right this moment. I tried master and release/v9.1. I stepped through lv_draw_span in a debugger although I'm not sure what I'm looking for yet 🙂 It's very possible that there is some subtle bug related to the second span of a spangroup. The span drawing code is large.

next steps

Are you sure the exact snippet you sent exposes the problem on your system? Or did you modify it and now the problem doesn't occur for that snippet?

Are you using a font that has some problem with the letter "B"? Can you try using a different character for the second span to rule out that possibility?

@andyrea76
I've formatted your comment to have the code in a

```c
code
```

block for syntax highlighting.

yes, having cut the code out of my project and got the windows simulator running, replacing lv_spangroup_create(rightHeaderRegion); with lv_spangroup_create(lv_screen_active());

i see the same thing as you where only the first 4 letters appear.

the real application is using unicode characters ( hence the commented out MSG_xxxx ends to the lines assigning 'A'-'F' instead ) but i just simplified it to prove the point

the fact that it stops showing the letters after D is surely a concern? all the test obviously does is to add padding after a string if there was a message in that element so it would make no sense to stop on the 4th item

image

the debugger shows that the elements are all looking vaguely valid in the watch window and appear to have the right string of text

image

but you only get A -> D printed

leaving the letter spacing adjustment but removing the 'if' line still shows up the issue
image

To summarize, there are two unique problems here

  1. The second span in a spangroup shows a garbage character instead of the real character.
  2. Setting the letter space style can cause the right side to be cut off.

Thanks for testing some things.

Problem 1

Can that problem be reproduced?

Problem 2

Yes, this is undesirable behavior. I spent a while creating a fix, but I began to see that zero-length spans and letter_space are totally incompatible with each other. It would easily be a 100 line refactor to make both work together. Congrats on exposing a significant weakness in spangroup!

My advice for getting your project working is to not use letter_space and zero-length spans at the same time.

As I said, getting both to work together is a big deal, but here are some fixes to make either letter_space or zero-length spans work reliably on their own. I tested with LV_SPAN_MODE_EXPAND and LV_SPAN_MODE_BREAK with your example using either letter_space style or zero-length spans but not both at the same time.

I can't make any promises about not breaking any other span features like ellipsis. I can be more thorough in a PR.

diff --git a/src/widgets/span/lv_span.c b/src/widgets/span/lv_span.c
index 8af5f2416..df38d4431 100644
--- a/src/widgets/span/lv_span.c
+++ b/src/widgets/span/lv_span.c
@@ -479,7 +479,11 @@ int32_t lv_spangroup_get_expand_height(lv_obj_t * obj, int32_t width)
 
             /* break word deal width */
             if(isfill && next_ofs > 0 && snippet_cnt > 0) {
-                if(max_w < use_width) {
+                int32_t drawn_width = use_width;
+                if(_lv_ll_get_next(&spans->child_ll, cur_span) == NULL) {
+                    drawn_width -= snippet.letter_space;
+                }
+                if(max_w < drawn_width) {
                     break;
                 }
 
@@ -502,7 +506,7 @@ int32_t lv_spangroup_get_expand_height(lv_obj_t * obj, int32_t width)
                 max_line_h = snippet.line_h;
             }
             snippet_cnt ++;
-            max_w = max_w - use_width - snippet.letter_space;
+            max_w = max_w - use_width;
             if(isfill  || max_w <= 0) {
                 break;
             }
@@ -871,9 +875,13 @@ static void lv_draw_span(lv_obj_t * obj, lv_layer_t * layer)
 
             if(isfill) {
                 if(next_ofs > 0 && lv_get_snippet_count() > 0) {
+                    int32_t drawn_width = use_width;
+                    if(_lv_ll_get_next(&spans->child_ll, cur_span) == NULL) {
+                        drawn_width -= snippet.letter_space;
+                    }
                     /* To prevent infinite loops, the lv_text_get_next_line() may return incomplete words, */
                     /* This phenomenon should be avoided when lv_get_snippet_count() > 0 */
-                    if(max_w < use_width) {
+                    if(max_w < drawn_width) {
                         break;
                     }
                     uint32_t tmp_ofs = next_ofs;
@@ -898,7 +906,7 @@ static void lv_draw_span(lv_obj_t * obj, lv_layer_t * layer)
             }
 
             lv_snippet_push(&snippet);
-            max_w = max_w - use_width - snippet.letter_space;
+            max_w = max_w - use_width;
             if(isfill || max_w <= 0) {
                 break;
             }
@@ -946,7 +954,7 @@ static void lv_draw_span(lv_obj_t * obj, lv_layer_t * layer)
             uint32_t i;
             for(i = 0; i < item_cnt; i++) {
                 lv_snippet_t * pinfo = lv_get_snippet(i);
-                txts_w = txts_w + pinfo->txt_w + pinfo->letter_space;
+                txts_w = txts_w + pinfo->txt_w;
             }
             txts_w -= lv_get_snippet(item_cnt - 1)->letter_space;
             align_ofs = max_width > txts_w ? max_width - txts_w : 0;