/** * @file lv_xml.c * */ /********************* * INCLUDES *********************/ #include "lv_xml.h" #if LV_USE_XML #include "lv_xml_base_types.h" #include "lv_xml_parser.h" #include "lv_xml_component.h" #include "lv_xml_component_private.h" #include "lv_xml_widget.h" #include "lv_xml_style.h" #include "lv_xml.h" #include "lv_xml_utils.h" #include "lv_xml_private.h" #include "parsers/lv_xml_obj_parser.h" #include "parsers/lv_xml_button_parser.h" #include "parsers/lv_xml_label_parser.h" #include "parsers/lv_xml_image_parser.h" #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" /********************* * DEFINES *********************/ /********************** * TYPEDEFS **********************/ /********************** * STATIC PROTOTYPES **********************/ 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 **********************/ static lv_ll_t font_ll; static lv_ll_t image_ll; /********************** * MACROS **********************/ /********************** * GLOBAL FUNCTIONS **********************/ 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_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); lv_xml_widget_register("lv_image", lv_xml_image_create, lv_xml_image_apply); lv_xml_widget_register("lv_slider", lv_xml_slider_create, lv_xml_slider_apply); lv_xml_widget_register("lv_tabview", lv_xml_tabview_create, lv_xml_tabview_apply); lv_xml_widget_register("lv_tabview-tab_bar", lv_xml_tabview_tab_bar_create, lv_xml_tabview_tab_bar_apply); lv_xml_widget_register("lv_tabview-tab", lv_xml_tabview_tab_create, lv_xml_tabview_tab_apply); 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); } 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; lv_xml_parser_state_init(&state); state.ctx = *ctx; state.parent = parent; state.parent_attrs = attrs; state.parent_ctx = parent_ctx; lv_obj_t ** parent_node = lv_ll_ins_head(&state.parent_ll); *parent_node = parent; /* Create an XML parser and set handlers */ XML_Parser parser = XML_ParserCreate(NULL); XML_SetUserData(parser, &state); XML_SetElementHandler(parser, view_start_element_handler, view_end_element_handler); /* Parse the XML */ if(XML_Parse(parser, ctx->view_def, lv_strlen(ctx->view_def), XML_TRUE) == XML_STATUS_ERROR) { LV_LOG_WARN("XML parsing error: %s on line %lu", XML_ErrorString(XML_GetErrorCode(parser)), XML_GetCurrentLineNumber(parser)); XML_ParserFree(parser); return NULL; } state.item = state.view; if(attrs) { ctx->root_widget->apply_cb(&state, attrs); } lv_ll_clear(&state.parent_ll); XML_ParserFree(parser); return state.view; } void * lv_xml_create(lv_obj_t * parent, const char * name, const char ** attrs) { lv_obj_t * item = NULL; /* Select the widget specific parser type based on the name */ lv_widget_processor_t * p = lv_xml_widget_get_processor(name); if(p) { lv_xml_parser_state_t state; lv_xml_parser_state_init(&state); state.parent = parent; state.item = p->create_cb(&state, attrs); if(attrs) { p->apply_cb(&state, attrs); } return state.item; } lv_xml_component_ctx_t * ctx = lv_xml_component_get_ctx(name); if(ctx) { item = lv_xml_create_from_ctx(parent, NULL, ctx, attrs); return item; } /* If it isn't a component either then it is unknown */ LV_LOG_WARN("'%s' in not a known widget, element, or component", name); return NULL; } lv_result_t lv_xml_register_font(const char * name, const lv_font_t * font) { lv_xml_font_t * f = lv_ll_ins_head(&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) { lv_xml_font_t * f; LV_LL_READ(&font_ll, f) { if(lv_streq(f->name, name)) return f->font; } return NULL; } lv_result_t lv_xml_register_image(const char * name, const void * src) { lv_xml_image_t * img = lv_ll_ins_head(&image_ll); img->name = lv_strdup(name); if(lv_image_src_get_type(src) == LV_IMAGE_SRC_FILE) { img->src = lv_strdup(src); } else { img->src = src; } return LV_RESULT_OK; } const void * lv_xml_get_image(const char * name) { lv_xml_image_t * img; LV_LL_READ(&image_ll, img) { if(lv_streq(img->name, name)) return img->src; } return NULL; } /********************** * STATIC FUNCTIONS **********************/ static const char * get_param_type(lv_xml_component_ctx_t * ctx, const char * name) { lv_xml_param_t * p; LV_LL_READ(&ctx->param_ll, p) { if(lv_streq(p->name, name)) return p->type; } return NULL; } static const char * get_param_default(lv_xml_component_ctx_t * ctx, const char * name) { lv_xml_param_t * p; LV_LL_READ(&ctx->param_ll, p) { if(lv_streq(p->name, name)) return p->def; } return NULL; } static void resolve_params(lv_xml_component_ctx_t * item_ctx, lv_xml_component_ctx_t * parent_ctx, const char ** item_attrs, const char ** parent_attrs) { uint32_t i; for(i = 0; item_attrs[i]; i += 2) { const char * name = item_attrs[i]; const char * value = item_attrs[i + 1]; if(lv_streq(name, "styles")) continue; /*Styles will handle it themselves*/ if(value[0] == '$') { /*E.g. the ${my_color} value is the my_color attribute name on the parent*/ const char * name_clean = &value[1]; /*skips `$`*/ const char * type = get_param_type(item_ctx, name_clean); if(type == NULL) { LV_LOG_WARN("'%s' parameter is not defined on '%s'", name_clean, item_ctx->name); } const char * ext_value = lv_xml_get_value_of(parent_attrs, name_clean); if(ext_value) { /*If the value is not resolved earlier (e.g. it's a top level element created manually) * use the default value*/ if(ext_value[0] == '#' || ext_value[0] == '$') { ext_value = get_param_default(item_ctx, name_clean); } else if(lv_streq(type, "style")) { lv_xml_style_t * s = lv_xml_get_style_by_name(parent_ctx, ext_value); ext_value = s->long_name; } } else { /*If the API attribute is not provide don't set it*/ ext_value = get_param_default(item_ctx, name_clean); } if(ext_value) { item_attrs[i + 1] = ext_value; } else { /*Not set and no default value either *Don't set this property*/ item_attrs[i] = ""; item_attrs[i + 1] = ""; } } } } static void resolve_consts(const char ** item_attrs, lv_xml_component_ctx_t * ctx) { uint32_t i; for(i = 0; item_attrs[i]; i += 2) { const char * name = item_attrs[i]; const char * value = item_attrs[i + 1]; if(lv_streq(name, "styles")) continue; /*Styles will handle it themselves*/ 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; } } /*If the const attribute is not provide don't set it*/ if(c == NULL) { item_attrs[i] = ""; item_attrs[i + 1] = ""; } } } } static void view_start_element_handler(void * user_data, const char * name, const char ** attrs) { lv_xml_parser_state_t * state = (lv_xml_parser_state_t *)user_data; bool is_view = false; if(lv_streq(name, "view")) { const char * extends = lv_xml_get_value_of(attrs, "extends"); name = extends ? extends : "lv_obj"; is_view = true; } lv_obj_t ** current_parent_p = lv_ll_get_tail(&state->parent_ll); if(current_parent_p == NULL) { if(state->parent == NULL) { LV_LOG_ERROR("There is no parent object available for %s. This also should never happen.", name); return; } else { current_parent_p = &state->parent; } } else { state->parent = *current_parent_p; } /*In `state->attrs` we have parameters of the component creation *E.g. *In `attrs` we have the attributes of child of the view. *E.g. in `my_button` ` "text", "Hello" */ resolve_params(&state->ctx, state->parent_ctx, attrs, state->parent_attrs); resolve_consts(attrs, &state->ctx); void * item = NULL; /* Select the widget specific parser type based on the name */ lv_widget_processor_t * p = lv_xml_widget_get_processor(name); if(p) { item = p->create_cb(state, attrs); state->item = item; /*If it's a widget remove all styles. E.g. if it extends an `lv_button` *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 && is_view) lv_obj_remove_style_all(item); /*Apply the attributes from e.g. ``*/ if(item) { p->apply_cb(state, attrs); } } /* If not a widget, check if it is a component */ if(item == NULL) { item = lv_xml_component_process(state, name, attrs); state->item = item; } /* If it isn't a component either then it is unknown */ if(item == NULL) { LV_LOG_WARN("'%s' in not a known widget, element, or component", name); return; } void ** new_parent = lv_ll_ins_tail(&state->parent_ll); *new_parent = item; if(is_view) { state->view = item; } } static void view_end_element_handler(void * user_data, const char * name) { LV_UNUSED(name); lv_xml_parser_state_t * state = (lv_xml_parser_state_t *)user_data; lv_obj_t ** current_parent = lv_ll_get_tail(&state->parent_ll); if(current_parent) { lv_ll_remove(&state->parent_ll, current_parent); lv_free(current_parent); } } 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 */