feat(obj): add API to set/get object ID. (#6278)

Signed-off-by: Neo Xu <neo.xu1990@gmail.com>
This commit is contained in:
Neo Xu
2024-06-13 05:39:59 +08:00
committed by GitHub
parent 864b54b851
commit cab55d8e5a
12 changed files with 163 additions and 22 deletions

View File

@@ -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

View File

@@ -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"

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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
}

View File

@@ -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*/

View File

@@ -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*/

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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