docs(restructure): restructure TOC and contents for ease of use (#7847)

This commit is contained in:
Victor Wheeler
2025-03-03 13:09:44 -07:00
committed by GitHub
parent b201713604
commit 0bef0125ea
286 changed files with 1286 additions and 1198 deletions

View File

@@ -1,226 +0,0 @@
.. _file_explorer:
=============
File Explorer
=============
``lv_file_explorer`` provides a UI enabling the end user to browse the contents of a
file system. Its main area is called the "Browsing Area" and provides the list of
files contained in the currently-viewed directory.
When enabled, there is also a "Quick-Access" panel on the left, which provides a
convenient way to reach parts of the file system that are frequently accessed.
Available "Quick-Access" destinations are:
- File System,
- HOME,
- Video,
- Pictures,
- Music, and
- Documents.
You specify what paths these lead to during ``lv_file_explorer``\ 's initialization.
``lv_file_explorer`` only provides the file browsing and events caused by user
activity (e.g. clicking a file), but does not provide the actual file operations.
Client code must hook various events and decide what to do when they are emitted
(e.g. a click or double-click on a file). The actions taken might to open the file,
display it, send it to some other part of the application, etc..
``lv_file_explorer`` passes the full path and name of file that was clicked to the
event callback functions. What happens next is up to the application designer.
``lv_file_explorer`` uses the :ref:`lv_table` Widget for the "Browsing Area", and the
:ref:`lv_list` Widget for the "Quick-Access" panel when it is enabled. Thus,
:c:macro:`LV_USE_TABLE` macro must be set to a non-zero value in ``lv_conf.h`` in
order to use ``lv_file_explorer``, and and :c:macro:`LV_USE_LIST` must be set to a
non-zero value to use the "Quick-Access" panel.
.. note::
In order to use File Explorer, :ref:`file_system` has to be set up and
know about all the drive letters you use when passing paths to File System
(described below).
Prerequisites
*************
If you haven't already done so, you will need to learn about the LVGL :ref:`File
System abstraction <file_system>`, since it must be set up and be functional
for File Explorer to work.
.. _file_explorer_usage:
Usage
*****
Set :c:macro:`LV_USE_FILE_EXPLORER` to a non-zero value in ``lv_conf.h``.
First use :cpp:expr:`lv_file_explorer_create(lv_screen_active())` to create a File
Explorer. The default size is the screen size. After that, you can
customize the style like any Widget.
The size of the ``current_path`` buffer is set by :c:macro:`LV_FILE_EXPLORER_PATH_MAX_LEN`
in ``lv_conf.h``.
The object hierarchy of a freshly-created File Explorer looks like this:
- ``File Explorer``: occupies full area of parent Widget, typically a Screen (Flex-Flow COLUMN)
- ``Container``: occupies full area of File Explorer (Flex grow 1)
- ``Quick-Access Panel``:
- ``Device List``: grows to accommodate children
- ``File System``: button
- ``Places List``: grows to accommodate children
- ``HOME``: button
- ``Video``: button
- ``Pictures``: button
- ``Music``: button
- ``Documents``: button
- ``Browser Panel``:
- ``Header``: 14% of ``Browser Panel`` height
- ``Current Path``: label
- ``File Table``: with 1 column, 86% of ``Browser Panel`` height
- Fields:
- ``home_dir`` = NULL
- ``video_dir`` = NULL
- ``pictures_dir`` = NULL
- ``music_dir`` = NULL
- ``docs_dir`` = NULL
- ``fs_dir`` = NULL
- ``current_path`` = [empty buffer]
- ``sel_fn`` (selected file)
- ``sort`` (default :cpp:enumerator:`LV_EXPLORER_SORT_NONE`)
Accessing the Parts
-------------------
This list of functions provides access to the parts shown in diagram above:
- :cpp:expr:`lv_file_explorer_get_selected_file_name(explorer)` (pointer
to NUL-terminated string containing file-path user selected; typically used inside
an :cpp:enumerator:`LV_EVENT_CLICKED` event)
- :cpp:expr:`lv_file_explorer_get_current_path(explorer)` (pointer to ``current_path`` ``char`` buffer)
- :cpp:expr:`lv_file_explorer_get_file_table(explorer)` (pointer to ``File Table`` :ref:`lv_table` Widget)
- :cpp:expr:`lv_file_explorer_get_header(explorer)` (pointer to ``Header`` :ref:`base_widget` Widget)
- :cpp:expr:`lv_file_explorer_get_path_label(explorer)` (pointer to ``Current Path Label`` :ref:`lv_label` Widget)
- :cpp:expr:`lv_file_explorer_get_quick_access_area(explorer)` (pointer to ``Quick-Access Panel`` :ref:`base_widget`)
- :cpp:expr:`lv_file_explorer_get_places_list(explorer)` (pointer to ``Places List`` :ref:`lv_list` Widget)
- :cpp:expr:`lv_file_explorer_get_device_list(explorer)` (pointer to ``Device List`` :ref:`lv_list` Widget)
Quick-Access Panel
------------------
The ``Quick-Access Panel`` behaves like a typical navigation panel and appears on the
left, while the ``Browser Panel`` appears on the right
This panel is optional. If you set :c:macro:`LV_FILE_EXPLORER_QUICK_ACCESS` to ``0``
in ``lv_conf.h``, the ``Quick-Access Panel`` will not be created. This saves only a
little bit of memory.
Soon after the File Explorer is created, you typically use
:cpp:expr:`lv_file_explorer_set_quick_access_path(explorer, LV_EXPLORER_XXX_DIR, "path")`
to set the path that will be navigated to when the buttons in the ``Quick-Access Panel``
are clicked, which is currently a fixed list. The corresponding values you will need
to pass as the 2nd argument are the following:
- :cpp:enumerator:`LV_EXPLORER_HOME_DIR`
- :cpp:enumerator:`LV_EXPLORER_MUSIC_DIR`
- :cpp:enumerator:`LV_EXPLORER_PICTURES_DIR`
- :cpp:enumerator:`LV_EXPLORER_VIDEO_DIR`
- :cpp:enumerator:`LV_EXPLORER_DOCS_DIR`
- :cpp:enumerator:`LV_EXPLORER_FS_DIR`
.. _file_explorer_sort:
Sort
----
You can use
:cpp:expr:`lv_file_explorer_set_sort(explorer, LV_EXPLORER_SORT_XX)` to set
the sorting method.
These are the possible sorting methods:
- :cpp:enumerator:`LV_EXPLORER_SORT_NONE` (default)
- :cpp:enumerator:`LV_EXPLORER_SORT_KIND`
:cpp:expr:`lv_file_explorer_get_sort(explorer)` returns the current sorting method.
.. _file_explorer_events:
Events
******
- :cpp:enumerator:`LV_EVENT_READY` Sent when a directory is opened, which can happen:
- when the File Explorer is initially opened,
- after a user clicks on a ``Quick-Access Panel`` navigation button, and
- after the user clicks on a directory displayed in the ``Browser Panel``.
You can use it to, for example, customize the file sort.
- :cpp:enumerator:`LV_EVENT_VALUE_CHANGED` Sent once when any item (file) in the
``Brwoser Panel``\ 's file list is clicked.
- :cpp:enumerator:`LV_EVENT_CLICKED` Sent twice when an item in the ``Browser Panel``
is clicked: once as a result of the input-device :cpp:enumerator:`LV_EVENT_RELEASED`
event and a second as a result of the input device :cpp:enumerator:`LV_EVENT_CLICKED`
event. This applies to files, directories, and the "< Back" item in the ``Browser Panel``.
In these events you can use :cpp:func:`lv_file_explorer_get_current_path` to get the
current path and :cpp:func:`lv_file_explorer_get_selected_file_name` to get the name
of the currently selected file in the event processing function. For example:
.. code-block:: c
static void file_explorer_event_handler(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
if(code == LV_EVENT_VALUE_CHANGED) {
char * cur_path = lv_file_explorer_get_current_path(widget);
char * sel_fn = lv_file_explorer_get_selected_file_name(widget);
LV_LOG_USER("%s%s", cur_path, sel_fn);
}
}
You can also save the obtained **path** and **file** name into an array
through functions such as :cpp:func:`strcpy` and :cpp:func:`strcat` for later use.
.. _file_explorer_example:
Example
*******
.. include:: ../../examples/others/file_explorer/index.rst
.. _file_explorer_api:
API
***

View File

@@ -1,124 +0,0 @@
.. _font_manager:
============
Font Manager
============
Font Manager is a secondary encapsulation of :ref:`freetype`, which provides
facilities for high-level applications to manage and use vector fonts. Currently
provided font-management functions includes:
- Font resource reference counting (reduces repeated creation of font resources).
- Font resource concatenation (font fallback).
- Font resource recycling mechanism (buffers recently deleted font resources to
reduce the time overhead of repeated creation).
.. _font_manager_usage:
Usage
*****
Enable FreeType and Font Manager in ``lv_conf.h`` by setting the
:c:macro:`LV_USE_FREETYPE` and :c:macro:`LV_USE_FONT_MANAGER` macros to non-zero
values, and configure :c:macro:`LV_FONT_MANAGER_NAME_MAX_LEN` to set the maximum
length of the font name.
Initialize Font Manager
-----------------------
Use :cpp:func:`lv_font_manager_create` to create a font manager, where the
:cpp:func:`recycle_cache_size` parameter is used to set the number of font recycling
caches, which can improve font creation efficiency.
Use :cpp:func:`lv_font_manager_add_path_static` to add a mapping between the font
file path and the custom font name, so that the application can access the font
resources more conveniently. It should be noted that if the file path is not static
(assigned from a local variable), use :cpp:func:`lv_font_manager_add_path` to
add the path. This function will make its own copy of the string.
Use :cpp:func:`lv_font_manager_remove_path` to remove the font path mapping.
.. code-block:: c
static lv_font_manager_t * g_font_manager = NULL;
void font_manager_init(void)
{
/* Create font manager, with 8 fonts recycling buffers */
g_font_manager = lv_font_manager_create(8);
/* Add font path mapping to font manager */
lv_font_manager_add_path_static(g_font_manager, "Lato-Regular", "./lvgl/examples/libs/freetype/Lato-Regular.ttf");
lv_font_manager_add_path_static(g_font_manager, "MyFont", "./path/to/myfont.ttf");
}
Create Font from Font Manager
-----------------------------
Use :cpp:func:`lv_font_manager_create_font` to create a font. The parameters are
basically the same as :cpp:func:`lv_freetype_font_create`.
The ``font_family`` parameter can be filled with the names of multiple fonts
(separated by ``,``) to achieve font concatenation (when the corresponding glyph is
not found in a font file, it will automatically search from the next concatenated
font).
.. code-block:: c
static lv_font_t * g_font = NULL;
/* Create font from font manager */
lv_font_t * g_font = lv_font_manager_create_font(g_font_manager,
"Lato-Regular,MyFont",
LV_FREETYPE_FONT_RENDER_MODE_BITMAP,
24,
LV_FREETYPE_FONT_STYLE_NORMAL);
/* Create label with the font */
lv_obj_t * label = lv_label_create(lv_screen_active());
lv_obj_set_style_text_font(label, g_font, 0);
lv_label_set_text(label, "Hello World!");
Delete Font
-----------
Use :cpp:func:`lv_font_manager_delete_font` to delete the font when it is no longer needed.
The font manager will mark the font resource as a recyclable font so that it has the
possibility of being more quickly created next time.
Note that you need to delete any Widgets that used the font first, and then
delete the font to avoid accessing wild pointers.
.. code-block:: c
/* Delete label and font */
lv_obj_delete(label);
lv_font_manager_delete_font(g_font_manager, g_font);
g_font = NULL;
Delete Font Manager
-------------------
Use :cpp:func:`lv_font_manager_delete` to destroy the entire font manager. It should
be noted that before destruction, it is necessary to ensure that the application has
deleted all fonts using :cpp:func:`lv_font_manager_delete_font`. The font manager
will check the reference status of all allocated fonts. If there are still fonts
being referenced, the font manager will fail to be destroyed and the function will return false.
.. _font_manager_example:
Example
*******
.. include:: ../../examples/others/font_manager/index.rst
.. _font_manager_api:
API
***

View File

@@ -1,92 +0,0 @@
.. _fragment:
========
Fragment
========
Fragment is a concept copied from
`Android <https://developer.android.com/guide/fragments>`__.
It represents a reusable portion of your app's UI. A fragment defines
and manages its own layout, has its own lifecycle, and can handle its
own events. Like Android's Fragment that must be hosted by an activity
or another fragment, Fragment in LVGL needs to be hosted by a Widget,
or another fragment. The fragment's view hierarchy becomes part of, or
attaches to, the host's view hierarchy.
Such concept also has some similarities to `UiViewController on
iOS <https://developer.apple.com/documentation/uikit/uiviewcontroller>`__.
Fragment Manager is a manager holding references to fragments attached
to it, and has an internal stack to achieve forward and backwards navigation. You can use
fragment manager to build a navigation stack, or a multi-pane application
easily.
.. _fragment_usage:
Usage
*****
Enable :c:macro:`LV_USE_FRAGMENT` in ``lv_conf.h``.
Create Fragment Class
---------------------
.. code-block:: c
struct sample_fragment_t {
/* IMPORTANT: don't miss this part */
lv_fragment_t base;
/* States, object references and data fields for this fragment */
const char *title;
};
const lv_fragment_class_t sample_cls = {
/* Initialize something needed */
.constructor_cb = sample_fragment_ctor,
/* Create view objects */
.create_obj_cb = sample_fragment_create_obj,
/* IMPORTANT: size of your fragment struct */
.instance_size = sizeof(struct sample_fragment_t),
};
Use ``lv_fragment_manager``
---------------------------
.. code-block:: c
/* Create fragment instance, and Widgets will be added to container */
lv_fragment_manager_t *manager = lv_fragment_manager_create(container, NULL);
/* Replace current fragment with instance of sample_cls, and init_argument is user defined pointer */
lv_fragment_manager_replace(manager, &sample_cls, init_argument);
Fragment Based Navigation
-------------------------
.. code-block:: c
/* Add one instance into manager stack. View object of current fragment will be destroyed,
* but instances created in class constructor will be kept.
*/
lv_fragment_manager_push(manager, &sample_cls, NULL);
/* Remove the top most fragment from the stack, and bring back previous one. */
lv_fragment_manager_pop(manager);
.. _fragment_example:
Example
*******
.. include:: ../../examples/others/fragment/index.rst
.. _fragment_api:
API
***

View File

@@ -1,93 +0,0 @@
.. _gridnav:
===============
Grid navigation
===============
Grid navigation (gridnav for short) is a feature that moves focus among a set
of child Widgets via arrow-key presses.
If the child Widgets are arranged into a grid-like layout then the up, down,
left and right arrows move focus to the nearest sibling in the
respective direction.
It doesn't matter how the children are positioned, as only the current x
and y coordinates are considered. This means that gridnav works with
manually positioned children, as well as :ref:`flex` and
:ref:`grid` layouts.
Gridnav also works if the children are arranged into a single row or
column. This is useful, for example, to simplify navigation among items in a
:ref:`List Widget <lv_list>`.
Gridnav assumes that the Widget to which gridnav is added is part of a
:ref:`group <indev_groups>`. This way, if the Widget with
gridnav has focus, the arrow key presses are automatically forwarded to
the Widget so that gridnav can process the arrow keys.
To move the focus to the next widget of the group use
:cpp:enumerator:`LV_KEY_NEXT` or :cpp:enumerator:`LV_KEY_PREV`.
Optionally you can also use :cpp:func:`lv_group_focus_next`
or :cpp:func:`lv_group_focus_prev` or the ``TAB``
key on keyboard as usual.
If the container is scrollable and the focused child is out of the view,
gridnav will automatically scroll the child into view.
.. _gridnav_usage:
Usage
*****
To add gridnav behavior to any Widget (e.g. one serving as a container for
child Widgets that the end user will navigate among using arrow keys) use
:cpp:expr:`lv_gridnav_add(container, flags)`.
The ``flags`` argument controls the navigation behavior:
- :cpp:enumerator:`LV_GRIDNAV_CTRL_NONE`: Default settings
- :cpp:enumerator:`LV_GRIDNAV_CTRL_ROLLOVER`: If there is no next/previous Widget in a
direction, the focus goes to the Widget in the next/previous row (on
left/right keys) or first/last row (on up/down keys)
- :cpp:enumerator:`LV_GRIDNAV_CTRL_SCROLL_FIRST`: If an arrow is pressed and the focused
Widget can be scrolled in that direction then it will be scrolled instead of
going to the next/previous Widget. If there is no more room for scrolling the
next/previous Widget will receive focus normally.
- :cpp:enumerator:`LV_GRIDNAV_CTRL_HORIZONTAL_MOVE_ONLY`: Only use the left/right keys
for grid navigation. Up/down key events will be sent to the Widget that has focus.
- :cpp:enumerator:`LV_GRIDNAV_CTRL_VERTICAL_MOVE_ONLY`: Only use the up/down keys
for grid navigation. Left/right key events will be sent to the Widget that has focus.
While the above behaviors can be combined by bit-wise OR-ing the above values together,
:cpp:enumerator:`LV_GRIDNAV_CTRL_HORIZONTAL_MOVE_ONLY` and :cpp:enumerator:`LV_GRIDNAV_CTRL_VERTICAL_MOVE_ONLY`
should not be used together.
:cpp:expr:`lv_gridnav_remove(container)` Removes gridnav behavior from a Widget.
Focusable Widgets
*****************
A Widget needs to be clickable or click focusable
(:cpp:enumerator:`LV_OBJ_FLAG_CLICKABLE` or :cpp:enumerator:`LV_OBJ_FLAG_CLICK_FOCUSABLE`) and not
hidden (:cpp:enumerator:`LV_OBJ_FLAG_HIDDEN`) to receive focus via gridnav.
.. _gridnav_example:
Examples
********
.. include:: ../../examples/others/gridnav/index.rst
.. _gridnav_api:
API
***

View File

@@ -1,132 +0,0 @@
.. _ime_pinyin:
==========
Pinyin IME
==========
Pinyin IME provides an API to provide Chinese Pinyin input method (Chinese
input) for a Keyboard Widget, which supports both 26-key and 9-key input modes.
You can think of ``lv_ime_pinyin`` as a Pinyin input method plug-in for
the Keyboard Widget.
Normally, an environment where :ref:`lv_keyboard` can
run can also run ``lv_ime_pinyin``. There are two main influencing
factors: the size of the font file and the size of the dictionary.
.. _ime_pinyin_usage:
Usage
*****
Enable :c:macro:`LV_USE_IME_PINYIN` in ``lv_conf.h``.
First use :cpp:expr:`lv_ime_pinyin_create(lv_screen_active())` to create a Pinyin
input-method plug-in, then use
:cpp:expr:`lv_ime_pinyin_set_keyboard(pinyin_ime, kb)` to add the Keyboard Widget
you created to the Pinyin input method plug-in. You can use
:cpp:expr:`lv_ime_pinyin_set_dict(pinyin_ime, your_dict)` to use a custom
dictionary. If you don't want to use the built-in dictionary,
you can disable :c:macro:`LV_IME_PINYIN_USE_DEFAULT_DICT` in ``lv_conf.h``,
which can save a lot of memory space.
The built-in thesaurus is customized based on the
**LV_FONT_SIMSUN_16_CJK** font library, which currently has more
than 1,000 of the most common CJK radicals, so it is recommended to use a
custom font and thesaurus.
In the process of using the Pinyin input method plug-in, you can change
the Keyboard and dictionary at any time.
Custom Dictionary
*****************
If you don't want to use the built-in Pinyin dictionary, or if you feel that the
built-in phonetic dictionary consumes too much memory, you can use a custom dictionary.
Customizing the dictionary is very simple.
First, set :c:macro:`LV_IME_PINYIN_USE_DEFAULT_DICT` to ``0`` in ``lv_conf.h``
Then, write a dictionary in the following format.
Dictionary format
-----------------
The arrangement order of each pinyin syllable is very important. If you
need to customize your own thesaurus according to the Hanyu Pinyin
syllable table, you can read
`here <https://baike.baidu.com/item/%E6%B1%89%E8%AF%AD%E6%8B%BC%E9%9F%B3%E9%9F%B3%E8%8A%82/9167981>`__
to learn about the Hanyu Pinyin syllables and the syllable table.
Then, write your own dictionary according to the following format:
.. code-block:: c
lv_100ask_pinyin_dict_t your_pinyin_dict[] = {
{ "a", "啊阿呵吖" },
{ "ai", "埃挨哎唉哀皑蔼矮碍爱隘癌艾" },
{ "an", "按安暗岸俺案鞍氨胺厂广庵揞犴铵桉谙鹌埯黯" },
{ "ang", "昂肮盎仰" },
{ "ao", "凹敖熬翱袄傲奥懊澳" },
{ "ba", "芭捌叭吧笆八疤巴拔跋靶把坝霸罢爸扒耙" },
{ "bai", "白摆佰败拜柏百稗伯" },
/* ...... */
{ "zuo", "昨左佐做作坐座撮琢柞"},
{NULL, NULL}
**The last item** must be ``{null, null}``, or it will not work
properly.
.. _ime_pinyin_apply_new_dictionary:
Applying a new dictionary
-------------------------
After writing a dictionary according to the above dictionary format, you
only need to call this function to set up and use your dictionary:
.. code-block:: c
lv_obj_t * pinyin_ime = lv_100ask_pinyin_ime_create(lv_screen_active());
lv_100ask_pinyin_ime_set_dict(pinyin_ime, your_pinyin_dict);
.. _ime_pinyin_modes:
Modes
*****
lv_ime_pinyin has the following modes:
- :cpp:enumerator:`LV_IME_PINYIN_MODE_K26`: Pinyin 26-key input mode
- :cpp:enumerator:`LV_IME_PINYIN_MODE_K9`: Pinyin 9-key input mode
- :cpp:enumerator:`LV_IME_PINYIN_MODE_K9_NUMBER`: Numeric keypad mode
The Keyboard's ``TEXT``-mode layout contains buttons to change mode.
To set the mode manually, use :cpp:expr:`lv_ime_pinyin_set_mode(pinyin_ime, mode)`.
The default mode is :cpp:enumerator:`LV_IME_PINYIN_MODE_K26`.
.. _ime_pinyin_example:
Example
*******
.. include:: ../../examples/others/ime/index.rst
.. _ime_pinyin_api:
API
***

View File

@@ -1,53 +0,0 @@
.. _lv_imgfont:
==========
Image font
==========
Draw image in **label** or **span** obj with :cpp:type:`lv_imgfont`. This is often used to
display Unicode emoji icons in text.
Supported image formats: determined by enabled LVGL :ref:`image decoders <overview_image_decoder>`.
.. _lv_imgfont_usage:
Usage
*****
Enable :c:macro:`LV_USE_IMGFONT` in ``lv_conf.h``.
To create a new *imgfont*:
.. code-block:: c
static lv_font_t * imgfont;
...
imgfont = lv_imgfont_create(height, path_cb, user_data);
- ``height`` Font size.
- ``path_cb`` A function to get the image path of a character.
Pass ``NULL`` if no image should be shown, but the character itself.
- ``user_data`` Pointer to user data.
To use the *imgfont* in a label, reference it:
:cpp:expr:`lv_obj_set_style_text_font(label, imgfont, LV_PART_MAIN)`
To destroy the *imgfont* that is no longer used, use :cpp:expr:`lv_imgfont_destroy(imgfont)`.
.. _lv_imgfont_example:
Example
*******
.. include:: ../../examples/others/imgfont/index.rst
.. _lv_imgfont_api:
API
***

View File

@@ -1,21 +0,0 @@
.. _others:
================
Other Components
================
.. toctree::
:maxdepth: 1
file_explorer
font_manager
fragment
gridnav
ime_pinyin
imgfont
monkey
obj_id
obj_property
observer
snapshot
xml/index

View File

@@ -1,62 +0,0 @@
.. _monkey:
======
Monkey
======
The Monkey module provides LVGL applications with a simple monkey test. Monkey
Testing is a technique where the user tests the application or system by providing
random inputs and checking the behavior or seeing whether the aplication or system
will crash. This module provides this service as simulated random input to stress
test an LVGL application.
.. _monkey_usage:
Usage
*****
First, enable :c:macro:`LV_USE_MONKEY` in ``lv_conf.h``.
Next, declare a variable (it can be local) of type :c:type:`lv_monkey_config_t` to
define the configuration structure, initialize it using
:cpp:expr:`lv_monkey_config_init(cfg)` then set its ``type`` member to the desired
type of :ref:`input device <indev>`, and set the ``min`` and ``max`` values for its
``period_range`` and ``input_range`` members to set the time ranges (in milliseconds)
and input ranges the Monkey module will use to generate random input at random times.
Next, call :cpp:expr:`lv_monkey_create(cfg)` to create the Monkey. It returns a
pointer to the ``lv_monkey_t`` created.
Finally call :cpp:expr:`lv_monkey_set_enable(monkey, true)` to enable Monkey.
If you want to pause the monkey, call
:cpp:expr:`lv_monkey_set_enable(monkey, false)`. To delete it, call
:cpp:expr:`lv_monkey_delete(monkey)`.
Note that ``input_range`` has different meanings depending on the ``type`` input device:
- :cpp:enumerator:`LV_INDEV_TYPE_POINTER`: No effect, click randomly within the pixels of the screen resolution.
- :cpp:enumerator:`LV_INDEV_TYPE_ENCODER`: The minimum and maximum values of ``enc_diff``.
- :cpp:enumerator:`LV_INDEV_TYPE_BUTTON`: The minimum and maximum values of ``btn_id``.
Use :cpp:func:`lv_monkey_get_indev` to get the input device, and use
:cpp:func:`lv_indev_set_button_points` to map the key ID to the coordinates.
- :cpp:enumerator:`LV_INDEV_TYPE_KEYPAD`: No effect, Send random :ref:`indev_keys`.
.. _monkey_example:
Example
*******
.. include:: ../../examples/others/monkey/index.rst
.. _monkey_api:
API
***

View File

@@ -1,196 +0,0 @@
.. _widget_id:
=========
Widget ID
=========
Widgets can optionally have identifiers added to their functionality if needed for
the application. Exactly how that happens is designed to be flexible, and can morph
with the needs of the application. It can even be a timestamp or other data current
at the time the Widget was created.
.. _widget_id_usage:
Usage
*****
Enable Widget ID functionality by setting :c:macro:`LV_USE_OBJ_ID` to ``1`` in ``lv_conf.h``.
Once enabled, several things change:
- each Widget will now have a ``void *`` field called ``id``;
- these two API functions become available:
- :cpp:expr:`lv_obj_get_id(widget)`,
- :cpp:expr:`lv_obj_get_child_by_id(widget, id)`;
- several more Widget-ID-related API functions become available if
:c:macro:`LV_USE_OBJ_ID_BUILTIN` is non-zero (more on this below);
- two additional configuration macros both :c:macro:`LV_OBJ_ID_AUTO_ASSIGN` and
:c:macro:`LV_USE_OBJ_ID_BUILTIN` now have meaning.
:c:macro:`LV_OBJ_ID_AUTO_ASSIGN`
--------------------------------
This macro in ``lv_conf.h`` defaults to whatever value :c:macro:`LV_USE_OBJ_ID`
equates to. You can change this if you wish. Either way, if it equates to a
non-zero value, it causes two things to happen:
- :cpp:expr:`lv_obj_assign_id(class_p, widget)` will be called at the end of each
Widget's creation, and
- :cpp:expr:`lv_obj_free_id(widget)` will be called at the end of the sequence when
each Widget is deleted.
Because of this timing, custom versions of these functions can be used according to
the below, and they can even be used like "event hooks" to implement a trace
operation that occurs when each Widget is created and deleted.
:cpp:expr:`lv_obj_assign_id(class_p, widget)`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This function (whether provided by LVGL or by you --- more on this below) is
responsible for assigning a value to the Widget's ``id`` field, and possibly do
other things, depending on the implementation.
:cpp:expr:`lv_obj_free_id(widget)`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This function (whether provided by LVGL or by you --- more on this below) is
responsible for doing the clean-up of any resources allocated by
:cpp:func:`lv_obj_assign_id()`
:c:macro:`LV_USE_OBJ_ID_BUILTIN`
--------------------------------
This macro in ``lv_conf.h`` equates to ``1`` by default. You can change this if you
wish. When it equates to a non-zero value the following function implementations are
provided by LVGL:
- :cpp:expr:`lv_obj_assign_id(class_p, widget)`
- :cpp:expr:`lv_obj_free_id(widget)`
- :cpp:expr:`lv_obj_set_id(widget, id)`
- :cpp:expr:`lv_obj_stringify_id(widget, buf, len)`
- :cpp:expr:`lv_obj_id_compare(id1, id2)`
These supply the default implementation for Widget IDs, namely that for each Widget
created, :cpp:expr:`lv_obj_stringify_id(widget, buf, len)` will produce a unique
string for it. Example: if the following 6 Widgets are created in this sequence:
- Screen
- Label
- Button
- Label
- Label
- Image
the strings produced by :cpp:expr:`lv_obj_stringify_id(widget, buf, len)` would be
- obj1
- label1
- btn1
- label2
- label3
- image1
respectively.
.. _widget_id_custom_generator:
Using a custom ID generator
---------------------------
If you wish, you can provide custom implementations for several Widget-ID related
functions. You do this by first setting :c:macro:`LV_USE_OBJ_ID_BUILTIN` to `0` in
``lv_conf.h``.
You will then need to provide implementations for the following functions (and link
them with LVGL):
.. code-block:: c
const char * lv_obj_stringify_id(lv_obj_t * widget, char * buf, uint32_t len);
int lv_obj_id_compare(const void * id1, const void * id2);
If :c:macro:`LV_OBJ_ID_AUTO_ASSIGN` equates to a non-zero value (or if you otherwise
simply need to use them), you will also need to provide implementations for:
.. code-block:: c
void lv_obj_assign_id(const lv_obj_class_t * class_p, lv_obj_t * widget);
void lv_obj_free_id(lv_obj_t * widget);
If :c:macro:`LV_BUILD_TEST` equates to a non-zero value and you are including LVGL
test code in your compile (or if you otherwise simply need to use them), you
will also need to provide an implementation for:
.. code-block:: c
void lv_obj_set_id(lv_obj_t * widget, void * id);
Examples of implementations of these functions exist in ``lv_obj_id_builtin.c``, but
you are free to use a different design if needed.
:cpp:func:`lv_obj_stringify_id` converts the passed ``widget`` to a string
representation (typically incorporating the ``id`` field) and writes it into the
buffer provided in its ``buf`` argument.
:cpp:func:`lv_obj_id_compare` compares 2 ``void * id`` values and returns ``0`` when
they are considered equal, and non-zero otherwise.
If :c:macro:`LV_OBJ_ID_AUTO_ASSIGN` equates to a non-zero value,
:cpp:func:`lv_obj_assign_id` is called when a Widget is created. It is responsible
for assigning a value to the Widget's ``id`` field. A pointer to the Widget's final
class is passed in its ``class_p`` argument in case it is needed for determining the
value for the ``id`` field, or for other possible needs related to your design for
Widget IDs. Note that this pointer may be different than :cpp:expr:`widget->class_p`
which is the class of the Widget currently being created.
If :c:macro:`LV_OBJ_ID_AUTO_ASSIGN` equates to a non-zero value,
:cpp:func:`lv_obj_free_id` is called when a Widget is deleted. It needs to perform
the clean-up for any resources allocated by :cpp:func:`lv_obj_assign_id`.
Dumping a Widget Tree
---------------------
Regardless of the state of any of the above macros, the function
:cpp:expr:`lv_obj_dump_tree(widget)` provides a "dump" of the Widget Tree for the
specified Widget (that Widget plus all its children recursively) using the
currently-configured method used by the :c:macro:`LV_LOG_USER` macro. If NULL is
passed instead of a pointer to a "root" Widget, the dump will include the Widget Tree
for all :ref:`Screens`, for all :ref:`Displays <display>` in the system.
For :c:macro:`LV_LOG_USER` to produce output, the following needs to be true in
``lv_conf.h``:
- :c:macro:`LV_USE_LOG` must equate to a non-zero value
- :c:macro:`LV_LOG_LEVEL` <= :c:macro:`LV_LOG_LEVEL_USER`
It will recursively walk through all that Widget's children (starting with the Widget
itself) and print the Widget's parent's address, the Widget's address, and if
:c:macro:`LV_USE_OBJ_ID` equates to a non-zero value, will also print the output of
:cpp:func:`lv_obj_stringify_id` for that Widget.
This can be useful in the event of a UI crash. From that log you can examine the
state of the Widget Tree when :cpp:expr:`lv_obj_dump_tree(widget)` was called.
For example, if a pointer to a deleted Widget is stored in a Timer's
:cpp:expr:`timer->user_data` field when the timer event callback is called, attempted
use of that pointer will likly cause a crash because the pointer is not valid any
more. However, a timely dump of the Widget Tree right before that point will show
that the Widget no longer exists.
Find child by ID
----------------
:cpp:expr:`lv_obj_get_child_by_id(widget, id)` will perform a recursive walk through
``widget``\ 's children and return the first child encountered having the given ID.

View File

@@ -1,278 +0,0 @@
.. _widget_property:
=================
Widget Properties
=================
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:
- 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 <style_local>` 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
*****
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``.
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
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, },
{ .id = LV_PROPERTY_OBJ_FLAG_CLICKABLE, .num = 1, },
{ .id = LV_STYLE_IMAGE_OPA, .num = 128, },
{ .id = LV_STYLE_BG_COLOR, .color = (lv_color_t){.red = 0x11, .green = 0x22, .blue = 0x33}, },
}
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 <https://github.com/lvgl/lvgl/blob/master/src/core/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
***

View File

@@ -1,574 +0,0 @@
.. _observer:
========
Observer
========
.. _observer_overview:
Overview
********
.. _observer pattern: https://en.wikipedia.org/wiki/Observer_pattern
The ``lv_observer`` module is an implemention of the `Observer Pattern`_.
This implementation consists of:
:Subjects: (in global memory or heap) are "logic packages", each containing the
value being "observed" and its type (integer (``int32_t``), a string, a
pointer, an :cpp:type:`lv_color_t`, or a group);
:Observers: (zero or more per Subject, always dynamically-allocated) are always
attached to exactly one Subject, and provide user-defined notifications
each the time Subject's value changes.
A Subject and its Observers can be used in various ways:
1. Simply subscribe to a Subject and get notified when the Subject's value changes.
2. Subscribe to a group Subject (connects a group of Subjects) to get notified when
any of the Subjects' values change in the group.
3. Bind Widgets to Subjects to automatically match the Widget's value with the
Subject (e.g. a Label's text or an Arc's value).
.. _observer_usage:
Usage
*****
Using Observer first requires :c:macro:`LV_USE_OBSERVER` be configured to ``1``.
(It is ``1`` by default, and can be set to ``0`` to save some program space if you
will not be using Observer.)
A typical use case looks like this:
.. code-block:: c
// Any typical global variable
lv_subject_t my_subject;
/*-------
* main.c
*-------*/
extern lv_subject_t my_subject;
void main(void)
{
// Initialize Subject as integer with the default value of 10.
lv_subject_init_int(&my_subject, 10);
some_module_init();
}
/*--------------
* some_module.c
*--------------*/
extern lv_subject_t some_subject;
// Will be called when Subject's value changes
static void some_observer_cb(lv_observer_t * observer, lv_subject_t * subject)
{
int32_t v = lv_subject_get_int(subject);
do_something(v);
}
void some_module_init(void)
{
// Subscribe to Subject as an Observer.
lv_subject_add_observer(&some_subject, some_observer_cb, NULL);
}
/*--------------
* some_system.c
*--------------*/
extern lv_subject_t some_subject;
void some_event(void)
{
// The below call sets Subject's value to 30 and notifies current Observers.
lv_subject_set_int(&some_subject, 30);
}
.. _observer_subject:
Subject
-------
Subject Initialization
~~~~~~~~~~~~~~~~~~~~~~
Subjects have to be static or global variables, or dynamically-allocated
:cpp:type:`lv_subject_t` objects. Reason: their content must remain valid through
the life of the Subject.
To initialize a Subject use ``lv_subject_init_<type>(&subject, params, init_value)``.
The following initialization functions exist, one for each of the Subject types:
:Integer: void :cpp:expr:`lv_subject_init_int(subject, int_value)`
:String: void :cpp:expr:`lv_subject_init_string(subject, buf, prev_buf, buf_size, initial_string)`
:Pointer: void :cpp:expr:`lv_subject_init_pointer(subject, ptr)`
:Color: void :cpp:expr:`lv_subject_init_color(subject, color)`
:Group: void :cpp:expr:`lv_subject_init_group(group_subject, subject_list[], count)`
Setting a Subject's Value
~~~~~~~~~~~~~~~~~~~~~~~~~
The following functions are used to update a Subject's value:
:Integer: void :cpp:expr:`lv_subject_set_int(subject, int_value)`
:String: void :cpp:expr:`lv_subject_copy_string(subject, buf)`
:Pointer: void :cpp:expr:`lv_subject_set_pointer(subject, ptr)`
:Color: void :cpp:expr:`lv_subject_set_color(subject, color)`
At the end of each of these calls, if the new value differs from the previous value,
a notification is sent to all current Observers.
Getting a Subject's Value
~~~~~~~~~~~~~~~~~~~~~~~~~
The following functions are used to get a Subject's current value:
:Integer: int32_t :cpp:expr:`lv_subject_get_int(subject)`
:String: const char * :cpp:expr:`lv_subject_get_string(subject)`
:Pointer: const void * :cpp:expr:`lv_subject_get_pointer(subject)`
:Color: lv_color_t :cpp:expr:`lv_subject_get_color(subject)`
Getting a Subject's Previous Value
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The following functions are used to get a Subject's previous value:
:Integer: int32_t :cpp:expr:`lv_subject_get_previous_int(subject)`
:String: const char * :cpp:expr:`lv_subject_get_previous_string(subject)`
:Pointer: const void * :cpp:expr:`lv_subject_get_previous_pointer(subject)`
:Color: lv_color_t :cpp:expr:`lv_subject_get_previous_color(subject)`
.. _observer_observer:
Observer
--------
Subscribing to a Subject
~~~~~~~~~~~~~~~~~~~~~~~~
The action of subscribing to a Subject:
- dynamically allocates an Observer object,
- attaches it to the Subject,
- performs an initial notification to the Observer (allowing the Observer to
update itself with the Subject's current value), and
- returns a pointer to the newly-created Observer.
Thereafter the Observer will receive a notification each time the Subject's value
changes, as long as that Observer remains attached (subscribed) to that Subject.
Notifications are performed by calling the callback function provided when
subscribing to the Subject.
To subscribe to a Subject one of the ``lv_subject_add_observer...()`` functions are
used. Alternately, if you want to bind a Subject's value to a Widget's property, one
of the ``lv_<widget_type>_bind_...()`` functions can be used. The former are covered
below. The latter are covered in the :ref:`observer_widget_binding` section.
For the most basic use case, subscribe to a Subject by using the following function:
lv_observer_t * observer = :cpp:expr:`lv_subject_add_observer(&some_subject, some_observer_cb, user_data)`
where the Observer's notification callback should look like this:
.. code-block:: c
static void some_observer_cb(lv_observer_t * observer, lv_subject_t * subject)
{
...
}
This function returns a pointer to the newly-created Observer.
When using this method of subscribing, it is the responsibility of the user to call
:cpp:expr:`lv_observer_remove(observer)` when the Observer is no longer needed, which
both unsubscribes it from the Subject and deletes it from the LVGL heap.
Subscribing While Associating Observer with a Non-Widget Object
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The function subscribes to a Subject additionally associates the Observer with a
pointer to any type of object, a copy of which is saved in the Observer's ``target``
field. This function should be used when the pointer *does not* point to a Widget.
lv_observer_t * observer = :cpp:expr:`lv_subject_add_observer_with_target(&some_subject, some_observer_cb, some_pointer, user_data)`
A copy of the passed pointer can be retrieved by calling
:cpp:expr:`lv_observer_get_target(observer)`, e.g. inside the callback function.
When using this method of subscribing, it is the responsibility of the user to call
:cpp:expr:`lv_observer_remove(observer)` when the Observer is no longer needed, which
both unsubscribes it from the Subject and deletes it from the LVGL heap.
Subscribing While Associating Observer with a Widget
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The function below assocates a Widget with an Observer while subscribing to a
Subject. A copy of the pointer to that Widget is saved in the Observer's ``target``
field. This works exactly like the above method except that when the Widget is
deleted, the Observer thus created will be automatically unsubscribed from the
Subject and deleted from the LVGL heap. Note this is different from
:ref:`observer_widget_binding`.
lv_observer_t * observer = :cpp:expr:`lv_subject_add_observer_obj(&some_subject, some_observer_cb, widget, user_data)`
Any number of Observers can be created and be associated with a Widget this way.
A copy of the pointer to the Widget can be retrieved by calling
:cpp:expr:`lv_observer_get_target_obj(observer)`, e.g. inside the callback function.
Note that this function returns the stored pointer as a ``lv_obj_t *`` type, as
opposed to the ``void *`` type returned by
:cpp:expr:`lv_observer_get_target_obj(observer)`.
(:cpp:expr:`lv_observer_get_target(observer)` can still be used if you need that
pointer as a ``void *`` type for any reason, but in practice, this would be rare.)
**Important:**
When using this method of subscribing to a Subject:
- :cpp:expr:`lv_observer_remove(observer)` must *never* be called.
The Observer MUST ONLY BE unsubscribed and deleted by:
- If Widget needs to be deleted, simply delete the Widget, which will automatically
remove the Observer from the Subject.
- If Widget does NOT need to be deleted:
- :cpp:expr:`lv_obj_remove_from_subject(widget, subject)` which will delete all
Observers associated with ``widget``, or
- :cpp:expr:`lv_subject_deinit(subject)`, which gracefully disconnects ``subject``
from all associated Observers and Widget events.
Unsubscribing from a Subject
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To unsubscribe a normal Observer or one associated with a non-Widget object, use
:cpp:expr:`lv_observer_remove(observer)`, where ``observer`` is the return value from
either the :cpp:func:`lv_subject_add_observer()` or
:cpp:func:`lv_subject_add_observer_with_target()` functions.
To unsubscribe an Observer created through :cpp:func:`lv_subject_add_observer_obj()`,
use :cpp:expr:`lv_obj_remove_from_subject(widget, subject)`. ``subject`` can be NULL
to unsubscribe the Widget from all associated Subjects.
To unsubscribe all Observers from a Subject that were subscribed using any method
(including :ref:`observer_widget_binding` covered below), use
:cpp:expr:`lv_subject_deinit(subject)`.
.. _observer_subject_groups:
Subject Groups
--------------
When something in your system relies on more than one value (i.e. it needs to be
notified when any of a SET of two or more values changes), it can be made an
Observer of a Subject Group.
Let us consider an example of an instrument which measures either voltage or current.
To display the measured value on a label, 3 things are required:
1. What is being measured (current or voltage)?
2. What is the measured value?
3. What is the range or unit ("mV", "V", "mA", "A")?
When any of these 3 input values change, the label needs to be updated, and it needs
to know all 3 values to compose its text.
To handle this you can create an array from the addresses of all the Subjects that
are relied upon, and pass that array as a parameter when you initialize a Subject
with GROUP type.
.. code-block:: c
static lv_subject_t * subject_list[3] = {&subject_1, &subject_2, &subject_3};
lv_subject_init_group(&subject_all, subject_list, 3); /* Last argument is number of elements. */
Observers are then added to Subject Groups (e.g. ``subject_all``) in the usual way.
When this is done, a change to the value of any of the Subjects in the group triggers
a notification to all Observers subscribed to the Subject Group (e.g. ``subject_all``).
As an example, the above scenario with Voltage/Current measurement might look like this:
.. code-block:: c
lv_obj_t * label = lv_label_create(lv_screen_active());
lv_subject_t subject_mode; // Voltage or Current
lv_subject_t subject_value; // Measured value
lv_subject_t subject_unit; // The unit
lv_subject_t subject_all; // Subject group that connects the above 3 Subjects
lv_subject_t * subject_list[3] = {&subject_mode, &subject_value, &subject_unit}; // The elements of the group
lv_subject_init_int(&subject_mode, 0); // Let's say 0 is Voltage, 1 is Current
lv_subject_init_int(&subject_value, 0);
lv_subject_init_pointer(&subject_unit, "V");
lv_subject_init_group(&subject_all, subject_list, 3);
lv_subject_add_observer_obj(&subject_all, all_observer_cb, label, NULL);
...
static void all_observer_cb(lv_observer_t * observer, lv_subject_t * subject)
{
lv_obj_t * label = lv_observer_get_target(observer);
lv_subject_t * subject_mode = lv_subject_get_group_element(subject, 0);
lv_subject_t * subject_value = lv_subject_get_group_element(subject, 1);
lv_subject_t * subject_unit = lv_subject_get_group_element(subject, 2);
int32_t mode = lv_subject_get_int(subject_mode);
int32_t value = lv_subject_get_int(subject_value);
const char * unit = lv_subject_get_pointer(subject_unit);
lv_label_set_text_fmt(label, "%s: %d %s", mode ? "Current" : "Voltage", value, unit);
}
.. _observer_widget_binding:
Widget Binding
--------------
The following methods of subscribing to an integer-type Subject associate the
Observer with ONE of a Widget's properties as thought that property itself were the
Observer. Any of the following Widget properties can be thus bound to an Subject's
integer value:
- flag (or OR-ed combination of flags) from from the ``LV_OBJ_FLAG_...`` enumeration values;
- state (or OR-ed combination of states) from the ``LV_STATE_...`` enumeration values;
- text value for Label Widgets;
- integer value for these Widget types:
- Arc
- Drop-Down
- Roller
- Slider
Any number of Observers can be created for a single Widget, each bound to ONE of
the above properties.
For all of the ``lv_..._bind_...()`` functions covered below, they are similar to
:cpp:expr:`lv_subject_add_observer_obj(&some_subject, some_observer_cb, widget, user_data)`
in that they create an Observer and associates the Widget with it. What is different
is that updates to the Widget's property thus bound are handled internally -- the
user *does not supply callback functions* for any of these subscribing methods -- the
callback methods are supplied by the Observer subsystem.
.. warning::
In all cases, the Observer is unsubscribed from the Subject and deleted from the
LVGL heap when the Widget is deleted. :cpp:expr:`lv_observer_remove(observer)`
must *never* be called when using the ``lv_..._bind_...()`` functions, but
:cpp:expr:`lv_subject_deinit(subject)` and
:cpp:expr:`lv_obj_remove_from_subject(widget, subject)` may both be used since
they both gracefully de-couple the Observer from the Widget before deleting the
Observer.
.. note::
While the examples below show saving a reference to the created Observer objects
for the various ``lv_..._bind_...()`` functions, it is not necessary to do so
unless you need them for some purpose, because the created Observer objects will
be automatically deleted when the Widget is deleted.
Any Widget Type
~~~~~~~~~~~~~~~
Flags
^^^^^
The following methods of subscribing to an integer Subject affect a Widget's flag (or
OR-ed combination of flags). When the subscribing occurs, and each time the
Subject's value is changed thereafter, the Subject's value is compared with the
specified reference value, and the specified flag(s) is (are):
- SET when the Subject's integer value fulfills the indicated condition, and
- CLEARED otherwise.
Here are the functions that carry out this method of subscribing to a Subject. The
``flags`` argument can contain a single, or bit-wise OR-ed combination of any of the
``LV_OBJ_FLAG_...`` enumeration values.
:equal: :cpp:expr:`lv_obj_bind_flag_if_eq(widget, &subject, flags, ref_value)`
:not equal: :cpp:expr:`lv_obj_bind_flag_if_not_eq(widget, &subject, flags, ref_value)`
:greater than: :cpp:expr:`lv_obj_bind_flag_if_gt(widget, &subject, flags, ref_value)`
:greater than or equal: :cpp:expr:`lv_obj_bind_flag_if_ge(widget, &subject, flags, ref_value)`
:less than: :cpp:expr:`lv_obj_bind_flag_if_lt(widget, &subject, flags, ref_value)`
:less than or equal: :cpp:expr:`lv_obj_bind_flag_if_le(widget, &subject, flags, ref_value)`
States
^^^^^^
The following methods of subscribing to an integer Subject affect a Widget's states
(or OR-ed combination of states). When the subscribing occurs, and each time the
Subject's value is changed thereafter, the Subject's value is compared with the
specified reference value, and the specified state(s) is (are):
- SET when the Subject's integer value fulfills the indicated condition, and
- CLEARED otherwise.
Here are the functions that carry out this method of subscribing to a Subject. The
``states`` argument can contain a single, or bit-wise OR-ed combination of any of the
``LV_STATE_...`` enumeration values.
:equal: :cpp:expr:`lv_obj_bind_state_if_eq(widget, &subject, states, ref_value)`
:not equal: :cpp:expr:`lv_obj_bind_state_if_not_eq(widget, &subject, states, ref_value)`
:greater than: :cpp:expr:`lv_obj_bind_state_if_gt(widget, &subject, states, ref_value)`
:greater than or equal: :cpp:expr:`lv_obj_bind_state_if_ge(widget, &subject, states, ref_value)`
:less than: :cpp:expr:`lv_obj_bind_state_if_lt(widget, &subject, states, ref_value)`
:less than or equal: :cpp:expr:`lv_obj_bind_state_if_le(widget, &subject, states, ref_value)`
Checked State
^^^^^^^^^^^^^
The following method of subscribing to an integer Subject affects a Widget's
:cpp:enumerator:`LV_STATE_CHECKED` state. When the subscribing occurs, and each time
the Subject's value is changed thereafter, the Subject's value is compared to a
reference value of ``0``, and the :cpp:enumerator:`LV_STATE_CHECKED` state is:
- CLEARED when the Subject's value is 0, and
- SET when the Subject's integer value is non-zero.
Note that this is a two-way binding (Subject <===> Widget) so direct (or
programmatic) interaction with the Widget that causes its
:cpp:enumerator:`LV_STATE_CHECKED` state to be SET or CLEARED also causes the
Subject's value to be set to ``1`` or ``0`` respectively.
- :cpp:expr:`lv_obj_bind_checked(widget, &subject)`
Label Widgets
~~~~~~~~~~~~~
.. |deg| unicode:: U+000B0 .. DEGREE SIGN
This method of subscribing to an integer Subject affects a Label Widget's
``text``. The Subject can be an STRING, POINTER or INTEGER type.
When the subscribing occurs, and each time the Subject's value is changed thereafter,
the Subject's value is used to update the Label's text as follows:
:string Subject: Subject's string is used to directly update the Label's text.
:pointer Subject: If NULL is passed as the ``format_string`` argument when
subscribing, the Subject's pointer value is assumed to point to a
NUL-terminated string. and is used to directly update the Label's
text. See :ref:`observer_format_string` for other options.
:integer Subject: Subject's integer value is used with the ``format_string`` argument.
See See :ref:`observer_format_string` for details.
Note that this is a one-way binding (Subject ===> Widget).
- :cpp:expr:`lv_label_bind_text(label, &subject, format_string)`
.. _observer_format_string:
The ``format_string`` Argument
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The ``format_string`` argument is optional and if provided, must contain exactly 1
printf-like format specifier and be one of the following:
:string or pointer Subject: "%s" to format the new pointer value as a string or "%p"
to format the pointer as a pointer (typically the
pointer's address value is spelled out with 4, 8 or 16
hexadecimal characters depending on the platform).
:integer Subject: "%d" format specifier (``"%" PRIdxx`` --- a
cross-platform equivalent where ``xx`` can be ``8``,
``16``, ``32`` or ``64``, depending on the platform).
If NULL is passed for the ``format_string`` argument:
:string or pointer Subject: Updates expect the pointer to point to a NUL-terminated string.
:integer Subject: The Label will display an empty string (i.e. nothing).
**Example:** "%d |deg|\C"
Arc Widgets
~~~~~~~~~~~
This method of subscribing to an integer Subject affects an Arc Widget's integer
value directly. Note that this is a two-way binding (Subject <===> Widget) so an end
user's direct interaction with the Arc Widget updates the Subject's value and vice
versa. (Requires :c:macro:`LV_USE_ARC` to be configured to ``1``.)
- :cpp:expr:`lv_arc_bind_value(arc, &subject)`
Slider Widgets
~~~~~~~~~~~~~~
This method of subscribing to an integer Subject affects a Slider Widget's integer
value directly. Note that this is a two-way binding (Subject <===> Widget) so an end
user's direct interaction with the Slider Widget updates the Subject's value and vice
versa. (Requires :c:macro:`LV_USE_SLIDER` to be configured to ``1``.)
- :cpp:expr:`lv_slider_bind_value(slider, &subject)`
Roller Widgets
~~~~~~~~~~~~~~
This method of subscribing to an integer Subject affects a Roller Widget's integer
value directly. Note that this is a two-way binding (Subject <===> Widget) so an end
user's direct interaction with the Slider Widget updates the Subject's value and vice
versa. (Requires :c:macro:`LV_USE_ROLLER` to be configured to ``1``.)
- :cpp:expr:`lv_roller_bind_value(roller, &subject)`
Drop-Down Widgets
~~~~~~~~~~~~~~~~~
This method of subscribing to an integer Subject affects a Drop-Down Widget's integer
value directly. Note that this is a two-way binding (Subject <===> Widget) so an end
user's direct interaction with the Drop-Down Widget updates the Subject's value and
vice versa. (Requires :c:macro:`LV_USE_DROPDOWN` to be configured to ``1``.)
- :cpp:expr:`lv_dropdown_bind_value(dropdown, &subject)`
.. _observer_example:
Examples
********
.. include:: ../../examples/others/observer/index.rst
.. _observer_api:
API
***

View File

@@ -1,86 +0,0 @@
.. _snapshot:
========
Snapshot
========
Snapshot provides an API to take a snapshot image for an LVGL Widget together
with its children. The image will look exactly like the Widget on the display.
.. _snapshot_usage:
Usage
*****
Simply call function :cpp:expr:`lv_snapshot_take(widget, color_format)` to generate
the image descriptor which can be used as an Image Widget's image source using
:cpp:func:`lv_image_set_src`.
Note, only following color formats are supported at this time:
- :cpp:enumerator:`LV_COLOR_FORMAT_RGB565`
- :cpp:enumerator:`LV_COLOR_FORMAT_RGB888`
- :cpp:enumerator:`LV_COLOR_FORMAT_XRGB8888`
- :cpp:enumerator:`LV_COLOR_FORMAT_ARGB8888`
Freeing the Image
-----------------
The memory :cpp:func:`lv_snapshot_take` uses is dynamically allocated using
:cpp:func:`lv_draw_buf_create`. Use :cpp:func:`lv_draw_buf_destroy` to free the
memory it allocated.
The snapshot image which is the draw buffer returned by :cpp:func:`lv_snapshot_take`
normally won't be added to the cache because it can be drawn directly. So you don't need
to invalidate the cache by calling :cpp:func:`lv_image_cache_drop` before destroying
the draw buffer.
The below code snippet demonstrates correct use of :cpp:func:`lv_snapshot_take`:
.. code-block:: c
void update_snapshot(lv_obj_t * widget, lv_obj_t * img_snapshot)
{
lv_draw_buf_t* snapshot = (void*)lv_image_get_src(img_snapshot);
if(snapshot) {
lv_draw_buf_destroy(snapshot);
}
snapshot = lv_snapshot_take(widget, LV_COLOR_FORMAT_ARGB8888);
lv_image_set_src(img_snapshot, snapshot);
}
Using an Existing Buffer
------------------------
If the snapshot needs to be updated repeatedly, or if the caller provides the draw
buffer, use :cpp:expr:`lv_snapshot_take_to_draw_buf(widget, color_format, draw_buf)`.
In this case, the caller is responsible for creating and destroying the draw buffer.
If snapshot is generated successfully, the image descriptor is updated,
the image data will be stored to the provided ``draw_buf``, and the function will
return :cpp:enumerator:`LV_RESULT_OK`.
Note that snapshot may fail if the provided buffer is not large enough, which can
happen if the Widget's size changes. It's recommended to use
:cpp:expr:`lv_snapshot_reshape_draw_buf(widget, draw_buf)` to first ensure the buffer
is large enough, and if it fails, destroy the existing draw buffer and call
`lv_snapshot_take` directly.
.. _snapshot_example:
Example
*******
.. include:: ../../examples/others/snapshot/index.rst
.. _snapshot_api:
API
***

View File

@@ -1,6 +0,0 @@
.. _xml_animation:
==========
Animations
==========

View File

@@ -1,275 +0,0 @@
.. _xml_api:
===
API
===
The ``<api>`` tag can be used in ``<widget>``s and ``<components>``, although each supports slightly different features.
Properties
**********
Inside ``<prop>`` elements, ``<param>`` elements can be defined to describe the arguments.
For **widgets**, all properties are optional.
If a property is not set on an instance of a widget, it simply won't be applied,
and the created widget's default value will be used (e.g., ``text`` for a label's text).
For **components**, all properties are mandatory; however, default values can be defined
to be used when a property is not set.
If a property has only one parameter (which is usually the case), a shorthand can be applied as shown below.
For example:
.. code-block:: xml
<api>
<prop name="range" default="0 100" help="Set the range.">
<param name="range_min" type="int" help="Sets the minimum value."/>
<param name="range_max" type="int" help="Sets the maximum value."/>
</prop>
<prop name="title" type="string" help="The title of the slider"/>
</api>
When a property is used, all parameters are set as a single attribute value. For example:
.. code-block:: xml
<my_slider range="-100 100" title="Room 1"/>
For **widgets**, each property corresponds to a setter function.
The ``name`` in ``<prop>`` is used to build the name of the setter function like this:
.. code-block:: c
<widget_name>_set_<prop_name>(lv_obj_t * obj, <param1_type> <param1_name>, <param2_type> <param2_name>, ...);
For **components**, the exported code contains only a single ``create`` function
to which all the properties need to be passed:
.. code-block:: c
<component_name>_create(lv_obj_t * parent, <param1_type> <param1_name>, <param2_type> <param2_name>, ...);
``<prop>`` elements have an optional ``<postponed>`` boolean attribute.
By default, it is ``false``, but if set to ``true``, the given property will be applied after all children are created.
A practical example is setting the current tab of a tab view, which cannot be set before the tabs are created.
This feature is not supported yet.
``<enumdef>``
*************
Can be used only for widgets.
Used to define new enum types for a given widget.
It should contain ``<enum>`` elements to define the possible options.
Example:
.. code-block:: xml
<!-- my_widget.xml -->
<api>
<enumdef name="my_widget_mode" help="Possible modes">
<enum name="normal" help="Normal mode" value="0x10"/>
<enum name="inverted" help="Inverted mode"/>
</enumdef>
<prop name="mode" help="Set the widget mode">
<param name="mode" type="enum:my_widget_mode"/>
</prop>
</api>
Note that the enum values are not important because:
1. When the code is exported, the enum names will be used, and the compiler will substitute the values.
2. When loaded from XML, the widget's XML parser should convert the enum names to C enum fields.
``<element>``
*************
Also applies only to widgets.
Elements are used to describe sub-widgets or internal parts of widgets.
Examples include the list of a dropdown, the tabs of a tab view, or the series of a chart.
Elements can have ``<arg>`` and ``<prop>`` definitions. ``<arg>`` elements are mandatory (default values are supported)
as they are used to create the element, whereas ``<prop>`` elements are optional as they are mapped to setter functions.
An element in a ``<view>`` can be referenced like this: ``<widget_name-element_name>``.
The widget name and the element name are separated by a ``-``, so ``-`` is not allowed in widget and
element names (only ``_`` can be used).
Example:
.. code-block:: xml
<my_chart-super_series color="0xff0000"/>
An important attribute of elements is ``access``. The possible values are:
- ``add``: Create any number of elements dynamically (e.g., chart series).
- ``get``: Get a pointer to an implicitly created widget or any data (e.g., list of the dropdown).
- ``set``: Select specific parts of the widget with indexes (e.g., table cells).
Elements with ``access="add"`` or ``access="get"`` can have a custom data type defined using ``type="my_data"``.
In these cases, no children can be added. If the ``type`` is ``lv_obj``, the element can have children.
It is not yet possible to describe the ``<view>`` of elements in XML; only the API can be defined.
The actual implementation needs to be done in C.
``access="add"``
----------------
The element is explicitly created with an ``add`` function, e.g., ``lv_tabview_add_tab(obj, "Title");``.
``<arg>`` elements defined directly inside the ``<element>`` are passed to the ``add`` function as arguments.
Example:
.. code-block:: xml
<!-- my_widget.xml -->
<api>
<element name="indicator" type="obj" help="The indicator of my_widget" access="add">
<arg name="color" type="color" help="Help for color"/>
<arg name="max_value" type="int" help="Help for max_value"/>
<prop name="value" help="Set a new value for the indicator">
<param name="value" type="int" help="Help for value"/>
</prop>
</element>
</api>
<view extends="obj">
<button name="btn1"/>
</view>
In a view it can be used like this:
.. code-block:: xml
<!-- complex_widget.xml -->
<view>
<lv_label text="Title"/>
<my_widget width="100px" y="40px">
<my_widget-indicator name="indic1" color="0xff0000" max_value="120" value="30"/>
</my_widget>
</view>
From the API definition the following functions are generated:
.. code-block:: c
lv_obj_t * my_widget_add_indicator(lv_obj_t * parent, lv_color_t color, int32_t max_value);
void my_widget_set_indicator_value(lv_obj_t * obj, int32_t value);
And this is the related C file where the indicator is created:
.. code-block:: c
lv_obj_t * indic1 = my_widget_add_indicator(parent, color, max_value);
lv_my_widget_set_indicator_value(indic1, value);
``access="get"``
----------------
If the element is created internally and implicitly, it can be retrieved with a function like ``lv_dropdown_get_list(obj);``.
``<arg>`` elements are passed to the ``get`` function as arguments.
Example:
.. code-block:: xml
<!-- my_widget.xml -->
<api>
<element name="control_button" type="obj" help="A control button of my_widget" access="get">
<arg name="index" type="int" help="Zero-based index of the control button"/>
<prop name="title">
<param name="text" type="string"/>
</prop>
</element>
</api>
In a view:
.. code-block:: xml
<!-- complex_widget.xml -->
<view>
<my_widget width="100px">
<my_widget-control_button name="btn1" index="3" title="Hello"/>
</my_widget>
</view>
Generated API:
.. code-block:: c
lv_obj_t * my_widget_get_control_button(lv_obj_t * parent, int32_t index);
void my_widget_set_control_button_title(lv_obj_t * obj, const char * text);
And this is a C file where the control button is retrieved:
.. code-block:: c
lv_obj_t * btn1 = lvmy_widget_get_control_button(parent, index);
my_widget_set_control_button_title(btn1, text);
``access="set"``
----------------
Used when elements are created automatically but need to be selected in API calls,
e.g., ``lv_table_set_cell_value(table, row, col, "text");``.
Example:
.. code-block:: xml
<!-- my_widget.xml -->
<api>
<element name="item" type="obj" help="An item on my_widget" access="set">
<arg name="index" type="int" help="The zero-based index of the item"/>
<prop name="icon" help="Set the icon of an item">
<param name="icon_src" type="img_src" help="The image to set as an icon."/>
</prop>
<prop name="color" help="Set the color">
<param name="color" type="color" help="The color to set for the item."/>
</prop>
</element>
</api>
In a view:
.. code-block:: xml
<!-- complex_widget.xml -->
<view>
<my_widget width="100px">
<my_widget-item index="3" icon_src="image1" color="0xff0000"/>
</my_widget>
</view>
This is the generated header file:
.. code-block:: c
void my_widget_set_item_icon(lv_obj_t * parent, int32_t index, const void * icon_src);
void my_widget_set_item_color(lv_obj_t * parent, int32_t index, lv_color_t color);
And this is the related C file where the item properties are set:
.. code-block:: c
my_widget_set_item_icon(parent, index, image1);
my_widget_set_item_color(parent, index, color);

View File

@@ -1,149 +0,0 @@
.. _xml_component_library:
=================
Component Library
=================
Overview
********
The collection of Components, Widgets, Screens, Images, Fonts, etc., is called a component library.
A component library can be fully self-sufficient, but it can also reference data from other component libraries.
LVGL itself is a component library that supplies the built-in widgets, types, etc., so typically, component libraries use
at least the core LVGL data. You can find the XML files that describe the LVGL widgets `here <https://github.com/lvgl/lvgl/tree/master/xmls>`_.
A project is also a component library, where the screens, components, and widgets of the project are implemented.
In light of that, a project can contain multiple component libraries (its own project component library, LVGL, and possibly others).
Structure
*********
A typical structure for a component library looks like this:
.. code-block:: none
name_of_the_component_library
├── globals.xml
├── components
│ ├── component1.xml
│ ├── component2.xml
│ └── other_folder
│ ├── component3.xml
│ └── component4.xml
├── widgets
│ ├── widget1
│ │ ├── widget1.xml
│ │ ├── widget1.c
│ │ ├── widget1.h
│ │ ├── widget1_gen.c
│ │ ├── widget1_gen.h
│ │ ├── widget1_private_gen.h
│ │ └── widget1_xml_parser.c
│ └── widget2
│ └── ...same as widget1...
├── screens
│ ├── screen1.xml
│ └── screen2.xml
├── fonts
│ ├── font1.ttf
│ └── font2.ttf
└── images
├── image1.png
└── image2.png
Visibility
**********
A component library can use images, fonts, components, widgets, etc., from other component libraries.
It is the user's responsibility to avoid naming conflicts by prefixing names. For example, all
data belonging to the LVGL core component library is prefixed by ``lv_`` (e.g., ``lv_label``, ``lv_montserrat_22``).
A custom component can be prefixed with ``watch_``, ``small_``, ``light_``, or anything else that the developer finds appropriate.
LVGL's UI editor will show an error if there is a naming conflict.
globals.xml
***********
A ``globals.xml`` file should be created in each component library.
The definitions in it do not belong to any specific widget but are available throughout the entire UI, widgets, and all XML files.
The valid tags in it are:
- ``<config>``: Can specify name and help.
- ``<api>``: Only for ``<enumdefs>``.
- ``<subjects>``: List of subjects. Can be considered the API of a component library.
- ``<consts>``: Globally available constants.
- ``<styles>``: Globally available styles.
- ``<fonts>``: Globally available fonts.
- ``<images>``: Globally available images.
- ``<const_variants>``: See below.
- ``<style_variants>``: See below.
``globals.xml`` files cannot be nested, meaning that there cannot be another ``globals.xml`` file in a subfolder.
From each ``globals.xml`` file, an ``<config.name>.h`` file is generated,
which is included in all generated header files — not only in the subfolders where ``globals.xml`` is created, but in all exported C and H files.
This ensures that 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 is useful for selecting 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 subject ``<style_variant.name>_variant`` is created.
Styles can subscribe to this, and the style properties can be changed according to the
selected variant.
In ``globals.xml``, the possible variants should be described.
Example
-------
A ``globals.xml`` file of a component library can 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"/>
<enum name="fast"/>
</enumdef>
</api>
<consts>
<px name="small_unit" value="8"/>
<px name="large_unit" value="16"/>
</consts>
<styles>
<style name="card" bg_color="0xeee" radius="#small_unit" padding="12px"/>
</styles>
<images>
<file name="arrow_left" src="A:/images/arrow_left.png"/>
</images>
<fonts>
<tinyttf name="big" src="A:/fonts/arial.ttf" size="28"/>
</fonts>
</globals>

View File

@@ -1,136 +0,0 @@
.. _xml_components:
==========
Components
==========
Overview
--------
``<component>`` can have ``<consts>``, ``<api>``, ``<styles>``, and ``<view>`` tags inside.
Unlike widgets (which are always compiled into the application), components can either:
1. Be loaded at runtime from XML.
2. Be exported to C code.
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...);
When a component is used in another components 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>`.
The user can also call these ``_create_`` functions at any time from the 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.
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="...">``).
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
***

View File

@@ -1,69 +0,0 @@
.. _xml_consts:
=========
Constants
=========
Overview
********
Constants can be defined to replace any value with a selected type or to be used as special values.
The supported types are:
- ``color``
- ``px``
- ``percentage``
- ``string``
- ``opa``
- ``bool``
Usage
*****
.. code-block:: xml
<consts>
<color name="color1" value="0xff0000" help="Primary color"/>
<px name="pad_xs" value="8" help="Small padding"/>
</consts>
Constants can be used in:
- Style properties
- Widget and component properties
And they can be used like this:
.. code-block:: xml
<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.

View File

@@ -1,7 +0,0 @@
.. _xml_events:
======
Events
======
TODO

View File

@@ -1,99 +0,0 @@
.. _xml_fonts:
=====
Fonts
=====
Overview
********
A ``<fonts>`` section can be added in ``globals.xml`` files.
Later, it might be supported in components and widgets to define local fonts and keep the global space cleaner.
Usage
*****
The following section creates a mapping between font names and their paths with various attributes:
.. code-block:: xml
<fonts>
<bin as_file="false" name="medium" src="path/to/file.ttf" range="0x20-0x7f" symbols="°" size="24"/>
<tiny_ttf as_file="true" name="big" src_path="path/to/file.ttf" range="0x20-0x7f" symbols="auto" size="48"/>
<freetype name="chinese" src_path="file.ttf" size="48" custom_freetype_attribute="abc"/>
</fonts>
In ``<styles>`` and ``<view>``, fonts can be referenced by their name, e.g.,
.. code-block:: xml
<style name="style1" text_font="medium"/>
The tag name determines how the font is loaded. Currently, only ``tinyttf as_file="true"`` is supported.
- ``bin``:
- If ``as_file="true"``: Converts the font file to ``bin`` (see `lv_font_conv`)
which will be loaded by ``lv_binfont_create()``.
- If ``as_file="false"`` (default): On export, the font file will be converted to a C array LVGL font
that can be used directly by LVGL.
- ``tinyttf``:
- If ``as_file="true"``: Can be loaded directly by ``lv_tiny_ttf_create_file()``.
- If ``as_file="false"`` (default): The font file will be converted to a raw C array on export
that will be loaded by ``lv_tiny_ttf_create_data()``.
- ``freetype``: The file can be loaded directly by ``lv_freetype_font_create()``.
For simplicity, if ``as_file="false"``, fonts will be loaded as files in the preview.
Setting ``as_file="false"`` affects only the C export.
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 creates the mapping. This is because the XML parser cannot
automatically map an LVGL font definition like:
.. code-block:: c
lv_font_t my_font_24;
to
.. code-block:: xml
<bin name="my_font_24"/>
Exported Code
-------------
When C code is exported, global ``const lv_font_t * <font_name>`` variables are created, and in the
initialization function of the component library (e.g., ``my_lib_init_gen()``), the actual font is assigned.
.. Note: :cpp:expr: role cannot be used here because it doesn't know how to parse
the ampersand and angle brackets. An alternate approach could be to make the
arguments "style1_p, font_name", but leaving the ampersand there seems more
appropriate due to that IS the normal way to pass a style as an argument.
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.

View File

@@ -1,78 +0,0 @@
.. _xml_images:
======
Images
======
Overview
********
An ``<images>`` section can be added to ``globals.xml`` files.
Later, it might also be supported in components and widgets to define local images and keep the global space cleaner.
This ``<images>`` section describes how to map images with names.
Only ``<file>`` is currently supported, 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>
</images>
- ``<file>`` means that the image source is used as a file path:
- ``<data>`` means that the image is converted to a C array on export.
In both cases in the exported C code global ``const void * <image_name>`` variables are created and in the
initialization function of the component library (e.g. ``my_lib_init_gen()``) either the path or
the pointer to the converted :cpp:expr:`lv_image_dsc_t` pointers are assigned to that variable.
In :cpp:expr:`lv_image_set_src(image, image_name)` is used
instead of the path or :cpp:expr:`lv_image_dsc_t` pointer directly.
For simplicity, in the UI Editor preview, images are always loaded as files.
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 creates the mapping. This is because the XML parser cannot
automatically map an image like:
.. code-block:: c
lv_image_dsc_t my_logo;
to
.. code-block:: xml
<data name="my_logo"/>
Constants
---------
Constants can be used with images as well.
.. code-block:: xml
<consts>
<int name="icon_size" value="32">
<variant name="size" case="small" value="16"/>
</int>
</consts>
<images>
<data name="icon_apply" src_path="apply.png">
<convert path="raw/apply.png" width="#icon_size"/>
</data>
</images>

View File

@@ -1,31 +0,0 @@
.. _xml_component:
====================
XML - Declarative UI
====================
.. toctree::
:maxdepth: 1
intro
component_library
project
syntax
components
screens
widgets
preview
api
styles
consts
view
fonts
images
events
subjects
animations
translations

View File

@@ -1,178 +0,0 @@
.. _xml_intro:
============
Introduction
============
Overview
********
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 Editor to write the XML files. The UI Editor provides
features like:
- Instant preview the XML files
- Autocomplete and Syntax highlight
- Online preview for collaboration and testing
- Figma integration to easily reimplement the designs
.. warning::
The UI 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:
- XML files can be loaded at runtime (e.g., from an SD card) to change the application build.
- XML is simpler to write than C, enabling people with different skill sets to create LVGL UIs.
- XML is textual data, making it easy to parse and manipulate with scripts.
- XML can be used to generate LVGL code in any language.
- XML helps to separate the view from the logic.
Currently supported features:
- Load XML components at runtime from file or data
- Nest components and widgets any deep
- Dynamically create instances of XML components in C
- Register images and font that can be accessed by name later in the XMLs (only from file, no C file is generated for image and fonts)
- Constants are working for widget and style properties
- Parameters can be defined and passed and used for components
- Most of the built-in widgets, even the complex ones (``label``, ``slider``, ``bar``, ``button``, ``chart``, ``scale``, ``button matrix``, ``table``, etc.)
- Style sheets and local styles that 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.
- Subjects are not supported yet.
- The documentation is not complete yet.
Concept
*******
The XML files are component-oriented. To be more specific, they are ``component library`` oriented.
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.
These component libraries are independent, can be reused across many products, and can be freely versioned and managed.
You can 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.
If someone finds a bug in the component library, they can just fix it and push it back to the git
repository so that other projects can pull it.
The built-in widgets of LVGL are also considered a ``component library`` which is always available.
Widgets, Components, and Screens
********************************
It's important to distinguish between ``widgets`` and ``components``:
**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.
- Similar to LVGL's built-in widgets.
- Built from ``lv_obj_class``-es.
- Have custom and complex logic inside.
- Cannot be loaded from XML at runtime because the custom code cannot be loaded.
- Have a large API with ``set/get/add`` functions.
- Support "internal widgets" (e.g., ``tabview``'s tabs, ``dropdown``'s list).
Any handwritten widget can be accessed from XML by:
1. Defining its API in an XML file.
2. Writing and registering an XML parser for it. See some examples here.
**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.
- Built in XML only and cannot have custom C code.
- Can be loaded from XML at runtime as they describe only the visuals.
- Built from widgets or other components.
- Can be used for styling widgets.
- Can contain widgets or other components.
- Can have a simple API to pass properties to the children (e.g., ``btn_text`` to label's text).
Regardless of whether the XML was written manually or by the UI Editor, the XMLs of the 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, so that it can create components accordingly.
**Screens** are similar to components:
- In XML, they start with a ``<screen>`` root element.
- Built from widgets or other components to describe the screen.
- Can be loaded from XML at runtime as they describe only the visuals.
- Do not have an API.
- 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 other XML elements inside are almost identical.
This is the high-level overview of the most important XML tags inside these root elements:
- ``<api>``: Describes the properties that can be ``set`` for a widget or component.
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 by ``#``.
- ``<styles>``: Describes styles (``lv_style_t``) that can be referenced by widgets and components later.
- ``<view>``: Specifies the appearance of the widget or component by describing the
children and their properties.
This is a simple example illustrating how an LVGL XML component looks like.
Note that only the basic features are shown here.
.. code-block:: xml
<component>
<consts>
<px name="size" value="100"/>
<color name="orange" value="0xffa020"/>
</consts>
<api>
<prop name="btn_text" default="Apply" type="string"/>
</api>
<styles>
<style name="blue" bg_color="0x0000ff" radius="2"/>
<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>
</component>
Usage teaser
************
LVGL's UI 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. The XML files were used only during
editing/implementing the widgets and components to save recompilation time and
optionally leverage other useful 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 ``lv_xml_component_register_from_file(path)`` and
an instance can be created with ``lv_obj_t * obj = lv_xml_create(parent, "my_button", NULL);``.

View File

@@ -1,47 +0,0 @@
.. _xml_preview:
=======
Preview
=======
Overview
********
In ``<component>`` and ``<widget>``, it is possible to define ``<preview>`` tags.
These are **not** exported to code and are **not** loaded from XML.
They are used only by the UI Editor to describe the context of the component.
For example, you might want to:
- Change the background of the Editor's preview to dark.
- Center the component.
- Set margins.
- Change the size of the preview.
Usage
*****
You can think of a ``<preview>`` tag as an ``lv_obj`` where the following properties can be used:
- ``width``, ``height``
- Any local style properties, for example, ``style_bg_color="0x333"``
- ``flex`` and ``flex_flow``
It is also possible to define multiple previews, and in the UI Editor, you can select one of them.
Example
*******
.. code-block:: xml
<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"/>
</previews>
<view>
...
</view>
</component>

View File

@@ -1,47 +0,0 @@
.. _xml_project:
=======
Project
=======
Overview
********
A single ``project.xml`` file should be created for each project where the following content is specified:
- ``<folders>``: Specifies the path to component libraries. LVGL's base widgets are always loaded automatically.
- ``<targets>``: Describes various hardware configurations, allowing the UI Editor to check if the UI is out of resources and to
select different previews for each screen according to the specified displays.
Example
*******
.. code-block:: xml
<project>
<folders>
<folder path="../widget_lib1"/>
<folder path="/home/user/work/ui_libs/modern"/>
<folder path="https://github.com/user/repo"/>
</folders>
<targets>
<renesas-RA8D1-EK gpu="true"/>
<target name="small">
<display width="320" height="240" color_format="RGB565"/>
<memory name="int_ram" size="128K"/>
<memory name="ext_ram" size="2M"/>
<memory name="int_flash" size="512K"/>
<memory name="ext_flash" size="32M"/>
</target>
<target name="large">
<display width="1024" height="768" color_format="XRGB8888"/>
<memory name="int_ram" size="128K"/>
<memory name="ext_ram" size="2M"/>
<memory name="int_flash" size="512K"/>
<memory name="ext_flash" size="32M"/>
</target>
</targets>
</project>

View File

@@ -1,53 +0,0 @@
.. _xml_screens:
=======
Screens
=======
Overview
********
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>``.
Usage
*****
Each XML file describes a screen. 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 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.
Example
*******
.. 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>

View File

@@ -1,136 +0,0 @@
.. _xml_styles:
======
Styles
======
Overview
********
In XML files, both style sheets (:cpp:expr:`lv_style_t`) and local styles can be used.
Style variants are also supported to change style properties at runtime.
Style Sheets
************
In the ``<styles>`` section, styles and their properties can be defined like this:
.. code-block:: xml
<style name="red"
help="What is this style about?"
border_width="2px"
border_color="0xff0000"/>
Styles can be referenced like this in the ``<view>``:
.. code-block:: xml
<view>
<slider styles="main red:indicator red:knob:focused"/>
</view>
As shown in the example, parts and states are appended after a ``:`` to the style's name.
Local Styles
************
Local styles can be used directly in a widget, for example:
.. code-block:: xml
<lv_label style_bg_opa="200" style_bg_opa:disabled="100"/>
Style Variants
**************
The ``<style>`` tags can have ``<variant>`` child tags:
.. code-block:: xml
<styles>
<style name="big_button" bg_color="0xf00" border_width="1px" pad_left="10px">
<variant name="color" case="red" bg_color="0xf00"/>
<variant name="color" case="green" bg_color="0x0f0"/>
<variant name="color" case="blue" bg_color="0x00f"/>
</style>
</styles>
``<variant>`` elements allow altering styles at runtime.
The ``variant_<name>`` subjects of the component library are used for each setting, and an observer callback is generated with all the style properties valid for that variant. The observer callback first resets the style and then sets all the properties.
This feature is not supported yet.
Gradients
*********
Before the ``<styles>`` tag, the ``<gradients>`` tag can be used to describe various gradients, which can later be referenced in styles.
When a gradient is created, it can be referenced by its name, like:
.. code-block:: xml
<style bg_grad="grad1"/>
or
.. code-block:: xml
<lv_button style_bg_grad="grad1"/>
Horizontal or Vertical Gradient
-------------------------------
Define simple ``<horizontal>`` or ``<vertical>`` gradients:
.. code-block:: xml
<gradients>
<horizontal name="grad1">
<stop color="#ff0000" offset="20%" opa="40%"/>
<stop color="#00ff00" offset="128" opa="100%"/>
</horizontal>
</gradients>
Linear Gradient
---------------
Define a skewed gradient from two points:
.. code-block:: xml
<gradients>
<linear name="grad1" start="50 50" end="100 80">
<stop color="#ff0000" offset="20%" opa="100%"/>
<stop color="#00ff00" offset="240" opa="100%"/>
</linear>
</gradients>
Radial Gradient
---------------
Define a radial gradient:
.. code-block:: xml
<gradients>
<radial name="grad1" center="100 50%" edge="200 50" focal_center="50 80%" focal_edge="55 80%">
<stop color="#ff0000" opa="100%"/>
<stop color="#00ff00" opa="100%"/>
</radial>
</gradients>
Conical Gradient
----------------
Define a conical gradient:
.. code-block:: xml
<gradients>
<conical name="grad1" center="80 50%" angle="45 270">
<stop color="#ff0000" opa="100%"/>
<stop color="#00ff00" opa="100%"/>
</conical>
</gradients>

View File

@@ -1,7 +0,0 @@
.. _xml_subjects:
========
Subjects
========
TODO

View File

@@ -1,115 +0,0 @@
.. _xml:
======
Syntax
======
Naming conventions
******************
- A standard XML syntax is used.
- Lowercase letters with ``_`` separation are used for attribute names.
- The usual variable name rules apply for attribute and tag names: only letters, numbers, ``'_'`` and cannot start with a number.
- LVGL API is followed as much as possible, e.g., ``align="center"``, ``bg_color="0xff0000"``.
- For colors, all these options are supported: ``0x112233``, ``#112233``, ``112233``, ``0x123``, ``#123``, ``123``.
- ``params`` can be referenced with ``$``.
- ``consts`` can be referenced with ``#``.
- ``styles`` can be attached to states and/or parts like ``styles="style1 style2:pressed style3:focused:scrollbar"``.
- Local styles can be used like ``<lv_label style_text_color="0xff0000" style_text_color:checked="0x00ff00"/>``.
Types
*****
All of the types can be used as API property types, but only a subset of them can be used as constant and subject types.
Simple types
------------
The following simple built-in types are supported:
- ``bool``: ``true`` or ``false``.
- ``int``: Integer number in the range of -2M to 2M by default. (``int32_t`` in C)
- ``px``: Simple pixel unit. The unit ``px`` can be omitted.
- ``%``: Percentage. ``%`` must be appended as a unit. (Means :cpp:expr:`lv_pct()`)
- ``content``: Means ``LV_SIZE_CONTENT``.
- ``string``: Simple ``\0`` terminated string. When multiple strings are used in a property or string array, ``'`` should be used. E.g. ``foo="'a' 'b'"``.
- ``color``: A color stored as 24-bit RGB. (:cpp:expr:`lv_color_t`)
- ``opa``: Opacity value in the range of 0 to 255 or 0 to 100%.
- ``lv_obj``: Pointer to a widget (:cpp:expr:`lv_obj_t *`).
- ``point``: A point with ``x`` and ``y`` values. (:cpp:expr:`lv_point_t`)
- ``arglist``: Just list all the parameters as arguments. Supports only ``int`` and ``string``. E.g. ``foo="1 23 'hello' 'a'"``.
Name-based types
----------------
In XML files, fonts, images, styles, etc., are not used by pointer but by string names. For example, a style is defined like
``<style name="red" bg_color="0xff0000"/>``. Later, these can be referenced by their names.
This means that the actual values need to be bound to the names when the UI is loaded from XML,
otherwise, LVGL wouldn't know what a name means.
Most of these connections are done automatically (e.g., for styles, fonts, images, animations, gradients, etc.),
but others need to be connected manually (e.g., event callbacks where the callback itself is available only in the code).
For fonts and images, the connections are created automatically if the source is a file.
If the font or image is compiled into the application (as a C array), the user needs to specify which
variable a given name refers to.
To create these connections, functions like ``lv_xml_register_image/font/event_cb/etc(name, pointer)`` can be used.
Later, the set pointer can be retrieved by ``lv_xml_get_image/font/event_cb(name)``.
- ``style``: The name of a style. :cpp:expr:`lv_xml_get_style(name)` returns an :cpp:expr:`lv_style_t *`.
- ``font``: The name of a font. :cpp:expr:`lv_xml_get_font(name)` returns an :cpp:expr:`lv_font_t *`.
- ``image``: The name of an image. :cpp:expr:`lv_xml_get_image(name)` returns an :cpp:expr:`const void *`, which can be :cpp:expr:`lv_image_dsc_t *` or a path to a file.
- ``animation``: The name of an animation descriptor. :cpp:expr:`lv_xml_get_anim(name)` returns an :cpp:expr:`lv_anim_t *`.
- ``subject``: The name of a subject. :cpp:expr:`lv_xml_get_subject(name)` returns an :cpp:expr:`lv_subject_t *`.
- ``grad``: The name of a gradient. :cpp:expr:`lv_xml_get_grad(name)` returns an :cpp:expr:`lv_grad_dsc_t *`.
- ``event_cb``: The name of an event callback. :cpp:expr:`lv_xml_get_event_cb(name)` returns an :cpp:expr:`lv_event_cb_t`.
Arrays
------
Any type can be an array in four ways:
- ``int[N]``: An integer array with ``N`` elements, and the count is passed as the next parameter.
- ``string[...NULL]``: An array terminated with ``NULL``. ``NULL`` can be replaced by any value, e.g., ``grid_template_last``.
- ``string[5]``: An array that must have exactly 5 elements.
- ``string[]``: No ``NULL`` termination and no count parameter.
Enums
-----
``<enumdef>`` can be used in the ``<api>`` tags to create custom enums for **widgets**. It is not supported for components.
For example:
.. code-block:: xml
<api>
<enumdef name="my_widget_mode" help="Possible modes" help-zh="Chinese help">
<enum name="normal" help="Normal mode" help-zh="Normal mode in Chinese" value="0x10"/>
<enum name="inverted" help="Inverted mode"/>
</enumdef>
<prop name="mode" help="help">
<param name="mode" type="enum:my_widget_mode" help="help"/>
</prop>
</api>
When used as a type, a ``+`` suffix means multiple values can be selected and ORed. For example: ``type="axis+"``.
In this case, the options should be separated by ``|``, for example: ``axis=primary_x|secondary_y``.
Compound types
--------------
Types can be compound, meaning multiple options/types are possible. For example, for width: ``type="px|%|content"``.
Limit the possible values
-------------------------
It is also possible to limit the possible options the user can select from an enum. For example:
- Enums: ``type="dir(top bottom)"``
- Colors: ``type="color(0xff0000 0x00ff00 0x0000ff)"``
- Strings: ``type="string('Ok' 'Cancel')``
These are checked in the UI Editor, and if an invalid option is selected, it will be highlighted as an error.

View File

@@ -1,7 +0,0 @@
.. _xml_translations:
============
Translations
============
TODO

View File

@@ -1,7 +0,0 @@
.. _xml_view:
=========
View
=========
TODO

View File

@@ -1,101 +0,0 @@
.. _xml_widgets:
=======
Widgets
=======
Overview
********
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 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.