feat(xml): add subject and global/local scoping support

This commit is contained in:
Gabor Kiss-Vamosi
2025-03-06 17:15:47 +01:00
parent b63472dc01
commit 2ca425c411
28 changed files with 526 additions and 93 deletions
@@ -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.
@@ -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:
<subjects>
<int name="a" value="20"/>
<string name="b" value="Hello"/>
<group name="a_and_b" value="a b"/>
</subjects>
These subjects can be used in widget APIs like:
<view>
<label bind_text="a 'Progress: %d'"/>
</view>
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:
<api>
<prop name="bind_value" help="">
<param name="subject" type="subject" help=""/>
<param name="max_value" type="int" help="Just another parameter, e.g., to limit the value"/>
</prop>
</api>
+172 -34
View File
@@ -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] = "";
}
+29 -6
View File
@@ -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
+94 -18
View File
@@ -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. <subjects>*/
process_subject_element(state, name, attrs);
break;
default:
break;
}
+16
View File
@@ -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
**********************/
+5 -4
View File
@@ -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;
+1
View File
@@ -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;
+23 -6
View File
@@ -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;
+1 -1
View File
@@ -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.
@@ -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));
}
}
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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));
@@ -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. */
+4 -4
View File
@@ -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));
@@ -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));
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

+15
View File
@@ -0,0 +1,15 @@
<globals>
<config name="mylib" help="This is my great component library"/>
<consts>
<int name="global_int" value="30"/>
</consts>
<styles>
<style name="global_red" bg_color="0xf00" radius="global_small_unit" pad_all="12px"/>
</styles>
<subjects>
<int name="global_subject" value="22"/>
</subjects>
</globals>
+23
View File
@@ -0,0 +1,23 @@
<component>
<consts>
<int name="local_int" value="15"/>
<color name="local_blue" value="0x0000ff"/>
</consts>
<styles>
<style name="local_style" bg_color="#local_blue" border_color="#global_red" border_width="5"/>
</styles>
<subjects>
<int name="local_subject" value="10"/>
</subjects>
<view extends="lv_obj" width="480" height="300" flex_flow="column">
<lv_label bind_text="global_subject"/>
<lv_slider bind_value="global_subject" range_max="#global_int"/>
<lv_label bind_text="local_subject" style_margin_top="32px"/>
<lv_slider bind_value="local_subject" range_max="#local_int"/>
</view>
</component>
+2 -2
View File
@@ -27,7 +27,7 @@ static void count_event_cb(lv_event_t * e)
void test_xml_event_call_function_attr(void)
{
lv_xml_register_event_cb("count_cb", count_event_cb);
lv_xml_register_event_cb(NULL, "count_cb", count_event_cb);
lv_obj_t * scr = lv_screen_active();
@@ -80,7 +80,7 @@ void test_xml_event_call_function_component(void)
"</component>"
};
lv_xml_register_event_cb("count_cb", count_event_cb);
lv_xml_register_event_cb(NULL, "count_cb", count_event_cb);
lv_xml_component_register_from_data("my_button", xml);
lv_xml_create(lv_screen_active(), "my_button", NULL);
+4 -4
View File
@@ -272,11 +272,11 @@ void test_xml_image_and_font(void)
/*Monstserrat fonts are registered by LVGL */
LV_IMAGE_DECLARE(img_render_lvgl_logo_l8);
LV_IMAGE_DECLARE(img_render_lvgl_logo_rgb565);
lv_xml_register_image("test_img1", &img_render_lvgl_logo_l8);
lv_xml_register_image("test_img2", &img_render_lvgl_logo_rgb565);
lv_xml_register_image(NULL, "test_img1", &img_render_lvgl_logo_l8);
lv_xml_register_image(NULL, "test_img2", &img_render_lvgl_logo_rgb565);
lv_xml_register_font("lv_montserrat_16", &lv_font_montserrat_16);
lv_xml_register_font("lv_montserrat_18", &lv_font_montserrat_18);
lv_xml_register_font(NULL, "lv_montserrat_16", &lv_font_montserrat_16);
lv_xml_register_font(NULL, "lv_montserrat_18", &lv_font_montserrat_18);
lv_xml_component_register_from_data("btn", btn_xml);
+1 -1
View File
@@ -17,7 +17,7 @@ void tearDown(void)
void test_xml_image_with_attrs(void)
{
LV_IMAGE_DECLARE(test_img_lvgl_logo_png);
lv_xml_register_image("logo", &test_img_lvgl_logo_png);
lv_xml_register_image(NULL, "logo", &test_img_lvgl_logo_png);
lv_obj_t * scr = lv_screen_active();
const char * image1_attrs[] = {
+38
View File
@@ -0,0 +1,38 @@
#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_label_with_attrs(void)
{
lv_obj_t * scr = lv_screen_active();
const char * textarea1_attrs[] = {
"text", "This is the text with ellipses added automatically",
"long_mode", "dots",
"width", "100",
"height", "40",
"align", "center",
"style_bg_opa", "50%",
"style_bg_color", "0xf00",
NULL, NULL,
};
lv_xml_create(scr, "lv_label", textarea1_attrs);
TEST_ASSERT_EQUAL_SCREENSHOT("xml/lv_label.png");
}
#endif
+2 -2
View File
@@ -1,4 +1,4 @@
#if LV_BUILD_TEST || 1
#if LV_BUILD_TEST
#include "../lvgl.h"
#include "unity/unity.h"
@@ -16,7 +16,7 @@ void tearDown(void)
void test_xml_view2_from_xml(void)
{
lv_xml_register_font("lv_montserrat_30", &lv_font_montserrat_30);
lv_xml_register_font(NULL, "lv_montserrat_30", &lv_font_montserrat_30);
lv_xml_component_register_from_file("A:src/test_assets/xml/view2.xml");
lv_xml_create(lv_screen_active(), "view2", NULL);
+27
View File
@@ -0,0 +1,27 @@
#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_view3_scoping(void)
{
lv_xml_component_register_from_file("A:src/test_assets/xml/globals.xml");
lv_xml_component_register_from_file("A:src/test_assets/xml/view3.xml");
lv_xml_create(lv_screen_active(), "view3", NULL);
TEST_ASSERT_EQUAL_SCREENSHOT("xml/view3.png");
}
#endif