From 9fc556a90b34b4ecd5dc8ffc4c293777d0f5b4ff Mon Sep 17 00:00:00 2001 From: guoweilkd <35251456+guoweilkd@users.noreply.github.com> Date: Thu, 13 May 2021 23:42:33 +0800 Subject: [PATCH] add span widget (#2227) Co-authored-by: guowei15 --- Kconfig | 2 + docs/widgets/extra/span.md | 74 ++ examples/widgets/lv_example_widgets.h | 2 + examples/widgets/span/index.rst | 13 + examples/widgets/span/lv_example_span_1.c | 60 ++ examples/widgets/span/lv_example_span_1.py | 0 lv_conf_template.h | 6 + src/draw/lv_draw_label.c | 11 +- src/draw/lv_draw_label.h | 3 + src/extra/widgets/lv_widgets.h | 2 +- src/extra/widgets/span/lv_span.c | 965 +++++++++++++++++++++ src/extra/widgets/span/lv_span.h | 203 +++++ src/lv_conf_internal.h | 8 + 13 files changed, 1341 insertions(+), 8 deletions(-) create mode 100644 docs/widgets/extra/span.md create mode 100644 examples/widgets/span/index.rst create mode 100644 examples/widgets/span/lv_example_span_1.c create mode 100644 examples/widgets/span/lv_example_span_1.py create mode 100644 src/extra/widgets/span/lv_span.c create mode 100644 src/extra/widgets/span/lv_span.h diff --git a/Kconfig b/Kconfig index be6cd65d6..f509d9654 100644 --- a/Kconfig +++ b/Kconfig @@ -698,6 +698,8 @@ menu "LVGL configuration" bool "Tileview" config LV_USE_WIN bool "Win" + config LV_USE_SPAN + bool "span" endmenu menu "Layouts" diff --git a/docs/widgets/extra/span.md b/docs/widgets/extra/span.md new file mode 100644 index 000000000..1d2979b7b --- /dev/null +++ b/docs/widgets/extra/span.md @@ -0,0 +1,74 @@ +```eval_rst +.. include:: /header.rst +:github_url: |github_link_base|/widgets/span.md +``` +# span (lv_span) + +## Overview + +A spangroup is the object that is used to display rich text. different from the label object, `spangroup` can automatically organize text of different fonts, colors, and sizes into the spangroup obj. + +## Parts and Styles +- `LV_PART_MAIN` The spangroup has only the part. + +## Usage + +### Set text and style + +spangroup object uses span to describe text and text style. so, first we need to create `span` descriptor use function `lv_span_t * span = lv_span_create(spangroup)`.then use `lv_span_set_text(span, "text")` to set text.The style of the modified text is the same as the normal style used,eg:`lv_style_set_text_color(&span->style, lv_palette_main(LV_PALETTE_RED))`. + +If spangroup object `mode != LV_SPAN_MODE_FIXED`.You must call `lv_span_refr_mode()` after you have modified `span` style(eg:set text, changed the font size, del span). + +### Text align +like label object, The spangroup can be one the following modes: +- `LV_TEXT_ALIGN_LEFT` Align text to left. +- `LV_TEXT_ALIGN_CENTER` Align text to center. +- `LV_TEXT_ALIGN_RIGHT` Align text to right. +- `LV_TEXT_ALIGN_AUTO` Align text auto. + +use function `lv_span_set_align(spangroup, LV_TEXT_ALIGN_CENTER)` to set text align. + +### Modes +The spangroup can be one the following modes: +- `LV_SPAN_MODE_FIXED` fixed the obj size. +- `LV_SPAN_MODE_EXPAND` Expand the object size to the text size. only one line. +- `LV_SPAN_MODE_BREAK` Keep width, break the too long lines and auto expand height. + +use function `lv_span_set_mode(spangroup, LV_SPAN_MODE_BREAK)` to set obj mode. + +### Overflow +The spangroup can be one the following modes: +- `LV_SPAN_OVERFLOW_CLIP` truncate the text at the limit of the area. +- `LV_SPAN_OVERFLOW_ELLIPSIS` This mode value will display an ellipsis(`...`) when text overflow the area. + +use function `lv_span_set_overflow(spangroup, LV_SPAN_OVERFLOW_CLIP)` to set obj Overflow. + +### first line indent +use function `lv_span_set_indent(spangroup, 20)` to set text indent of first line. + +## Events +Only the [Generic events](../overview/event.html#generic-events) are sent by the object type. + +Learn more about [Events](/overview/event). + +## Keys +No *Keys* are processed by the object type. + +Learn more about [Keys](/overview/indev). + +## Example + +```eval_rst + +.. include:: ../../../examples/widgets/span/index.rst + +``` + +## API + +```eval_rst + +.. doxygenfile:: lv_span.h + :project: lvgl + +``` diff --git a/examples/widgets/lv_example_widgets.h b/examples/widgets/lv_example_widgets.h index f66eeb048..b67890d68 100644 --- a/examples/widgets/lv_example_widgets.h +++ b/examples/widgets/lv_example_widgets.h @@ -118,6 +118,8 @@ void lv_example_tileview_1(void); void lv_example_win_1(void); +void lv_example_span_1(void); + /********************** * MACROS **********************/ diff --git a/examples/widgets/span/index.rst b/examples/widgets/span/index.rst new file mode 100644 index 000000000..50dfb72b0 --- /dev/null +++ b/examples/widgets/span/index.rst @@ -0,0 +1,13 @@ +C +^ + +span with custom style +""""""""""""""""""""" + +.. lv_example:: lv_ex_widgets/lv_ex_span/lv_ex_span_1 + :language: c + +MicroPython +^^^^^^^^^^^ + +No examples yet. diff --git a/examples/widgets/span/lv_example_span_1.c b/examples/widgets/span/lv_example_span_1.c new file mode 100644 index 000000000..31c5caa69 --- /dev/null +++ b/examples/widgets/span/lv_example_span_1.c @@ -0,0 +1,60 @@ +#include "../../lv_examples.h" +#if LV_USE_SPAN && LV_BUILD_EXAMPLES + +/** + * Create span. + */ +void lv_example_span_1(void) +{ + static lv_style_t style; + lv_style_init(&style); + lv_style_set_border_width(&style, 1); + lv_style_set_border_color(&style, lv_palette_main(LV_PALETTE_ORANGE)); + lv_style_set_pad_all(&style, 2); + + lv_obj_t * spans = lv_spangroup_create(lv_scr_act()); + lv_obj_set_width(spans, 300); + lv_obj_set_height(spans,300); + lv_obj_center(spans); + lv_obj_add_style(spans, &style, 0); + + lv_span_set_align(spans, LV_TEXT_ALIGN_LEFT); + lv_span_set_overflow(spans, LV_SPAN_OVERFLOW_CLIP); + lv_span_set_indent(spans, 20); + lv_span_set_mode(spans, LV_SPAN_MODE_BREAK); + + lv_span_t * span = lv_span_create(spans); + lv_span_set_text(span, "china is a beautiful country."); + lv_style_set_text_color(&span->style, lv_palette_main(LV_PALETTE_RED)); + lv_style_set_text_decor(&span->style, LV_TEXT_DECOR_STRIKETHROUGH | LV_TEXT_DECOR_UNDERLINE); + lv_style_set_text_opa(&span->style, LV_OPA_30); + + span = lv_span_create(spans); + lv_span_set_text_static(span, "good good study, day day up."); +#if LV_FONT_MONTSERRAT_24 + lv_style_set_text_font(&span->style, &lv_font_montserrat_24); +#endif + lv_style_set_text_color(&span->style, lv_palette_main(LV_PALETTE_GREEN)); + + span = lv_span_create(spans); + lv_span_set_text_static(span, "LVGL is an open-source graphics library."); + lv_style_set_text_color(&span->style, lv_palette_main(LV_PALETTE_BLUE)); + + span = lv_span_create(spans); + lv_span_set_text_static(span, "the boy no name."); + lv_style_set_text_color(&span->style, lv_palette_main(LV_PALETTE_GREEN)); +#if LV_FONT_MONTSERRAT_20 + lv_style_set_text_font(&span->style, &lv_font_montserrat_20); +#endif + lv_style_set_text_decor(&span->style, LV_TEXT_DECOR_UNDERLINE); + + span = lv_span_create(spans); + lv_span_set_text(span, "I have a dream that hope to come true."); + + lv_span_refr_mode(spans); + + //lv_span_del(spans, span); + //lv_obj_del(spans); +} + +#endif diff --git a/examples/widgets/span/lv_example_span_1.py b/examples/widgets/span/lv_example_span_1.py new file mode 100644 index 000000000..e69de29bb diff --git a/lv_conf_template.h b/lv_conf_template.h index 3afe18403..29225f123 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -459,6 +459,12 @@ e.g. "stm32f769xx.h" or "stm32f429xx.h"*/ #define LV_USE_WIN 1 +#define LV_USE_SPAN 1 +#if LV_USE_SPAN +/*A line text can contain maximum num of span descriptor */ +# define LV_SPAN_SNIPPET_STACK_SIZE 64 +#endif + /*----------- * Themes *----------*/ diff --git a/src/draw/lv_draw_label.c b/src/draw/lv_draw_label.c index 8a9c85f33..23fab20f1 100644 --- a/src/draw/lv_draw_label.c +++ b/src/draw/lv_draw_label.c @@ -32,9 +32,6 @@ typedef uint8_t cmd_state_t; /********************** * STATIC PROTOTYPES **********************/ -LV_ATTRIBUTE_FAST_MEM static void lv_draw_letter(const lv_point_t * pos_p, const lv_area_t * clip_area, - const lv_font_t * font_p, - uint32_t letter, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode); LV_ATTRIBUTE_FAST_MEM static void draw_letter_normal(lv_coord_t pos_x, lv_coord_t pos_y, lv_font_glyph_dsc_t * g, const lv_area_t * clip_area, const uint8_t * map_p, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode); @@ -398,10 +395,10 @@ LV_ATTRIBUTE_FAST_MEM void lv_draw_label(const lv_area_t * coords, const lv_area * @param color color of letter * @param opa opacity of letter (0..255) */ -LV_ATTRIBUTE_FAST_MEM static void lv_draw_letter(const lv_point_t * pos_p, const lv_area_t * clip_area, - const lv_font_t * font_p, - uint32_t letter, - lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode) +LV_ATTRIBUTE_FAST_MEM void lv_draw_letter(const lv_point_t * pos_p, const lv_area_t * clip_area, + const lv_font_t * font_p, + uint32_t letter, + lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode) { if(opa < LV_OPA_MIN) return; if(opa > LV_OPA_MAX) opa = LV_OPA_COVER; diff --git a/src/draw/lv_draw_label.h b/src/draw/lv_draw_label.h index 90f93080f..9cc7abb5f 100644 --- a/src/draw/lv_draw_label.h +++ b/src/draw/lv_draw_label.h @@ -84,6 +84,9 @@ LV_ATTRIBUTE_FAST_MEM void lv_draw_label(const lv_area_t * coords, const lv_area const lv_draw_label_dsc_t * dsc, const char * txt, lv_draw_label_hint_t * hint); +LV_ATTRIBUTE_FAST_MEM void lv_draw_letter(const lv_point_t * pos_p, const lv_area_t * clip_area, + const lv_font_t * font_p, + uint32_t letter, lv_color_t color, lv_opa_t opa, lv_blend_mode_t blend_mode); //! @endcond /*********************** * GLOBAL VARIABLES diff --git a/src/extra/widgets/lv_widgets.h b/src/extra/widgets/lv_widgets.h index e94acce39..0dcaccadd 100644 --- a/src/extra/widgets/lv_widgets.h +++ b/src/extra/widgets/lv_widgets.h @@ -30,7 +30,7 @@ extern "C" { #include "colorwheel/lv_colorwheel.h" #include "led/lv_led.h" #include "imgbtn/lv_imgbtn.h" - +#include "span/lv_span.h" /********************* * DEFINES diff --git a/src/extra/widgets/span/lv_span.c b/src/extra/widgets/span/lv_span.c new file mode 100644 index 000000000..8632f38ef --- /dev/null +++ b/src/extra/widgets/span/lv_span.c @@ -0,0 +1,965 @@ +/** + * @file lv_span.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_span.h" + +#if LV_USE_SPAN != 0 + +/********************* + * DEFINES + *********************/ +#define MY_CLASS &lv_spangroup_class + +/********************** + * TYPEDEFS + **********************/ +typedef struct { + lv_span_t * span; + const char * txt; + const lv_font_t * font; + uint16_t bytes; + lv_coord_t txt_w; + lv_coord_t line_h; + lv_coord_t letter_space; +} lv_snippet_t; + +struct _snippet_stack { + lv_snippet_t stack[LV_SPAN_SNIPPET_STACK_SIZE]; + uint16_t index; +}; + +/********************** + * STATIC PROTOTYPES + **********************/ +static void lv_spangroup_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj); +static void lv_spangroup_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj); +static void lv_spangroup_event(const lv_obj_class_t * class_p, lv_event_t * e); +static void draw_main(lv_event_t * e); + +static const lv_font_t * lv_span_get_style_text_font(lv_obj_t * par, lv_span_t * span); +static lv_coord_t lv_span_get_style_text_letter_space(lv_obj_t * par, lv_span_t * span); +static lv_color_t lv_span_get_style_text_color(lv_obj_t * par, lv_span_t * span); +static lv_color_t lv_span_get_style_text_color(lv_obj_t * par, lv_span_t * span); +static lv_opa_t lv_span_get_style_text_opa(lv_obj_t * par, lv_span_t * span); +static lv_opa_t lv_span_get_style_text_blend_mode(lv_obj_t * par, lv_span_t * span); +static int32_t lv_span_get_style_text_decor(lv_obj_t * par, lv_span_t * span); + +static inline void span_text_check(const char ** text); +static inline bool is_break_char(uint32_t letter); +static void get_txt_coords(const lv_obj_t * span, lv_area_t * area); +static void lv_draw_span(lv_obj_t * spans, const lv_area_t * coords, const lv_area_t * mask); +static bool lv_txt_get_snippet(const char * txt, const lv_font_t * font, lv_coord_t letter_space, + lv_coord_t max_width, lv_text_flag_t flag, lv_coord_t * use_width, + uint32_t * end_ofs); + +static void lv_snippet_clear(void); +static uint16_t lv_get_snippet_cnt(); +static void lv_snippet_push(lv_snippet_t * item); +static lv_snippet_t * lv_get_snippet(uint16_t index); + +/********************** + * STATIC VARIABLES + **********************/ +struct _snippet_stack snippet_stack; +const lv_obj_class_t lv_spangroup_class = { + .base_class = &lv_obj_class, + .constructor_cb = lv_spangroup_constructor, + .destructor_cb = lv_spangroup_destructor, + .event_cb = lv_spangroup_event, + .instance_size = sizeof(lv_spangroup_t), +}; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create a spangroup objects + * @param par pointer to an object, it will be the parent of the new spangroup + * @return pointer to the created spangroup + */ +lv_obj_t * lv_spangroup_create(lv_obj_t * par) +{ + return lv_obj_class_create_obj(&lv_spangroup_class, par, NULL); +} + +/** + * Create a span string descriptor and add to spangroup. + * @param obj pointer to a spangroup object. + * @return pointer to the created span. + */ +lv_span_t * lv_span_create(lv_obj_t * obj) +{ + if(obj == NULL) { + return NULL; + } + + lv_spangroup_t * spans = (lv_spangroup_t *)obj; + lv_span_t * span = _lv_ll_ins_tail(&spans->child_ll); + LV_ASSERT_MALLOC(span); + + lv_style_init(&span->style); + span->txt = (char *)""; + span->static_flag = 1; + + lv_obj_invalidate(obj); + return span; +} + +/** + * Remove the span from the spangroup and free memory. + * @param obj pointer to a spangroup object. + * @param span pointer to a span. + */ +void lv_span_del(lv_obj_t * obj, lv_span_t * span) +{ + if(obj == NULL) { + return; + } + + lv_spangroup_t * spans = (lv_spangroup_t *)obj; + lv_span_t * cur_span; + _LV_LL_READ(&spans->child_ll, cur_span) { + if(cur_span == span) { + _lv_ll_remove(&spans->child_ll, cur_span); + if(cur_span->txt && cur_span->static_flag == 0) { + lv_mem_free(cur_span->txt); + } + lv_mem_free(cur_span); + break; + } + } + + lv_span_refr_mode(obj); +} + +/*===================== + * Setter functions + *====================*/ + +/** + * Set a new text for a span. Memory will be allocated to store the text by the span. + * @param span pointer to a span. + * @param text pointer to a text. + */ +void lv_span_set_text(lv_span_t * span, const char * text) +{ + if(span == NULL || text == NULL) { + return; + } + + if(span->txt == NULL || span->static_flag == 1) { + span->txt = lv_mem_alloc(strlen(text) + 1); + } + else { + lv_mem_realloc(span->txt, strlen(text) + 1); + } + span->static_flag = 0; + strcpy(span->txt, text); +} + +/** + * Set a static text. It will not be saved by the span so the 'text' variable + * has to be 'alive' while the span exist. + * @param span pointer to a span. + * @param text pointer to a text. + */ +void lv_span_set_text_static(lv_span_t * span, const char * text) +{ + if(span == NULL || text == NULL) { + return; + } + + if(span->txt && span->static_flag == 0) { + lv_mem_free(span->txt); + } + span->static_flag = 1; + span->txt = (char *)text; +} + +/** + * Set the align of the spangroup. + * @param obj pointer to a spangroup object. + * @param align see lv_text_align_t for details. + */ +void lv_span_set_align(lv_obj_t * obj, lv_text_align_t align) +{ + lv_spangroup_t * spans = (lv_spangroup_t *)obj; + if(spans->align == align) return; + + spans->align = align; + lv_obj_invalidate(obj); +} + +/** + * Set the overflow of the spangroup. + * @param obj pointer to a spangroup object. + * @param overflow see lv_span_overflow_t for details. + */ +void lv_span_set_overflow(lv_obj_t * obj, lv_span_overflow_t overflow) +{ + lv_spangroup_t * spans = (lv_spangroup_t *)obj; + if(spans->overflow == overflow) return; + + spans->overflow = overflow; + lv_obj_invalidate(obj); +} + +/** + * Set the indent of the spangroup. + * @param obj pointer to a spangroup object. + * @param indent The first line indentation + */ +void lv_span_set_indent(lv_obj_t * obj, lv_coord_t indent) +{ + lv_spangroup_t * spans = (lv_spangroup_t *)obj; + if(spans->indent == indent) return; + + spans->indent = indent; + lv_obj_invalidate(obj); +} + +/** + * Set the mode of the spangroup. + * @param obj pointer to a spangroup object. + * @param mode see lv_span_mode_t for details. + */ +void lv_span_set_mode(lv_obj_t * obj, lv_span_mode_t mode) +{ + lv_spangroup_t * spans = (lv_spangroup_t *)obj; + if(spans->mode == mode) return; + + spans->mode = mode; + lv_span_refr_mode(obj); +} + +/*===================== + * Getter functions + *====================*/ + +/** + * get the align of the spangroup. + * @param obj pointer to a spangroup object. + * @return the align value. + */ +lv_text_align_t lv_span_get_align(lv_obj_t * obj) +{ + lv_spangroup_t * spans = (lv_spangroup_t *)obj; + return spans->align; +} + +/** + * get the overflow of the spangroup. + * @param obj pointer to a spangroup object. + * @return the overflow value. + */ +lv_span_overflow_t lv_span_get_overflow(lv_obj_t * obj) +{ + lv_spangroup_t * spans = (lv_spangroup_t *)obj; + return spans->overflow; +} + +/** + * get the indent of the spangroup. + * @param obj pointer to a spangroup object. + * @return the indent value. + */ +lv_coord_t lv_span_get_indent(lv_obj_t * obj) +{ + lv_spangroup_t * spans = (lv_spangroup_t *)obj; + return spans->indent; +} + +/** + * Set the mode of the spangroup. + * @param obj pointer to a spangroup object. + * @return the mode value. + */ +lv_span_mode_t lv_span_get_mode(lv_obj_t * obj) +{ + lv_spangroup_t * spans = (lv_spangroup_t *)obj; + return spans->mode; +} + +/** + * update the mode of the spangroup. + * @param obj pointer to a spangroup object. + */ +void lv_span_refr_mode(lv_obj_t * obj) +{ + lv_spangroup_t * spans = (lv_spangroup_t *)obj; + + if(_lv_ll_get_head(&spans->child_ll) == NULL) { + return; + } + + if(spans->mode == LV_SPAN_MODE_EXPAND) { + lv_coord_t width = lv_span_get_expand_width(obj); + lv_coord_t height = lv_span_get_max_line_h(obj); + lv_coord_t top_pad = lv_obj_get_style_pad_top(obj, LV_PART_MAIN); + lv_coord_t bottom_pad = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN); + lv_obj_set_width(obj, width + spans->indent); + lv_obj_set_height(obj, height + top_pad + bottom_pad); + } + else if(spans->mode == LV_SPAN_MODE_BREAK) { + lv_coord_t height = lv_span_get_expand_height(obj, lv_obj_get_width(obj)); + lv_obj_set_height(obj, height); + } + + lv_obj_invalidate(obj); +} + +/** + * get max line height of all span in the spangroup. + * @param obj pointer to a spangroup object. + */ +lv_coord_t lv_span_get_max_line_h(lv_obj_t * obj) +{ + lv_spangroup_t * spans = (lv_spangroup_t *)obj; + + lv_coord_t max_line_h = 0; + lv_span_t * cur_span; + _LV_LL_READ(&spans->child_ll, cur_span) { + const lv_font_t * font = lv_span_get_style_text_font(obj, cur_span); + lv_coord_t line_h = lv_font_get_line_height(font); + if(line_h > max_line_h) { + max_line_h = line_h; + } + } + + return max_line_h; +} + +/** + * get the width when all span of spangroup on a line. include spangroup pad. + * @param obj pointer to a spangroup object. + */ +lv_coord_t lv_span_get_expand_width(lv_obj_t * obj) +{ + lv_spangroup_t * spans = (lv_spangroup_t *)obj; + + if(_lv_ll_get_head(&spans->child_ll) == NULL) { + return 0; + } + + lv_coord_t width = 0; + lv_span_t * cur_span; + _LV_LL_READ(&spans->child_ll, cur_span) { + const lv_font_t * font = lv_span_get_style_text_font(obj, cur_span); + lv_coord_t letter_space = lv_span_get_style_text_letter_space(obj, cur_span); + uint32_t j = 0; + const char * cur_txt = cur_span->txt; + span_text_check(&cur_txt); + while(cur_txt[j] != 0) { + uint32_t letter = _lv_txt_encoded_next(cur_txt, &j); + uint32_t letter_next = _lv_txt_encoded_next(&cur_txt[j], NULL); + int32_t letter_w = lv_font_get_glyph_width(font, letter, letter_next); + width = width + letter_w + letter_space; + } + } + + lv_coord_t left_pad = lv_obj_get_style_pad_left(obj, LV_PART_MAIN); + lv_coord_t right_pad = lv_obj_get_style_pad_right(obj, LV_PART_MAIN); + width = width + left_pad + right_pad; + + return width; +} + +/** + * get the height with width fixed. the height include spangroup pad. + * @param obj pointer to a spangroup object. + */ +lv_coord_t lv_span_get_expand_height(lv_obj_t * obj, lv_coord_t width) +{ + lv_spangroup_t * spans = (lv_spangroup_t *)obj; + if(_lv_ll_get_head(&spans->child_ll) == NULL) { + return 0; + } + + /* init draw variable */ + lv_text_flag_t txt_flag = LV_TEXT_FLAG_NONE; + lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN); + lv_coord_t left_pad = lv_obj_get_style_pad_left(obj, LV_PART_MAIN); + lv_coord_t right_pad = lv_obj_get_style_pad_right(obj, LV_PART_MAIN); + lv_coord_t max_width = width - left_pad - right_pad; + lv_coord_t max_w = max_width - spans->indent; /* first line need minus indent */ + + /* coords of draw span-txt */ + lv_point_t txt_pos; + txt_pos.y = 0; + txt_pos.x = 0 + spans->indent; /* first line need add indent */ + + lv_span_t * cur_span = _lv_ll_get_head(&spans->child_ll); + const char * cur_txt = cur_span->txt; + span_text_check(&cur_txt); + uint32_t cur_txt_ofs = 0; + lv_snippet_t snippet; /* use to save cur_span info and push it to stack */ + memset(&snippet, 0, sizeof(snippet)); + + /* the loop control how many lines need to draw */ + while(cur_span) { + int snippet_cnt = 0; + lv_coord_t max_line_h = 0; /* the max height of span-font when a line have a lot of span */ + + /* the loop control to find a line and push the relevant span info into stack */ + while(1) { + /* switch to the next span when current is end */ + if(cur_txt[cur_txt_ofs] == '\0') { + cur_span = _lv_ll_get_next(&spans->child_ll, cur_span); + if(cur_span == NULL) break; + cur_txt = cur_span->txt; + span_text_check(&cur_txt); + cur_txt_ofs = 0; + } + + /* init span info to snippet. */ + if(cur_txt_ofs == 0) { + snippet.span = cur_span; + snippet.font = lv_span_get_style_text_font(obj, cur_span); + snippet.letter_space = lv_span_get_style_text_letter_space(obj, cur_span); + snippet.line_h = lv_font_get_line_height(snippet.font) + line_space; + } + + /* get current span text line info */ + uint32_t next_ofs = 0; + lv_coord_t use_width = 0; + bool isfill = lv_txt_get_snippet(&cur_txt[cur_txt_ofs], snippet.font, snippet.letter_space, + max_w, txt_flag, &use_width, &next_ofs); + + /* break word deal width */ + if(isfill && next_ofs > 0 && snippet_cnt > 0) { + uint32_t letter = (uint32_t)cur_txt[cur_txt_ofs + next_ofs - 1]; + if(!(letter == '\0' || letter == '\n' || letter == '\r' || is_break_char(letter))) { + letter = (uint32_t)cur_txt[cur_txt_ofs + next_ofs]; + if(!(letter == '\0' || letter == '\n' || letter == '\r' || is_break_char(letter))) { + break; + } + } + } + + snippet.txt = &cur_txt[cur_txt_ofs]; + snippet.bytes = next_ofs; + snippet.txt_w = use_width; + cur_txt_ofs += next_ofs; + if(max_line_h < snippet.line_h) { + max_line_h = snippet.line_h; + } + snippet_cnt ++; + if(isfill) { + break; + } + max_w -= use_width; + } + + /* next line init */ + txt_pos.x = 0; + txt_pos.y += max_line_h; + max_w = max_width; + } + + lv_coord_t top_pad = lv_obj_get_style_pad_top(obj, LV_PART_MAIN); + lv_coord_t bottom_pad = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN); + txt_pos.y = txt_pos.y + top_pad + bottom_pad - line_space; + + return txt_pos.y; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void lv_spangroup_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj) +{ + LV_UNUSED(class_p); + lv_spangroup_t * spans = (lv_spangroup_t *)obj; + _lv_ll_init(&spans->child_ll, sizeof(lv_span_t)); + spans->indent = 0; + spans->align = LV_TEXT_ALIGN_LEFT; + spans->mode = LV_SPAN_MODE_FIXED; + spans->overflow = LV_SPAN_OVERFLOW_CLIP; +} + +static void lv_spangroup_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj) +{ + LV_UNUSED(class_p); + lv_spangroup_t * spans = (lv_spangroup_t *)obj; + lv_span_t * cur_span = _lv_ll_get_head(&spans->child_ll); + while(cur_span) { + _lv_ll_remove(&spans->child_ll, cur_span); + if(cur_span->txt && cur_span->static_flag == 0) { + lv_mem_free(cur_span->txt); + } + lv_mem_free(cur_span); + cur_span = _lv_ll_get_head(&spans->child_ll); + } +} + +static void lv_spangroup_event(const lv_obj_class_t * class_p, lv_event_t * e) +{ + LV_UNUSED(class_p); + + /* Call the ancestor's event handler */ + if(lv_obj_event_base(MY_CLASS, e) != LV_RES_OK) return; + + lv_event_code_t code = lv_event_get_code(e); + lv_obj_t * obj = lv_event_get_target(e); + + if(code == LV_EVENT_DRAW_MAIN) { + draw_main(e); + } + else if(code == LV_EVENT_STYLE_CHANGED) { + lv_span_refr_mode(obj); + } + else if(code == LV_EVENT_SIZE_CHANGED) { + lv_span_refr_mode(obj); + } +} + +static void draw_main(lv_event_t * e) +{ + lv_obj_t * obj = lv_event_get_target(e); + const lv_area_t * clip_area = lv_event_get_param(e); + + lv_area_t txt_coords; + get_txt_coords(obj, &txt_coords); + + lv_area_t txt_clip; + bool is_common = _lv_area_intersect(&txt_clip, clip_area, &txt_coords); + if(!is_common) return; + + lv_draw_span(obj, &txt_coords, clip_area); +} + +static void get_txt_coords(const lv_obj_t * obj, lv_area_t * area) +{ + lv_obj_get_coords(obj, area); + + lv_coord_t left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN); + lv_coord_t right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN); + lv_coord_t top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN); + lv_coord_t bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN); + area->x1 += left; + area->x2 -= right; + area->y1 += top; + area->y2 -= bottom; +} + +/** + * @return true for txt fill the max_width. + */ +static bool lv_txt_get_snippet(const char * txt, const lv_font_t * font, + lv_coord_t letter_space, lv_coord_t max_width, lv_text_flag_t flag, + lv_coord_t * use_width, uint32_t * end_ofs) +{ + uint32_t ofs = _lv_txt_get_next_line(txt, font, letter_space, max_width, flag); + lv_coord_t width = lv_txt_get_width(txt, ofs, font, letter_space, flag); + *end_ofs = ofs; + *use_width = width; + + if(txt[ofs] == '\0' && width < max_width) { + return false; + } + else { + return true; + } +} + +static void lv_snippet_push(lv_snippet_t * item) +{ + if(snippet_stack.index < LV_SPAN_SNIPPET_STACK_SIZE) { + memcpy(&snippet_stack.stack[snippet_stack.index], item, sizeof(lv_snippet_t)); + snippet_stack.index++; + } + else { + LV_LOG_ERROR("span draw stack overflow, please set LV_SPAN_SNIPPET_STACK_SIZE too larger"); + } +} + +static uint16_t lv_get_snippet_cnt() +{ + return snippet_stack.index; +} + +static lv_snippet_t * lv_get_snippet(uint16_t index) +{ + return &snippet_stack.stack[index]; +} + +static void lv_snippet_clear(void) +{ + snippet_stack.index = 0; +} + +static const lv_font_t * lv_span_get_style_text_font(lv_obj_t * par, lv_span_t * span) +{ + const lv_font_t * font; + lv_style_value_t value; + lv_res_t res = lv_style_get_prop(&span->style, LV_STYLE_TEXT_FONT, &value); + if(res != LV_RES_OK) { + font = lv_obj_get_style_text_font(par, LV_PART_MAIN); + } + else { + font = (const lv_font_t *)value.ptr; + } + return font; +} + +static lv_coord_t lv_span_get_style_text_letter_space(lv_obj_t * par, lv_span_t * span) +{ + lv_coord_t letter_space; + lv_style_value_t value; + lv_res_t res = lv_style_get_prop(&span->style, LV_STYLE_TEXT_LETTER_SPACE, &value); + if(res != LV_RES_OK) { + letter_space = lv_obj_get_style_text_letter_space(par, LV_PART_MAIN); + } + else { + letter_space = (lv_coord_t)value.num; + } + return letter_space; +} + +static lv_color_t lv_span_get_style_text_color(lv_obj_t * par, lv_span_t * span) +{ + lv_style_value_t value; + lv_res_t res = lv_style_get_prop(&span->style, LV_STYLE_TEXT_COLOR, &value); + if(res != LV_RES_OK) { + value.color = lv_obj_get_style_text_color(par, LV_PART_MAIN); + } + return value.color; +} + +static lv_opa_t lv_span_get_style_text_opa(lv_obj_t * par, lv_span_t * span) +{ + lv_opa_t opa; + lv_style_value_t value; + lv_res_t res = lv_style_get_prop(&span->style, LV_STYLE_TEXT_OPA, &value); + if(res != LV_RES_OK) { + opa = (lv_opa_t)lv_obj_get_style_text_opa(par, LV_PART_MAIN); + } + else { + opa = (lv_opa_t)value.num; + } + return opa; +} + +static lv_blend_mode_t lv_span_get_style_text_blend_mode(lv_obj_t * par, lv_span_t * span) +{ + lv_blend_mode_t mode; + lv_style_value_t value; + lv_res_t res = lv_style_get_prop(&span->style, LV_STYLE_BLEND_MODE, &value); + if(res != LV_RES_OK) { + mode = (lv_blend_mode_t)lv_obj_get_style_blend_mode(par, LV_PART_MAIN); + } + else { + mode = (lv_blend_mode_t)value.num; + } + return mode; +} + +static int32_t lv_span_get_style_text_decor(lv_obj_t * par, lv_span_t * span) +{ + LV_UNUSED(par); + + int32_t decor; + lv_style_value_t value; + lv_res_t res = lv_style_get_prop(&span->style, LV_STYLE_TEXT_DECOR, &value); + if(res != LV_RES_OK) { + decor = LV_TEXT_DECOR_NONE; + } + else { + decor = (int32_t)value.num; + } + return decor; +} + +static inline void span_text_check(const char ** text) +{ + if(*text == NULL) { + *text = ""; + LV_LOG_ERROR("occur an error that span text == NULL"); + } +} + +static inline bool is_break_char(uint32_t letter) +{ + uint8_t i; + bool ret = false; + + /*Compare the letter to TXT_BREAK_CHARS*/ + for(i = 0; LV_TXT_BREAK_CHARS[i] != '\0'; i++) { + if(letter == (uint32_t)LV_TXT_BREAK_CHARS[i]) { + ret = true; /*If match then it is break char*/ + break; + } + } + + return ret; +} + +/** + * draw span group + * @param spans obj handle + * @param coords coordinates of the label + * @param mask the label will be drawn only in this area + */ +static void lv_draw_span(lv_obj_t * obj, const lv_area_t * coords, const lv_area_t * mask) +{ + /* return if no draw area */ + lv_area_t clipped_area; + bool clip_ok = _lv_area_intersect(&clipped_area, coords, mask); + if(!clip_ok) return; + + lv_spangroup_t * spans = (lv_spangroup_t *)obj; + + /* return if not span */ + if(_lv_ll_get_head(&spans->child_ll) == NULL) { + return; + } + + /* init draw variable */ + lv_text_flag_t txt_flag = LV_TEXT_FLAG_NONE; + lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN);; + lv_coord_t max_width = lv_area_get_width(coords); + lv_coord_t max_w = max_width - spans->indent; /* first line need minus indent */ + + /* coords of draw span-txt */ + lv_point_t txt_pos; + txt_pos.y = coords->y1; + txt_pos.x = coords->x1 + spans->indent; /* first line need add indent */ + + lv_span_t * cur_span = _lv_ll_get_head(&spans->child_ll); + const char * cur_txt = cur_span->txt; + span_text_check(&cur_txt); + uint32_t cur_txt_ofs = 0; + lv_snippet_t snippet; /* use to save cur_span info and push it to stack */ + memset(&snippet, 0, sizeof(snippet)); + + /* the loop control how many lines need to draw */ + while(cur_span) { + lv_coord_t max_line_h = 0; /* the max height of span-font when a line have a lot of span */ + lv_snippet_clear(); + + /* the loop control to find a line and push the relevant span info into stack */ + while(1) { + /* switch to the next span when current is end */ + if(cur_txt[cur_txt_ofs] == '\0') { + cur_span = _lv_ll_get_next(&spans->child_ll, cur_span); + if(cur_span == NULL) break; + cur_txt = cur_span->txt; + span_text_check(&cur_txt); + cur_txt_ofs = 0; + } + + /* init span info to snippet. */ + if(cur_txt_ofs == 0) { + snippet.span = cur_span; + snippet.font = lv_span_get_style_text_font(obj, cur_span); + snippet.letter_space = lv_span_get_style_text_letter_space(obj, cur_span); + snippet.line_h = lv_font_get_line_height(snippet.font) + line_space; + } + + if(spans->overflow == LV_SPAN_OVERFLOW_ELLIPSIS) { + /* span txt overflow, don't push */ + if(txt_pos.y + snippet.line_h - line_space > coords->y2 + 1) { + break; + } + } + + /* get current span text line info */ + uint32_t next_ofs = 0; + lv_coord_t use_width = 0; + bool isfill = lv_txt_get_snippet(&cur_txt[cur_txt_ofs], snippet.font, snippet.letter_space, + max_w, txt_flag, &use_width, &next_ofs); + + /* break word deal width */ + if(isfill && next_ofs > 0 && lv_get_snippet_cnt() > 0) { + uint32_t letter = (uint32_t)cur_txt[cur_txt_ofs + next_ofs - 1]; + if(!(letter == '\0' || letter == '\n' || letter == '\r' || is_break_char(letter))) { + letter = (uint32_t)cur_txt[cur_txt_ofs + next_ofs]; + if(!(letter == '\0' || letter == '\n' || letter == '\r' || is_break_char(letter))) { + break; + } + } + } + + snippet.txt = &cur_txt[cur_txt_ofs]; + snippet.bytes = next_ofs; + snippet.txt_w = use_width; + cur_txt_ofs += next_ofs; + if(max_line_h < snippet.line_h) { + max_line_h = snippet.line_h; + } + + lv_snippet_push(&snippet); + if(isfill) { + break; + } + max_w -= use_width; + } + + /* start current line deal width */ + + uint16_t item_cnt = lv_get_snippet_cnt(); + if(item_cnt == 0) { /* break if stack is empty */ + break; + } + + /*Go the first visible line*/ + if(txt_pos.y + max_line_h < mask->y1) { + goto Next_line_init; + } + + /* overflow deal width */ + bool ellipsis_valid = false; + if(spans->overflow == LV_SPAN_OVERFLOW_ELLIPSIS) { + lv_coord_t next_line_h = snippet.line_h; + if(cur_txt[cur_txt_ofs] == '\0') { /* current span deal with ok, need get next line first line height */ + next_line_h = 0; + if(cur_span) { + lv_span_t * next_span = _lv_ll_get_next(&spans->child_ll, cur_span); + if(next_span) { /* 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 > coords->y2 + 1) { + ellipsis_valid = true; + } + } + + /* align deal with */ + if(spans->align != LV_TEXT_ALIGN_LEFT) { + lv_coord_t align_ofs = 0; + lv_coord_t txts_w = 0; + for(int i = 0; i < item_cnt; i++) { + lv_snippet_t * pinfo = lv_get_snippet(i); + txts_w += pinfo->txt_w; + } + if(spans->align == LV_TEXT_ALIGN_CENTER) { + align_ofs = (max_width - txts_w) / 2; + } + else if(spans->align == LV_TEXT_ALIGN_RIGHT) { + align_ofs = max_width - txts_w; + } + txt_pos.x += align_ofs; + } + + /* draw line letters */ + for(int i = 0; i < item_cnt; i++) { + lv_snippet_t * pinfo = lv_get_snippet(i); + + /* bidi deal with:todo */ + const char * bidi_txt = pinfo->txt; + + lv_point_t pos; + pos.x = txt_pos.x; + pos.y = txt_pos.y + max_line_h - pinfo->line_h; + lv_color_t letter_color = lv_span_get_style_text_color(obj, pinfo->span); + lv_opa_t letter_opa = lv_span_get_style_text_opa(obj, pinfo->span); + lv_blend_mode_t blend_mode = lv_span_get_style_text_blend_mode(obj, pinfo->span); + uint32_t txt_bytes = pinfo->bytes; + + /* overflow */ + uint16_t dot_letter_w = 0; + uint16_t dot_width = 0; + if(ellipsis_valid) { + txt_bytes = strlen(bidi_txt); + dot_letter_w = lv_font_get_glyph_width(pinfo->font, '.', '.'); + dot_width = dot_letter_w * 3; + } + lv_coord_t ellipsis_width = coords->x1 + max_width - dot_width; + + uint32_t j = 0; + while(j < txt_bytes) { + /* skip invalid fields */ + if(pos.x > clipped_area.x2) { + break; + } + uint32_t letter = _lv_txt_encoded_next(bidi_txt, &j); + uint32_t letter_next = _lv_txt_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 < clipped_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_letter(&pos, &clipped_area, pinfo->font, '.', letter_color, letter_opa, blend_mode); + pos.x = pos.x + dot_letter_w + pinfo->letter_space; + } + break; + } + else { + lv_draw_letter(&pos, &clipped_area, pinfo->font, letter, letter_color, letter_opa, blend_mode); + if(letter_w > 0) { + pos.x = pos.x + letter_w + pinfo->letter_space; + } + } + } + + if(ellipsis_valid && i == item_cnt - 1 && pos.x <= ellipsis_width) { + for(int ell = 0; ell < 3; ell++) { + lv_draw_letter(&pos, &clipped_area, pinfo->font, '.', letter_color, letter_opa, blend_mode); + pos.x = pos.x + dot_letter_w + pinfo->letter_space; + } + } + + /* 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 = letter_color; + line_dsc.width = pinfo->font->underline_thickness ? pinfo->font->underline_thickness : 1; + line_dsc.opa = letter_opa; + line_dsc.blend_mode = blend_mode; + + if(decor & LV_TEXT_DECOR_STRIKETHROUGH) { + lv_point_t p1; + lv_point_t p2; + p1.x = txt_pos.x; + p1.y = pos.y + (pinfo->line_h / 2) + line_dsc.width / 2; + p2.x = pos.x; + p2.y = p1.y; + lv_draw_line(&p1, &p2, mask, &line_dsc); + } + + if(decor & LV_TEXT_DECOR_UNDERLINE) { + lv_point_t p1; + lv_point_t p2; + p1.x = txt_pos.x; + p1.y = pos.y + pinfo->line_h + line_dsc.width / 2; + p2.x = pos.x; + p2.y = p1.y; + lv_draw_line(&p1, &p2, &clipped_area, &line_dsc); + } + } + txt_pos.x = pos.x; + } + +Next_line_init: + /* next line init */ + txt_pos.x = coords->x1; + txt_pos.y += max_line_h; + if(txt_pos.y > clipped_area.y2 + 1) { + return; + } + max_w = max_width; + } +} + +#endif diff --git a/src/extra/widgets/span/lv_span.h b/src/extra/widgets/span/lv_span.h new file mode 100644 index 000000000..da25ae07d --- /dev/null +++ b/src/extra/widgets/span/lv_span.h @@ -0,0 +1,203 @@ +/** + * @file lv_span.h + * + */ + +#ifndef LV_SPAN_H +#define LV_SPAN_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../../../lvgl.h" + +#if LV_USE_SPAN != 0 + +/********************* + * DEFINES + *********************/ +#ifndef LV_SPAN_SNIPPET_STACK_SIZE +#define LV_SPAN_SNIPPET_STACK_SIZE 64 +#endif + +/********************** + * TYPEDEFS + **********************/ +enum { + LV_SPAN_OVERFLOW_CLIP, + LV_SPAN_OVERFLOW_ELLIPSIS, +}; +typedef uint8_t lv_span_overflow_t; + +enum { + LV_SPAN_MODE_FIXED, /**< fixed the obj size*/ + LV_SPAN_MODE_EXPAND, /**< Expand the object size to the text size*/ + LV_SPAN_MODE_BREAK, /**< Keep width, break the too long lines and expand height*/ +}; +typedef uint8_t lv_span_mode_t; + +typedef struct { + char * txt; + lv_style_t style; + uint8_t static_flag : 1; +} lv_span_t; + +/** Data of label*/ +typedef struct { + lv_obj_t obj; + lv_coord_t indent; + lv_ll_t child_ll; + uint8_t mode : 2; + uint8_t align : 2; + uint8_t overflow : 1; +} lv_spangroup_t; + +extern const lv_obj_class_t lv_spangroup_class; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a spangroup objects + * @param par pointer to an object, it will be the parent of the new spangroup + * @return pointer to the created spangroup + */ +lv_obj_t * lv_spangroup_create(lv_obj_t * par); + +/** + * Create a span string descriptor and add to spangroup. + * @param obj pointer to a spangroup object. + * @return pointer to the created span. + */ +lv_span_t * lv_span_create(lv_obj_t * obj); + +/** + * Remove the span from the spangroup and free memory. + * @param obj pointer to a spangroup object. + * @param span pointer to a span. + */ +void lv_span_del(lv_obj_t * obj, lv_span_t * span); + +/*===================== + * Setter functions + *====================*/ + +/** + * Set a new text for a span. Memory will be allocated to store the text by the span. + * @param span pointer to a span. + * @param text pointer to a text. + */ +void lv_span_set_text(lv_span_t * span, const char * text); + +/** + * Set a static text. It will not be saved by the span so the 'text' variable + * has to be 'alive' while the span exist. + * @param span pointer to a span. + * @param text pointer to a text. + */ +void lv_span_set_text_static(lv_span_t * span, const char * text); + +/** + * Set the align of the spangroup. + * @param obj pointer to a spangroup object. + * @param align see lv_text_align_t for details. + */ +void lv_span_set_align(lv_obj_t * obj, lv_text_align_t align); + +/** + * Set the overflow of the spangroup. + * @param obj pointer to a spangroup object. + * @param overflow see lv_span_overflow_t for details. + */ +void lv_span_set_overflow(lv_obj_t * obj, lv_span_overflow_t overflow); + +/** + * Set the indent of the spangroup. + * @param obj pointer to a spangroup object. + * @param indent The first line indentation + */ +void lv_span_set_indent(lv_obj_t * obj, lv_coord_t indent); + +/** + * Set the mode of the spangroup. + * @param obj pointer to a spangroup object. + * @param mode see lv_span_mode_t for details. + */ +void lv_span_set_mode(lv_obj_t * obj, lv_span_mode_t mode); + +/*===================== + * Getter functions + *====================*/ + +/** + * get the align of the spangroup. + * @param obj pointer to a spangroup object. + * @return the align value. + */ +lv_text_align_t lv_span_get_align(lv_obj_t * obj); + +/** + * get the overflow of the spangroup. + * @param obj pointer to a spangroup object. + * @return the overflow value. + */ +lv_span_overflow_t lv_span_get_overflow(lv_obj_t * obj); + +/** + * get the indent of the spangroup. + * @param obj pointer to a spangroup object. + * @return the indent value. + */ +lv_coord_t lv_span_get_indent(lv_obj_t * obj); + +/** + * get the mode of the spangroup. + * @param obj pointer to a spangroup object. + */ +lv_span_mode_t lv_span_get_mode(lv_obj_t * obj); + +/** + * get max line height of all span in the spangroup. + * @param obj pointer to a spangroup object. + */ +lv_coord_t lv_span_get_max_line_h(lv_obj_t * obj); + +/** + * get the width when all span of spangroup on a line. include spangroup pad. + * @param obj pointer to a spangroup object. + */ +lv_coord_t lv_span_get_expand_width(lv_obj_t * obj); + +/** + * get the height with width fixed. the height include spangroup pad. + * @param obj pointer to a spangroup object. + */ +lv_coord_t lv_span_get_expand_height(lv_obj_t * obj, lv_coord_t width); + + +/*===================== + * Other functions + *====================*/ + +/** + * update the mode of the spangroup. + * @param obj pointer to a spangroup object. + */ +void lv_span_refr_mode(lv_obj_t * obj); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_SPAN*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_SPAN_H*/ diff --git a/src/lv_conf_internal.h b/src/lv_conf_internal.h index 5fb67c72a..6b1b9a24a 100644 --- a/src/lv_conf_internal.h +++ b/src/lv_conf_internal.h @@ -1393,6 +1393,14 @@ e.g. "stm32f769xx.h" or "stm32f429xx.h"*/ # endif #endif +#ifndef LV_USE_SPAN +# ifdef CONFIG_LV_USE_SPAN +# define LV_USE_SPAN CONFIG_LV_USE_SPAN +# else +# define LV_USE_SPAN 1 +# endif +#endif + /*----------- * Themes *----------*/