From cab55d8e5a4b26d2e9f2ca04edb2d028a0a40b54 Mon Sep 17 00:00:00 2001 From: Neo Xu Date: Thu, 13 Jun 2024 05:39:59 +0800 Subject: [PATCH] feat(obj): add API to set/get object ID. (#6278) Signed-off-by: Neo Xu --- .devcontainer/__lv_conf.h__ | 3 ++ Kconfig | 6 +++ docs/others/obj_id.rst | 20 ++++++--- env_support/cmsis-pack/lv_conf_cmsis.h | 3 ++ lv_conf_template.h | 12 +++++- src/core/lv_obj.c | 41 ++++++++++++++++++- src/core/lv_obj.h | 49 ++++++++++++++++++++--- src/core/lv_obj_id_builtin.c | 7 +++- src/lv_conf_internal.h | 28 ++++++++++--- src/lv_init.c | 2 +- tests/src/lv_test_conf_full.h | 1 + tests/src/test_cases/widgets/test_objid.c | 13 ++++++ 12 files changed, 163 insertions(+), 22 deletions(-) diff --git a/.devcontainer/__lv_conf.h__ b/.devcontainer/__lv_conf.h__ index 796181973..d9fabe18c 100644 --- a/.devcontainer/__lv_conf.h__ +++ b/.devcontainer/__lv_conf.h__ @@ -290,6 +290,9 @@ /* Add `id` field to `lv_obj_t` */ #define LV_USE_OBJ_ID 0 +/* Automatically assign an ID when obj is created */ +#define LV_OBJ_ID_AUTO_ASSIGN 1 + /* Use lvgl builtin method for obj ID */ #define LV_USE_OBJ_ID_BUILTIN 0 diff --git a/Kconfig b/Kconfig index d94ed8290..71295ade8 100644 --- a/Kconfig +++ b/Kconfig @@ -612,9 +612,15 @@ menu "LVGL configuration" bool "Add id field to obj" default n + config LV_OBJ_ID_AUTO_ASSIGN + bool "Automatically assign an ID when obj is created" + default y + depends on LV_USE_OBJ_ID + config LV_USE_OBJ_ID_BUILTIN bool "Use builtin method to deal with obj ID" default n + depends on LV_USE_OBJ_ID config LV_USE_OBJ_PROPERTY bool "Use obj property set/get API" diff --git a/docs/others/obj_id.rst b/docs/others/obj_id.rst index 81ae5f779..4da50ffd6 100644 --- a/docs/others/obj_id.rst +++ b/docs/others/obj_id.rst @@ -14,19 +14,20 @@ Usage ----- Enable this feature by setting :c:macro:`LV_USE_OBJ_ID` to `1` in ``lv_conf.h``. -Use the builtin obj ID generator by setting :c:macro:`LV_USE_OBJ_ID_BUILTIN` to `1`. -Otherwise provide your own custom implementation. -The ID is automatically generated and assigned to :cpp:expr:`obj->id` during obj's -construction by calling API :cpp:expr:`lv_obj_assign_id(obj)` from :cpp:func:`lv_obj_constructor`. +Enable :c:macro:`LV_OBJ_ID_AUTO_ASSIGN` to automatically assign an ID to object when it's created. +It's done by calling function :cpp:func:`lv_obj_assign_id` from :cpp:func:`lv_obj_constructor`. -You can directly access the ID by :cpp:expr:`obj->id` or use API :cpp:expr:`lv_obj_stringify_id(obj, buf, len)` +You can either use your own ID generator by defining the function :cpp:func:`lv_obj_assign_id` or you can utilize the built-in one. +To use the builtin ID generator, set :c:macro:`LV_USE_OBJ_ID_BUILTIN` to `1`. + +You can directly access the ID by :cpp:expr:`lv_obj_get_id(obj)` or use API :cpp:expr:`lv_obj_stringify_id(obj, buf, len)` to get a string representation of the ID. Use custom ID generator ~~~~~~~~~~~~~~~~~~~~~~~ -Set :c:macro:`LV_USE_OBJ_ID_BUILTIN` to `0` in ``lv_conf.h``. +Set :c:macro:`LV_USE_OBJ_ID_BUILTIN` to `0` in ``lv_conf.h``. Below APIs needed to be implemented and linked to lvgl. @@ -35,6 +36,7 @@ Below APIs needed to be implemented and linked to lvgl. void lv_obj_assign_id(const lv_obj_class_t * class_p, lv_obj_t * obj); void lv_obj_free_id(lv_obj_t * obj); const char * lv_obj_stringify_id(lv_obj_t * obj, char * buf, uint32_t len); + int lv_obj_id_compare(void * id1, void * id2); :cpp:func:`lv_obj_assign_id` is called when an object is created. The object final class is passed from @@ -55,3 +57,9 @@ This is useful to debug UI crash. From log we can rebuilt UI the moment before c For example, if the obj is stored to a :cpp:expr:`timer->user_data`, but obj is deleted when timer expired. Timer callback will crash because of accessing wild pointer. From the dump log we can clearly see that the obj does not exist. + +Find child by ID +~~~~~~~~~~~~~~~~ + +Use API :cpp:expr:`lv_obj_t * lv_obj_get_child_by_id(const lv_obj_t * obj, void * id);` to find a child by ID. +It will walk through all children and return the first child with the given ID. diff --git a/env_support/cmsis-pack/lv_conf_cmsis.h b/env_support/cmsis-pack/lv_conf_cmsis.h index b4ea3dd88..13f4634f0 100644 --- a/env_support/cmsis-pack/lv_conf_cmsis.h +++ b/env_support/cmsis-pack/lv_conf_cmsis.h @@ -312,6 +312,9 @@ /* Add `id` field to `lv_obj_t` */ #define LV_USE_OBJ_ID 0 +/* Automatically assign an ID when obj is created */ +#define LV_OBJ_ID_AUTO_ASSIGN 1 + /* Use lvgl builtin method for obj ID */ #define LV_USE_OBJ_ID_BUILTIN 0 diff --git a/lv_conf_template.h b/lv_conf_template.h index be7ab3ed0..a30ea1d91 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -353,8 +353,16 @@ /* Add `id` field to `lv_obj_t` */ #define LV_USE_OBJ_ID 0 -/* Use lvgl builtin method for obj ID */ -#define LV_USE_OBJ_ID_BUILTIN 0 +/* Automatically assign an ID when obj is created */ +#define LV_OBJ_ID_AUTO_ASSIGN LV_USE_OBJ_ID + +/*Use the builtin obj ID handler functions: +* - lv_obj_assign_id: Called when a widget is created. Use a separate counter for each widget class as an ID. +* - lv_obj_id_compare: Compare the ID to decide if it matches with a requested value. +* - lv_obj_stringify_id: Return e.g. "button3" +* - lv_obj_free_id: Does nothing, as there is no memory allocation for the ID. +* When disabled these functions needs to be implemented by the user.*/ +#define LV_USE_OBJ_ID_BUILTIN 1 /*Use obj property set/get API*/ #define LV_USE_OBJ_PROPERTY 0 diff --git a/src/core/lv_obj.c b/src/core/lv_obj.c index 7e3d5899f..7b589b2f5 100644 --- a/src/core/lv_obj.c +++ b/src/core/lv_obj.c @@ -44,6 +44,7 @@ static void draw_scrollbar(lv_obj_t * obj, lv_layer_t * layer); static lv_result_t scrollbar_init_draw_dsc(lv_obj_t * obj, lv_draw_rect_dsc_t * dsc); static bool obj_valid_child(const lv_obj_t * parent, const lv_obj_t * obj_to_find); static void update_obj_state(lv_obj_t * obj, lv_state_t new_state); + #if LV_USE_OBJ_PROPERTY static lv_result_t lv_obj_set_any(lv_obj_t *, lv_prop_id_t, const lv_property_t *); static lv_result_t lv_obj_get_any(const lv_obj_t *, lv_prop_id_t, lv_property_t *); @@ -311,6 +312,42 @@ bool lv_obj_is_valid(const lv_obj_t * obj) return false; } +#if LV_USE_OBJ_ID +void lv_obj_set_id(lv_obj_t * obj, void * id) +{ + LV_ASSERT_NULL(obj); + obj->id = id; +} + +void * lv_obj_get_id(const lv_obj_t * obj) +{ + LV_ASSERT_NULL(obj); + return obj->id; +} + +lv_obj_t * lv_obj_get_child_by_id(const lv_obj_t * obj, void * id) +{ + if(obj == NULL) obj = lv_display_get_screen_active(NULL); + if(obj == NULL) return NULL; + + uint32_t i; + uint32_t child_cnt = lv_obj_get_child_count(obj); + for(i = 0; i < child_cnt; i++) { + lv_obj_t * child = obj->spec_attr->children[i]; + if(lv_obj_id_compare(child->id, id) == 0) return child; + } + + /*Search children*/ + for(i = 0; i < child_cnt; i++) { + lv_obj_t * child = obj->spec_attr->children[i]; + lv_obj_t * found = lv_obj_get_child_by_id(child, id); + if(found != NULL) return found; + } + + return NULL; +} +#endif + /********************** * STATIC FUNCTIONS **********************/ @@ -343,7 +380,7 @@ static void lv_obj_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj) obj->flags |= LV_OBJ_FLAG_SCROLL_WITH_ARROW; if(parent) obj->flags |= LV_OBJ_FLAG_GESTURE_BUBBLE; -#if LV_USE_OBJ_ID +#if LV_OBJ_ID_AUTO_ASSIGN lv_obj_assign_id(class_p, obj); #endif @@ -380,7 +417,7 @@ static void lv_obj_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj) obj->spec_attr = NULL; } -#if LV_USE_OBJ_ID +#if LV_OBJ_ID_AUTO_ASSIGN lv_obj_free_id(obj); #endif } diff --git a/src/core/lv_obj.h b/src/core/lv_obj.h index 95bfe59f0..471ed88b1 100644 --- a/src/core/lv_obj.h +++ b/src/core/lv_obj.h @@ -405,11 +405,38 @@ const lv_obj_class_t * lv_obj_get_class(const lv_obj_t * obj); bool lv_obj_is_valid(const lv_obj_t * obj); #if LV_USE_OBJ_ID +/** + * Set an id for an object. + * @param obj pointer to an object + * @param id the id of the object + */ +void lv_obj_set_id(lv_obj_t * obj, void * id); /** - * Assign an id to an object if not previously assigned - * Set `LV_USE_OBJ_ID_BUILTIN` to 1 to use builtin method to generate object ID. - * Otherwise, these functions including `lv_obj_[assign|free|stringify]_id` should be implemented externally. + * Get the id of an object. + * @param obj pointer to an object + * @return the id of the object + */ +void * lv_obj_get_id(const lv_obj_t * obj); + +/** + * Get the child object by its id. + * It will check children and grandchildren recursively. + * Function `lv_obj_id_compare` is used to matched obj id with given id. + * + * @param obj pointer to an object + * @param id the id of the child object + * @return pointer to the child object or NULL if not found + */ +lv_obj_t * lv_obj_get_child_by_id(const lv_obj_t * obj, void * id); + +/** + * Assign id to object if not previously assigned. + * This function gets called automatically when LV_OBJ_ID_AUTO_ASSIGN is enabled. + * + * Set `LV_USE_OBJ_ID_BUILTIN` to use the builtin method to generate object ID. + * Otherwise, these functions including `lv_obj_[assign|free|stringify]_id` and + * `lv_obj_id_compare`should be implemented externally. * * @param class_p the class this obj belongs to. Note obj->class_p is the class currently being constructed. * @param obj pointer to an object @@ -417,11 +444,24 @@ bool lv_obj_is_valid(const lv_obj_t * obj); void lv_obj_assign_id(const lv_obj_class_t * class_p, lv_obj_t * obj); /** - * Free resources allocated by `lv_obj_assign_id` + * Free resources allocated by `lv_obj_assign_id`. + * This function gets called automatically when object is deleted. * @param obj pointer to an object */ void lv_obj_free_id(lv_obj_t * obj); +/** + * Compare two obj id, return 0 if they are equal. + * + * Set `LV_USE_OBJ_ID_BUILTIN` to use the builtin method for compare. + * Otherwise, it must be implemented externally. + * + * @param id1: the first id + * @param id2: the second id + * @return 0 if they are equal, non-zero otherwise. + */ +int lv_obj_id_compare(void * id1, void * id2); + /** * Format an object's id into a string. * @param obj pointer to an object @@ -435,7 +475,6 @@ const char * lv_obj_stringify_id(lv_obj_t * obj, char * buf, uint32_t len); * Free resources used by builtin ID generator. */ void lv_objid_builtin_destroy(void); - #endif #endif /*LV_USE_OBJ_ID*/ diff --git a/src/core/lv_obj_id_builtin.c b/src/core/lv_obj_id_builtin.c index 4e1976bce..5512cf74c 100644 --- a/src/core/lv_obj_id_builtin.c +++ b/src/core/lv_obj_id_builtin.c @@ -40,7 +40,7 @@ typedef struct _class_info_t { * GLOBAL FUNCTIONS **********************/ -#if LV_USE_OBJ_ID_BUILTIN +#if LV_USE_OBJ_ID && LV_USE_OBJ_ID_BUILTIN void lv_obj_assign_id(const lv_obj_class_t * class_p, lv_obj_t * obj) { @@ -105,4 +105,9 @@ void lv_objid_builtin_destroy(void) global->objid_count = 0; } +int lv_obj_id_compare(void * id1, void * id2) +{ + return id1 == id2 ? 0 : 1; +} + #endif /*LV_USE_OBJ_ID_BUILTIN*/ diff --git a/src/lv_conf_internal.h b/src/lv_conf_internal.h index 9781c209a..a1a6f2005 100644 --- a/src/lv_conf_internal.h +++ b/src/lv_conf_internal.h @@ -1042,12 +1042,30 @@ #endif #endif -/* Use lvgl builtin method for obj ID */ -#ifndef LV_USE_OBJ_ID_BUILTIN - #ifdef CONFIG_LV_USE_OBJ_ID_BUILTIN - #define LV_USE_OBJ_ID_BUILTIN CONFIG_LV_USE_OBJ_ID_BUILTIN +/* Automatically assign an ID when obj is created */ +#ifndef LV_OBJ_ID_AUTO_ASSIGN + #ifdef CONFIG_LV_OBJ_ID_AUTO_ASSIGN + #define LV_OBJ_ID_AUTO_ASSIGN CONFIG_LV_OBJ_ID_AUTO_ASSIGN #else - #define LV_USE_OBJ_ID_BUILTIN 0 + #define LV_OBJ_ID_AUTO_ASSIGN LV_USE_OBJ_ID + #endif +#endif + +/*Use the builtin obj ID handler functions: +* - lv_obj_assign_id: Called when a widget is created. Use a separate counter for each widget class as an ID. +* - lv_obj_id_compare: Compare the ID to decide if it matches with a requested value. +* - lv_obj_stringify_id: Return e.g. "button3" +* - lv_obj_free_id: Does nothing, as there is no memory allocation for the ID. +* When disabled these functions needs to be implemented by the user.*/ +#ifndef LV_USE_OBJ_ID_BUILTIN + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_OBJ_ID_BUILTIN + #define LV_USE_OBJ_ID_BUILTIN CONFIG_LV_USE_OBJ_ID_BUILTIN + #else + #define LV_USE_OBJ_ID_BUILTIN 0 + #endif + #else + #define LV_USE_OBJ_ID_BUILTIN 1 #endif #endif diff --git a/src/lv_init.c b/src/lv_init.c index afedb8a1c..35a068342 100644 --- a/src/lv_init.c +++ b/src/lv_init.c @@ -411,7 +411,7 @@ void lv_deinit(void) lv_profiler_builtin_uninit(); #endif -#if LV_USE_OBJ_ID_BUILTIN +#if LV_USE_OBJ_ID && LV_USE_OBJ_ID_BUILTIN lv_objid_builtin_destroy(); #endif diff --git a/tests/src/lv_test_conf_full.h b/tests/src/lv_test_conf_full.h index 8be882645..b1f64cbe1 100644 --- a/tests/src/lv_test_conf_full.h +++ b/tests/src/lv_test_conf_full.h @@ -109,6 +109,7 @@ #define LV_USE_DEMO_VECTOR_GRAPHIC 1 #define LV_USE_OBJ_ID 1 +#define LV_OBJ_ID_AUTO_ASSIGN 1 #define LV_USE_OBJ_ID_BUILTIN 1 #define LV_CACHE_DEF_SIZE (10 * 1024 * 1024) diff --git a/tests/src/test_cases/widgets/test_objid.c b/tests/src/test_cases/widgets/test_objid.c index 695ffe26c..8ec2a8361 100644 --- a/tests/src/test_cases/widgets/test_objid.c +++ b/tests/src/test_cases/widgets/test_objid.c @@ -27,4 +27,17 @@ void test_obj_id_should_grow_by_one(void) TEST_ASSERT_EQUAL(id1 + 1, id2); } +void test_obj_id_get_child(void) +{ + lv_obj_t * parent = lv_obj_create(lv_screen_active()); + lv_obj_t * child = lv_label_create(parent); + lv_obj_t * grandchild = lv_label_create(child); + + lv_obj_set_id(child, (void *)(lv_uintptr_t)1); + lv_obj_set_id(grandchild, (void *)(lv_uintptr_t)2); + + TEST_ASSERT_EQUAL_PTR(child, lv_obj_get_child_by_id(NULL, (void *)(lv_uintptr_t)1)); + TEST_ASSERT_EQUAL_PTR(grandchild, lv_obj_get_child_by_id(NULL, (void *)(lv_uintptr_t)2)); +} + #endif