From 1c9c9f66011a0ea170c2ea860be90f4e1a4d9404 Mon Sep 17 00:00:00 2001 From: Gabor Kiss-Vamosi Date: Wed, 22 Jan 2025 17:38:48 +0800 Subject: [PATCH] feat(xml): add basic callback event support --- examples/others/xml/view.xml | 6 +- src/others/xml/lv_xml.c | 30 +++- src/others/xml/lv_xml.h | 5 + src/others/xml/lv_xml_component_private.h | 17 ++ src/others/xml/lv_xml_parser.h | 16 -- src/others/xml/lv_xml_private.h | 5 + src/others/xml/parsers/lv_xml_event_parser.c | 167 +++++++++++++++++++ src/others/xml/parsers/lv_xml_event_parser.h | 40 +++++ tests/src/test_cases/xml/test_xml_event.c | 98 +++++++++++ tests/src/test_cases/xml/test_xml_image.c | 2 +- 10 files changed, 367 insertions(+), 19 deletions(-) create mode 100644 src/others/xml/parsers/lv_xml_event_parser.c create mode 100644 src/others/xml/parsers/lv_xml_event_parser.h create mode 100644 tests/src/test_cases/xml/test_xml_event.c diff --git a/examples/others/xml/view.xml b/examples/others/xml/view.xml index 89390df2b..cca279f6d 100644 --- a/examples/others/xml/view.xml +++ b/examples/others/xml/view.xml @@ -12,6 +12,10 @@ - + + + + + diff --git a/src/others/xml/lv_xml.c b/src/others/xml/lv_xml.c index 255fedd14..feec65c47 100644 --- a/src/others/xml/lv_xml.c +++ b/src/others/xml/lv_xml.c @@ -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; } diff --git a/src/others/xml/lv_xml.h b/src/others/xml/lv_xml.h index 1a0948d9b..ec059e997 100644 --- a/src/others/xml/lv_xml.h +++ b/src/others/xml/lv_xml.h @@ -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 **********************/ diff --git a/src/others/xml/lv_xml_component_private.h b/src/others/xml/lv_xml_component_private.h index 3f2f37aa9..069758c12 100644 --- a/src/others/xml/lv_xml_component_private.h +++ b/src/others/xml/lv_xml_component_private.h @@ -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 **********************/ diff --git a/src/others/xml/lv_xml_parser.h b/src/others/xml/lv_xml_parser.h index cf95363a4..e040ed23c 100644 --- a/src/others/xml/lv_xml_parser.h +++ b/src/others/xml/lv_xml_parser.h @@ -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 **********************/ diff --git a/src/others/xml/lv_xml_private.h b/src/others/xml/lv_xml_private.h index ad67f9bf2..6c6a26b88 100644 --- a/src/others/xml/lv_xml_private.h +++ b/src/others/xml/lv_xml_private.h @@ -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 diff --git a/src/others/xml/parsers/lv_xml_event_parser.c b/src/others/xml/parsers/lv_xml_event_parser.c new file mode 100644 index 000000000..5aef5e33d --- /dev/null +++ b/src/others/xml/parsers/lv_xml_event_parser.c @@ -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 */ diff --git a/src/others/xml/parsers/lv_xml_event_parser.h b/src/others/xml/parsers/lv_xml_event_parser.h new file mode 100644 index 000000000..28498fba9 --- /dev/null +++ b/src/others/xml/parsers/lv_xml_event_parser.h @@ -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*/ diff --git a/tests/src/test_cases/xml/test_xml_event.c b/tests/src/test_cases/xml/test_xml_event.c new file mode 100644 index 000000000..cb90ce34a --- /dev/null +++ b/tests/src/test_cases/xml/test_xml_event.c @@ -0,0 +1,98 @@ +#if LV_BUILD_TEST +#include "../lvgl.h" + +#include "unity/unity.h" +#include "lv_test_indev.h" +#include + +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 = { + "" + " " + " " + " " + " " + "" + }; + + 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 diff --git a/tests/src/test_cases/xml/test_xml_image.c b/tests/src/test_cases/xml/test_xml_image.c index f7aa06b7d..4ef965727 100644 --- a/tests/src/test_cases/xml/test_xml_image.c +++ b/tests/src/test_cases/xml/test_xml_image.c @@ -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);