diff --git a/docs/details/base-widget/coord.rst b/docs/details/base-widget/coord.rst index 95bd882740..60b72ffd81 100644 --- a/docs/details/base-widget/coord.rst +++ b/docs/details/base-widget/coord.rst @@ -360,7 +360,7 @@ Using styles Under the hood the position, size and alignment properties are style properties. The above described "simple functions" hide the style related code for the sake of simplicity and set the position, size, and -alignment properties in the local styles of the Widget. +alignment properties in the :ref:`local styles ` of the Widget. However, using styles to set the coordinates has some great advantages: diff --git a/docs/details/base-widget/styles/style.rst b/docs/details/base-widget/styles/style.rst index 0d08391eb2..6dd55eb3e3 100644 --- a/docs/details/base-widget/styles/style.rst +++ b/docs/details/base-widget/styles/style.rst @@ -24,7 +24,7 @@ border width, font, text color and so on. It's similar to a ``class`` in CSS. - The most recently added style has higher precedence. This means if a property is specified in two styles the newest style in the Widget will be used. - Some properties (e.g. text color) can be inherited from a parent(s) if it's not specified in a Widget. -- Widgets can also have local styles with higher precedence than "normal" styles. +- Widgets can also have :ref:`local styles ` with higher precedence than "normal" styles. - Unlike CSS (where pseudo-classes_ describe different states, e.g. ``:focus``), in LVGL a property is assigned to a given state. - Transitions can be applied when the Widget changes state. diff --git a/docs/details/other-components/obj_property.rst b/docs/details/other-components/obj_property.rst index 0d779634d8..ed7946aa1a 100644 --- a/docs/details/other-components/obj_property.rst +++ b/docs/details/other-components/obj_property.rst @@ -1,83 +1,110 @@ -.. _obj_property: +.. _widget_property: -=============== -Widget Property -=============== +================= +Widget Properties +================= -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. +Widget Properties provides a way to greatly reduce the size of the interface between +LVGL and whatever logic layer is just above it, to get and set the most important +properties of Widgets. It's intended use is to: -.. _obj_property_usage: +- simplify (decreasing development time) writing bindings for LVGL in another + language, such as: + + - Micropython, + - Lua, + - Python, + - Perl, + - .NET + +- make it possible to control the UI (or parts of it, e.g. animation) via external + input, without modifying firmware, such as: + + - an external text file (YAML, JSON, XML, custom) + - any external input source (e.g. serial) + +While using it consumes more program space and more CPU overhead while setting and +getting Widget properties, it is designed so minimize that additional CPU overhead. + + + +What is a Widget Property? +************************** + +A Widget's properties are the combined set of :ref:`styles` plus additional properties +that are unique to each type of Widget, that determine what the Widget looks like and +how it behaves. Examples: size, position, color, are properties of all Widgets +whereas text, long-mode, selection-start, and selection-end, are properties unique to +Label Widgets. A Widget's :ref:`local styles ` are also valid +properties in this context. + +The non-style Widget properties available for a given Widget are implemented at the +top of that Widget's primary ``.c`` file as a ``const`` id-to-function-pointer lookup +array, like this example for the Label Widget: + +.. code:: c + + #if LV_USE_OBJ_PROPERTY + static const lv_property_ops_t properties[] = { + { + .id = LV_PROPERTY_LABEL_TEXT, + .setter = lv_label_set_text, + .getter = lv_label_get_text, + }, + { + .id = LV_PROPERTY_LABEL_LONG_MODE, + .setter = lv_label_set_long_mode, + .getter = lv_label_get_long_mode, + }, + { + .id = LV_PROPERTY_LABEL_TEXT_SELECTION_START, + .setter = lv_label_set_text_selection_start, + .getter = lv_label_get_text_selection_start, + }, + { + .id = LV_PROPERTY_LABEL_TEXT_SELECTION_END, + .setter = lv_label_set_text_selection_end, + .getter = lv_label_get_text_selection_end, + }, + }; + #endif + +This array is attached to the ``properties`` field of the Widget's class, so all +Widgets of the same type share the same id-to-function-pointer lookup array. + +Some properties are read-only. When this is the case, only the ``getter`` field in +the corresponding array element will be initialized with a function pointer. +Example: an object's child-Widget count or scroll position must be controlled via +other types of input, but their values are readable through this API. + + + +.. _widget_property_usage: Usage ------ +***** -Two APIs are provided to get/set widget properties. It can be enabled by setting -:c:macro:`LV_USE_OBJ_PROPERTY` to `1` in ``lv_conf.h``. +By default, this feature of LVGL is turned off. It can be turned on by configuring +:c:macro:`LV_USE_OBJ_PROPERTY` to ``1`` in ``lv_conf.h``. -Set :c:macro:`LV_USE_OBJ_PROPERTY_NAME` to `1` in order to use property name instead of ID. +The 3 functions that then become available are: + +- :cpp:type:`lv_result_t` :cpp:expr:`lv_obj_set_property(widget, lv_property_t * value)` + Sets specified property of Widget. +- :cpp:type:`lv_property_t` :cpp:expr:`lv_obj_get_property(widget, lv_prop_id_t id)` + Reads property value from Widget. +- :cpp:type:`lv_result_t` :cpp:expr:`lv_obj_set_properties(widget, lv_property_t * values, count)` + Sets multiple Widget properties from an array of :cpp:type:`lv_property_t`. + +An ``lv_prop_id_t`` is a :ref:`widget_property_id`, whereas an ``lv_property_t`` is a +struct that pairs a :ref:`widget_property_id` with a :ref:`widget_property_value`. + +The following is an example of an array that could be used as the ``values`` argument +in :cpp:func:`lv_obj_set_properties`: .. code-block:: 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_value_precise_t precise; /**< float or int for precise value */ - struct { - lv_style_value_t style; /**< Make sure it's the first element in struct. */ - uint32_t selector; /**< Style selector, lv_part_t | lv_state_t */ - }; - }; - } lv_property_t; - - lv_result_t lv_obj_set_property(lv_obj_t * widget, const lv_property_t * value); - lv_property_t lv_obj_get_property(lv_obj_t * widget, lv_prop_id_t id); - - lv_prop_id_t lv_obj_property_get_id(const lv_obj_class_t * clz, const char * name); - -.. _obj_property_id: - -Property ID -~~~~~~~~~~~ - -:cpp:type:`lv_prop_id_t` identifies which property to get/set. :cpp:type:`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 :c: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 :cpp:type:`lv_style_prop_t` is also valid property ID. - -.. _obj_property_value: - -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-block:: c - - lv_property_t props[] = { + lv_property_t values[] = { { .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, }, @@ -86,4 +113,166 @@ all properties to an array now and set properties with a for loop. { .id = LV_STYLE_BG_COLOR, .color = (lv_color_t){.red = 0x11, .green = 0x22, .blue = 0x33}, }, } - LV_OBJ_SET_PROPERTY_ARRAY(widget, props); +Alternately, :cpp:expr:`lv_obj_set_property(widget, value)` could be called using +this array's individual ``value`` elements inside a loop. + + +.. _widget_property_id: + +Property ID +----------- + +:cpp:type:`lv_prop_id_t` identifies which property to get/set. It is an enum value +defined in the primary ``.h`` file for the Widget in question. Because the actual +names are "assembled" by a preprocessor string-concatenation macro and are thus +hard to visualize, you can also find the names in the Widget's primary ``.c`` file in +the ``properties[]`` array initializing the ``.id`` fields in the array. For example, +``LV_PROPERTY_LABEL_TEXT`` is one found in ``lv_label.c``, and the properties +available to all Widgets are found near the top of the ``lv_obj.c`` file. + +That array is attached to the Widget's class, enabling "getter" and "setter" functions +to be looked up for each type of Widget where Widget properties has been implemented. +(Note: this is done internally so you don't have to.) + +If the property you need to set or get using this API is not implemented yet, you can +add your own Widget property ID following same rules and using one of two helper +macros in the ``enum`` in the Widget's primary ``.h`` file. In both cases, the +"assembled" value is a 32-bit value: + +- :c:macro:`LV_PROPERTY_ID` (for single values -- see :ref:`Single Values` below)`; + bits ``<31:28>`` contain the property's value type and bits ``<27:0>`` contain the + property ID. +- :c:macro:`LV_PROPERTY_ID2` (for paired values -- see :ref:`Paired Values` below)`; + bits ``<31:28>`` contain the type for the property's 1st value, bits ``<27:24>`` + contain the type for the 2nd value, and bits ``<23:0>`` contain the property ID. + +Just make sure the ID is unique across all Widgets. + +Note that :cpp:type:`lv_style_prop_t` (enumerator values beginning with ``LV_PROPERTY_STYLE_...``) +are also valid property IDs, and can be used to set or get a Widget's style values. + + +.. _widget_property_value: + +Property Value +-------------- + +:cpp:type:`lv_property_t` is a struct that begins with an ``id`` field whose meaning +is the same as property ID described above, paired with a value, which is a union of +all possible property types including integer, pointer and color. The ``value`` +field is also capable of carrying the different value types for styles. It does this +by being a union of all the different types that might be needed. The list of +"union-ed" fields at this writing are: + +.. _single values: + +Single Values +~~~~~~~~~~~~~ + +.. code-block:: c + + int32_t num; /**< Signed integer number (enums or "normal" numbers) */ + uint32_t num_u; /**< Unsigned integer number (opacity, Booleans) */ + bool enable; /**< Booleans */ + const void * ptr; /**< Constant pointers (font, cone text, etc.) */ + lv_color_t color; /**< Colors */ + lv_value_precise_t precise; /**< float or int for precise value */ + lv_point_t point; /**< Point, contains two int32_t */ + + struct { + /** + * Note that place struct member `style` at first place is intended. + * `style` shares same memory with `num`, `ptr`, `color`. + * So we set the style value directly without using `prop.style.num`. + * + * E.g. + * + * static const lv_property_t obj_pos_x = { + * .id = LV_PROPERTY_STYLE_X, + * .num = 123, + * .selector = LV_STATE_PRESSED, + * } + * + * instead of: + * static const lv_property_t obj_pos_x = { + * .id = LV_PROPERTY_STYLE_X, + * .style.num = 123, // note this line. + * .selector = LV_STATE_PRESSED, + * } + */ + lv_style_value_t style; /**< Make sure it's the first element in struct. */ + uint32_t selector; /**< Style selector, lv_part_t | lv_state_t */ + }; + +.. _paired values: + +Paired Values +~~~~~~~~~~~~~ + +.. code-block:: c + + /** + * For some properties like slider range, it contains two simple (4-byte) values + * so we can use `arg1.num` and `arg2.num` to set the argument. + */ + struct { + union { + int32_t num; + uint32_t num_u; + bool enable; + const void * ptr; + lv_color_t color; + lv_value_precise_t precise; + } arg1, arg2; + }; + +You can find the current :cpp:type:`lv_property_t` struct in the +`lv_obj_property.h `__ file. + + +Property ID Lookup by Name +-------------------------- + +Setting configuration macro :c:macro:`LV_USE_OBJ_PROPERTY_NAME` to ``1`` enables the +following functions to look up property IDs by passing property name (a string): + +- :cpp:type:`lv_prop_id_t` :cpp:expr:`lv_obj_property_get_id(widget, name)` + Gets property ID by recursively searching for ``name`` in Widget's class hierarchy, + and if still not found, then searches style properties. + +- :cpp:type:`lv_prop_id_t` :cpp:expr:`lv_obj_class_property_get_id(class_p, name)` + Gets property ID by doing a non-recursive search for ``name`` directly in Widget + class properties. + +- :cpp:type:`lv_prop_id_t` :cpp:expr:`lv_style_property_get_id(name)` + Gets style property ID by name. + +The latter two functions are useful when you already know ``name`` is among the +properties of a specific Widget class, or is a style name, since a property name may +exist in both lists. Because of the search sequence in +:cpp:expr:`lv_obj_property_get_id(widget, name)`, if a name does exist in both lists, +then using this function forces the name in the Widget's class hierarchy properties +to have precedence over the style name. + +You can tell which names are available by looking in the ``.c`` files in the +``./src/widgets/property/`` directory. Note that to support binary name searches, +these arrays are generated so that they are guaranteed to be in alphabetical order. +If you need to add a property that is not present, it is recommended to add it in the +``enum`` near the top of the Widget's primary ``.h`` file, and re-generate these +lists using ``./scripts/properties.py`` to ensure alphabetical ordering is preserved. + + + +Additional Notes +**************** + +For the ``lv_property_t * value`` argument of the :cpp:func:`lv_obj_set_property` +function, the language used to call that function (e.g. in a static or +dynamically-loaded library) may need additional code to convert values from their +local data type (e.g. dict, table, etc.) to a C struct before passing it to the +:cpp:func:`lv_obj_set_property` function. + + + +API +*** diff --git a/docs/details/other-components/xml.rst b/docs/details/other-components/xml.rst index 1ba7d74e4b..06c90098e9 100644 --- a/docs/details/other-components/xml.rst +++ b/docs/details/other-components/xml.rst @@ -32,7 +32,8 @@ Currently supported features: - Constants are working for widget and style properties - Parameters can be defined and passed and used for components - Basic built in widgets (label, slider, bar, button, etc) -- Style sheets and local styles that can be assigned to parts and states support the basic style properties +- Style sheets and :ref:`local styles ` that can be assigned to parts + and states support the basic style properties Limitations: diff --git a/src/core/lv_obj_property.h b/src/core/lv_obj_property.h index 09d04743b6..5cd16aa2df 100644 --- a/src/core/lv_obj_property.h +++ b/src/core/lv_obj_property.h @@ -88,13 +88,13 @@ struct _lv_property_name_t { typedef struct { lv_prop_id_t id; union { - int32_t num; /**< Number integer number (opacity, enums, booleans or "normal" numbers)*/ - uint32_t num_u; - bool enable; /**< booleans*/ - const void * ptr; /**< Constant pointers (font, cone text, etc)*/ - lv_color_t color; /**< Colors*/ - lv_value_precise_t precise; /**< float or int for precise value*/ - lv_point_t point; /**< Point, contains two int32_t.*/ + int32_t num; /**< Signed integer number (enums or "normal" numbers)*/ + uint32_t num_u; /**< Unsigned integer number (opacity, Booleans) */ + bool enable; /**< Booleans */ + const void * ptr; /**< Constant pointers (font, cone text, etc.) */ + lv_color_t color; /**< Colors */ + lv_value_precise_t precise; /**< float or int for precise value */ + lv_point_t point; /**< Point, contains two int32_t */ struct { /** @@ -122,7 +122,7 @@ typedef struct { }; /** - * For some property like slider range, it contains two simple(4byte) value + * For some properties like slider range, it contains two simple (4-byte) values * so we can use `arg1.num` and `arg2.num` to set the argument. */ struct { @@ -154,19 +154,19 @@ typedef struct { *====================*/ /** - * Set widget property. - * @param obj pointer to an object - * @param value The property value to set - * @return return LV_RESULT_OK if success + * Set Widget property. + * @param obj pointer to Widget + * @param value property value to set + * @return return LV_RESULT_OK if call succeeded */ lv_result_t lv_obj_set_property(lv_obj_t * obj, const lv_property_t * value); /** - * Set multiple widget properties. Helper `LV_OBJ_SET_PROPERTY_ARRAY` can be used for constant property array. - * @param obj pointer to an object - * @param value The property value array to set - * @param count The count of the property value array - * @return return LV_RESULT_OK if success + * Set multiple Widget properties. Helper `LV_OBJ_SET_PROPERTY_ARRAY` can be used for constant property array. + * @param obj pointer to Widget + * @param value property value array to set + * @param count number of array elements + * @return return LV_RESULT_OK if call succeeded */ lv_result_t lv_obj_set_properties(lv_obj_t * obj, const lv_property_t * value, uint32_t count); @@ -175,41 +175,44 @@ lv_result_t lv_obj_set_properties(lv_obj_t * obj, const lv_property_t * value, u *====================*/ /** - * Read property value from object. - * If id is a style property, the style selector is default to 0. - * @param obj pointer to an object - * @param id ID of which property to read - * @return return the property value read. The returned property ID is set to `LV_PROPERTY_ID_INVALID` if failed. + * Read property value from Widget. + * If id is a style property. Style selector is 0 by default. + * @param obj pointer to Widget + * @param id ID of property to read + * @return return property value read. The returned property ID is set to `LV_PROPERTY_ID_INVALID` if read failed. */ lv_property_t lv_obj_get_property(lv_obj_t * obj, lv_prop_id_t id); /** - * Read a style property value from object - * @param obj pointer to an object + * Read style property value from Widget + * @param obj pointer to Widget * @param id ID of style property - * @param selector selector for the style property. - * @return return the property value read. The returned property ID is set to `LV_PROPERTY_ID_INVALID` if failed. + * @param selector selector for style property + * @return return property value read. The returned property ID is set to `LV_PROPERTY_ID_INVALID` if read failed. */ lv_property_t lv_obj_get_style_property(lv_obj_t * obj, lv_prop_id_t id, uint32_t selector); /** - * Get the property ID by name recursively to base classes. Requires to enable `LV_USE_OBJ_PROPERTY_NAME`. - * @param obj pointer to an object that has specified property or base class has. + * Get property ID by recursively searching for name in Widget's class hierarchy, and + * if still not found, then search style properties. + * Requires to enabling `LV_USE_OBJ_PROPERTY_NAME`. + * @param obj pointer to Widget whose class and base-class hierarchy are to be searched. * @param name property name * @return property ID found or `LV_PROPERTY_ID_INVALID` if not found. */ lv_prop_id_t lv_obj_property_get_id(const lv_obj_t * obj, const char * name); /** - * Get the property ID by name without check base class recursively. Requires to enable `LV_USE_OBJ_PROPERTY_NAME`. - * @param clz pointer to an object class that has specified property or base class has. + * Get property ID by doing a non-recursive search for name directly in Widget class properties. + * Requires enabling `LV_USE_OBJ_PROPERTY_NAME`. + * @param clz pointer to Widget class that has specified property. * @param name property name * @return property ID found or `LV_PROPERTY_ID_INVALID` if not found. */ lv_prop_id_t lv_obj_class_property_get_id(const lv_obj_class_t * clz, const char * name); /** - * Get the style property ID by name. Requires to enable `LV_USE_OBJ_PROPERTY_NAME`. + * Get style property ID by name. Requires enabling `LV_USE_OBJ_PROPERTY_NAME`. * @param name property name * @return property ID found or `LV_PROPERTY_ID_INVALID` if not found. */