feat(xml): add basic callback event support

This commit is contained in:
Gabor Kiss-Vamosi
2025-01-22 17:38:48 +08:00
parent a535063308
commit 1c9c9f6601
10 changed files with 367 additions and 19 deletions

View File

@@ -12,6 +12,10 @@
</styles>
<view extends="lv_obj" width="280" height="240" style_bg_color="#light_blue">
<lv_roller options="'a&0x30;b\nc\nd' infinite" selected="2 true" visible_row_count="3"/>
<lv_button>
<lv_label text="Click me"/>
<lv_event-call_function trigger="clicked" callback="print" user_data="Hello"/>
<lv_event-call_function trigger="clicked" callback="print" user_data="World"/>
</lv_button>
</view>
</component>

View File

@@ -32,6 +32,7 @@
#include "parsers/lv_xml_roller_parser.h"
#include "parsers/lv_xml_scale_parser.h"
#include "parsers/lv_xml_spangroup_parser.h"
#include "parsers/lv_xml_event_parser.h"
#include "../../libs/expat/expat.h"
#include "../../draw/lv_draw_image.h"
@@ -55,6 +56,7 @@ static void register_builtin_fonts(void);
**********************/
static lv_ll_t font_ll;
static lv_ll_t image_ll;
static lv_ll_t event_cb_ll;
/**********************
* MACROS
@@ -68,6 +70,7 @@ 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_component_init();
@@ -96,6 +99,8 @@ void lv_xml_init(void)
lv_xml_widget_register("lv_scale-section", lv_xml_scale_section_create, lv_xml_scale_section_apply);
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_event-call_function", lv_xml_event_call_function_create, lv_xml_event_call_function_apply);
}
void * lv_xml_create_from_ctx(lv_obj_t * parent, lv_xml_component_ctx_t * parent_ctx, lv_xml_component_ctx_t * ctx,
@@ -182,7 +187,8 @@ const lv_font_t * lv_xml_get_font(const char * name)
if(lv_streq(f->name, name)) return f->font;
}
return NULL;
LV_LOG_WARN("No font was found with name \"%s\". Using LV_FONT_DEFAULT instead.", name);
return LV_FONT_DEFAULT;
}
lv_result_t lv_xml_register_image(const char * name, const void * src)
@@ -206,6 +212,28 @@ const void * lv_xml_get_image(const char * name)
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_xml_event_cb_t * e = lv_ll_ins_head(&event_cb_ll);
e->name = lv_strdup(name);
e->cb = cb;
return LV_RESULT_OK;
}
lv_event_cb_t lv_xml_get_event_cb(const char * name)
{
lv_xml_event_cb_t * e;
LV_LL_READ(&event_cb_ll, e) {
if(lv_streq(e->name, name)) return e->cb;
}
LV_LOG_WARN("No event_cb was found with name \"%s\"", name);
return NULL;
}

View File

@@ -14,6 +14,7 @@ extern "C" {
* INCLUDES
*********************/
#include "../../misc/lv_types.h"
#include "../../misc/lv_event.h"
#if LV_USE_XML
/*********************
@@ -44,6 +45,10 @@ lv_result_t lv_xml_register_image(const char * name, const void * src);
const void * lv_xml_get_image(const char * name);
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);
/**********************
* MACROS
**********************/

View File

@@ -18,6 +18,7 @@ extern "C" {
#include "lv_xml_utils.h"
#include "../../misc/lv_ll.h"
#include "../../misc/lv_style.h"
/**********************
* TYPEDEFS
@@ -37,6 +38,22 @@ struct _lv_xml_component_ctx_t {
struct _lv_xml_component_ctx_t * next;
};
typedef struct {
const char * name;
const char * value;
} lv_xml_const_t;
typedef struct {
const char * name;
const char * def;
const char * type;
} lv_xml_param_t;
typedef struct {
const char * name;
lv_grad_dsc_t grad_dsc;
} lv_xml_grad_t;
/**********************
* GLOBAL PROTOTYPES
**********************/

View File

@@ -49,22 +49,6 @@ struct _lv_xml_parser_state_t {
lv_xml_parser_section_t section;
};
typedef struct {
const char * name;
const char * value;
} lv_xml_const_t;
typedef struct {
const char * name;
const char * def;
const char * type;
} lv_xml_param_t;
typedef struct {
const char * name;
lv_grad_dsc_t grad_dsc;
} lv_xml_grad_t;
/**********************
* GLOBAL PROTOTYPES
**********************/

View File

@@ -40,6 +40,11 @@ typedef struct {
const void * src;
} lv_xml_image_t;
typedef struct {
const char * name;
lv_event_cb_t cb;
} lv_xml_event_cb_t;
/**********************
* GLOBAL PROTOTYPES

View File

@@ -0,0 +1,167 @@
/**
* @file lv_xml_event_parser.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_xml_event_parser.h"
#if LV_USE_XML
#include "../../../lvgl.h"
#include "../../../lvgl_private.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_event_code_t trigger_text_to_enum_value(const char * txt);
static void free_user_data_event_cb(lv_event_t * e);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void * lv_xml_event_call_function_create(lv_xml_parser_state_t * state, const char ** attrs)
{
LV_UNUSED(attrs);
const char * cb_txt = lv_xml_get_value_of(attrs, "callback");
if(cb_txt == NULL) {
LV_LOG_WARN("callback is mandatory for event-call_function");
return NULL;
}
lv_event_cb_t cb = lv_xml_get_event_cb(cb_txt);
if(cb == NULL) {
LV_LOG_WARN("Couldn't add call function event because \"%s\" callback is not found.", cb_txt);
return NULL;
}
const char * trigger = lv_xml_get_value_of(attrs, "trigger");
lv_event_code_t code = LV_EVENT_CLICKED;
if(trigger) code = trigger_text_to_enum_value(trigger);
if(code == LV_EVENT_LAST) {
LV_LOG_WARN("Couldn't add call function event because \"%s\" trigger is invalid.", trigger);
return NULL;
}
const char * user_data_xml = lv_xml_get_value_of(attrs, "user_data");
char * user_data = NULL;
if(user_data_xml) user_data = lv_strdup(user_data_xml);
lv_obj_t * obj = lv_xml_state_get_parent(state);
lv_obj_add_event_cb(obj, cb, code, user_data);
if(user_data) lv_obj_add_event_cb(obj, free_user_data_event_cb, LV_EVENT_DELETE, user_data);
return obj;
}
void lv_xml_event_call_function_apply(lv_xml_parser_state_t * state, const char ** attrs)
{
LV_UNUSED(state);
LV_UNUSED(attrs);
/*Nothing to apply*/
}
/**********************
* STATIC FUNCTIONS
**********************/
static void free_user_data_event_cb(lv_event_t * e)
{
char * user_data = lv_event_get_user_data(e);
lv_free(user_data);
}
static lv_event_code_t trigger_text_to_enum_value(const char * txt)
{
if(lv_streq("all", txt)) return LV_EVENT_ALL;
if(lv_streq("pressed", txt)) return LV_EVENT_PRESSED;
if(lv_streq("pressing", txt)) return LV_EVENT_PRESSING;
if(lv_streq("press_lost", txt)) return LV_EVENT_PRESS_LOST;
if(lv_streq("short_clicked", txt)) return LV_EVENT_SHORT_CLICKED;
if(lv_streq("single_clicked", txt)) return LV_EVENT_SINGLE_CLICKED;
if(lv_streq("double_clicked", txt)) return LV_EVENT_DOUBLE_CLICKED;
if(lv_streq("triple_clicked", txt)) return LV_EVENT_TRIPLE_CLICKED;
if(lv_streq("long_pressed", txt)) return LV_EVENT_LONG_PRESSED;
if(lv_streq("long_pressed_repeat", txt)) return LV_EVENT_LONG_PRESSED_REPEAT;
if(lv_streq("clicked", txt)) return LV_EVENT_CLICKED;
if(lv_streq("released", txt)) return LV_EVENT_RELEASED;
if(lv_streq("scroll_begin", txt)) return LV_EVENT_SCROLL_BEGIN;
if(lv_streq("scroll_throw_begin", txt)) return LV_EVENT_SCROLL_THROW_BEGIN;
if(lv_streq("scroll_end", txt)) return LV_EVENT_SCROLL_END;
if(lv_streq("scroll", txt)) return LV_EVENT_SCROLL;
if(lv_streq("gesture", txt)) return LV_EVENT_GESTURE;
if(lv_streq("key", txt)) return LV_EVENT_KEY;
if(lv_streq("rotary", txt)) return LV_EVENT_ROTARY;
if(lv_streq("focused", txt)) return LV_EVENT_FOCUSED;
if(lv_streq("defocused", txt)) return LV_EVENT_DEFOCUSED;
if(lv_streq("leave", txt)) return LV_EVENT_LEAVE;
if(lv_streq("hit_test", txt)) return LV_EVENT_HIT_TEST;
if(lv_streq("indev_reset", txt)) return LV_EVENT_INDEV_RESET;
if(lv_streq("hover_over", txt)) return LV_EVENT_HOVER_OVER;
if(lv_streq("hover_leave", txt)) return LV_EVENT_HOVER_LEAVE;
if(lv_streq("cover_check", txt)) return LV_EVENT_COVER_CHECK;
if(lv_streq("refr_ext_draw_size", txt)) return LV_EVENT_REFR_EXT_DRAW_SIZE;
if(lv_streq("draw_main_begin", txt)) return LV_EVENT_DRAW_MAIN_BEGIN;
if(lv_streq("draw_main", txt)) return LV_EVENT_DRAW_MAIN;
if(lv_streq("draw_main_end", txt)) return LV_EVENT_DRAW_MAIN_END;
if(lv_streq("draw_post_begin", txt)) return LV_EVENT_DRAW_POST_BEGIN;
if(lv_streq("draw_post", txt)) return LV_EVENT_DRAW_POST;
if(lv_streq("draw_post_end", txt)) return LV_EVENT_DRAW_POST_END;
if(lv_streq("draw_task_added", txt)) return LV_EVENT_DRAW_TASK_ADDED;
if(lv_streq("value_changed", txt)) return LV_EVENT_VALUE_CHANGED;
if(lv_streq("insert", txt)) return LV_EVENT_INSERT;
if(lv_streq("refresh", txt)) return LV_EVENT_REFRESH;
if(lv_streq("ready", txt)) return LV_EVENT_READY;
if(lv_streq("cancel", txt)) return LV_EVENT_CANCEL;
if(lv_streq("create", txt)) return LV_EVENT_CREATE;
if(lv_streq("delete", txt)) return LV_EVENT_DELETE;
if(lv_streq("child_changed", txt)) return LV_EVENT_CHILD_CHANGED;
if(lv_streq("child_created", txt)) return LV_EVENT_CHILD_CREATED;
if(lv_streq("child_deleted", txt)) return LV_EVENT_CHILD_DELETED;
if(lv_streq("screen_unload_start", txt)) return LV_EVENT_SCREEN_UNLOAD_START;
if(lv_streq("screen_load_start", txt)) return LV_EVENT_SCREEN_LOAD_START;
if(lv_streq("screen_loaded", txt)) return LV_EVENT_SCREEN_LOADED;
if(lv_streq("screen_unloaded", txt)) return LV_EVENT_SCREEN_UNLOADED;
if(lv_streq("size_changed", txt)) return LV_EVENT_SIZE_CHANGED;
if(lv_streq("style_changed", txt)) return LV_EVENT_STYLE_CHANGED;
if(lv_streq("layout_changed", txt)) return LV_EVENT_LAYOUT_CHANGED;
if(lv_streq("get_self_size", txt)) return LV_EVENT_GET_SELF_SIZE;
if(lv_streq("invalidate_area", txt)) return LV_EVENT_INVALIDATE_AREA;
if(lv_streq("resolution_changed", txt)) return LV_EVENT_RESOLUTION_CHANGED;
if(lv_streq("color_format_changed", txt)) return LV_EVENT_COLOR_FORMAT_CHANGED;
if(lv_streq("refr_request", txt)) return LV_EVENT_REFR_REQUEST;
if(lv_streq("refr_start", txt)) return LV_EVENT_REFR_START;
if(lv_streq("refr_ready", txt)) return LV_EVENT_REFR_READY;
if(lv_streq("render_start", txt)) return LV_EVENT_RENDER_START;
if(lv_streq("render_ready", txt)) return LV_EVENT_RENDER_READY;
if(lv_streq("flush_start", txt)) return LV_EVENT_FLUSH_START;
if(lv_streq("flush_finish", txt)) return LV_EVENT_FLUSH_FINISH;
if(lv_streq("flush_wait_start", txt)) return LV_EVENT_FLUSH_WAIT_START;
if(lv_streq("flush_wait_finish", txt)) return LV_EVENT_FLUSH_WAIT_FINISH;
if(lv_streq("vsync", txt)) return LV_EVENT_VSYNC;
LV_LOG_WARN("%s is an unknown value for event's trigger", txt);
return LV_EVENT_LAST; /*Indicate error*/
}
#endif /* LV_USE_XML */

View File

@@ -0,0 +1,40 @@
/**
* @file lv_xml_event_parser.h
*
*/
#ifndef LV_EVENT_XML_PARSER_H
#define LV_EVENT_XML_PARSER_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_xml.h"
#if LV_USE_XML
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
void * lv_xml_event_call_function_create(lv_xml_parser_state_t * state, const char ** attrs);
void lv_xml_event_call_function_apply(lv_xml_parser_state_t * state, const char ** attrs);
/**********************
* MACROS
**********************/
#endif /* LV_USE_XML */
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_EVENT_XML_PARSE_H*/

View File

@@ -0,0 +1,98 @@
#if LV_BUILD_TEST
#include "../lvgl.h"
#include "unity/unity.h"
#include "lv_test_indev.h"
#include <stdlib.h>
void setUp(void)
{
/* Function run before every test */
}
void tearDown(void)
{
/* Function run after every test */
lv_obj_clean(lv_screen_active());
}
static int32_t cnt;
static void count_event_cb(lv_event_t * e)
{
const char * user_data = lv_event_get_user_data(e);
int inc = atoi(user_data);
cnt += inc;
}
void test_xml_event_call_function_attr(void)
{
lv_xml_register_event_cb("count_cb", count_event_cb);
lv_obj_t * scr = lv_screen_active();
const char * button_attrs[] = {
"width", "300",
"x", "10",
"y", "10",
NULL, NULL,
};
lv_obj_t * button = lv_xml_create(scr, "lv_button", button_attrs);
const char * event_attrs[] = {
"callback", "count_cb",
"user_data", "3",
"trigger", "clicked",
NULL, NULL,
};
lv_xml_create(button, "lv_event-call_function", event_attrs);
const char * label_attrs[] = {
"text", "Click me!",
"align", "center",
NULL, NULL,
};
lv_xml_create(button, "lv_label", label_attrs);
lv_refr_now(NULL); /*Make sure that the coordinates are calculated*/
cnt = 0;
lv_test_mouse_click_at(30, 20);
TEST_ASSERT_EQUAL(3, cnt);
lv_test_indev_wait(100);
lv_test_mouse_click_at(30, 20);
TEST_ASSERT_EQUAL(6, cnt);
}
void test_xml_event_call_function_component(void)
{
const char * xml = {
"<component>"
" <view extends=\"lv_button\" x=\"5\" y=\"5\">"
" <lv_label text=\"Click me\"/>"
" <lv_event-call_function trigger=\"clicked\" callback=\"count_cb\" user_data=\"3\"/>"
" </view>"
"</component>"
};
lv_xml_register_event_cb("count_cb", count_event_cb);
lv_xml_component_register_from_data("my_button", xml);
lv_xml_create(lv_screen_active(), "my_button", NULL);
lv_refr_now(NULL); /*Make sure that the coordinates are calculated*/
cnt = 0;
lv_test_mouse_click_at(30, 10);
TEST_ASSERT_EQUAL(3, cnt);
lv_test_indev_wait(100);
lv_test_mouse_click_at(30, 10);
TEST_ASSERT_EQUAL(6, cnt);
}
#endif

View File

@@ -14,7 +14,7 @@ void tearDown(void)
lv_obj_clean(lv_screen_active());
}
void test_xml_tabview_with_attrs(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);