docs(xml): restructure and extend the XML docs (#8495)

This commit is contained in:
Gabor Kiss-Vamosi
2025-07-03 12:59:54 +02:00
committed by GitHub
parent 3bcefbc68f
commit dec36ddb69
35 changed files with 928 additions and 697 deletions
@@ -20,4 +20,3 @@ Auxiliary Modules
snapshot
test
translation
xml/index
@@ -1,163 +0,0 @@
.. _xml_components:
==========
Components
==========
Overview
********
``<component>`` the following child elements:
- ``<consts>``,
- ``<api>``,
- ``<styles>``, and
- ``<view>``.
Unlike Widgets (which are always compiled into the application), Components can either:
1. be loaded at runtime from XML, or
2. be exported to C code and compiled with the application.
Usage from Exported Code
************************
From each Component XML file, a C and H file is exported with a single function inside:
.. code-block:: c
lv_obj_t * component_name_create(lv_obj_t * parent, ...api properties...);
where 'component_name' (in the function above) is replaced by the Component's XML
file name.
When a Component is used in another Component's XML code and the code is exported,
this ``create`` function will be called. This means that Components do not have a
detailed set/get API but can be created with a fixed set of parameters.
If the user needs to access or modify values dynamically, it is recommended to use a
:ref:`Subject <observer_subject>`.
The user can also call these ``..._create()`` functions at any time from application code.
Usage from XML
**************
Registration
------------
Once a Component is created (e.g., ``my_button``), it can be registered by calling either:
- ``lv_xml_component_register_from_file("A:lvgl/examples/others/xml/my_button.xml");``
- ``lv_xml_component_register_from_data("my_button", xml_data_of_my_button);``
These registration functions process the XML data and store relevant information internally.
This is required to make LVGL recognize the Component by name.
Note that the "A:" in the above path is a file system "driver identifier letter" from
:ref:`file_system` and used accordingly. See that documentation for details.
When loaded from a file, the file name is used as the Component name.
Instantiation
-------------
After registration, a new instance of any registered Component can be created with:
.. code-block:: c
lv_obj_t * obj = lv_xml_create(lv_screen_active(), "my_button", NULL);
The created Widget is a normal LVGL Widget that can be used like any other manually-created Widget.
The last parameter can be ``NULL`` or an attribute list, like this:
.. code-block:: c
/* Can be local */
char * my_button_attrs[] = {
"x", "10",
"y", "-10",
"align", "bottom_left",
"btn_text", "New button",
NULL, NULL,
};
lv_obj_t * btn1 = lv_xml_create(lv_screen_active(), "my_button", my_button_attrs);
Parameters
**********
The properties of child elements can be adjusted, such as:
.. code-block:: xml
<lv_label x="10" text="Hello"/>
These parameters can be set to a fixed value. However, with the help of ``<prop>``
elements inside the ``<api>`` tag, they can also be passed when an instance is
created. Only the whole property can be passed, but not individual ``<param>``
elements.
Additionally, when a Component is created, it can use the extended Widget's attributes
(see ``<view extends="...">`` in the code examples below).
This means that Components inherit the API of the extended Widget as well.
The following example demonstrates parameter passing and the use of the
``text`` label property on a Component:
.. code-block:: xml
<!-- h3.xml -->
<component>
<view extends="lv_label"/>
</component>
.. code-block:: xml
<!-- red_button.xml -->
<component>
<api>
<prop type="string" name="btn_text" default="None"/>
</api>
<view extends="lv_button" style_radius="0" style_bg_color="0xff0000">
<h3 text="Some text"/>
<h3 text="$btn_text" y="40"/>
</view>
</component>
.. code-block:: c
lv_xml_component_register_from_file("A:path/to/h3.xml");
lv_xml_component_register_from_file("A:path/to/red_button.xml");
/* Creates a button with "None" text */
lv_xml_create(lv_screen_active(), "red_button", NULL);
/* Use attributes to set the button text */
const char * attrs[] = {
"btn_text", "Click here",
NULL, NULL,
};
lv_xml_create(lv_screen_active(), "red_button", attrs);
Example
*******
.. include:: ../../../examples/others/xml/index.rst
API
***
@@ -1,32 +0,0 @@
.. _xml_component:
====================
XML - Declarative UI
====================
.. toctree::
:class: toctree-1-deep
:maxdepth: 1
intro
component_library
project
syntax
components
screens
widgets
preview
api
styles
consts
view
fonts
images
events
subjects
animations
translation
test
license
@@ -1,123 +0,0 @@
.. _xml_screens:
=======
Screens
=======
Overview
********
.. |nbsp| unicode:: U+000A0 .. NO-BREAK SPACE
:trim:
Screens work very similarly to Components. Both can be:
- Loaded from XML
- Contain Widgets and Components as children
- Have ``<styles>``
- Have ``<consts>``
- Have a ``<view>``
However, Screens **cannot** have an ``<api>``.
It's also possible to define ``<screen permanent="true">`` which
will make the screen created automatically.
Usage
*****
Each XML file describes a :ref:`Screen <screens>`. The name of the XML file will
also be the name of the Screen.
In the ``project.xml`` file, multiple ``<display>`` elements can be defined. In the
UI |nbsp| Editor, when a Screen is being developed, the user can select from all the
defined displays in the Preview, and the Screen will be shown with the given
resolution and color depth.
This is useful for verifying responsive designs.
This example illustrates a screen in XML:
.. code-block:: xml
<screen>
<consts>
<string name="title" value="Main menu"/>
</consts>
<styles>
<style name="dark" bg_color="0x333"/>
</styles>
<view>
<header label="#title"/>
<selector_container styles="dark">
<button text="Weather" icon="cloudy"/>
<button text="Messages" icon="envelope"/>
<button text="Settings" icon="cogwheel"/>
<button text="About" icon="questionmark"/>
</selector_container>
</view>
</screen>
Screen Load and Create events
*****************************
By using the ``<screen_load_event>`` and ``<screen_create_event>`` tags as a child
of a widget or component, screens can be loaded or created on a trigger (e.g. click).
The difference between load and create is that:
- **load**: Just loads an already existing screen. When the screen is left, it remains in memory,
so all states are preserved.
- **create**: The screen is created dynamically, and when it's left, it is deleted, so all changes are lost
(unless they are saved in ``subjects``).
Both tags support the following optional attributes:
- ``trigger``: Event code that triggers the action (e.g. ``"clicked"``, ``"long_pressed"``, etc). Default: ``"clicked"``.
- ``anim_type``: Describes how the screen is loaded (e.g. ``"move_right"``, ``"fade_in"``). Default: ``"none"``.
- ``duration``: Length of the animation in milliseconds. Default: ``0``. Only used if ``anim_type`` is not ``"none"``.
- ``delay``: Wait time before loading the screen in milliseconds.
This is a simple example of both load and create:
.. code-block:: xml
<!-- screen1.xml -->
<screen>
<view style_bg_color="0xff7788" name="first">
<lv_button>
<lv_label text="Create"/>
<!-- Create an instance of "screen2" and load it.
Note that here the name of the XML file is used. -->
<screen_create_event screen="screen2" anim_type="over_right" duration="500" delay="1000"/>
</lv_button>
</view>
</screen>
<!-- screen2.xml -->
<screen>
<view style_bg_color="0x77ff88" name="second">
<lv_button>
<lv_label text="Load"/>
<!-- Load an already created screen that has the name "first".
Note that here the name of the instance is used,
and not the name of the XML file. -->
<screen_load_event screen="first"/>
</lv_button>
</view>
</screen>
.. code-block:: c
lv_xml_component_register_from_data("screen1", screen1_xml);
lv_xml_component_register_from_data("screen2", screen2_xml);
/*Create an instance of screen_1 so that it can loaded from screen2.*/
lv_obj_t * screen1 = lv_xml_create(NULL, "screen1", NULL);
lv_screen_load(screen1);
@@ -1,7 +0,0 @@
.. _xml_view:
=========
View
=========
TODO
@@ -1,120 +0,0 @@
.. _xml_widgets:
=======
Widgets
=======
Overview
********
.. |nbsp| unicode:: U+000A0 .. NO-BREAK SPACE
:trim:
Widgets are written in C and compiled into the application.
They can be referenced from Components, and their API can be used via the exposed attributes
(e.g., label text or slider value).
Using the UI |nbsp| Editor, all the following .C/.H files can be exported from the XML of the Widgets:
:<widget_name>_gen.h: Contains the generated API implementation of the widget
(overwritten on each code export).
:<widget_name>_private_gen.h: Contains private API and the data for the widget
(overwritten on each code export).
:<widget_name>_gen.c: Contains the internals of the Widget (overwritten on
each code export).
:<widget_name>.h: Includes ``<widget_name>_gen.h`` and allows the user to
define custom APIs. Only a skeleton is exported once.
:<widget_name>.c: Contains hooks from ``<widget_name>_gen.c`` and allows
the user to write custom code. Only a skeleton is
exported once.
:<widget_name>_xml_parser.c: Processes the XML strings and calls the required
functions according to the set attributes. Only a
skeleton is exported once.
Usage
*****
XML Parser
----------
To make the Widgets accessible from XML, an XML parser needs to be created and
registered for each Widget. The XML parser for the label Widget looks like this:
.. code-block:: c
void * lv_xml_label_create(lv_xml_parser_state_t * state, const char ** attrs)
{
/* Create the label */
void * obj = lv_label_create(lv_xml_state_get_parent(state));
return obj;
}
void lv_xml_label_apply(lv_xml_parser_state_t * state, const char ** attrs)
{
void * obj = lv_xml_state_get_item(state);
/* Apply the common properties, e.g., width, height, styles, flags, etc. */
lv_xml_obj_apply(state, attrs);
/* Process the label-specific attributes */
for(int i = 0; attrs[i]; i += 2) {
const char * name = attrs[i];
const char * value = attrs[i + 1];
if(lv_streq("text", name)) lv_label_set_text(obj, value);
if(lv_streq("long_mode", name)) lv_label_set_long_mode(obj, long_mode_text_to_enum(value));
}
}
/* Helper to convert the string to enum values */
static lv_label_long_mode_t long_mode_text_to_enum(const char * txt)
{
if(lv_streq("wrap", txt)) return LV_LABEL_LONG_WRAP;
if(lv_streq("scroll", txt)) return LV_LABEL_LONG_SCROLL;
LV_LOG_WARN("%s is an unknown value for label's long_mode", txt);
return 0; /* Return 0 in the absence of a better option. */
}
A Widget XML processor can be registered like this:
.. code-block:: c
lv_xml_widget_register("lv_label", lv_xml_label_create, lv_xml_label_apply);
After registration, a Widget can be created like this:
.. code-block:: c
const char * attrs[] = {
"text", "Click here",
"align", "center",
NULL, NULL,
};
lv_xml_create(lv_screen_active(), "lv_label", attrs);
LVGL automatically registers its built-in Widgets, so only custom Widgets need to be
registered manually.
Adding Custom Code
------------------
``<widget_name>.c`` contains three hooks:
- **Constructor hook**: Called when the Widget and all its children are created. Any
modifications can be done on the children here.
- **Destructor hook**: Called when the Widget is deleted. All manually allocated
memory needs to be freed here.
- **Event hook**: Called at the beginning of the Widget's event callback to perform
any custom action.
In this C file, the ``set`` functions for each API ``<prop>`` also need to be
implemented. The declaration of these functions is automatically exported in
``<widget_name>_gen.h``.
Besides these, any custom code and functions can be freely implemented in this file.
@@ -4,6 +4,10 @@
Fonts
=====
.. |nbsp| unicode:: U+000A0 .. NO-BREAK SPACE
:trim:
Overview
********
@@ -84,26 +88,47 @@ created, and in the initialization function of the Component Library (e.g.,
In ``lv_style_set_text_font(&style1, <font_name>)``, the created font is referenced.
Constants
---------
Constants can also be used with fonts.
.. code-block:: xml
<consts>
<int name="font_size" value="32">
<variant name="size" case="small" value="24"/>
</int>
</consts>
<fonts>
<bin name="medium" src_path="file.ttf" range="0x20-0x7f" symbols="°" size="#font_size"/>
</fonts>
Default Font
------------
``"lv_font_default"`` can be used to access ``LV_FONT_DEFAULT``. Other built-in fonts
are not exposed by default.
Registering fonts
-----------------
If the UI is created from XML at runtime and a ``globals.xml`` is parsed, the ``<... as_file="false">`` tags are skipped
because it is assumed that the user manually created the mapping.
This is because the XML parser cannot automatically map fonts like:
.. code-block:: c
lv_font_t my_font;
to
.. code-block:: xml
<data name="my_font"/>
To register a font in the XML engine use:
.. code-block:: c
lv_xml_register_font(scope, "font_name", &my_font);
``scope`` is usually ``NULL`` to register the font in the global scope.
To register a font locally for a component you can get its scope with:
.. code-block:: c
lv_xml_component_get_scope("component_name");
After calling this function, when ``"font_name"`` is used as a font in XML, ``&my_font`` will be used.
Notes for the UI Editor
-----------------------
For simplicity, in the UI |nbsp| Editor's preview, fonts are always loaded as tinyttf fonts.
It makes the preview dynamic as no code export and compilation is needed when a font changes.
@@ -13,28 +13,14 @@ Overview
An ``<images>`` section can be added to ``globals.xml`` files. If present, it
describes how to map images with names.
(Later, it might also be possible in Components and Widgets to define local images to
keep the global space cleaner.)
Currently ``<file>`` is the only supported child element, and ``<convert>`` is not
yet implemented.
Usage
*****
.. code-block:: xml
<images>
<file name="avatar" src_path="avatar1.png">
<convert path="raw/avatar.svg" width="100px" color_format="L8"/>
</file>
<data name="logo" src_path="logo1.png" color-format="rgb565" memory="RAM2">
<convert path="https://foo.com/image.png" width="50%" height="80%"
color_format="RGB565"/>
</data>
<file name="avatar" src_path="avatar1.png"/>
<data name="logo" src_path="logo1.png" color-format="rgb565" />
</images>
- ``<file>`` means that the image source is used as a file path:
@@ -48,10 +34,11 @@ In :cpp:expr:`lv_image_set_src(image, image_name)` ``image_name`` is used
instead of the path or :cpp:type:`lv_image_dsc_t` pointer.
For simplicity, in the UI |nbsp| Editor preview, images are always loaded as files.
Registering images
------------------
If the UI is created from XML at runtime and a ``globals.xml`` is parsed, the ``<data>`` tags are skipped
because it is assumed that the user manually created the mapping. This is because the XML parser cannot
because it is assumed that the user manually created the mapping. This is because the XML parser cannot
automatically map an image like:
.. code-block:: c
@@ -64,25 +51,26 @@ to
<data name="my_logo"/>
To register an image path or data in the XML engine use:
Constants
---------
.. code-block:: cpp
Constants can be used with images as well.
lv_xml_register_image(scope, "image_name", data)
.. code-block:: xml
``scope`` is usually ``NULL`` to register the image in the global scope.
To register an image locally for a component you can get its scope with:
<consts>
<int name="icon_size" value="32">
<variant name="size" case="small" value="16"/>
</int>
</consts>
.. code-block:: cpp
<images>
<data name="icon_apply" src_path="apply.png">
<convert path="raw/apply.png" width="#icon_size"/>
</data>
</images>
lv_xml_component_get_scope("component_name")
After calling this function, when ``"image_name"`` is used as an image source in XML, ``data``
(can be a path or a pointer to an image descriptor) will be used.
Notes for the UI Editor
-----------------------
For simplicity, in the UI |nbsp| Editor's preview, images are always loaded as files.
It makes the preview dynamic so no code export and compilation is needed when an image changes.
@@ -0,0 +1,13 @@
.. _xml_assets:
======
Assets
======
.. toctree::
:class: toctree-1-deep
:maxdepth: 2
images
fonts
@@ -51,6 +51,70 @@ For example, ``callback="my_callback_1"`` will be exported as:
The ``user_data`` is optional. If omitted, ``NULL`` will be passed.
.. _xml_events_screen:
Screen Load and Create events
*****************************
By using the ``<screen_load_event>`` and ``<screen_create_event>`` tags as a child
of a widget or component, screens can be loaded or created on a trigger (e.g. click).
The difference between load and create is that:
- **load**: Just loads an already existing screen. When the screen is left, it remains in memory,
so all states are preserved.
- **create**: The screen is created dynamically, and when it's left, it is deleted, so all changes are lost
(unless they are saved in ``subjects``).
Both tags support the following optional attributes:
- ``trigger``: Event code that triggers the action (e.g. ``"clicked"``, ``"long_pressed"``, etc). Default: ``"clicked"``.
- ``anim_type``: Describes how the screen is loaded (e.g. ``"move_right"``, ``"fade_in"``). Default: ``"none"``.
- ``duration``: Length of the animation in milliseconds. Default: ``0``. Only used if ``anim_type`` is not ``"none"``.
- ``delay``: Wait time before loading the screen in milliseconds.
This is a simple example of both load and create:
.. code-block:: xml
<!-- screen1.xml -->
<screen>
<view style_bg_color="0xff7788" name="first">
<lv_button>
<lv_label text="Create"/>
<!-- Create an instance of "screen2" and load it.
Note that here the name of the XML file is used. -->
<screen_create_event screen="screen2" anim_type="over_right" duration="500" delay="1000"/>
</lv_button>
</view>
</screen>
<!-- screen2.xml -->
<screen>
<view style_bg_color="0x77ff88" name="second">
<lv_button>
<lv_label text="Load"/>
<!-- Load an already created screen that has the name "first".
Note that here the name of the instance is used,
and not the name of the XML file. -->
<screen_load_event screen="first"/>
</lv_button>
</view>
</screen>
.. code-block:: c
lv_xml_component_register_from_data("screen1", screen1_xml);
lv_xml_component_register_from_data("screen2", screen2_xml);
/*Create an instance of screen_1 so that it can loaded from screen2.*/
lv_obj_t * screen1 = lv_xml_create(NULL, "screen1", NULL);
lv_obj_set_name(screen1, "first"); /*Will be referenced by this name when loaded*/
lv_screen_load(screen1);
Set subject value
*****************
+16
View File
@@ -0,0 +1,16 @@
.. _xml_build_ui:
=========
Build UIs
=========
.. toctree::
:class: toctree-1-deep
:maxdepth: 1
root_elements/index
main_tags/index
assets/index
events
subjects
translation
@@ -5,6 +5,7 @@ API
===
The ``<api>`` tag can be a child of ``<widget>`` and ``<component>`` tags.
(``<screen>``\ s don't support custom APIs.)
The only common point is that both Widgets and Components support having
``<prop>`` (properties) in the ``<api>`` tag to describe their interface.
@@ -55,8 +56,8 @@ Default values
Since each property is passed as an argument to the create function, each must have a value.
This can be ensured by:
- Setting them in the XML instance
- Providing a default value, e.g., ``<prop name="foo" type="string" default="bar"/>``
- Simply setting them in the XML instance
- Providing a default value in the ``<api>``, e.g., ``<prop name="foo" type="string" default="bar"/>``
Limitations
-----------
@@ -72,7 +73,7 @@ Example
<!-- my_button.xml -->
<component>
<api>
<prop name="button_icon" type="image"/>
<prop name="button_icon" type="image" default="NULL"/>
<prop name="button_label" type="string" default="Label"/>
</api>
@@ -167,6 +168,8 @@ Only used with Widgets, this tag defines enums for parameter values.
Enum values are ignored in export; the names are used and resolved by the compiler.
XML parsers must handle mapping enum names to C enums.
.. _xml_widget_element:
<element>
---------
@@ -175,22 +178,27 @@ Also exclusive to Widgets, elements define sub-widgets or internal structures
They support ``<arg>`` and ``<prop>``:
- ``<arg>``\ s are required and used for creation.
- ``<arg>``\ s are required and used when creating/getting the element.
- ``<prop>``\ s are optional and mapped to setters.
Elements are referenced as ``<widget-element>`` in views.
Name parts are separated by `-` (not allowed inside names).
Name parts are separated by `-` ( as `-` is not allowed inside names).
Element `access` types:
- ``add``: Create multiple elements dynamically.
- ``get``: Access implicitly created elements.
- ``set``: Access indexed parts (e.g., table cells).
- ``custom``: Map custom C function to XML.
``type="obj"`` allows children; custom types do not.
As ``add`` and ``get`` elements return an object they also have a type.
This type can be any custom type, for example, `type="my_data"`. In the exported code the
return value will be saved in a ``my_data_t *`` variable.
Only the API can be defined in XML for elements; implementations must be in C.
If the type is ``type="lv_obj"`` it allows the element to have children widgets or components.
Note that, only the API can be defined in XML for elements; implementations must be in C.
access="add"
~~~~~~~~~~~~
@@ -234,9 +242,7 @@ Used for internal/implicit elements:
<api>
<element name="control_button" type="obj" help="A control button of my_widget" access="get">
<arg name="index" type="int"/>
<prop name="title">
<param name="text" type="string"/>
</prop>
<prop name="title" type="string"/>
</element>
</api>
@@ -265,12 +271,8 @@ Used for indexed access, like setting values in a table:
<api>
<element name="item" type="obj" access="set">
<arg name="index" type="int"/>
<prop name="icon">
<param name="icon_src" type="img_src"/>
</prop>
<prop name="color">
<param name="color" type="color"/>
</prop>
<prop name="icon" type="img_src"/>
<prop name="color" type="color"/>
</element>
</api>
@@ -18,7 +18,6 @@ The supported types are:
- ``bool``
Usage
*****
@@ -41,33 +40,3 @@ And they can be used like this:
<styles>
<style name="style1" bg_color="#color1"/>
</styles>
Variants
********
Constants support a ``<variant>`` attribute to change the constants at compile time. For example:
.. code-block:: xml
<consts>
<px name="pad" value="8" help="General padding">
<variant name="size" case="small" value="4"/>
<variant name="size" case="large" value="12"/>
</px>
</consts>
from which the following C code can be exported:
.. code-block:: c
#if SIZE == SMALL
#define PAD 4
#elif SIZE == LARGE
#define PAD 12
#else
#define PAD 8
#endif
Where ``SMALL`` and ``LARGE`` are just preprocessor defines with incremental values.
@@ -0,0 +1,16 @@
.. _xml_main_tags:
=========
Main tags
=========
.. toctree::
:class: toctree-1-deep
:maxdepth: 2
api
consts
styles
view
animations
preview
@@ -21,6 +21,7 @@ For example, you might want to:
- Set margins.
- Change the size of the preview.
``style_radius`` can be used to make preview rounded.
Usage
@@ -44,9 +45,16 @@ Example
<component>
<previews>
<preview name="small_dark" width="content" height="100" style_bg_color="0x333" style_pad_all="32"/>
<preview name="centered" width="200" height="100" flex="row center"/>
<preview name="large_light" width="1980" height="1080" style_bg_color="0xeeeeee"/>
<preview name="small_dark_round"
width="240" height="240"
style_bg_color="0x333" style_pad_all="32"
style_radius="32"/>
<preview name="large_light"
width="1980" height="1080"
style_bg_color="0xeeeeee"/>
</previews>
<view>
@@ -0,0 +1,51 @@
.. _xml_view:
=========
View
=========
Overview
********
The ``<view>`` tag can be used in:
- ``<component>``\ s
- ``<widget>``\ s
- ``<screen>``\ s
- ``<test>``\ s
to describe how these items look. Inside ``<view>``, children can be added in a nested way
using already defined ``widget``\ s and ``component``\ s. For example:
.. code-block:: xml
<view>
<lv_button width="200">
<my_icon src="image1"/>
<lv_label text="Click me"/>
</lv_button>
</view>
Extends
*******
The ``<view>`` itself is also a widget or component, which will become the parent of the children.
To define the type of the ``<view>``, use the ``extends`` attribute. For example, ``extends="lv_slider"``.
In this case, an ``lv_slider`` will be created first, and the children will be added to it.
By adding properties to the ``<view>``, the extended type can be customized. For example:
.. code-block:: xml
<view extends="lv_slider" width="100%" style_bg_color="0xff8800" flex_flow="row">
<style name="my_style" selector="pressed|knob"/>
<lv_label text="Current value: "/>
<lv_label bind_text="subject_1"/>
</view>
Rules for the allowed values of ``extends``:
- ``<component>``: can extend both ``<widget>``\ s and ``<component>``\ s
- ``<widget>``: can extend only ``<widget>``\ s
- ``<screen>``: cannot extend anything
- ``<test>``: can extend ``<widget>``, ``<component>``, or ``<screen>``
@@ -0,0 +1,244 @@
.. _xml_components:
==========
Components
==========
Overview
********
Components are one of the main building blocks for creating new UI elements.
``<component>``\ s support the following child XML tags:
- :ref:`<consts> <xml_consts>`
- :ref:`<api> <xml_api>`
- :ref:`<styles> <xml_styles>`, and
- :ref:`<view> <xml_view>`
- :ref:`<previews> <xml_preview>`
Although they can't contain C code, they are very powerful:
- They can extend another Component or Widget (the base can be defined)
- Components can be built from Widgets and other Components
- A custom API can be defined
- Local styles can be defined, and the global styles can be used
- Local constants can be defined, and the global constants can be used
- Function calls, subject changes, or screen load/create events can be added. See :ref:`XML Events <xml_events>`
- Previews can be defined to preview the components in various settings
Unlike Widgets (which are always compiled into the application), Components can either:
1. be loaded at runtime from XML, or
2. be exported to C code and compiled with the application.
Usage from XML
**************
In XML Files
------------
Using Components in XMLs is very intuitive. The name of the components can be used as XML tag
in the ``<view>`` of other Components, Screens, and Widgets.
The Component properties are just XML attributes.
To load Components from file, it's assumed that the XML files are saved to the device
either as data (byte array) or as file. Once the data is saved, each component
can be registered, and instances can be created after that.
.. code-block:: xml
<!-- my_button.xml -->
<component>
<view extends="lv_button" flex_flow="row">
<lv_image src="logo"/>
<my_h3 text="Title"/>
</view>
</component>
:ref:`Styles <xml_styles>`, :ref:`Constants <xml_consts>`, and :ref:`custom API <component_custom_api>`
can also be described in the XML files.
Registration
------------
Once a Component is created (e.g., ``my_button``), it can be registered by calling either:
- :cpp:expr:`lv_xml_component_register_from_file("A:lvgl/examples/others/xml/my_button.xml")`
- :cpp:expr:`lv_xml_component_register_from_data("my_button", xml_data_of_my_button)`
These registration functions process the XML data and store relevant information internally.
This is required to make LVGL recognize the Component by name.
Note that the "A:" in the above path is a file system "driver identifier letter" from
:ref:`file_system` and used accordingly. See that documentation for details.
When loaded from a file, the file name is used as the Component name.
During registration, the ``<view>`` of the Component is saved in RAM.
Instantiation
-------------
After registration, a new instance of any registered Component can be created with:
.. code-block:: c
lv_obj_t * obj = lv_xml_create(lv_screen_active(), "my_button", NULL);
The created Widget is a normal LVGL Widget that can be used like any other manually-created Widget.
The last parameter can be ``NULL`` or an attribute list, like this:
.. code-block:: c
/* Can be local */
char * my_button_attrs[] = {
"x", "10",
"y", "-10",
"align", "bottom_left",
NULL, NULL,
};
lv_obj_t * btn1 = lv_xml_create(lv_screen_active(), "my_button", my_button_attrs);
Usage from Exported Code
************************
From each Component XML file, a C and H file is exported with a single function inside:
.. code-block:: c
lv_obj_t * component_name_create(lv_obj_t * parent, ...api properties...);
where 'component_name' (in the function above) is replaced by the Component's XML
file name.
When a Component is used in another Component's XML code and the code is exported,
this ``create`` function will be called. This means that Components do not have a
detailed set/get API but can be created with a fixed set of parameters.
If the user needs to access or modify values dynamically, it is recommended to use a
:ref:`Data bindings via Subject <xml_subjects>`.
The user can also call these ``..._create()`` functions at any time from application code
to create new components on demand.
Extending
*********
Additionally, when a Component is created, it can use the extended Widget's attributes
(see ``<view extends="...">`` in the code examples below).
This means that Components inherit the API of the extended Widget as well.
.. _component_custom_api:
Custom Properties
*****************
The properties of child elements can be adjusted, such as:
.. code-block:: xml
<my_button x="10" width="200"/>
However, it's also possible to define custom properties in the ``<api>`` tag.
The properties then can be passed to any properties of the children by
referencing them by ``$``. For example:
.. code-block:: xml
<!-- my_button.xml -->
<component>
<api>
<prop name="btn_text" type="string"/>
</api>
<view extends="lv_button">
<lv_label text="$btn_text"/>
</view>
</component>
And it can be used like
.. code-block:: xml
<!-- my_list.xml -->
<component>
<view>
<my_button btn_text="First"/>
<my_button btn_text="Second"/>
<my_button btn_text="Third"/>
</view>
</component>
In this setup, the ``btn_text`` property is mandatory, however it can be made optional
by setting a default value:
.. code-block:: xml
<prop name="btn_text" type="string" default="Title"/>
See :ref:`<api> <xml_api>` for more details and :ref:`XML Syntax <xml_syntax>` for all the supported types.
Examples
********
The following example demonstrates parameter passing and the use of the
``text`` label property on a Component. Styles and Constants are also shown.
.. code-block:: xml
<!-- h3.xml -->
<component>
<view extends="lv_label" style_text_color="0xffff00"/>
</component>
.. code-block:: xml
<!-- red_button.xml -->
<component>
<api>
<prop type="string" name="btn_text" default="None"/>
</api>
<consts>
<int name="thin" value="2"/>
</consts>
<styles>
<style name="pressed_style" border_width="#thin" border_color="0xff0000"/>
</styles>
<view extends="lv_button" style_radius="0" style_bg_color="0xff0000">
<style name="pressed_style" selector="pressed"/>
<h3 text="Some text"/>
<h3 text="$btn_text" y="40"/>
</view>
</component>
.. code-block:: c
lv_xml_component_register_from_file("A:path/to/h3.xml");
lv_xml_component_register_from_file("A:path/to/red_button.xml");
/* Creates a button with "None" text */
lv_xml_create(lv_screen_active(), "red_button", NULL);
/* Use attributes to set the button text */
const char * attrs[] = {
"btn_text", "Click here",
NULL, NULL,
};
lv_xml_create(lv_screen_active(), "red_button", attrs);
Live Example
*************
.. include:: ../../../../../../examples/others/xml/index.rst
API
***
@@ -0,0 +1,14 @@
.. _xml_root_elements:
=============
Root elements
=============
.. toctree::
:class: toctree-1-deep
:maxdepth: 2
components
widgets
screens
@@ -0,0 +1,107 @@
.. _xml_screens:
=======
Screens
=======
Overview
********
.. |nbsp| unicode:: U+000A0 .. NO-BREAK SPACE
:trim:
Screens work very similarly to Components. Both can be:
- Loaded from XML
- Contain Widgets and Components as children
Screens are wrapped in the ``<screen>`` XML root element and used to organize
the content of the UI.
Screens can have only the following child XML tags:
- :ref:`<consts> <xml_consts>`
- :ref:`<styles> <xml_styles>`, and
- :ref:`<view> <xml_view>`
That is, Screens **cannot** have an :ref:`<api> <xml_api>` or :ref:`<preview> <xml_preview>`.
Usage
*****
Each XML file describes a :ref:`Screen <screens>`. The name of the XML file will
also be the name of the Screen.
This example illustrates a screen in XML. In the example, a ``<my_header>``
and a ``<my_main_cont>`` component is used to keep the screen simple.
.. code-block:: xml
<screen>
<consts>
<string name="title" value="Main menu"/>
</consts>
<styles>
<style name="dark" bg_color="0x333"/>
</styles>
<view>
<my_header label="#title"/>
<my_main_cont>
<style name="dark"/>
<button text="Weather" icon="cloudy"/>
<button text="Messages" icon="envelope"/>
<button text="Settings" icon="cogwheel"/>
<button text="About" icon="questionmark"/>
</my_main_cont>
</view>
</screen>
Code export
***********
When the C code is exported from the UI |nbsp| Editor, ``screen_name_gen.c/h`` files are exported,
containing only a single ``lv_obj_t * screen_name_create(void)`` create function.
By using this function, any number of screen instances can be created and loaded as needed.
Preview
*******
Screens don't support the :ref:`<preview> <xml_preview>` tag because it doesn't make
sense to preview each screen in different resolutions.
As Screens are related to the target hardware in the ``project.xml`` file, multiple
``<display>`` elements can be defined. In the UI |nbsp| Editor, when a Screen is being developed,
the user can select from all the defined displays in the Preview, and the Screen will be shown with
the given resolution and color depth.
This is useful for verifying responsive designs.
Events
******
It's very common to load or create Screens on a button click or other events.
Both are supported by adding special XML tags as children of Components or Widgets:
.. code-block:: xml
<view>
<lv_button>
<lv_label text="Click or Long press me"/>
<!-- Load an already created screen that has the name "first".
Note that here the name of the instance is used,
and not the name of the XML file. -->
<screen_load_event screen="first" trigger="clicked" anim_type="fade"/>
<!-- Create an instance of "about" and load it.
Note that here the name of the XML file is used. -->
<screen_create_event screen="about" trigger="long_pressed"/>
</lv_button>
</view>
Learn more on :ref:`XML Events <xml_events_screen>` page.
@@ -0,0 +1,180 @@
.. _xml_widgets:
=======
Widgets
=======
Overview
********
.. |nbsp| unicode:: U+000A0 .. NO-BREAK SPACE
:trim:
Besides components and Screens, Widgets are the other main building blocks of UIs.
The XML file of the Widgets is wrapped in a ``<widget>`` XML root element.
``<widget>``\ s support the following child XML tags:
- :ref:`<consts> <xml_consts>`
- :ref:`<api> <xml_api>`
- :ref:`<styles> <xml_styles>`, and
- :ref:`<view> <xml_view>`
- :ref:`<previews> <xml_preview>`
Just like Components, Widgets also can be the children of other Widgets and Components.
The main difference is that Widgets are written in C and compiled into the application.
It means unlike Components, Widgets can contain custom C code. For example, when a property is set,
any complex C code can run to set that value.
To connect the C code to XML, XML parser functions need to be implemented and registered. It's
pretty simple as LVGL already provides all the helper functions and the required libraries. Also there
are `many XML parser examples here. <https://github.com/lvgl/lvgl/tree/master/src/others/xml/parsers>`__
Built-in Widgets
****************
The built-in LVGL widgets (e.g. :ref:`lv_slider`, :ref:`lv_label`, :ref:`lv_chart`, etc.) already
have XML parsers and therefore are available in XML.
For example:
.. code-block:: xml
<view>
<lv_label x="10" text="Hello"/>
</view>
The built-in widgets are
- pure C code
(e.g. `lv_slider.c <https://github.com/lvgl/lvgl/tree/master/src/widgets/slider/lv_slider.c>`__)
- an XML file to define only the API
(e.g. `lv_slider.xml <https://github.com/lvgl/lvgl/blob/master/xmls/lv_slider.xml>`__).
It is used only in the UI |nbsp| Editor to validate and autocomplete properties.
- an XML parser C file to map the XML attributes to C functions.
(e.g. `lv_xml_slider_parser.c <https://github.com/lvgl/lvgl/blob/master/src/others/xml/parsers/lv_xml_slider_parser.c>`__)
Creating New Widgets
********************
It's possible to create new widgets in the same way as the built-in LVGL widgets are handled.
However, using the UI |nbsp| Editor it's much faster and simpler. When an XML file is created and
the ``<widget>`` root element is used, the following .C/.H files are generated automatically:
:<widget_name>_gen.h: Contains the generated API implementation of the widget
(overwritten on each code export).
:<widget_name>_private_gen.h: Contains private API and the data for the widget
(overwritten on each code export).
:<widget_name>_gen.c: Contains the internals of the Widget, e.g. constructor with the children,
destructors, event handler, etc. (overwritten on each code export).
:<widget_name>.h: Includes ``<widget_name>_gen.h`` and allows the user to
define custom APIs. Only a skeleton is exported once.
:<widget_name>.c: Contains hooks from ``<widget_name>_gen.c`` and allows
the user to write custom code. Only a skeleton is
exported once.
:<widget_name>_xml_parser.c: Processes the XML strings and calls the required
functions according to the set attributes. Only a
skeleton is exported once.
Usage
*****
XML Parser
----------
To make the Widgets accessible from XML, an XML parser needs to be created and
registered for each Widget. The XML parser for the label Widget looks like this:
.. code-block:: c
void * lv_xml_label_create(lv_xml_parser_state_t * state, const char ** attrs)
{
/* Create the label */
void * obj = lv_label_create(lv_xml_state_get_parent(state));
return obj;
}
void lv_xml_label_apply(lv_xml_parser_state_t * state, const char ** attrs)
{
void * obj = lv_xml_state_get_item(state);
/* Apply the common properties, e.g., width, height, styles, flags, etc. */
lv_xml_obj_apply(state, attrs);
/* Process the label-specific attributes */
for(int i = 0; attrs[i]; i += 2) {
const char * name = attrs[i];
const char * value = attrs[i + 1];
if(lv_streq("text", name)) lv_label_set_text(obj, value);
if(lv_streq("long_mode", name)) lv_label_set_long_mode(obj, long_mode_text_to_enum(value));
/* Process more props here */
}
}
/* Helper to convert strings to enum values */
static lv_label_long_mode_t long_mode_text_to_enum(const char * txt)
{
if(lv_streq("wrap", txt)) return LV_LABEL_LONG_WRAP;
if(lv_streq("scroll", txt)) return LV_LABEL_LONG_SCROLL;
LV_LOG_WARN("%s is an unknown value for label's long_mode", txt);
return 0; /* Return 0 in the absence of a better option. */
}
A Widget XML processor can be registered like this:
.. code-block:: c
lv_xml_widget_register("lv_label", lv_xml_label_create, lv_xml_label_apply);
After registration, a Widget can be created like this from C code:
.. code-block:: c
const char * attrs[] = {
"text", "Click here",
"align", "center",
NULL, NULL,
};
lv_xml_create(lv_screen_active(), "lv_label", attrs);
And in XML it can be used like
.. code-block:: xml
<view>
<lv_label width="100" text="I'm a label!" wrap="scroll"/>
</view>
Adding Custom Code
------------------
``<widget_name>.c`` contains three hooks:
- **Constructor hook**: Called when the Widget and all its children are created. Any
modifications can be done on the children here.
- **Destructor hook**: Called when the Widget is deleted. All manually allocated
memory needs to be freed here.
- **Event hook**: Called at the beginning of the Widget's event callback to perform
any custom action.
In this C file, the ``set`` functions for each API ``<prop>`` also need to be
implemented. The declaration of these functions is automatically exported in
``<widget_name>_gen.h``.
Besides these, any custom code and functions can be freely implemented in this file.
Elements
--------
Elements are internal parts of the widget that can be accessed and/or created dynamically.
For example, tabs of a tabview, list of a dropdown, series of a chart, etc.
Just like any other Widget API properties, Elements also can be defined in the ``<api>``
tag of the Widget's XML.
Learn more about the Elements in the documentation page of :ref:`<api> <xml_widget_element>`.
@@ -1,8 +1,8 @@
.. _xml_subjects:
========
Subjects
========
=======================
Data binding (Subjects)
=======================
Overview
********
@@ -9,6 +9,8 @@ Overview
The XML translation module allows defining and using translated strings directly within XML files.
It's built on top of :ref:`LVGL's translation module <translation>`.
Check it out to learn more about selecting the active language, retrieving translations, and fallback behavior.
Usage
@@ -51,10 +53,3 @@ Some widget properties support a ``*-translated`` suffix to refer to translation
This sets the label's text to the translated string for ``"dog"``.
More Details
************
For information on selecting the active language, retrieving translations, and fallback behavior,
refer to the general :ref:`LVGL translation module <translation>`.
+14
View File
@@ -0,0 +1,14 @@
.. _xml_main:
====================
XML - Declarative UI
====================
.. toctree::
:class: toctree-1-deep
:maxdepth: 2
overview/index
build_ui/index
test
license
@@ -12,21 +12,26 @@ License
1. Overview
===========
The LVGL XML Format (the "Specification") defines an XML-based language for describing user interfaces within the LVGL ecosystem.
The LVGL XML Specification (the "Specification") defines an XML-based language for describing user interfaces within the LVGL ecosystem.
This Specification is protected by copyright. It is provided with limited rights of use as outlined below.
The purpose of this license is to ensure that the Specification remains freely usable for application developers working with LVGL,
while restricting its use in third-party UI editors or design tools.
This protects the long-term integrity and sustainability of the LVGL ecosystem by preventing unlicensed tools
from reusing or replicating the Specification in ways that compete with official LVGL offerings.
2. Permitted Usage
==================
You are permitted to:
- Use this format in **any firmware or embedded application** built on top of the official `LVGL library <https://github.com/lvgl/lvgl>`_ (MIT-licensed).
- Use this Specification in **any firmware or embedded application** built on top of the official `LVGL library <https://github.com/lvgl/lvgl>`_ (MIT-licensed).
- Use the LVGL XML loader freely in accordance with its MIT license.
- Use the format with the **LVGL UI Editor** and **official LVGL tools such as the CLI or XML loader**.
- Write, load, edit, or generate user interfaces described in the LVGL XML format for **internal use** only, including automation or configuration tools used solely inside your organization.
- Create **internal scripts or plugins** that help your own developers use or manage this format, as long as these are **not shared outside your organization or made publicly available**.
- Use the Specification with the **LVGL UI Editor** and **official LVGL tools such as the CLI or XML loader**.
- Write, load, edit, or generate user interfaces described in the LVGL XML Specification for **internal use** only, including automation or configuration tools used solely inside your organization.
- Create **internal scripts or plugins** that help your own developers use or manage this Specification, as long as these are **not shared outside your organization or made publicly available**.
- Share XML-based UI files within customer firmware or projects that target LVGL-based embedded systems.
@@ -35,11 +40,11 @@ You are permitted to:
You are **not permitted** to:
- Create, release, or distribute any **UI editor, visual builder, layout designer, code generator, or tool** that reads, writes, or interprets this format**whether commercial, open-source, or intended for public use, use by customers or partners, or any use outside your own organization** — without written permission from LVGL LLC.
- Create, release, or distribute any **UI editor, visual builder, layout designer, code generator, or tool** that reads, writes, or interprets XML files using this Specification**whether commercial, open-source, or intended for public use, use by customers or partners, or any use outside your own organization** — without written permission from LVGL LLC.
- Implement or extend this Specification in any **software or platform that provides UI creation, editing, or design capabilities similar to those in the LVGL UI Editor**.
- Build public APIs, plugins, converters, or SDKs based on this format.
- Share or publish internal tooling built around this format.
- Embed this format into general-purpose design tools or frameworks outside the LVGL ecosystem.
- Build public APIs, plugins, converters, or SDKs based on this Specification.
- Share or publish internal tooling built around this Specification.
- Use this Specification into general-purpose design tools or frameworks outside the LVGL ecosystem.
These restrictions apply regardless of whether the software is commercial, free, or open-source.
@@ -48,16 +53,16 @@ These restrictions apply regardless of whether the software is commercial, free,
You are **explicitly permitted** to:
- Use this XML format in your own projects.
- Use XML files with this Specification in your own projects.
- Load XMLs at runtime using LVGLs XML loader.
- Generate XMLs using the LVGL UI Editor.
- Customize XMLs manually or with internal utilities.
- Share LVGL-based projects with XML files as part of a commercial product or firmware.
If you are building embedded software using LVGL, **this license does not affect you**.
This license only applies to **UI development tools** that **read, generate, or process this XML format for UI development purposes** in a public or commercial manner.
This license only applies to **UI development tools** that **read, generate, or process XML files using this Specification for UI development purposes** in a public or commercial manner.
You may create **internal tools** to aid development using the format, but these must not be published, sold, or shared externally without a license.
You may create **internal tools** to aid development using the Specification, but these must not be published, sold, or shared externally without a license.
5. Trademarks
@@ -69,7 +74,7 @@ This license does not grant any rights to use the LVGL name, logo, or trademarks
6. Licensing and Permissions
============================
To request permission for broader use or commercial licensing (e.g. using this format in your own UI tool), contact:
To request permission for broader use or commercial licensing (e.g. using this Specification in your own UI tool), contact:
**lvgl@lvgl.io**
@@ -17,7 +17,7 @@ A Component Library can be fully self-sufficient, but it can also reference data
other Component Libraries.
LVGL itself is a Component Library that supplies the built-in Widgets, data types,
etc., so typically Component Libraries use at least the core LVGL data. You can
etc. You can
find the XML files that describe the LVGL Widgets
`here <https://github.com/lvgl/lvgl/tree/master/xmls>`__.
@@ -26,7 +26,6 @@ above, and its own where the Screens, Components, and Widgets of the project are
defined. A project may include additional Component Libraries.
Structure
*********
@@ -64,7 +63,6 @@ A typical structure for a Component Library looks like this:
└── image2.png
Visibility
**********
@@ -124,22 +122,6 @@ included by all generated header files --- not only in the sub-folders where
constants, fonts, and other global data are available for all Widgets and new Widgets.
Variants
--------
``<const_variant>`` can be used by constants to create variants that can be selected at compile time.
This can be used to select a different display size, color scheme, etc.
``<style_variant>`` can be used by styles only, to modify styles at runtime. To
select the current style variant, an integer :ref:`Subject <observer_subject>` (i.e.
a Subject containing an integer value in the Observer Pattern implemented in
``lv_observer.c/.h``) ``<style_variant.name>_variant`` is created. Styles can
subscribe to this, and the style properties can be changed according to the selected
variant's integer value. (See `observer` for details about how to do this.)
All possible variants should be defined in ``globals.xml``.
Example
-------
@@ -148,21 +130,6 @@ A ``globals.xml`` file of a Component Library might look like this:
.. code-block:: xml
<globals>
<config name="mylib" help="This is my great Component Library"/>
<const_variants>
<const_variant name="size" help="Select the size">
<case name="small" help="Assets for 320x240 Screen"/>
<case name="large" help="Assets for 1280x768 Screen"/>
</const_variant>
</const_variants>
<style_variants>
<style_variant name="color" help="Select the color of the UI">
<case name="red" help="Select a red theme"/>
<case name="blue" help="Select a blue theme"/>
</style_variant>
</style_variants>
<api>
<enumdef name="mode">
<enum name="slow"/>
@@ -176,7 +143,7 @@ A ``globals.xml`` file of a Component Library might look like this:
</consts>
<styles>
<style name="card" bg_color="0xeee" radius="#small_unit" padding="12px"/>
<style name="style_red" bg_color="0xff0000">
</styles>
<images>
+15
View File
@@ -0,0 +1,15 @@
.. _xml_overview:
========
Overview
========
.. toctree::
:class: toctree-1-deep
:maxdepth: 2
intro
component_library
project
syntax
@@ -4,24 +4,21 @@
Introduction
============
Overview
********
.. |nbsp| unicode:: U+000A0 .. NO-BREAK SPACE
:trim:
LVGL is capable of loading UI elements written in XML. The XML file can be written by hand, but
it's highly recommended to use LVGL's UI |nbsp| Editor to write the XML files. The UI |nbsp| Editor provides
it's highly recommended to use `LVGL's UI |nbsp| Editor <https://lvgl.io/editor>`__ to write the XML files. The UI |nbsp| Editor provides
features like:
- Instant preview the XML files
- Instant preview of the XML files
- Autocomplete and Syntax highlighting
- Online preview for collaboration and testing
- `Figma <https://www.figma.com/>`__ integration to easily reimplement the designs
.. warning::
The UI |nbsp| Editor and the XML loader are still under development and not production ready.
The UI |nbsp| Editor and the XML loader are still under development and not production-ready.
Consider them as an open beta, or experimental features.
Describing the UI in XML in a declarative manner offers several advantages:
@@ -32,30 +29,6 @@ Describing the UI in XML in a declarative manner offers several advantages:
- XML can be used to generate LVGL code in any language.
- XML helps to separate the view from the internal logic.
Currently supported features:
- Load XML Components at runtime from file or any type of input
- Nest Components and Widgets to any depth
- Dynamically create instances of XML Components in C
- Register images and font that can be accessed by name later in the XML data (only from
file, no C file is generated for images and fonts)
- Constants are working for Widget- and style-properties
- Parameters can be defined, passed and used for Components
- Most of the built-in Widgets are supported, even the complex ones (``label``, ``slider``,
``bar``, ``button``, ``chart``, ``scale``, ``button matrix``, ``table``, etc.)
- Style sheets and local styles can be assigned to parts and states supporting
almost all style properties
Limitations:
- Screens are not supported yet (only Components)
- Events are not supported yet.
- Animations are not supported yet.
- Observer pattern Subjects are not supported yet.
- The documentation is not complete yet.
Concept
*******
@@ -63,40 +36,37 @@ The XML files are Component-oriented. To be more specific, they are ``Component-
That is, they are structured in a way to make it easy to create reusable Component Libraries.
For example, a company can have a Component Library for the basic Widgets for all its products
(smart home, smart watch, smart oven, etc.), and create other industry-specific Libraries
(smart-home specific, smart-watch specific, etc.) containing only a few extra Widgets.
(generic widgets for all smart devices), and create other industry-specific Libraries
(smart-home specific, smart-watch specific, etc.) containing only a few extra Widgets and Components.
These Component Libraries are independent, can be reused across many products, and
can be freely versioned and managed.
Imagine a Component Library as a collection of XML files, images, fonts, and other
assets stored in a git repository, which can be a submodule in many projects.
Imagine a Component Library as a collection of XML files that describe Widgets, Components, Screens,
images, fonts, and other assets stored in a git repository, which can be a submodule in many projects.
If someone finds a bug in the Component Library, they can just fix it in just one
If someone finds a bug in the Component Library, they can just fix it in one
place and push it back to the git repository so that other projects can be updated
from it.
The built-in Widgets of LVGL are also considered a ``Component Library`` which is
always available.
A UI |nbsp| Editor project can have any number of Component Libraries and will always have
A UI |nbsp| Editor project can have any number of Component Libraries but will always have
at least 2:
- LVGL's built-in Widgets, and
- XML-based definitions of Screen contents, along with other project-specific Components.
Widgets, Components, and Screens
********************************
It is important to distinguish between :dfn:`Widgets` and :dfn:`Components`.
It is important to distinguish between :dfn:`Widgets`, :dfn:`Components`, and :dfn:`Screens`.
Widgets
-------
:dfn:`Widgets` are the core building blocks of the UI and are not meant to be loaded at runtime
:dfn:`Widgets` are the core building blocks of the UI and are **not meant to be loaded at runtime**
but rather compiled into the application. The main characteristics of Widgets are:
- In XML, they start with a ``<widget>`` root element.
@@ -104,7 +74,7 @@ but rather compiled into the application. The main characteristics of Widgets ar
- They are built using ``lv_obj_class`` objects.
- They have custom and complex logic inside.
- They cannot be loaded from XML at runtime because the custom code cannot be loaded.
- They have a large API with ``set/get/add`` functions.
- They can have a large API with ``set/get/add`` functions.
- They can themselves contain Widgets as children (e.g., ``Tabview``'s tabs, ``Dropdown``'s lists).
Any handwritten Widget can be accessed from XML by:
@@ -113,26 +83,27 @@ Any handwritten Widget can be accessed from XML by:
2. Writing and registering an XML parser for it.
`See some examples here. <https://github.com/lvgl/lvgl/tree/master/src/others/xml/parsers>`__
Components
----------
:dfn:`Components` are built from other Components and Widgets, and can be loaded at runtime.
:dfn:`Components` are built from other Components and Widgets, and **can be loaded at runtime**.
The main characteristics of Components are:
- In XML, they start with a ``<component>`` root element.
- They are built in XML only and cannot have custom C code.
- They can be loaded from XML at runtime as they describe only visual aspects of the UI.
- They can be loaded from XML as they don't contain custom C code, only XML.
- They are built from Widgets or other Components.
- They can be used for styling Widgets.
- They can be used for styling Widgets and other Components.
- They can contain (as children) Widgets or other Components.
- They can have a simple API to pass properties to their children (e.g., ``btn_text`` to a Label's text).
Regardless of whether the XML was written manually or by the UI |nbsp| Editor, the XML files
of Components can be registered in LVGL, and after that, instances can be created.
In other words, LVGL can just read the XML files, "learn" the Components from them, and
thereafter create Components as part of a :ref:`Screen's <screens>` :ref:`Widget Tree
<basic_data_flow>` according to their structure.
thereafter create children as part of Screens and other Components.
Screens
-------
:dfn:`Screens` are similar to Components:
@@ -142,22 +113,20 @@ thereafter create Components as part of a :ref:`Screen's <screens>` :ref:`Widget
- They do not have an API.
- They can be referenced in Screen load events.
Syntax Teaser
*************
Each Widget or Component XML file describes a single Widget or Component. The root
element for Widgets, Components, and Screens are ``<widget>``, ``<component>`` and
``<screen>`` respectively. Other than that, the contained XML elements are almost
identical. This is a high-level overview of the most important XML elements that
Each Widget, Component, or Screen XML file describes a single UI element. The root
element for Widgets, Components, and Screens is ``<widget>``, ``<component>``, and
``<screen>``, respectively. Other than that, the contained XML elements are almost
identical. This is a high-level overview of the most important XML elements that
will be children of these root elements:
:<api>: Describes the properties that can be ``set`` for a Widget or Component.
Properties can be referenced ysubg ``$``. For Widgets, custom enums can
Properties can be referenced by ``$``. For Widgets, custom enums can
also be defined with the ``<enumdef>`` tag.
:<consts>: Specifies constants (local to the Widget or Component) for colors, sizes,
and other values. Constant values can be referenced using ``#``.
and other values. Constant values can be referenced using ``#``.
:<styles>: Describes style (``lv_style_t``) objects that can be referenced (and
shared) by Widgets and Components later.
:<view>: Specifies the appearance of the Widget or Component by describing the
@@ -183,35 +152,48 @@ Note that only the basic features are shown here.
<style name="red" bg_color="0xff0000"/>
</styles>
<view extends="lv_button" width="#size" styles="blue red:pressed">
<my_h3 text="$btn_text" align="center" color="#orange" style_text_color:checked="0x00ff00"/>
<view extends="lv_button" width="#size">
<style name="blue"/>
<style name="red" selector="pressed"/>
<my_h3 text="$btn_text"
color="#orange"
align="center"/>
</view>
</component>
Usage Teaser
************
LVGL's UI |nbsp| Editor can be used in two different ways.
Export C and H Files
--------------------
The Widgets, Components, images, fonts, etc., can be converted to .C/.H files with
plain LVGL code. The exported code works the same way as if it was written by the
user. In this case, the XML files are not required anymore unless modifications may
be made later. The XML files were used only during editing/implementing the Widgets
and Components to save recompilation time and optionally leverage other useful
UI |nbsp| Editor features.
The Widgets, Components, Screens, images, fonts, etc., can be converted to .C/.H files having
plain LVGL code. The exported code works the same way as if it was written by the
user.
In this case, the XML files are not required anymore to run the C code (unless modifications may
be made later and code is exported again).
The XML files were used only during editing/implementing the Widgets and Components to save
recompilation time and optionally leverage other useful UI |nbsp| Editor features.
Load the UI from XML
--------------------
Although the Widgets' code always needs to be exported in C and compiled into the
application (just like the built-in LVGL Widgets are also part of the application), the Components'
XML can be loaded and any number of instances can be created at runtime. In the simplest case,
a Component can be registered with :cpp:expr:`lv_xml_component_register_from_file(path)` and
an instance can be created with :cpp:expr:`lv_obj_t * obj = lv_xml_create(parent, "my_button", NULL)`.
XML can be loaded and any number of instances can be created at runtime.
In the simplest case, a Component can be registered with
:cpp:expr:`lv_xml_component_register_from_file(path)` and an instance can be created with
:cpp:expr:`lv_obj_t * obj = lv_xml_create(parent, "my_button", NULL)`.
Note that loading the UI from XML practically has no impact on performance.
Once the XML files are registered and the UI is created, it behaves the same way
as if it were created from C code.
Registering XMLs and creating instances is not memory hungry nor slow. The biggest
memory overhead is that the ``<view>`` of the Components is saved in RAM (typically
12 kB/component).
@@ -20,6 +20,7 @@ content is specified:
for each Screen according to the specified displays.
In LVGL's Editor the ``folders``, ``memory`` and `` <display color_format="..."/>`` are not supported yet.
Example
*******
@@ -34,8 +35,6 @@ Example
</folders>
<targets>
<renesas-RA8D1-EK gpu="true"/>
<target name="small">
<display width="320" height="240" color_format="RGB565"/>
<memory name="int_ram" size="128K"/>
@@ -90,18 +90,21 @@ to specify which variable a given name refers to.
To create these connections, functions like
- ``lv_xml_register_image(&ctx, name, pointer)``
- ``lv_xml_register_font(&ctx, name, pointer)``
- ``lv_xml_register_event_cb(&ctx, name, callback)``
- ``lv_xml_register_image(scope, name, pointer)``
- ``lv_xml_register_font(scope, name, pointer)``
- ``lv_xml_register_event_cb(scope, name, callback)``
- etc.
can be used. Later, a pointer to the object can be retrieved by
- ``lv_xml_get_image(&ctx, name)``
- ``lv_xml_get_font(&ctx, name)``
- ``lv_xml_get_event_cb(&ctx, name)``
- ``lv_xml_get_image(scope, name)``
- ``lv_xml_get_font(scope, name)``
- ``lv_xml_get_event_cb(scope, name)``
- etc.
``scope`` can be ``NULL`` to use the global scope or :cpp:expr:`lv_xml_component_get_scope("my_component")`
returns the a component's local scope.
:style: Name of a style. :cpp:expr:`lv_xml_get_style_by_name(&ctx, name)` returns an :cpp:expr:`lv_style_t *`.
:font: Name of a font. :cpp:expr:`lv_xml_get_font(&ctx, name)` returns an :cpp:expr:`lv_font_t *`.
:image: Name of an image. :cpp:expr:`lv_xml_get_image(&ctx, name)` returns an :cpp:expr:`const void *`,
@@ -118,14 +121,15 @@ Arrays
An array of any type can be defined in four ways:
:int[N]: An integer array with ``N`` elements.
In the exported code ``N`` is passed a parameter after the array.
:string[...NULL]: An array terminated with a ``NULL`` element. ``NULL`` can be
replaced by any value, e.g., ``grid_template_last``.
:string[5]: An array that must have exactly 5 elements.
replaced by any value.
:string[5]: An array that must have exactly 5 elements. In the exported code only the array will be passed
as the Widget assumes the given number of elements.
:string[]: No ``NULL`` termination and no count parameter, used when the
number of elements is not known or delivered via another
mechanism, such as via a function parameter.
Enums
-----
@@ -142,9 +146,7 @@ For example:
<enum name="inverted" help="Inverted mode"/>
</enumdef>
<prop name="mode" help="help">
<param name="mode" type="enum:my_widget_mode" help="help"/>
</prop>
<prop name="mode" help="help"type="enum:my_widget_mode" help="help"/>
</api>
When used as a type, a ``+`` suffix means multiple values can be selected and ORed.
@@ -169,5 +171,5 @@ For example:
- Colors: ``type="color(0xff0000 0x00ff00 0x0000ff)"``
- Strings: ``type="string('Ok' 'Cancel')``
These are checked in the UI |nbsp| Editor, and if an invalid option is selected, it
will be highlighted as an error.
Limiting accepted values is not supported yet, however in the UI |nbsp| Editor if
an invalid option is selected, it will be highlighted as an error.
@@ -1,8 +1,8 @@
.. _xml_test:
====
Test
====
==========
UI Testing
==========
Overview
********
+1
View File
@@ -77,6 +77,7 @@ Details
details/common-widget-features/index
details/widgets/index
details/main-modules/index
details/xml/index
details/auxiliary-modules/index
details/libs/index
details/debugging/index
+1 -1
View File
@@ -223,7 +223,7 @@ lv_translation_tag_dsc_t * lv_translation_add_tag(lv_translation_pack_t * pack,
{
if(pack->is_static) {
LV_LOG_WARN("Can't add tag `%s` to static translation pack `%p`", tag_name, (void *)pack);
return LV_RESULT_INVALID;
return NULL;
}
lv_translation_tag_dsc_t tag;
+3 -3
View File
@@ -22,14 +22,14 @@
<element name="column" access="set">
<arg name="column" type="int" help=""/>
<arg name="width" type="px" help="width of the column"/>
<prop name="width" type="px" help="width of the column"/>
</element>
<element name="cell" access="set">
<arg name="row" type="int" help=""/>
<arg name="column" type="int" help=""/>
<arg name="value" type="string" help=""/>
<arg name="ctrl" type="enum:lv_table_cell_ctrl+" help=""/>
<prop name="value" type="string" help=""/>
<prop name="ctrl" type="enum:lv_table_cell_ctrl+" help=""/>
</element>
</api>
</widget>