From 726620d0fcc86075d6f6886da8b0f1e5fdacda06 Mon Sep 17 00:00:00 2001 From: Neo Xu Date: Thu, 26 Oct 2023 15:39:30 +0800 Subject: [PATCH] feat(obj): add unified obj property set/get API (#4579) Signed-off-by: Xu Xingliang --- docs/others/index.rst | 1 + docs/others/obj_property.rst | 74 ++++++++++ lv_conf_template.h | 3 + src/core/lv_obj.c | 56 ++++++- src/core/lv_obj.h | 46 ++++++ src/core/lv_obj_class.h | 9 ++ src/core/lv_obj_property.c | 179 +++++++++++++++++++++++ src/core/lv_obj_property.h | 128 ++++++++++++++++ src/lv_conf_internal.h | 9 ++ src/widgets/image/lv_image.c | 51 +++++++ src/widgets/image/lv_image.h | 22 +++ tests/CMakeLists.txt | 2 +- tests/src/lv_test_conf_full.h | 1 + tests/src/test_cases/test_obj_property.c | 157 ++++++++++++++++++++ 14 files changed, 736 insertions(+), 2 deletions(-) create mode 100644 docs/others/obj_property.rst create mode 100644 src/core/lv_obj_property.c create mode 100644 src/core/lv_obj_property.h create mode 100644 tests/src/test_cases/test_obj_property.c diff --git a/docs/others/index.rst b/docs/others/index.rst index 4c81bd7dc..d185e68af 100644 --- a/docs/others/index.rst +++ b/docs/others/index.rst @@ -14,3 +14,4 @@ Others imgfont ime_pinyin obj_id + obj_property diff --git a/docs/others/obj_property.rst b/docs/others/obj_property.rst new file mode 100644 index 000000000..fc52f6898 --- /dev/null +++ b/docs/others/obj_property.rst @@ -0,0 +1,74 @@ +=============== +Widget Property +=============== + +Widgets have many properties that can decide what they look like and how they behave. +For example, the size, position, color, font, etc. are properties of a widget. +Specially, widget local style is also a property of a widget. + +Usage +----- + +Two APIs are provided to get/set widget properties. It can be enabled by setting +``LV_USE_OBJ_PROPERTY`` to 1 in `lv_conf.h`. + +.. code:: c + + typedef struct { + lv_prop_id_t id; + union { + int32_t num; /**< Number integer number (opacity, enums, booleans or "normal" numbers)*/ + const void * ptr; /**< Constant pointers (font, cone text, etc)*/ + lv_color_t color; /**< Colors*/ + lv_style_value_t _style; /**< A place holder for style value which is same as property value.*/ + }; + } lv_property_t; + + lv_result_t lv_obj_set_property(struct _lv_obj_t * obj, const lv_property_t * value); + lv_property_t lv_obj_get_property(struct _lv_obj_t * obj, lv_prop_id_t id); + + +Property ID +~~~~~~~~~~~ + +``lv_prop_id_t`` identifies which property to get/set. ``lv_property_t`` is an enum value +defined in `lv_obj_property.h` that are grouped by widget class. You can add your own +widget property ID following same rule and using helper macro ``LV_PROPERTY_ID``. +Do make sure the ID is unique across all widgets. + + +Property ID is a 32-bit value. The higher 4bits indicates the property value type. +The lower 28bits is the property ID. + +Note that ``lv_style_prop_t`` is also valid property ID. + +Property Value +~~~~~~~~~~~~~~ + +Property value is a union of all possible property types including integer, pointer and color. +``_style`` is kept their just to indicate it's compatible with ``style`` value type. + + +A Step Further +-------------- +The unified widget property set/get API is useful when developing wrapper layer for other +modules like micropython, lua, or for an external animation engine. + +For pointer type of property value, which typically points to a specific struct, it still needs +additional code to convert values from dict, table etc to a C struct before setting to widget. + +Another possible use case is to ease of creating UI from lots of code. For example, you can gather +all properties to an array now and set properties with a for loop. + +.. code:: c + + lv_property_t props[] = { + { .id = LV_PROPERTY_IMAGE_SRC, .ptr = &img_demo_widgets_avatar, }, + { .id = LV_PROPERTY_IMAGE_PIVOT, .ptr = &pivot_50, }, + { .id = LV_PROPERTY_IMAGE_SCALE, .num = 128, }, + { .id = LV_PROPERTY_OBJ_FLAG_CLICKABLE, .num = 1, }, + { .id = LV_STYLE_IMAGE_OPA, .num = 128, }, + { .id = LV_STYLE_BG_COLOR, .color = (lv_color_t){.red = 0x11, .green = 0x22, .blue = 0x33}, }, + } + + LV_OBJ_PROPERTY_ARRAY_SET(obj, props); diff --git a/lv_conf_template.h b/lv_conf_template.h index c8a5246f0..90f76e216 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -277,6 +277,9 @@ /* Use lvgl builtin method for obj ID */ #define LV_USE_OBJ_ID_BUILTIN 0 +/*Use obj property set/get API*/ +#define LV_USE_OBJ_PROPERTY 0 + /*===================== * COMPILER SETTINGS *====================*/ diff --git a/src/core/lv_obj.c b/src/core/lv_obj.c index de1d49ec5..c06aa0669 100644 --- a/src/core/lv_obj.c +++ b/src/core/lv_obj.c @@ -45,10 +45,28 @@ 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 *); +#endif /********************** * STATIC VARIABLES **********************/ +#if LV_USE_OBJ_PROPERTY +static const lv_property_ops_t properties[] = { + { + .id = LV_PROPERTY_OBJ_PARENT, + .setter = lv_obj_set_parent, + .getter = lv_obj_get_parent, + }, + { + .id = LV_PROPERTY_ID_ANY, + .setter = lv_obj_set_any, + .getter = lv_obj_get_any, + } +}; +#endif const lv_obj_class_t lv_obj_class = { .constructor_cb = lv_obj_constructor, @@ -61,6 +79,12 @@ const lv_obj_class_t lv_obj_class = { .instance_size = (sizeof(lv_obj_t)), .base_class = NULL, .name = "obj", +#if LV_USE_OBJ_PROPERTY + .prop_index_start = LV_PROPERTY_OBJ_START, + .prop_index_end = LV_PROPERTY_OBJ_END, + .properties = properties, + .properties_count = sizeof(properties) / sizeof(properties[0]), +#endif }; /********************** @@ -183,7 +207,6 @@ void lv_obj_remove_state(lv_obj_t * obj, lv_state_t state) } } - void lv_obj_set_state(lv_obj_t * obj, lv_state_t state, bool v) { if(v) lv_obj_add_state(obj, state); @@ -784,3 +807,34 @@ static bool obj_valid_child(const lv_obj_t * parent, const lv_obj_t * obj_to_fin } return false; } + +#if LV_USE_OBJ_PROPERTY +static lv_result_t lv_obj_set_any(lv_obj_t * obj, lv_prop_id_t id, const lv_property_t * prop) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + + if(id >= LV_PROPERTY_OBJ_FLAG_START && id <= LV_PROPERTY_OBJ_FLAG_END) { + lv_obj_flag_t flag = 1L << (id - LV_PROPERTY_OBJ_FLAG_START); + if(prop->num) lv_obj_add_flag(obj, flag); + else lv_obj_remove_flag(obj, flag); + return LV_RESULT_OK; + } + else { + return LV_RESULT_INVALID; + } +} + +static lv_result_t lv_obj_get_any(const lv_obj_t * obj, lv_prop_id_t id, lv_property_t * prop) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + if(id >= LV_PROPERTY_OBJ_FLAG_START && id <= LV_PROPERTY_OBJ_FLAG_END) { + lv_obj_flag_t flag = 1L << (id - LV_PROPERTY_OBJ_FLAG_START); + prop->id = id; + prop->num = obj->flags & flag; + return LV_RESULT_OK; + } + else { + return LV_RESULT_INVALID; + } +} +#endif diff --git a/src/core/lv_obj.h b/src/core/lv_obj.h index 621989e27..902d4242b 100644 --- a/src/core/lv_obj.h +++ b/src/core/lv_obj.h @@ -22,6 +22,7 @@ extern "C" { #include "../misc/lv_area.h" #include "../misc/lv_color.h" #include "../misc/lv_assert.h" +#include "lv_obj_property.h" /********************* * DEFINES @@ -92,6 +93,9 @@ typedef uint32_t lv_part_t; /** * On/Off features controlling the object's behavior. * OR-ed values are possible + * + * Note: update obj flags corresponding properties below + * whenever add/remove flags or change bit definition of flags. */ typedef enum { LV_OBJ_FLAG_HIDDEN = (1L << 0), /**< Make the object hidden. (Like it wasn't there at all)*/ @@ -137,6 +141,48 @@ typedef _lv_obj_flag_t lv_obj_flag_t; typedef uint32_t lv_obj_flag_t; #endif /*DOXYGEN*/ +#if LV_USE_OBJ_PROPERTY +enum { + /*OBJ flag properties */ + LV_PROPERTY_ID(OBJ, FLAG_START, LV_PROPERTY_TYPE_INT, 0), + LV_PROPERTY_ID(OBJ, FLAG_HIDDEN, LV_PROPERTY_TYPE_INT, 0), + LV_PROPERTY_ID(OBJ, FLAG_CLICKABLE, LV_PROPERTY_TYPE_INT, 1), + LV_PROPERTY_ID(OBJ, FLAG_CLICK_FOCUSABLE, LV_PROPERTY_TYPE_INT, 2), + LV_PROPERTY_ID(OBJ, FLAG_CHECKABLE, LV_PROPERTY_TYPE_INT, 3), + LV_PROPERTY_ID(OBJ, FLAG_SCROLLABLE, LV_PROPERTY_TYPE_INT, 4), + LV_PROPERTY_ID(OBJ, FLAG_SCROLL_ELASTIC, LV_PROPERTY_TYPE_INT, 5), + LV_PROPERTY_ID(OBJ, FLAG_SCROLL_MOMENTUM, LV_PROPERTY_TYPE_INT, 6), + LV_PROPERTY_ID(OBJ, FLAG_SCROLL_ONE, LV_PROPERTY_TYPE_INT, 7), + LV_PROPERTY_ID(OBJ, FLAG_SCROLL_CHAIN_HOR, LV_PROPERTY_TYPE_INT, 8), + LV_PROPERTY_ID(OBJ, FLAG_SCROLL_CHAIN_VER, LV_PROPERTY_TYPE_INT, 9), + LV_PROPERTY_ID(OBJ, FLAG_SCROLL_ON_FOCUS, LV_PROPERTY_TYPE_INT, 10), + LV_PROPERTY_ID(OBJ, FLAG_SCROLL_WITH_ARROW, LV_PROPERTY_TYPE_INT, 11), + LV_PROPERTY_ID(OBJ, FLAG_SNAPPABLE, LV_PROPERTY_TYPE_INT, 12), + LV_PROPERTY_ID(OBJ, FLAG_PRESS_LOCK, LV_PROPERTY_TYPE_INT, 13), + LV_PROPERTY_ID(OBJ, FLAG_EVENT_BUBBLE, LV_PROPERTY_TYPE_INT, 14), + LV_PROPERTY_ID(OBJ, FLAG_GESTURE_BUBBLE, LV_PROPERTY_TYPE_INT, 15), + LV_PROPERTY_ID(OBJ, FLAG_ADV_HITTEST, LV_PROPERTY_TYPE_INT, 16), + LV_PROPERTY_ID(OBJ, FLAG_IGNORE_LAYOUT, LV_PROPERTY_TYPE_INT, 17), + LV_PROPERTY_ID(OBJ, FLAG_FLOATING, LV_PROPERTY_TYPE_INT, 18), + LV_PROPERTY_ID(OBJ, FLAG_SEND_DRAW_TASK_EVENTS, LV_PROPERTY_TYPE_INT, 19), + LV_PROPERTY_ID(OBJ, FLAG_OVERFLOW_VISIBLE, LV_PROPERTY_TYPE_INT, 20), + LV_PROPERTY_ID(OBJ, FLAG_FLEX_IN_NEW_TRACK, LV_PROPERTY_TYPE_INT, 21), + 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_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), + + /*OBJ normal properties*/ + LV_PROPERTY_ID(OBJ, PARENT, LV_PROPERTY_TYPE_POINTER, 31), + + LV_PROPERTY_OBJ_END, +}; +#endif #include "lv_obj_tree.h" #include "lv_obj_pos.h" diff --git a/src/core/lv_obj_class.h b/src/core/lv_obj_class.h index 6cddf3f8c..61f1c35e6 100644 --- a/src/core/lv_obj_class.h +++ b/src/core/lv_obj_class.h @@ -15,6 +15,7 @@ extern "C" { *********************/ #include #include +#include "lv_obj_property.h" #include "../misc/lv_area.h" /********************* @@ -61,6 +62,14 @@ typedef struct _lv_obj_class_t { /*class_p is the class in which event is being processed.*/ void (*event_cb)(const struct _lv_obj_class_t * class_p, struct _lv_event_t * e); /**< Widget type specific event function*/ + +#if LV_USE_OBJ_PROPERTY + uint32_t prop_index_start; + uint32_t prop_index_end; + const lv_property_ops_t * properties; + uint32_t properties_count; +#endif + void * user_data; const char * name; lv_coord_t width_def; diff --git a/src/core/lv_obj_property.c b/src/core/lv_obj_property.c new file mode 100644 index 000000000..1e9a14b30 --- /dev/null +++ b/src/core/lv_obj_property.c @@ -0,0 +1,179 @@ +/** + * @file lv_obj_id.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "../core/lv_obj.h" +#include "../stdlib/lv_string.h" +#include "lv_obj_property.h" + +#if LV_USE_OBJ_PROPERTY + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef void (*lv_property_set_int_t)(struct _lv_obj_t *, int32_t); +typedef void (*lv_property_set_pointer_t)(struct _lv_obj_t *, const void *); +typedef void (*lv_property_set_color_t)(struct _lv_obj_t *, lv_color_t); +typedef lv_result_t (*lv_property_setter_t)(struct _lv_obj_t *, lv_prop_id_t, const lv_property_t *); + +typedef int32_t (*lv_property_get_int_t)(const struct _lv_obj_t *); +typedef void * (*lv_property_get_pointer_t)(const struct _lv_obj_t *); +typedef lv_color_t (*lv_property_get_color_t)(const struct _lv_obj_t *); +typedef lv_result_t (*lv_property_getter_t)(const struct _lv_obj_t *, lv_prop_id_t, lv_property_t *); + +/********************** + * STATIC PROTOTYPES + **********************/ + +static lv_result_t obj_property(lv_obj_t * obj, lv_prop_id_t id, lv_property_t * value, bool set); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_result_t lv_obj_set_property(lv_obj_t * obj, const lv_property_t * value) +{ + LV_ASSERT(obj && value); + + if(value->id == LV_PROPERTY_ID_INVALID) { + LV_LOG_WARN("invalid property id set to %p\n", obj); + return LV_RESULT_INVALID; + } + + if(value->id < LV_PROPERTY_ID_START) { + lv_obj_set_local_style_prop(obj, value->id, value->_style, 0); + return LV_RESULT_OK; + } + + return obj_property(obj, value->id, (lv_property_t *)value, true); +} + +lv_result_t lv_obj_set_properties(struct _lv_obj_t * obj, const lv_property_t * value, uint32_t count) +{ + for(uint32_t i = 0; i < count; i++) { + lv_result_t result = lv_obj_set_property(obj, &value[i]); + if(result != LV_RESULT_OK) { + return result; + } + } + + return LV_RESULT_OK; +} + +lv_property_t lv_obj_get_property(lv_obj_t * obj, lv_prop_id_t id) +{ + lv_result_t result; + lv_property_t value; + + if(id == LV_PROPERTY_ID_INVALID) { + LV_LOG_WARN("invalid property id to get from %p\n", obj); + value.id = 0; + value.num = 0; + return value; + } + + if(id < LV_PROPERTY_ID_START) { + lv_obj_get_local_style_prop(obj, id, &value._style, 0); + value.id = id; + return value; + } + + result = obj_property(obj, id, &value, false); + if(result != LV_RESULT_OK) + value.id = 0; + + return value; +} + +static lv_result_t obj_property(lv_obj_t * obj, lv_prop_id_t id, lv_property_t * value, bool set) +{ + const lv_property_ops_t * properties; + const lv_property_ops_t * prop; + + const lv_obj_class_t * clz; + uint32_t index = LV_PROPERTY_ID_INDEX(id); + + for(clz = obj->class_p ; clz; clz = clz->base_class) { + properties = clz->properties; + if(properties == NULL) { + /* try base class*/ + continue; + } + + if(id != LV_PROPERTY_ID_ANY && (index < clz->prop_index_start || index > clz->prop_index_end)) { + /* try base class*/ + continue; + } + + /*Check if there's setter available for this class*/ + for(uint32_t i = 0; i < clz->properties_count; i++) { + prop = &properties[i]; + + /*pass id and value directly to widget's property method*/ + if(prop->id == LV_PROPERTY_ID_ANY) { + value->id = prop->id; + if(set) return ((lv_property_setter_t)prop->setter)(obj, id, value); + else return ((lv_property_getter_t)prop->getter)(obj, id, value); + } + + /*Not this id, check next*/ + if(prop->id != id) + continue; + + /*id matched but we got null pointer to functions*/ + if(set ? prop->setter == NULL : prop->getter == NULL) { + LV_LOG_WARN("null %s provided, id: %d\n", set ? "setter" : "getter", id); + return LV_RESULT_INVALID; + } + + /*Update value id if it's a read*/ + if(!set) value->id = prop->id; + + switch(LV_PROPERTY_ID_TYPE(prop->id)) { + case LV_PROPERTY_TYPE_INT: + if(set)((lv_property_set_int_t)(prop->setter))(obj, value->num); + else value->num = ((lv_property_get_int_t)(prop->getter))(obj); + break; + case LV_PROPERTY_TYPE_POINTER: + case LV_PROPERTY_TYPE_IMGSRC: + if(set)((lv_property_set_pointer_t)(prop->setter))(obj, value->ptr); + else value->ptr = ((lv_property_get_pointer_t)(prop->getter))(obj); + break; + case LV_PROPERTY_TYPE_COLOR: + if(set)((lv_property_set_color_t)prop->setter)(obj, value->color); + else value->color = ((lv_property_get_color_t)(prop->getter))(obj); + break; + default: + LV_LOG_WARN("unknown property id: 0x%08x\n", prop->id); + return LV_RESULT_INVALID; + break; + } + + return LV_RESULT_OK; + } + + /*If no setter found, try base class then*/ + } + + LV_LOG_WARN("unknown property id: 0x%08x\n", id); + return LV_RESULT_INVALID; +} + +#endif /*LV_USE_OBJ_PROPERTY*/ diff --git a/src/core/lv_obj_property.h b/src/core/lv_obj_property.h new file mode 100644 index 000000000..eaf3006e1 --- /dev/null +++ b/src/core/lv_obj_property.h @@ -0,0 +1,128 @@ +/** + * @file lv_obj_property.h + * + */ + +#ifndef LV_OBJ_PROPERTY_H +#define LV_OBJ_PROPERTY_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../misc/lv_types.h" +#include "../misc/lv_style.h" + +#if LV_USE_OBJ_PROPERTY + +/********************* + * DEFINES + *********************/ + +/*All possible property value types*/ +#define LV_PROPERTY_TYPE_INVALID 0 /*Use default 0 as invalid to detect program outliers*/ +#define LV_PROPERTY_TYPE_INT 1 /*int32_t type*/ +#define LV_PROPERTY_TYPE_COLOR 2 /*ARGB8888 type*/ +#define LV_PROPERTY_TYPE_POINTER 3 /*void * pointer*/ +#define LV_PROPERTY_TYPE_IMGSRC 4 /*Special pointer for image*/ + +/********************** + * TYPEDEFS + **********************/ + +struct _lv_obj_t; + +#define LV_PROPERTY_ID(clz, name, type, index) LV_PROPERTY_## clz ##_##name = (LV_PROPERTY_## clz ##_START + (index)) | ((type) << 28) + +#define LV_PROPERTY_ID_TYPE(id) ((id) >> 28) +#define LV_PROPERTY_ID_INDEX(id) ((id) & 0xfffffff) + +/*Set properties from an array of lv_property_t*/ +#define LV_OBJ_PROPERTY_ARRAY_SET(obj, array) lv_obj_set_properties(obj, array, sizeof(array)/sizeof(array[0])) + +/** + * Group of predefined widget ID start value. + */ +enum { + LV_PROPERTY_ID_INVALID = 0, + + /*ID 0 to 0xff are style ID, check lv_style_prop_t*/ + LV_PROPERTY_ID_START = 0x100, /*ID little than 0xff is style ID*/ + + /* lv_obj.c */ + LV_PROPERTY_OBJ_START = 1000, + + /* lv_image.c */ + LV_PROPERTY_IMAGE_START = 1100, + + /*Special ID*/ + LV_PROPERTY_ID_BUILTIN_LAST, /*Use it to extend ID and make sure it's unique and compile time determinant*/ + + LV_PROPERTY_ID_ANY = 0x7ffffffe, /*Special ID used by lvgl to intercept all setter/getter call.*/ +}; + +typedef uint32_t lv_prop_id_t; + +typedef struct { + lv_prop_id_t id; + union { + int32_t num; /**< Number integer number (opacity, enums, booleans or "normal" numbers)*/ + const void * ptr; /**< Constant pointers (font, cone text, etc)*/ + lv_color_t color; /**< Colors*/ + lv_style_value_t _style; /**< A place holder for style value which is same as property value.*/ + }; +} lv_property_t; + +typedef struct { + lv_prop_id_t id; + + void * setter; + void * getter; +} lv_property_ops_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/*===================== + * Setter functions + *====================*/ + +/** + * Set widget property value. + * @param obj pointer to an object + * @param id ID of which property + * @param value The property value to set + * @return return LV_RESULT_OK if success + */ +lv_result_t lv_obj_set_property(struct _lv_obj_t * obj, const lv_property_t * value); + +lv_result_t lv_obj_set_properties(struct _lv_obj_t * obj, const lv_property_t * value, uint32_t count); + +/*===================== + * Getter functions + *====================*/ + +/** + * Read property value from object + * @param obj pointer to an object + * @param id ID of which property + * @param value pointer to a buffer to store the value + * @return ? to be discussed, LV_RESULT_OK or LV_RESULT_INVALID + */ +lv_property_t lv_obj_get_property(struct _lv_obj_t * obj, lv_prop_id_t id); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_OBJ_PROPERTY*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_OBJ_PROPERTY_H*/ diff --git a/src/lv_conf_internal.h b/src/lv_conf_internal.h index b93346c8b..b096512c8 100644 --- a/src/lv_conf_internal.h +++ b/src/lv_conf_internal.h @@ -767,6 +767,15 @@ #endif #endif +/*Use obj property set/get API*/ +#ifndef LV_USE_OBJ_PROPERTY + #ifdef CONFIG_LV_USE_OBJ_PROPERTY + #define LV_USE_OBJ_PROPERTY CONFIG_LV_USE_OBJ_PROPERTY + #else + #define LV_USE_OBJ_PROPERTY 0 + #endif +#endif + /*===================== * COMPILER SETTINGS *====================*/ diff --git a/src/widgets/image/lv_image.c b/src/widgets/image/lv_image.c index e33999a37..53ec9f55c 100644 --- a/src/widgets/image/lv_image.c +++ b/src/widgets/image/lv_image.c @@ -28,6 +28,51 @@ static void lv_image_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj); static void lv_image_event(const lv_obj_class_t * class_p, lv_event_t * e); static void draw_image(lv_event_t * e); +#if LV_USE_OBJ_PROPERTY +static const lv_property_ops_t properties[] = { + { + .id = LV_PROPERTY_IMAGE_SRC, + .setter = lv_image_set_src, + .getter = lv_image_get_src, + }, + { + .id = LV_PROPERTY_IMAGE_OFFSET_X, + .setter = lv_image_set_offset_x, + .getter = lv_image_get_offset_x, + }, + { + .id = LV_PROPERTY_IMAGE_OFFSET_Y, + .setter = lv_image_set_offset_y, + .getter = lv_image_get_offset_y, + }, + { + .id = LV_PROPERTY_IMAGE_ROTATION, + .setter = lv_image_set_rotation, + .getter = lv_image_get_rotation, + }, + { + .id = LV_PROPERTY_IMAGE_PIVOT, + .setter = _lv_image_set_pivot, + .getter = lv_image_get_pivot, + }, + { + .id = LV_PROPERTY_IMAGE_SCALE, + .setter = lv_image_set_scale, + .getter = lv_image_get_scale, + }, + { + .id = LV_PROPERTY_IMAGE_ANTIALIAS, + .setter = lv_image_set_antialias, + .getter = lv_image_get_antialias, + }, + { + .id = LV_PROPERTY_IMAGE_SIZE_MODE, + .setter = lv_image_set_size_mode, + .getter = lv_image_get_size_mode, + }, +}; +#endif + /********************** * STATIC VARIABLES **********************/ @@ -40,6 +85,12 @@ const lv_obj_class_t lv_image_class = { .instance_size = sizeof(lv_image_t), .base_class = &lv_obj_class, .name = "image", +#if LV_USE_OBJ_PROPERTY + .prop_index_start = LV_PROPERTY_IMAGE_START, + .prop_index_end = LV_PROPERTY_IMAGE_END, + .properties = properties, + .properties_count = sizeof(properties) / sizeof(properties[0]), +#endif }; /********************** diff --git a/src/widgets/image/lv_image.h b/src/widgets/image/lv_image.h index 713f287fc..d5edb3e3e 100644 --- a/src/widgets/image/lv_image.h +++ b/src/widgets/image/lv_image.h @@ -75,6 +75,20 @@ typedef _lv_image_size_mode_t lv_image_size_mode_t; typedef uint8_t lv_image_size_mode_t; #endif /*DOXYGEN*/ +#if LV_USE_OBJ_PROPERTY +enum { + LV_PROPERTY_ID(IMAGE, SRC, LV_PROPERTY_TYPE_POINTER, 0), + LV_PROPERTY_ID(IMAGE, OFFSET_X, LV_PROPERTY_TYPE_INT, 1), + LV_PROPERTY_ID(IMAGE, OFFSET_Y, LV_PROPERTY_TYPE_INT, 2), + LV_PROPERTY_ID(IMAGE, ROTATION, LV_PROPERTY_TYPE_INT, 3), + LV_PROPERTY_ID(IMAGE, PIVOT, LV_PROPERTY_TYPE_POINTER, 4), + LV_PROPERTY_ID(IMAGE, SCALE, LV_PROPERTY_TYPE_INT, 5), + LV_PROPERTY_ID(IMAGE, ANTIALIAS, LV_PROPERTY_TYPE_INT, 6), + LV_PROPERTY_ID(IMAGE, SIZE_MODE, LV_PROPERTY_TYPE_INT, 7), + LV_PROPERTY_IMAGE_END, +}; +#endif + /********************** * GLOBAL PROTOTYPES **********************/ @@ -134,6 +148,14 @@ void lv_image_set_rotation(lv_obj_t * obj, int32_t angle); */ void lv_image_set_pivot(lv_obj_t * obj, lv_coord_t x, lv_coord_t y); +/** + * Set pivot similar to get_pivot + */ +static inline void _lv_image_set_pivot(lv_obj_t * obj, lv_point_t * pivot) +{ + lv_image_set_pivot(obj, pivot->x, pivot->y); +} + /** * Set the zoom factor of the image. diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f5a4eccaf..fb2645473 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -97,7 +97,7 @@ set(COMPILE_OPTIONS -Wmissing-prototypes -Wpointer-arith -Wmultichar - -Wpedantic + -Wno-pedantic # ignored for now, we convert functions to pointers for propertis table. -Wreturn-type -Wshadow -Wshift-negative-value diff --git a/tests/src/lv_test_conf_full.h b/tests/src/lv_test_conf_full.h index 6f0eb61af..5d3aedd0c 100644 --- a/tests/src/lv_test_conf_full.h +++ b/tests/src/lv_test_conf_full.h @@ -89,3 +89,4 @@ #define LV_USE_OBJ_ID 1 #define LV_USE_OBJ_ID_BUILTIN 1 +#define LV_USE_OBJ_PROPERTY 1 diff --git a/tests/src/test_cases/test_obj_property.c b/tests/src/test_cases/test_obj_property.c new file mode 100644 index 000000000..181ae5a66 --- /dev/null +++ b/tests/src/test_cases/test_obj_property.c @@ -0,0 +1,157 @@ +#if LV_BUILD_TEST +#include "../lvgl.h" + +#include "unity/unity.h" + +void test_obj_property_fail_on_invalid_id(void) +{ + lv_obj_t * obj = lv_obj_create(lv_scr_act()); + lv_property_t prop = { }; + + prop.id = LV_PROPERTY_ID_INVALID; + TEST_ASSERT_EQUAL_INT(LV_RESULT_INVALID, lv_obj_set_property(obj, &prop)); + + prop.id = LV_PROPERTY_ID_BUILTIN_LAST; /* No widget use this ID */ + TEST_ASSERT_EQUAL_INT(LV_RESULT_INVALID, lv_obj_set_property(obj, &prop)); + + prop.id = LV_PROPERTY_OBJ_PARENT + 1; /* Not a valid ID for obj */ + TEST_ASSERT_EQUAL_INT(LV_RESULT_INVALID, lv_obj_set_property(obj, &prop)); + + prop.id = LV_PROPERTY_IMAGE_OFFSET_X; /* Not an ID for obj but for image */ + TEST_ASSERT_EQUAL_INT(LV_RESULT_INVALID, lv_obj_set_property(obj, &prop)); + + prop.id = LV_PROPERTY_OBJ_PARENT; /* Valid ID */ + prop.ptr = lv_scr_act(); + TEST_ASSERT_EQUAL_INT(LV_RESULT_OK, lv_obj_set_property(obj, &prop)); +} + +void test_obj_property_set_get_should_match(void) +{ + lv_obj_t * obj = lv_obj_create(lv_scr_act()); + lv_obj_t * root = lv_obj_create(lv_scr_act()); + lv_property_t prop = { }; + lv_color_t color = {.red = 0x11, .green = 0x22, .blue = 0x33}; + + /* Style property should work */ + /* int type */ + prop.id = LV_STYLE_X; + prop.num = 0xaabb; + TEST_ASSERT_TRUE(lv_obj_set_property(obj, &prop) == LV_RESULT_OK); + TEST_ASSERT_EQUAL_UINT32(0xaabb, lv_obj_get_style_x(obj, 0)); + TEST_ASSERT_EQUAL_UINT32(0xaabb, lv_obj_get_property(obj, LV_STYLE_X).num); + + /* color type */ + prop.id = LV_STYLE_BG_COLOR; + prop.color = color; + TEST_ASSERT_TRUE(lv_obj_set_property(obj, &prop) == LV_RESULT_OK); + TEST_ASSERT_EQUAL_COLOR(color, lv_obj_get_style_bg_color(obj, LV_PART_MAIN)); + TEST_ASSERT_EQUAL_COLOR(color, lv_obj_get_property(obj, LV_STYLE_BG_COLOR).color); + + /* pointer type */ + prop.id = LV_STYLE_TEXT_FONT; + prop.ptr = &lv_font_montserrat_26; + TEST_ASSERT_TRUE(lv_obj_set_property(obj, &prop) == LV_RESULT_OK); + TEST_ASSERT_EQUAL_PTR(&lv_font_montserrat_26, lv_obj_get_style_text_font(obj, LV_PART_MAIN)); + TEST_ASSERT_EQUAL_PTR(&lv_font_montserrat_26, lv_obj_get_property(obj, LV_STYLE_TEXT_FONT).ptr); + + /* Object flags */ + prop.id = LV_PROPERTY_OBJ_FLAG_HIDDEN ; + prop.num = 1; + TEST_ASSERT_TRUE(lv_obj_set_property(obj, &prop) == LV_RESULT_OK); + TEST_ASSERT_TRUE(lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN)); + TEST_ASSERT_TRUE(lv_obj_get_property(obj, LV_PROPERTY_OBJ_FLAG_HIDDEN).num); + + prop.id = LV_PROPERTY_OBJ_FLAG_CLICKABLE; + prop.num = 0; + TEST_ASSERT_TRUE(lv_obj_set_property(obj, &prop) == LV_RESULT_OK); + TEST_ASSERT_FALSE(lv_obj_has_flag(obj, LV_OBJ_FLAG_CLICKABLE)); + TEST_ASSERT_FALSE(lv_obj_get_property(obj, LV_PROPERTY_OBJ_FLAG_CLICKABLE).num); + + /* Obj property */ + prop.id = LV_PROPERTY_OBJ_PARENT; + prop.ptr = root; + TEST_ASSERT_TRUE(lv_obj_set_property(obj, &prop) == LV_RESULT_OK); + TEST_ASSERT_EQUAL_PTR(root, lv_obj_get_parent(obj)); + TEST_ASSERT_EQUAL_PTR(root, lv_obj_get_property(obj, LV_PROPERTY_OBJ_PARENT).ptr); + + /* Derived widget could use same property */ + lv_obj_t * img = lv_image_create(obj); + prop.id = LV_PROPERTY_OBJ_PARENT; + prop.ptr = root; + TEST_ASSERT_TRUE(lv_obj_set_property(img, &prop) == LV_RESULT_OK); + TEST_ASSERT_EQUAL_PTR(root, lv_obj_get_parent(img)); + TEST_ASSERT_EQUAL_PTR(root, lv_obj_get_property(img, LV_PROPERTY_OBJ_PARENT).ptr); + + /* Image properties */ + prop.id = LV_PROPERTY_IMAGE_OFFSET_X; + prop.num = 0x1234; + TEST_ASSERT_TRUE(lv_obj_set_property(img, &prop) == LV_RESULT_OK); + TEST_ASSERT_EQUAL_UINT16(0x1234, lv_img_get_offset_x(img)); + TEST_ASSERT_EQUAL_UINT16(0x1234, lv_obj_get_property(img, LV_PROPERTY_IMAGE_OFFSET_X).num); +} + +void test_obj_property_flag(void) +{ + const struct { + uint32_t flag; + uint32_t id; + } properties[] = { + { LV_OBJ_FLAG_HIDDEN, LV_PROPERTY_OBJ_FLAG_HIDDEN }, + { LV_OBJ_FLAG_CLICKABLE, LV_PROPERTY_OBJ_FLAG_CLICKABLE }, + { LV_OBJ_FLAG_CLICK_FOCUSABLE, LV_PROPERTY_OBJ_FLAG_CLICK_FOCUSABLE }, + { LV_OBJ_FLAG_CHECKABLE, LV_PROPERTY_OBJ_FLAG_CHECKABLE }, + { LV_OBJ_FLAG_SCROLLABLE, LV_PROPERTY_OBJ_FLAG_SCROLLABLE }, + { LV_OBJ_FLAG_SCROLL_ELASTIC, LV_PROPERTY_OBJ_FLAG_SCROLL_ELASTIC }, + { LV_OBJ_FLAG_SCROLL_MOMENTUM, LV_PROPERTY_OBJ_FLAG_SCROLL_MOMENTUM }, + { LV_OBJ_FLAG_SCROLL_ONE, LV_PROPERTY_OBJ_FLAG_SCROLL_ONE }, + { LV_OBJ_FLAG_SCROLL_CHAIN_HOR, LV_PROPERTY_OBJ_FLAG_SCROLL_CHAIN_HOR }, + { LV_OBJ_FLAG_SCROLL_CHAIN_VER, LV_PROPERTY_OBJ_FLAG_SCROLL_CHAIN_VER }, + { LV_OBJ_FLAG_SCROLL_ON_FOCUS, LV_PROPERTY_OBJ_FLAG_SCROLL_ON_FOCUS }, + { LV_OBJ_FLAG_SCROLL_WITH_ARROW, LV_PROPERTY_OBJ_FLAG_SCROLL_WITH_ARROW }, + { LV_OBJ_FLAG_SNAPPABLE, LV_PROPERTY_OBJ_FLAG_SNAPPABLE }, + { LV_OBJ_FLAG_PRESS_LOCK, LV_PROPERTY_OBJ_FLAG_PRESS_LOCK }, + { LV_OBJ_FLAG_EVENT_BUBBLE, LV_PROPERTY_OBJ_FLAG_EVENT_BUBBLE }, + { LV_OBJ_FLAG_GESTURE_BUBBLE, LV_PROPERTY_OBJ_FLAG_GESTURE_BUBBLE }, + { LV_OBJ_FLAG_ADV_HITTEST, LV_PROPERTY_OBJ_FLAG_ADV_HITTEST }, + { LV_OBJ_FLAG_IGNORE_LAYOUT, LV_PROPERTY_OBJ_FLAG_IGNORE_LAYOUT }, + { 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_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 }, + { LV_OBJ_FLAG_WIDGET_1, LV_PROPERTY_OBJ_FLAG_WIDGET_1 }, + { 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_scr_act()); + obj->flags = 0; + for(unsigned long i = 0; i < sizeof(properties) / sizeof(properties[0]); i++) { + + TEST_ASSERT_FALSE(lv_obj_get_property(obj, properties[i].id).num); + lv_obj_add_flag(obj, properties[i].flag); + TEST_ASSERT_TRUE(lv_obj_get_property(obj, properties[i].id).num); + + lv_obj_remove_flag(obj, properties[i].flag); + TEST_ASSERT_FALSE(lv_obj_get_property(obj, properties[i].id).num); + + lv_property_t prop = { }; + prop.id = properties[i].id; + prop.num = 1; + TEST_ASSERT_TRUE(lv_obj_set_property(obj, &prop) == LV_RESULT_OK); + TEST_ASSERT_TRUE(lv_obj_get_property(obj, properties[i].id).num); + TEST_ASSERT_TRUE(lv_obj_has_flag(obj, properties[i].flag)); + + prop.id = properties[i].id; + prop.num = 0; + TEST_ASSERT_TRUE(lv_obj_set_property(obj, &prop) == LV_RESULT_OK); + TEST_ASSERT_FALSE(lv_obj_get_property(obj, properties[i].id).num); + TEST_ASSERT_FALSE(lv_obj_has_flag(obj, properties[i].flag)); + } +} + +#endif