docs(restructure): restructure TOC and contents for ease of use (#7847)
This commit is contained in:
@@ -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
|
||||
***
|
||||
|
||||
@@ -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
|
||||
***
|
||||
@@ -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
|
||||
***
|
||||
@@ -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
|
||||
***
|
||||
|
||||
@@ -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
|
||||
***
|
||||
|
||||
@@ -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
|
||||
***
|
||||
@@ -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
|
||||
@@ -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
|
||||
***
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
***
|
||||
@@ -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
|
||||
***
|
||||
@@ -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
|
||||
***
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
.. _xml_animation:
|
||||
|
||||
==========
|
||||
Animations
|
||||
==========
|
||||
|
||||
@@ -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);
|
||||
@@ -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>
|
||||
@@ -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 component’s XML code and the code is exported, this ``create`` function will be called.
|
||||
This means that components do not have a detailed set/get API but can be created with a fixed set of parameters.
|
||||
|
||||
If the user needs to access or modify values dynamically, it is recommended to use a :ref:`subject <observer>`.
|
||||
|
||||
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
|
||||
***
|
||||
@@ -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.
|
||||
@@ -1,7 +0,0 @@
|
||||
.. _xml_events:
|
||||
|
||||
======
|
||||
Events
|
||||
======
|
||||
|
||||
TODO
|
||||
@@ -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.
|
||||
@@ -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>
|
||||
@@ -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
|
||||
|
||||
@@ -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);``.
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -1,7 +0,0 @@
|
||||
.. _xml_subjects:
|
||||
|
||||
========
|
||||
Subjects
|
||||
========
|
||||
|
||||
TODO
|
||||
@@ -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.
|
||||
@@ -1,7 +0,0 @@
|
||||
.. _xml_translations:
|
||||
|
||||
============
|
||||
Translations
|
||||
============
|
||||
|
||||
TODO
|
||||
@@ -1,7 +0,0 @@
|
||||
.. _xml_view:
|
||||
|
||||
=========
|
||||
View
|
||||
=========
|
||||
|
||||
TODO
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user