From 724fe880eed39a07efb508990e90ae9bb84deb77 Mon Sep 17 00:00:00 2001 From: Gabor Kiss-Vamosi Date: Thu, 12 Feb 2026 16:00:41 +0100 Subject: [PATCH] feat(obj): add back support for user flags (#9711) --- .../widgets/checkbox/lv_example_checkbox_2.c | 2 +- src/core/lv_obj.c | 20 +++++++-- src/core/lv_obj.h | 45 ++++++++++++------- src/core/lv_obj_private.h | 1 + src/widgets/property/lv_obj_properties.c | 5 ++- src/widgets/property/lv_obj_property_names.h | 2 +- tests/src/test_cases/widgets/test_obj_flags.c | 2 +- .../test_cases/widgets/test_obj_property.c | 3 +- 8 files changed, 55 insertions(+), 25 deletions(-) diff --git a/examples/widgets/checkbox/lv_example_checkbox_2.c b/examples/widgets/checkbox/lv_example_checkbox_2.c index d79dd46740..f8f77e5263 100644 --- a/examples/widgets/checkbox/lv_example_checkbox_2.c +++ b/examples/widgets/checkbox/lv_example_checkbox_2.c @@ -43,7 +43,7 @@ void lv_example_checkbox_2(void) lv_obj_add_event_cb(obj, event_cb, LV_EVENT_VALUE_CHANGED, NULL); /*This makes the checkboxes act as radio buttons*/ - lv_obj_add_flag(obj, LV_OBJ_FLAG_RADIO_BUTTON); + lv_obj_set_radio_button(obj, true); lv_obj_add_style(obj, &style_radio, LV_PART_INDICATOR); lv_obj_add_style(obj, &style_radio_chk, LV_PART_INDICATOR | LV_STATE_CHECKED); diff --git a/src/core/lv_obj.c b/src/core/lv_obj.c index ff6f38f4ee..9fbd04020e 100644 --- a/src/core/lv_obj.c +++ b/src/core/lv_obj.c @@ -355,6 +355,12 @@ void lv_obj_set_state(lv_obj_t * obj, lv_state_t state, bool v) else lv_obj_remove_state(obj, state); } +void lv_obj_set_radio_button(lv_obj_t * obj, bool en) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + obj->radio_button = en; +} + /*======================= * Getter functions *======================*/ @@ -387,6 +393,12 @@ bool lv_obj_has_state(const lv_obj_t * obj, lv_state_t state) return !!(obj->state & state); } +bool lv_obj_is_radio_button(const lv_obj_t * obj) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + return obj->radio_button; +} + lv_group_t * lv_obj_get_group(const lv_obj_t * obj) { LV_ASSERT_OBJ(obj, MY_CLASS); @@ -862,7 +874,7 @@ static void lv_obj_event(const lv_obj_class_t * class_p, lv_event_t * e) lv_obj_add_state(obj, LV_STATE_CHECKED); } /*Radio buttons can't be checked off directly*/ - else if(!lv_obj_has_flag(obj, LV_OBJ_FLAG_RADIO_BUTTON)) { + else if(!lv_obj_is_radio_button(obj)) { lv_obj_remove_state(obj, LV_STATE_CHECKED); } if(was_checked != lv_obj_has_state(obj, LV_STATE_CHECKED)) { @@ -872,7 +884,7 @@ static void lv_obj_event(const lv_obj_class_t * class_p, lv_event_t * e) } } else if(code == LV_EVENT_VALUE_CHANGED) { - if(lv_obj_has_flag(obj, LV_OBJ_FLAG_RADIO_BUTTON) && lv_obj_has_state(obj, LV_STATE_CHECKED)) { + if(lv_obj_is_radio_button(obj) && lv_obj_has_state(obj, LV_STATE_CHECKED)) { lv_obj_t * parent = lv_obj_get_parent(obj); if(parent) { uint32_t child_cnt = lv_obj_get_child_count(parent); @@ -881,7 +893,7 @@ static void lv_obj_event(const lv_obj_class_t * class_p, lv_event_t * e) lv_obj_t * sibling = lv_obj_get_child(parent, i); if(obj == sibling) continue; - if(lv_obj_has_flag(sibling, LV_OBJ_FLAG_RADIO_BUTTON) && lv_obj_has_state(sibling, LV_STATE_CHECKED)) { + if(lv_obj_is_radio_button(sibling) && lv_obj_has_state(sibling, LV_STATE_CHECKED)) { lv_obj_remove_state(sibling, LV_STATE_CHECKED); lv_result_t res = lv_obj_send_event(sibling, LV_EVENT_VALUE_CHANGED, NULL); if(res != LV_RESULT_OK) return; @@ -909,7 +921,7 @@ static void lv_obj_event(const lv_obj_class_t * class_p, lv_event_t * e) } else if(c == LV_KEY_LEFT || c == LV_KEY_DOWN) { /*Radio buttons can't be checked off directly*/ - if(!lv_obj_has_flag(obj, LV_OBJ_FLAG_RADIO_BUTTON)) { + if(!lv_obj_is_radio_button(obj)) { lv_obj_remove_state(obj, LV_STATE_CHECKED); } } diff --git a/src/core/lv_obj.h b/src/core/lv_obj.h index 097e8d4876..d18887e132 100644 --- a/src/core/lv_obj.h +++ b/src/core/lv_obj.h @@ -70,18 +70,19 @@ typedef enum { LV_OBJ_FLAG_OVERFLOW_VISIBLE = (1u << 20),/**< Do not clip the children to the parent's ext draw size*/ LV_OBJ_FLAG_EVENT_TRICKLE = (1u << 21), /**< Propagate the events to the children too*/ LV_OBJ_FLAG_STATE_TRICKLE = (1u << 22), /**< Propagate the states to the children too*/ - LV_OBJ_FLAG_RADIO_BUTTON = (1u << 23), /**< Allow only one RADIO_BUTTON sibling to be checked*/ - LV_OBJ_FLAG_LAYOUT_1 = (1u << 24), /**< Custom flag, free to use by layouts*/ - LV_OBJ_FLAG_LAYOUT_2 = (1u << 25), /**< Custom flag, free to use by layouts*/ + LV_OBJ_FLAG_LAYOUT_1 = (1u << 23), /**< Custom flag, free to use by layouts*/ + LV_OBJ_FLAG_LAYOUT_2 = (1u << 24), /**< Custom flag, free to use by layouts*/ #if LV_USE_FLEX LV_OBJ_FLAG_FLEX_IN_NEW_TRACK = LV_OBJ_FLAG_LAYOUT_1, /**< Start a new flex track on this item*/ #endif - LV_OBJ_FLAG_WIDGET_1 = (1u << 26), /**< Custom flag, free to use by widget*/ - LV_OBJ_FLAG_WIDGET_2 = (1u << 27), /**< Custom flag, free to use by widget*/ - LV_OBJ_FLAG_USER_1 = (1u << 28), /**< Custom flag, free to use by user*/ - LV_OBJ_FLAG_USER_2 = (1u << 29), /**< Custom flag, free to use by user*/ + LV_OBJ_FLAG_WIDGET_1 = (1u << 25), /**< Custom flag, free to use by widget*/ + LV_OBJ_FLAG_WIDGET_2 = (1u << 26), /**< Custom flag, free to use by widget*/ + LV_OBJ_FLAG_USER_1 = (1u << 27), /**< Custom flag, free to use by user*/ + LV_OBJ_FLAG_USER_2 = (1u << 28), /**< Custom flag, free to use by user*/ + LV_OBJ_FLAG_USER_3 = (1u << 29), /**< Custom flag, free to use by user*/ + LV_OBJ_FLAG_USER_4 = (1u << 30), /**< Custom flag, free to use by user*/ } lv_obj_flag_t; #if LV_USE_OBJ_PROPERTY @@ -111,14 +112,15 @@ enum _lv_signed_prop_id_t { LV_PROPERTY_ID(OBJ, FLAG_OVERFLOW_VISIBLE, LV_PROPERTY_TYPE_INT, 20), LV_PROPERTY_ID(OBJ, FLAG_EVENT_TRICKLE, LV_PROPERTY_TYPE_INT, 21), LV_PROPERTY_ID(OBJ, FLAG_STATE_TRICKLE, LV_PROPERTY_TYPE_INT, 22), - LV_PROPERTY_ID(OBJ, FLAG_RADIO_BUTTON, LV_PROPERTY_TYPE_INT, 23), - LV_PROPERTY_ID(OBJ, FLAG_LAYOUT_1, LV_PROPERTY_TYPE_INT, 24), - LV_PROPERTY_ID(OBJ, FLAG_LAYOUT_2, LV_PROPERTY_TYPE_INT, 25), - LV_PROPERTY_ID(OBJ, FLAG_FLEX_IN_NEW_TRACK, LV_PROPERTY_TYPE_INT, 24), /*Mapped to FLAG_LAYOUT_1*/ - LV_PROPERTY_ID(OBJ, FLAG_WIDGET_1, LV_PROPERTY_TYPE_INT, 26), - LV_PROPERTY_ID(OBJ, FLAG_WIDGET_2, LV_PROPERTY_TYPE_INT, 27), - LV_PROPERTY_ID(OBJ, FLAG_USER_1, LV_PROPERTY_TYPE_INT, 28), - LV_PROPERTY_ID(OBJ, FLAG_USER_2, LV_PROPERTY_TYPE_INT, 29), + LV_PROPERTY_ID(OBJ, FLAG_LAYOUT_1, LV_PROPERTY_TYPE_INT, 23), + LV_PROPERTY_ID(OBJ, FLAG_LAYOUT_2, LV_PROPERTY_TYPE_INT, 24), + LV_PROPERTY_ID(OBJ, FLAG_FLEX_IN_NEW_TRACK, LV_PROPERTY_TYPE_INT, 23), /*Mapped to FLAG_LAYOUT_1*/ + LV_PROPERTY_ID(OBJ, FLAG_WIDGET_1, LV_PROPERTY_TYPE_INT, 25), + LV_PROPERTY_ID(OBJ, FLAG_WIDGET_2, LV_PROPERTY_TYPE_INT, 26), + LV_PROPERTY_ID(OBJ, FLAG_USER_1, LV_PROPERTY_TYPE_INT, 27), + LV_PROPERTY_ID(OBJ, FLAG_USER_2, LV_PROPERTY_TYPE_INT, 28), + LV_PROPERTY_ID(OBJ, FLAG_USER_3, LV_PROPERTY_TYPE_INT, 29), + LV_PROPERTY_ID(OBJ, FLAG_USER_4, LV_PROPERTY_TYPE_INT, 30), LV_PROPERTY_ID(OBJ, FLAG_END, LV_PROPERTY_TYPE_INT, 30), LV_PROPERTY_ID(OBJ, STATE_START, LV_PROPERTY_TYPE_INT, 31), @@ -245,6 +247,13 @@ void lv_obj_set_state(lv_obj_t * obj, lv_state_t state, bool v); */ void lv_obj_set_user_data(lv_obj_t * obj, void * user_data); + +/** Allow only one RADIO_BUTTON sibling to be checked + * @param obj pointer to a widget + * @param en enable or disable radio button behavior + */ +void lv_obj_set_radio_button(lv_obj_t * obj, bool en); + /*======================= * Getter functions *======================*/ @@ -280,6 +289,12 @@ lv_state_t lv_obj_get_state(const lv_obj_t * obj); */ bool lv_obj_has_state(const lv_obj_t * obj, lv_state_t state); +/** Get whether the object is a radio button + * @param obj pointer to a widget + * @return true if radio button behavior is enabled + */ +bool lv_obj_is_radio_button(const lv_obj_t * obj); + /** * Get the group of the object * @param obj pointer to an object diff --git a/src/core/lv_obj_private.h b/src/core/lv_obj_private.h index b44fe39348..adec3f2b09 100644 --- a/src/core/lv_obj_private.h +++ b/src/core/lv_obj_private.h @@ -85,6 +85,7 @@ struct _lv_obj_t { uint16_t h_ignore_size : 1; /* ignore this obj when calculating content height of parent */ uint16_t w_ignore_size : 1; /* ignore this obj when calculating content width of parent */ uint16_t is_deleting : 1; + uint16_t radio_button : 1; /**< Allow only one RADIO_BUTTON sibling to be checked*/ /** The widget is rendered at least once already. * It's used to skip initial animations and transitions. */ diff --git a/src/widgets/property/lv_obj_properties.c b/src/widgets/property/lv_obj_properties.c index b40b6e34fa..71d70db0d5 100644 --- a/src/widgets/property/lv_obj_properties.c +++ b/src/widgets/property/lv_obj_properties.c @@ -14,7 +14,7 @@ * Generated code from properties.py */ /* *INDENT-OFF* */ -const lv_property_name_t lv_obj_property_names[75] = { +const lv_property_name_t lv_obj_property_names[76] = { {"align", LV_PROPERTY_OBJ_ALIGN,}, {"child_count", LV_PROPERTY_OBJ_CHILD_COUNT,}, {"content_height", LV_PROPERTY_OBJ_CONTENT_HEIGHT,}, @@ -38,7 +38,6 @@ const lv_property_name_t lv_obj_property_names[75] = { {"flag_layout_2", LV_PROPERTY_OBJ_FLAG_LAYOUT_2,}, {"flag_overflow_visible", LV_PROPERTY_OBJ_FLAG_OVERFLOW_VISIBLE,}, {"flag_press_lock", LV_PROPERTY_OBJ_FLAG_PRESS_LOCK,}, - {"flag_radio_button", LV_PROPERTY_OBJ_FLAG_RADIO_BUTTON,}, {"flag_scroll_chain_hor", LV_PROPERTY_OBJ_FLAG_SCROLL_CHAIN_HOR,}, {"flag_scroll_chain_ver", LV_PROPERTY_OBJ_FLAG_SCROLL_CHAIN_VER,}, {"flag_scroll_elastic", LV_PROPERTY_OBJ_FLAG_SCROLL_ELASTIC,}, @@ -53,6 +52,8 @@ const lv_property_name_t lv_obj_property_names[75] = { {"flag_state_trickle", LV_PROPERTY_OBJ_FLAG_STATE_TRICKLE,}, {"flag_user_1", LV_PROPERTY_OBJ_FLAG_USER_1,}, {"flag_user_2", LV_PROPERTY_OBJ_FLAG_USER_2,}, + {"flag_user_3", LV_PROPERTY_OBJ_FLAG_USER_3,}, + {"flag_user_4", LV_PROPERTY_OBJ_FLAG_USER_4,}, {"flag_widget_1", LV_PROPERTY_OBJ_FLAG_WIDGET_1,}, {"flag_widget_2", LV_PROPERTY_OBJ_FLAG_WIDGET_2,}, {"h", LV_PROPERTY_OBJ_H,}, diff --git a/src/widgets/property/lv_obj_property_names.h b/src/widgets/property/lv_obj_property_names.h index 6e34861f10..c15ce33f71 100644 --- a/src/widgets/property/lv_obj_property_names.h +++ b/src/widgets/property/lv_obj_property_names.h @@ -23,7 +23,7 @@ extern const lv_property_name_t lv_led_property_names[2]; extern const lv_property_name_t lv_line_property_names[1]; extern const lv_property_name_t lv_menu_property_names[2]; - extern const lv_property_name_t lv_obj_property_names[75]; + extern const lv_property_name_t lv_obj_property_names[76]; extern const lv_property_name_t lv_roller_property_names[3]; extern const lv_property_name_t lv_scale_property_names[8]; extern const lv_property_name_t lv_slider_property_names[8]; diff --git a/tests/src/test_cases/widgets/test_obj_flags.c b/tests/src/test_cases/widgets/test_obj_flags.c index ea925343dc..6f3dcb25c7 100644 --- a/tests/src/test_cases/widgets/test_obj_flags.c +++ b/tests/src/test_cases/widgets/test_obj_flags.c @@ -159,7 +159,7 @@ void test_obj_flag_radio_button(void) for(uint32_t i = 0; i < 5; i++) { cb[i] = lv_checkbox_create(scr); lv_obj_set_y(cb[i], i * 50); - lv_obj_add_flag(cb[i], LV_OBJ_FLAG_RADIO_BUTTON); + lv_obj_set_radio_button(cb[i], true); lv_group_add_obj(g, cb[i]); lv_obj_add_event_cb(cb[i], event_cb, LV_EVENT_VALUE_CHANGED, &called[i]); called[i] = 0; diff --git a/tests/src/test_cases/widgets/test_obj_property.c b/tests/src/test_cases/widgets/test_obj_property.c index c7f5159d4f..c3381e3c5b 100644 --- a/tests/src/test_cases/widgets/test_obj_property.c +++ b/tests/src/test_cases/widgets/test_obj_property.c @@ -147,7 +147,6 @@ void test_obj_property_flag(void) { LV_OBJ_FLAG_FLOATING, LV_PROPERTY_OBJ_FLAG_FLOATING }, { LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS, LV_PROPERTY_OBJ_FLAG_SEND_DRAW_TASK_EVENTS }, { LV_OBJ_FLAG_OVERFLOW_VISIBLE, LV_PROPERTY_OBJ_FLAG_OVERFLOW_VISIBLE }, - { LV_OBJ_FLAG_RADIO_BUTTON, LV_PROPERTY_OBJ_FLAG_RADIO_BUTTON }, { LV_OBJ_FLAG_FLEX_IN_NEW_TRACK, LV_PROPERTY_OBJ_FLAG_FLEX_IN_NEW_TRACK }, { LV_OBJ_FLAG_LAYOUT_1, LV_PROPERTY_OBJ_FLAG_LAYOUT_1 }, { LV_OBJ_FLAG_LAYOUT_2, LV_PROPERTY_OBJ_FLAG_LAYOUT_2 }, @@ -155,6 +154,8 @@ void test_obj_property_flag(void) { LV_OBJ_FLAG_WIDGET_2, LV_PROPERTY_OBJ_FLAG_WIDGET_2 }, { LV_OBJ_FLAG_USER_1, LV_PROPERTY_OBJ_FLAG_USER_1 }, { LV_OBJ_FLAG_USER_2, LV_PROPERTY_OBJ_FLAG_USER_2 }, + { LV_OBJ_FLAG_USER_3, LV_PROPERTY_OBJ_FLAG_USER_3 }, + { LV_OBJ_FLAG_USER_4, LV_PROPERTY_OBJ_FLAG_USER_4 }, }; lv_obj_t * obj = lv_obj_create(lv_screen_active());