feat(docs): reorganize docs (#7136)

This commit is contained in:
Victor Wheeler
2024-10-23 12:53:33 -06:00
committed by GitHub
parent c61ca42a2a
commit 9b6f6d23f1
212 changed files with 6314 additions and 5806 deletions

View File

@@ -0,0 +1,115 @@
.. _file_explorer:
=============
File Explorer
=============
``lv_file_explorer`` provides an API to browse the contents of the file
system. ``lv_file_explorer`` only provides the file browsing function,
but does not provide the actual file operation function. In other words,
you can't click a picture file to open and view the picture like a PC.
``lv_file_explorer`` will tell you the full path and name of the
currently clicked file. The file operation function needs to be
implemented by the user.
The file list in ``lv_file_explorer`` is based on
:ref:`lv_table`, and the quick access bar is based on
:ref:`lv_list`. Therefore, care should be taken to ensure
that :ref:`lv_table` and :ref:`lv_list` are
enabled.
.. _file_explorer_usage:
Usage
-----
Enable :c:macro:`LV_USE_FILE_EXPLORER` 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 widget.
Quick access
~~~~~~~~~~~~
The quick access bar is optional. You can turn off
:c:macro:`LV_FILE_EXPLORER_QUICK_ACCESS` in ``lv_conf.h`` so that the quick
access bar will not be created. This can save some memory, but not much.
After the quick access bar is created, it can be hidden by clicking the
button at the top left corner of the browsing area, which is very useful
for small screen devices.
You can use
:cpp:expr:`lv_file_explorer_set_quick_access_path(file_explorer, LV_FILE_EXPLORER_QA_XX, "path")`
to set the path of the quick access bar. The items of the quick access
bar are fixed. Currently, there are the following items:
- :cpp:enumerator:`LV_FILE_EXPLORER_QA_HOME`
- :cpp:enumerator:`LV_FILE_EXPLORER_QA_MUSIC`
- :cpp:enumerator:`LV_FILE_EXPLORER_QA_PICTURES`
- :cpp:enumerator:`LV_FILE_EXPLORER_QA_VIDEO`
- :cpp:enumerator:`LV_FILE_EXPLORER_QA_DOCS`
- :cpp:enumerator:`LV_FILE_EXPLORER_QA_MNT`
- :cpp:enumerator:`LV_FILE_EXPLORER_QA_FS`
.. _file_explorer_sort:
Sort
~~~~
You can use
:cpp:expr:`lv_file_explorer_set_sort(file_explorer, LV_EXPLORER_SORT_XX)` to set
sorting method.
There are the following sorting methods:
- :cpp:enumerator:`LV_EXPLORER_SORT_NONE`
- :cpp:enumerator:`LV_EXPLORER_SORT_KIND`
You can customize the sorting. Before custom sort, please set the
default sorting to :cpp:enumerator:`LV_EXPLORER_SORT_NONE`. The default is
:cpp:enumerator:`LV_EXPLORER_SORT_NONE`.
.. _file_explorer_events:
Events
------
- :cpp:enumerator:`LV_EVENT_READY` Sent when a directory is opened. You can customize
the sort.
- :cpp:enumerator:`LV_EVENT_VALUE_CHANGED` Sent when an item (file) in the file list
is clicked.
You can use :cpp:func:`lv_file_explorer_get_cur_path` to get the current path
and :cpp:func:`lv_file_explorer_get_sel_fn` 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_cur_path(widget);
char * sel_fn = lv_file_explorer_get_sel_fn(widget);
LV_LOG_USER("%s%s", cur_path, sel_fn);
}
}
You can also save the obtained **path** and **file** name into an array
through functions such as :cpp:func:`strcpy` and :cpp:func:`strcat` for later use.
.. _file_explorer_example:
Example
-------
.. include:: ../../examples/others/file_explorer/index.rst
.. _file_explorer_api:
API
---

View File

@@ -0,0 +1,118 @@
.. _font_manager:
============
Font Manager
============
Font Manager is a secondary encapsulation based on :ref:`freetype`, which
facilitates upper-level applications to manage and use vector fonts. Currently
supported functions include:
- 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 :c:macro:`LIB_FREETYPE` and `LV_USE_FONT_MANAGER` in ``lv_conf.h``.
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), please use :cpp:func:`lv_font_manager_add_path` to
add the path. This API will copy the path content to the internal management.
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 :cpp:func:`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.
The font manager will mark the font resource as a recyclable font so that it can be
quickly created next time.
Note that you need to delete the widget that references 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 destroy and return false.
.. _font_manager_example:
Example
*******
.. include:: ../../examples/others/font_manager/index.rst
.. _font_manager_api:
API
***

View File

@@ -0,0 +1,86 @@
.. _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 navigation. You can use
fragment manager to build navigation stack, or multi pane application
easily.
.. _fragment_usage:
Usage
-----
Enable :c:macro:`LV_USE_FRAGMENT` in ``lv_conf.h``.
Create Fragment Class
~~~~~~~~~~~~~~~~~~~~~
.. code-block:: c
struct sample_fragment_t {
/* IMPORTANT: don't miss this part */
lv_fragment_t base;
/* States, object references and data fields for this fragment */
const char *title;
};
const lv_fragment_class_t sample_cls = {
/* Initialize something needed */
.constructor_cb = sample_fragment_ctor,
/* Create view objects */
.create_obj_cb = sample_fragment_create_obj,
/* IMPORTANT: size of your fragment struct */
.instance_size = sizeof(struct sample_fragment_t),
};
Use ``lv_fragment_manager``
~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: c
/* Create fragment instance, and Widgets will be added to container */
lv_fragment_manager_t *manager = lv_fragment_manager_create(container, NULL);
/* Replace current fragment with instance of sample_cls, and init_argument is user defined pointer */
lv_fragment_manager_replace(manager, &sample_cls, init_argument);
Fragment Based Navigation
~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: c
/* Add one instance into manager stack. View object of current fragment will be destroyed,
* but instances created in class constructor will be kept.
*/
lv_fragment_manager_push(manager, &sample_cls, NULL);
/* Remove the top most fragment from the stack, and bring back previous one. */
lv_fragment_manager_pop(manager);
.. _fragment_example:
Example
-------
.. include:: ../../examples/others/fragment/index.rst
.. _fragment_api:
API
---

View File

@@ -0,0 +1,83 @@
.. _gridnav:
===============
Grid navigation
===============
Grid navigation (gridnav for short) is a feature that changes the
currently focused child Widget as arrow keys are pressed.
If the children 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. That makes it useful, for example, to simplify navigation on 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 the gridnav feature to a Widget use
:cpp:expr:`lv_gridnav_add(cont, flags)`.
``flags`` control the behavior of gridnav:
- :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 be focused 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 focused Widget.
- :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 focused Widget.
: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(cont)` Removes gridnav 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 be focusable by gridnav.
.. _gridnav_example:
Example
-------
.. include:: ../../examples/others/gridnav/index.rst
.. _gridnav_api:
API
---

View File

@@ -0,0 +1,127 @@
.. _ime_pinyin:
==========
Pinyin IME
==========
Pinyin IME provides API to provide Chinese Pinyin input method (Chinese
input) for keyboard object, which supports 26 key and 9 key input modes.
You can think of ``lv_ime_pinyin`` as a Pinyin input method plug-in for
keyboard objects.
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``
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 at first,
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 only has more
than ``1,000`` most common CJK radicals, so it is recommended to use
custom fonts 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, you can use the
custom dictionary. Or if you think that the built-in phonetic dictionary
consumes a lot of memory, you can also 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. 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 end with ``{null, null}``, or it will not work
properly.
.. _ime_pinyin_apply_new_dictionary:
Apply 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
-----
The lv_ime_pinyin have 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 ``TEXT`` modes' layout contains buttons to change mode.
To set the mode manually, use :cpp:expr:`lv_ime_pinyin_set_mode(pinyin_ime, mode)`.
The default mode is :cpp:enumerator:`LV_IME_PINYIN_MODE_K26`.
.. _ime_pinyin_example:
Example
-------
.. include:: ../../examples/others/ime/index.rst
.. _ime_pinyin_api:
API
---

View File

@@ -0,0 +1,41 @@
.. _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* use :cpp:expr:`lv_imgfont_create(height, path_cb, user_data)`.
- ``height`` Font size.
- ``path_cb`` A function to get the image path of a character.
Return ``NULL`` if no image should be shown, but the character itself.
- ``user_data`` Pointer to user data.
To use the *imgfont* in a label, reference it:
:cpp:expr:`lv_obj_set_style_text_font(label, imgfont, LV_PART_MAIN)`
To destroy the *imgfont* that is no longer used, use :cpp:expr:`lv_imgfont_destroy(imgfont)`.
.. _lv_imgfont_example:
Example
-------
.. include:: ../../examples/others/imgfont/index.rst
.. _lv_imgfont_api:
API
---

View File

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

View File

@@ -0,0 +1,48 @@
.. _monkey:
======
Monkey
======
A simple monkey test. Use random input to stress test the application.
.. _monkey_usage:
Usage
-----
Enable :c:macro:`LV_USE_MONKEY` in ``lv_conf.h``.
First configure monkey, use :c:struct:`lv_monkey_config_t` to define the
configuration structure, set the ``type`` (check :ref:`Input Devices <indev>`
for the supported types), and then set the
range of ``period_range`` and ``input_range``, the monkey will output
random operations at random times within this range. Call
:cpp:func:`lv_monkey_create` to create monkey. 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 the monkey, call
:cpp:expr:`lv_monkey_delete(monkey)`.
Note that ``input_range`` has different meanings in different ``type``:
- :cpp:enumerator:`LV_INDEV_TYPE_POINTER`: No effect, click randomly within the pixels of the screen resolution.
- :cpp:enumerator:`LV_INDEV_TYPE_ENCODER`: The minimum and maximum values of ``enc_diff``.
- :cpp:enumerator:`LV_INDEV_TYPE_BUTTON`: The minimum and maximum values of ``btn_id``.
Use :cpp:func:`lv_monkey_get_indev` to get the input device, and use
:cpp:func:`lv_indev_set_button_points` to map the key ID to the coordinates.
- :cpp:enumerator:`LV_INDEV_TYPE_KEYPAD`: No effect, Send random :ref:`indev_keys`.
.. _monkey_example:
Example
-------
.. include:: ../../examples/others/monkey/index.rst
.. _monkey_api:
API
---

View File

@@ -0,0 +1,66 @@
.. _obj_id:
=========
Widget ID
=========
LVGL provides an optional field in :cpp:type:`lv_obj_t` to store the Widget ID.
Widget ID can be used in many cases, for example, to identify the Widget.
Or we can store a program backtrace to where the Widget is created.
.. _obj_id_usage:
Usage
-----
Enable this feature by setting :c:macro:`LV_USE_OBJ_ID` to `1` in ``lv_conf.h``.
Enable :c:macro:`LV_OBJ_ID_AUTO_ASSIGN` to automatically assign an ID to Widget when it's created.
It's done by calling function :cpp:func:`lv_obj_assign_id` from :cpp:func:`lv_obj_constructor`.
You can either use your own ID generator by defining the function :cpp:func:`lv_obj_assign_id` or you can utilize the built-in one.
To use the builtin ID generator, set :c:macro:`LV_USE_OBJ_ID_BUILTIN` to `1`.
You can directly access the ID by :cpp:expr:`lv_obj_get_id(widget)` or use API :cpp:expr:`lv_obj_stringify_id(widget, buf, len)`
to get a string representation of the ID.
Use custom ID generator
~~~~~~~~~~~~~~~~~~~~~~~
Set :c:macro:`LV_USE_OBJ_ID_BUILTIN` to `0` in ``lv_conf.h``.
Below APIs needed to be implemented and linked to lvgl.
.. code-block:: c
void lv_obj_set_id(lv_obj_t * widget, void * id);
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);
const char * lv_obj_stringify_id(lv_obj_t * widget, char * buf, uint32_t len);
int lv_obj_id_compare(void * id1, void * id2);
:cpp:func:`lv_obj_assign_id` is called when a Widget is created. The Widget final class is passed from
parameter ``class_p``. Note it may be different than :cpp:expr:`obj->class_p` which is the class
currently being constructed.
:cpp:func:`lv_obj_free_id` is called when Widget is deconstructed. Free any resource allocated in :cpp:func:`lv_obj_assign_id`.
:cpp:func:`lv_obj_stringify_id` converts id to a string representation. The string is stored in ``buf``.
Dump Widget Tree
~~~~~~~~~~~~~~~~
Use API ``lv_obj_dump_tree(lv_obj_t * widget, int depth)`` to dump the Widget Tree.
It will walk through all children and print the Widget ID together with Widget address.
This is useful to debug UI crash. From log we can rebuilt UI the moment before crash.
For example, if the obj is stored to a :cpp:expr:`timer->user_data`, but obj is deleted when timer expired.
Timer callback will crash because of accessing wild pointer.
From the dump log we can clearly see that the obj does not exist.
Find child by ID
~~~~~~~~~~~~~~~~
Use API ``lv_obj_t * lv_obj_get_child_by_id(const lv_obj_t * widget, void * id)`` to find a child by ID.
It will walk through all children and return the first child with the given ID.

View File

@@ -0,0 +1,89 @@
.. _obj_property:
===============
Widget Property
===============
Widgets have many properties that can decide what they look like and how they behave.
For example, the size, position, color, font, etc. are properties of a widget.
Specially, widget local style is also a property of a widget.
.. _obj_property_usage:
Usage
-----
Two APIs are provided to get/set widget properties. It can be enabled by setting
:c:macro:`LV_USE_OBJ_PROPERTY` to `1` in ``lv_conf.h``.
Set :c:macro:`LV_USE_OBJ_PROPERTY_NAME` to `1` in order to use property name instead of ID.
.. code-block:: c
typedef struct {
lv_prop_id_t id;
union {
int32_t num; /**< Number integer number (opacity, enums, booleans or "normal" numbers) */
const void * ptr; /**< Constant pointers (font, cone text, etc) */
lv_color_t color; /**< Colors */
lv_value_precise_t precise; /**< float or int for precise value */
struct {
lv_style_value_t style; /**< Make sure it's the first element in struct. */
uint32_t selector; /**< Style selector, lv_part_t | lv_state_t */
};
};
} lv_property_t;
lv_result_t lv_obj_set_property(lv_obj_t * widget, const lv_property_t * value);
lv_property_t lv_obj_get_property(lv_obj_t * widget, lv_prop_id_t id);
lv_prop_id_t lv_obj_property_get_id(const lv_obj_class_t * clz, const char * name);
.. _obj_property_id:
Property ID
~~~~~~~~~~~
:cpp:type:`lv_prop_id_t` identifies which property to get/set. :cpp:type:`lv_property_t` is an enum value
defined in ``lv_obj_property.h`` that are grouped by widget class. You can add your own
widget property ID following same rule and using helper macro :c:macro:`LV_PROPERTY_ID`.
Do make sure the ID is unique across all widgets.
Property ID is a 32-bit value. The higher 4bits indicates the property value type.
The lower 28bits is the property ID.
Note that :cpp:type:`lv_style_prop_t` is also valid property ID.
.. _obj_property_value:
Property Value
~~~~~~~~~~~~~~
Property value is a union of all possible property types including integer, pointer and color.
``_style`` is kept their just to indicate it's compatible with ``style`` value type.
A Step Further
--------------
The unified widget property set/get API is useful when developing wrapper layer for other
modules like micropython, lua, or for an external animation engine.
For pointer type of property value, which typically points to a specific struct, it still needs
additional code to convert values from dict, table etc to a C struct before setting to widget.
Another possible use case is to ease of creating UI from lots of code. For example, you can gather
all properties to an array now and set properties with a for loop.
.. code-block:: c
lv_property_t props[] = {
{ .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}, },
}
LV_OBJ_SET_PROPERTY_ARRAY(widget, props);

View File

@@ -0,0 +1,342 @@
.. _observer:
========
Observer
========
.. _observer_overview:
Overview
********
The ``lv_observer`` module implements a standard `Observer pattern <https://en.wikipedia.org/wiki/Observer_pattern>`__.
It consists of:
- **subjects**: each containing a value
- **observers**: attached to subjects to be notified on value change
A typical use case looks like this:
.. code-block:: c
//It's a global variable
lv_subject_t my_subject;
/*-------
* main.c
*-------*/
extern lv_subject_t my_subject;
void main(void)
{
//Initialize the 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 the related 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 a subject
lv_subject_add_observer(&some_subject, some_observer_cb, NULL);
}
/*--------------
* some_system.c
*--------------*/
extern lv_subject_t some_subject;
void some_event(void)
{
//Set the subject's value to 30. It will notify `some_observer_cb`
lv_subject_set_int(&some_subject, 30);
}
.. _observer_subject:
Subject
*******
Subject initialization
----------------------
Subjects have to be static or global :cpp:type:`lv_subject_t` type variables.
To initialize a subject use ``lv_subject_init_<type>(&subject, params, init_value)``.
The following initializations exist for types:
- **Integer** ``void lv_subject_init_int(lv_subject_t * subject, int32_t value)``
- **String** ``void lv_subject_init_string(lv_subject_t * subject, char * buf, char * prev_buf, size_t size, const char * value)``
- **Pointer** ``void lv_subject_init_pointer(lv_subject_t * subject, void * value)``
- **Color** ``void lv_subject_init_color(lv_subject_t * subject, lv_color_t color)``
- **Group** ``void lv_subject_init_group(lv_subject_t * subject, lv_subject_t * list[], uint32_t list_len)``
Set subject value
-----------------
The following functions can be used to set a subject's value:
- **Integer** ``void lv_subject_set_int(lv_subject_t * subject, int32_t value)``
- **String** ``void lv_subject_copy_string(lv_subject_t * subject, char * buf)``
- **Pointer** ``void lv_subject_set_pointer(lv_subject_t * subject, void * ptr)``
- **Color** ``void lv_subject_set_color(lv_subject_t * subject, lv_color_t color)``
Get subject's value
-------------------
The following functions can be used to get a subject's value:
- **Integer** ``int32_t lv_subject_get_int(lv_subject_t * subject)``
- **String** ``const char * lv_subject_get_string(lv_subject_t * subject)``
- **Pointer** ``const void * lv_subject_get_pointer(lv_subject_t * subject)``
- **Color** ``lv_color_t lv_subject_get_color(lv_subject_t * subject)``
Get subject's previous value
----------------------------
The following functions can be used to get a subject's previous value:
- **Integer** ``int32_t lv_subject_get_previous_int(lv_subject_t * subject)``
- **String** ``const char * lv_subject_get_previous_string(lv_subject_t * subject)``
- **Pointer** ``const void * lv_subject_get_previous_pointer(lv_subject_t * subject)``
- **Color** ``lv_color_t lv_subject_get_previous_color(lv_subject_t * subject)``
.. _observer_observer:
Observer
********
Subscribe to a subject
----------------------
To subscribe to a subject the following function can be used:
.. code-block:: c
lv_observer_t * observer = lv_subject_add_observer(&some_subject, some_observer_cb, user_data);
Where the observer callback should look like this:
.. code-block:: c
static void some_observer_cb(lv_observer_t * observer, lv_subject_t * subject)
{
...
}
It's also possible to save a target widget when subscribing to a subject.
In this case when widget is deleted, it will automatically unsubscribe from the subject.
In the observer callback :cpp:expr:`lv_observer_get_target(observer)` can be used to get the saved widget.
.. code-block:: c
lv_observer_t * observer = lv_subject_add_observer_obj(&some_subject, some_observer_cb, widget, user_data);
In more generic case any pointer can be saved a target:
.. code-block:: c
lv_observer_t * observer = lv_subject_add_observer_with_target(&some_subject, some_observer_cb, some_pointer, user_data);
Unsubscribe from a subject
--------------------------
.. code-block:: c
/* `observer` is the return value of `lv_subject_add_observer*` */
lv_observer_remove(observer);
To unsubscribe a widget from a given or all subject use:
.. code-block:: c
lv_obj_remove_from_subject(widget, subject); /* `subject` can be NULL to unsubcribe from all */
.. _observer_subject_groups:
Subject groups
**************
There are cases when a subject changes and the value of some other subjects are also required by the observer.
As a practical example imagine an instrument which measures either voltage or current.
To display the measured value on a label 3 things are required:
1. What do we measure (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 parameters changes the label needs to be updated,
and it needs to know all 3 parameters to compose its text.
To handle this you can create an array from some existing subjects and pass
this 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); /*The last parameter is the number of elements */
You can add observers to subject groups in the regular way.
The trick is that when any element of the group is notified the subject group will be notified as well.
The above Voltage/Current measurement example looks like this in the practice:
.. 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; //It will be the subject group
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
**************
Base Widget
-----------
Set a Widget flag if an integer subject's value is equal to a reference value, clear the flag otherwise
.. code-block:: c
observer = lv_obj_bind_flag_if_eq(widget, &subject, LV_OBJ_FLAG_*, ref_value);
Set a Widget flag if an integer subject's value is not equal to a reference value, clear the flag otherwise
.. code-block:: c
observer = lv_obj_bind_flag_if_not_eq(widget, &subject, LV_OBJ_FLAG_*, ref_value);
Set a Widget state if an integer subject's value is equal to a reference value, clear the flag otherwise
.. code-block:: c
observer = lv_obj_bind_state_if_eq(widget, &subject, LV_STATE_*, ref_value);
Set a Widget state if an integer subject's value is not equal to a reference value, clear the flag otherwise
.. code-block:: c
observer = lv_obj_bind_state_if_not_eq(widget, &subject, LV_STATE_*, ref_value);
Set an integer subject to 1 when a Widget is checked and set it 0 when unchecked.
.. code-block:: c
observer = lv_obj_bind_checked(widget, &subject);
Label
-----
Bind an integer, string, or pointer (pointing to a string) subject to a label.
An optional format string can be added with 1 format specifier (e.g. ``"%d °C"``)
If the format string is ``NULL`` the value will be used directly. In this case on string and pointer type subjects can be used.
.. code-block:: c
observer = lv_label_bind_text(widget, &subject, format_string);
Arc
---
Bind an integer subject to an arc's value.
.. code-block:: c
observer = lv_arc_bind_value(widget, &subject);
Slider
------
Bind an integer subject to a slider's value
.. code-block:: c
observer = lv_slider_bind_value(widget, &subject);
Roller
------
Bind an integer subject to a roller's value
.. code-block:: c
observer = lv_roller_bind_value(widget, &subject);
Drop-down
---------
Bind an integer subject to a drop-down's value
.. code-block:: c
observer = lv_dropdown_bind_value(widget, &subject);
.. _observer_example:
Example
*******
.. include:: ../../examples/others/observer/index.rst
.. _observer_api:
API
***

View File

@@ -0,0 +1,77 @@
.. _snapshot:
========
Snapshot
========
Snapshot provides API to take snapshot image for LVGL Widget together
with its children. The image will look exactly like the Widget on display.
.. _snapshot_usage:
Usage
-----
Simply call API :cpp:func:`lv_snapshot_take` to generate the image descriptor
which can be set as image Widget src using :cpp:func:`lv_image_set_src`.
Note, only following color formats are supported for now:
- :cpp:enumerator:`LV_COLOR_FORMAT_RGB565`
- :cpp:enumerator:`LV_COLOR_FORMAT_RGB888`
- :cpp:enumerator:`LV_COLOR_FORMAT_XRGB8888`
- :cpp:enumerator:`LV_COLOR_FORMAT_ARGB8888`
Free the Image
~~~~~~~~~~~~~~
The memory :cpp:func:`lv_snapshot_take` uses are dynamically allocated using
:cpp:func:`lv_draw_buf_create`. Use API :cpp:func:`lv_draw_buf_destroy` to free the memory it
takes. This will firstly free memory the image data takes, then the
image descriptor.
The snapshot image which is the draw buffer returned by :cpp:func:`lv_snapshot_take`
normally won't be added to cache because it can be drawn directly. So you don't need
to invalidate cache by :cpp:func:`lv_image_cache_drop` before destroy the draw buffer.
Below code snippet explains usage of this API.
.. 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);
}
Use Existing Buffer
~~~~~~~~~~~~~~~~~~~
If the snapshot needs update now and then, or simply caller provides memory, use API
``lv_result_t lv_snapshot_take_to_draw_buf(lv_obj_t * widget, lv_color_format_t cf, lv_draw_buf_t * draw_buf);``
for this case. It's caller's responsibility to create and destroy the draw buffer.
If snapshot is generated successfully, the image descriptor is updated
and image data will be stored to provided ``buf``.
Note that snapshot may fail if provided buffer is not enough, which may
happen when Widget size changes. It's recommended to use API
:cpp:func:`lv_snapshot_reshape_draw_buf` to prepare the buffer firstly 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
---