From 6a1ecfcf344c67de832db29d0960d355989f2fc2 Mon Sep 17 00:00:00 2001 From: Gabor Kiss-Vamosi Date: Wed, 8 Jan 2025 17:25:06 +0100 Subject: [PATCH] feat(xml): add support for more properties and add more examples (#7417) --- examples/others/xml/index.rst | 3 + examples/others/xml/lv_example_xml.h | 1 + examples/others/xml/lv_example_xml_1.c | 76 ++++--- examples/others/xml/lv_example_xml_2.c | 43 ++++ src/misc/lv_style.h | 12 ++ src/others/xml/lv_xml.c | 139 ++++++++++-- src/others/xml/lv_xml.h | 6 +- src/others/xml/lv_xml_base_types.c | 104 ++++++++- src/others/xml/lv_xml_base_types.h | 64 +++++- src/others/xml/lv_xml_component.c | 21 +- src/others/xml/lv_xml_style.c | 164 ++++++++++++-- src/others/xml/lv_xml_style.h | 4 +- src/others/xml/lv_xml_utils.c | 69 +++--- src/others/xml/lv_xml_utils.h | 17 ++ src/others/xml/parsers/lv_xml_chart_parser.c | 97 +++++++-- src/others/xml/parsers/lv_xml_chart_parser.h | 2 + .../xml/parsers/lv_xml_dropdown_parser.c | 84 ++++++++ .../xml/parsers/lv_xml_dropdown_parser.h | 41 ++++ src/others/xml/parsers/lv_xml_image_parser.c | 69 +++--- src/others/xml/parsers/lv_xml_obj_parser.c | 202 ++++++++++++++++-- src/others/xml/parsers/lv_xml_obj_parser.h | 1 - src/others/xml/parsers/lv_xml_table_parser.c | 149 +++++++++++++ src/others/xml/parsers/lv_xml_table_parser.h | 43 ++++ .../xml/parsers/lv_xml_tabview_parser.c | 21 +- src/widgets/table/lv_table.c | 5 +- src/widgets/table/lv_table.h | 1 + src/widgets/tabview/lv_tabview.c | 19 +- tests/ref_imgs/widgets/table_1.png | Bin 10592 -> 10331 bytes tests/ref_imgs/xml/lv_chart.png | Bin 0 -> 3446 bytes tests/ref_imgs/xml/lv_dropdown.png | Bin 0 -> 6136 bytes tests/ref_imgs/xml/lv_image.png | Bin 0 -> 16151 bytes tests/ref_imgs/xml/lv_table.png | Bin 0 -> 5889 bytes tests/ref_imgs/xml/lv_tabview.png | Bin 0 -> 5845 bytes tests/ref_imgs_vg_lite/widgets/table_1.png | Bin 10437 -> 10184 bytes tests/ref_imgs_vg_lite/xml/lv_chart.png | Bin 0 -> 3392 bytes tests/ref_imgs_vg_lite/xml/lv_dropdown.png | Bin 0 -> 6287 bytes tests/ref_imgs_vg_lite/xml/lv_image.png | Bin 0 -> 16606 bytes tests/ref_imgs_vg_lite/xml/lv_table.png | Bin 0 -> 5691 bytes tests/ref_imgs_vg_lite/xml/lv_tabview.png | Bin 0 -> 5759 bytes tests/src/test_cases/xml/test_xml_chart.c | 71 ++++++ tests/src/test_cases/xml/test_xml_dropdown.c | 43 ++++ tests/src/test_cases/xml/test_xml_image.c | 53 +++++ tests/src/test_cases/xml/test_xml_table.c | 69 ++++++ tests/src/test_cases/xml/test_xml_tabview.c | 72 +++++++ xmls/globals.xml | 171 ++++++++++++++- xmls/lv_chart.xml | 4 +- xmls/lv_dropdown.xml | 16 +- xmls/lv_image.xml | 35 +++ xmls/lv_obj.xml | 141 ++++++------ xmls/lv_table.xml | 13 +- xmls/lv_tabview.xml | 10 +- 51 files changed, 1826 insertions(+), 329 deletions(-) create mode 100644 examples/others/xml/lv_example_xml_2.c create mode 100644 src/others/xml/parsers/lv_xml_dropdown_parser.c create mode 100644 src/others/xml/parsers/lv_xml_dropdown_parser.h create mode 100644 src/others/xml/parsers/lv_xml_table_parser.c create mode 100644 src/others/xml/parsers/lv_xml_table_parser.h create mode 100644 tests/ref_imgs/xml/lv_chart.png create mode 100644 tests/ref_imgs/xml/lv_dropdown.png create mode 100644 tests/ref_imgs/xml/lv_image.png create mode 100644 tests/ref_imgs/xml/lv_table.png create mode 100644 tests/ref_imgs/xml/lv_tabview.png create mode 100644 tests/ref_imgs_vg_lite/xml/lv_chart.png create mode 100644 tests/ref_imgs_vg_lite/xml/lv_dropdown.png create mode 100644 tests/ref_imgs_vg_lite/xml/lv_image.png create mode 100644 tests/ref_imgs_vg_lite/xml/lv_table.png create mode 100644 tests/ref_imgs_vg_lite/xml/lv_tabview.png create mode 100644 tests/src/test_cases/xml/test_xml_chart.c create mode 100644 tests/src/test_cases/xml/test_xml_dropdown.c create mode 100644 tests/src/test_cases/xml/test_xml_image.c create mode 100644 tests/src/test_cases/xml/test_xml_table.c create mode 100644 tests/src/test_cases/xml/test_xml_tabview.c create mode 100644 xmls/lv_image.xml diff --git a/examples/others/xml/index.rst b/examples/others/xml/index.rst index 73e9e738a..a5c8cd8b8 100644 --- a/examples/others/xml/index.rst +++ b/examples/others/xml/index.rst @@ -3,3 +3,6 @@ Load components at runtime .. lv_example:: others/xml/lv_example_xml_1 :language: c + +.. lv_example:: others/xml/lv_example_xml_2 + :language: c diff --git a/examples/others/xml/lv_example_xml.h b/examples/others/xml/lv_example_xml.h index 341144881..a37cd2159 100644 --- a/examples/others/xml/lv_example_xml.h +++ b/examples/others/xml/lv_example_xml.h @@ -26,6 +26,7 @@ extern "C" { * GLOBAL PROTOTYPES **********************/ void lv_example_xml_1(void); +void lv_example_xml_2(void); /********************** * MACROS diff --git a/examples/others/xml/lv_example_xml_1.c b/examples/others/xml/lv_example_xml_1.c index 6b1dcd894..d0e78fc44 100644 --- a/examples/others/xml/lv_example_xml_1.c +++ b/examples/others/xml/lv_example_xml_1.c @@ -3,35 +3,61 @@ void lv_example_xml_1(void) { - lv_xml_component_register_from_file("A:lvgl/examples/others/xml/my_h3.xml"); - lv_xml_component_register_from_file("A:lvgl/examples/others/xml/my_card.xml"); - lv_xml_component_register_from_file("A:lvgl/examples/others/xml/my_button.xml"); - lv_xml_component_register_from_file("A:lvgl/examples/others/xml/view.xml"); + /*A red button created from builti-in LVGL widgets + *It has an API parameter too to change its text.*/ + const char * red_button_xml = + "" + " " + " " + " " + " " + " " + " " + ""; - lv_obj_t * obj = lv_xml_create(lv_screen_active(), "view", NULL); - lv_obj_set_pos(obj, 10, 10); + /*The card is just an lv_obj where a label and two red buttons are used. + * Its API allow setting a title (label test) and the action (the text of a button)*/ + const char * card_xml = + "" + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""; - const char * my_button_attrs[] = { - "x", "10", - "y", "-10", - "align", "bottom_left", - "btn_text", "New button", + /* Motor card is a special case of a card where the title and action are already set*/ + const char * motor_card_xml = + "" + " " + " " + ""; + + /*Register all the custom components*/ + lv_xml_component_register_from_data("red_button", red_button_xml); + lv_xml_component_register_from_data("card", card_xml); + lv_xml_component_register_from_data("motor_card", motor_card_xml); + + lv_obj_t * card; + /*Create a card with the default values*/ + card = lv_xml_create(lv_screen_active(), "card", NULL); + + /*Create a motor card too. The returned value can be adjusted freely*/ + card = lv_xml_create(lv_screen_active(), "motor_card", NULL); + lv_obj_set_y(card, 90); + + /*Pass properties to a card*/ + const char * attrs[] = { + "y", "180", + "action", "Apply", + "title", "New title", NULL, NULL, }; + card = lv_xml_create(lv_screen_active(), "card", attrs); - lv_xml_component_unregister("my_button"); - - lv_xml_create(lv_screen_active(), "my_button", my_button_attrs); - - const char * slider_attrs[] = { - "x", "200", - "y", "-15", - "align", "bottom_left", - "value", "30", - NULL, NULL, - }; - - lv_obj_t * slider = lv_xml_create(lv_screen_active(), "lv_slider", slider_attrs); - lv_obj_set_width(slider, 100); } #endif diff --git a/examples/others/xml/lv_example_xml_2.c b/examples/others/xml/lv_example_xml_2.c new file mode 100644 index 000000000..d3e702010 --- /dev/null +++ b/examples/others/xml/lv_example_xml_2.c @@ -0,0 +1,43 @@ +#include "../../lv_examples.h" +#if LV_BUILD_EXAMPLES && LV_USE_XML + +void lv_example_xml_2(void) +{ + lv_result_t res; + res = lv_xml_component_register_from_file("A:lvgl/examples/others/xml/my_h3.xml"); + if(res != LV_RESULT_OK) { + lv_obj_t * label = lv_label_create(lv_screen_active()); + lv_label_set_text(label, "Couldn't open the XML files."); + lv_obj_center(label); + } + lv_xml_component_register_from_file("A:lvgl/examples/others/xml/my_card.xml"); + lv_xml_component_register_from_file("A:lvgl/examples/others/xml/my_button.xml"); + lv_xml_component_register_from_file("A:lvgl/examples/others/xml/view.xml"); + + lv_obj_t * obj = lv_xml_create(lv_screen_active(), "view", NULL); + lv_obj_set_pos(obj, 10, 10); + + const char * my_button_attrs[] = { + "x", "10", + "y", "-10", + "align", "bottom_left", + "btn_text", "New button", + NULL, NULL, + }; + + lv_xml_component_unregister("my_button"); + + lv_xml_create(lv_screen_active(), "my_button", my_button_attrs); + + const char * slider_attrs[] = { + "x", "200", + "y", "-15", + "align", "bottom_left", + "value", "30", + NULL, NULL, + }; + + lv_obj_t * slider = lv_xml_create(lv_screen_active(), "lv_slider", slider_attrs); + lv_obj_set_width(slider, 100); +} +#endif diff --git a/src/misc/lv_style.h b/src/misc/lv_style.h index 60ae0f163..89409a041 100644 --- a/src/misc/lv_style.h +++ b/src/misc/lv_style.h @@ -587,6 +587,18 @@ static inline void lv_style_set_pad_gap(lv_style_t * style, int32_t value) lv_style_set_pad_column(style, value); } +static inline void lv_style_set_margin_hor(lv_style_t * style, int32_t value) +{ + lv_style_set_margin_left(style, value); + lv_style_set_margin_right(style, value); +} + +static inline void lv_style_set_margin_ver(lv_style_t * style, int32_t value) +{ + lv_style_set_margin_top(style, value); + lv_style_set_margin_bottom(style, value); +} + static inline void lv_style_set_margin_all(lv_style_t * style, int32_t value) { lv_style_set_margin_left(style, value); diff --git a/src/others/xml/lv_xml.c b/src/others/xml/lv_xml.c index d937c548a..1fcc70c1c 100644 --- a/src/others/xml/lv_xml.c +++ b/src/others/xml/lv_xml.c @@ -26,6 +26,8 @@ #include "parsers/lv_xml_slider_parser.h" #include "parsers/lv_xml_tabview_parser.h" #include "parsers/lv_xml_chart_parser.h" +#include "parsers/lv_xml_table_parser.h" +#include "parsers/lv_xml_dropdown_parser.h" #include "../../libs/expat/expat.h" #include "../../draw/lv_draw_image.h" @@ -42,6 +44,7 @@ **********************/ static void view_start_element_handler(void * user_data, const char * name, const char ** attrs); static void view_end_element_handler(void * user_data, const char * name); +static void register_builtin_fonts(void); /********************** * STATIC VARIABLES @@ -62,24 +65,10 @@ void lv_xml_init(void) lv_ll_init(&font_ll, sizeof(lv_xml_font_t)); lv_ll_init(&image_ll, sizeof(lv_xml_image_t)); -#if LV_FONT_MONTSERRAT_14 - lv_xml_register_font("lv_montserrat_14", &lv_font_montserrat_14); -#endif - -#if LV_FONT_MONTSERRAT_16 - lv_xml_register_font("lv_montserrat_16", &lv_font_montserrat_16); -#endif - -#if LV_FONT_MONTSERRAT_18 - lv_xml_register_font("lv_montserrat_18", &lv_font_montserrat_18); -#endif - -#if LV_FONT_MONTSERRAT_20 - lv_xml_register_font("lv_montserrat_20", &lv_font_montserrat_20); -#endif - lv_xml_component_init(); + register_builtin_fonts(); + lv_xml_widget_register("lv_obj", lv_xml_obj_create, lv_xml_obj_apply); lv_xml_widget_register("lv_button", lv_xml_button_create, lv_xml_button_apply); lv_xml_widget_register("lv_label", lv_xml_label_create, lv_xml_label_apply); @@ -91,10 +80,16 @@ void lv_xml_init(void) lv_xml_widget_register("lv_chart", lv_xml_chart_create, lv_xml_chart_apply); lv_xml_widget_register("lv_chart-cursor", lv_xml_chart_cursor_create, lv_xml_chart_cursor_apply); lv_xml_widget_register("lv_chart-series", lv_xml_chart_series_create, lv_xml_chart_series_apply); + lv_xml_widget_register("lv_chart-axis", lv_xml_chart_axis_create, lv_xml_chart_axis_apply); + lv_xml_widget_register("lv_table", lv_xml_table_create, lv_xml_table_apply); + lv_xml_widget_register("lv_table-column", lv_xml_table_column_create, lv_xml_table_column_apply); + lv_xml_widget_register("lv_table-cell", lv_xml_table_cell_create, lv_xml_table_cell_apply); + lv_xml_widget_register("lv_dropdown", lv_xml_dropdown_create, lv_xml_dropdown_apply); + lv_xml_widget_register("lv_dropdown-list", lv_xml_dropdown_list_create, lv_xml_dropdown_list_apply); } -lv_obj_t * lv_xml_create_from_ctx(lv_obj_t * parent, lv_xml_component_ctx_t * parent_ctx, lv_xml_component_ctx_t * ctx, - const char ** attrs) +void * lv_xml_create_from_ctx(lv_obj_t * parent, lv_xml_component_ctx_t * parent_ctx, lv_xml_component_ctx_t * ctx, + const char ** attrs) { /* Initialize the parser state */ lv_xml_parser_state_t state; @@ -132,7 +127,7 @@ lv_obj_t * lv_xml_create_from_ctx(lv_obj_t * parent, lv_xml_component_ctx_t * pa return state.view; } -lv_obj_t * lv_xml_create(lv_obj_t * parent, const char * name, const char ** attrs) +void * lv_xml_create(lv_obj_t * parent, const char * name, const char ** attrs) { lv_obj_t * item = NULL; @@ -345,7 +340,7 @@ static void view_start_element_handler(void * user_data, const char * name, cons *now it has the button theme styles. However if it were a real widget *it had e.g. `my_widget_class` so the button's theme wouldn't apply on it. *Removing the style will ensure a better preview*/ - if(state->ctx.is_widget) lv_obj_remove_style_all(item); + if(state->ctx.is_widget && is_view) lv_obj_remove_style_all(item); /*Apply the attributes from e.g. ``*/ if(item) { @@ -386,4 +381,108 @@ static void view_end_element_handler(void * user_data, const char * name) } } + +static void register_builtin_fonts(void) +{ +#if LV_FONT_MONTSERRAT_8 + lv_xml_register_font("lv_montserrat_8", &lv_font_montserrat_8); +#endif + +#if LV_FONT_MONTSERRAT_10 + lv_xml_register_font("lv_montserrat_10", &lv_font_montserrat_10); +#endif + +#if LV_FONT_MONTSERRAT_12 + lv_xml_register_font("lv_montserrat_12", &lv_font_montserrat_12); +#endif + +#if LV_FONT_MONTSERRAT_14 + lv_xml_register_font("lv_montserrat_14", &lv_font_montserrat_14); +#endif + +#if LV_FONT_MONTSERRAT_16 + lv_xml_register_font("lv_montserrat_16", &lv_font_montserrat_16); +#endif + +#if LV_FONT_MONTSERRAT_18 + lv_xml_register_font("lv_montserrat_18", &lv_font_montserrat_18); +#endif + +#if LV_FONT_MONTSERRAT_20 + lv_xml_register_font("lv_montserrat_20", &lv_font_montserrat_20); +#endif + +#if LV_FONT_MONTSERRAT_22 + lv_xml_register_font("lv_montserrat_22", &lv_font_montserrat_22); +#endif + +#if LV_FONT_MONTSERRAT_24 + lv_xml_register_font("lv_montserrat_24", &lv_font_montserrat_24); +#endif + +#if LV_FONT_MONTSERRAT_26 + lv_xml_register_font("lv_montserrat_26", &lv_font_montserrat_26); +#endif + +#if LV_FONT_MONTSERRAT_28 + lv_xml_register_font("lv_montserrat_28", &lv_font_montserrat_28); +#endif + +#if LV_FONT_MONTSERRAT_30 + lv_xml_register_font("lv_montserrat_30", &lv_font_montserrat_30); +#endif + +#if LV_FONT_MONTSERRAT_32 + lv_xml_register_font("lv_montserrat_32", &lv_font_montserrat_32); +#endif + +#if LV_FONT_MONTSERRAT_34 + lv_xml_register_font("lv_montserrat_34", &lv_font_montserrat_34); +#endif + +#if LV_FONT_MONTSERRAT_36 + lv_xml_register_font("lv_montserrat_36", &lv_font_montserrat_36); +#endif + +#if LV_FONT_MONTSERRAT_38 + lv_xml_register_font("lv_montserrat_38", &lv_font_montserrat_38); +#endif + +#if LV_FONT_MONTSERRAT_40 + lv_xml_register_font("lv_montserrat_40", &lv_font_montserrat_40); +#endif + +#if LV_FONT_MONTSERRAT_42 + lv_xml_register_font("lv_montserrat_42", &lv_font_montserrat_42); +#endif + +#if LV_FONT_MONTSERRAT_44 + lv_xml_register_font("lv_montserrat_44", &lv_font_montserrat_44); +#endif + +#if LV_FONT_MONTSERRAT_46 + lv_xml_register_font("lv_montserrat_46", &lv_font_montserrat_46); +#endif + +#if LV_FONT_MONTSERRAT_48 + lv_xml_register_font("lv_montserrat_48", &lv_font_montserrat_48); +#endif + +#if LV_FONT_UNSCII_8 + lv_xml_register_font("lv_unscii_8", &lv_font_unscii_8); +#endif + +#if LV_FONT_UNSCII_16 + lv_xml_register_font("lv_unscii_16", &lv_font_unscii_16); +#endif + +#if LV_FONT_SIMSUN_16_CJK + lv_xml_register_font("lv_simsun_cjk_16", &lv_font_simsun_16_cjk); +#endif + +#if LV_FONT_DEJAVU_16_PERSIAN_HEBREW + lv_xml_register_font("lv_font_dejavu_16_persian_hebrew", &lv_font_dejavu_16_persian_hebrew); +#endif +} + #endif /* LV_USE_XML */ diff --git a/src/others/xml/lv_xml.h b/src/others/xml/lv_xml.h index 1b5646097..1a0948d9b 100644 --- a/src/others/xml/lv_xml.h +++ b/src/others/xml/lv_xml.h @@ -30,10 +30,10 @@ extern "C" { void lv_xml_init(void); -lv_obj_t * lv_xml_create(lv_obj_t * parent, const char * name, const char ** attrs); +void * lv_xml_create(lv_obj_t * parent, const char * name, const char ** attrs); -lv_obj_t * lv_xml_create_from_ctx(lv_obj_t * parent, lv_xml_component_ctx_t * parent_ctx, lv_xml_component_ctx_t * ctx, - const char ** attrs); +void * lv_xml_create_from_ctx(lv_obj_t * parent, lv_xml_component_ctx_t * parent_ctx, lv_xml_component_ctx_t * ctx, + const char ** attrs); lv_result_t lv_xml_register_font(const char * name, const lv_font_t * font); diff --git a/src/others/xml/lv_xml_base_types.c b/src/others/xml/lv_xml_base_types.c index b1aa834af..ef9ec3aa4 100644 --- a/src/others/xml/lv_xml_base_types.c +++ b/src/others/xml/lv_xml_base_types.c @@ -48,11 +48,11 @@ int32_t lv_xml_to_size(const char * txt) else return v; } -lv_align_t lv_xml_align_string_to_enum_value(const char * txt) +lv_align_t lv_xml_align_to_enum(const char * txt) { if(lv_streq("top_left", txt)) return LV_ALIGN_TOP_LEFT; if(lv_streq("top_mid", txt)) return LV_ALIGN_TOP_MID; - if(lv_streq("top_right", txt)) return LV_ALIGN_TOP_LEFT; + if(lv_streq("top_right", txt)) return LV_ALIGN_TOP_RIGHT; if(lv_streq("bottom_left", txt)) return LV_ALIGN_BOTTOM_LEFT; if(lv_streq("bottom_mid", txt)) return LV_ALIGN_BOTTOM_MID; if(lv_streq("bottom_right", txt)) return LV_ALIGN_BOTTOM_RIGHT; @@ -64,8 +64,9 @@ lv_align_t lv_xml_align_string_to_enum_value(const char * txt) return 0; /*Return 0 in lack of a better option. */ } -lv_dir_t lv_xml_dir_string_to_enum_value(const char * txt) +lv_dir_t lv_xml_dir_to_enum(const char * txt) { + if(lv_streq("none", txt)) return LV_DIR_NONE; if(lv_streq("top", txt)) return LV_DIR_TOP; if(lv_streq("bottom", txt)) return LV_DIR_BOTTOM; if(lv_streq("left", txt)) return LV_DIR_LEFT; @@ -76,7 +77,61 @@ lv_dir_t lv_xml_dir_string_to_enum_value(const char * txt) return 0; /*Return 0 in lack of a better option. */ } -lv_flex_flow_t lv_xml_flex_flow_string_to_enum_value(const char * txt) +lv_border_side_t lv_xml_border_side_to_enum(const char * txt) +{ + if(lv_streq("none", txt)) return LV_BORDER_SIDE_NONE; + if(lv_streq("top", txt)) return LV_BORDER_SIDE_TOP; + if(lv_streq("bottom", txt)) return LV_BORDER_SIDE_BOTTOM; + if(lv_streq("left", txt)) return LV_BORDER_SIDE_LEFT; + if(lv_streq("right", txt)) return LV_BORDER_SIDE_RIGHT; + if(lv_streq("full", txt)) return LV_BORDER_SIDE_FULL; + + LV_LOG_WARN("%s is an unknown value for border_side", txt); + return 0; /*Return 0 in lack of a better option. */ +} + +lv_grad_dir_t lv_xml_grad_dir_to_enum(const char * txt) +{ + if(lv_streq("none", txt)) return LV_GRAD_DIR_NONE; + if(lv_streq("hor", txt)) return LV_GRAD_DIR_HOR; + if(lv_streq("ver", txt)) return LV_GRAD_DIR_VER; + + LV_LOG_WARN("%s is an unknown value for grad_dir", txt); + return 0; /*Return 0 in lack of a better option. */ +} + +lv_base_dir_t lv_xml_base_dir_to_enum(const char * txt) +{ + if(lv_streq("auto", txt)) return LV_BASE_DIR_AUTO; + if(lv_streq("ltr", txt)) return LV_BASE_DIR_LTR; + if(lv_streq("rtl", txt)) return LV_BASE_DIR_RTL; + + LV_LOG_WARN("%s is an unknown value for base_dir", txt); + return 0; /*Return 0 in lack of a better option. */ +} + +lv_text_align_t lv_xml_text_align_to_enum(const char * txt) +{ + if(lv_streq("left", txt)) return LV_TEXT_ALIGN_LEFT; + if(lv_streq("right", txt)) return LV_TEXT_ALIGN_RIGHT; + if(lv_streq("center", txt)) return LV_TEXT_ALIGN_CENTER; + if(lv_streq("auto", txt)) return LV_TEXT_ALIGN_AUTO; + + LV_LOG_WARN("%s is an unknown value for text_align", txt); + return 0; /*Return 0 in lack of a better option. */ +} + +lv_text_decor_t lv_xml_text_decor_to_enum(const char * txt) +{ + if(lv_streq("none", txt)) return LV_TEXT_DECOR_NONE; + if(lv_streq("underline", txt)) return LV_TEXT_DECOR_UNDERLINE; + if(lv_streq("strikethrough", txt)) return LV_TEXT_DECOR_STRIKETHROUGH; + + LV_LOG_WARN("%s is an unknown value for text_decor", txt); + return 0; /*Return 0 in lack of a better option. */ +} + +lv_flex_flow_t lv_xml_flex_flow_to_enum(const char * txt) { if(lv_streq("column", txt)) return LV_FLEX_FLOW_COLUMN; if(lv_streq("column_reverse", txt)) return LV_FLEX_FLOW_COLUMN_REVERSE; @@ -87,11 +142,11 @@ lv_flex_flow_t lv_xml_flex_flow_string_to_enum_value(const char * txt) if(lv_streq("row_wrap", txt)) return LV_FLEX_FLOW_ROW_WRAP; if(lv_streq("row_wrap_reverse", txt)) return LV_FLEX_FLOW_ROW_WRAP_REVERSE; - LV_LOG_WARN("%s is an unknown value for flex flow", txt); + LV_LOG_WARN("%s is an unknown value for flex_flow", txt); return 0; /*Return 0 in lack of a better option. */ } -lv_flex_align_t lv_xml_flex_align_string_to_enum_value(const char * txt) +lv_flex_align_t lv_xml_flex_align_to_enum(const char * txt) { if(lv_streq("center", txt)) return LV_FLEX_ALIGN_CENTER; if(lv_streq("end", txt)) return LV_FLEX_ALIGN_END; @@ -100,10 +155,45 @@ lv_flex_align_t lv_xml_flex_align_string_to_enum_value(const char * txt) if(lv_streq("space_between", txt)) return LV_FLEX_ALIGN_SPACE_BETWEEN; if(lv_streq("space_evenly", txt)) return LV_FLEX_ALIGN_SPACE_EVENLY; - LV_LOG_WARN("%s is an unknown value for flex align", txt); + LV_LOG_WARN("%s is an unknown value for flex_align", txt); return 0; /*Return 0 in lack of a better option. */ } +lv_grid_align_t lv_xml_grid_align_to_enum(const char * txt) +{ + if(lv_streq("center", txt)) return LV_GRID_ALIGN_CENTER; + if(lv_streq("end", txt)) return LV_GRID_ALIGN_END; + if(lv_streq("start", txt)) return LV_GRID_ALIGN_START; + if(lv_streq("stretch", txt)) return LV_GRID_ALIGN_STRETCH; + if(lv_streq("space_around", txt)) return LV_GRID_ALIGN_SPACE_AROUND; + if(lv_streq("space_between", txt)) return LV_GRID_ALIGN_SPACE_BETWEEN; + if(lv_streq("space_evenly", txt)) return LV_GRID_ALIGN_SPACE_EVENLY; + + LV_LOG_WARN("%s is an unknown value for grid_align", txt); + return 0; /*Return 0 in lack of a better option. */ +} + + +lv_layout_t lv_xml_layout_to_enum(const char * txt) +{ + if(lv_streq("none", txt)) return LV_LAYOUT_NONE; + if(lv_streq("flex", txt)) return LV_LAYOUT_FLEX; + if(lv_streq("grid", txt)) return LV_LAYOUT_GRID; + + LV_LOG_WARN("%s is an unknown value for layout", txt); + return 0; /*Return 0 in lack of a better option. */ +} + +lv_blend_mode_t lv_xml_blend_mode_to_enum(const char * txt) +{ + if(lv_streq("normal", txt)) return LV_BLEND_MODE_NORMAL; + if(lv_streq("additive", txt)) return LV_BLEND_MODE_ADDITIVE; + if(lv_streq("subtractive", txt)) return LV_BLEND_MODE_SUBTRACTIVE; + if(lv_streq("multiply", txt)) return LV_BLEND_MODE_MULTIPLY; + + LV_LOG_WARN("%s is an unknown value for blend_mode", txt); + return 0; /*Return 0 in lack of a better option. */ +} /********************** * STATIC FUNCTIONS **********************/ diff --git a/src/others/xml/lv_xml_base_types.h b/src/others/xml/lv_xml_base_types.h index bd8bd6863..87936c170 100644 --- a/src/others/xml/lv_xml_base_types.h +++ b/src/others/xml/lv_xml_base_types.h @@ -39,29 +39,85 @@ int32_t lv_xml_to_size(const char * txt); * @param txt e.g. "center" * @return the related enum, e.g. `LV_ALIGN_CENTER` */ -lv_align_t lv_xml_align_string_to_enum_value(const char * txt); +lv_align_t lv_xml_align_to_enum(const char * txt); /** * Convert a direction string to enum * @param txt e.g. "top" * @return the related enum, e.g. `LV_DIR_TOP` */ -lv_dir_t lv_xml_dir_string_to_enum_value(const char * txt); +lv_dir_t lv_xml_dir_to_enum(const char * txt); +/** + * Convert a direction string to enum + * @param txt e.g. "top" + * @return the related enum, e.g. `LV_BORDER_SIDE_TOP` + */ +lv_border_side_t lv_xml_border_side_to_enum(const char * txt); + +/** + * Convert a base dir string to enum + * @param txt e.g. "rtl" + * @return the related enum, e.g. `LV_BASE_DIR_RTL` + */ +lv_base_dir_t lv_xml_base_dir_to_enum(const char * txt); + +/** + * Convert a grad dir string to enum + * @param txt e.g. "hor" + * @return the related enum, e.g. `LV_GRAD_DIR_HOR` + */ +lv_grad_dir_t lv_xml_grad_dir_to_enum(const char * txt); + + +/** + * Convert a text align string to enum + * @param txt e.g. "left" + * @return the related enum, e.g. `LV_TEXT_ALIGN_LEFT` + */ +lv_text_align_t lv_xml_text_align_to_enum(const char * txt); + +/** + * Convert a text decor string to enum + * @param txt e.g. "underline" + * @return the related enum, e.g. `LV_TEXT_DECOR_UNDERLINE` + */ +lv_text_decor_t lv_xml_text_decor_to_enum(const char * txt); /** * Convert a flex flow string to enum * @param txt e.g. "row_wrap" * @return the related enum, e.g. `LV_FLEX_FLOW_ROW_WRAP` */ -lv_flex_flow_t lv_xml_flex_flow_string_to_enum_value(const char * txt); +lv_flex_flow_t lv_xml_flex_flow_to_enum(const char * txt); /** * Convert a flex align string to enum * @param txt e.g. "space_between" * @return the related enum, e.g. `LV_FLEX_ALIGN_SPACE_BETWEEN` */ -lv_flex_align_t lv_xml_flex_align_string_to_enum_value(const char * txt); +lv_flex_align_t lv_xml_flex_align_to_enum(const char * txt); + +/** + * Convert a grid align string to enum + * @param txt e.g. "space_between" + * @return the related enum, e.g. `LV_GRID_ALIGN_SPACE_BETWEEN` + */ +lv_grid_align_t lv_xml_grid_align_to_enum(const char * txt); + +/** + * Convert a layout string to enum + * @param txt e.g. "flex" + * @return the related enum, e.g. `LV_LAYOUT_FLEX` + */ +lv_layout_t lv_xml_layout_to_enum(const char * txt); + +/** + * Convert a blend mode string to enum + * @param txt e.g. "additive" + * @return the related enum, e.g. `LV_BLEND_MODE_ADDITIVE` + */ +lv_blend_mode_t lv_xml_blend_mode_to_enum(const char * txt); /********************** * MACROS diff --git a/src/others/xml/lv_xml_component.c b/src/others/xml/lv_xml_component.c index 439fdd8a2..da88f20e7 100644 --- a/src/others/xml/lv_xml_component.c +++ b/src/others/xml/lv_xml_component.c @@ -224,15 +224,15 @@ static void process_const_element(lv_xml_parser_state_t * state, const char ** a static void process_prop_element(lv_xml_parser_state_t * state, const char ** attrs) { - lv_xml_param_t * cnst = lv_ll_ins_tail(&state->ctx.param_ll); - cnst->name = lv_strdup(lv_xml_get_value_of(attrs, "name")); + lv_xml_param_t * prop = lv_ll_ins_tail(&state->ctx.param_ll); + prop->name = lv_strdup(lv_xml_get_value_of(attrs, "name")); const char * def = lv_xml_get_value_of(attrs, "default"); - if(def) cnst->def = lv_strdup(def); - else cnst->def = NULL; + if(def) prop->def = lv_strdup(def); + else prop->def = NULL; const char * type = lv_xml_get_value_of(attrs, "type"); if(type == NULL) type = "compound"; /*If there in no type it means there are s*/ - cnst->type = lv_strdup(lv_xml_get_value_of(attrs, "type")); + prop->type = lv_strdup(type); } static void start_metadata_handler(void * user_data, const char * name, const char ** attrs) @@ -244,7 +244,18 @@ static void start_metadata_handler(void * user_data, const char * name, const ch if(lv_streq(name, "view")) { const char * extends = lv_xml_get_value_of(attrs, "extends"); if(extends == NULL) extends = "lv_obj"; + state->ctx.root_widget = lv_xml_widget_get_processor(extends); + if(state->ctx.root_widget == NULL) { + lv_xml_component_ctx_t * extended_component = lv_xml_component_get_ctx(extends); + if(extended_component) { + state->ctx.root_widget = extended_component->root_widget; + } + else { + LV_LOG_WARN("The 'extend'ed widget is not found, using `lv_obj` as a fall back"); + state->ctx.root_widget = lv_xml_widget_get_processor("lv_obj"); + } + } } if(lv_streq(name, "widget")) state->ctx.is_widget = 1; diff --git a/src/others/xml/lv_xml_style.c b/src/others/xml/lv_xml_style.c index aaf294b0f..7bf0c2dab 100644 --- a/src/others/xml/lv_xml_style.c +++ b/src/others/xml/lv_xml_style.c @@ -39,26 +39,39 @@ * MACROS **********************/ +/*Expands to e.g. + if(lv_streq(name, "height")) lv_style_set_height(style, lv_xml_to_size(value)); + */ +#define SET_STYLE_IF(prop, value) if(lv_streq(name, #prop)) lv_style_set_##prop(style, value) + /********************** * GLOBAL FUNCTIONS **********************/ -lv_state_t lv_xml_style_state_string_to_enum_value(const char * txt) +lv_state_t lv_xml_style_state_to_enum(const char * txt) { if(lv_streq("default", txt)) return LV_STATE_DEFAULT; - if(lv_streq("pressed", txt)) return LV_STATE_PRESSED; - if(lv_streq("checked", txt)) return LV_STATE_CHECKED; - if(lv_streq("scrolled", txt)) return LV_STATE_SCROLLED; + else if(lv_streq("pressed", txt)) return LV_STATE_PRESSED; + else if(lv_streq("checked", txt)) return LV_STATE_CHECKED; + else if(lv_streq("scrolled", txt)) return LV_STATE_SCROLLED; + else if(lv_streq("focused", txt)) return LV_STATE_FOCUSED; + else if(lv_streq("focus_key", txt)) return LV_STATE_FOCUS_KEY; + else if(lv_streq("edited", txt)) return LV_STATE_EDITED; + else if(lv_streq("hovered", txt)) return LV_STATE_HOVERED; + else if(lv_streq("disabled", txt)) return LV_STATE_DISABLED; return 0; /*Return 0 in lack of a better option. */ } -lv_part_t lv_xml_style_part_string_to_enum_value(const char * txt) +lv_part_t lv_xml_style_part_to_enum(const char * txt) { if(lv_streq("main", txt)) return LV_PART_MAIN; - if(lv_streq("scrollbar", txt)) return LV_PART_SCROLLBAR; - if(lv_streq("indicator", txt)) return LV_PART_INDICATOR; - if(lv_streq("knob", txt)) return LV_PART_KNOB; + else if(lv_streq("scrollbar", txt)) return LV_PART_SCROLLBAR; + else if(lv_streq("indicator", txt)) return LV_PART_INDICATOR; + else if(lv_streq("knob", txt)) return LV_PART_KNOB; + else if(lv_streq("selected", txt)) return LV_PART_SELECTED; + else if(lv_streq("items", txt)) return LV_PART_ITEMS; + else if(lv_streq("cursor", txt)) return LV_PART_CURSOR; return 0; /*Return 0 in lack of a better option. */ } @@ -96,18 +109,125 @@ void lv_xml_style_register(lv_xml_component_ctx_t * ctx, const char ** attrs) } } - if(lv_streq(name, "width")) lv_style_set_width(style, lv_xml_to_size(value)); - else if(lv_streq(name, "height")) lv_style_set_height(style, lv_xml_to_size(value)); - else if(lv_streq(name, "radius")) lv_style_set_radius(style, lv_xml_atoi(value)); - else if(lv_streq(name, "bg_opa")) lv_style_set_bg_opa(style, lv_xml_atoi(value)); - else if(lv_streq(name, "bg_color")) lv_style_set_bg_color(style, lv_xml_to_color(value)); - else if(lv_streq(name, "border_color")) lv_style_set_border_color(style, lv_xml_to_color(value)); - else if(lv_streq(name, "border_width")) lv_style_set_border_width(style, lv_xml_atoi(value)); - else if(lv_streq(name, "border_opa")) lv_style_set_border_opa(style, lv_xml_atoi(value)); - else if(lv_streq(name, "text_color")) lv_style_set_text_color(style, lv_xml_to_color(value)); - else if(lv_streq(name, "text_font")) lv_style_set_text_font(style, lv_xml_get_font(value)); - else if(lv_streq(name, "bg_image_src")) lv_style_set_bg_image_src(style, lv_xml_get_image(value)); - else if(lv_streq(name, "bg_image_tiled")) lv_style_set_bg_image_tiled(style, lv_xml_to_bool(value)); + + SET_STYLE_IF(width, lv_xml_to_size(value)); + else SET_STYLE_IF(height, lv_xml_to_size(value)); + else SET_STYLE_IF(length, lv_xml_to_size(value)); + else SET_STYLE_IF(radius, lv_xml_to_size(value)); + + else SET_STYLE_IF(pad_left, lv_xml_atoi(value)); + else SET_STYLE_IF(pad_right, lv_xml_atoi(value)); + else SET_STYLE_IF(pad_top, lv_xml_atoi(value)); + else SET_STYLE_IF(pad_bottom, lv_xml_atoi(value)); + else SET_STYLE_IF(pad_hor, lv_xml_atoi(value)); + else SET_STYLE_IF(pad_ver, lv_xml_atoi(value)); + else SET_STYLE_IF(pad_all, lv_xml_atoi(value)); + else SET_STYLE_IF(pad_row, lv_xml_atoi(value)); + else SET_STYLE_IF(pad_column, lv_xml_atoi(value)); + else SET_STYLE_IF(pad_gap, lv_xml_atoi(value)); + else SET_STYLE_IF(pad_radial, lv_xml_atoi(value)); + + else SET_STYLE_IF(margin_left, lv_xml_atoi(value)); + else SET_STYLE_IF(margin_right, lv_xml_atoi(value)); + else SET_STYLE_IF(margin_top, lv_xml_atoi(value)); + else SET_STYLE_IF(margin_bottom, lv_xml_atoi(value)); + else SET_STYLE_IF(margin_hor, lv_xml_atoi(value)); + else SET_STYLE_IF(margin_ver, lv_xml_atoi(value)); + else SET_STYLE_IF(margin_all, lv_xml_atoi(value)); + + else SET_STYLE_IF(base_dir, lv_xml_base_dir_to_enum(value)); + else SET_STYLE_IF(clip_corner, lv_xml_to_bool(value)); + + else SET_STYLE_IF(bg_opa, lv_xml_to_opa(value)); + else SET_STYLE_IF(bg_color, lv_xml_to_color(value)); + else SET_STYLE_IF(bg_grad_dir, lv_xml_grad_dir_to_enum(value)); + else SET_STYLE_IF(bg_grad_color, lv_xml_to_color(value)); + else SET_STYLE_IF(bg_main_stop, lv_xml_atoi(value)); + else SET_STYLE_IF(bg_grad_stop, lv_xml_atoi(value)); + + else SET_STYLE_IF(bg_image_src, lv_xml_get_image(value)); + else SET_STYLE_IF(bg_image_tiled, lv_xml_to_bool(value)); + else SET_STYLE_IF(bg_image_recolor, lv_xml_to_color(value)); + else SET_STYLE_IF(bg_image_recolor_opa, lv_xml_to_opa(value)); + + else SET_STYLE_IF(border_color, lv_xml_to_color(value)); + else SET_STYLE_IF(border_width, lv_xml_atoi(value)); + else SET_STYLE_IF(border_opa, lv_xml_to_opa(value)); + else SET_STYLE_IF(border_side, lv_xml_border_side_to_enum(value)); + else SET_STYLE_IF(border_post, lv_xml_to_bool(value)); + + else SET_STYLE_IF(outline_color, lv_xml_to_color(value)); + else SET_STYLE_IF(outline_width, lv_xml_atoi(value)); + else SET_STYLE_IF(outline_opa, lv_xml_to_opa(value)); + else SET_STYLE_IF(outline_pad, lv_xml_atoi(value)); + + else SET_STYLE_IF(shadow_width, lv_xml_atoi(value)); + else SET_STYLE_IF(shadow_color, lv_xml_to_color(value)); + else SET_STYLE_IF(shadow_offset_x, lv_xml_atoi(value)); + else SET_STYLE_IF(shadow_offset_y, lv_xml_atoi(value)); + else SET_STYLE_IF(shadow_spread, lv_xml_atoi(value)); + else SET_STYLE_IF(shadow_opa, lv_xml_to_opa(value)); + + else SET_STYLE_IF(text_color, lv_xml_to_color(value)); + else SET_STYLE_IF(text_font, lv_xml_get_font(value)); + else SET_STYLE_IF(text_opa, lv_xml_to_opa(value)); + else SET_STYLE_IF(text_align, lv_xml_text_align_to_enum(value)); + else SET_STYLE_IF(text_letter_space, lv_xml_atoi(value)); + else SET_STYLE_IF(text_line_space, lv_xml_atoi(value)); + else SET_STYLE_IF(text_decor, lv_xml_text_decor_to_enum(value)); + + else SET_STYLE_IF(image_opa, lv_xml_to_opa(value)); + else SET_STYLE_IF(image_recolor, lv_xml_to_color(value)); + else SET_STYLE_IF(image_recolor_opa, lv_xml_to_opa(value)); + + else SET_STYLE_IF(line_color, lv_xml_to_color(value)); + else SET_STYLE_IF(line_opa, lv_xml_to_opa(value)); + else SET_STYLE_IF(line_width, lv_xml_atoi(value)); + else SET_STYLE_IF(line_dash_width, lv_xml_atoi(value)); + else SET_STYLE_IF(line_dash_gap, lv_xml_atoi(value)); + else SET_STYLE_IF(line_rounded, lv_xml_to_bool(value)); + + else SET_STYLE_IF(arc_color, lv_xml_to_color(value)); + else SET_STYLE_IF(arc_opa, lv_xml_to_opa(value)); + else SET_STYLE_IF(arc_width, lv_xml_atoi(value)); + else SET_STYLE_IF(arc_rounded, lv_xml_to_bool(value)); + else SET_STYLE_IF(arc_image_src, lv_xml_get_image(value)); + + else SET_STYLE_IF(opa, lv_xml_to_opa(value)); + else SET_STYLE_IF(opa_layered, lv_xml_to_opa(value)); + else SET_STYLE_IF(color_filter_opa, lv_xml_to_opa(value)); + else SET_STYLE_IF(anim_duration, lv_xml_atoi(value)); + else SET_STYLE_IF(blend_mode, lv_xml_blend_mode_to_enum(value)); + else SET_STYLE_IF(transform_width, lv_xml_atoi(value)); + else SET_STYLE_IF(transform_height, lv_xml_atoi(value)); + else SET_STYLE_IF(translate_x, lv_xml_atoi(value)); + else SET_STYLE_IF(translate_y, lv_xml_atoi(value)); + else SET_STYLE_IF(translate_radial, lv_xml_atoi(value)); + else SET_STYLE_IF(transform_scale_x, lv_xml_atoi(value)); + else SET_STYLE_IF(transform_scale_y, lv_xml_atoi(value)); + else SET_STYLE_IF(transform_rotation, lv_xml_atoi(value)); + else SET_STYLE_IF(transform_pivot_x, lv_xml_atoi(value)); + else SET_STYLE_IF(transform_pivot_y, lv_xml_atoi(value)); + else SET_STYLE_IF(transform_skew_x, lv_xml_atoi(value)); + else SET_STYLE_IF(bitmap_mask_src, lv_xml_get_image(value)); + else SET_STYLE_IF(rotary_sensitivity, lv_xml_atoi(value)); + + else SET_STYLE_IF(layout, lv_xml_layout_to_enum(value)); + + else SET_STYLE_IF(flex_flow, lv_xml_flex_flow_to_enum(value)); + else SET_STYLE_IF(flex_grow, lv_xml_atoi(value)); + else SET_STYLE_IF(flex_main_place, lv_xml_flex_align_to_enum(value)); + else SET_STYLE_IF(flex_cross_place, lv_xml_flex_align_to_enum(value)); + else SET_STYLE_IF(flex_track_place, lv_xml_flex_align_to_enum(value)); + + else SET_STYLE_IF(grid_column_align, lv_xml_grid_align_to_enum(value)); + else SET_STYLE_IF(grid_row_align, lv_xml_grid_align_to_enum(value)); + else SET_STYLE_IF(grid_cell_column_pos, lv_xml_atoi(value)); + else SET_STYLE_IF(grid_cell_column_span, lv_xml_atoi(value)); + else SET_STYLE_IF(grid_cell_x_align, lv_xml_grid_align_to_enum(value)); + else SET_STYLE_IF(grid_cell_row_pos, lv_xml_atoi(value)); + else SET_STYLE_IF(grid_cell_row_span, lv_xml_atoi(value)); + else SET_STYLE_IF(grid_cell_y_align, lv_xml_grid_align_to_enum(value)); else { LV_LOG_WARN("%s style property is not supported", name); } @@ -122,8 +242,8 @@ const char * lv_xml_style_string_process(char * txt, lv_style_selector_t * selec char * selector_str = lv_xml_split_str(&txt, ':'); while(selector_str != NULL) { /* Handle different states and parts */ - *selector |= lv_xml_style_state_string_to_enum_value(selector_str); - *selector |= lv_xml_style_part_string_to_enum_value(selector_str); + *selector |= lv_xml_style_state_to_enum(selector_str); + *selector |= lv_xml_style_part_to_enum(selector_str); /* Move to the next token */ selector_str = lv_xml_split_str(&txt, ':'); diff --git a/src/others/xml/lv_xml_style.h b/src/others/xml/lv_xml_style.h index d7288bd34..e54e26595 100644 --- a/src/others/xml/lv_xml_style.h +++ b/src/others/xml/lv_xml_style.h @@ -53,14 +53,14 @@ void lv_xml_style_add_to_obj(lv_xml_parser_state_t * state, lv_obj_t * obj, cons * @param txt e.g. "pressed" * @return the enum `LV_STATE_PRESSED` */ -lv_state_t lv_xml_style_state_string_to_enum_value(const char * txt); +lv_state_t lv_xml_style_state_to_enum(const char * txt); /** * Convert a style part to enum * @param txt e.g. "knob" * @return the enum `LV_PART_KNOB` */ -lv_part_t lv_xml_style_part_string_to_enum_value(const char * txt); +lv_part_t lv_xml_style_part_to_enum(const char * txt); /** * Decompose a string like `"style1:pressed:checked:knob"` to style name and selector diff --git a/src/others/xml/lv_xml_utils.c b/src/others/xml/lv_xml_utils.c index ad4f72523..d16b20eb0 100644 --- a/src/others/xml/lv_xml_utils.c +++ b/src/others/xml/lv_xml_utils.c @@ -25,9 +25,7 @@ /********************** * STATIC PROTOTYPES **********************/ -#if LV_USE_STDLIB_STRING != LV_STDLIB_CLIB - static bool lv_xml_is_digit(char c, int base); -#endif +static bool is_digit(char c, int base); /********************** * STATIC VARIABLES @@ -59,29 +57,26 @@ lv_color_t lv_xml_to_color(const char * str) return lv_color_hex(lv_xml_strtol(str, NULL, 16)); } +lv_opa_t lv_xml_to_opa(const char * str) +{ + int32_t v = lv_xml_atoi(str); + size_t len = lv_strlen(str); + if(str[len - 1] == '%') { + v = v * 255 / 100; + } + + v = LV_CLAMP(0, v, 255); + return (lv_opa_t)v; +} + bool lv_xml_to_bool(const char * str) { return lv_streq(str, "false") ? false : true; } - -#if LV_USE_STDLIB_STRING == LV_STDLIB_CLIB -int32_t lv_xml_atoi(const char * str) +int32_t lv_xml_atoi_split(const char ** str, char delimiter) { - return atoi(str); -} - - -int32_t lv_xml_strtol(const char * str, char ** endptr, int32_t base) -{ - return strtol(str, endptr, base); -} - -#else /*LV_USE_STDLIB_STRING == LV_STDLIB_CLIB*/ - -int32_t lv_xml_atoi(const char * str) -{ - const char * s = str; + const char * s = *str; int32_t result = 0; int sign = 1; @@ -98,24 +93,32 @@ int32_t lv_xml_atoi(const char * str) } /* Convert the string*/ - while(*s) { + while(*s != delimiter) { if(*s >= '0' && *s <= '9') { int32_t digit = *s - '0'; - /* Check for overflow before it happens */ - if(result > (INT_MAX - digit) / 10) { - return (sign == 1) ? INT_MAX : INT_MIN; // Return limits on overflow - } - result = result * 10 + digit; s++; } else { - break; // Non-digit character + break; /* Non-digit character */ } } - return result * sign; + result = result * sign; + + if(*s != '\0') s++; /*Skip the delimiter*/ + *str = s; + return result; + +} + + +int32_t lv_xml_atoi(const char * str) +{ + + return lv_xml_atoi_split(&str, '\0'); + } int32_t lv_xml_strtol(const char * str, char ** endptr, int32_t base) @@ -157,7 +160,7 @@ int32_t lv_xml_strtol(const char * str, char ** endptr, int32_t base) while(*s) { int32_t digit; - if(lv_xml_is_digit(*s, base)) { + if(is_digit(*s, base)) { if(*s >= '0' && *s <= '9') { digit = *s - '0'; } @@ -168,7 +171,7 @@ int32_t lv_xml_strtol(const char * str, char ** endptr, int32_t base) digit = *s - 'A' + 10; } else { - /* This should not happen due to lv_xml_is_digit check*/ + /* This should not happen due to is_digit check*/ break; } @@ -192,8 +195,6 @@ int32_t lv_xml_strtol(const char * str, char ** endptr, int32_t base) return result * sign; } -#endif /*LV_USE_STDLIB_STRING == LV_STDLIB_CLIB*/ - char * lv_xml_split_str(char ** src, char delimiter) { if(*src[0] == '\0') return NULL; @@ -221,8 +222,7 @@ char * lv_xml_split_str(char ** src, char delimiter) * STATIC FUNCTIONS **********************/ -#if LV_USE_STDLIB_STRING != LV_STDLIB_CLIB -static bool lv_xml_is_digit(char c, int base) +static bool is_digit(char c, int base) { if(base <= 10) { return (c >= '0' && c < '0' + base); @@ -232,6 +232,5 @@ static bool lv_xml_is_digit(char c, int base) } } -#endif /*LV_USE_STDLIB_STRING == LV_STDLIB_CLIB*/ #endif /* LV_USE_XML */ diff --git a/src/others/xml/lv_xml_utils.h b/src/others/xml/lv_xml_utils.h index dfde5f894..d5bd5ec34 100644 --- a/src/others/xml/lv_xml_utils.h +++ b/src/others/xml/lv_xml_utils.h @@ -26,8 +26,25 @@ const char * lv_xml_get_value_of(const char ** attrs, const char * name); int32_t lv_xml_atoi(const char * str); + +/** + * Convert sections of a string to int. + * The end of the string is indicated by the `delimiter`. + * @param str pointer to a string, it will point to the character after the delimiter + * @param delimiter a character to indicate the end of the int + * @return the int before the next delimiter + */ +int32_t lv_xml_atoi_split(const char ** str, char delimiter); + lv_color_t lv_xml_to_color(const char * str); +/** + * Concert percentage or integer opacity value from string to integer. + * @param str e.g. "70%" or 180 + * @return 0..255 + */ +lv_opa_t lv_xml_to_opa(const char * str); + bool lv_xml_to_bool(const char * str); int32_t lv_xml_strtol(const char * str, char ** endptr, int32_t base); diff --git a/src/others/xml/parsers/lv_xml_chart_parser.c b/src/others/xml/parsers/lv_xml_chart_parser.c index 5ef70d882..3d91659fb 100644 --- a/src/others/xml/parsers/lv_xml_chart_parser.c +++ b/src/others/xml/parsers/lv_xml_chart_parser.c @@ -23,9 +23,9 @@ /********************** * STATIC PROTOTYPES **********************/ -static lv_chart_type_t chart_type_string_to_enum_value(const char * txt); -static lv_chart_update_mode_t chart_update_mode_string_to_enum_value(const char * txt); -static lv_chart_axis_t chart_axis_string_to_enum_value(const char * txt); +static lv_chart_type_t chart_type_to_enum(const char * txt); +static lv_chart_update_mode_t chart_update_mode_to_enum(const char * txt); +static lv_chart_axis_t chart_axis_to_enum(const char * txt); /********************** * STATIC VARIABLES @@ -59,8 +59,14 @@ void lv_xml_chart_apply(lv_xml_parser_state_t * state, const char ** attrs) const char * value = attrs[i + 1]; if(lv_streq("point_count", name)) lv_chart_set_point_count(item, lv_xml_atoi(value)); - if(lv_streq("type", name)) lv_chart_set_type(item, chart_type_string_to_enum_value(value)); - if(lv_streq("mode", name)) lv_chart_set_update_mode(item, chart_update_mode_string_to_enum_value(value)); + else if(lv_streq("type", name)) lv_chart_set_type(item, chart_type_to_enum(value)); + else if(lv_streq("update_mode", name)) lv_chart_set_update_mode(item, chart_update_mode_to_enum(value)); + else if(lv_streq("div_line_count", name)) { + + int32_t value1 = lv_xml_atoi_split(&value, ' '); + int32_t value2 = lv_xml_atoi_split(&value, ' '); + lv_chart_set_div_line_count(item, value1, value2); + } } } @@ -69,11 +75,7 @@ void * lv_xml_chart_series_create(lv_xml_parser_state_t * state, const char ** a const char * color = lv_xml_get_value_of(attrs, "color"); const char * axis = lv_xml_get_value_of(attrs, "axis"); void * item = lv_chart_add_series(lv_xml_state_get_parent(state), lv_color_hex(lv_xml_strtol(color, NULL, 16)), - chart_axis_string_to_enum_value(axis)); - - lv_obj_t * parent = lv_xml_state_get_parent(state); - lv_chart_set_all_value(parent, item, lv_rand(10, 90)); - + chart_axis_to_enum(axis)); return item; } @@ -82,7 +84,20 @@ void lv_xml_chart_series_apply(lv_xml_parser_state_t * state, const char ** attr LV_UNUSED(state); LV_UNUSED(attrs); - /*Nothing to apply*/ + lv_obj_t * chart = lv_xml_state_get_parent(state); + lv_chart_series_t * ser = lv_xml_state_get_item(state); + + for(int i = 0; attrs[i]; i += 2) { + const char * name = attrs[i]; + const char * value = attrs[i + 1]; + + if(lv_streq("values", name)) { + while(value[0] != '\0') { + int32_t v = lv_xml_atoi_split(&value, ' '); + lv_chart_set_next_value(chart, ser, v); + } + } + } } void * lv_xml_chart_cursor_create(lv_xml_parser_state_t * state, const char ** attrs) @@ -90,13 +105,7 @@ void * lv_xml_chart_cursor_create(lv_xml_parser_state_t * state, const char ** a const char * color = lv_xml_get_value_of(attrs, "color"); const char * dir = lv_xml_get_value_of(attrs, "dir"); void * item = lv_chart_add_cursor(lv_xml_state_get_parent(state), lv_color_hex(lv_xml_strtol(color, NULL, 16)), - lv_xml_dir_string_to_enum_value(dir)); - - lv_obj_t * parent = lv_xml_state_get_parent(state); - lv_point_t p = {30, 40}; - lv_chart_set_cursor_pos(parent, item, &p); - - /* There are no properties to process */ + lv_xml_dir_to_enum(dir)); return item; } @@ -106,14 +115,56 @@ void lv_xml_chart_cursor_apply(lv_xml_parser_state_t * state, const char ** attr LV_UNUSED(state); LV_UNUSED(attrs); - /*Nothing to apply*/ + lv_obj_t * chart = lv_xml_state_get_parent(state); + lv_chart_cursor_t * cursor = lv_xml_state_get_item(state); + + for(int i = 0; attrs[i]; i += 2) { + const char * name = attrs[i]; + const char * value = attrs[i + 1]; + + + if(lv_streq("pos", name)) { + int32_t x = lv_xml_atoi_split(&value, ' '); + int32_t y = lv_xml_atoi_split(&value, ' '); + lv_point_t p = {x, y}; + lv_chart_set_cursor_pos(chart, cursor, &p); + } + } +} + +void * lv_xml_chart_axis_create(lv_xml_parser_state_t * state, const char ** attrs) +{ + LV_UNUSED(attrs); + + /*Nothing to create*/ + return lv_xml_state_get_parent(state);; +} + +void lv_xml_chart_axis_apply(lv_xml_parser_state_t * state, const char ** attrs) +{ + LV_UNUSED(state); + LV_UNUSED(attrs); + + lv_obj_t * chart = lv_xml_state_get_parent(state); + lv_chart_axis_t axis = chart_axis_to_enum(lv_xml_get_value_of(attrs, "axis")); + + for(int i = 0; attrs[i]; i += 2) { + const char * name = attrs[i]; + const char * value = attrs[i + 1]; + + if(lv_streq("range", name)) { + int32_t min_val = lv_xml_atoi_split(&value, ' '); + int32_t max_val = lv_xml_atoi_split(&value, ' '); + lv_chart_set_range(chart, axis, min_val, max_val); + } + } } /********************** * STATIC FUNCTIONS **********************/ -static lv_chart_type_t chart_type_string_to_enum_value(const char * txt) +static lv_chart_type_t chart_type_to_enum(const char * txt) { if(lv_streq("none", txt)) return LV_CHART_TYPE_NONE; if(lv_streq("line", txt)) return LV_CHART_TYPE_LINE; @@ -123,7 +174,8 @@ static lv_chart_type_t chart_type_string_to_enum_value(const char * txt) LV_LOG_WARN("%s is an unknown value for chart's chart_type", txt); return 0; /*Return 0 in lack of a better option. */ } -static lv_chart_update_mode_t chart_update_mode_string_to_enum_value(const char * txt) + +static lv_chart_update_mode_t chart_update_mode_to_enum(const char * txt) { if(lv_streq("shift", txt)) return LV_CHART_UPDATE_MODE_SHIFT; if(lv_streq("circular", txt)) return LV_CHART_UPDATE_MODE_CIRCULAR; @@ -131,7 +183,8 @@ static lv_chart_update_mode_t chart_update_mode_string_to_enum_value(const char LV_LOG_WARN("%s is an unknown value for chart's chart_update_mode", txt); return 0; /*Return 0 in lack of a better option. */ } -static lv_chart_axis_t chart_axis_string_to_enum_value(const char * txt) + +static lv_chart_axis_t chart_axis_to_enum(const char * txt) { if(lv_streq("primary_x", txt)) return LV_CHART_AXIS_PRIMARY_X; if(lv_streq("primary_y", txt)) return LV_CHART_AXIS_PRIMARY_Y; diff --git a/src/others/xml/parsers/lv_xml_chart_parser.h b/src/others/xml/parsers/lv_xml_chart_parser.h index 34a3104c9..0307e4f8e 100644 --- a/src/others/xml/parsers/lv_xml_chart_parser.h +++ b/src/others/xml/parsers/lv_xml_chart_parser.h @@ -30,6 +30,8 @@ void * lv_xml_chart_series_create(lv_xml_parser_state_t * state, const char ** a void lv_xml_chart_series_apply(lv_xml_parser_state_t * state, const char ** attrs); void * lv_xml_chart_cursor_create(lv_xml_parser_state_t * state, const char ** attrs); void lv_xml_chart_cursor_apply(lv_xml_parser_state_t * state, const char ** attrs); +void * lv_xml_chart_axis_create(lv_xml_parser_state_t * state, const char ** attrs); +void lv_xml_chart_axis_apply(lv_xml_parser_state_t * state, const char ** attrs); /********************** * MACROS diff --git a/src/others/xml/parsers/lv_xml_dropdown_parser.c b/src/others/xml/parsers/lv_xml_dropdown_parser.c new file mode 100644 index 000000000..ffa32e351 --- /dev/null +++ b/src/others/xml/parsers/lv_xml_dropdown_parser.c @@ -0,0 +1,84 @@ +/** + * @file lv_xml_dropdown_parser.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_xml_dropdown_parser.h" +#if LV_USE_XML + +#include "../../../lvgl.h" +#include "../../../lvgl_private.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void * lv_xml_dropdown_create(lv_xml_parser_state_t * state, const char ** attrs) +{ + LV_UNUSED(attrs); + void * item = lv_dropdown_create(lv_xml_state_get_parent(state)); + + return item; +} + + +void lv_xml_dropdown_apply(lv_xml_parser_state_t * state, const char ** attrs) +{ + void * item = lv_xml_state_get_item(state); + + lv_xml_obj_apply(state, attrs); /*Apply the common properties, e.g. width, height, styles flags etc*/ + + for(int i = 0; attrs[i]; i += 2) { + const char * name = attrs[i]; + const char * value = attrs[i + 1]; + + if(lv_streq("options", name)) lv_dropdown_set_options(item, value); + if(lv_streq("text", name)) lv_dropdown_set_text(item, value); + if(lv_streq("selected", name)) lv_dropdown_set_selected(item, lv_xml_atoi(value), LV_ANIM_OFF); + if(lv_streq("symbol", name)) lv_dropdown_set_symbol(item, lv_xml_get_image(value)); + } +} + +void * lv_xml_dropdown_list_create(lv_xml_parser_state_t * state, const char ** attrs) +{ + LV_UNUSED(attrs); + + return lv_dropdown_get_list(lv_xml_state_get_parent(state)); +} + +void lv_xml_dropdown_list_apply(lv_xml_parser_state_t * state, const char ** attrs) +{ + LV_UNUSED(state); + LV_UNUSED(attrs); + + lv_xml_obj_apply(state, attrs); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /* LV_USE_XML */ diff --git a/src/others/xml/parsers/lv_xml_dropdown_parser.h b/src/others/xml/parsers/lv_xml_dropdown_parser.h new file mode 100644 index 000000000..3a6919256 --- /dev/null +++ b/src/others/xml/parsers/lv_xml_dropdown_parser.h @@ -0,0 +1,41 @@ +/** + * @file lv_xml_dropdown_parser.h + * + */ + +#ifndef LV_DROPDOWN_XML_PARSER_H +#define LV_DROPDOWN_XML_PARSER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../lv_xml.h" +#if LV_USE_XML + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void * lv_xml_dropdown_create(lv_xml_parser_state_t * state, const char ** attrs); +void lv_xml_dropdown_apply(lv_xml_parser_state_t * state, const char ** attrs); +void * lv_xml_dropdown_list_create(lv_xml_parser_state_t * state, const char ** attrs); +void lv_xml_dropdown_list_apply(lv_xml_parser_state_t * state, const char ** attrs); + +/********************** + * MACROS + **********************/ +#endif /* LV_USE_XML */ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_DROPDOWN_XML_PARSE_H*/ diff --git a/src/others/xml/parsers/lv_xml_image_parser.c b/src/others/xml/parsers/lv_xml_image_parser.c index b33c1abff..ff1d0e6c6 100644 --- a/src/others/xml/parsers/lv_xml_image_parser.c +++ b/src/others/xml/parsers/lv_xml_image_parser.c @@ -23,6 +23,7 @@ /********************** * STATIC PROTOTYPES **********************/ +static lv_image_align_t image_align_to_enum(const char * txt); /********************** * STATIC VARIABLES @@ -49,56 +50,25 @@ void * lv_xml_image_create(lv_xml_parser_state_t * state, const char ** attrs) return item; } -void lv_xml_check_file(const char * filepath) -{ - lv_fs_file_t f; - lv_fs_res_t res = lv_fs_open(&f, filepath, LV_FS_MODE_RD); - - if(res == LV_FS_RES_OK) { - uint32_t size; - uint8_t buffer[10]; - lv_fs_read(&f, buffer, 0, &size); - - lv_fs_seek(&f, 0, LV_FS_SEEK_END); - lv_fs_tell(&f, &size); - LV_LOG_USER("File %s exists (%u bytes)", filepath, size); - - - lv_fs_close(&f); - - lv_image_header_t info; - lv_image_decoder_get_info(filepath, &info); - LV_LOG_USER("Image info: %d x %d, %d color", info.w, info.h, info.cf); - } - else { - LV_LOG_ERROR("Failed to open file: %s", filepath); - } -} - void lv_xml_image_apply(lv_xml_parser_state_t * state, const char ** attrs) { void * item = lv_xml_state_get_item(state); - if(item == NULL) { - LV_LOG_ERROR("Failed to get image"); - return; - } - lv_xml_obj_apply(state, attrs); /*Apply the common properties, e.g. width, height, styles flags etc*/ for(int i = 0; attrs[i]; i += 2) { const char * name = attrs[i]; const char * value = attrs[i + 1]; - if(lv_streq("src", name)) { - const void * img_src = lv_xml_get_image(value); - if(img_src == NULL) { - LV_LOG_WARN("Failed to get image source for '%s'", value); - return; - } - - lv_image_set_src(item, (const char *)img_src); - + if(lv_streq("src", name)) lv_image_set_src(item, lv_xml_get_image(value)); + if(lv_streq("inner_align", name)) lv_image_set_inner_align(item, image_align_to_enum(value)); + if(lv_streq("rotation", name)) lv_image_set_rotation(item, lv_xml_atoi(value)); + if(lv_streq("scale_x", name)) lv_image_set_scale_x(item, lv_xml_atoi(value)); + if(lv_streq("scale_y", name)) lv_image_set_scale_y(item, lv_xml_atoi(value)); + if(lv_streq("pivot", name)) { + int32_t x = lv_xml_atoi_split(&value, ' '); + int32_t y = lv_xml_atoi_split(&value, ' '); + lv_image_set_pivot(item, x, y); } } @@ -109,4 +79,23 @@ void lv_xml_image_apply(lv_xml_parser_state_t * state, const char ** attrs) * STATIC FUNCTIONS **********************/ +static lv_image_align_t image_align_to_enum(const char * txt) +{ + if(lv_streq("top_left", txt)) return LV_IMAGE_ALIGN_TOP_LEFT; + if(lv_streq("top_mid", txt)) return LV_IMAGE_ALIGN_TOP_MID; + if(lv_streq("top_right", txt)) return LV_IMAGE_ALIGN_TOP_RIGHT; + if(lv_streq("bottom_left", txt)) return LV_IMAGE_ALIGN_BOTTOM_LEFT; + if(lv_streq("bottom_mid", txt)) return LV_IMAGE_ALIGN_BOTTOM_MID; + if(lv_streq("bottom_right", txt)) return LV_IMAGE_ALIGN_BOTTOM_RIGHT; + if(lv_streq("right_mid", txt)) return LV_IMAGE_ALIGN_RIGHT_MID; + if(lv_streq("left_mid", txt)) return LV_IMAGE_ALIGN_LEFT_MID; + if(lv_streq("center", txt)) return LV_IMAGE_ALIGN_CENTER; + if(lv_streq("stretch", txt)) return LV_IMAGE_ALIGN_STRETCH; + if(lv_streq("tile", txt)) return LV_IMAGE_ALIGN_TILE; + + LV_LOG_WARN("%s is an unknown value for image align", txt); + return 0; /*Return 0 in lack of a better option. */ +} + + #endif /* LV_USE_XML */ diff --git a/src/others/xml/parsers/lv_xml_obj_parser.c b/src/others/xml/parsers/lv_xml_obj_parser.c index 2af6b02f4..2802d31fc 100644 --- a/src/others/xml/parsers/lv_xml_obj_parser.c +++ b/src/others/xml/parsers/lv_xml_obj_parser.c @@ -23,6 +23,7 @@ /********************** * STATIC PROTOTYPES **********************/ +static void apply_styles(lv_obj_t * obj, const char * name, const char * value); /********************** * STATIC VARIABLES @@ -32,6 +33,11 @@ * MACROS **********************/ +/* Expands to + if(lv_streq(prop_name, "style_height")) lv_obj_set_style_height(obj, value, selector) + */ +#define SET_STYLE_IF(prop, value) if(lv_streq(prop_name, "style_" #prop)) lv_obj_set_style_##prop(obj, value, selector) + /********************** * GLOBAL FUNCTIONS **********************/ @@ -53,30 +59,56 @@ void lv_xml_obj_apply(lv_xml_parser_state_t * state, const char ** attrs) const char * value = attrs[i + 1]; if(lv_streq("x", name)) lv_obj_set_x(item, lv_xml_to_size(value)); - if(lv_streq("y", name)) lv_obj_set_y(item, lv_xml_to_size(value)); - if(lv_streq("width", name)) lv_obj_set_width(item, lv_xml_to_size(value)); - if(lv_streq("height", name)) lv_obj_set_height(item, lv_xml_to_size(value)); - if(lv_streq("align", name)) lv_obj_set_align(item, lv_xml_align_string_to_enum_value(value)); + else if(lv_streq("y", name)) lv_obj_set_y(item, lv_xml_to_size(value)); + else if(lv_streq("width", name)) lv_obj_set_width(item, lv_xml_to_size(value)); + else if(lv_streq("height", name)) lv_obj_set_height(item, lv_xml_to_size(value)); + else if(lv_streq("align", name)) lv_obj_set_align(item, lv_xml_align_to_enum(value)); + else if(lv_streq("flex_flow", name)) lv_obj_set_flex_flow(item, lv_xml_flex_flow_to_enum(value)); + else if(lv_streq("flex_grow", name)) lv_obj_set_flex_grow(item, lv_xml_atoi(value)); - if(lv_streq("styles", name)) lv_xml_style_add_to_obj(state, item, value); + else if(lv_streq("hidden", name)) lv_obj_update_flag(item, LV_OBJ_FLAG_HIDDEN, lv_xml_to_bool(value)); + else if(lv_streq("clickable", name)) lv_obj_update_flag(item, LV_OBJ_FLAG_CLICKABLE, lv_xml_to_bool(value)); + else if(lv_streq("click_focusable", name)) lv_obj_update_flag(item, LV_OBJ_FLAG_CLICK_FOCUSABLE, + lv_xml_to_bool(value)); + else if(lv_streq("checkable", name)) lv_obj_update_flag(item, LV_OBJ_FLAG_CHECKABLE, lv_xml_to_bool(value)); + else if(lv_streq("scrollable", name)) lv_obj_update_flag(item, LV_OBJ_FLAG_SCROLLABLE, lv_xml_to_bool(value)); + else if(lv_streq("scroll_elastic", name)) lv_obj_update_flag(item, LV_OBJ_FLAG_SCROLL_ELASTIC, + lv_xml_to_bool(value)); + else if(lv_streq("scroll_momentum", name)) lv_obj_update_flag(item, LV_OBJ_FLAG_SCROLL_MOMENTUM, + lv_xml_to_bool(value)); + else if(lv_streq("scroll_one", name)) lv_obj_update_flag(item, LV_OBJ_FLAG_SCROLL_ONE, lv_xml_to_bool(value)); + else if(lv_streq("scroll_chain_hor", name)) lv_obj_update_flag(item, LV_OBJ_FLAG_SCROLL_CHAIN_HOR, + lv_xml_to_bool(value)); + else if(lv_streq("scroll_chain_ver", name)) lv_obj_update_flag(item, LV_OBJ_FLAG_SCROLL_CHAIN_VER, + lv_xml_to_bool(value)); + else if(lv_streq("scroll_chain", name)) lv_obj_update_flag(item, LV_OBJ_FLAG_SCROLL_CHAIN, + lv_xml_to_bool(value)); + else if(lv_streq("scroll_on_focus", name)) lv_obj_update_flag(item, LV_OBJ_FLAG_SCROLL_ON_FOCUS, + lv_xml_to_bool(value)); + else if(lv_streq("scroll_with_arrow", name)) lv_obj_update_flag(item, LV_OBJ_FLAG_SCROLL_WITH_ARROW, + lv_xml_to_bool(value)); + else if(lv_streq("snappable", name)) lv_obj_update_flag(item, LV_OBJ_FLAG_SNAPPABLE, lv_xml_to_bool(value)); + else if(lv_streq("press_lock", name)) lv_obj_update_flag(item, LV_OBJ_FLAG_PRESS_LOCK, lv_xml_to_bool(value)); + else if(lv_streq("event_bubble", name)) lv_obj_update_flag(item, LV_OBJ_FLAG_EVENT_BUBBLE, + lv_xml_to_bool(value)); + else if(lv_streq("gesture_bubble", name)) lv_obj_update_flag(item, LV_OBJ_FLAG_GESTURE_BUBBLE, + lv_xml_to_bool(value)); + else if(lv_streq("adv_hittest", name)) lv_obj_update_flag(item, LV_OBJ_FLAG_ADV_HITTEST, + lv_xml_to_bool(value)); + else if(lv_streq("ignore_layout", name)) lv_obj_update_flag(item, LV_OBJ_FLAG_IGNORE_LAYOUT, + lv_xml_to_bool(value)); + else if(lv_streq("floating", name)) lv_obj_update_flag(item, LV_OBJ_FLAG_FLOATING, lv_xml_to_bool(value)); + else if(lv_streq("send_draw_task_events", name))lv_obj_update_flag(item, LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS, + lv_xml_to_bool(value)); + else if(lv_streq("overflow_visible", name)) lv_obj_update_flag(item, LV_OBJ_FLAG_OVERFLOW_VISIBLE, + lv_xml_to_bool(value)); + else if(lv_streq("flex_in_new_track", name)) lv_obj_update_flag(item, LV_OBJ_FLAG_FLEX_IN_NEW_TRACK, + lv_xml_to_bool(value)); - if(lv_strlen(name) > 6 && lv_memcmp("style_", name, 6) == 0) { - char name_local[512]; - lv_strlcpy(name_local, name, sizeof(name_local)); + else if(lv_streq("styles", name)) lv_xml_style_add_to_obj(state, item, value); - lv_style_selector_t selector; - const char * prop_name = lv_xml_style_string_process(name_local, &selector); - - if(lv_streq("style_radius", prop_name)) lv_obj_set_style_radius(item, lv_xml_atoi(value), selector); - if(lv_streq("style_bg_color", prop_name)) lv_obj_set_style_bg_color(item, lv_xml_to_color(value), selector); - if(lv_streq("style_bg_opa", prop_name)) lv_obj_set_style_bg_opa(item, lv_xml_atoi(value), selector); - if(lv_streq("style_border_color", prop_name)) lv_obj_set_style_border_color(item, lv_xml_to_color(value), selector); - if(lv_streq("style_border_opa", prop_name)) lv_obj_set_style_border_opa(item, lv_xml_atoi(value), selector); - if(lv_streq("style_border_width", prop_name)) lv_obj_set_style_border_width(item, lv_xml_atoi(value), selector); - if(lv_streq("style_bg_image_src", prop_name)) lv_obj_set_style_bg_image_src(item, lv_xml_get_image(value), selector); - if(lv_streq("style_bg_image_tiled", prop_name)) lv_obj_set_style_bg_image_tiled(item, lv_xml_to_bool(value), selector); - if(lv_streq("style_text_color", prop_name)) lv_obj_set_style_text_color(item, lv_xml_to_color(value), selector); - if(lv_streq("style_text_font", prop_name)) lv_obj_set_style_text_font(item, lv_xml_get_font(value), selector); + else if(lv_strlen(name) > 6 && lv_memcmp("style_", name, 6) == 0) { + apply_styles(item, name, value); } } } @@ -85,5 +117,133 @@ void lv_xml_obj_apply(lv_xml_parser_state_t * state, const char ** attrs) * STATIC FUNCTIONS **********************/ +static void apply_styles(lv_obj_t * obj, const char * name, const char * value) +{ + char name_local[512]; + lv_strlcpy(name_local, name, sizeof(name_local)); + + lv_style_selector_t selector; + const char * prop_name = lv_xml_style_string_process(name_local, &selector); + + SET_STYLE_IF(width, lv_xml_to_size(value)); + else SET_STYLE_IF(height, lv_xml_to_size(value)); + else SET_STYLE_IF(length, lv_xml_to_size(value)); + else SET_STYLE_IF(radius, lv_xml_to_size(value)); + + else SET_STYLE_IF(pad_left, lv_xml_atoi(value)); + else SET_STYLE_IF(pad_right, lv_xml_atoi(value)); + else SET_STYLE_IF(pad_top, lv_xml_atoi(value)); + else SET_STYLE_IF(pad_bottom, lv_xml_atoi(value)); + else SET_STYLE_IF(pad_hor, lv_xml_atoi(value)); + else SET_STYLE_IF(pad_ver, lv_xml_atoi(value)); + else SET_STYLE_IF(pad_all, lv_xml_atoi(value)); + else SET_STYLE_IF(pad_row, lv_xml_atoi(value)); + else SET_STYLE_IF(pad_column, lv_xml_atoi(value)); + else SET_STYLE_IF(pad_gap, lv_xml_atoi(value)); + else SET_STYLE_IF(pad_radial, lv_xml_atoi(value)); + + else SET_STYLE_IF(margin_left, lv_xml_atoi(value)); + else SET_STYLE_IF(margin_right, lv_xml_atoi(value)); + else SET_STYLE_IF(margin_top, lv_xml_atoi(value)); + else SET_STYLE_IF(margin_bottom, lv_xml_atoi(value)); + else SET_STYLE_IF(margin_hor, lv_xml_atoi(value)); + else SET_STYLE_IF(margin_ver, lv_xml_atoi(value)); + else SET_STYLE_IF(margin_all, lv_xml_atoi(value)); + + else SET_STYLE_IF(base_dir, lv_xml_base_dir_to_enum(value)); + else SET_STYLE_IF(clip_corner, lv_xml_to_bool(value)); + + else SET_STYLE_IF(bg_opa, lv_xml_to_opa(value)); + else SET_STYLE_IF(bg_color, lv_xml_to_color(value)); + else SET_STYLE_IF(bg_grad_dir, lv_xml_grad_dir_to_enum(value)); + else SET_STYLE_IF(bg_grad_color, lv_xml_to_color(value)); + else SET_STYLE_IF(bg_main_stop, lv_xml_atoi(value)); + else SET_STYLE_IF(bg_grad_stop, lv_xml_atoi(value)); + + else SET_STYLE_IF(bg_image_src, lv_xml_get_image(value)); + else SET_STYLE_IF(bg_image_tiled, lv_xml_to_bool(value)); + else SET_STYLE_IF(bg_image_recolor, lv_xml_to_color(value)); + else SET_STYLE_IF(bg_image_recolor_opa, lv_xml_to_opa(value)); + + else SET_STYLE_IF(border_color, lv_xml_to_color(value)); + else SET_STYLE_IF(border_width, lv_xml_atoi(value)); + else SET_STYLE_IF(border_opa, lv_xml_to_opa(value)); + else SET_STYLE_IF(border_side, lv_xml_border_side_to_enum(value)); + else SET_STYLE_IF(border_post, lv_xml_to_bool(value)); + + else SET_STYLE_IF(outline_color, lv_xml_to_color(value)); + else SET_STYLE_IF(outline_width, lv_xml_atoi(value)); + else SET_STYLE_IF(outline_opa, lv_xml_to_opa(value)); + else SET_STYLE_IF(outline_pad, lv_xml_atoi(value)); + + else SET_STYLE_IF(shadow_width, lv_xml_atoi(value)); + else SET_STYLE_IF(shadow_color, lv_xml_to_color(value)); + else SET_STYLE_IF(shadow_offset_x, lv_xml_atoi(value)); + else SET_STYLE_IF(shadow_offset_y, lv_xml_atoi(value)); + else SET_STYLE_IF(shadow_spread, lv_xml_atoi(value)); + else SET_STYLE_IF(shadow_opa, lv_xml_to_opa(value)); + + else SET_STYLE_IF(text_color, lv_xml_to_color(value)); + else SET_STYLE_IF(text_font, lv_xml_get_font(value)); + else SET_STYLE_IF(text_opa, lv_xml_to_opa(value)); + else SET_STYLE_IF(text_align, lv_xml_text_align_to_enum(value)); + else SET_STYLE_IF(text_letter_space, lv_xml_atoi(value)); + else SET_STYLE_IF(text_line_space, lv_xml_atoi(value)); + else SET_STYLE_IF(text_decor, lv_xml_text_decor_to_enum(value)); + + else SET_STYLE_IF(image_opa, lv_xml_to_opa(value)); + else SET_STYLE_IF(image_recolor, lv_xml_to_color(value)); + else SET_STYLE_IF(image_recolor_opa, lv_xml_to_opa(value)); + + else SET_STYLE_IF(line_color, lv_xml_to_color(value)); + else SET_STYLE_IF(line_opa, lv_xml_to_opa(value)); + else SET_STYLE_IF(line_width, lv_xml_atoi(value)); + else SET_STYLE_IF(line_dash_width, lv_xml_atoi(value)); + else SET_STYLE_IF(line_dash_gap, lv_xml_atoi(value)); + else SET_STYLE_IF(line_rounded, lv_xml_to_bool(value)); + + else SET_STYLE_IF(arc_color, lv_xml_to_color(value)); + else SET_STYLE_IF(arc_opa, lv_xml_to_opa(value)); + else SET_STYLE_IF(arc_width, lv_xml_atoi(value)); + else SET_STYLE_IF(arc_rounded, lv_xml_to_bool(value)); + else SET_STYLE_IF(arc_image_src, lv_xml_get_image(value)); + + else SET_STYLE_IF(opa, lv_xml_to_opa(value)); + else SET_STYLE_IF(opa_layered, lv_xml_to_opa(value)); + else SET_STYLE_IF(color_filter_opa, lv_xml_to_opa(value)); + else SET_STYLE_IF(anim_duration, lv_xml_atoi(value)); + else SET_STYLE_IF(blend_mode, lv_xml_blend_mode_to_enum(value)); + else SET_STYLE_IF(transform_width, lv_xml_atoi(value)); + else SET_STYLE_IF(transform_height, lv_xml_atoi(value)); + else SET_STYLE_IF(translate_x, lv_xml_atoi(value)); + else SET_STYLE_IF(translate_y, lv_xml_atoi(value)); + else SET_STYLE_IF(translate_radial, lv_xml_atoi(value)); + else SET_STYLE_IF(transform_scale_x, lv_xml_atoi(value)); + else SET_STYLE_IF(transform_scale_y, lv_xml_atoi(value)); + else SET_STYLE_IF(transform_rotation, lv_xml_atoi(value)); + else SET_STYLE_IF(transform_pivot_x, lv_xml_atoi(value)); + else SET_STYLE_IF(transform_pivot_y, lv_xml_atoi(value)); + else SET_STYLE_IF(transform_skew_x, lv_xml_atoi(value)); + else SET_STYLE_IF(bitmap_mask_src, lv_xml_get_image(value)); + else SET_STYLE_IF(rotary_sensitivity, lv_xml_atoi(value)); + + else SET_STYLE_IF(layout, lv_xml_layout_to_enum(value)); + + else SET_STYLE_IF(flex_flow, lv_xml_flex_flow_to_enum(value)); + else SET_STYLE_IF(flex_grow, lv_xml_atoi(value)); + else SET_STYLE_IF(flex_main_place, lv_xml_flex_align_to_enum(value)); + else SET_STYLE_IF(flex_cross_place, lv_xml_flex_align_to_enum(value)); + else SET_STYLE_IF(flex_track_place, lv_xml_flex_align_to_enum(value)); + + else SET_STYLE_IF(grid_column_align, lv_xml_grid_align_to_enum(value)); + else SET_STYLE_IF(grid_row_align, lv_xml_grid_align_to_enum(value)); + else SET_STYLE_IF(grid_cell_column_pos, lv_xml_atoi(value)); + else SET_STYLE_IF(grid_cell_column_span, lv_xml_atoi(value)); + else SET_STYLE_IF(grid_cell_x_align, lv_xml_grid_align_to_enum(value)); + else SET_STYLE_IF(grid_cell_row_pos, lv_xml_atoi(value)); + else SET_STYLE_IF(grid_cell_row_span, lv_xml_atoi(value)); + else SET_STYLE_IF(grid_cell_y_align, lv_xml_grid_align_to_enum(value)); +} + #endif /* LV_USE_XML */ diff --git a/src/others/xml/parsers/lv_xml_obj_parser.h b/src/others/xml/parsers/lv_xml_obj_parser.h index d08014560..238881278 100644 --- a/src/others/xml/parsers/lv_xml_obj_parser.h +++ b/src/others/xml/parsers/lv_xml_obj_parser.h @@ -27,7 +27,6 @@ extern "C" { void * lv_xml_obj_create(lv_xml_parser_state_t * state, const char ** attrs); void lv_xml_obj_apply(lv_xml_parser_state_t * state, const char ** attrs); - /********************** * MACROS **********************/ diff --git a/src/others/xml/parsers/lv_xml_table_parser.c b/src/others/xml/parsers/lv_xml_table_parser.c new file mode 100644 index 000000000..8a6504719 --- /dev/null +++ b/src/others/xml/parsers/lv_xml_table_parser.c @@ -0,0 +1,149 @@ +/** + * @file lv_xml_table_parser.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_xml_table_parser.h" +#if LV_USE_XML + +#include "../../../lvgl.h" +#include "../../../lvgl_private.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static lv_table_cell_ctrl_t table_ctrl_to_enum(const char * txt); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void * lv_xml_table_create(lv_xml_parser_state_t * state, const char ** attrs) +{ + LV_UNUSED(attrs); + void * item = lv_table_create(lv_xml_state_get_parent(state)); + + return item; +} + + +void lv_xml_table_apply(lv_xml_parser_state_t * state, const char ** attrs) +{ + void * item = lv_xml_state_get_item(state); + + lv_xml_obj_apply(state, attrs); /*Apply the common properties, e.g. width, height, styles flags etc*/ + + for(int i = 0; attrs[i]; i += 2) { + const char * name = attrs[i]; + const char * value = attrs[i + 1]; + + if(lv_streq("column_conunt", name)) lv_table_set_column_count(item, lv_xml_atoi(value)); + else if(lv_streq("row_conunt", name)) lv_table_set_row_count(item, lv_xml_atoi(value)); + else if(lv_streq("selected_cell", name)) { + + int32_t value1 = lv_xml_atoi_split(&value, ' '); + int32_t value2 = lv_xml_atoi_split(&value, ' '); + lv_table_set_selected_cell(item, value1, value2); + } + } +} + +void * lv_xml_table_column_create(lv_xml_parser_state_t * state, const char ** attrs) +{ + LV_UNUSED(attrs); + + /*Nothing to create*/ + return lv_xml_state_get_parent(state);; +} + +void lv_xml_table_column_apply(lv_xml_parser_state_t * state, const char ** attrs) +{ + LV_UNUSED(state); + LV_UNUSED(attrs); + + lv_obj_t * table = lv_xml_state_get_parent(state); + int32_t column = lv_xml_atoi(lv_xml_get_value_of(attrs, "column")); + + for(int i = 0; attrs[i]; i += 2) { + const char * name = attrs[i]; + const char * value = attrs[i + 1]; + + if(lv_streq("width", name)) lv_table_set_column_width(table, column, lv_xml_atoi(value)); + } +} + +void * lv_xml_table_cell_create(lv_xml_parser_state_t * state, const char ** attrs) +{ + LV_UNUSED(attrs); + + /*Nothing to create*/ + return lv_xml_state_get_parent(state);; +} + +void lv_xml_table_cell_apply(lv_xml_parser_state_t * state, const char ** attrs) +{ + LV_UNUSED(state); + LV_UNUSED(attrs); + + lv_obj_t * table = lv_xml_state_get_parent(state); + int32_t row = lv_xml_atoi(lv_xml_get_value_of(attrs, "row")); + int32_t column = lv_xml_atoi(lv_xml_get_value_of(attrs, "column")); + + for(int i = 0; attrs[i]; i += 2) { + const char * name = attrs[i]; + const char * value = attrs[i + 1]; + + if(lv_streq("value", name)) lv_table_set_cell_value(table, row, column, value); + if(lv_streq("ctrl", name)) { + lv_table_cell_ctrl_t ctrl = 0; + char buf[256]; + lv_strncpy(buf, value, sizeof(buf)); + char * buf_p = buf; + const char * str; + while((str = lv_xml_split_str(&buf_p, ' ')) != NULL) { + ctrl |= table_ctrl_to_enum(str); + } + + lv_table_add_cell_ctrl(table, row, column, ctrl); + } + } +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static lv_table_cell_ctrl_t table_ctrl_to_enum(const char * txt) +{ + if(lv_streq("none", txt)) return LV_TABLE_CELL_CTRL_NONE; + if(lv_streq("merge_right", txt)) return LV_TABLE_CELL_CTRL_MERGE_RIGHT; + if(lv_streq("text_crop", txt)) return LV_TABLE_CELL_CTRL_TEXT_CROP; + if(lv_streq("custom_1", txt)) return LV_TABLE_CELL_CTRL_CUSTOM_1; + if(lv_streq("custom_2", txt)) return LV_TABLE_CELL_CTRL_CUSTOM_2; + if(lv_streq("custom_3", txt)) return LV_TABLE_CELL_CTRL_CUSTOM_3; + if(lv_streq("custom_4", txt)) return LV_TABLE_CELL_CTRL_CUSTOM_4; + + LV_LOG_WARN("%s is an unknown value for table's ctrl", txt); + return 0; /*Return 0 in lack of a better option. */ +} + +#endif /* LV_USE_XML */ diff --git a/src/others/xml/parsers/lv_xml_table_parser.h b/src/others/xml/parsers/lv_xml_table_parser.h new file mode 100644 index 000000000..111c1ffaa --- /dev/null +++ b/src/others/xml/parsers/lv_xml_table_parser.h @@ -0,0 +1,43 @@ +/** + * @file lv_xml_table_parser.h + * + */ + +#ifndef LV_TABLE_XML_PARSER_H +#define LV_TABLE_XML_PARSER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../lv_xml.h" +#if LV_USE_XML + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void * lv_xml_table_create(lv_xml_parser_state_t * state, const char ** attrs); +void lv_xml_table_apply(lv_xml_parser_state_t * state, const char ** attrs); +void * lv_xml_table_column_create(lv_xml_parser_state_t * state, const char ** attrs); +void lv_xml_table_column_apply(lv_xml_parser_state_t * state, const char ** attrs); +void * lv_xml_table_cell_create(lv_xml_parser_state_t * state, const char ** attrs); +void lv_xml_table_cell_apply(lv_xml_parser_state_t * state, const char ** attrs); + +/********************** + * MACROS + **********************/ +#endif /* LV_USE_XML */ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_TABLE_XML_PARSE_H*/ diff --git a/src/others/xml/parsers/lv_xml_tabview_parser.c b/src/others/xml/parsers/lv_xml_tabview_parser.c index 93c020d59..e1e8947b0 100644 --- a/src/others/xml/parsers/lv_xml_tabview_parser.c +++ b/src/others/xml/parsers/lv_xml_tabview_parser.c @@ -38,17 +38,9 @@ void * lv_xml_tabview_create(lv_xml_parser_state_t * state, const char ** attrs) { + LV_UNUSED(attrs); void * item = lv_tabview_create(lv_xml_state_get_parent(state)); - lv_xml_obj_apply(state, attrs); /*Apply the common properties, e.g. width, height, styles flags etc*/ - - for(int i = 0; attrs[i]; i += 2) { - const char * name = attrs[i]; - const char * value = attrs[i + 1]; - - if(lv_streq("active", name)) lv_tabview_set_active(item, lv_xml_atoi(value), 0); - if(lv_streq("tab_bar_position", name)) lv_tabview_set_tab_bar_position(item, lv_xml_dir_string_to_enum_value(value)); - } return item; } @@ -64,17 +56,14 @@ void lv_xml_tabview_apply(lv_xml_parser_state_t * state, const char ** attrs) const char * value = attrs[i + 1]; if(lv_streq("active", name)) lv_tabview_set_active(item, lv_xml_atoi(value), 0); - if(lv_streq("tab_bar_position", name)) lv_tabview_set_tab_bar_position(item, lv_xml_dir_string_to_enum_value(value)); + if(lv_streq("tab_bar_position", name)) lv_tabview_set_tab_bar_position(item, lv_xml_dir_to_enum(value)); } } void * lv_xml_tabview_tab_bar_create(lv_xml_parser_state_t * state, const char ** attrs) { + LV_UNUSED(attrs); void * item = lv_tabview_get_tab_bar(lv_xml_state_get_parent(state)); - - /*Apply the common properties, e.g. width, height, styles flags etc*/ - lv_xml_obj_apply(state, attrs); - return item; } @@ -89,10 +78,6 @@ void * lv_xml_tabview_tab_create(lv_xml_parser_state_t * state, const char ** at { const char * text = lv_xml_get_value_of(attrs, "text"); void * item = lv_tabview_add_tab(lv_xml_state_get_parent(state), text); - - /*Apply the common properties, e.g. width, height, styles flags etc*/ - lv_xml_obj_apply(state, attrs); - return item; } diff --git a/src/widgets/table/lv_table.c b/src/widgets/table/lv_table.c index 948292aa4..6a198484a 100644 --- a/src/widgets/table/lv_table.c +++ b/src/widgets/table/lv_table.c @@ -828,7 +828,10 @@ static void draw_main(lv_event_t * e) /*Align the content to the middle if not cropped*/ bool crop = ctrl & LV_TABLE_CELL_CTRL_TEXT_CROP; - if(crop) txt_flags = LV_TEXT_FLAG_EXPAND; + if(crop) { + txt_flags = LV_TEXT_FLAG_EXPAND; + label_dsc_act.flag |= LV_TEXT_FLAG_EXPAND; + } lv_text_get_size(&txt_size, table->cell_data[cell]->txt, label_dsc_def.font, label_dsc_act.letter_space, label_dsc_act.line_space, diff --git a/src/widgets/table/lv_table.h b/src/widgets/table/lv_table.h index 0d615a1b1..d8df7406b 100644 --- a/src/widgets/table/lv_table.h +++ b/src/widgets/table/lv_table.h @@ -34,6 +34,7 @@ LV_EXPORT_CONST_INT(LV_TABLE_CELL_NONE); **********************/ typedef enum { + LV_TABLE_CELL_CTRL_NONE = 0 << 0, LV_TABLE_CELL_CTRL_MERGE_RIGHT = 1 << 0, LV_TABLE_CELL_CTRL_TEXT_CROP = 1 << 1, LV_TABLE_CELL_CTRL_CUSTOM_1 = 1 << 4, diff --git a/src/widgets/tabview/lv_tabview.c b/src/widgets/tabview/lv_tabview.c index 29e3e2e27..b639d6821 100644 --- a/src/widgets/tabview/lv_tabview.c +++ b/src/widgets/tabview/lv_tabview.c @@ -74,7 +74,6 @@ lv_obj_t * lv_tabview_add_tab(lv_obj_t * obj, const char * name) lv_obj_t * page = lv_obj_create(cont); lv_obj_set_size(page, lv_pct(100), lv_pct(100)); - uint32_t tab_idx = lv_obj_get_child_count(cont); lv_obj_t * tab_bar = lv_tabview_get_tab_bar(obj); @@ -89,8 +88,10 @@ lv_obj_t * lv_tabview_add_tab(lv_obj_t * obj, const char * name) lv_label_set_text(label, name); lv_obj_center(label); - if(tab_idx == 1) { - lv_tabview_set_active(obj, 0, LV_ANIM_OFF); + uint32_t tab_idx = lv_obj_get_child_count(cont) - 1; + lv_tabview_t * tabview = (lv_tabview_t *)obj; + if(tab_idx == tabview->tab_cur) { + lv_tabview_set_active(obj, tab_idx, LV_ANIM_OFF); } return page; @@ -111,19 +112,19 @@ void lv_tabview_set_active(lv_obj_t * obj, uint32_t idx, lv_anim_enable_t anim_e LV_ASSERT_OBJ(obj, MY_CLASS); lv_tabview_t * tabview = (lv_tabview_t *)obj; + tabview->tab_cur = idx; + lv_obj_t * cont = lv_tabview_get_content(obj); lv_obj_t * tab_bar = lv_tabview_get_tab_bar(obj); uint32_t tab_cnt = lv_tabview_get_tab_count(obj); - if(idx >= tab_cnt) { - idx = tab_cnt - 1; - } + if(idx >= tab_cnt) return; /*To be sure lv_obj_get_content_width will return valid value*/ - lv_obj_update_layout(obj); - if(cont == NULL) return; + lv_obj_update_layout(obj); + if((tabview->tab_pos & LV_DIR_VER) != 0) { int32_t gap = lv_obj_get_style_pad_column(cont, LV_PART_MAIN); int32_t w = lv_obj_get_content_width(cont); @@ -149,7 +150,6 @@ void lv_tabview_set_active(lv_obj_t * obj, uint32_t idx, lv_anim_enable_t anim_e button = lv_obj_get_child_by_type(tab_bar, (int32_t)i, &lv_button_class); } - tabview->tab_cur = idx; } void lv_tabview_set_tab_bar_position(lv_obj_t * obj, lv_dir_t dir) @@ -233,7 +233,6 @@ void lv_tabview_set_tab_bar_size(lv_obj_t * obj, int32_t size) else { lv_obj_set_width(tab_bar, size); } - } uint32_t lv_tabview_get_tab_active(lv_obj_t * obj) diff --git a/tests/ref_imgs/widgets/table_1.png b/tests/ref_imgs/widgets/table_1.png index a702cf3c1dbe2f10097bbbd027d484704fdedbc0..ba465d5ce49d90ac373490579fe59cc01a517c37 100644 GIT binary patch literal 10331 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 k?{DoE|9?77D;%!h+6a2_hwkF4{f*4`KCcMF0Q* diff --git a/tests/ref_imgs/xml/lv_chart.png b/tests/ref_imgs/xml/lv_chart.png new file mode 100644 index 0000000000000000000000000000000000000000..51c01d916a61665356052fd3368f82b4cb75acad GIT binary patch literal 3446 zcmeH~e>Bs37{|XxnYe~-y2Yj+DsJSSFc-~oLm^vtHBqMZgXLFBW{bu|*XeX>xuTne zN|EnwtQN8`KMuRKtKCZ)X2xY0*<>`!uRF&%_nc1W+&?<~>EHL~`Q!8XJkR^}JkON_ zn7!YuTE7YafNy;FdEo#+cLMy9dB_Q*a&9|90xLJqR<8BAK?p=7a=XvQGsLclT_W2Fw_ikRFYp8TT~adr4G zjjr$b=a8y18kvW=B?z+(b6P*9P<(KMk7xU*vrQGFk-HE|rBaPDF55#8kqHZ$p#qUe z)FzKFPj@nphAa#i&oPBVtjcD!Qp(z%Tt7cQKciOq+fpbLm8M#G4oF>L(K|R;baN+y zvA6JNDQEo1HJ`%w)R6g5%x<2Z`D8SbNhVZsIQ^XMZ=dmaJPxOgEQv;rCYe)57mvxF zq^VBb-y_OzgltLgF|Zr%Jn_Wl;WT3Nra;4p@<(*UqBL@}>|&a!DeLk8>&;!tr$DNF zm6}f=5T;NN7{SQlbx$6_c!$4j#?{6)VKJZ6zn0eA*VOca!>N~PpMQ=zt(lBZsDA5+ zu(V9~(M`TwcPaldWVD#_d~|LHwFWlsPCCsE#|0={7~H~&$Yb&J(g^S+TG#=H<6~wd zs1&z7;tFq~r!-`f9(7pEO{~_K)jnqNnRTqqUy+J*>0@`aGPmduz630d;Hp%$+l~lw z@{CP3vguOAz=PcVf#|?2UsqQ-ZkPrQ+be4dhR$5_)*ac9r6Ul-^`nFamD!4sVWRQ-~!iaEK5L!|R=kyHDrxCJ65 z{!sZChk?h#tmNDUk48>`A_AL-!6Pso*@~As`9J0rMQv?I>!U}mca6wJBWT`MG|jZD z!X9GyanNkzxRG@5kN%d!L*Ce-#XK2AyL`>w73)n&?r{z%0daUjTl~-v4f`y|-W^U9 zK<%iG7!3IG)YM7m+c&ealKH_RZARDxJ@e`{YS#%#%VClLqu;Uf(=z_TyUi3Tb?7+1 z2r=@h5atwR#f}kA!JZCUtCMQqSu>K)rHu216NdU4W!C4JkWpK*f!USqV`DK=$2w*L znT+>~$mJ{93#}DnSheN~m^i&pG^ggxyVWJ?2cQ2nr3UKY>WfEs^kggg0AAt^Qb22b z%Y)o*biy0>TE=BnIN~wqYIxY7|sVEWZKr z*IK4Z{fVAJE%j`}kj$wiJ@D$P;Y_VD+sOpK+n8b+tN|Hh|DgRoB+;6Em!yRw2+HCv z&}b61UG*6)t^~5DAlP@dLv1(yR*MU1Pfw_Am|I?tL^6dkENhoYBqRN-_BhDLE$q7~ zWCFvd5cjGWTX6Aulf(1c+WATO!9_4uc|e(UE>(>_9TJIsN(%J7OmOQxb(f`a&B2M2 zT1X?A0mW^yxM06^*L+x)^(HY|0Bb|xMk`%hTm-U-GTwY*b=pzc%}NUEj&k;&Bz_4P z85@huY#cNRVtbBoOGgut%$6Zl(}}<58fuPF{XuZO(MmF5rYxbuN#P9zOa=?Ob(VYT mS0LB&n7TZv|NpY`ZlNVJ*n2!9$6C8j0lwZCuhO50m;MFvnO|7| literal 0 HcmV?d00001 diff --git a/tests/ref_imgs/xml/lv_dropdown.png b/tests/ref_imgs/xml/lv_dropdown.png new file mode 100644 index 0000000000000000000000000000000000000000..bf89f7c9bc5431adce30475b5045ab22ed1969e7 GIT binary patch literal 6136 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

r;@6%w+y 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 OqNSz>Mm>G??tcL}I)U#1 literal 0 HcmV?d00001 diff --git a/tests/ref_imgs/xml/lv_table.png b/tests/ref_imgs/xml/lv_table.png new file mode 100644 index 0000000000000000000000000000000000000000..03856a1a9ef10770b75c31d4a2bdf4cd677a5067 GIT binary patch literal 5889 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 a`l)G8=f@>}pMj%o2=$5QiR$BFpZyaMCgkG) literal 0 HcmV?d00001 diff --git a/tests/ref_imgs/xml/lv_tabview.png b/tests/ref_imgs/xml/lv_tabview.png new file mode 100644 index 0000000000000000000000000000000000000000..51c3fb3892c8db0290232f761bfe014142464069 GIT binary patch literal 5845 zcmeHL{Xdg^-#-%RN$M(`C7fL;NghflkJnY5X?aQ%VW!AK6B34vg-gyNuw#2XU5F)Y|G;qHnaQdzJ9r1_aAV-&inQ1_-)_gIKIdC^ZC3VKA%0fCJ9f|Otv&RvF}jlV+BhM~=JV5R+L-U|p)eGWVKPuJ+@Q*3Nt&k~+CmtMzu*%cZt zdFi`zbYiRGd`Z@{mPvRiitH-LKXGEIWv8;av!&i1=WUMzk3RN!blm5mL$`u~x)=_7 zxWhj7I?)&BF*6ify}6ceD{-_@Du=lw9K{9!pm;Zr`gqtii)=IoAVZ-tPi>l)L>H!{xmE4;NF$)LWkVO+z9m<%I^J=PumP% zr4X-GOwiR3tKX(P$sr~Gec`{IGW4#AX5aB%+IXTM5@hkJ={gt=X``t(3s6<~R5}Et#8F<{o!{ex) zp0bAdpS31Yl5K`HmufDd(GnVsR_5EZZ{NPgv@$nq{!+V8cz0n7G0)c?f=TA;vu+7r!>g+r@e|R9e z2S3zXTU#pcR;M7T0nT(SrCItbGP1k;ufUuoM_oIh;(k}1LFxFFa@KT`}?Hfam{5vfX(z{ALvUXZ3lO!Z0I44}Wb}eRgc}_*|s^$Eb zgLY4k6%mQghjw%;OBO+Nq&aZ*^8%@@vGnS&w?7&ARKG*@j5mcgw-(}9-J`ewM5CZjGAxwn6Ggd+XlD* zAYdix^;J?*%CVmqp+6jTFq9j(l~aFE%2kZgie%(mb`7$9~tOi6++6V1A^F z#DaI}m(xk@}B#&PyL>lIl5YZe}4zJTyfL-)t>#4hsPbe*3M!*tNb!Q{#7UM z>@h<_CmkLr)l)-+H6R>Ft#4D>+i4SU=3I7o9B9Y0VYzpbpk^SZofn%*WLqzL%IPVu$e?Op* zyBBifug__Ty5*Ra`G~vy<-P4Pg9PZpJ(HRpFV)O zK42qjj!@oE_`Ajbla8!@dwGm-0&jszlNv*aV{+RPHBN{7^_Abu?AuJ6y7-^w(nzUY z-Q%H}p?6(;916AMx8roo{20Hh7_8L9F7jPoJF@w^`vqWz*0W6^bz>)de?1LW?S zIfoKH-mE2CZrYHP3p1@<7>pznFn)t>6U?f%3hF|xGG(wzG)>4vbK@6=;wn&+$x)n{vzx5_ zZh-)g%tGV}7wht*)3dgQb&-ohb?~6ol)BXY1_N{cO3KPkOTYM}4%}s0by&$a0uVT< zY)vMtExe}dv)Sy$#l_?^2QnG1uCAS3T?KcGa?L^wbaizN4Q=AY3*WzAGR=*f$O%Z@ zU1))tt^o*e9jxcCG<4}!U(L{FO1W7aDJSJ1deUvi`!-t0@5i0zq?yltUE(tBYB4j! zQdFrD0#Ev=y~0hIV9N?l!!o?$Wep?x#n?DWQl>0&%2pQLvuEHl1-q8z8Mh_-z`*WJ z{%^PhZ2T09bo=)0$jHd#GsAODT86bA_;8GfcA1HBy`#*fe9y+PC^ zFM=JX-d0&?K3l4;PfuHuDALKTvKd>hjN720+)@lTL9M|nL^b02P+7D-ETc^SX6nux zIe(Y;fAa*DfAQkQ%HY1sm>h;*Yo1l^;CBx|8S$bZH#e7no~{b|Wqfj#5#WfWbm4n#Da1dHcOG}5*=bQuhzhh9#FP%)kiKT?n0;a!JH0Q??a^cbDiJH}J zgni)jS|56;)h;T2w6sXwU|{cRi9+^+O^S+Sa9n5SJE9+bh}97_-o_t~B3Go!$m)02 zV1xU0b%GTW)C}A@bY5|N2UtdQjYfz^^!J?}WRagrCmJ!~x9En*?x zDec&yiinDey6Wm`&s+9?#8HaR)@G1p{?h>LdnNn3u(IKXuQOP8%nFM}Qk==fYcs}| zYzBlvJ>kr=g3=+?UGB3*2W``c-c+$F-cm{@|KXzVS^1+floS!u)$!U%2iD%v0op<1 zc{X69lDg@004c!Wb;Rx2+D{H8(z!GW^iMlnauvAqo3E?>Uv;*x355jOEH$07TcKt0TNNG zQ?$x@t|b+BxQe^o0+dv*pU7K$Tg54APQ>C%)wi+z`)wirshPBeR(ocCJ{~9 zVO2_Bycl8V5bRN>BKe(bQVAz|^zn#%ACUW{seUkKQOew|?{kpfDF1fsgr?X1#Q7w z$HfA7dFlV|KmPELL&DX1A`qPJ?sZ4W%gZaU!+}Xeb!)Ph0glP-2&DrXK=zbU3kVS8 z41{qEV6b^S98e(0$HdHR29&<@bjO1)01|u1uMo~&piKv|+uGU+c8ZFKAbM4WgMgH6 zR%eHXd_W~_ZEf!qCE6d(WcY=&rEjNkb>91U@h;ngjco3WoP)t&`Iml)QAAo+d4-HN zJ_g*AN~LGdo*j+T69Q{#aui+xD$?0$xiXdsW*>O|uemLG)^XwlMa>Htua@!O3pa1@ zKb4>`dene^GD@RxEVCdxHB<$6!-EnLWSR@I7FxUFZ-7%r`%s{N+S-Da*P>mYE+m4{ zwWk#R+7Zv6nQ{6W40sMq7<@b3S{yjH9cWpZjV@GXIktn)>|X)adHjn>Nl9kZJuc>U z(O`fVZ0cUm`EQD8`%kyC*>U{NqR@C@F3@RSHOL^l4vU?;Qw)scO#UBCuI;Ao(R7=5 z9jx(o+Bco|LoR~ulCR_8)lpM#i;BRUXotJe%oT#>VUUFEdE)_6zJ3k|hn7-!H5fk5 z8_@q8#DIg0>bFKL&kj@3OCZ_7edPtfCc!jxN{YMh3eGG(5oe^A$=HJexv8s@wHMg6 z*;ql>iAi(~zmdrILlf?b$a(M!-=OgqZP{Bua|B_^Zew^-GzUF-fh{Me*?-`@Le&-3o*%WsDII(v8z z^CA$4J*Z2+7$Febk021+hIqK(leU}5uMmhM2-Gh>n*_X^8*q1d*_|Z8lzI@7_dd@t z{FUE`VARFJpNwKnUSB9Nf4UrDeMIsHzt=lXolNxm;qD3=M z?boJv*6bXQ$S?Xh{>S}TtHW6Zqb!$zCI`*w`cCp8?C^QzrjR9oj6{d0ZA0tmziBT3gEff%woZo3Wfi{yWX|M=kl zMINk{#?dPMmKWqz5AArUn*im6^{(*qa z-`>&&=btABq$zjLR`OFS)~o#oWyLkVhAk0~hm|Db^RJ^B_+7byJm$Ib8TlR4a>5m1 z6L$$#f$19t?B4j~lEnvv7=AKyT|m8JNoVDF2!%0DkrP*4bd1r?vu9PWk^M)e{G)0c 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 X^My^;C3*OT9s+ei|Cj7@w!i)t>iX_X literal 10437 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 bEID**wS(*-kp%dAA_8?)_e$<%+sFS44>_47 diff --git a/tests/ref_imgs_vg_lite/xml/lv_chart.png b/tests/ref_imgs_vg_lite/xml/lv_chart.png new file mode 100644 index 0000000000000000000000000000000000000000..fee22593b48efa85a0b5893ddc53905b085abab0 GIT binary patch literal 3392 zcmeH~`BRf;5XWDRNPu$HX|xCkRXfuW)G3FU1`rQ~QQ`q11Q4iHlyF1B5n_TwVLE_E z)l#`aizVq3@+%cXxob_1Uip8jdYib>BnOGC@->E@V-ysX2q zm-+kbrS3;gvU|QIO);Jyxic?;RXW}+hW**Htq3PhBmIUxYehv{tfBVbI6oYA4#5U9 z@sV7d4seG%s)D|6^a|GjZW!7FnTM{!0Q2uT0MM$B0FM44)CJ(%0|DS?U1PxZ%$Czu z3AVc6^Mzpg$_7vmU8;F15QJ6-@OV6#lCMY{yWl%0S15?clD4+CIG#x5D%4(u)G2ZV zQdulkBzL^G*H=Vapp8#WQ5WCb?8#Etal(UpBDo&gH7-*x%pZ(&9Pjgdrv- zF3t`!>kY_r_0t%&>?@g)R2Src$ekhZLk=*(QJGS+9$=t9=Xw$zJS||M(dcKVmYfSK zW(de4D>|A{P*AYE`V5qNa7@V0tzV@QiFH-XFv@Cs@MtwwWOikd#!~_9p-N{OOB#}tajO65Gi7lcQ z9q!r9VzZgEYV}iv=NPQ$%#_^!Xzpf6T@YrtzyG|5#xW)}g<@g_{_t9F$UQDTo;fQP zVo8Q$a_^!NqpNp?S-;0pnAeswunXQr)?F~5jJQduo`Vg?yI(hDjH=-BvVwzQ^=`VT zXm^!P@HCy>^2B6ERC<7yv*k#_g?pgokl42={UV#L!tBh)yFTuE1mX`>9*&+9?$b~-swN+KR7fKRYK>vy_BG&@AB|Ym7adS ze!Wp8rK4sWAv$JuoXnh^rqMq(G`iW0j^mhvs8@b*FF4uv_`&L@9aoez53KMw++J)q zm;6fvDlY2Q9mYhlhR+X9Sr_+xvta&(()~EKO7r_Gx6$y0_(G&x_40DP0jbzpWEIYX z+o!o?w-uh5i@V)POc|MVx?LYpq$fvQhxK(_yA_ky>CkKOHZEP*DYKXdkuF7bZhENb`PWTM5X*L_=B%@!7%D}G zjVe4lk7GN$jo&da#8_cRueW2T?Tpjk?=819iFU2>!I zSStN{`%N;p{Ze9jIu5f=X;$EX>#*Q5kwQgk0!bQGwIq;Cn<0EqDMa9`lJvr?)g__I zYRssp+;n+$ZD}b9S)$Uchq6->62ux6xeG!^bpZ6zmSff}eFBo2O53oUefo%)TuSrQ z3;|+lQUh7Lv94K}*M7i`ez(Rl9xsiypiCZ9t*mI)1rb<|8{~Z#!m%QS4N;iQF6fpj z6xF+JrKbxk#zi6-0hyDN<34OE>S!<30a_tee*R$C+O@XU&8?05zaAYMdM%+#wiW2` R80d5Xd`_ZIu#Sai{R@n2ST6to literal 0 HcmV?d00001 diff --git a/tests/ref_imgs_vg_lite/xml/lv_dropdown.png b/tests/ref_imgs_vg_lite/xml/lv_dropdown.png new file mode 100644 index 0000000000000000000000000000000000000000..b3dce7077c815724a7cdfeeb96dc01d0cff4a753 GIT binary patch literal 6287 zcmeHMXIPWjwhkZz${;!@C@nh9h!K>rfC_|Q8F7Ld4ZRnYNbdv)B%p&_WduAFk(N;m z2q7Vq5Q-2i2&jP4LNN(SPk_)u351-@oSE~Sd!D&J?sNX!=ehHDe|zt>*Spqw*Sq(3 z_sV56#Vu-EAP|V+PZ!RiArP585Qx;Eyfhf;xRLt=0@-E#)49`@;n`Dc7oRk2Rmr)sE2h~-g0z@Rgdp{ke!9x z{q)kg^F`lW4Z2!}JP*I?fjwTjqbt_Rp~a)aAolul!QIDUBg%=%3;Au%Ug?UW#pfP(F|5Xg&7DiG5jVr3!QF1kuV zHYxu#^cNC;<;4H1Qn0=bha^9;rx+L+;YQx3#l^)nHZ~SFRi8bsudjdN1f#ODl1|^b zC9b$hs_dgB#kxFK>So2GM~}Sdh6n^vxCdL6o}QkjWAj$Gd2g(2wFSkxAU_|C?Chkd z>QQsEb=%sgwzfClMpBK=1<$)X45YQS_1A?; zs{Q@@>+r1~PD(2TJ*{_-9(|vQY_vM9IJYv^cnnk1(X9<_-W)3%ah2RXIyP2N;6QQG z?`=#Vd((SXRw5E?lcB)6dsvtODP;BwuUM`nue8)^f@cmHj%~R0yeen}iS!IA%Mk8~ zuq>=7NF5y=Ez{a`a~s&PltxQNXXb?BK7ZXsTonDj;u)9B7E_NNL2y&4^_xt!E#00(_CAY3 z2@*9|S;c)-#oP)N$I3A}`4#d}Tztsi_@5!8scr21VeXzSt+lI0e?J{*pwj3-~;o3or5Ktl37J6C`ck zN(UasCqfZuvQ~K#}`_H#NpiLnHjR`;p~l%MBH=^-+GRhLNpAWtifz<{>^Fy1Dl_EImd93gab|+ z8QSAVAE_=);Z|eiDkYN`#QJ!ilHUX^&x}YA)IF^9V^?F^`E_+N{&m8aX3%JwH|qt( zfzZ`WcjMMGmIMSsAHK?ee4;>UM*_*762vy_L90&p%}0iHN-_3rxET@>Vj^CMWpEDQj~+(fO`o zZ$E$zz((XIq>-UfwRYtmL&5EJ^KzBZR)Zr0C>L;Wl91NS`<GOBToUaO*KqDt! z-^G<{eOFNF2TwuU*M|A_+=kpARQt2C{jHMco)i|sNL`fKVHa`#MknANU52*o=)7cB@Xawe*S_s;i!iuS*nM!y7oB!O=czAZiZe?a1PwYN0Bu}yY4cI;Srd3kVfu!4d@LV};ET3%scVRdyiold7vTCJv% z9dydHAk7!)_L}PI-dvIeyd%4RZ&i(4O-<*BW($dn`*Z0VNYK|*r6F9WJ{J@~k(6(GBah+i2y~fH)gRw1p z5ua}E2)gd$qe}{zo?>)cy;_@ib05RS8<7_JFvJ^hg%2mKX?yhc@81tPP`h~a#DJbl zm3 zsP91@6(OdkN4Ko(1>1tqSRjGl|H!&}H2aJn2vdTnuAeP9s7$`s7$W>0GH3c9>KfBu z*zsGZ;87>}Fo>VRMi`gR9<@BoG&GK4zmY*9>dKkS^1N%WoUm-3jZRYHR;8_P6y*aP z0J~9OI}_xvQ#E1c{wNf3qUJs%Vm~d$VX=3#^@QK2=`X}VzH=n&g$TrMvw{0j3 zpMQN<(aEXZWcHjts(pNAexuniuQe^oL5)uwxvHf}Dd^P^FbR`gg|}qeVnmyFus*dez%I9ynRgefj8ZcTf~6}@9>fQ*}(*gZbw)Gr%g_RT*K@LwC&HYeh{ z`|7-vivUvL0W@R7NN|?NK`U{fQQ^2I3N{Uz;3~$LRIj*-F>7jDU(XSXVWg(`JBb>I z!?YM9AR*G(4~CkOG!XUWOacHqXLX5y0M3<=Ik~wu@$v8W?Sqk~l#BN6Zl~G%p0#1EYZ~#)?PQ{(5u)Xxx_BiP%4l(m?%?G=q}|Wv4-b=4r{RI$@=pix z-uF;oB)T`t11|qTo|CZcze|`uQ8oWMG+HOW*JWD55O~1PToxa^kAX{XRKaiR=U?aj zE{OhQXi&+yFxg}!!D;|!4fy zC+AHn0ScUDeA65B{D%@uo0W<|YNnh4Wmj*Ph=*wPfU7o4{pc#?!n%XD5cH3-5k!+P zya_3_V=W8X=7eil_w_Y`dh&Ijel%Cm6Gx8n++d`Iv82kWsHdUV3MYJS%%%}CogxQq zd8{0=Y%glvGc;a!WcL_lh0e7V5}Lz+3ieg1va~+Spi9 z)JZJ0EPQCO!mjno0W&?6m-D zRHUD)1^*iSkersDR=9E94~{y-`i|Pl?@0@7N;Zcz`|Os*azZC_SUS4MtoQx^Fk}P_ zyz3uLd8mR$HnmBZPQpX#=B;A~=TDf>q9QMY64)6l`ziD7#mM1n0#KZwIKB?1G=>bT8u(6 zC96E@PFC~-b%v6U*CiWOvfmf7mVCe}?UrnmlS7XIbN*H0+=R|Zr_Fwh3ZvLJV)0|8 zQd_%?2l|G`Y;%0@?SXzng^Avk6-CnM3*WdpW(LTcb;lbU-{hDUjy3zmeP#nZ7L!{Q zJIlu1%GQt%Ag zpN48zbgKLDrNXOY7@Y(VmuPT?mPaAOulh(X29Evo(yp+|vgNfIfnYFTXpuj?I%BoC z{-l!NFfdqoMr@#nBOIAh-5fiBw4mF!Mt=3{_QOs!sc2DH#HF6w!(Am`y|F-gv#70+ z+kV~dc>90%q8$_-`PY)yXw zjVFD2T9p{#5fmv(TjFv-*LmdwWENMkh^;V`X21o+U6|aug38VzW`=g#t`d6LVqGtw;iOibm4|Oeo#drlyyk8wYiOp z^d6;6D#MKn3n9n}Y)T5SYbGqHv!`tIq&*uK_rSm-q=mvCQ+2@=JU>6bSWZgqoa<@l zVh6@yQrAb8jou+O4>vcrZbp6DKXp2lO1*pcZf-nueZ-%TCDdJ+dk0<$ppiGDqMnn< zI()mBueRGJ@8h#dP%z&pULoNm&VX+>_eGL@DwsLlWc0-$~EP$Ak5wzkVIM7+ohWzuJT!17=kx-eCE&UEPb3Pq(ktFr*bl9VGP z?Gi`L-PMGVw?a}!k^2#T_4Kxf-gNGz%XmY5I?sX2!;e4yH|3hNUt1foI(h8jrbCR` z*;(9lbyNS&;pB!}Ynd4tyVTVkfQYURx?yH!)-&{JFTkXz+sneDvnG`Po$he@#v{b& qx7a`Fw|@=&@jCN=9Eo0uY?13Nh0y!*s^CWhI@bF&ZDa*ar3(Va?8hg?i2lJd@bH^&lK_58(t;LF;x>t9>jvOh* z<4Pu*?5oLxTN7#-$cUuRlvfm?XX7jA$3yWD7k2lb^_7!x3OW;N*rgn1CA|CZKfnA3 z+?!ycb!m~caaK6*DD=d0oaOPsO>DxG`lKvBur<}$P=@8lsM2Yt+os?h?jbF1?>`_9 z?-1PHZ66!merJ*War^sQ>bn%c?bGnH+S{jUk)Iv_H!HsTT(@6SUs>G&ZkcMx-3OWP zREDWW*S(ob;m&Jx6<(+27dg;{-Pb>Sf)06b)-4%$c(qYo_;)z7R3e3${5#ZXrdB-X zcZDGEc0?>t)u*|+d}5=V$)A76j@z;jLOERx#l_GXP2nS%I>!bRL3P9~9H{IE+R@`t z3vyj0!?7jJuN@ z@R=A{GK<)9eO`Te_GfS`ZUTFHc`bGGXKzo-)Wt=6&dMnCK(udbK!x` zheUH->kqiknc|z*eh0$N_{!zu?`l+47+lg1x+C@@}x#(ob>oU@{EYNt} zP@(rKgNgAxgx3W<0bq{o^c&qcy$k`&aSl0sJ-EVTA9^1s7#PYHc5E2dgm4M$wB<+} z`HLUyay`Pln8HTe$LVEfTh+mr-w}6dgcv?jT(&DZJ zj@W20!g5jiLfS~4kl@U9_fla|!8AJov=rZxv*bfSK>N>2LqT@iR|P@qElD!| zr~ZC^0rBzi&oc~pEaT;iVgmWd@a2nn^+5|z##_u5I%l@tpYPq+LT@zN;k|yc$^{?p z@F0x7yEA}_9V$!PD#oXMK|P?Y5xAa+noP<8*K00zTR*>JQ;!0%kWDSSAbBnlsHmSV zpLOmNc7%!P*4AM5?J#yO-5H(V!ENJy>A6>W zY9V@Hl_y((AzGi2vbQ+ z*%as6+8PAyqNZi1Z|7nNna2G1SNj$K6CST0*%z6iK8W{B1sMa3EsG@cH6k+O4TEV#A4 zu+j3vrsHbNKh=e}_xw_~i)W|5#T0&e=gDM(G$Z2&qtrVX>fG28k1?LVK0)lSH#Dg! zsHx?dNHop5{MGg0j$A^5f`XEMOa&uYaf$Y@z{=9$!`y<~fw0izvH6h#46pBQOQOg- zn}_Hno&=oh#ZsxSK)y0_*2PhtPD->cOKO`K@5(8U%6m>u&U3Zy;+C$k+A!4HL8Dvj z`GdFark`y}PeZSV@;-9Jh@?_PkZa(qM_TvV>=He(K@@xot`y)Nz!B^8F_^V~>=OowrzzDs{>+>+9sYBnR;xIY;wgIZIDV&}+u7j!e=wna#rQ)zoIu@y{yfG%vd^OZ1F1YAA^H;ghfUxF zPGR@zr7q4d`w~9jdBc(oU9J^2bIBx1w<;Hd>E6t=<0m zFO&i5H7zboX=D++PMFW|l5pX#*XdK=M8cKz+cFY08EjVUb05o-^kU9rsa2ozDo{jx zW&OyT@v8>5WRe9{jZ>p;;3i}zTtK1(g+%_iJBHv0fN}cag0XvIbZ~^4?q`k5RMB}| zq0}Zg7>Zthc}zE@iTg7$R+V;sezsm_yvF$<8iT9G_25{y+!9~^*-~4~6@CaTABHok zyk+mXkk*tpP4DoTpz_)@LKZvP~WVBCEo z|7_rFtV-O+hx72z9y$Ll(ih&|e$k=uNRb}22fVxc>R@V3bycuQaNkHGKN_(!`}M#( zs_z5L8K~g5e%<9AwEZ^5p10j8raedUIA3aZiAl1{8Ur@UO0Bmd(W&EzScMuj&J^UF z&6SD=sG;2`b++pr{ss9umO#1=Cm%%#3ty|o@=pu*^>~VbZS5EwohH`HcSfGKvvG2w={M9c&6|-#-QVB8v8Bncu?ex8*d_%uqchn5jg=eO zY{a+HY%M3wckH$*F&j;;7Pda$nv`ap1-x|{g8KBQkFK#d-z(aJ)@R3#w}w60T&ZlS zO37}G3uo%4%wl6C$FXzjm+hD#w?PuI>bH-f44J?Cz zD;|8vfI?s_k2Ez=t&-w~Duzvd)w9j%Oi@H0AWx}+f-bMik%;~5!E9fSJdHBy@=zul z&Boj9PGH22#*geVGt|1ivQCEWv+3`@9|ssEl3Bw^g(HOf{j!|EYsp_+t6eUO3w-_j z`~!S_ms@!yL6_+tlwSG1A|IreexZsFxID>Ul0p)Tvr`wF;o*h9KTn7Y23=eDrp_$= z5%u2Ff|T1KRrOiX}EtxEQWmS25~ zbj7-(X*XYhJ`WdJK*O95E=ES2x1{VF$}3u0K;;MIB%wiT-U1YYPs2sXy&vQZEFzJw zpT3{hdIY!=!Na0^vx;?^(s4NVAqG`zu$Jrj>UKbSh@EiP7^P0C@z?Sh-`Nzk4Vz)J zx9J2!@;+5=t~W8Tgz9wr36HSL$)y(sui_Pur zI^fQ3?#FbZzOCSTZ^Y`oo*%tMhEzIy)^W~*CTjzJH9`1HGdEc0vBkd3~$lkxmohET(S+`9z zZH0&HAN&mO^4Mjjr;~7$sEEkk{DD4q!RdLZuI}`HVD9%B#Il5##MK~d4bt=@SmJr& zm!ddcr=RPyEG^}Q)nz9n*dp4C;{S5wpc#THE2PqdT4{OA?tBtV(7cX3!FajLD+j~v z6Q*QW`7<*~=lQU0HtRG@PDQn3#)y$plTu&aOQ+}U*K@N3$OzN2$!1Y~eSHVi#nn-7 zn%-v`2Hk`Px%gXK*XwnIE5WYrPEsLzzchx*`M1nog*o_lH$D*5&1{%jm|Ze3KmNY) zD8tb3De`+w73BN(z@uOQAuErd!aFW3Al2HbBk(SNx-%zTOE+4D*rHW-`4gD-q0Q%L zs#F~sxzEvx&rKw%1~|E7wjW*EM!-|XOX-L}LIo%%wLZgQYB9wq#fx?jL)TA7E)$w@ zt?XL)lx#`$C?YzlO*CSkPC<9B>~rBu-Du_o*XBi&xs~fj0b*tb$dsr3Ni6`7<88;nq+$hk_iszg2H< zqZh>%6X=%q*I$x6xL2r}QR5jwVE!!G;#VSlkWcv!JuV8uzO$&XtF^(4cCa%i+J&~~ zog@F!G~68RE$JTwv~TYK`PT>7tLM7@xqQJM04>+Yq!S79dfacy*ed|bV3Xa3V`zXkZ*g7o|;^BU((QJD9^#o)z5lH`|~kn)!D z4U%H&Xh*+Lw>PcH3FXwRDRMF=gr~gL6yx>d#~GA0>SmX_P+S5tc?3Ov1O=9x{Mg#f z%}upoqiz(4kv~bU$m`WI4=YKH_*wiSXrIZljWcrM{?4qN(#k<*iu-;6yZXFVmT`WU zBe{E8i&HsHDagf=u^@JQ0x5NUo~2mA`QdTwz!ZE=P)x+cxbFH(#2W%B?9Of*y7VB8 zIS0A!6tZs9^GIa6KEyaN}b?IkkRglcCV?a@e_8{-5;^#&tM=(VCBl3wS zO=kPbVW_VjI==aPFPuSFYE^0$0I?QrA@?AbgmZ4hxvJV;HFZ$ny-d^LmD#B z$AV+ef0}_U&BMd5FC!Tm zcr{pZ_TOBTv(%L9uro3$=Tl5<{`^^Z?-6~dfdqX)b}LLzAw?i|V7<}5*-xYyv3h>g z*03>Q*7|Yxp`nQKpE!P4_vIt(^&Tkf7kskKFY^OcDd(fU!f~a)lcTqhZN@Kx?Vc$m zbe%7$c7#ry_L322C25RKO@}qSO5^(b2`oNGprt?bef;;@X+P%uZTw(Rm{>u)MMtpT z)rfxcVPC*hl3?{V<{!*;pG`>Av*rQxDJr+k@A{(aKwsZ4t-R^{XW4pe;!S!IPl@*Z=|nQd1TSAbfT%0Wz+T3Ur77!C`wGZ zo%i<2IQr-|I1c;PC-La%>-qcp&bFK7`ph)m8txZL@>;U0C7szGgo~}8dC!`TivaFTcsCnEN05 z#S;+*`1wi(2PG@@LpKX@a~FeIH6a^6dt@cek%>~1=tJ{1Aq}0BZ~aC>d6M3LJ-d@y zjG^?u^T+LkKhr|5)@o+c=MEMmbTem4Au#ylG;S#DNY}6lGE?=s6u`vCEi}~sq#1>6 zF{v_u2pax0b!$#&ZvLmcdop=a5mgCLEuS*_3Gj{l;nm*oB4;9`!Ce}wJeH5KYv#J!yiQ@ubr!W;!133517?<4a4Ojz0$*C35&v~KpA#zGV!CG-xyVNtrofr%f-3Q!ot3DEnxWw3QdpLCGC* zzBN?9n?}l-!b~XYZ*`Q{g4*8gruRaU`x zxYW#YIJ#1!!n~&Ds0*4a6^_ii_qVe=9aPU3T?gA4xeL|^yPFDLpL9r!`VA>NBa&1X6Fan2UGYh zo=!?RNCQj5Q`WG9>4X!FC}jS`Mwe=L;>XM3G@hFK!ZJ&}Z6h&h^i{UZ%8!#L*8R-q z^xkx~wKX@-&LLbL-@B#df7>;SBO1PWmG@as)K!iMnhVqUgRAutpHf_-$r4CxJ z^up5A)!>S(5af80mE!o%$ka%NrNtk7*7rhD$n{3QrT}+x_@Ckaa{@}Ki?iu@`?!j@ z`xReJu+^8T1-M3N4RlOfL5`BgvfXpPw;XyY<30T5%3@37nT?vR{-E(I)0d2WZ;NuY zwaN6?(srwMF&BewQLeW%zN+=y33RppMRsbmZ{N$Zr;S_WL=eR5EA&_kOow_HYsWQA zMWVaBT;S}X^lilOvtxPrU2MEP6`F>IE+nvZ=AKT)%x4r3IZI}Yri~a@y>NSUvP(-F z6oN>HTfcz%t2a0f6luh@j$E`~UVyN>hON}dql?g+Bh4GS#Qv`Xj5cay@;6GTWSgju zXV(nhzh=X1Lq6Wz91`DP2c3T;JzTAv$OxqQ>nt%lVSWqhI+lejTtO zCDpCkt;)-Lk>|GAAB;b#X5wt1Xq;6|$q;t5Y9#Q6>}l)h2*af#2e6ZB_74mHpszjh zqE~k)&%d}Z5^fk9NrSOuMTtELkLHC1+T=-yrVp1fWk5S6M7{EMLwvMoru5O<7%WZb z=eAu7D0Y3UDl03e)K`BO?2dH>t?nhW8lN1E(;hAdb^RN5d{_KpowoO_l6JJ?#rl{- zK?rY{!#^&79#HD)E1VmQ?Vs%Pa0aLI{1xt{gA)25P0_Z8dv(GQ9~USNRW zzw!s{i!nXXKmWj+8pAYwDxe5` zF}3Y2rTcROU(Ke7ErC9?xVu|pT7dDw`|2R8!J)>ivzLs6knFVv>Jk@#k~mT@G-V|T zy6z^R-5YAEYIwMLG*OurrlzSf&y&A>Q?^Ul zm*dM4pn1C1ol~+eCqcI`R?_fd*KN&iEz$f2-N!(Dje?$=)p_TPbF7`LO6QX?J!d9l zFV$-k+c3ROOh(;INMFb32eIQw?f@3P_WA;;rO%z`@4aZ3mB*3WyZa}e+l1&bME~k{ zAUDaVWw$RJYx##F9wTTk+1k@EW2XvIop^WJT{whV?GO*!*gg}4hSfE4HVnAGD$31= z`iC+;J7^3taR$5iiwTo0EIn1Eq)gB3ElJYX*GFDU3PGyAQ-=5ahK8K2ERDChN)73; z#MXnmw}1CsOJ5Ibj*N~(J2FvacKfX!ZESQ~G$(m<2mS3g9a@$=PJlbVR-hQyG1lI4 zchnOlj_OS{tl5YSVw{Q%W+`~HJ#aa#&yr(I?3ODD$|pfQekiuMD87i$_w6Aine^Z` zTcNSx(EWS(@s*Cs;KHdUeI>Wg$B%x&Z=T1>Pa9qn40T+n8Q)F@6^o)z@^aZK$8bH& z%ksRD7kY-T#j?Hg#=i~@*ozzuY;4>oAx)X@a`X!WxSPNI;=zc)$U$fGro@lMYw$Gj z!^7#+Y33Ma5~jjfnd;is+RW(JLPB?Yv})ngi;Fv6QUZwSw@+d7%ARpY*u-2*9A?@jo&iDiqG`)^#vvPd{@`U+&1z;BsAl=z-3An#uh<08WE!|oEsD7 zt)3@EkY)164~i!p3zM+uE&Txc=O-6N#PV$s(}!u*Dka=uo(I27CGW}2++3I49B15o z8`!D%|Piegn~ z@mV_+9W@m-^+(rpmAPXpw$|=uja*}{bn8N;IA2-f>Ia7lA#R~=66ufKBEt){-jcaG zoisKzE=yCqR}tm+@!7r*SXEQm;tE*IJIPPL46KzxNat|phBwxai}lEv!V@}!Hs|+? z`($NHeLTj;HPqCXvh&{{srAU4Dw?(uKVGGFFI#l%_UsZ6_Wg<#GbEFpD@}^4QxijT z`A~d~sPZL`kvE(*l)gFhwPH%rC|{jy9d~}QiIr{VnB;PPKgSTZlp7?iG{Phcy zv)%gSx@2qHf4>YAt??NTQmyX%-s~P38JXVD2)mfv7_d%ekE2p%Po$GP!d+sCP6)qg zj8PtB)L68ET%?kfJmXc({fT_(fRMKX+imH*9Q>(>tx2$n2*Q-5#tSP+B8QW!t*yao zXd&S_g`<>18HNT_<-db}y?vT817AMhgIj2_3^EMPW2-f0poji#b|CtLg zZX__E*F3GE-l0Sp*?#y85U>MzEtY}#5|>=`mhrhxJM9w9G$0l(VH9d6u!k2#rSCVu z1{@n9ryy*BNN{I4WTP(Q4G(94ut2_8(8$Iq=vpdjdc{*LW#5)BnM=KI@u?dN>9Cwc za`bD)LM4t=Fu2ki-m2muAQ~$9?5TYUwRIe?fxdRo)%^ab;Si5GO+i;sjuWs>toZ8b zrQZxs;96@s0wCzC#lQVKRS4YqfX+F1P@liuYM z8?+413n2+H5iyCy4j)F_I3T1>*Z(FcBp~-^(u~E>(ok3+5bJJJs5Sri0f20h8cSAh zrmbJR-%ujMCF~L_ITvv4%=_i-u1QK}uP-usV9E?sG&QdxA|VJURm!quQ&~~>(tFR3 zjXf$<)iw{T927x*$i6w=FK6QsUdjBKBk`4WH&&VPuFoac__!eL|Yx*7K+xv(pMQS9Jk3L-Gm?}E0W~tEl;r*fanN(>@*x~ zWMx$$tliRu;O$}EAqXdUgbr3tbAQcOL?J3N2dS=(DGS2(ehzr`>~Xq^ag0%s5tquV z>ETA{jB^hhvOw%b!j$o_FJEmluP`X8A})G_bF@lml+r-fu{O{`o;Fe)8&ts)ZGX}qe3esm`cQO4KJgznbOI9{qK*K(A6M5 z|JbyOTrUMRgKuuS21}?VTp`+oBh7|`3EA4zT<`YdK;bSUPpkSXM)y^0nBe=lq(9PO z`wPej!2z&m4r2tfvRoW9Z-h_0_xYb1sWvjH^sBz)ngH%OGT^lpxtW#Rigl!TzfJ2L z?iwiD+1VKz8)r=$Ya6e)yL0)Zr+(9i>yz7&Px!w#RF8+0l~qh4XEyUsxyL}1)*2ms zPMZBG`%1ZbV%}%J0+z#SX8u|%-rrk)Yb>(HD7`D>+K(VDfSQzt?F>Ep z(KGUOq$Wl#W&IuM@T&$vi zOrTx7g$d(Mnu1ND*^*Tr3d?RxSiJn`A*JheC8OgoA_ zUcA}e;t$xEBa4zrk<<2$@DrFbOwW)f9UeITmrcoi$mc-LGJ(cyow<*{zhV?o90CQJ(-T3m!BpqLq} z4?NlGC=vzqr9?jMmIY+F=W1efRJivpP(DPLZ-_EOuVFx#jB(uOu-lZN^QZd&@%1=H?t?z&E5ISm!^}X!~NTf+Y5&q%*9qBGuuv1$BnakhNQ_zcPtw1 z=Jncbxc*6x&e zh?UBrUx`dE0JK^FJwCa1Xd8vxL}5SfL9gbZ8SS?C{N97-Z41N-CnQ-Hj<12_EUTJ_ zl^)7L#^<=nI{OyH^b%dd7vf#%z?-q7j^*<}0g5LIfZD0Dps`uh-tO#ayEi0#q3vh4 zc3uAXRsH!=Jet2QFqd6zyS}@wdSIIGU_Aai5Ew{Z?!-b?=gj;BCzS-K@E^(+HmA=T zXvQMlS&P76Fb9~!{OvP*GZBziXlOWl+&4~evY6MwgAi#D?8(grd-asCY{bjw zayAgl5RBAXwaZhk8tm+W%3!dVR7YT*UW5ohM|Po3v_8Mnq#WTHNDXN$hy>^}0aEI3}+Ne^i@GQEbo9hrBs;^XZtzWcGL04!s` zB|%^F{rfyeVkv)?9!pnqt40b2UfulBMODoEXs<$-m1qZ4`;S73nXyS*h!fxzQv@Jj zlbKMa;!{K9-JTzx=RoU6ZR5Y}8MD+_bYcP-$%p`Y^Bvp#;*OJt3rZ}Ii7vBCGN2~p z^{{;dd=}mvdN5;C9@dp_{Z&0pOp=U)D*1OdS&`PSfUY9`si~7TK6N>>H5c0Can}*M zo8Xy+IFCBA&C*w=ek~amM-T2zC8>QVViQ}Wao#~-MJ0FKsr_3*(6@GhC%Y7zsc4bH2Yn2o(BjhTl~^{jwh%kB$L~|3Q?<$NbzNVF zS_W8BN0KDSRhv;K$}#qDEyqn86$xw!JbU`3Z^c#&jzHQp8Tp)>y`}5)Jh28+WemH) zJ}<<4_x1{9iJ^keNa_@KQu~^54gop43{v};-Vl-J>5<=x6S5+yQCxIWE`!@=;(w%bXXSpVD=0<0Z(1F)*s&35I4jcB+<-RBqWxg-p**Py^ zzu6@BB!C$eA`bN6@kTE1GHSZq+pY(TchPz>QU3i`$YNR4q93NTSkF|f9LS7yECAbhO~H0_yq(I zK0St>&WZ!V$S%9g*AOA>AvXtwyh??!VAjN3kF*Fr_`&P@))Kph@mn{I0 zgmr&XmL$dT5BCEyw~r4$Nd#lzp##qt%9t(Q{=1icOH)giAAT{l#e-wm$ohIVJneCd zCzHn#QRNFKG=6z1gf<75xiL(=K&~^8r1Ln9H z@^u>wk@UNgs~SDFmO6tdJ)b-z*5)7WhQZ)#N?Q{x2I+~7e&cSH!$r0REcit&kT$(( zj*1yN)8f|#eaV+KtJPqf6^d#jj&zjZx?zi&otgI!k8B4upmyK8gRTJdL?9b;c9!Cm zo%}*E`0l=(nYKvA4&)zwHTXB{y6+q#_Y`MmXT@?8w3tIqVIfZTE#L}$L4DNa@9^ww zn!dN6b9xRPxWCA(R4tz*ue|zJLl5cw`GgVoOZUs?=+yUe6=vEr;CQfn7VgSLd{_H0g70{;GblN z1!`*i5yM4XJkX3S%XGOXczY^{aq3x+iF}YeJXH9y*{;s$>$$a50V?=4iE1TRRdscZ zTbB;_U)%bHg^reqd{N5S!DuD?EpIs867(joWtp)k-t#xW{8b&K^?e9{w`t^woO^18 zKU%7y9w!f)`DGnxhlYNy9%e)|`trk2o0SbN55`kk7QUX|YiMEobyqpW5|kDa_N<&Mj-2g>YDeH1cZ0#jv?+9jazY+Wt~u zD#?w|GR*|5X~d&`hZyfSc{u)T3tPbi!K=}cQPN@NRkg=;btF^;tp*mxfUE+9?*NYy zVf~5kk6 zH!ZVQ>cac0*I?il&Pq3Hjn)T7WrGssI~he;>ItTqo*(U&S|Vyf-U_UrZJ)<{E-YyC zarra&*;>2gz1r%f$s&D-wDR`!P9hPo3naROHnj~9$u`Lz;TUbKEhFeUG7IHMYxe^Q z<*C{q2yjqk}}=8H!O**PAckAHeCi22I-s0gDq8!S3&r?merbU3LJW^e|3{Q9ttr{=P4 z?fh#*E`^ZLNB&Utx%kVD?vhuzM2JV>HuxKI0#i!|HH#v{H5d&(Mn?8ETy;VePf%mm zsW%!4Z&G((HM5~)b=Mp}zgW!X7#)4SwR3e4%$U#Z*Abh$e^Qsc9Tl0(r$IaUQ`(Y& zGLr1HMnxqS+^rn8Z^l54_xm{V)A5@W8T=29U=uE~7!ogzzXU5k%E}y*l^V6!z-o48 z)m1>|d9|wYZC)W}xmwnPP9*5+K8ZYOT~quq;Jubz9;WD-8iEea_7hG{kk^i&fhJOj znhm1yg;zQoG4|$i@DL|Ux=`n)D63r5eQ<+TI{lF`M;V>ZX~i2MZ(VV?D$EcY1-ruhHM=Gley7)%Ouc5OVQ_R^KB>P!USffSqpJ@B$?FENHxhFLk0J zai${5&mT-qFCX=-+(YEn)Y^&#tZeTbh6v!{%~RZ+ZTV^6)GW|)$QRJ$Eb-h`F>DZg_t~W0-wMxJ-WQkl(ts-twaKGV-HZDp^e&&fJBHy25tAp znB%NKQDgYYi0W(Q6lcOw4^QQYjE_ z*%g=X|BYG2LQmag-R_Y*=9&FuTZCBY7v6mefw}U81#I5x;X)*<&B9?s_EP z!akWdTwRT?4z+{I(yzOltQb?-$$TNQ5Dp%+Yb6RbJ87nW_=7!7NKw-v70lrpm4%Uc z42fLk!uz!G`PR^Mz+fGHefa!*ZF6(__!_fv-!mCXIp(L@i%oL)TfQQuCX$zlJD2+m zSGCn;8v1aVIxP-M`JzcsF}1U~O>uFlX;dxiQz0Q@jAwucs~rV8ED((>4=p}fyf|45 zUes)}E_9@#&~L&lbzv&LpJP!snCWgHxlV!{fRV(evaG!fS`N*eQ~n8=V=0>aZeN%5 zZwR*s)k9umZS4GVUV~oR0+5cp+tx8l`#f*NT>bUo^;@omMw?r^ z7&K-Vjh6J?`#sxLqQ~;$X%wvB+!^scd>f5UV2~8zlj!Uyu$v&EN#3uD2c2esl=h_IYdWi{qxu%SN|NOwc#5RMN^kB?5A;ieM}Aw&ErRa7RSdQ+AxE# zSOvSgxC|_vIQ))-Y?;XZjX1yTf6J&K2z%sk8v{SPI~!Zb_O;9@jhx(rPLxkTq?P3H zcd(AGd9hPA(tfUcL5nU%KEp5_Fdu3XUVER#%Zp%I8XAA6bI|jUSM##oDDbBc&9cGs*5**RcVqgSr?)W{A1?{PJ;C7?8m*9}aZm{$M7+wCeB; zf*}Ak2tJJH1b-iq1tFE#=RUho!Ys^8Ox8JAl?`$Rtl?y_d|sjTr3j!S8YwTgaJ z)nmrjgYW)zWdFG8^)78bGJ=2BsO@dH(2x0Wa&C70>diG1i#HIC?T5v8-6VZjmGKu$ zEKd(lHPUw9z5{ee3!leEJg{rc0n;~#8|&rA59~e0^Tk6~mV0v3z^pAB7&)7|cW-}9 zElq9j&*({??MUv}vd#Vyu}Fuk*xIN)BT;4%^-@)0E$WMp^{Y-~7LD%at@+1Co_Cg0 zz@kw9X070gxj-1>@wa|nucf$i>>_^G1;D!brB#<4{(uY@Nr#}1>71WJ9Dv<}u4iTh zPs|4y_dNrBn5dpces+6P^@cvN4>P~}8cWEHRPKqgY<`}sZx_1VTdaw#uDUTu2dub; z`+jAHl28W0{m(A6XiWN`En`+(`PYG>Z+v+w<+@veToxBRWJC}83gfN*ttxG*D)lS( zN(Nd{YYM>6Xb?N#v=ir<>(V<|#MVS?3`ELAQJuW4cm*>aZMNni2^TG(T?L)Vgiv zN9c!HA1t`pLH*bHuufO4p5Qi57^nl34N!dX7?u{$GTPkK)JNonf0QJ(=6by-Be3A0 zInfnz+t&fh^6fw-c;J&)R$Xzq8XNOJ9aB3h7LWp1^UTxW@SdcGv8*^&(mJ_1BjzI6 zN5of0Lt(85P5_usZUD@k+s-a%XBQ-(Z0=g@0aREQQ8^M*3!E2P4%($mu=5wJH7d>_tv#=Whl=zYGKMu$ZGK2w~GT>?m|JNgOPyg>v*ZrsH|Is1D z|G@MgnEtnOssAyF|Cq%8yC!jVgQ&6r))Tx>?{TxK^TuT3_X+T6$~zj77-jo_GP334QQ~45ZQvrE}IkxkOZ*kP-PGY3K>91 zEmg!2wuk{kAm~KI0FkC41_%KJEQSykAqgSLymY>=*L1%5es=zJ&aZR5*E#R@+|PaA z&vWw}!sDYYd$vFj^b!2?(>@TiVIKskjcrl~BR@n`eFs5$>hRO2eADlW_xo6HI4kU7)h&-pS(T7}mO#VCcd%Fo7UINW;uCK3OgsHt`PftgV9;AET?fPFZ zw4Hw0@WbGs-{?WCU2#u1>9#~T8T^tTZrOv;kZSaYszDkbBO$0uLl64n@oO8Q?Pq>h zLscUqBa==W>fXP9Zz|rA;~1ZiFzuvXH%V>R%3(FNw3J9Tq58uuFB0D>>@)O}ZOYDo zYj(cob^SkaWpw=c1}<}I5$nvtY#+P2*(}E*+!2Nf3DL`Ej3@n7Vn%?p&aJ*oVTrOvNS5;Nb z&CN|)?f7|_YJ+W>9v&W?*wlqyth0-w<6T4z&+W4@13*nM|MCZ1E|=sd99$Y69?tFh zmOV3R`N{mlk=%{*tza9~1Fdo_ndj)>u;k$}Uh!dadfG(1JM@CTzdtXRL?U4i%Zf~j zdjj@~xjoc7)SmDn=B%2(d7Sevb^bB4bxO#gAO(sXUn zErgZ2A#4A&ji}El!~EglK%Ct5-Z?Wpqr|H2mD+CkE{yacN`7DiiX;XC(u(R7urSvX z32vIOn&Xtd8ozKkLLbuJ&qaX?6en7zGfzLL1wi z!B=P<*=OAtbi7CP7i~eX!fvJmf%Q(O4*5g{wjfQmH7sRLO;wK$4|}mc%&~Bxe=$OS zoa23{5$O?A9ugFkVqfXs5OTJLotvA>pbUx9${$oB-mR^*2asSeSk~jF+l7S~S_qkk zRjj6x{2c7;c5l<9JeiTm3*4)bW>o}2Cq|M`0@r)-_9~U1U|sR<-MdP}VR(`2y`(D@ zY&JW9w6eM^MAQ_=tGpt?hAa9a6WtVnVI>B;H=0o5d*k|m2$t9y3Hb#UOt zixw}h#GzK z207b^G#d>dOzD?;-p5>?n_F;hd{T*sN=y_>rP7Is?*^j7iHdulFvHcXV1;0k6nk2G zdz{st=i`mMI1{*~p*G$>ro^p-YSI_^Wm6y6{08CXBs@KA?=h|OD{=>ULB;(uv=9_l zGRi)|Y{r|Z>=UgkXdxDIzKU0DmUy1XX+9d%5^!v59W|=az50s=ZeWKr741S~tZqT}TXrBa#IgzQBO;C%0$d+0}uDfcriu{ulS#1c8H<9SBYOBvw&rb$f; zrukUO>r$KaZyVL-4mIwBXDOLV(J%$QH8PbrqIx`GT;_UjUa64J+WS+6o$T!FTwGA~ z6d5)vJzYA%$e*1UN_WhXZX1m1T^o2EG>|uFZ4l~Ah`RCkRXqsc!omU*Z;|)>5|LwC z;t9g^d^`z*agZT(-S%qJ^IEmGDp4y`yR+$|6WPLlkUQ(uexrEu~R$DJj0SzJ99w^DP0Q)zwT} z!~RI}?5j|cpBF#_o5LdV_a-j%k^<#w&Nt~_sQ@_k6u38Vg=tifp|*x|B9Rle7_(B8 zMs?b?4cshcShSo8h#5_(^ED;&%#P-G+cQrZY6EyC+4j1Z_eD}XkH{^rR4$}rlarfY zU8=WSqFMCLeQi|Wp4+ugeo^AbC%wgm#t)0q?)${F1Sqny9bpy;6xC0+BG6&Sk{|Kn zLPA0S9&zrW{NQ4vEa@di_Gep?@Lt#FPgEjmd`&;fdk#{mpr9aRFWaMKZz` zI7ENm=90kJmVlL`j?^32E0jni`YP*r1M9LgYbbofzetVqDFEV~ zTUr80NHY+a1WF*uuT%C^UFxg8RjSbtLbG>r3Jz+Bv#zi}b@XD6{%@3SDo zK}J)k9qPZ4$7(!mV`G!Sf{0I_K7EDJ*ymkQQ2{jQ=(=W)eHj-YKjricf1>Qn;QRDW z#ORHYvnIvu9y#7)y8Hj5Uat7%sso+r0C5?K1IS&`^|Ou2DSS6A1s*t^yPLA4%F7xpCod?_5jk^*{I8E zb>wv+?06VJN=r_*{C`MKKefcT=O0YH^@i)pRE14qX=iGBdh6QyYx6O=B8wK{;^ZWf z$WcE%gru)*a~YYR?%V=k?GRD&03=U*O8_2^=Ph?woizZUeCv6?`5|$#6TbPM+8Cx_*eutIMHqO=nO60?Cz_H?kdZEbJIE=({k!_*+u$!iSz zSFW*FsTa~N53Wl=P34vw&xG6tvWxI_0)>1Y#dH7(0i>TtNVIivVSTkNXZhb)yo+f` zN#~C2go*3xdcqek>=kD&nolNJmKnpF+uF(``x zOT0KgU|psb7b)O}{(eMTkh85V68qhki%VYDW6A-amN2Jg$ppNMsaDR(!;MH3FaQJ5 zJM$S!OG{|+=&>qq_6H!D?3wn=WgyHjL&FiADBZDNStXarGHTdBhk9N^b$0ko7g70z z@g2)e)&>R!AXD<< zw&jK^nKvtuyC|iXn~;Xuc^TdsbnY`JWL=Br{p8Hd@&_?R#=y8Q8*uV~s@Z~IF~Y7| z1Fu64v`JQL?AOq{Lh>V?+*6Q7r8PAzO!rlA4%P;c)(seyFBA&Hn$lBJevGp=O7(@Z z-2S28EUtzPd#F#`FH{1}%NU zEud&@E1YC5xYjqoQ6OASL!%uk`vK5ESQreq>VY?WJ`LCH?^@-FQh)ZuFz;YG&lNa*+CTM}4g6|evp8z_f5lFN8HJUWV4FV}#d zfEOvy&IF+3)z#IW_lx*{-Utz;g5u)h_Nbf-3b{bl4_liJS!)hf3$ zOC=Y$U{+>p2w6av6s&Azv2hQB!9Xb&@gkAPr}hK4Q@P9|M*-6;TYAS&uz?r3C>ILR zlKBra?5}g>-^#E`uiN^`+A$;pc~E{Ykj zK3cLp1uI!1Dz1zLzpSKBaHo3u`p%A?pg#i5jApICz(5&d@et6g7W>hLV)TA=agQ;) zsIRZDUF%v2uwK7;qvuUA(9`wX4h{~Ua#Fujv%a=RPKj+UXp{8rUT?hp?)xXd)3(vC ck1cokR-G@n2)`@_O<@S`hB#gC8v51W0VSe&IsgCw literal 0 HcmV?d00001 diff --git a/tests/ref_imgs_vg_lite/xml/lv_tabview.png b/tests/ref_imgs_vg_lite/xml/lv_tabview.png new file mode 100644 index 0000000000000000000000000000000000000000..1e47084562a0564757d1797f0e1cdb9f1afc4df7 GIT binary patch literal 5759 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 literal 0 HcmV?d00001 diff --git a/tests/src/test_cases/xml/test_xml_chart.c b/tests/src/test_cases/xml/test_xml_chart.c new file mode 100644 index 000000000..657500463 --- /dev/null +++ b/tests/src/test_cases/xml/test_xml_chart.c @@ -0,0 +1,71 @@ +#if LV_BUILD_TEST +#include "../lvgl.h" + +#include "unity/unity.h" + +void setUp(void) +{ + /* Function run before every test */ +} + +void tearDown(void) +{ + /* Function run after every test */ + lv_obj_clean(lv_screen_active()); +} + +void test_xml_chart_with_attrs(void) +{ + lv_obj_t * scr = lv_screen_active(); + + const char * chart_attrs[] = { + "width", "200", + "height", "100", + "type", "bar", + "point_count", "8", + NULL, NULL, + }; + + lv_obj_t * chart = lv_xml_create(scr, "lv_chart", chart_attrs); + lv_obj_center(chart); + + const char * primary_y_axis_attrs[] = { + "axis", "primary_y", + "range", "0 40", + NULL, NULL, + }; + lv_xml_create(chart, "lv_chart-axis", primary_y_axis_attrs); + + const char * series_1_axis_attrs[] = { + "axis", "primary_y", + "color", "0xff0000", + "values", "10 20 30 10 20 30", + NULL, NULL, + }; + + lv_chart_series_t * ser1 = lv_xml_create(chart, "lv_chart-series", series_1_axis_attrs); + + const char * series_2_axis_attrs[] = { + "axis", "secondary_y", + "color", "0x00ff00", + "values", "90 80", + NULL, NULL, + }; + + lv_chart_series_t * ser2 = lv_xml_create(chart, "lv_chart-series", series_2_axis_attrs); + + const char * secondary_y_axis_attrs[] = { + "axis", "secondary_y", + "range", "70 90", + NULL, NULL, + }; + lv_xml_create(chart, "lv_chart-axis", secondary_y_axis_attrs); + + lv_chart_set_next_value(chart, ser1, 40); + lv_chart_set_next_value(chart, ser2, 70); + + + TEST_ASSERT_EQUAL_SCREENSHOT("xml/lv_chart.png"); +} + +#endif diff --git a/tests/src/test_cases/xml/test_xml_dropdown.c b/tests/src/test_cases/xml/test_xml_dropdown.c new file mode 100644 index 000000000..0aea1b214 --- /dev/null +++ b/tests/src/test_cases/xml/test_xml_dropdown.c @@ -0,0 +1,43 @@ +#if LV_BUILD_TEST +#include "../lvgl.h" + +#include "unity/unity.h" + +void setUp(void) +{ + /* Function run before every test */ +} + +void tearDown(void) +{ + /* Function run after every test */ + lv_obj_clean(lv_screen_active()); +} + +void test_xml_tabview_with_attrs(void) +{ + lv_obj_t * scr = lv_screen_active(); + + const char * dropdown_attrs[] = { + "options", "First\nSecond\nThird", + "text", "Select", + "selected", "1", + "style_bg_color", "0xaaaaff", + NULL, NULL, + }; + + lv_obj_t * dropdown = lv_xml_create(scr, "lv_dropdown", dropdown_attrs); + lv_obj_center(dropdown); + lv_dropdown_open(dropdown); + + const char * list_attrs[] = { + "style_bg_color", "0xffaaaa", + NULL, NULL, + }; + lv_xml_create(dropdown, "lv_dropdown-list", list_attrs); + + + TEST_ASSERT_EQUAL_SCREENSHOT("xml/lv_dropdown.png"); +} + +#endif diff --git a/tests/src/test_cases/xml/test_xml_image.c b/tests/src/test_cases/xml/test_xml_image.c new file mode 100644 index 000000000..f7aa06b7d --- /dev/null +++ b/tests/src/test_cases/xml/test_xml_image.c @@ -0,0 +1,53 @@ +#if LV_BUILD_TEST +#include "../lvgl.h" + +#include "unity/unity.h" + +void setUp(void) +{ + /* Function run before every test */ +} + +void tearDown(void) +{ + /* Function run after every test */ + lv_obj_clean(lv_screen_active()); +} + +void test_xml_tabview_with_attrs(void) +{ + LV_IMAGE_DECLARE(test_img_lvgl_logo_png); + lv_xml_register_image("logo", &test_img_lvgl_logo_png); + lv_obj_t * scr = lv_screen_active(); + + const char * image1_attrs[] = { + "src", "logo", + "rotation", "300", + "scale_x", "300", + "scale_y", "500", + "x", "50", + "y", "50", + NULL, NULL, + }; + + lv_xml_create(scr, "lv_image", image1_attrs); + + const char * image2_attrs[] = { + "src", "logo", + "inner_align", "bottom_right", + "style_bg_color", "0xff0000", + "style_bg_opa", "100", + "x", "200", + "y", "10", + "width", "150", + "height", "100", + NULL, NULL, + }; + + lv_xml_create(scr, "lv_image", image2_attrs); + + + TEST_ASSERT_EQUAL_SCREENSHOT("xml/lv_image.png"); +} + +#endif diff --git a/tests/src/test_cases/xml/test_xml_table.c b/tests/src/test_cases/xml/test_xml_table.c new file mode 100644 index 000000000..28ce82f39 --- /dev/null +++ b/tests/src/test_cases/xml/test_xml_table.c @@ -0,0 +1,69 @@ +#if LV_BUILD_TEST +#include "../lvgl.h" + +#include "unity/unity.h" + +void setUp(void) +{ + /* Function run before every test */ +} + +void tearDown(void) +{ + /* Function run after every test */ + lv_obj_clean(lv_screen_active()); +} + +void test_xml_table_with_attrs(void) +{ + lv_obj_t * scr = lv_screen_active(); + + const char * table_attrs[] = { + "width", "content", + "style_border_side:items", "full", + NULL, NULL, + }; + + lv_obj_t * table = lv_xml_create(scr, "lv_table", table_attrs); + lv_obj_center(table); + + const char * column_1_attrs[] = { + "column", "0", + "width", "80", + NULL, NULL, + }; + lv_xml_create(table, "lv_table-column", column_1_attrs); + + const char * cell_1_2_attrs[] = { + "row", "1", + "column", "2", + "value", "A", + NULL, NULL, + }; + lv_xml_create(table, "lv_table-cell", cell_1_2_attrs); + + + const char * cell_2_0_attrs[] = { + "row", "2", + "column", "0", + "value", "hello this a long text which should be cropped", + "ctrl", "text_crop merge_right", + NULL, NULL, + }; + + lv_xml_create(table, "lv_table-cell", cell_2_0_attrs); + + const char * cell_3_0_attrs[] = { + "row", "3", + "column", "0", + "value", "wrap this text", + NULL, NULL, + }; + + lv_xml_create(table, "lv_table-cell", cell_3_0_attrs); + + + TEST_ASSERT_EQUAL_SCREENSHOT("xml/lv_table.png"); +} + +#endif diff --git a/tests/src/test_cases/xml/test_xml_tabview.c b/tests/src/test_cases/xml/test_xml_tabview.c new file mode 100644 index 000000000..af334d548 --- /dev/null +++ b/tests/src/test_cases/xml/test_xml_tabview.c @@ -0,0 +1,72 @@ +#if LV_BUILD_TEST +#include "../lvgl.h" + +#include "unity/unity.h" + +void setUp(void) +{ + /* Function run before every test */ +} + +void tearDown(void) +{ + /* Function run after every test */ + lv_obj_clean(lv_screen_active()); +} + +void test_xml_tabview_with_attrs(void) +{ + lv_obj_t * scr = lv_screen_active(); + + const char * tabview_attrs[] = { + "width", "300", + "height", "250", + "tab_bar_position", "bottom", + "active", "1", + NULL, NULL, + }; + + lv_obj_t * tabview = lv_xml_create(scr, "lv_tabview", tabview_attrs); + lv_obj_center(tabview); + + const char * tabbar_attrs[] = { + "style_pad_left", "100", + "height", "100", + NULL, NULL, + }; + lv_obj_t * tabbar = lv_xml_create(tabview, "lv_tabview-tab_bar", tabbar_attrs); + + const char * tabbar_label_attrs[] = { + "ignore_layout", "true", + "text", "Hello\ntabview!", + "x", "-90", + "style_text_align", "right", + "align", "left_mid", + NULL, NULL, + }; + lv_xml_create(tabbar, "lv_label", tabbar_label_attrs); + + + const char * tab1_attrs[] = { + "text", "Tab1", + NULL, NULL, + }; + lv_obj_t * tab1 = lv_xml_create(tabview, "lv_tabview-tab", tab1_attrs); + + lv_obj_t * label1 = lv_label_create(tab1); + lv_label_set_text(label1, "This is the first tab"); + + const char * tab2_attrs[] = { + "text", "Tab2", + NULL, NULL, + }; + lv_obj_t * tab2 = lv_xml_create(tabview, "lv_tabview-tab", tab2_attrs); + + lv_obj_t * label2 = lv_label_create(tab2); + lv_label_set_text(label2, "This is the second tab"); + + + TEST_ASSERT_EQUAL_SCREENSHOT("xml/lv_tabview.png"); +} + +#endif diff --git a/xmls/globals.xml b/xmls/globals.xml index c4e6e7278..acbdad942 100644 --- a/xmls/globals.xml +++ b/xmls/globals.xml @@ -1,5 +1,3 @@ - - @@ -18,6 +16,7 @@ + @@ -41,5 +40,173 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xmls/lv_chart.xml b/xmls/lv_chart.xml index cc7ab250a..9b275c5e7 100644 --- a/xmls/lv_chart.xml +++ b/xmls/lv_chart.xml @@ -42,9 +42,7 @@ - - - + diff --git a/xmls/lv_dropdown.xml b/xmls/lv_dropdown.xml index 3c48ff553..0d756d59d 100644 --- a/xmls/lv_dropdown.xml +++ b/xmls/lv_dropdown.xml @@ -7,18 +7,10 @@ Example - - - - - - - - - - - - + + + + diff --git a/xmls/lv_image.xml b/xmls/lv_image.xml new file mode 100644 index 000000000..445df68a8 --- /dev/null +++ b/xmls/lv_image.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/xmls/lv_obj.xml b/xmls/lv_obj.xml index 9449e689b..70cf72abf 100644 --- a/xmls/lv_obj.xml +++ b/xmls/lv_obj.xml @@ -1,71 +1,92 @@ - - - + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/xmls/lv_table.xml b/xmls/lv_table.xml index 799f84a44..5eb148aa1 100644 --- a/xmls/lv_table.xml +++ b/xmls/lv_table.xml @@ -17,11 +17,10 @@ - - + - + @@ -39,12 +38,8 @@ - - - - - - + + diff --git a/xmls/lv_tabview.xml b/xmls/lv_tabview.xml index b7ddbc258..8aa360f7e 100644 --- a/xmls/lv_tabview.xml +++ b/xmls/lv_tabview.xml @@ -9,16 +9,12 @@ Example - - - - - - + + - +