Components

Overview

Components are one of the main building blocks for creating new UI elements.

<component> elements may contain the following child elements:

Although they can't contain C code, they are very powerful:

  • They can extend another Component or Widget (an "inherited" base Component or Widget can be specified).

  • Components can be built from Widgets and/or other Components.

  • A custom API can be defined.

  • Local styles can be defined, and global styles can be referenced.

  • Local constants can be defined, and global constants can be referenced.

  • Events can be added as function calls, or responses to changes of Subject values, or responses to Screens being created or loaded. See 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.

Using Components from XML

In XML Files

Using Components in XMLs is very intuitive. The name of the components can be used as the name of the 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 a file. Once the data is saved, each component can be registered, after which instances of that Component can be created.

<!-- my_button.xml -->
<component>
    <view extends="lv_button" flex_flow="row">
        <lv_image src="logo"/>
        <my_h3 text="Title"/>
    </view>
</component>

Styles, Constants, and a 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:

These registration functions process the XML data and store relevant information internally by name. 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 as a template for creating instances of that Component.

Note

Note that the "A:" in the above path is a file system "driver identifier letter" from File System (lv_fs_drv) and used accordingly. See that documentation for details.

Instantiation

After registration, a new instance of any registered Component can be created with:

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 Widget.

The last parameter can be NULL or an attribute list, like this:

/* 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);

Using Components from Exported C Code

From each Component XML file, a C and H file is exported with a single function inside:

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 Data bindings via Subject.

The user can also call these ..._create() functions at any time from application code to create new components on demand.

Extending

When a Component is defined as "extending" a Widget, it effectively inherits that Widget's attributes, and they can be used with that Component. (See <view extends="..."> in the code examples below.)

Custom Properties

The properties of child elements can be set directly, such as:

<my_button x="10" width="200"/>

It is also possible to define custom properties in the <api> tag. Those properties then can thereafter be referenced in any properties of the children by using a $ prefix with its name. Example:

<!-- 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 by other Components like this:

<!-- 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 providing a default value like this:

<prop name="btn_text" type="string" default="Title"/>

See <api> for more details, and XML Syntax for all the supported types.

Examples

The following example demonstrates parameter passing and the use of the text label property in a Component. Styles and Constants are also shown.

<!-- h3.xml -->
<component>
    <view extends="lv_label" style_text_color="0xffff00"/>
</component>
<!-- 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>
lv_xml_component_register_from_file("A:path/to/h3.xml");
lv_xml_component_register_from_file("A:path/to/red_button.xml");

/* Create 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

Load components at runtime

#include "../../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_XML

void lv_example_xml_1(void)
{
    /*A red button created from builti-in LVGL widgets
     *It has an API parameter too to change its text.*/
    const char * red_button_xml =
        "<component>"
        "  <api>"
        "    <prop name=\"button_text\" type=\"string\" default=\"None\"/>"
        "  </api>"
        "  <view extends=\"lv_button\" radius=\"0\" style_bg_color=\"0xa91500\">"
        "    <lv_label text=\"$button_text\" align=\"center\"/>"
        "  </view>"
        "</component>";

    /*The card is just an lv_obj where a label and two red buttons are used.
     * Its API allow setting a title (label test) and the action (the text of a button)*/
    const char * card_xml =
        "<component>"
        "  <api>"
        "    <prop name=\"title\" type=\"string\" default=\"Hello world\"/>"
        "    <prop name=\"action\" type=\"string\"/>"
        "  </api>"
        "  <view width=\"200\" height=\"content\">"
        "    <lv_label text=\"$title\" align=\"top_mid\"/>"
        "    <red_button y=\"20\" align=\"top_left\" button_text=\"Cancel\"/>"
        "    <red_button y=\"20\" align=\"top_right\" button_text=\"$action\"/>"
        "  </view>"
        "</component>";

    /* Motor card is a special case of a card where the title and action are already set*/
    const char * motor_card_xml =
        "<component>"
        "  <view extends=\"card\" title=\"Motor start?\" action=\"Start\">"
        "  </view>"
        "</component>";

    /*Register all the custom components*/
    lv_xml_component_register_from_data("red_button", red_button_xml);
    lv_xml_component_register_from_data("card", card_xml);
    lv_xml_component_register_from_data("motor_card", motor_card_xml);

    lv_obj_t * card;
    /*Create a card with the default values*/
    card = (lv_obj_t *) lv_xml_create(lv_screen_active(), "card", NULL);

    /*Create a motor card too. The returned value can be adjusted freely*/
    card = (lv_obj_t *) lv_xml_create(lv_screen_active(), "motor_card", NULL);
    lv_obj_set_y(card, 90);

    /*Pass properties to a card*/
    const char * attrs[] = {
        "y", "180",
        "action", "Apply",
        "title", "New title",
        NULL, NULL,
    };
    card = (lv_obj_t *) lv_xml_create(lv_screen_active(), "card", attrs);

}
#endif

#include "../../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_XML && LV_USE_TRANSLATION

void lv_example_xml_2(void)
{
    lv_result_t res;
    res = lv_xml_component_register_from_file("A:lvgl/examples/others/xml/my_h3.xml");
    if(res != LV_RESULT_OK) {
        lv_obj_t * label = lv_label_create(lv_screen_active());
        lv_label_set_text(label, "Couldn't open the XML files.");
        lv_obj_center(label);
        return;
    }
    lv_xml_component_register_from_file("A:lvgl/examples/others/xml/my_card.xml");
    lv_xml_component_register_from_file("A:lvgl/examples/others/xml/my_button.xml");
    lv_xml_component_register_from_file("A:lvgl/examples/others/xml/view.xml");
    lv_xml_translation_register_from_file("A:lvgl/examples/others/xml/translations.xml");

    lv_xml_register_font(NULL, "lv_montserrat_18", &lv_font_montserrat_18);

    lv_translation_set_language("de");

    lv_obj_t * obj = (lv_obj_t *) lv_xml_create(lv_screen_active(), "view", NULL);
    lv_obj_set_pos(obj, 10, 10);

    lv_xml_component_unregister("my_button");

    const char * slider_attrs[] = {
        "x", "200",
        "y", "-15",
        "align", "bottom_left",
        "value", "30",
        NULL, NULL,
    };

    lv_obj_t * slider = (lv_obj_t *) lv_xml_create(lv_screen_active(), "lv_slider", slider_attrs);
    lv_obj_set_width(slider, 100);
}
#endif

API

lv_xml.h

lv_xml_component.h

lv_xml_component_private.h

lv_xml_style.h