From 03c48701b7842eefe2571f26e3787e8b379df985 Mon Sep 17 00:00:00 2001 From: Gabor Kiss-Vamosi Date: Thu, 14 Aug 2025 17:18:19 +0200 Subject: [PATCH] feat(span): add lv_spangroup_bind_span_text and lv_spangroup_set_span_text_fmt --- examples/widgets/span/lv_example_span_1.c | 9 ++- src/misc/lv_text_private.h | 2 +- src/others/observer/lv_observer.c | 96 +++++++++++++++++++++++ src/others/observer/lv_observer.h | 18 +++++ src/widgets/label/lv_label.h | 12 ++- src/widgets/span/lv_span.c | 47 +++++++++++ src/widgets/span/lv_span.h | 20 +++++ 7 files changed, 197 insertions(+), 7 deletions(-) diff --git a/examples/widgets/span/lv_example_span_1.c b/examples/widgets/span/lv_example_span_1.c index 93f877366b..d269d89752 100644 --- a/examples/widgets/span/lv_example_span_1.c +++ b/examples/widgets/span/lv_example_span_1.c @@ -24,6 +24,10 @@ void lv_example_span_1(void) lv_style_set_border_color(&style, lv_palette_main(LV_PALETTE_ORANGE)); lv_style_set_pad_all(&style, 2); + static lv_subject_t subject; + lv_subject_init_int(&subject, 23); + + lv_obj_t * spans = lv_spangroup_create(lv_screen_active()); /* Setting a fixed width and height to LV_SIZE_CONTENT will make the text wrap */ lv_obj_set_width(spans, 300); @@ -36,14 +40,15 @@ void lv_example_span_1(void) lv_spangroup_set_overflow(spans, LV_SPAN_OVERFLOW_CLIP); lv_spangroup_set_indent(spans, 20); + lv_span_t * span = lv_spangroup_add_span(spans); - lv_span_set_text(span, "China is a beautiful country."); + lv_span_set_text(span, "good good study, day day up."); lv_style_set_text_color(lv_span_get_style(span), lv_palette_main(LV_PALETTE_RED)); lv_style_set_text_decor(lv_span_get_style(span), LV_TEXT_DECOR_UNDERLINE); lv_style_set_text_opa(lv_span_get_style(span), LV_OPA_50); span = lv_spangroup_add_span(spans); - lv_span_set_text_static(span, "good good study, day day up."); + lv_spangroup_bind_span_text(spans, span, &subject, "%d users"); #if LV_FONT_MONTSERRAT_24 lv_style_set_text_font(lv_span_get_style(span), &lv_font_montserrat_24); #endif diff --git a/src/misc/lv_text_private.h b/src/misc/lv_text_private.h index 1088c85eef..b553b4b3d3 100644 --- a/src/misc/lv_text_private.h +++ b/src/misc/lv_text_private.h @@ -80,7 +80,7 @@ void lv_text_ins(char * txt_buf, uint32_t pos, const char * ins_txt); void lv_text_cut(char * txt, uint32_t pos, uint32_t len); /** - * return a new formatted text. Memory will be allocated to store the text. + * Return a new formatted text. Memory will be allocated to store the text. * @param fmt `printf`-like format * @param ap items to print diff --git a/src/others/observer/lv_observer.c b/src/others/observer/lv_observer.c index 9f9d8e71b7..a32a7a909b 100644 --- a/src/others/observer/lv_observer.c +++ b/src/others/observer/lv_observer.c @@ -55,6 +55,17 @@ typedef struct { const char * value; } subject_set_string_user_data_t; +typedef struct { + lv_subject_t * subject; + void * element; /**< E.g. span of a span group*/ + const char * fmt; +} bind_element_string_t; + +typedef struct { + lv_subject_t * subject; + void * element; /**< E.g. min_value of a scale section*/ + int32_t * num; +} bind_element_num_t; typedef struct { lv_subject_t * subject; @@ -92,6 +103,10 @@ static void lv_subject_notify_if_changed(lv_subject_t * subject); static void label_text_observer_cb(lv_observer_t * observer, lv_subject_t * subject); #endif +#if LV_USE_SPAN + static void span_text_observer_cb(lv_observer_t * observer, lv_subject_t * subject); +#endif + #if LV_USE_ARC static void arc_value_changed_event_cb(lv_event_t * e); static void arc_value_observer_cb(lv_observer_t * observer, lv_subject_t * subject); @@ -779,6 +794,54 @@ lv_observer_t * lv_label_bind_text(lv_obj_t * obj, lv_subject_t * subject, const } #endif /*LV_USE_LABEL*/ +#if LV_USE_SPAN +lv_observer_t * lv_spangroup_bind_span_text(lv_obj_t * obj, lv_span_t * span, lv_subject_t * subject, const char * fmt) +{ + LV_ASSERT_NULL(subject); + LV_ASSERT_NULL(obj); + LV_ASSERT_NULL(span); + + if(fmt == NULL) { + if(subject->type == LV_SUBJECT_TYPE_INT) { + fmt = "%d"; + } +#if LV_USE_FLOAT + else if(subject->type == LV_SUBJECT_TYPE_FLOAT) { + fmt = "%0.1f"; + } +#endif + else if(subject->type != LV_SUBJECT_TYPE_STRING && subject->type != LV_SUBJECT_TYPE_POINTER) { + LV_LOG_WARN("Incompatible subject type: %d", subject->type); + return NULL; + } + } + else { + if(subject->type != LV_SUBJECT_TYPE_STRING && subject->type != LV_SUBJECT_TYPE_POINTER && + subject->type != LV_SUBJECT_TYPE_INT && subject->type != LV_SUBJECT_TYPE_FLOAT) { + LV_LOG_WARN("Incompatible subject type: %d", subject->type); + return NULL; + } + } + + bind_element_string_t * user_data = lv_zalloc(sizeof(bind_element_string_t)); + if(user_data == NULL) { + LV_LOG_WARN("Couldn't allocate user_data"); + LV_ASSERT_MALLOC(user_data); + return NULL; + } + + user_data->subject = subject; + user_data->element = span; + user_data->fmt = fmt; + + lv_observer_t * observer = lv_subject_add_observer_obj(subject, span_text_observer_cb, obj, user_data); + observer->auto_free_user_data = 1; + + return observer; +} +#endif /*LV_USE_SPAN*/ + + #if LV_USE_ARC lv_observer_t * lv_arc_bind_value(lv_obj_t * obj, lv_subject_t * subject) { @@ -1090,6 +1153,39 @@ static void label_text_observer_cb(lv_observer_t * observer, lv_subject_t * subj #endif /*LV_USE_LABEL*/ +#if LV_USE_SPAN + +static void span_text_observer_cb(lv_observer_t * observer, lv_subject_t * subject) +{ + bind_element_string_t * user_data = observer->user_data; + + if(user_data->fmt == NULL) { + lv_spangroup_set_span_text(observer->target, user_data->element, subject->value.pointer); + } + else { + switch(subject->type) { + + case LV_SUBJECT_TYPE_INT: + lv_spangroup_set_span_text_fmt(observer->target, user_data->element, user_data->fmt, subject->value.num); + break; +#if LV_USE_FLOAT + case LV_SUBJECT_TYPE_FLOAT: + lv_spangroup_set_span_text_fmt(observer->target, user_data->element, user_data->fmt, subject->value.float_v); + break; +#endif + case LV_SUBJECT_TYPE_STRING: + case LV_SUBJECT_TYPE_POINTER: + lv_spangroup_set_span_text_fmt(observer->target, user_data->element, user_data->fmt, subject->value.pointer); + break; + default: + return; + } + } +} + +#endif /*LV_USE_SPAN*/ + + #if LV_USE_ARC static void arc_value_changed_event_cb(lv_event_t * e) diff --git a/src/others/observer/lv_observer.h b/src/others/observer/lv_observer.h index 1ce17c45d4..fcfb4c3c6d 100644 --- a/src/others/observer/lv_observer.h +++ b/src/others/observer/lv_observer.h @@ -545,6 +545,24 @@ lv_observer_t * lv_obj_bind_checked(lv_obj_t * obj, lv_subject_t * subject); lv_observer_t * lv_label_bind_text(lv_obj_t * obj, lv_subject_t * subject, const char * fmt); #endif +#if LV_USE_LABEL + +/** + * Bind an integer, string, or pointer Subject to a Label. + * @param obj pointer to Spangroup + * @param span pointer to Span + * @param subject pointer to Subject + * @param fmt optional printf-like format string with 1 format specifier (e.g. "%d °C") + * or NULL to bind to the value directly. + * @return pointer to newly-created Observer + * @note `fmt == NULL` can be used only with string and pointer Subjects. + * @note If Subject is a pointer and `fmt == NULL`, pointer must point + * to a `\0` terminated string. + */ +lv_observer_t * lv_spangroup_bind_span_text(lv_obj_t * obj, lv_span_t * span, lv_subject_t * subject, const char * fmt); + +#endif + #if LV_USE_ARC /** * Bind an integer subject to an Arc's value. diff --git a/src/widgets/label/lv_label.h b/src/widgets/label/lv_label.h index c7800ec4d1..202427b5d4 100644 --- a/src/widgets/label/lv_label.h +++ b/src/widgets/label/lv_label.h @@ -84,26 +84,28 @@ lv_obj_t * lv_label_create(lv_obj_t * parent); * Set a new text for a label. Memory will be allocated to store the text by the label. * @param obj pointer to a label object * @param text '\0' terminated character string. NULL to refresh with the current text. + * @note If `LV_USE_ARABIC_PERSIAN_CHARS` is enabled the text will be modified to have the correct Arabic + * characters in it. */ void lv_label_set_text(lv_obj_t * obj, const char * text); /** * Set a new formatted text for a label. Memory will be allocated to store the text by the label. * @param obj pointer to a label object - * @param fmt `printf`-like format - * + * @param fmt `printf`-like format string * Example: * @code * lv_label_set_text_fmt(label1, "%d user", user_num); * @endcode + * @note It ignores `LV_USE_ARABIC_PERSIAN_CHARS` */ void lv_label_set_text_fmt(lv_obj_t * obj, const char * fmt, ...) LV_FORMAT_ATTRIBUTE(2, 3); /** * Set a new formatted text for a label. Memory will be allocated to store the text by the label. * @param obj pointer to a label object - * @param fmt `printf`-like format - * @param args variadic argments list + * @param fmt `printf`-like format string + * @param args variadic arguments list * * Example: * @code @@ -112,6 +114,7 @@ void lv_label_set_text_fmt(lv_obj_t * obj, const char * fmt, ...) LV_FORMAT_ATTR * lv_label_set_text_vfmt(label1, fmt, args); * va_end(args); * @endcode + * @note It ignores `LV_USE_ARABIC_PERSIAN_CHARS` */ void lv_label_set_text_vfmt(lv_obj_t * obj, const char * fmt, va_list args); @@ -120,6 +123,7 @@ void lv_label_set_text_vfmt(lv_obj_t * obj, const char * fmt, va_list args); * has to be 'alive' while the label exists. * @param obj pointer to a label object * @param text pointer to a text. NULL to refresh with the current text. + * @note It ignores `LV_USE_ARABIC_PERSIAN_CHARS` */ void lv_label_set_text_static(lv_obj_t * obj, const char * text); diff --git a/src/widgets/span/lv_span.c b/src/widgets/span/lv_span.c index 4231c71f11..dfb6b187c3 100644 --- a/src/widgets/span/lv_span.c +++ b/src/widgets/span/lv_span.c @@ -199,6 +199,29 @@ void lv_span_set_text(lv_span_t * span, const char * text) #endif } +void lv_span_set_text_fmt(lv_span_t * span, const char * fmt, ...) +{ + if(span == NULL || fmt == NULL) { + return; + } + + va_list args; + va_start(args, fmt); + char * text = lv_text_set_text_vfmt(fmt, args); + LV_ASSERT_MALLOC(text); + if(text == NULL) return; + + va_end(args); + + if(span->txt == NULL && span->static_flag) { + lv_free(span->txt); + } + + span->static_flag = 0; + span->txt = text; +} + + void lv_spangroup_set_span_text(lv_obj_t * obj, lv_span_t * span, const char * text) { lv_span_set_text(span, text); @@ -234,6 +257,30 @@ void lv_spangroup_set_span_text_static(lv_obj_t * obj, lv_span_t * span, const c lv_spangroup_refresh(obj); } +void lv_spangroup_set_span_text_fmt(lv_obj_t * obj, lv_span_t * span, const char * fmt, ...) +{ + if(span == NULL || fmt == NULL) { + return; + } + + va_list args; + va_start(args, fmt); + char * text = lv_text_set_text_vfmt(fmt, args); + LV_ASSERT_MALLOC(text); + if(text == NULL) return; + + va_end(args); + + if(span->txt == NULL && span->static_flag) { + lv_free(span->txt); + } + + span->static_flag = 0; + span->txt = text; + + lv_spangroup_refresh(obj); +} + void lv_spangroup_set_span_style(lv_obj_t * obj, lv_span_t * span, const lv_style_t * style) { LV_ASSERT_OBJ(obj, MY_CLASS); diff --git a/src/widgets/span/lv_span.h b/src/widgets/span/lv_span.h index ff6fd310ff..38b211da4b 100644 --- a/src/widgets/span/lv_span.h +++ b/src/widgets/span/lv_span.h @@ -91,6 +91,17 @@ void lv_spangroup_delete_span(lv_obj_t * obj, lv_span_t * span); */ void lv_span_set_text(lv_span_t * span, const char * text); + +/** + * Set a new text for a span using a printf-like formatting string. + * Memory will be allocated to store the text by the span. + * As the spangroup is not passed a redraw (invalidation) can't be triggered automatically. + * Therefore `lv_spangroup_refresh(spangroup)` needs to be called manually, + * @param span pointer to a span. + * @param fmt `printf`-like format string + */ +void lv_span_set_text_fmt(lv_span_t * span, const char * fmt, ...); + /** * Set a static text. It will not be saved by the span so the 'text' variable * has to be 'alive' while the span exist. @@ -118,6 +129,15 @@ void lv_spangroup_set_span_text(lv_obj_t * obj, lv_span_t * span, const char * t */ void lv_spangroup_set_span_text_static(lv_obj_t * obj, lv_span_t * span, const char * text); +/** + * Set a new text for a span using a printf-like formatting string. + * Memory will be allocated to store the text by the span. + * @param obj pointer to a spangroup widget. + * @param span pointer to a span. + * @param fmt `printf`-like format string + */ +void lv_spangroup_set_span_text_fmt(lv_obj_t * obj, lv_span_t * span, const char * fmt, ...); + /** * Set a static text. It will not be saved by the span so the 'text' variable * has to be 'alive' while the span exist.