diff --git a/a.patch b/a.patch new file mode 100644 index 0000000000..76d72cd595 --- /dev/null +++ b/a.patch @@ -0,0 +1,35 @@ +diff --git a/src/draw/opengles/lv_draw_opengles.c b/src/draw/opengles/lv_draw_opengles.c +index b3f2122a7..bfb167bc9 100644 +--- a/src/draw/opengles/lv_draw_opengles.c ++++ b/src/draw/opengles/lv_draw_opengles.c +@@ -275,6 +275,10 @@ static bool draw_to_texture(lv_draw_opengles_unit_t * u, cache_data_t * cache_da + lv_obj_remove_flag(obj, LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS); + } + ++ lv_draw_dsc_base_t * base_dsc = task->draw_dsc; ++ cache_data->draw_dsc = lv_malloc(base_dsc->dsc_size); ++ lv_memcpy((void *)cache_data->draw_dsc, base_dsc, base_dsc->dsc_size); ++ + switch(task->type) { + case LV_DRAW_TASK_TYPE_FILL: { + lv_draw_fill_dsc_t * fill_dsc = task->draw_dsc; +@@ -355,6 +359,8 @@ static bool draw_to_texture(lv_draw_opengles_unit_t * u, cache_data_t * cache_da + break; + } + default: ++ /*The malloced cache_data->draw_dsc will be freed automatically on failure ++ *in opengles_texture_cache_free_cb*/ + return false; + } + +@@ -367,10 +373,7 @@ static bool draw_to_texture(lv_draw_opengles_unit_t * u, cache_data_t * cache_da + + unsigned int texture = create_texture(texture_w, texture_h, u->render_draw_buf.data); + +- lv_draw_dsc_base_t * base_dsc = task->draw_dsc; + +- cache_data->draw_dsc = lv_malloc(base_dsc->dsc_size); +- lv_memcpy((void *)cache_data->draw_dsc, base_dsc, base_dsc->dsc_size); + cache_data->w = texture_w; + cache_data->h = texture_h; + cache_data->texture = texture; diff --git a/docs/src/details/common-widget-features/basics.rst b/docs/src/details/common-widget-features/basics.rst index c00e00db7e..f8384ef1d3 100644 --- a/docs/src/details/common-widget-features/basics.rst +++ b/docs/src/details/common-widget-features/basics.rst @@ -577,9 +577,87 @@ more on encoder behaviors and the edit mode. Learn more about :ref:`indev_keys`. +.. _widget_names: +Names +***** +When a widget is created, its reference can be stored in an :cpp:expr:`lv_obj_t *` pointer +variable. To use this widget in multiple places in the code, the variable can be passed +as a function parameter or made a global variable. However, this approach has some drawbacks: +- Using global variables is not clean and generally not recommended. +- It's not scalable. Passing references to 20 widgets as function parameters is not ideal. +- It's hard to track whether a widget still exists or has been deleted. + +Setting names +------------- + +To address these issues, LVGL introduces a powerful widget naming system that can be enabled +by setting ``LV_USE_OBJ_NAME`` in ``lv_conf.h``. + +A custom name can be assigned using :cpp:expr:`lv_obj_set_name(obj, "name")` or +:cpp:expr:`lv_obj_set_name_static(obj, "name")`. The "static" variant means the passed name +must remain valid while the widget exists, as only the pointer is stored. Otherwise, LVGL will +allocate memory to store a copy of the name. + +If a name ends with ``#``, LVGL will automatically replace it with an index based on the +number of siblings with the same base name. If no name is provided, the default is +``_#``. + +Below is an example showing how manually and automatically assigned names are resolved: + +- Main ``lv_obj`` container named ``"cont"``: "cont" + - ``lv_obj`` container named ``"header"``: "header" + - ``lv_label`` with no name: "lv_label_0" + - ``lv_label`` named ``"title"``: "title" + - ``lv_label`` with no name: "lv_label_1" (It's the third label, but custom-named widgets are not counted) + - ``lv_obj`` container named ``"buttons"``: + - ``lv_button`` with no name: "lv_button_0" + - ``lv_button`` named ``"second_button"``: "second_button" + - ``lv_button`` with no name: "lv_button_1" + - ``lv_button`` named ``lv_button_#``: "lv_button_2" + - ``lv_button`` named ``mybtn_#``: "mybtn_0" + - ``lv_button`` with no name: "lv_button_2" + - ``lv_button`` named ``mybtn_#``: "mybtn_1" + - ``lv_button`` named ``mybtn_#``: "mybtn_2" + - ``lv_button`` named ``mybtn_#``: "mybtn_3" + +Finding widgets +--------------- + +Widgets can be found by name in two ways: + +1. **Get a direct child by name** using :cpp:func:`lv_obj_get_child_by_name(parent, "child_name")`. + Example: + ``lv_obj_get_child_by_name(header, "title")`` + You can also use a "path" to find nested children: + ``lv_obj_get_child_by_name(cont, "buttons/mybtn_2")`` + +2. **Find a descendant at any level** using :cpp:func:`lv_obj_find_by_name(parent, "child_name")`. + Example: + ``lv_obj_find_by_name(cont, "mybtn_1")`` + Note that ``"mybtn_1"`` is a child of ``"buttons"``, not directly of ``"cont"``. + This is useful when you want to ignore hierarchy and search by name alone. + +Since both functions start searching from a specific parent, it’s possible to have multiple widget +subtrees with identical names under different parents. + +For example, if ``my_listitem_create(parent)`` creates a widget named ``"list_item_#"`` +with children like ``"icon"``, ``"title"``, ``"ok_button"``, and ``"lv_label_0"``, +and it's called 10 times, a specific ``"ok_button"`` can be found like this: + +.. code-block:: c + + lv_obj_t * item = lv_obj_find_by_name(lv_screen_active(), "list_item_5"); + lv_obj_t * ok_btn = lv_obj_find_by_name(item, "ok_button"); + + // Or + lv_obj_t * ok_btn = lv_obj_get_child_by_name(some_list_container, "list_item_5/ok_button"); + +Names are resolved **when they are retrieved**, not when they are set. +This means the indices always reflect the current state of the widget tree +at the time the name is used. .. _widget_snapshot: diff --git a/src/core/lv_obj_tree.c b/src/core/lv_obj_tree.c index 79ce5d3f26..95396c4963 100644 --- a/src/core/lv_obj_tree.c +++ b/src/core/lv_obj_tree.c @@ -464,13 +464,65 @@ void lv_obj_get_name_resolved(const lv_obj_t * obj, char buf[], size_t buf_size) { LV_ASSERT_OBJ(obj, MY_CLASS); - if(obj->spec_attr && obj->spec_attr->name) { - lv_strlcpy(buf, obj->spec_attr->name, buf_size); + const char * name = lv_obj_get_name(obj); + /*Use a default name which auto-indexing*/ + char name_buf[LV_OBJ_NAME_MAX_LEN]; + if(name == NULL) { + lv_snprintf(name_buf, sizeof(name_buf), "%s_#", obj->class_p->name); + name = name_buf; + } + + size_t name_len = lv_strlen(name); + lv_obj_t * parent = lv_obj_get_parent(obj); + + /*If the last character is # automatically index the children with the same name start*/ + if(parent && name_len > 0 && name[name_len - 1] == '#') { + uint32_t child_cnt = lv_obj_get_child_count(parent); + uint32_t cnt = 0; + uint32_t i; + for(i = 0; i < child_cnt; i++) { + lv_obj_t * child = lv_obj_get_child(parent, i); + /*All siblings older siblings are checked, craft the name of this widget*/ + if(child == obj) { + char num_buf[8]; + size_t num_len; + num_len = lv_snprintf(num_buf, sizeof(num_buf), "%d", cnt); + /*Is there enough space for the name and the index?*/ + if(buf_size > name_len + num_len) { + /*E.g. buf = "some_name_", so trim the # from the end*/ + lv_strncpy(buf, name, name_len - 1); + lv_strcpy(&buf[name_len - 1], num_buf); + } + else { + /*Use the name as it is as a fallback*/ + lv_strlcpy(buf, obj->spec_attr->name, buf_size); + } + break; + } + /*Check the older siblings. IF they start with the same name count them*/ + else { + const char * child_name = lv_obj_get_name(child); + if(child_name == NULL) { + /*If the name we are looking for start with the child's class name + *increment the index. E.g. _#*/ + size_t class_name_len = lv_strlen(child->class_p->name); + if(name_len > 3 && class_name_len == name_len - 2 && + lv_strncmp(child->class_p->name, name, class_name_len) == 0) { + cnt++; + } + } + /*The name is set, check if it's e.g. #*/ + else { + if(lv_strcmp(child->spec_attr->name, name) == 0) { + cnt++; + } + } + } + } } - /*Craft a name if not set. E.g. "lv_button_1"*/ else { - uint32_t idx = lv_obj_get_index_by_type(obj, obj->class_p); - lv_snprintf(buf, buf_size, "%s_%"LV_PRIu32, obj->class_p->name, idx); + /*Just use the set name*/ + lv_strlcpy(buf, obj->spec_attr->name, buf_size); } } diff --git a/src/core/lv_obj_tree.h b/src/core/lv_obj_tree.h index 3ed2011499..e869576345 100644 --- a/src/core/lv_obj_tree.h +++ b/src/core/lv_obj_tree.h @@ -196,36 +196,55 @@ uint32_t lv_obj_get_child_count_by_type(const lv_obj_t * obj, const lv_obj_class #if LV_USE_OBJ_NAME /** - * Set a name for a widget. The name will be allocated. + * Set a name for a widget. The name will be allocated and freed when the + * widget is deleted or a new name is set. * @param obj pointer to an object - * @param name the name to set + * @param name the name to set. If set to `NULL` the default "_#" + * name will be used. + * @note If the name ends with a `#`, older siblings with the same name + * will be counted, and the `#` will be replaced by the index of the + * given widget. For example, creating multiple widgets with the name + * "mybtn_#" will result in resolved names like "mybtn_0", "mybtn_1", + * "mybtn_2", etc. The name is resolved when `lv_obj_get_name_resolved` + * is called, so the result reflects the currently existing widgets at + * that time. */ void lv_obj_set_name(lv_obj_t * obj, const char * name); /** * Set a name for a widget. Only a pointer will be saved. * @param obj pointer to an object - * @param name the name to set + * @param name the name to set. If set to `NULL` the default "_#" + * name will be used. + * @note If the name ends with a `#`, older siblings with the same name + * will be counted, and the `#` will be replaced by the index of the + * given widget. For example, creating multiple widgets with the name + * "mybtn_#" will result in resolved names like "mybtn_0", "mybtn_1", + * "mybtn_2", etc. The name is resolved when `lv_obj_get_name_resolved` + * is called, so the result reflects the currently existing widgets at + * that time. */ void lv_obj_set_name_static(lv_obj_t * obj, const char * name); /** - * Get the set name as it was set + * Get the set name as it was set. * @param obj pointer to an object * @return get the set name or NULL if it wasn't set yet */ const char * lv_obj_get_name(const lv_obj_t * obj); /** - * Get the set name or craft a name automatically if there is no set name. - * The crafted names are built like + "_" + - * For example if a parent has two button and two label children the names will be - * "lv_button_0", "lv_button1", "lv_label_0", "lv_label_1" - * The comes from the `class->name` field. - * The index is 0 based. + * Get the set name or craft a name automatically. * @param obj pointer to an object * @param buf buffer to store the name * @param buf_size the size of the buffer in bytes + * @note If the name ends with a `#`, older siblings with the same name + * will be counted, and the `#` will be replaced by the index of the + * given widget. For example, creating multiple widgets with the name + * "mybtn_#" will result in resolved names like "mybtn_0", "mybtn_1", + * "mybtn_2", etc. The name is resolved when `lv_obj_get_name_resolved` + * is called, so the result reflects the currently existing widgets at + * that time. */ void lv_obj_get_name_resolved(const lv_obj_t * obj, char buf[], size_t buf_size); diff --git a/src/others/xml/lv_xml.c b/src/others/xml/lv_xml.c index 6b3e800e81..4b5d705a6d 100644 --- a/src/others/xml/lv_xml.c +++ b/src/others/xml/lv_xml.c @@ -40,7 +40,6 @@ #include "parsers/lv_xml_canvas_parser.h" #include "parsers/lv_xml_calendar_parser.h" #include "parsers/lv_xml_event_parser.h" -#include "parsers/lv_xml_update_parser.h" #include "../../libs/expat/expat.h" #include "../../draw/lv_draw_image.h" diff --git a/src/others/xml/lv_xml_component.c b/src/others/xml/lv_xml_component.c index 0ca7c3af7b..0636fb950f 100644 --- a/src/others/xml/lv_xml_component.c +++ b/src/others/xml/lv_xml_component.c @@ -93,6 +93,19 @@ lv_obj_t * lv_xml_component_process(lv_xml_parser_state_t * state, const char * lv_widget_processor_t * extended_proc = lv_xml_widget_get_extended_widget_processor(ctx->extends); extended_proc->apply_cb(state, attrs); + +#if LV_USE_OBJ_NAME + /*Set a default indexed name*/ + if(state->item) { + const char * value_of_name = lv_xml_get_value_of(attrs, "name"); + if(value_of_name) lv_obj_set_name(item, value_of_name); + else { + char name_buf[128]; + lv_snprintf(name_buf, sizeof(name_buf), "%s_#", scope->name); + lv_obj_set_name(state->item, name_buf); + } + } +#endif return item; } diff --git a/src/others/xml/parsers/lv_xml_obj_parser.c b/src/others/xml/parsers/lv_xml_obj_parser.c index 59d9e674da..b836349152 100644 --- a/src/others/xml/parsers/lv_xml_obj_parser.c +++ b/src/others/xml/parsers/lv_xml_obj_parser.c @@ -61,7 +61,9 @@ void lv_xml_obj_apply(lv_xml_parser_state_t * state, const char ** attrs) size_t name_len = lv_strlen(name); #if LV_USE_OBJ_NAME - if(lv_streq("name", name)) lv_obj_set_name(item, value); + if(lv_streq("name", name)) { + lv_obj_set_name(item, value); + } #endif if(lv_streq("x", name)) lv_obj_set_x(item, lv_xml_to_size(value)); else if(lv_streq("y", name)) lv_obj_set_y(item, lv_xml_to_size(value)); diff --git a/tests/src/test_cases/widgets/test_obj_tree.c b/tests/src/test_cases/widgets/test_obj_tree.c index 115da157cd..bc26e24c6e 100644 --- a/tests/src/test_cases/widgets/test_obj_tree.c +++ b/tests/src/test_cases/widgets/test_obj_tree.c @@ -209,6 +209,8 @@ void test_obj_get_by_name(void) lv_obj_t * cont3 = lv_obj_create(lv_screen_active()); lv_obj_set_name_static(cont3, "third_static"); lv_obj_set_name_static(cont3, "third"); + lv_obj_t * cont4 = lv_obj_create(lv_screen_active()); + lv_obj_t * root_label = lv_label_create(lv_screen_active()); lv_label_set_text(root_label, "Root"); @@ -223,6 +225,19 @@ void test_obj_get_by_name(void) lv_label_set_text(hello_label, "Hello"); lv_obj_set_name(hello_label, "my_label"); /*Same name as ofr the other label*/ + /*Test auto indexing*/ + + lv_obj_t * label0 = lv_label_create(cont3); + lv_obj_set_name(label0, "title_#"); + lv_obj_t * label1 = lv_label_create(cont3); + lv_obj_set_name(label1, "title_#"); + lv_obj_t * label2 = lv_label_create(cont3); //lv_label_0 + lv_obj_t * label3 = lv_label_create(cont3); + lv_obj_set_name(label3, "title_#?"); + lv_obj_t * label4 = lv_label_create(cont3); //lv_label_1 + lv_obj_t * label5 = lv_label_create(cont3); + lv_obj_set_name(label5, "title_#"); + lv_obj_t * found_obj; @@ -233,9 +248,12 @@ void test_obj_get_by_name(void) found_obj = lv_obj_get_child_by_name(lv_screen_active(), "first"); TEST_ASSERT_EQUAL(cont1, found_obj); - found_obj = lv_obj_get_child_by_name(lv_screen_active(), "lv_obj_2"); + found_obj = lv_obj_get_child_by_name(lv_screen_active(), "lv_obj_0"); TEST_ASSERT_EQUAL(cont2, found_obj); + found_obj = lv_obj_get_child_by_name(lv_screen_active(), "lv_obj_1"); + TEST_ASSERT_EQUAL(cont4, found_obj); + found_obj = lv_obj_get_child_by_name(lv_screen_active(), "fifth"); TEST_ASSERT_EQUAL(NULL, found_obj); @@ -258,11 +276,30 @@ void test_obj_get_by_name(void) found_obj = lv_obj_get_child_by_name(lv_screen_active(), ""); TEST_ASSERT_EQUAL(NULL, found_obj); + /* Test auto indexed names*/ + found_obj = lv_obj_get_child_by_name(lv_screen_active(), "third/title_0"); + TEST_ASSERT_EQUAL(label0, found_obj); + + found_obj = lv_obj_get_child_by_name(lv_screen_active(), "third/title_1"); + TEST_ASSERT_EQUAL(label1, found_obj); + + found_obj = lv_obj_get_child_by_name(lv_screen_active(), "third/lv_label_0"); + TEST_ASSERT_EQUAL(label2, found_obj); + + found_obj = lv_obj_get_child_by_name(lv_screen_active(), "third/title_#?"); + TEST_ASSERT_EQUAL(label3, found_obj); + + found_obj = lv_obj_get_child_by_name(lv_screen_active(), "third/lv_label_1"); + TEST_ASSERT_EQUAL(label4, found_obj); + + found_obj = lv_obj_get_child_by_name(lv_screen_active(), "third/title_2"); + TEST_ASSERT_EQUAL(label5, found_obj); + /*------------- * Find by name *------------*/ - found_obj = lv_obj_find_by_name(lv_screen_active(), "lv_obj_2"); + found_obj = lv_obj_find_by_name(lv_screen_active(), "lv_obj_0"); TEST_ASSERT_EQUAL(cont2, found_obj); found_obj = lv_obj_find_by_name(lv_screen_active(), "my_label"); @@ -270,6 +307,10 @@ void test_obj_get_by_name(void) found_obj = lv_obj_find_by_name(cont1, "my_label"); TEST_ASSERT_EQUAL(hello_label, found_obj); + + found_obj = lv_obj_find_by_name(lv_screen_active(), "title_2"); + TEST_ASSERT_EQUAL(label5, found_obj); } + #endif diff --git a/tests/src/test_cases/xml/test_xml_general.c b/tests/src/test_cases/xml/test_xml_general.c index 2e35beebd8..faaac6dbf1 100644 --- a/tests/src/test_cases/xml/test_xml_general.c +++ b/tests/src/test_cases/xml/test_xml_general.c @@ -228,22 +228,23 @@ void test_xml_error_resilience_syntax_ok(void) { const char * my_btn_xml = "" - "" - "" - "" - "" - "" - "" - "" - "