diff --git a/docs/src/details/auxiliary-modules/xml/component_library.rst b/docs/src/details/auxiliary-modules/xml/component_library.rst index 7a2bd81b09..d91a5b52af 100644 --- a/docs/src/details/auxiliary-modules/xml/component_library.rst +++ b/docs/src/details/auxiliary-modules/xml/component_library.rst @@ -57,15 +57,34 @@ A typical structure for a component library looks like this: Visibility ********** -A component library can use images, fonts, components, widgets, etc., from other component libraries. -It is the user's responsibility to avoid naming conflicts by prefixing names. For example, all -data belonging to the LVGL core component library is prefixed by ``lv_`` (e.g., ``lv_label``, ``lv_montserrat_22``). +The content of all ``globals.xml`` files is part of a common global scope, and +any components, widgets, or screens can use data from there. + +Styles, constants, and other data defined in the XML file of components, widgets, or screens +are local to the given file. + +In this sense, there are two namespaces: + +1. **Local namespace** in the given XML file of components, widgets, and screens. +2. **Global namespace** created from the data in all ``globals.xml`` files. + +The referenced names are always checked first in the local namespace. +If not found there, the global namespace is also checked. + +The defined components, widgets, and screens are part of the global namespace, meaning +there cannot be two ``mybutton`` components. + +All data belonging to the LVGL core component library is prefixed by ``lv_`` +(e.g., ``lv_label``, ``lv_font_default``). + +A custom component can be prefixed with ``watch_``, ``small_``, ``light_``, or +anything else that the developer finds appropriate. -A custom component can be prefixed with ``watch_``, ``small_``, ``light_``, or anything else that the developer finds appropriate. LVGL's UI editor will show an error if there is a naming conflict. -globals.xml -*********** + +``globals.xml`` +*************** A ``globals.xml`` file should be created in each component library. The definitions in it do not belong to any specific widget but are available throughout the entire UI, widgets, and all XML files. diff --git a/docs/src/details/auxiliary-modules/xml/subjects.rst b/docs/src/details/auxiliary-modules/xml/subjects.rst index c63824a03b..8d1b35a167 100644 --- a/docs/src/details/auxiliary-modules/xml/subjects.rst +++ b/docs/src/details/auxiliary-modules/xml/subjects.rst @@ -4,4 +4,39 @@ Subjects ======== -TODO +T +Subjects +To connect values of the widget internally or to external data, subjects can be used. For example, an internally connected value could be a slider's value mapped to a label. Externally connected data could be the current number of users shown on a label. + +To handle internal connections, local subjects can be created like this: + + + + + + + +These subjects can be used in widget APIs like: + + + +When generating code, the subjects are saved in the widget's data and are used like this: + +lv_subject_init_int(&my_widget->subject_a, 20); +lv_subject_init_string(&my_widget->subject_b, "Hello"); + +my_widget->subject_a_and_b_list = lv_malloc(sizeof(lv_subject_t *) * 2); +my_widget->subject_a_and_b_list[0] = &my_widget->subject_a; +my_widget->subject_a_and_b_list[1] = &my_widget->subject_b; +lv_subject_init_group(&my_widget->subject_a_and_b, my_widget->subject_a_and_b_list); +If the connection is more complex and not supported out of the box, it can be handled from code. + +External subjects are defined in the API of the widget: + + + + + + + diff --git a/src/others/xml/lv_xml.c b/src/others/xml/lv_xml.c index 62b8c657d9..092670e352 100644 --- a/src/others/xml/lv_xml.c +++ b/src/others/xml/lv_xml.c @@ -33,6 +33,8 @@ #include "parsers/lv_xml_scale_parser.h" #include "parsers/lv_xml_buttonmatrix_parser.h" #include "parsers/lv_xml_spangroup_parser.h" +#include "parsers/lv_xml_textarea_parser.h" +#include "parsers/lv_xml_keyboard_parser.h" #include "parsers/lv_xml_event_parser.h" #include "../../libs/expat/expat.h" #include "../../draw/lv_draw_image.h" @@ -54,9 +56,6 @@ static void view_end_element_handler(void * user_data, const char * name); /********************** * STATIC VARIABLES **********************/ -static lv_ll_t font_ll; -static lv_ll_t image_ll; -static lv_ll_t event_cb_ll; /********************** * MACROS @@ -68,14 +67,10 @@ static lv_ll_t event_cb_ll; 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)); - lv_ll_init(&event_cb_ll, sizeof(lv_xml_event_cb_t)); - - lv_xml_register_font("lv_font_default", lv_font_default); - lv_xml_component_init(); + lv_xml_register_font(NULL, "lv_font_default", lv_font_default); + 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); @@ -100,6 +95,8 @@ void lv_xml_init(void) lv_xml_widget_register("lv_spangroup", lv_xml_spangroup_create, lv_xml_spangroup_apply); lv_xml_widget_register("lv_spangroup-span", lv_xml_spangroup_span_create, lv_xml_spangroup_span_apply); lv_xml_widget_register("lv_buttonmatrix", lv_xml_buttonmatrix_create, lv_xml_buttonmatrix_apply); + lv_xml_widget_register("lv_textarea", lv_xml_textarea_create, lv_xml_textarea_apply); + lv_xml_widget_register("lv_keyboard", lv_xml_keyboard_create, lv_xml_keyboard_apply); lv_xml_widget_register("lv_event-call_function", lv_xml_event_call_function_create, lv_xml_event_call_function_apply); } @@ -153,6 +150,7 @@ void * lv_xml_create(lv_obj_t * parent, const char * name, const char ** attrs) lv_xml_parser_state_t state; lv_xml_parser_state_init(&state); state.parent = parent; + state.ctx.name = ""; state.item = p->create_cb(&state, attrs); if(attrs) { p->apply_cb(&state, attrs); @@ -172,29 +170,137 @@ void * lv_xml_create(lv_obj_t * parent, const char * name, const char ** attrs) } -lv_result_t lv_xml_register_font(const char * name, const lv_font_t * font) +lv_result_t lv_xml_register_font(lv_xml_component_ctx_t * ctx, const char * name, const lv_font_t * font) { - lv_xml_font_t * f = lv_ll_ins_head(&font_ll); + if(ctx == NULL) ctx = lv_xml_component_get_ctx("globals"); + if(ctx == NULL) { + LV_LOG_WARN("No component found to register font `%s`", name); + return LV_RESULT_INVALID; + } + + lv_xml_font_t * f = lv_ll_ins_head(&ctx->font_ll); f->name = lv_strdup(name); f->font = font; return LV_RESULT_OK; } -const lv_font_t * lv_xml_get_font(const char * name) +const lv_font_t * lv_xml_get_font(lv_xml_component_ctx_t * ctx, const char * name) { lv_xml_font_t * f; - LV_LL_READ(&font_ll, f) { - if(lv_streq(f->name, name)) return f->font; + if(ctx) { + LV_LL_READ(&ctx->font_ll, f) { + if(lv_streq(f->name, name)) return f->font; + } + } + + /*If not found in the component check the global space*/ + if(!lv_streq(ctx->name, "globals")) { + ctx = lv_xml_component_get_ctx("globals"); + if(ctx) { + LV_LL_READ(&ctx->font_ll, f) { + if(lv_streq(f->name, name)) return f->font; + } + } } LV_LOG_WARN("No font was found with name \"%s\". Using LV_FONT_DEFAULT instead.", name); - return LV_FONT_DEFAULT; + return lv_font_get_default(); } -lv_result_t lv_xml_register_image(const char * name, const void * src) +lv_result_t lv_xml_register_subject(lv_xml_component_ctx_t * ctx, const char * name, lv_subject_t * subject) { - lv_xml_image_t * img = lv_ll_ins_head(&image_ll); + if(ctx == NULL) ctx = lv_xml_component_get_ctx("globals"); + if(ctx == NULL) { + LV_LOG_WARN("No component found to register subject `%s`", name); + return LV_RESULT_INVALID; + } + + lv_xml_subject_t * s = lv_ll_ins_head(&ctx->subjects_ll); + s->name = lv_strdup(name); + s->subject = subject; + + return LV_RESULT_OK; +} + +lv_subject_t * lv_xml_get_subject(lv_xml_component_ctx_t * ctx, const char * name) +{ + lv_xml_subject_t * s; + if(ctx) { + LV_LL_READ(&ctx->subjects_ll, s) { + if(lv_streq(s->name, name)) return s->subject; + } + } + + /*If not found in the component check the global space*/ + if(!lv_streq(ctx->name, "globals")) { + ctx = lv_xml_component_get_ctx("globals"); + if(ctx) { + LV_LL_READ(&ctx->subjects_ll, s) { + if(lv_streq(s->name, name)) return s->subject; + } + } + } + + LV_LOG_WARN("No subject was found with name \"%s\".", name); + return NULL; +} + +lv_result_t lv_xml_register_const(lv_xml_component_ctx_t * ctx, const char * name, const char * value) +{ + if(ctx == NULL) ctx = lv_xml_component_get_ctx("globals"); + if(ctx == NULL) { + LV_LOG_WARN("No component found to register constant `%s`", name); + return LV_RESULT_INVALID; + } + + lv_xml_const_t * cnst; + cnst = lv_ll_ins_head(&ctx->const_ll); + + cnst->name = lv_strdup(name); + cnst->value = lv_strdup(value); + + return LV_RESULT_OK; +} + +const char * lv_xml_get_const(lv_xml_component_ctx_t * ctx, const char * name) +{ + + if(ctx == NULL) ctx = lv_xml_component_get_ctx("globals"); + if(ctx == NULL) return LV_RESULT_INVALID; + + lv_xml_const_t * cnst; + if(ctx) { + LV_LL_READ(&ctx->const_ll, cnst) { + if(lv_streq(cnst->name, name)) return cnst->value; + } + } + + /*If not found in the component check the global space*/ + if(!lv_streq(ctx->name, "globals")) { + ctx = lv_xml_component_get_ctx("globals"); + if(ctx) { + LV_LL_READ(&ctx->const_ll, cnst) { + if(lv_streq(cnst->name, name)) return cnst->value; + } + } + } + + + LV_LOG_WARN("No constant was found with name \"%s\".", name); + return NULL; +} + + +lv_result_t lv_xml_register_image(lv_xml_component_ctx_t * ctx, const char * name, const void * src) +{ + if(ctx == NULL) ctx = lv_xml_component_get_ctx("globals"); + if(ctx == NULL) { + LV_LOG_WARN("No component found to register image `%s`", name); + return LV_RESULT_INVALID; + } + + lv_xml_image_t * img = lv_ll_ins_head(&ctx->image_ll); img->name = lv_strdup(name); if(lv_image_src_get_type(src) == LV_IMAGE_SRC_FILE) { img->src = lv_strdup(src); @@ -206,20 +312,41 @@ lv_result_t lv_xml_register_image(const char * name, const void * src) return LV_RESULT_OK; } -const void * lv_xml_get_image(const char * name) +const void * lv_xml_get_image(lv_xml_component_ctx_t * ctx, const char * name) { + if(ctx == NULL) ctx = lv_xml_component_get_ctx("globals"); + if(ctx == NULL) return LV_RESULT_INVALID; + lv_xml_image_t * img; - LV_LL_READ(&image_ll, img) { - if(lv_streq(img->name, name)) return img->src; + if(ctx) { + LV_LL_READ(&ctx->image_ll, img) { + if(lv_streq(img->name, name)) return img->src; + } + } + + /*If not found in the component check the global space*/ + if(!lv_streq(ctx->name, "globals")) { + ctx = lv_xml_component_get_ctx("globals"); + if(ctx) { + LV_LL_READ(&ctx->image_ll, img) { + if(lv_streq(img->name, name)) return img->src; + } + } } LV_LOG_WARN("No image was found with name \"%s\"", name); return NULL; } -lv_result_t lv_xml_register_event_cb(const char * name, lv_event_cb_t cb) +lv_result_t lv_xml_register_event_cb(lv_xml_component_ctx_t * ctx, const char * name, lv_event_cb_t cb) { - lv_xml_event_cb_t * e = lv_ll_ins_head(&event_cb_ll); + if(ctx == NULL) ctx = lv_xml_component_get_ctx("globals"); + if(ctx == NULL) { + LV_LOG_WARN("No component found to register event `%s`", name); + return LV_RESULT_INVALID; + } + + lv_xml_event_cb_t * e = lv_ll_ins_head(&ctx->event_ll); e->name = lv_strdup(name); e->cb = cb; @@ -227,14 +354,29 @@ lv_result_t lv_xml_register_event_cb(const char * name, lv_event_cb_t cb) } -lv_event_cb_t lv_xml_get_event_cb(const char * name) +lv_event_cb_t lv_xml_get_event_cb(lv_xml_component_ctx_t * ctx, const char * name) { + if(ctx == NULL) ctx = lv_xml_component_get_ctx("globals"); + if(ctx == NULL) return LV_RESULT_INVALID; + lv_xml_event_cb_t * e; - LV_LL_READ(&event_cb_ll, e) { - if(lv_streq(e->name, name)) return e->cb; + if(ctx) { + LV_LL_READ(&ctx->event_ll, e) { + if(lv_streq(e->name, name)) return e->cb; + } } - LV_LOG_WARN("No event_cb was found with name \"%s\"", name); + /*If not found in the component check the global space*/ + if(!lv_streq(ctx->name, "globals")) { + ctx = lv_xml_component_get_ctx("globals"); + if(ctx) { + LV_LL_READ(&ctx->event_ll, e) { + if(lv_streq(e->name, name)) return e->cb; + } + } + } + + LV_LOG_WARN("No event was found with name \"%s\"", name); return NULL; } @@ -315,16 +457,12 @@ static void resolve_consts(const char ** item_attrs, lv_xml_component_ctx_t * ct if(value[0] == '#') { const char * value_clean = &value[1]; - lv_xml_const_t * c; - LV_LL_READ(&ctx->const_ll, c) { - if(lv_streq(c->name, value_clean)) { - item_attrs[i + 1] = c->value; - break; - } + const char * const_value = lv_xml_get_const(ctx, value_clean); + if(const_value) { + item_attrs[i + 1] = const_value; } - /*If the const attribute is not provide don't set it*/ - if(c == NULL) { + else { item_attrs[i] = ""; item_attrs[i + 1] = ""; } diff --git a/src/others/xml/lv_xml.h b/src/others/xml/lv_xml.h index ec059e9971..9ef2d44f90 100644 --- a/src/others/xml/lv_xml.h +++ b/src/others/xml/lv_xml.h @@ -15,6 +15,8 @@ extern "C" { *********************/ #include "../../misc/lv_types.h" #include "../../misc/lv_event.h" +#include "../../others/observer/lv_observer.h" + #if LV_USE_XML /********************* @@ -36,18 +38,39 @@ void * lv_xml_create(lv_obj_t * parent, const char * name, 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); +lv_result_t lv_xml_register_font(lv_xml_component_ctx_t * ctx, const char * name, const lv_font_t * font); -const lv_font_t * lv_xml_get_font(const char * name); +const lv_font_t * lv_xml_get_font(lv_xml_component_ctx_t * ctx, const char * name); -lv_result_t lv_xml_register_image(const char * name, const void * src); +lv_result_t lv_xml_register_image(lv_xml_component_ctx_t * ctx, const char * name, const void * src); -const void * lv_xml_get_image(const char * name); +const void * lv_xml_get_image(lv_xml_component_ctx_t * ctx, const char * name); +/** + * Map globally available subject name to an actual subject variable + * @param name name of the subject + * @param subject pointer to a subject + * @return `LV_RESULT_OK`: success + */ +lv_result_t lv_xml_register_subject(lv_xml_component_ctx_t * ctx, const char * name, lv_subject_t * subject); -lv_result_t lv_xml_register_event_cb(const char * name, lv_event_cb_t cb); +/** + * Get a subject by name. + * @param ctx If specified start searching in that component's subject list, + * and if not found search in the global space. + * If `NULL` search in global space immediately. + * @param name Name of the subject to find. + * @return Pointer to the subject or NULL if not found. + */ +lv_subject_t * lv_xml_get_subject(lv_xml_component_ctx_t * ctx, const char * name); -lv_event_cb_t lv_xml_get_event_cb(const char * name); +lv_result_t lv_xml_register_const(lv_xml_component_ctx_t * ctx, const char * name, const char * value); + +const char * lv_xml_get_const(lv_xml_component_ctx_t * ctx, const char * name); + +lv_result_t lv_xml_register_event_cb(lv_xml_component_ctx_t * ctx, const char * name, lv_event_cb_t cb); + +lv_event_cb_t lv_xml_get_event_cb(lv_xml_component_ctx_t * ctx, const char * name); /********************** * MACROS diff --git a/src/others/xml/lv_xml_component.c b/src/others/xml/lv_xml_component.c index 78ad59dd84..fa55aa6c07 100644 --- a/src/others/xml/lv_xml_component.c +++ b/src/others/xml/lv_xml_component.c @@ -54,6 +54,24 @@ static lv_ll_t component_ctx_ll; void lv_xml_component_init(void) { lv_ll_init(&component_ctx_ll, sizeof(lv_xml_component_ctx_t)); + + lv_xml_component_ctx_t * global_ctx = lv_ll_ins_head(&component_ctx_ll); + lv_memzero(global_ctx, sizeof(lv_xml_component_ctx_t)); + lv_xml_component_ctx_init(global_ctx); + global_ctx->name = lv_strdup("globals"); + +} + +void lv_xml_component_ctx_init(lv_xml_component_ctx_t * ctx) +{ + lv_ll_init(&ctx->style_ll, sizeof(lv_xml_style_t)); + lv_ll_init(&ctx->const_ll, sizeof(lv_xml_const_t)); + lv_ll_init(&ctx->param_ll, sizeof(lv_xml_param_t)); + lv_ll_init(&ctx->gradient_ll, sizeof(lv_xml_grad_t)); + lv_ll_init(&ctx->subjects_ll, sizeof(lv_xml_subject_t)); + lv_ll_init(&ctx->event_ll, sizeof(lv_xml_event_cb_t)); + lv_ll_init(&ctx->image_ll, sizeof(lv_xml_image_t)); + lv_ll_init(&ctx->font_ll, sizeof(lv_xml_font_t)); } @@ -86,10 +104,19 @@ lv_xml_component_ctx_t * lv_xml_component_get_ctx(const char * component_name) lv_result_t lv_xml_component_register_from_data(const char * name, const char * xml_def) { + bool globals = false; + if(lv_streq(name, "globals")) globals = true; + /* Create a temporary parser state to extract styles/params/consts */ lv_xml_parser_state_t state; - lv_xml_parser_state_init(&state); - state.ctx.name = name; + if(globals) { + lv_xml_component_ctx_t * global_ctx = lv_xml_component_get_ctx("globals"); + state.ctx = *global_ctx; + } + else { + lv_xml_parser_state_init(&state); + state.ctx.name = name; + } /* Parse the XML to extract metadata */ XML_Parser parser = XML_ParserCreate(NULL); @@ -106,19 +133,26 @@ lv_result_t lv_xml_component_register_from_data(const char * name, const char * XML_ParserFree(parser); - /* Copy extracted metadata to component processor */ - lv_xml_component_ctx_t * ctx = lv_ll_ins_head(&component_ctx_ll); - lv_memzero(ctx, sizeof(lv_xml_component_ctx_t)); - lv_memcpy(ctx, &state.ctx, sizeof(lv_xml_component_ctx_t)); - /* Extract view content directly instead of using XML parser */ - ctx->view_def = extract_view_content(xml_def); - ctx->name = lv_strdup(name); - if(!ctx->view_def) { - LV_LOG_WARN("Failed to extract view content"); - /* Clean up and return error */ - lv_free(ctx); - return LV_RESULT_INVALID; + /* Copy extracted metadata to component processor */ + if(globals) { + lv_xml_component_ctx_t * global_ctx = lv_xml_component_get_ctx("globals"); + lv_memcpy(global_ctx, &state.ctx, sizeof(lv_xml_component_ctx_t)); + } + else { + lv_xml_component_ctx_t * ctx = lv_ll_ins_head(&component_ctx_ll); + lv_memzero(ctx, sizeof(lv_xml_component_ctx_t)); + lv_memcpy(ctx, &state.ctx, sizeof(lv_xml_component_ctx_t)); + + /* Extract view content directly instead of using XML parser */ + ctx->view_def = extract_view_content(xml_def); + ctx->name = lv_strdup(name); + if(!ctx->view_def) { + LV_LOG_WARN("Failed to extract view content"); + /* Clean up and return error */ + lv_free(ctx); + return LV_RESULT_INVALID; + } } return LV_RESULT_OK; @@ -223,6 +257,17 @@ lv_result_t lv_xml_component_unregister(const char * name) } lv_ll_clear(&ctx->gradient_ll); + lv_xml_subject_t * subject; + LV_LL_READ(&ctx->subjects_ll, subject) { + lv_free((char *)subject->name); + if(subject->subject->type == LV_SUBJECT_TYPE_STRING) { + lv_free((char *)subject->subject->prev_value.pointer); + lv_free((char *)subject->subject->value.pointer); + } + lv_free(subject->subject); + } + lv_ll_clear(&ctx->subjects_ll); + lv_free(ctx); return LV_RESULT_OK; @@ -246,14 +291,40 @@ static void process_const_element(lv_xml_parser_state_t * state, const char ** a return; } - lv_xml_const_t * cnst = lv_ll_ins_tail(&state->ctx.const_ll); - cnst->name = lv_strdup(name); - cnst->value = lv_strdup(value); + lv_xml_register_const(&state->ctx, name, value); +} + +static void process_subject_element(lv_xml_parser_state_t * state, const char * type, const char ** attrs) +{ + const char * name = lv_xml_get_value_of(attrs, "name"); + const char * value = lv_xml_get_value_of(attrs, "value"); + + if(name == NULL) { + LV_LOG_WARN("'name' is missing from a subject"); + return; + } + if(value == NULL) { + LV_LOG_WARN("'value' is missing from a subject"); + return; + } + + lv_subject_t * subject = lv_malloc(sizeof(lv_subject_t)); + + + if(lv_streq(type, "int")) lv_subject_init_int(subject, lv_xml_atoi(value)); + else if(lv_streq(type, "color")) lv_subject_init_color(subject, lv_xml_to_color(value)); + else if(lv_streq(type, "string")) { + /*Simple solution for now. Will be improved later*/ + char * buf_prev = lv_malloc(256); + char * buf_act = lv_malloc(256); + lv_subject_init_string(subject, buf_act, buf_prev, 256, value); + } + + lv_xml_register_subject(&state->ctx, name, subject); } static void process_grad_element(lv_xml_parser_state_t * state, const char * tag_name, const char ** attrs) { - lv_xml_grad_t * grad = lv_ll_ins_tail(&state->ctx.gradient_ll); grad->name = lv_strdup(lv_xml_get_value_of(attrs, "name")); lv_grad_dsc_t * dsc = &grad->grad_dsc; @@ -470,6 +541,11 @@ static void start_metadata_handler(void * user_data, const char * name, const ch lv_xml_style_register(&state->ctx, attrs); break; + case LV_XML_PARSER_SECTION_SUBJECTS: + if(old_section != state->section) return; /*Ignore the section opening, e.g. */ + process_subject_element(state, name, attrs); + break; + default: break; } diff --git a/src/others/xml/lv_xml_component_private.h b/src/others/xml/lv_xml_component_private.h index 069758c127..1f53c4f57c 100644 --- a/src/others/xml/lv_xml_component_private.h +++ b/src/others/xml/lv_xml_component_private.h @@ -19,6 +19,7 @@ extern "C" { #include "lv_xml_utils.h" #include "../../misc/lv_ll.h" #include "../../misc/lv_style.h" +#include "../../others/observer/lv_observer.h" /********************** * TYPEDEFS @@ -32,6 +33,10 @@ struct _lv_xml_component_ctx_t { lv_ll_t const_ll; lv_ll_t param_ll; lv_ll_t gradient_ll; + lv_ll_t subjects_ll; + lv_ll_t font_ll; + lv_ll_t image_ll; + lv_ll_t event_ll; const char * view_def; struct _lv_widget_processor_t * root_widget; uint32_t is_widget : 1; /*1: not component but widget registered as a component for preview*/ @@ -43,6 +48,11 @@ typedef struct { const char * value; } lv_xml_const_t; +typedef struct { + const char * name; + lv_subject_t * subject; +} lv_xml_subject_t; + typedef struct { const char * name; const char * def; @@ -63,6 +73,12 @@ typedef struct { */ void lv_xml_component_init(void); +/** + * Initialize the linked lists of a component context + * @param ctx pointer to a component contexts + */ +void lv_xml_component_ctx_init(lv_xml_component_ctx_t * ctx); + /********************** * MACROS **********************/ diff --git a/src/others/xml/lv_xml_parser.c b/src/others/xml/lv_xml_parser.c index 5c19423379..bb2e425c45 100644 --- a/src/others/xml/lv_xml_parser.c +++ b/src/others/xml/lv_xml_parser.c @@ -42,11 +42,8 @@ void lv_xml_parser_state_init(lv_xml_parser_state_t * state) { lv_memzero(state, sizeof(lv_xml_parser_state_t)); - lv_ll_init(&state->ctx.style_ll, sizeof(lv_xml_style_t)); - lv_ll_init(&state->ctx.const_ll, sizeof(lv_xml_const_t)); - lv_ll_init(&state->ctx.param_ll, sizeof(lv_xml_param_t)); - lv_ll_init(&state->ctx.gradient_ll, sizeof(lv_xml_grad_t)); lv_ll_init(&state->parent_ll, sizeof(lv_obj_t *)); + lv_xml_component_ctx_init(&state->ctx); } void lv_xml_parser_start_section(lv_xml_parser_state_t * state, const char * name) @@ -72,6 +69,10 @@ void lv_xml_parser_start_section(lv_xml_parser_state_t * state, const char * nam state->section = LV_XML_PARSER_SECTION_STYLES; return; } + else if(lv_streq(name, "subjects")) { + state->section = LV_XML_PARSER_SECTION_SUBJECTS; + return; + } else if(lv_streq(name, "view")) { state->section = LV_XML_PARSER_SECTION_VIEW; return; diff --git a/src/others/xml/lv_xml_parser.h b/src/others/xml/lv_xml_parser.h index e040ed23cc..a7956cd36d 100644 --- a/src/others/xml/lv_xml_parser.h +++ b/src/others/xml/lv_xml_parser.h @@ -35,6 +35,7 @@ typedef enum { LV_XML_PARSER_SECTION_GRAD, LV_XML_PARSER_SECTION_GRAD_STOP, LV_XML_PARSER_SECTION_STYLES, + LV_XML_PARSER_SECTION_SUBJECTS, LV_XML_PARSER_SECTION_VIEW } lv_xml_parser_section_t; diff --git a/src/others/xml/lv_xml_style.c b/src/others/xml/lv_xml_style.c index b2490f3297..e15906b2c8 100644 --- a/src/others/xml/lv_xml_style.c +++ b/src/others/xml/lv_xml_style.c @@ -76,14 +76,19 @@ lv_part_t lv_xml_style_part_to_enum(const char * txt) return 0; /*Return 0 in lack of a better option. */ } -void lv_xml_style_register(lv_xml_component_ctx_t * ctx, const char ** attrs) +lv_result_t lv_xml_style_register(lv_xml_component_ctx_t * ctx, const char ** attrs) { const char * style_name = lv_xml_get_value_of(attrs, "name"); if(style_name == NULL) { LV_LOG_WARN("'name' is missing from a style"); - return; + return LV_RESULT_INVALID; } + + if(ctx == NULL) ctx = lv_xml_component_get_ctx("globals"); + if(ctx == NULL) return LV_RESULT_INVALID; + lv_xml_style_t * xml_style = lv_ll_ins_tail(&ctx->style_ll); + lv_style_t * style = &xml_style->style; lv_style_init(style); xml_style->name = lv_strdup(style_name); @@ -146,7 +151,7 @@ void lv_xml_style_register(lv_xml_component_ctx_t * ctx, const char ** attrs) else SET_STYLE_IF(bg_grad_stop, lv_xml_atoi(value)); else SET_STYLE_IF(bg_grad, lv_xml_component_get_grad(ctx, value)); - else SET_STYLE_IF(bg_image_src, lv_xml_get_image(value)); + else SET_STYLE_IF(bg_image_src, lv_xml_get_image(ctx, 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)); @@ -170,7 +175,7 @@ void lv_xml_style_register(lv_xml_component_ctx_t * ctx, const char ** attrs) 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_font, lv_xml_get_font(ctx, 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)); @@ -192,7 +197,7 @@ void lv_xml_style_register(lv_xml_component_ctx_t * ctx, const char ** attrs) 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(arc_image_src, lv_xml_get_image(ctx, value)); else SET_STYLE_IF(opa, lv_xml_to_opa(value)); else SET_STYLE_IF(opa_layered, lv_xml_to_opa(value)); @@ -210,7 +215,7 @@ void lv_xml_style_register(lv_xml_component_ctx_t * ctx, const char ** attrs) 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(bitmap_mask_src, lv_xml_get_image(ctx, value)); else SET_STYLE_IF(rotary_sensitivity, lv_xml_atoi(value)); else SET_STYLE_IF(layout, lv_xml_layout_to_enum(value)); @@ -235,6 +240,8 @@ void lv_xml_style_register(lv_xml_component_ctx_t * ctx, const char ** attrs) LV_LOG_WARN("%s style property is not supported", name); } } + + return LV_RESULT_OK; } const char * lv_xml_style_string_process(char * txt, lv_style_selector_t * selector) @@ -313,6 +320,16 @@ lv_xml_style_t * lv_xml_get_style_by_name(lv_xml_component_ctx_t * ctx, const ch if(lv_streq(xml_style->name, style_name)) return xml_style; } + /*If not found in the component check the global space*/ + if(!lv_streq(ctx->name, "globals")) { + ctx = lv_xml_component_get_ctx("globals"); + if(ctx) { + LV_LL_READ(&ctx->style_ll, xml_style) { + if(lv_streq(xml_style->name, style_name)) return xml_style; + } + } + } + LV_LOG_WARN("No style found with %s name", style_name_raw); return NULL; diff --git a/src/others/xml/lv_xml_style.h b/src/others/xml/lv_xml_style.h index 5f8f401076..af1e3b3ffa 100644 --- a/src/others/xml/lv_xml_style.h +++ b/src/others/xml/lv_xml_style.h @@ -38,7 +38,7 @@ typedef struct _lv_xml_style_t { * @param ctx add styles here. (Constants should be already added as style properties might use them) * @param attrs list of attribute names and values */ -void lv_xml_style_register(lv_xml_component_ctx_t * ctx, const char ** attrs); +lv_result_t lv_xml_style_register(lv_xml_component_ctx_t * ctx, const char ** attrs); /** * Add the styles to an object. Handles multiple styles and selectors too. diff --git a/src/others/xml/parsers/lv_xml_dropdown_parser.c b/src/others/xml/parsers/lv_xml_dropdown_parser.c index ffa32e3518..8d5644d5ff 100644 --- a/src/others/xml/parsers/lv_xml_dropdown_parser.c +++ b/src/others/xml/parsers/lv_xml_dropdown_parser.c @@ -58,7 +58,7 @@ void lv_xml_dropdown_apply(lv_xml_parser_state_t * state, const char ** attrs) 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)); + if(lv_streq("symbol", name)) lv_dropdown_set_symbol(item, lv_xml_get_image(&state->ctx, value)); } } diff --git a/src/others/xml/parsers/lv_xml_event_parser.c b/src/others/xml/parsers/lv_xml_event_parser.c index 5aef5e33db..87e635ce46 100644 --- a/src/others/xml/parsers/lv_xml_event_parser.c +++ b/src/others/xml/parsers/lv_xml_event_parser.c @@ -48,7 +48,7 @@ void * lv_xml_event_call_function_create(lv_xml_parser_state_t * state, const ch return NULL; } - lv_event_cb_t cb = lv_xml_get_event_cb(cb_txt); + lv_event_cb_t cb = lv_xml_get_event_cb(&state->ctx, cb_txt); if(cb == NULL) { LV_LOG_WARN("Couldn't add call function event because \"%s\" callback is not found.", cb_txt); return NULL; diff --git a/src/others/xml/parsers/lv_xml_image_parser.c b/src/others/xml/parsers/lv_xml_image_parser.c index ff1d0e6c6a..b9e097bd20 100644 --- a/src/others/xml/parsers/lv_xml_image_parser.c +++ b/src/others/xml/parsers/lv_xml_image_parser.c @@ -60,7 +60,7 @@ void lv_xml_image_apply(lv_xml_parser_state_t * state, const char ** attrs) const char * name = attrs[i]; const char * value = attrs[i + 1]; - if(lv_streq("src", name)) lv_image_set_src(item, lv_xml_get_image(value)); + if(lv_streq("src", name)) lv_image_set_src(item, lv_xml_get_image(&state->ctx, 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)); diff --git a/src/others/xml/parsers/lv_xml_label_parser.c b/src/others/xml/parsers/lv_xml_label_parser.c index a861c29315..fd078c68ee 100644 --- a/src/others/xml/parsers/lv_xml_label_parser.c +++ b/src/others/xml/parsers/lv_xml_label_parser.c @@ -57,6 +57,7 @@ void lv_xml_label_apply(lv_xml_parser_state_t * state, const char ** attrs) if(lv_streq("text", name)) lv_label_set_text(item, value); if(lv_streq("long_mode", name)) lv_label_set_long_mode(item, long_mode_text_to_enum_value(value)); + if(lv_streq("bind_text", name)) lv_label_bind_text(item, lv_xml_get_subject(&state->ctx, value), NULL); } } @@ -68,6 +69,9 @@ static lv_label_long_mode_t long_mode_text_to_enum_value(const char * txt) { if(lv_streq("wrap", txt)) return LV_LABEL_LONG_MODE_WRAP; if(lv_streq("scroll", txt)) return LV_LABEL_LONG_MODE_SCROLL; + if(lv_streq("scroll_circular", txt)) return LV_LABEL_LONG_MODE_SCROLL_CIRCULAR; + if(lv_streq("dots", txt)) return LV_LABEL_LONG_MODE_DOTS; + if(lv_streq("clip", txt)) return LV_LABEL_LONG_MODE_CLIP; LV_LOG_WARN("%s is an unknown value for label's long_mode", txt); return 0; /*Return 0 in lack of a better option. */ diff --git a/src/others/xml/parsers/lv_xml_obj_parser.c b/src/others/xml/parsers/lv_xml_obj_parser.c index 851e92fb3c..845cc5c60a 100644 --- a/src/others/xml/parsers/lv_xml_obj_parser.c +++ b/src/others/xml/parsers/lv_xml_obj_parser.c @@ -163,7 +163,7 @@ static void apply_styles(lv_xml_parser_state_t * state, lv_obj_t * obj, const ch else SET_STYLE_IF(bg_grad_stop, lv_xml_atoi(value)); else SET_STYLE_IF(bg_grad, lv_xml_component_get_grad(&state->ctx, value)); - else SET_STYLE_IF(bg_image_src, lv_xml_get_image(value)); + else SET_STYLE_IF(bg_image_src, lv_xml_get_image(&state->ctx, 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)); @@ -187,7 +187,7 @@ static void apply_styles(lv_xml_parser_state_t * state, lv_obj_t * obj, const ch 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_font, lv_xml_get_font(&state->ctx, 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)); @@ -209,7 +209,7 @@ static void apply_styles(lv_xml_parser_state_t * state, lv_obj_t * obj, const ch 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(arc_image_src, lv_xml_get_image(&state->ctx, value)); else SET_STYLE_IF(opa, lv_xml_to_opa(value)); else SET_STYLE_IF(opa_layered, lv_xml_to_opa(value)); @@ -227,7 +227,7 @@ static void apply_styles(lv_xml_parser_state_t * state, lv_obj_t * obj, const ch 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(bitmap_mask_src, lv_xml_get_image(&state->ctx, value)); else SET_STYLE_IF(rotary_sensitivity, lv_xml_atoi(value)); else SET_STYLE_IF(layout, lv_xml_layout_to_enum(value)); diff --git a/src/others/xml/parsers/lv_xml_slider_parser.c b/src/others/xml/parsers/lv_xml_slider_parser.c index 347107ffef..a974900a96 100644 --- a/src/others/xml/parsers/lv_xml_slider_parser.c +++ b/src/others/xml/parsers/lv_xml_slider_parser.c @@ -63,6 +63,7 @@ void lv_xml_slider_apply(lv_xml_parser_state_t * state, const char ** attrs) bool v2 = lv_xml_to_bool(buf_p); lv_bar_set_value(item, v1, v2); } + if(lv_streq("bind_value", name)) lv_slider_bind_value(item, lv_xml_get_subject(&state->ctx, value)); if(lv_streq("start_value", name)) { char buf[64]; lv_strlcpy(buf, value, sizeof(buf)); @@ -71,7 +72,6 @@ void lv_xml_slider_apply(lv_xml_parser_state_t * state, const char ** attrs) bool v2 = lv_xml_to_bool(buf_p); lv_bar_set_start_value(item, v1, v2); } - if(lv_streq("orientation", name)) lv_slider_set_orientation(item, orentation_text_to_enum_value(value)); if(lv_streq("mode", name)) lv_slider_set_mode(item, mode_text_to_enum_value(value)); if(lv_streq("range_min", name)) lv_slider_set_range(item, lv_xml_atoi(value), lv_slider_get_max_value(item)); diff --git a/tests/ref_imgs/xml/lv_label.png b/tests/ref_imgs/xml/lv_label.png new file mode 100644 index 0000000000..0cd970a540 Binary files /dev/null and b/tests/ref_imgs/xml/lv_label.png differ diff --git a/tests/ref_imgs/xml/view3.png b/tests/ref_imgs/xml/view3.png new file mode 100644 index 0000000000..c926ddaa8b Binary files /dev/null and b/tests/ref_imgs/xml/view3.png differ diff --git a/tests/ref_imgs_vg_lite/xml/lv_label.png b/tests/ref_imgs_vg_lite/xml/lv_label.png new file mode 100644 index 0000000000..bab25a1daf Binary files /dev/null and b/tests/ref_imgs_vg_lite/xml/lv_label.png differ diff --git a/tests/ref_imgs_vg_lite/xml/view3.png b/tests/ref_imgs_vg_lite/xml/view3.png new file mode 100644 index 0000000000..43695b7ec7 Binary files /dev/null and b/tests/ref_imgs_vg_lite/xml/view3.png differ diff --git a/tests/src/test_assets/xml/globals.xml b/tests/src/test_assets/xml/globals.xml new file mode 100644 index 0000000000..9b6782b620 --- /dev/null +++ b/tests/src/test_assets/xml/globals.xml @@ -0,0 +1,15 @@ + + + + + + + + +