From 7f57f37560c29ae6007b828fb93a61764f483e1f Mon Sep 17 00:00:00 2001 From: Benign X <1341398182@qq.com> Date: Thu, 12 Sep 2024 17:56:35 +0800 Subject: [PATCH] feat(spangroup): add `lv_spangroup_get_span_by_point` in spangroup (#6579) Co-authored-by: Gabor Kiss-Vamosi --- examples/widgets/span/lv_example_span_1.c | 16 ++- src/widgets/span/lv_span.c | 134 +++++++++++++++++++++- src/widgets/span/lv_span.h | 47 ++++++++ src/widgets/span/lv_span_private.h | 3 + tests/ref_imgs/widgets/span_09.png | Bin 0 -> 14491 bytes tests/src/test_cases/widgets/test_span.c | 104 +++++++++++++++++ 6 files changed, 302 insertions(+), 2 deletions(-) create mode 100644 tests/ref_imgs/widgets/span_09.png diff --git a/examples/widgets/span/lv_example_span_1.c b/examples/widgets/span/lv_example_span_1.c index f74e0a3f8..26466a9c7 100644 --- a/examples/widgets/span/lv_example_span_1.c +++ b/examples/widgets/span/lv_example_span_1.c @@ -1,8 +1,19 @@ #include "../../lv_examples.h" #if LV_USE_SPAN && LV_BUILD_EXAMPLES +static void click_event_cb(lv_event_t * e) +{ + lv_obj_t * spans = lv_event_get_target(e); + lv_indev_t * indev = lv_event_get_indev(e); + lv_point_t point; + lv_indev_get_point(indev, &point); + lv_span_t * span = lv_spangroup_get_span_by_point(spans, &point); + + LV_LOG_USER("%s", span ? lv_span_get_text(span) : "NULL"); +} + /** - * Create span. + * Create spans and get clicked one */ void lv_example_span_1(void) { @@ -17,6 +28,7 @@ void lv_example_span_1(void) lv_obj_set_height(spans, 300); lv_obj_center(spans); lv_obj_add_style(spans, &style, 0); + lv_obj_add_flag(spans, LV_OBJ_FLAG_CLICKABLE); lv_spangroup_set_align(spans, LV_TEXT_ALIGN_LEFT); lv_spangroup_set_overflow(spans, LV_SPAN_OVERFLOW_CLIP); @@ -53,6 +65,8 @@ void lv_example_span_1(void) lv_style_set_text_decor(lv_span_get_style(span), LV_TEXT_DECOR_STRIKETHROUGH); lv_spangroup_refr_mode(spans); + + lv_obj_add_event_cb(spans, click_event_cb, LV_EVENT_CLICKED, NULL); } #endif diff --git a/src/widgets/span/lv_span.c b/src/widgets/span/lv_span.c index c4afb4373..a942dba1e 100644 --- a/src/widgets/span/lv_span.c +++ b/src/widgets/span/lv_span.c @@ -69,6 +69,9 @@ static void lv_snippet_push(lv_snippet_t * item); static lv_snippet_t * lv_get_snippet(uint32_t index); static int32_t convert_indent_pct(lv_obj_t * spans, int32_t width); +static lv_span_coords_t make_span_coords(const lv_span_t * prev_span, const lv_span_t * curr_span, int32_t width, + lv_area_t padding, int32_t indent); + /********************** * STATIC VARIABLES **********************/ @@ -258,6 +261,11 @@ lv_style_t * lv_span_get_style(lv_span_t * span) return &span->style; } +const char * lv_span_get_text(lv_span_t * span) +{ + return span->txt; +} + lv_span_t * lv_spangroup_get_child(const lv_obj_t * obj, int32_t id) { if(obj == NULL) { @@ -451,6 +459,7 @@ int32_t lv_spangroup_get_expand_height(lv_obj_t * obj, int32_t width) lv_snippet_t snippet; /* use to save cur_span info and push it to stack */ lv_memset(&snippet, 0, sizeof(snippet)); + lv_span_t * prev_span = cur_span; int32_t line_cnt = 0; int32_t lines = spans->lines < 0 ? INT32_MAX : spans->lines; /* the loop control how many lines need to draw */ @@ -462,6 +471,8 @@ int32_t lv_spangroup_get_expand_height(lv_obj_t * obj, int32_t width) while(1) { /* switch to the next span when current is end */ if(cur_txt[cur_txt_ofs] == '\0') { + cur_span->trailing_pos = txt_pos; + cur_span = lv_ll_get_next(&spans->child_ll, cur_span); if(cur_span == NULL) break; cur_txt = cur_span->txt; @@ -484,6 +495,8 @@ int32_t lv_spangroup_get_expand_height(lv_obj_t * obj, int32_t width) int32_t use_width = 0; bool isfill = lv_text_get_snippet(&cur_txt[cur_txt_ofs], snippet.font, snippet.letter_space, max_w, txt_flag, &use_width, &next_ofs); + if(isfill) txt_pos.x = 0; + else txt_pos.x += use_width; /* break word deal width */ if(isfill && next_ofs > 0 && snippet_cnt > 0) { @@ -521,8 +534,16 @@ int32_t lv_spangroup_get_expand_height(lv_obj_t * obj, int32_t width) } /* next line init */ - txt_pos.x = 0; txt_pos.y += max_line_h; + + /* iterate all the spans in the current line and set the trailing height to the max line height */ + for(lv_span_t * tmp_span = prev_span; + tmp_span && tmp_span != cur_span; + tmp_span = lv_ll_get_next(&spans->child_ll, tmp_span)) + tmp_span->trailing_height = max_line_h; + + prev_span = cur_span; + max_w = max_width; line_cnt += 1; if(line_cnt >= lines) { @@ -534,6 +555,69 @@ int32_t lv_spangroup_get_expand_height(lv_obj_t * obj, int32_t width) return txt_pos.y; } +lv_span_coords_t lv_spangroup_get_span_coords(lv_obj_t * obj, const lv_span_t * span) +{ + /* find previous span */ + const lv_spangroup_t * spangroup = (lv_spangroup_t *)obj; + const lv_ll_t * spans = &spangroup->child_ll; + const int32_t width = lv_obj_get_content_width(obj); + const int32_t indent = lv_spangroup_get_indent(obj); + + if(obj == NULL || span == NULL || lv_ll_get_head(spans) == NULL) return (lv_span_coords_t) { + 0 + }; + + lv_span_t * prev_span = NULL; + lv_span_t * curr_span; + LV_LL_READ(spans, curr_span) { + if(curr_span == span) break; + prev_span = curr_span; + } + + const uint32_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN); + return make_span_coords(prev_span, curr_span, width, (lv_area_t) { + .x1 = lv_obj_get_style_pad_left(obj, LV_PART_MAIN) + border_width, + .y1 = lv_obj_get_style_pad_top(obj, LV_PART_MAIN) + border_width, + .x2 = lv_obj_get_style_pad_right(obj, LV_PART_MAIN) + border_width, .y2 = 0 + }, + indent); +} + +lv_span_t * lv_spangroup_get_span_by_point(lv_obj_t * obj, const lv_point_t * p) +{ + const lv_spangroup_t * spangroup = (lv_spangroup_t *)obj; + const lv_ll_t * spans = &spangroup->child_ll; + const int32_t width = lv_obj_get_content_width(obj); + const int32_t indent = lv_spangroup_get_indent(obj); + + if(obj == NULL || p == NULL || lv_ll_get_head(spans) == NULL) return NULL; + + lv_point_t point; + point.x = p->x - obj->coords.x1; + point.y = p->y - obj->coords.y1; + + /* find previous span */ + + const lv_span_t * prev_span = NULL; + lv_span_t * curr_span; + LV_LL_READ(spans, curr_span) { + lv_span_coords_t coords = make_span_coords(prev_span, curr_span, width, (lv_area_t) { + .x1 = lv_obj_get_style_pad_left(obj, LV_PART_MAIN), + .y1 = lv_obj_get_style_pad_top(obj, LV_PART_MAIN), + .x2 = lv_obj_get_style_pad_right(obj, LV_PART_MAIN), + .y2 = 0 + }, + indent); + if(lv_area_is_point_on(&coords.heading, &point, 0) || + lv_area_is_point_on(&coords.middle, &point, 0) || + lv_area_is_point_on(&coords.trailing, &point, 0)) { + return curr_span; + } + prev_span = curr_span; + } + return NULL; +} + /********************** * STATIC FUNCTIONS **********************/ @@ -1086,4 +1170,52 @@ static void refresh_self_size(lv_obj_t * obj) lv_obj_refresh_self_size(obj); } +static lv_span_coords_t make_span_coords(const lv_span_t * prev_span, const lv_span_t * curr_span, const int32_t width, + const lv_area_t padding, const int32_t indent) +{ + lv_span_coords_t coords = { 0 }; + + if(curr_span == NULL) return coords; + + /* first line */ + if(prev_span == NULL) { + lv_area_set(&coords.heading, padding.x1 + indent, padding.y1, width + padding.x1, + curr_span->trailing_pos.y + padding.y1); + lv_area_set(&coords.middle, coords.heading.x1, coords.heading.y2, curr_span->trailing_pos.x + padding.x1, + coords.heading.y2 + curr_span->trailing_height); + lv_area_set(&coords.trailing, 0, 0, 0, 0); + + return coords; + } + + /* start and end on the same line */ + const bool is_same_line = prev_span->trailing_pos.y == curr_span->trailing_pos.y; + if(is_same_line == true) { + lv_area_set(&coords.heading, + prev_span->trailing_pos.x + padding.x1, prev_span->trailing_pos.y + padding.y1, + curr_span->trailing_pos.x + padding.x1, curr_span->trailing_pos.y + curr_span->trailing_height + padding.y1); + return coords; + } + + /* common case */ + const lv_point_t pre_trailing_pos = prev_span->trailing_pos; + const int32_t pre_trailing_height = prev_span->trailing_height; + + lv_area_set(&coords.heading, + pre_trailing_pos.x + padding.x1, pre_trailing_pos.y + padding.y1, + width + padding.x1, pre_trailing_pos.y + pre_trailing_height + padding.y1); + /* When it happens to be two lines of text, + * the y2 of the middle area is exactly the y1 + line height of the first line of text, + * so the area of the middle area is empty. + * */ + lv_area_set(&coords.middle, + padding.x1, coords.heading.y2, + width + padding.x1, curr_span->trailing_pos.y + padding.y1); + lv_area_set(&coords.trailing, + coords.middle.x1, coords.middle.y2, + curr_span->trailing_pos.x + padding.x1, curr_span->trailing_pos.y + curr_span->trailing_height + padding.y1); + + return coords; +} + #endif diff --git a/src/widgets/span/lv_span.h b/src/widgets/span/lv_span.h index f0406b918..17bcf51e3 100644 --- a/src/widgets/span/lv_span.h +++ b/src/widgets/span/lv_span.h @@ -41,6 +41,13 @@ typedef enum { LV_SPAN_MODE_LAST /**< Fence member */ } lv_span_mode_t; +/** Coords of a span */ +typedef struct _lv_span_coords_t { + lv_area_t heading; + lv_area_t middle; + lv_area_t trailing; +} lv_span_coords_t; + LV_ATTRIBUTE_EXTERN_DATA extern const lv_obj_class_t lv_spangroup_class; /********************** @@ -136,6 +143,13 @@ void lv_spangroup_set_max_lines(lv_obj_t * obj, int32_t lines); */ lv_style_t * lv_span_get_style(lv_span_t * span); +/** + * Get a pointer to the text of a span + * @param span pointer to the span + * @return pointer to the text +*/ +const char * lv_span_get_text(lv_span_t * span); + /** * Get a spangroup child by its index. * @@ -214,6 +228,39 @@ uint32_t lv_spangroup_get_expand_width(lv_obj_t * obj, uint32_t max_width); */ int32_t lv_spangroup_get_expand_height(lv_obj_t * obj, int32_t width); +/** + * Get the span's coords in the spangroup. + * @note Before calling this function, please make sure that the layout of span group has been updated. + * Like calling lv_obj_update_layout() like function. + * + * +--------+ + * |Heading +--->------------------+ + * | Pos | | Heading | + * +--------+---+------------------+ + * | | + * | | + * | | + * | Middle +--------+| + * | |Trailing|| + * | +-| Pos || + * | | +--------+| + * +-------------------v-----------+ + * | Trailing | + * +-------------------+ + * @param obj pointer to a spangroup object. + * @param span pointer to a span. + * @return the span's coords in the spangroup. + */ +lv_span_coords_t lv_spangroup_get_span_coords(lv_obj_t * obj, const lv_span_t * span); + +/** + * Get the span object by point. + * @param obj pointer to a spangroup object. + * @param point pointer to point containing absolute coordinates + * @return pointer to the span under the point or `NULL` if not found. + */ +lv_span_t * lv_spangroup_get_span_by_point(lv_obj_t * obj, const lv_point_t * point); + /*===================== * Other functions *====================*/ diff --git a/src/widgets/span/lv_span_private.h b/src/widgets/span/lv_span_private.h index 471b50db4..6e80405ef 100644 --- a/src/widgets/span/lv_span_private.h +++ b/src/widgets/span/lv_span_private.h @@ -32,6 +32,9 @@ struct _lv_span_t { lv_obj_t * spangroup; /**< a pointer to spangroup */ lv_style_t style; /**< display text style */ uint32_t static_flag : 1; /**< the text is static flag */ + + lv_point_t trailing_pos; + int32_t trailing_height; }; /** Data of label*/ diff --git a/tests/ref_imgs/widgets/span_09.png b/tests/ref_imgs/widgets/span_09.png new file mode 100644 index 0000000000000000000000000000000000000000..2ad195e70d696ae056b8bf1fcd87599df1cf039d GIT binary patch literal 14491 zcmeHuWl&pd+iohmg0)awcSC^|ZL#3&7Hy$81b4UKF72jxflVoHn^H6c4*^1;g#txW z+@ZKT!R0)C-*3+M&Np+;%$Z|$+t{s;MZD644VuAP~}*in1CI z$c^72kZV8hTn9(`t+GBrAiqOi%0Acf$=IBFt(LBrwS0CJ^4HBfkBqrzm1XJvdSG~L zb!*v!Llh>kShQA-6yv2E5a>sQ0G3c_zsn>0em2+(|#n`V?&&-Zc+~S-_ z;sS?~jg-n$c9rqJs7GU-{!a1&@#fLby66xV%a`A;$M!@wW$#?iT9#POPU3L%T_TAUlX4~b1p3)upT{NgI}1uXW*`!~NqAb-PvB zKYuM_>}`xG?&FCFR7iOKwvoRG5`$_{n+?*3+2pfTa6|ibPG6D>K z`#aNKm?cKZBEfZU6XoEpmwIe-_kf;2YH(NkTfmMg)I8&CBow3RSMx%f@5GF!TPZ-@ zP1IFn;T_&qa`(eL6+`h3>%?yC23f_JrJZ`v4>r|;)*(v|4`=Uf6Xvc8;!en$-1VQ- zb%+urH$9a`bY`in>_TYML5J1Lg3m2{h%>h2#P)b5hiJZ6QXJANA9^)g*|J-|4R9M> zTixjzj}pE3{Gyx2_M_3UY-OeSXaW^g_v5Mb`w}?bI1#FD6+b5I?4K=+P^p zx#XNb6?r+VZVdObVV0)cM3G-&pS=FIBbe@9wKQ7jla|*wy(5^Ms#;Rn+?G#MN)YUN zp=`<3(vf?2Knz`1flV_Jpb{glT+&H?6^E%w-LH~MXb>;H3`YtIHthJ;RCn}$Dx~W; z{*+;QOayU>VATD>zqbCG9Qf1|cFo)((3=f5yyz;ca{L+HQ$E%`G?Yw2JZ2x$$dVQb z9B<`396--woLxOZytY9KY)N-d zIbq>P$yLKa*B~ymjFa@-2B_L}aG7o^;(G?fiPdTk;=@L4L^8yA?79 zTNI04hbwwwSciFNblHt0Tyt&B%1%9r8pJJP5g@_9a}{!WrXY7t=z}ZAn(CK`Kx7o=yJtwjxWQm3xvHumGI-L%Dxtvq!Jn@&w@E~)c)=~A3f zHBy{kf@euti!l}@WG0+GI9kk9a1-)Vo1Z_m`>kMiS5`$Z*tH(V(QV>!lU-Rxyd%#q z=&wVJ<3lF$%?`53itQMEi{6Q>TY72_?^jlsbw*oQ1fR@a^CX76;kkW8IeQWusP=l^ zc0w0ckPVELWqtB|cTbS|`-ha3)s>-37cr_q$$EH_)9H1` zJsKnaCVuJEj3}FAfpt2p8N#*ZpcbnXy_w^pZX?EU5!i!6ENxoC+~#+8NA0<@45-P6 zbikD+rZ%fZw|YW9rP68hS7p(~I&L!hHxedOlcWe}F`wOUe|#Cx^rP{%-j4dt@pcZj zt36;tFgq0VY+A?a)-1!AXDD^P-GIdYE_uzta%p{3zxz}ev6P#dYHOX3ud*O}d_GXh z8;NQ)cc|E1+`Tt*`NPhA1LfkyN|Sb$Unr1JRMaszcrxsc7~lNtzB;3D^%rbE E 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 literal 0 HcmV?d00001 diff --git a/tests/src/test_cases/widgets/test_span.c b/tests/src/test_cases/widgets/test_span.c index 44ee88690..5cbc8c223 100644 --- a/tests/src/test_cases/widgets/test_span.c +++ b/tests/src/test_cases/widgets/test_span.c @@ -375,4 +375,108 @@ void test_spangroup_style_text_letter_space(void) TEST_ASSERT_EQUAL_SCREENSHOT("widgets/span_08.png"); } +#if LV_FONT_MONTSERRAT_24 && LV_FONT_MONTSERRAT_20 +void test_spangroup_get_span_coords(void) +{ + /* Initialize the active screen and create a new span group */ + active_screen = lv_screen_active(); + spangroup = lv_spangroup_create(active_screen); + + const uint32_t span_count = 5; + lv_span_t * spans[span_count]; + + /* Set styles and properties for the span group */ + lv_obj_set_style_outline_width(spangroup, 1, 0); + lv_spangroup_set_indent(spangroup, 20); + lv_spangroup_set_mode(spangroup, LV_SPAN_MODE_BREAK); + lv_obj_set_width(spangroup, 300); + lv_obj_set_style_pad_all(spangroup, 20, LV_PART_MAIN); + + /* Create spans and set their properties */ + spans[0] = lv_spangroup_new_span(spangroup); + lv_span_set_text(spans[0], "China is a beautiful country."); + lv_style_set_text_color(lv_span_get_style(spans[0]), lv_palette_main(LV_PALETTE_RED)); + lv_style_set_text_decor(lv_span_get_style(spans[0]), LV_TEXT_DECOR_UNDERLINE); + lv_style_set_text_opa(lv_span_get_style(spans[0]), LV_OPA_50); + + spans[1] = lv_spangroup_new_span(spangroup); + lv_span_set_text_static(spans[1], "good good study, day day up."); + lv_style_set_text_font(lv_span_get_style(spans[1]), &lv_font_montserrat_24); + lv_style_set_text_color(lv_span_get_style(spans[1]), lv_palette_main(LV_PALETTE_GREEN)); + + spans[2] = lv_spangroup_new_span(spangroup); + lv_span_set_text_static(spans[2], "LVGL is an open-source graphics library."); + lv_style_set_text_color(lv_span_get_style(spans[2]), lv_palette_main(LV_PALETTE_BLUE)); + + spans[3] = lv_spangroup_new_span(spangroup); + lv_span_set_text_static(spans[3], "the boy no name."); + lv_style_set_text_color(lv_span_get_style(spans[3]), lv_palette_main(LV_PALETTE_GREEN)); + lv_style_set_text_font(lv_span_get_style(spans[3]), &lv_font_montserrat_20); + lv_style_set_text_decor(lv_span_get_style(spans[3]), LV_TEXT_DECOR_UNDERLINE); + + spans[4] = lv_spangroup_new_span(spangroup); + lv_span_set_text(spans[4], "I have a dream that hope to come true."); + lv_style_set_text_decor(lv_span_get_style(spans[4]), LV_TEXT_DECOR_STRIKETHROUGH); + + /* Refresh the span group mode and update layout */ + lv_spangroup_refr_mode(spangroup); + lv_obj_update_layout(spangroup); + + /* Define expected coordinates for testing */ + const lv_span_coords_t test_coords[] = { + {.heading = {.x1 = 40, .y1 = 20, .x2 = 280, .y2 = 20}, .middle = {.x1 = 40, .y1 = 20, .x2 = 241, .y2 = 36}, .trailing = {.x1 = 0, .y1 = 0, .x2 = 0, .y2 = 0}}, + {.heading = {.x1 = 241, .y1 = 20, .x2 = 280, .y2 = 36}, .middle = {.x1 = 20, .y1 = 36, .x2 = 280, .y2 = 63}, .trailing = {.x1 = 20, .y1 = 63, .x2 = 155, .y2 = 90}}, + {.heading = {.x1 = 155, .y1 = 63, .x2 = 280, .y2 = 90}, .middle = {.x1 = 20, .y1 = 90, .x2 = 280, .y2 = 90}, .trailing = {.x1 = 20, .y1 = 90, .x2 = 188, .y2 = 112}}, + {.heading = {.x1 = 188, .y1 = 90, .x2 = 280, .y2 = 112}, .middle = {.x1 = 20, .y1 = 112, .x2 = 280, .y2 = 112}, .trailing = {.x1 = 20, .y1 = 112, .x2 = 116, .y2 = 134}}, + {.heading = {.x1 = 116, .y1 = 112, .x2 = 280, .y2 = 134}, .middle = {.x1 = 20, .y1 = 134, .x2 = 280, .y2 = 134}, .trailing = {.x1 = 20, .y1 = 134, .x2 = 160, .y2 = 150}} + }; + + /* Define colors for visual testing */ + const lv_color_t colors[] = { + lv_palette_main(LV_PALETTE_RED), lv_palette_main(LV_PALETTE_GREEN), lv_palette_main(LV_PALETTE_BLUE), + lv_palette_main(LV_PALETTE_YELLOW), lv_palette_main(LV_PALETTE_PURPLE), lv_palette_main(LV_PALETTE_ORANGE), + lv_palette_main(LV_PALETTE_INDIGO), lv_palette_main(LV_PALETTE_BROWN), lv_palette_main(LV_PALETTE_GREY), + lv_palette_main(LV_PALETTE_PINK) + }; + const uint32_t color_count = sizeof(colors) / sizeof(colors[0]); + const lv_area_t area = spangroup->coords; + + /* Iterate through spans and validate coordinates */ + for(uint32_t i = 0; i < span_count; i++) { + lv_span_coords_t coords = lv_spangroup_get_span_coords(spangroup, spans[i]); + TEST_ASSERT_EQUAL_MEMORY(&coords.heading, &test_coords[i].heading, sizeof(lv_span_coords_t)); + + /* Visual testing */ + const lv_color_t color = colors[i % color_count]; + + /* Create and style heading object */ + lv_obj_t * obj_head = lv_obj_create(active_screen); + lv_obj_remove_style_all(obj_head); + lv_obj_set_pos(obj_head, coords.heading.x1 + area.x1, coords.heading.y1 + area.y1); + lv_obj_set_size(obj_head, coords.heading.x2 - coords.heading.x1, coords.heading.y2 - coords.heading.y1); + lv_obj_set_style_bg_color(obj_head, color, LV_PART_MAIN); + lv_obj_set_style_bg_opa(obj_head, LV_OPA_50, LV_PART_MAIN); + + /* Create and style middle object */ + lv_obj_t * obj_middle = lv_obj_create(active_screen); + lv_obj_remove_style_all(obj_middle); + lv_obj_set_pos(obj_middle, coords.middle.x1 + area.x1, coords.middle.y1 + area.y1); + lv_obj_set_size(obj_middle, coords.middle.x2 - coords.middle.x1, coords.middle.y2 - coords.middle.y1); + lv_obj_set_style_bg_color(obj_middle, color, LV_PART_MAIN); + lv_obj_set_style_bg_opa(obj_middle, LV_OPA_50, LV_PART_MAIN); + + /* Create and style trailing object */ + lv_obj_t * obj_trailing = lv_obj_create(active_screen); + lv_obj_remove_style_all(obj_trailing); + lv_obj_set_pos(obj_trailing, coords.trailing.x1 + area.x1, coords.trailing.y1 + area.y1); + lv_obj_set_size(obj_trailing, coords.trailing.x2 - coords.trailing.x1, coords.trailing.y2 - coords.trailing.y1); + lv_obj_set_style_bg_color(obj_trailing, color, LV_PART_MAIN); + lv_obj_set_style_bg_opa(obj_trailing, LV_OPA_50, LV_PART_MAIN); + } + + /* Validate the final screenshot */ + TEST_ASSERT_EQUAL_SCREENSHOT("widgets/span_09.png"); +} +#endif + #endif