init Files
This commit is contained in:
193
libraries/lvgl/docs/overview/animations.rst
Normal file
193
libraries/lvgl/docs/overview/animations.rst
Normal file
@@ -0,0 +1,193 @@
|
||||
.. _animations:
|
||||
|
||||
==========
|
||||
Animations
|
||||
==========
|
||||
|
||||
You can automatically change the value of a variable between a start and
|
||||
an end value using animations. Animation will happen by periodically
|
||||
calling an "animator" function with the corresponding value parameter.
|
||||
|
||||
The *animator* functions have the following prototype:
|
||||
|
||||
.. code:: c
|
||||
|
||||
void func(void * var, lv_anim_var_t value);
|
||||
|
||||
This prototype is compatible with the majority of the property *set*
|
||||
functions in LVGL. For example :cpp:expr:`lv_obj_set_x(obj, value)` or
|
||||
:cpp:expr:`lv_obj_set_width(obj, value)`
|
||||
|
||||
.. _animations_create:
|
||||
|
||||
Create an animation
|
||||
*******************
|
||||
|
||||
To create an animation an :cpp:type:`lv_anim_t` variable has to be initialized
|
||||
and configured with ``lv_anim_set_...()`` functions.
|
||||
|
||||
.. code:: c
|
||||
|
||||
|
||||
/* INITIALIZE AN ANIMATION
|
||||
*-----------------------*/
|
||||
|
||||
lv_anim_t a;
|
||||
lv_anim_init(&a);
|
||||
|
||||
/* MANDATORY SETTINGS
|
||||
*------------------*/
|
||||
|
||||
/*Set the "animator" function*/
|
||||
lv_anim_set_exec_cb(&a, (lv_anim_exec_xcb_t) lv_obj_set_x);
|
||||
|
||||
/*Set target of the animation*/
|
||||
lv_anim_set_var(&a, obj);
|
||||
|
||||
/*Length of the animation [ms]*/
|
||||
lv_anim_set_duration(&a, duration);
|
||||
|
||||
/*Set start and end values. E.g. 0, 150*/
|
||||
lv_anim_set_values(&a, start, end);
|
||||
|
||||
/* OPTIONAL SETTINGS
|
||||
*------------------*/
|
||||
|
||||
/*Time to wait before starting the animation [ms]*/
|
||||
lv_anim_set_delay(&a, delay);
|
||||
|
||||
/*Set path (curve). Default is linear*/
|
||||
lv_anim_set_path_cb(&a, lv_anim_path_ease_in);
|
||||
|
||||
/*Set a callback to indicate when the animation is completed.*/
|
||||
lv_anim_set_completed_cb(&a, completed_cb);
|
||||
|
||||
/*Set a callback to indicate when the animation is deleted (idle).*/
|
||||
lv_anim_set_deleted_cb(&a, deleted_cb);
|
||||
|
||||
/*Set a callback to indicate when the animation is started (after delay).*/
|
||||
lv_anim_set_start_cb(&a, start_cb);
|
||||
|
||||
/*When ready, play the animation backward with this duration. Default is 0 (disabled) [ms]*/
|
||||
lv_anim_set_playback_duration(&a, time);
|
||||
|
||||
/*Delay before playback. Default is 0 (disabled) [ms]*/
|
||||
lv_anim_set_playback_delay(&a, delay);
|
||||
|
||||
/*Number of repetitions. Default is 1. LV_ANIM_REPEAT_INFINITE for infinite repetition*/
|
||||
lv_anim_set_repeat_count(&a, cnt);
|
||||
|
||||
/*Delay before repeat. Default is 0 (disabled) [ms]*/
|
||||
lv_anim_set_repeat_delay(&a, delay);
|
||||
|
||||
/*true (default): apply the start value immediately, false: apply start value after delay when the anim. really starts. */
|
||||
lv_anim_set_early_apply(&a, true/false);
|
||||
|
||||
/* START THE ANIMATION
|
||||
*------------------*/
|
||||
lv_anim_start(&a); /*Start the animation*/
|
||||
|
||||
You can apply multiple different animations on the same variable at the
|
||||
same time. For example, animate the x and y coordinates with
|
||||
:cpp:func:`lv_obj_set_x` and :cpp:func:`lv_obj_set_y`. However, only one animation can
|
||||
exist with a given variable and function pair and :cpp:func:`lv_anim_start`
|
||||
will remove any existing animations for such a pair.
|
||||
|
||||
.. _animations_path:
|
||||
|
||||
Animation path
|
||||
**************
|
||||
|
||||
You can control the path of an animation. The most simple case is
|
||||
linear, meaning the current value between *start* and *end* is changed
|
||||
with fixed steps. A *path* is a function which calculates the next value
|
||||
to set based on the current state of the animation. Currently, there are
|
||||
the following built-in path functions:
|
||||
|
||||
- :cpp:func:`lv_anim_path_linear`: linear animation
|
||||
- :cpp:func:`lv_anim_path_step`: change in one step at the end
|
||||
- :cpp:func:`lv_anim_path_ease_in`: slow at the beginning
|
||||
- :cpp:func:`lv_anim_path_ease_out`: slow at the end
|
||||
- :cpp:func:`lv_anim_path_ease_in_out`: slow at the beginning and end
|
||||
- :cpp:func:`lv_anim_path_overshoot`: overshoot the end value
|
||||
- :cpp:func:`lv_anim_path_bounce`: bounce back a little from the end value (like
|
||||
hitting a wall)
|
||||
|
||||
.. _animations_speed_vs_time:
|
||||
|
||||
Speed vs time
|
||||
*************
|
||||
|
||||
By default, you set the animation time directly. But in some cases,
|
||||
setting the animation speed is more practical.
|
||||
|
||||
The :cpp:expr:`lv_anim_speed_to_time(speed, start, end)` function calculates the
|
||||
required time in milliseconds to reach the end value from a start value
|
||||
with the given speed. The speed is interpreted in *unit/sec* dimension.
|
||||
For example, :cpp:expr:`lv_anim_speed_to_time(20, 0, 100)` will yield 5000
|
||||
milliseconds. For example, in the case of :cpp:func:`lv_obj_set_x` *unit* is
|
||||
pixels so *20* means *20 px/sec* speed.
|
||||
|
||||
.. _animations_delete:
|
||||
|
||||
Delete animations
|
||||
*****************
|
||||
|
||||
You can delete an animation with :cpp:expr:`lv_anim_delete(var, func)` if you
|
||||
provide the animated variable and its animator function.
|
||||
|
||||
.. _animations_timeline:
|
||||
|
||||
Timeline
|
||||
********
|
||||
|
||||
A timeline is a collection of multiple animations which makes it easy to
|
||||
create complex composite animations.
|
||||
|
||||
Firstly, create an animation element but don't call :cpp:func:`lv_anim_start`.
|
||||
|
||||
Secondly, create an animation timeline object by calling
|
||||
:cpp:func:`lv_anim_timeline_create`.
|
||||
|
||||
Thirdly, add animation elements to the animation timeline by calling
|
||||
:cpp:expr:`lv_anim_timeline_add(at, start_time, &a)`. ``start_time`` is the
|
||||
start time of the animation on the timeline. Note that ``start_time``
|
||||
will override the value of ``delay``.
|
||||
|
||||
Finally, call :cpp:expr:`lv_anim_timeline_start(at)` to start the animation
|
||||
timeline.
|
||||
|
||||
It supports forward and backward playback of the entire animation group,
|
||||
using :cpp:expr:`lv_anim_timeline_set_reverse(at, reverse)`.
|
||||
Note that if you want to play in reverse from the end of the timeline,
|
||||
you need to call :cpp:expr:`lv_anim_timeline_set_progress(at, LV_ANIM_TIMELINE_PROGRESS_MAX)`
|
||||
after adding all animations and before starting to play.
|
||||
|
||||
Call :cpp:expr:`lv_anim_timeline_stop(at)` to stop the animation timeline.
|
||||
|
||||
Call :cpp:expr:`lv_anim_timeline_set_progress(at, progress)` function to set the
|
||||
state of the object corresponding to the progress of the timeline.
|
||||
|
||||
Call :cpp:expr:`lv_anim_timeline_get_playtime(at)` function to get the total
|
||||
duration of the entire animation timeline.
|
||||
|
||||
Call :cpp:expr:`lv_anim_timeline_get_reverse(at)` function to get whether to
|
||||
reverse the animation timeline.
|
||||
|
||||
Call :cpp:expr:`lv_anim_timeline_delete(at)` function to delete the animation timeline.
|
||||
**Note**: If you need to delete an object during animation, be sure to delete the
|
||||
anim timeline before deleting the object. Otherwise, the program may crash or behave abnormally.
|
||||
|
||||
.. image:: /misc/anim-timeline.png
|
||||
|
||||
.. _animations_example:
|
||||
|
||||
Examples
|
||||
********
|
||||
|
||||
.. include:: ../examples/anim/index.rst
|
||||
|
||||
.. _animations_api:
|
||||
|
||||
API
|
||||
***
|
||||
148
libraries/lvgl/docs/overview/color.rst
Normal file
148
libraries/lvgl/docs/overview/color.rst
Normal file
@@ -0,0 +1,148 @@
|
||||
.. _color:
|
||||
|
||||
======
|
||||
Colors
|
||||
======
|
||||
|
||||
The color module handles all color-related functions like changing color
|
||||
depth, creating colors from hex code, converting between color depths,
|
||||
mixing colors, etc.
|
||||
|
||||
The type :cpp:type:`lv_color_t` is used to store a color in RGB888 format.
|
||||
This type and format is used in almost all APIs regardless to :cpp:expr:`LV_COLOR_DEPTH`.
|
||||
|
||||
.. _color_create:
|
||||
|
||||
Creating colors
|
||||
***************
|
||||
|
||||
RGB
|
||||
---
|
||||
|
||||
Create colors from Red, Green and Blue channel values:
|
||||
|
||||
.. code:: c
|
||||
|
||||
/*All channels are 0-255*/
|
||||
lv_color_t c = lv_color_make(red, green, blue);
|
||||
|
||||
|
||||
/*Same but can be used for const initialization too */
|
||||
lv_color_t c = LV_COLOR_MAKE(red, green, blue);
|
||||
|
||||
/*From hex code 0x000000..0xFFFFFF interpreted as RED + GREEN + BLUE*/
|
||||
lv_color_t c = lv_color_hex(0x123456);
|
||||
|
||||
/*From 3 digits. Same as lv_color_hex(0x112233)*/
|
||||
lv_color_t c = lv_color_hex3(0x123);
|
||||
|
||||
HSV
|
||||
---
|
||||
|
||||
Create colors from Hue, Saturation and Value values:
|
||||
|
||||
.. code:: c
|
||||
|
||||
//h = 0..359, s = 0..100, v = 0..100
|
||||
lv_color_t c = lv_color_hsv_to_rgb(h, s, v);
|
||||
|
||||
//All channels are 0-255
|
||||
lv_color_hsv_t c_hsv = lv_color_rgb_to_hsv(r, g, b);
|
||||
|
||||
|
||||
//From lv_color_t variable
|
||||
lv_color_hsv_t c_hsv = lv_color_to_hsv(color);
|
||||
|
||||
.. _color_palette:
|
||||
|
||||
Palette
|
||||
-------
|
||||
|
||||
LVGL includes `Material Design's palette <https://vuetifyjs.com/en/styles/colors/#material-colors>`__ of
|
||||
colors. In this system all named colors have a nominal main color as
|
||||
well as four darker and five lighter variants.
|
||||
|
||||
The names of the colors are as follows:
|
||||
|
||||
- :c:macro:`LV_PALETTE_RED`
|
||||
- :c:macro:`LV_PALETTE_PINK`
|
||||
- :c:macro:`LV_PALETTE_PURPLE`
|
||||
- :c:macro:`LV_PALETTE_DEEP_PURPLE`
|
||||
- :c:macro:`LV_PALETTE_INDIGO`
|
||||
- :c:macro:`LV_PALETTE_BLUE`
|
||||
- :c:macro:`LV_PALETTE_LIGHT_BLUE`
|
||||
- :c:macro:`LV_PALETTE_CYAN`
|
||||
- :c:macro:`LV_PALETTE_TEAL`
|
||||
- :c:macro:`LV_PALETTE_GREEN`
|
||||
- :c:macro:`LV_PALETTE_LIGHT_GREEN`
|
||||
- :c:macro:`LV_PALETTE_LIME`
|
||||
- :c:macro:`LV_PALETTE_YELLOW`
|
||||
- :c:macro:`LV_PALETTE_AMBER`
|
||||
- :c:macro:`LV_PALETTE_ORANGE`
|
||||
- :c:macro:`LV_PALETTE_DEEP_ORANGE`
|
||||
- :c:macro:`LV_PALETTE_BROWN`
|
||||
- :c:macro:`LV_PALETTE_BLUE_GREY`
|
||||
- :c:macro:`LV_PALETTE_GREY`
|
||||
|
||||
To get the main color use
|
||||
``lv_color_t c = lv_palette_main(LV_PALETTE_...)``.
|
||||
|
||||
For the lighter variants of a palette color use
|
||||
``lv_color_t c = lv_palette_lighten(LV_PALETTE_..., v)``. ``v`` can be
|
||||
1..5. For the darker variants of a palette color use
|
||||
``lv_color_t c = lv_palette_darken(LV_PALETTE_..., v)``. ``v`` can be
|
||||
1..4.
|
||||
|
||||
.. _color_modify_and_mix:
|
||||
|
||||
Modify and mix colors
|
||||
---------------------
|
||||
|
||||
The following functions can modify a color:
|
||||
|
||||
.. code:: c
|
||||
|
||||
// Lighten a color. 0: no change, 255: white
|
||||
lv_color_t c = lv_color_lighten(c, lvl);
|
||||
|
||||
// Darken a color. 0: no change, 255: black
|
||||
lv_color_t c = lv_color_darken(lv_color_t c, lv_opa_t lvl);
|
||||
|
||||
// Lighten or darken a color. 0: black, 128: no change 255: white
|
||||
lv_color_t c = lv_color_change_lightness(lv_color_t c, lv_opa_t lvl);
|
||||
|
||||
|
||||
// Mix two colors with a given ratio 0: full c2, 255: full c1, 128: half c1 and half c2
|
||||
lv_color_t c = lv_color_mix(c1, c2, ratio);
|
||||
|
||||
.. _color_builtin:
|
||||
|
||||
Built-in colors
|
||||
---------------
|
||||
|
||||
:cpp:func:`lv_color_white` and :cpp:func:`lv_color_black` return ``0xFFFFFF`` and
|
||||
``0x000000`` respectively.
|
||||
|
||||
.. _color_opacity:
|
||||
|
||||
Opacity
|
||||
*******
|
||||
|
||||
To describe opacity the :cpp:type:`lv_opa_t` type is created from ``uint8_t``.
|
||||
Some special purpose defines are also introduced:
|
||||
|
||||
- :cpp:enumerator:`LV_OPA_TRANSP` Value: 0, means no opacity making the color
|
||||
completely transparent
|
||||
- :cpp:enumerator:`LV_OPA_10` Value: 25, means the color covers only a little
|
||||
- ``LV_OPA_20 ... OPA_80`` follow logically
|
||||
- :cpp:enumerator:`LV_OPA_90` Value: 229, means the color near completely covers
|
||||
- :cpp:enumerator:`LV_OPA_COVER` Value: 255, means the color completely covers (full
|
||||
opacity)
|
||||
|
||||
You can also use the ``LV_OPA_*`` defines in :cpp:func:`lv_color_mix` as a
|
||||
mixing *ratio*.
|
||||
|
||||
.. _color_api:
|
||||
|
||||
API
|
||||
***
|
||||
536
libraries/lvgl/docs/overview/coord.rst
Normal file
536
libraries/lvgl/docs/overview/coord.rst
Normal file
@@ -0,0 +1,536 @@
|
||||
.. _coord:
|
||||
|
||||
=============================
|
||||
Positions, sizes, and layouts
|
||||
=============================
|
||||
|
||||
Overview
|
||||
********
|
||||
|
||||
Similarly to many other parts of LVGL, the concept of setting the
|
||||
coordinates was inspired by CSS. LVGL has by no means a complete
|
||||
implementation of CSS but a comparable subset is implemented (sometimes
|
||||
with minor adjustments).
|
||||
|
||||
In short this means:
|
||||
|
||||
- Explicitly set coordinates are stored in styles (size, position, layouts, etc.)
|
||||
- support min-width, max-width, min-height, max-height
|
||||
- have pixel, percentage, and "content" units
|
||||
- x=0; y=0 coordinate means the top-left corner of the parent plus the left/top padding plus border width
|
||||
- width/height means the full size, the "content area" is smaller with padding and border width
|
||||
- a subset of flexbox and grid layouts are supported
|
||||
|
||||
.. _coord_unites:
|
||||
|
||||
Units
|
||||
-----
|
||||
|
||||
- pixel: Simply a position in pixels. An integer always means pixels.
|
||||
E.g. :cpp:expr:`lv_obj_set_x(btn, 10)`
|
||||
- percentage: The percentage of the size of the object or its parent
|
||||
(depending on the property). :cpp:expr:`lv_pct(value)` converts a value to
|
||||
percentage. E.g. :cpp:expr:`lv_obj_set_width(btn, lv_pct(50))`
|
||||
- :c:macro:`LV_SIZE_CONTENT`: Special value to set the width/height of an
|
||||
object to involve all the children. It's similar to ``auto`` in CSS.
|
||||
E.g. :cpp:expr:`lv_obj_set_width(btn, LV_SIZE_CONTENT)`.
|
||||
|
||||
.. _coord_boxing_model:
|
||||
|
||||
Boxing model
|
||||
------------
|
||||
|
||||
LVGL follows CSS's `border-box <https://developer.mozilla.org/en-US/docs/Web/CSS/box-sizing>`__
|
||||
model. An object's "box" is built from the following parts:
|
||||
|
||||
- bounding box: the width/height of the elements.
|
||||
- border width: the width of the border.
|
||||
- padding: space between the sides of the object and its children.
|
||||
- margin: space outside of the object (considered only by some layouts)
|
||||
- content: the content area which is the size of the bounding box reduced by the border width and padding.
|
||||
|
||||
.. image:: /misc/boxmodel.png
|
||||
:alt: The box models of LVGL: The content area is smaller than the bounding box with the padding and border width
|
||||
|
||||
The border is drawn inside the bounding box. Inside the border LVGL
|
||||
keeps a "padding margin" when placing an object's children.
|
||||
|
||||
The outline is drawn outside the bounding box.
|
||||
|
||||
.. _coord_notes:
|
||||
|
||||
Important notes
|
||||
---------------
|
||||
|
||||
This section describes special cases in which LVGL's behavior might be
|
||||
unexpected.
|
||||
|
||||
.. _coord_postponed_coordinate_calculation:
|
||||
|
||||
Postponed coordinate calculation
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
LVGL doesn't recalculate all the coordinate changes immediately. This is
|
||||
done to improve performance. Instead, the objects are marked as "dirty"
|
||||
and before redrawing the screen LVGL checks if there are any "dirty"
|
||||
objects. If so it refreshes their position, size and layout.
|
||||
|
||||
In other words, if you need to get the coordinate of an object and the
|
||||
coordinates were just changed, LVGL needs to be forced to recalculate
|
||||
the coordinates. To do this call :cpp:func:`lv_obj_update_layout`.
|
||||
|
||||
The size and position might depend on the parent or layout. Therefore
|
||||
:cpp:func:`lv_obj_update_layout` recalculates the coordinates of all objects on
|
||||
the screen of ``obj``.
|
||||
|
||||
.. _coord_removing styles:
|
||||
|
||||
Removing styles
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
As it's described in the :ref:`coord_using_styles` section,
|
||||
coordinates can also be set via style properties. To be more precise,
|
||||
under the hood every style coordinate related property is stored as a
|
||||
style property. If you use :cpp:expr:`lv_obj_set_x(obj, 20)` LVGL saves ``x=20``
|
||||
in the local style of the object.
|
||||
|
||||
This is an internal mechanism and doesn't matter much as you use LVGL.
|
||||
However, there is one case in which you need to be aware of the
|
||||
implementation. If the style(s) of an object are removed by
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_obj_remove_style_all(obj)
|
||||
|
||||
or
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_obj_remove_style(obj, NULL, LV_PART_MAIN);
|
||||
|
||||
the earlier set coordinates will be removed as well.
|
||||
|
||||
For example:
|
||||
|
||||
.. code:: c
|
||||
|
||||
/*The size of obj1 will be set back to the default in the end*/
|
||||
lv_obj_set_size(obj1, 200, 100); /*Now obj1 has 200;100 size*/
|
||||
lv_obj_remove_style_all(obj1); /*It removes the set sizes*/
|
||||
|
||||
|
||||
/*obj2 will have 200;100 size in the end */
|
||||
lv_obj_remove_style_all(obj2);
|
||||
lv_obj_set_size(obj2, 200, 100);
|
||||
|
||||
.. _coord_position:
|
||||
|
||||
Position
|
||||
********
|
||||
|
||||
Simple way
|
||||
----------
|
||||
|
||||
To simply set the x and y coordinates of an object use:
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_obj_set_x(obj, 10); //Separate...
|
||||
lv_obj_set_y(obj, 20);
|
||||
lv_obj_set_pos(obj, 10, 20); //Or in one function
|
||||
|
||||
By default, the x and y coordinates are measured from the top left
|
||||
corner of the parent's content area. For example if the parent has five
|
||||
pixels of padding on every side the above code will place ``obj`` at
|
||||
(15, 25) because the content area starts after the padding.
|
||||
|
||||
Percentage values are calculated from the parent's content area size.
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_obj_set_x(btn, lv_pct(10)); //x = 10 % of parent content area width
|
||||
|
||||
Align
|
||||
-----
|
||||
|
||||
In some cases it's convenient to change the origin of the positioning
|
||||
from the default top left. If the origin is changed e.g. to
|
||||
bottom-right, the (0,0) position means: align to the bottom-right
|
||||
corner. To change the origin use:
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_obj_set_align(obj, align);
|
||||
|
||||
To change the alignment and set new coordinates:
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_obj_align(obj, align, x, y);
|
||||
|
||||
The following alignment options can be used:
|
||||
|
||||
- :cpp:enumerator:`LV_ALIGN_TOP_LEFT`
|
||||
- :cpp:enumerator:`LV_ALIGN_TOP_MID`
|
||||
- :cpp:enumerator:`LV_ALIGN_TOP_RIGHT`
|
||||
- :cpp:enumerator:`LV_ALIGN_BOTTOM_LEFT`
|
||||
- :cpp:enumerator:`LV_ALIGN_BOTTOM_MID`
|
||||
- :cpp:enumerator:`LV_ALIGN_BOTTOM_RIGHT`
|
||||
- :cpp:enumerator:`LV_ALIGN_LEFT_MID`
|
||||
- :cpp:enumerator:`LV_ALIGN_RIGHT_MID`
|
||||
- :cpp:enumerator:`LV_ALIGN_CENTER`
|
||||
|
||||
It's quite common to align a child to the center of its parent,
|
||||
therefore a dedicated function exists:
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_obj_center(obj);
|
||||
|
||||
//Has the same effect
|
||||
lv_obj_align(obj, LV_ALIGN_CENTER, 0, 0);
|
||||
|
||||
If the parent's size changes, the set alignment and position of the
|
||||
children is updated automatically.
|
||||
|
||||
The functions introduced above align the object to its parent. However,
|
||||
it's also possible to align an object to an arbitrary reference object.
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_obj_align_to(obj_to_align, reference_obj, align, x, y);
|
||||
|
||||
Besides the alignments options above, the following can be used to align
|
||||
an object outside the reference object:
|
||||
|
||||
- :cpp:enumerator:`LV_ALIGN_OUT_TOP_LEFT`
|
||||
- :cpp:enumerator:`LV_ALIGN_OUT_TOP_MID`
|
||||
- :cpp:enumerator:`LV_ALIGN_OUT_TOP_RIGHT`
|
||||
- :cpp:enumerator:`LV_ALIGN_OUT_BOTTOM_LEFT`
|
||||
- :cpp:enumerator:`LV_ALIGN_OUT_BOTTOM_MID`
|
||||
- :cpp:enumerator:`LV_ALIGN_OUT_BOTTOM_RIGHT`
|
||||
- :cpp:enumerator:`LV_ALIGN_OUT_LEFT_TOP`
|
||||
- :cpp:enumerator:`LV_ALIGN_OUT_LEFT_MID`
|
||||
- :cpp:enumerator:`LV_ALIGN_OUT_LEFT_BOTTOM`
|
||||
- :cpp:enumerator:`LV_ALIGN_OUT_RIGHT_TOP`
|
||||
- :cpp:enumerator:`LV_ALIGN_OUT_RIGHT_MID`
|
||||
- :cpp:enumerator:`LV_ALIGN_OUT_RIGHT_BOTTOM`
|
||||
|
||||
For example to align a label above a button and center the label
|
||||
horizontally:
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_obj_align_to(label, btn, LV_ALIGN_OUT_TOP_MID, 0, -10);
|
||||
|
||||
Note that, unlike with :cpp:func:`lv_obj_align`, :cpp:func:`lv_obj_align_to` cannot
|
||||
realign the object if its coordinates or the reference object's
|
||||
coordinates change.
|
||||
|
||||
.. _coord_size:
|
||||
|
||||
Size
|
||||
****
|
||||
|
||||
Sizing the Simple way
|
||||
---------------------
|
||||
|
||||
The width and the height of an object can be set easily as well:
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_obj_set_width(obj, 200); //Separate...
|
||||
lv_obj_set_height(obj, 100);
|
||||
lv_obj_set_size(obj, 200, 100); //Or in one function
|
||||
|
||||
Percentage values are calculated based on the parent's content area
|
||||
size. For example to set the object's height to the screen height:
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_obj_set_height(obj, lv_pct(100));
|
||||
|
||||
The size settings support a special value: :c:macro:`LV_SIZE_CONTENT`. It means
|
||||
the object's size in the respective direction will be set to the size of
|
||||
its children. Note that only children on the right and bottom sides will
|
||||
be considered and children on the top and left remain cropped. This
|
||||
limitation makes the behavior more predictable.
|
||||
|
||||
Objects with :cpp:enumerator:`LV_OBJ_FLAG_HIDDEN` or :cpp:enumerator:`LV_OBJ_FLAG_FLOATING` will be
|
||||
ignored by the :c:macro:`LV_SIZE_CONTENT` calculation.
|
||||
|
||||
The above functions set the size of an object's bounding box but the
|
||||
size of the content area can be set as well. This means an object's
|
||||
bounding box will be enlarged with the addition of padding.
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_obj_set_content_width(obj, 50); //The actual width: padding left + 50 + padding right
|
||||
lv_obj_set_content_height(obj, 30); //The actual width: padding top + 30 + padding bottom
|
||||
|
||||
The size of the bounding box and the content area can be retrieved with
|
||||
the following functions:
|
||||
|
||||
.. code:: c
|
||||
|
||||
int32_t w = lv_obj_get_width(obj);
|
||||
int32_t h = lv_obj_get_height(obj);
|
||||
int32_t content_w = lv_obj_get_content_width(obj);
|
||||
int32_t content_h = lv_obj_get_content_height(obj);
|
||||
|
||||
.. _coord_using_styles:
|
||||
|
||||
Using styles
|
||||
************
|
||||
|
||||
Under the hood the position, size and alignment properties are style
|
||||
properties. The above described "simple functions" hide the style
|
||||
related code for the sake of simplicity and set the position, size, and
|
||||
alignment properties in the local styles of the object.
|
||||
|
||||
However, using styles to set the coordinates has some great advantages:
|
||||
|
||||
- It makes it easy to set the width/height/etc. for several objects
|
||||
together. E.g. make all the sliders 100x10 pixels sized.
|
||||
- It also makes possible to modify the values in one place.
|
||||
- The values can be partially overwritten by other styles. For example
|
||||
``style_btn`` makes the object ``100x50`` by default but adding
|
||||
``style_full_width`` overwrites only the width of the object.
|
||||
- The object can have different position or size depending on state.
|
||||
E.g. 100 px wide in :cpp:enumerator:`LV_STATE_DEFAULT` but 120 px
|
||||
in :cpp:enumerator:`LV_STATE_PRESSED`.
|
||||
- Style transitions can be used to make the coordinate changes smooth.
|
||||
|
||||
Here are some examples to set an object's size using a style:
|
||||
|
||||
.. code:: c
|
||||
|
||||
static lv_style_t style;
|
||||
lv_style_init(&style);
|
||||
lv_style_set_width(&style, 100);
|
||||
|
||||
lv_obj_t * btn = lv_button_create(lv_screen_active());
|
||||
lv_obj_add_style(btn, &style, LV_PART_MAIN);
|
||||
|
||||
As you will see below there are some other great features of size and
|
||||
position setting. However, to keep the LVGL API lean, only the most
|
||||
common coordinate setting features have a "simple" version and the more
|
||||
complex features can be used via styles.
|
||||
|
||||
.. _coord_translation:
|
||||
|
||||
Translation
|
||||
***********
|
||||
|
||||
Let's say the there are 3 buttons next to each other. Their position is
|
||||
set as described above. Now you want to move a button up a little when
|
||||
it's pressed.
|
||||
|
||||
One way to achieve this is by setting a new Y coordinate for the pressed
|
||||
state:
|
||||
|
||||
.. code:: c
|
||||
|
||||
static lv_style_t style_normal;
|
||||
lv_style_init(&style_normal);
|
||||
lv_style_set_y(&style_normal, 100);
|
||||
|
||||
static lv_style_t style_pressed;
|
||||
lv_style_init(&style_pressed);
|
||||
lv_style_set_y(&style_pressed, 80);
|
||||
|
||||
lv_obj_add_style(btn1, &style_normal, LV_STATE_DEFAULT);
|
||||
lv_obj_add_style(btn1, &style_pressed, LV_STATE_PRESSED);
|
||||
|
||||
lv_obj_add_style(btn2, &style_normal, LV_STATE_DEFAULT);
|
||||
lv_obj_add_style(btn2, &style_pressed, LV_STATE_PRESSED);
|
||||
|
||||
lv_obj_add_style(btn3, &style_normal, LV_STATE_DEFAULT);
|
||||
lv_obj_add_style(btn3, &style_pressed, LV_STATE_PRESSED);
|
||||
|
||||
This works, but it's not really flexible because the pressed coordinate
|
||||
is hard-coded. If the buttons are not at y=100, ``style_pressed`` won't
|
||||
work as expected. Translations can be used to solve this:
|
||||
|
||||
.. code:: c
|
||||
|
||||
static lv_style_t style_normal;
|
||||
lv_style_init(&style_normal);
|
||||
lv_style_set_y(&style_normal, 100);
|
||||
|
||||
static lv_style_t style_pressed;
|
||||
lv_style_init(&style_pressed);
|
||||
lv_style_set_translate_y(&style_pressed, -20);
|
||||
|
||||
lv_obj_add_style(btn1, &style_normal, LV_STATE_DEFAULT);
|
||||
lv_obj_add_style(btn1, &style_pressed, LV_STATE_PRESSED);
|
||||
|
||||
lv_obj_add_style(btn2, &style_normal, LV_STATE_DEFAULT);
|
||||
lv_obj_add_style(btn2, &style_pressed, LV_STATE_PRESSED);
|
||||
|
||||
lv_obj_add_style(btn3, &style_normal, LV_STATE_DEFAULT);
|
||||
lv_obj_add_style(btn3, &style_pressed, LV_STATE_PRESSED);
|
||||
|
||||
Translation is applied from the current position of the object.
|
||||
|
||||
Percentage values can be used in translations as well. The percentage is
|
||||
relative to the size of the object (and not to the size of the parent).
|
||||
For example :cpp:expr:`lv_pct(50)` will move the object with half of its
|
||||
width/height.
|
||||
|
||||
The translation is applied after the layouts are calculated. Therefore,
|
||||
even laid out objects' position can be translated.
|
||||
|
||||
The translation actually moves the object. That means it makes the
|
||||
scrollbars and :c:macro:`LV_SIZE_CONTENT` sized objects react to the position
|
||||
change.
|
||||
|
||||
.. _coord_transformation:
|
||||
|
||||
Transformation
|
||||
**************
|
||||
|
||||
Similarly to position, an object's size can be changed relative to the
|
||||
current size as well. The transformed width and height are added on both
|
||||
sides of the object. This means a 10 px transformed width makes the
|
||||
object 2x10 pixels wider.
|
||||
|
||||
Unlike position translation, the size transformation doesn't make the
|
||||
object "really" larger. In other words scrollbars, layouts, and
|
||||
:c:macro:`LV_SIZE_CONTENT` will not react to the transformed size. Hence, size
|
||||
transformation is "only" a visual effect.
|
||||
|
||||
This code enlarges a button when it's pressed:
|
||||
|
||||
.. code:: c
|
||||
|
||||
static lv_style_t style_pressed;
|
||||
lv_style_init(&style_pressed);
|
||||
lv_style_set_transform_width(&style_pressed, 10);
|
||||
lv_style_set_transform_height(&style_pressed, 10);
|
||||
|
||||
lv_obj_add_style(btn, &style_pressed, LV_STATE_PRESSED);
|
||||
|
||||
.. _coord_min_max_size:
|
||||
|
||||
Min and Max size
|
||||
----------------
|
||||
|
||||
Similarly to CSS, LVGL also supports ``min-width``, ``max-width``,
|
||||
``min-height`` and ``max-height``. These are limits preventing an
|
||||
object's size from becoming smaller/larger than these values. They are
|
||||
especially useful if the size is set by percentage or
|
||||
:c:macro:`LV_SIZE_CONTENT`.
|
||||
|
||||
.. code:: c
|
||||
|
||||
static lv_style_t style_max_height;
|
||||
lv_style_init(&style_max_height);
|
||||
lv_style_set_y(&style_max_height, 200);
|
||||
|
||||
lv_obj_set_height(obj, lv_pct(100));
|
||||
lv_obj_add_style(obj, &style_max_height, LV_STATE_DEFAULT); //Limit the height to 200 px
|
||||
|
||||
Percentage values can be used as well which are relative to the size of
|
||||
the parent's content area.
|
||||
|
||||
.. code:: c
|
||||
|
||||
static lv_style_t style_max_height;
|
||||
lv_style_init(&style_max_height);
|
||||
lv_style_set_y(&style_max_height, lv_pct(50));
|
||||
|
||||
lv_obj_set_height(obj, lv_pct(100));
|
||||
lv_obj_add_style(obj, &style_max_height, LV_STATE_DEFAULT); //Limit the height to half parent height
|
||||
|
||||
.. _coord_layout:
|
||||
|
||||
Layout
|
||||
******
|
||||
|
||||
Layout Overview
|
||||
---------------
|
||||
|
||||
Layouts can update the position and size of an object's children. They
|
||||
can be used to automatically arrange the children into a line or column,
|
||||
or in much more complicated forms.
|
||||
|
||||
The position and size set by the layout overwrites the "normal" x, y,
|
||||
width, and height settings.
|
||||
|
||||
There is only one function that is the same for every layout:
|
||||
:cpp:func:`lv_obj_set_layout` ``(obj, <LAYOUT_NAME>)`` sets the layout on an object.
|
||||
For further settings of the parent and children see the documentation of
|
||||
the given layout.
|
||||
|
||||
Built-in layout
|
||||
---------------
|
||||
|
||||
LVGL comes with two very powerful layouts:
|
||||
|
||||
* Flexbox: arrange objects into rows or columns, with support for wrapping and expanding items.
|
||||
* Grid: arrange objects into fixed positions in 2D table.
|
||||
|
||||
Both are heavily inspired by the CSS layouts with the same name.
|
||||
Layouts are described in detail in their own section of documentation.
|
||||
|
||||
Flags
|
||||
-----
|
||||
|
||||
There are some flags that can be used on objects to affect how they
|
||||
behave with layouts:
|
||||
|
||||
- :cpp:enumerator:`LV_OBJ_FLAG_HIDDEN` Hidden objects are ignored in layout calculations.
|
||||
- :cpp:enumerator:`LV_OBJ_FLAG_IGNORE_LAYOUT` The object is simply ignored by the layouts. Its coordinates can be set as usual.
|
||||
- :cpp:enumerator:`LV_OBJ_FLAG_FLOATING` Same as :cpp:enumerator:`LV_OBJ_FLAG_IGNORE_LAYOUT` but the object with :cpp:enumerator:`LV_OBJ_FLAG_FLOATING` will be ignored in :c:macro:`LV_SIZE_CONTENT` calculations.
|
||||
|
||||
These flags can be added/removed with :cpp:expr:`lv_obj_add_flag(obj, FLAG)` and :cpp:expr:`lv_obj_remove_flag(obj, FLAG)`
|
||||
|
||||
Adding new layouts
|
||||
------------------
|
||||
|
||||
LVGL can be freely extended by a custom layout like this:
|
||||
|
||||
.. code:: c
|
||||
|
||||
uint32_t MY_LAYOUT;
|
||||
|
||||
...
|
||||
|
||||
MY_LAYOUT = lv_layout_register(my_layout_update, &user_data);
|
||||
|
||||
...
|
||||
|
||||
void my_layout_update(lv_obj_t * obj, void * user_data)
|
||||
{
|
||||
/*Will be called automatically if it's required to reposition/resize the children of "obj" */
|
||||
}
|
||||
|
||||
Custom style properties can be added which can be retrieved and used in
|
||||
the update callback. For example:
|
||||
|
||||
.. code:: c
|
||||
|
||||
uint32_t MY_PROP;
|
||||
...
|
||||
|
||||
LV_STYLE_MY_PROP = lv_style_register_prop();
|
||||
|
||||
...
|
||||
static inline void lv_style_set_my_prop(lv_style_t * style, uint32_t value)
|
||||
{
|
||||
lv_style_value_t v = {
|
||||
.num = (int32_t)value
|
||||
};
|
||||
lv_style_set_prop(style, LV_STYLE_MY_PROP, v);
|
||||
}
|
||||
|
||||
.. _coord_example:
|
||||
|
||||
Examples
|
||||
********
|
||||
|
||||
.. _coord_api:
|
||||
|
||||
API
|
||||
***
|
||||
175
libraries/lvgl/docs/overview/display.rst
Normal file
175
libraries/lvgl/docs/overview/display.rst
Normal file
@@ -0,0 +1,175 @@
|
||||
.. _display:
|
||||
|
||||
========
|
||||
Displays
|
||||
========
|
||||
|
||||
:important: The basic concept of a *display* in LVGL is explained in the :ref:`porting` section. So before reading further, please read that section first.
|
||||
|
||||
.. _display_multi_display_support:
|
||||
|
||||
Multiple display support
|
||||
************************
|
||||
|
||||
In LVGL you can have multiple displays, each with their own driver,
|
||||
widgets and color depth.
|
||||
|
||||
Creating more displays is easy: just use :cpp:func:`lv_display_create` and
|
||||
add set the buffer and the ``flush_cb``. When you create the UI, use
|
||||
:cpp:expr:`lv_display_set_default(disp)` to tell the library on which display to
|
||||
create objects.
|
||||
|
||||
Why would you want multi-display support? Here are some examples:
|
||||
|
||||
- Have a "normal" TFT display with local UI and create "virtual" screens on VNC
|
||||
on demand. (You need to add your VNC driver).
|
||||
- Have a large TFT display and a small monochrome display.
|
||||
- Have some smaller and simple displays in a large instrument or technology.
|
||||
- Have two large TFT displays: one for a customer and one for the shop assistant.
|
||||
|
||||
.. _display_one_display:
|
||||
|
||||
Using only one display
|
||||
----------------------
|
||||
|
||||
Using more displays can be useful but in most cases it's not required.
|
||||
Therefore, the whole concept of multi-display handling is completely
|
||||
hidden if you register only one display. By default, the last created
|
||||
(and only) display is used.
|
||||
|
||||
:cpp:func:`lv_screen_active`, :cpp:func:`lv_screen_load`, :cpp:func:`lv_layer_top`,
|
||||
:cpp:func:`lv_layer_sys`, :c:macro:`LV_HOR_RES` and :c:macro:`LV_VER_RES` are always applied
|
||||
on the most recently created (default) display. If you pass ``NULL`` as
|
||||
``disp`` parameter to display related functions the default display will
|
||||
usually be used. E.g. :cpp:expr:`lv_display_trigger_activity(NULL)` will trigger a
|
||||
user activity on the default display. (See below in :ref:`Inactivity <display_inactivity>`).
|
||||
|
||||
Mirror display
|
||||
--------------
|
||||
|
||||
To mirror the image of a display to another display, you don't need to
|
||||
use multi-display support. Just transfer the buffer received in
|
||||
``flush_cb`` to the other display too.
|
||||
|
||||
Split image
|
||||
-----------
|
||||
|
||||
You can create a larger virtual display from an array of smaller ones.
|
||||
You can create it as below: 1. Set the resolution of the displays to the
|
||||
large display's resolution. 2. In ``flush_cb``, truncate and modify the
|
||||
``area`` parameter for each display. 3. Send the buffer's content to
|
||||
each real display with the truncated area.
|
||||
|
||||
.. _display_screens:
|
||||
|
||||
Screens
|
||||
*******
|
||||
|
||||
Every display has its own set of :ref:`screens <objects_screens>` and the
|
||||
objects on each screen.
|
||||
|
||||
Be sure not to confuse displays and screens:
|
||||
|
||||
- **Displays** are the physical hardware drawing the pixels.
|
||||
- **Screens** are the high-level root objects associated with a
|
||||
particular display. One display can have multiple screens associated
|
||||
with it, but not vice versa.
|
||||
|
||||
Screens can be considered the highest level containers which have no
|
||||
parent. A screen's size is always equal to its display and their origin
|
||||
is (0;0). Therefore, a screen's coordinates can't be changed,
|
||||
i.e. :cpp:func:`lv_obj_set_pos`, :cpp:func:`lv_obj_set_size` or similar functions
|
||||
can't be used on screens.
|
||||
|
||||
A screen can be created from any object type but the two most typical
|
||||
types are :ref:`Base object <lv_obj>` and :ref:`Image <lv_image>`
|
||||
(to create a wallpaper).
|
||||
|
||||
To create a screen, use
|
||||
:cpp:expr:`lv_obj_t * scr = lv_<type>_create(NULL)`. ``NULL`` indicates no parent.
|
||||
|
||||
To load a screen, use :cpp:expr:`lv_screen_load(scr)`. To get the active screen,
|
||||
use :cpp:expr:`lv_screen_active()`. These functions work on the default display. If
|
||||
you want to specify which display to work on, use
|
||||
:cpp:expr:`lv_display_get_screen_active(disp)` and :cpp:expr:`lv_display_load_screen(disp, scr)`. A
|
||||
screen can be loaded with animations too. Read more
|
||||
:ref:`here <objects_load_screens>`.
|
||||
|
||||
Screens can be deleted with :cpp:expr:`lv_obj_delete(scr)`, but ensure that you do
|
||||
not delete the currently loaded screen.
|
||||
|
||||
Transparent screens
|
||||
-------------------
|
||||
|
||||
Usually, the opacity of the screen is :cpp:enumerator:`LV_OPA_COVER` to provide a
|
||||
solid background for its children. If this is not the case (opacity <
|
||||
100%) the display's ``bottom_layer`` be visible. If the bottom layer's
|
||||
opacity is also not :cpp:enumerator:`LV_OPA_COVER` LVGL has no solid background to
|
||||
draw.
|
||||
|
||||
This configuration (transparent screen and display) could be used to
|
||||
create for example OSD menus where a video is played on a lower layer,
|
||||
and a menu is overlaid on an upper layer.
|
||||
|
||||
To properly render the screen the display's color format needs to be set
|
||||
to one with alpha channel.
|
||||
|
||||
In summary, to enable transparent screens and displays for OSD menu-like
|
||||
UIs:
|
||||
|
||||
- Set the screen's ``bg_opa`` to transparent:
|
||||
:cpp:expr:`lv_obj_set_style_bg_opa(lv_screen_active(), LV_OPA_TRANSP, LV_PART_MAIN)`
|
||||
- Set the bottom layer's ``bg_opa`` to transparent:
|
||||
:cpp:expr:`lv_obj_set_style_bg_opa(lv_layer_bottom(), LV_OPA_TRANSP, LV_PART_MAIN)`
|
||||
- Set the screen's ``bg_opa`` to 0:
|
||||
:cpp:expr:`lv_obj_set_style_bg_opa(lv_screen_active(), LV_OPA_0, LV_PART_MAIN)`
|
||||
- Set a color format with alpha channel. E.g.
|
||||
:cpp:expr:`lv_display_set_color_format(disp, LV_COLOR_FORMAT_ARGB8888)`
|
||||
|
||||
.. _display_features:
|
||||
|
||||
Features of displays
|
||||
********************
|
||||
|
||||
.. _display_inactivity:
|
||||
|
||||
Inactivity
|
||||
----------
|
||||
|
||||
A user's inactivity time is measured on each display. Every use of an
|
||||
:ref:`Input device <indev>` (if :ref:`associated with the display <porting_indev_other_features>`) counts as an activity. To
|
||||
get time elapsed since the last activity, use
|
||||
:cpp:expr:`lv_display_get_inactive_time(disp)`. If ``NULL`` is passed, the lowest
|
||||
inactivity time among all displays will be returned (**NULL isn't just
|
||||
the default display**).
|
||||
|
||||
You can manually trigger an activity using
|
||||
:cpp:expr:`lv_display_trigger_activity(disp)`. If ``disp`` is ``NULL``, the default
|
||||
screen will be used (**and not all displays**).
|
||||
|
||||
Background
|
||||
----------
|
||||
|
||||
Every display has a background color, background image and background
|
||||
opacity properties. They become visible when the current screen is
|
||||
transparent or not positioned to cover the whole display.
|
||||
|
||||
The background color is a simple color to fill the display. It can be
|
||||
adjusted with :cpp:expr:`lv_obj_set_style_bg_color(obj, color)`;
|
||||
|
||||
The display background image is a path to a file or a pointer to an
|
||||
:cpp:struct:`lv_image_dsc_t` variable (converted image data) to be used as
|
||||
wallpaper. It can be set with :cpp:expr:`lv_obj_set_style_bg_image_src(obj, &my_img)`;
|
||||
If a background image is configured the background won't be filled with
|
||||
``bg_color``.
|
||||
|
||||
The opacity of the background color or image can be adjusted with
|
||||
:cpp:expr:`lv_obj_set_style_bg_opa(obj, opa)`.
|
||||
|
||||
The ``disp`` parameter of these functions can be ``NULL`` to select the
|
||||
default display.
|
||||
|
||||
.. _display_api:
|
||||
|
||||
API
|
||||
***
|
||||
260
libraries/lvgl/docs/overview/event.rst
Normal file
260
libraries/lvgl/docs/overview/event.rst
Normal file
@@ -0,0 +1,260 @@
|
||||
.. _events:
|
||||
|
||||
======
|
||||
Events
|
||||
======
|
||||
|
||||
Events are triggered in LVGL when something happens which might be
|
||||
interesting to the user, e.g. when an object:
|
||||
|
||||
- is clicked
|
||||
- is scrolled
|
||||
- has its value changed
|
||||
- is redrawn, etc.
|
||||
|
||||
Besides widgets, events can registered from displays and input devices too.
|
||||
|
||||
Add events to a widget
|
||||
**********************
|
||||
|
||||
The user can assign callback functions to an object to see its events.
|
||||
In practice, it looks like this:
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_obj_t * btn = lv_button_create(lv_screen_active());
|
||||
lv_obj_add_event_cb(btn, my_event_cb, LV_EVENT_CLICKED, NULL); /*Assign an event callback*/
|
||||
|
||||
...
|
||||
|
||||
static void my_event_cb(lv_event_t * event)
|
||||
{
|
||||
printf("Clicked\n");
|
||||
}
|
||||
|
||||
In the example :cpp:enumerator:`LV_EVENT_CLICKED` means that only the click event will
|
||||
call ``my_event_cb``. See the :ref:`list of event codes <events_codes>` for
|
||||
all the options. :cpp:enumerator:`LV_EVENT_ALL` can be used to receive all events.
|
||||
|
||||
The last parameter of :cpp:func:`lv_obj_add_event` is a pointer to any custom
|
||||
data that will be available in the event. It will be described later in
|
||||
more detail.
|
||||
|
||||
More events can be added to an object, like this:
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_obj_add_event_cb(obj, my_event_cb_1, LV_EVENT_CLICKED, NULL);
|
||||
lv_obj_add_event_cb(obj, my_event_cb_2, LV_EVENT_PRESSED, NULL);
|
||||
lv_obj_add_event_cb(obj, my_event_cb_3, LV_EVENT_ALL, NULL); /*No filtering, receive all events*/
|
||||
|
||||
Even the same event callback can be used on an object with different
|
||||
``user_data``. For example:
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_obj_add_event_cb(obj, increment_on_click, LV_EVENT_CLICKED, &num1);
|
||||
lv_obj_add_event_cb(obj, increment_on_click, LV_EVENT_CLICKED, &num2);
|
||||
|
||||
The events will be called in the order as they were added.
|
||||
|
||||
Other objects can use the same *event callback*.
|
||||
|
||||
In the very same way events can attached to the input devices and displays like this
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_display_add_event_cb(disp, event_cb, LV_EVENT_RESOLUTION_CHANGED, NULL);
|
||||
lv_indev_add_event_cb(indev, event_cb, LV_EVENT_CLICKED, NULL);
|
||||
|
||||
|
||||
Remove event(s) from widgets
|
||||
****************************
|
||||
|
||||
.. code:: c
|
||||
|
||||
uint32_t i;
|
||||
uint32_t event_cnt = lv_obj_get_event_count(obj);
|
||||
for(i = 0; i < event_cnt; i++) {
|
||||
lv_event_dsc_t * event_dsc = lv_obj_get_event_dsc(obj, i);
|
||||
if(lv_event_dsc_get_cb(event_dsc) == some_event_cb) {
|
||||
lv_obj_remove_event(obj, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
.. _events_codes:
|
||||
|
||||
Event codes
|
||||
***********
|
||||
|
||||
The event codes can be grouped into these categories: - Input device
|
||||
events - Drawing events - Other events - Special events - Custom events
|
||||
|
||||
All objects (such as Buttons/Labels/Sliders etc.) regardless their type
|
||||
receive the *Input device*, *Drawing* and *Other* events.
|
||||
|
||||
However, the *Special events* are specific to a particular widget type.
|
||||
See the :ref:`widgets' documentation <widgets>` to learn when they
|
||||
are sent,
|
||||
|
||||
*Custom events* are added by the user and are never sent by LVGL.
|
||||
|
||||
The following event codes exist:
|
||||
|
||||
|
||||
Input device events
|
||||
-------------------
|
||||
|
||||
- :cpp:enumerator:`LV_EVENT_PRESSED`: The object has been pressed
|
||||
- :cpp:enumerator:`LV_EVENT_PRESSING`: The object is being pressed (called continuously while pressing)
|
||||
- :cpp:enumerator:`LV_EVENT_PRESS_LOST`: The object is still being pressed but slid cursor/finger off of the object
|
||||
- :cpp:enumerator:`LV_EVENT_SHORT_CLICKED`: The object was pressed for a short period of time, then released it. Not called if scrolled.
|
||||
- :cpp:enumerator:`LV_EVENT_LONG_PRESSED`: Object has been pressed for at least `long_press_time`. Not called if scrolled.
|
||||
- :cpp:enumerator:`LV_EVENT_LONG_PRESSED_REPEAT`: Called after `long_press_time` in every `long_press_repeat_time` ms. Not called if scrolled.
|
||||
- :cpp:enumerator:`LV_EVENT_CLICKED`: Called on release if not scrolled (regardless to long press)
|
||||
- :cpp:enumerator:`LV_EVENT_RELEASED`: Called in every cases when the object has been released
|
||||
- :cpp:enumerator:`LV_EVENT_SCROLL_BEGIN`: Scrolling begins. The event parameter is a pointer to the animation of the scroll. Can be modified
|
||||
- :cpp:enumerator:`LV_EVENT_SCROLL_THROW_BEGIN`:
|
||||
- :cpp:enumerator:`LV_EVENT_SCROLL_END`: Scrolling ends
|
||||
- :cpp:enumerator:`LV_EVENT_SCROLL`: Scrolling
|
||||
- :cpp:enumerator:`LV_EVENT_GESTURE`: A gesture is detected. Get the gesture with :cpp:expr:`lv_indev_get_gesture_dir(lv_indev_active());`
|
||||
- :cpp:enumerator:`LV_EVENT_KEY`: A key is sent to the object. Get the key with :cpp:expr:`lv_indev_get_key(lv_indev_active());`
|
||||
- :cpp:enumerator:`LV_EVENT_FOCUSED`: The object is focused
|
||||
- :cpp:enumerator:`LV_EVENT_DEFOCUSED`: The object is defocused
|
||||
- :cpp:enumerator:`LV_EVENT_LEAVE`: The object is defocused but still selected
|
||||
- :cpp:enumerator:`LV_EVENT_HIT_TEST`: Perform advanced hit-testing
|
||||
- :cpp:enumerator:`LV_EVENT_INDEV_RESET`: Indev has been reset
|
||||
- :cpp:enumerator:`LV_EVENT_HOVER_OVER`: Indev hover over object
|
||||
- :cpp:enumerator:`LV_EVENT_HOVER_LEAVE`: Indev hover leave object
|
||||
|
||||
Drawing events
|
||||
--------------
|
||||
|
||||
- :cpp:enumerator:`LV_EVENT_COVER_CHECK`: Check if the object fully covers an area. The event parameter is :cpp:type:`lv_cover_check_info_t *`.
|
||||
- :cpp:enumerator:`LV_EVENT_REFR_EXT_DRAW_SIZE`: Get the required extra draw area around the object (e.g. for shadow). The event parameter is :cpp:type:`int32_t *` to store the size.
|
||||
- :cpp:enumerator:`LV_EVENT_DRAW_MAIN_BEGIN`: Starting the main drawing phase
|
||||
- :cpp:enumerator:`LV_EVENT_DRAW_MAIN`: Perform the main drawing
|
||||
- :cpp:enumerator:`LV_EVENT_DRAW_MAIN_END`: Finishing the main drawing phase
|
||||
- :cpp:enumerator:`LV_EVENT_DRAW_POST_BEGIN`: Starting the post draw phase (when all children are drawn)
|
||||
- :cpp:enumerator:`LV_EVENT_DRAW_POST`: Perform the post draw phase (when all children are drawn)
|
||||
- :cpp:enumerator:`LV_EVENT_DRAW_POST_END`: Finishing the post draw phase (when all children are drawn)
|
||||
- :cpp:enumerator:`LV_EVENT_DRAW_TASK_ADDED`: Adding a draw task
|
||||
|
||||
Special events
|
||||
--------------
|
||||
|
||||
- :cpp:enumerator:`LV_EVENT_VALUE_CHANGED`: The object's value has changed (i.e. slider moved)
|
||||
- :cpp:enumerator:`LV_EVENT_INSERT`: A text is inserted to the object. The event data is `char *` being inserted.
|
||||
- :cpp:enumerator:`LV_EVENT_REFRESH`: Notify the object to refresh something on it (for the user)
|
||||
- :cpp:enumerator:`LV_EVENT_READY`: A process has finished
|
||||
- :cpp:enumerator:`LV_EVENT_CANCEL`: A process has been cancelled
|
||||
|
||||
Other events
|
||||
------------
|
||||
|
||||
- :cpp:enumerator:`LV_EVENT_CREATE`: Object is being created
|
||||
- :cpp:enumerator:`LV_EVENT_DELETE`: Object is being deleted
|
||||
- :cpp:enumerator:`LV_EVENT_CHILD_CHANGED`: Child was removed, added, or its size, position were changed
|
||||
- :cpp:enumerator:`LV_EVENT_CHILD_CREATED`: Child was created, always bubbles up to all parents
|
||||
- :cpp:enumerator:`LV_EVENT_CHILD_DELETED`: Child was deleted, always bubbles up to all parents
|
||||
- :cpp:enumerator:`LV_EVENT_SCREEN_UNLOAD_START`: A screen unload started, fired immediately when scr_load is called
|
||||
- :cpp:enumerator:`LV_EVENT_SCREEN_LOAD_START`: A screen load started, fired when the screen change delay is expired
|
||||
- :cpp:enumerator:`LV_EVENT_SCREEN_LOADED`: A screen was loaded
|
||||
- :cpp:enumerator:`LV_EVENT_SCREEN_UNLOADED`: A screen was unloaded
|
||||
- :cpp:enumerator:`LV_EVENT_SIZE_CHANGED`: Object coordinates/size have changed
|
||||
- :cpp:enumerator:`LV_EVENT_STYLE_CHANGED`: Object's style has changed
|
||||
- :cpp:enumerator:`LV_EVENT_LAYOUT_CHANGED`: The children position has changed due to a layout recalculation
|
||||
- :cpp:enumerator:`LV_EVENT_GET_SELF_SIZE`: Get the internal size of a widget
|
||||
|
||||
Display events
|
||||
--------------
|
||||
|
||||
- :cpp:enumerator:`LV_EVENT_INVALIDATE_AREA`
|
||||
- :cpp:enumerator:`LV_EVENT_RESOLUTION_CHANGED`
|
||||
- :cpp:enumerator:`LV_EVENT_COLOR_FORMAT_CHANGED`
|
||||
- :cpp:enumerator:`LV_EVENT_REFR_REQUEST`
|
||||
- :cpp:enumerator:`LV_EVENT_REFR_START`
|
||||
- :cpp:enumerator:`LV_EVENT_REFR_READY`
|
||||
- :cpp:enumerator:`LV_EVENT_RENDER_START`
|
||||
- :cpp:enumerator:`LV_EVENT_RENDER_READY`
|
||||
- :cpp:enumerator:`LV_EVENT_FLUSH_START`
|
||||
- :cpp:enumerator:`LV_EVENT_FLUSH_FINISH`
|
||||
|
||||
|
||||
Custom events
|
||||
-------------
|
||||
|
||||
Any custom event codes can be registered by
|
||||
``uint32_t MY_EVENT_1 =`` :cpp:func:`lv_event_register_id`
|
||||
|
||||
They can be sent to any object with
|
||||
:cpp:expr:`lv_event_send(obj, MY_EVENT_1, &some_data)`
|
||||
|
||||
Sending events
|
||||
**************
|
||||
|
||||
To manually send events to an object, use
|
||||
:cpp:expr:`lv_obj_send_event(obj, <EVENT_CODE>, &some_data)`.
|
||||
|
||||
For example, this can be used to manually close a message box by
|
||||
simulating a button press (although there are simpler ways to do this):
|
||||
|
||||
.. code:: c
|
||||
|
||||
/*Simulate the press of the first button (indexes start from zero)*/
|
||||
uint32_t btn_id = 0;
|
||||
lv_event_send(mbox, LV_EVENT_VALUE_CHANGED, &btn_id);
|
||||
|
||||
The same works for display and input devices with
|
||||
:cpp:expr:`lv_display_send_event(obj, <EVENT_CODE>, &some_data)` and
|
||||
:cpp:expr:`lv_indev_send_event(obj, <EVENT_CODE>, &some_data)`.
|
||||
|
||||
Refresh event
|
||||
-------------
|
||||
|
||||
:cpp:enumerator:`LV_EVENT_REFRESH` is a special event because it's designed to let the
|
||||
user notify an object to refresh itself. Some examples:
|
||||
|
||||
- notify a label to refresh its text according to one or more variables (e.g. current time)
|
||||
- refresh a label when the language changes
|
||||
- enable a button if some conditions are met (e.g. the correct PIN is entered)
|
||||
- add/remove styles to/from an object if a limit is exceeded, etc
|
||||
|
||||
Fields of lv_event_t
|
||||
********************
|
||||
|
||||
:cpp:type:`lv_event_t` is the only parameter passed to the event callback and it
|
||||
contains all data about the event. The following values can be gotten from it:
|
||||
|
||||
- :cpp:expr:`lv_event_get_code(e)`: get the event code
|
||||
- :cpp:expr:`lv_event_get_current_target(e)`: get the object to which an event was sent. I.e. the object whose event handler is being called.
|
||||
- :cpp:expr:`lv_event_get_target(e)`: get the object that originally triggered the event (different from :cpp:func:`lv_event_get_target` if :ref:`event bubbling <events_bubbling>` is enabled)
|
||||
- :cpp:expr:`lv_event_get_user_data(e)`: get the pointer passed as the last parameter of :cpp:func:`lv_obj_add_event`.
|
||||
- :cpp:expr:`lv_event_get_param(e)`: get the parameter passed as the last parameter of :cpp:func:`lv_event_send`
|
||||
|
||||
.. _events_bubbling:
|
||||
|
||||
Event bubbling
|
||||
**************
|
||||
|
||||
If :cpp:expr:`lv_obj_add_flag(obj, LV_OBJ_FLAG_EVENT_BUBBLE)` is enabled all
|
||||
events will be sent to an object's parent too. If the parent also has
|
||||
:cpp:enumerator:`LV_OBJ_FLAG_EVENT_BUBBLE` enabled the event will be sent to its
|
||||
parent and so on.
|
||||
|
||||
The *target* parameter of the event is always the current target object,
|
||||
not the original object. To get the original target call
|
||||
:cpp:expr:`lv_event_get_target_obj(e)` in the event handler.
|
||||
|
||||
.. _events_examples:
|
||||
|
||||
Examples
|
||||
********
|
||||
|
||||
.. include:: ../examples/event/index.rst
|
||||
|
||||
.. _events_api:
|
||||
|
||||
API
|
||||
***
|
||||
505
libraries/lvgl/docs/overview/font.rst
Normal file
505
libraries/lvgl/docs/overview/font.rst
Normal file
@@ -0,0 +1,505 @@
|
||||
.. _fonts:
|
||||
|
||||
=====
|
||||
Fonts
|
||||
=====
|
||||
|
||||
In LVGL fonts are collections of bitmaps and other information required
|
||||
to render images of individual letters (glyph). A font is stored in a
|
||||
:cpp:type:`lv_font_t` variable and can be set in a style's *text_font* field.
|
||||
For example:
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_style_set_text_font(&my_style, &lv_font_montserrat_28); /*Set a larger font*/
|
||||
|
||||
Fonts have a **format** property. It describes how the glyph draw data is stored.
|
||||
It has *2* categories: `Legacy simple format` and `Advanced format`.
|
||||
In the legacy simple format, the font is stored in a simple array of bitmaps.
|
||||
In the advanced format, the font is stored in a different way like `Vector`, `SVG`, etc.
|
||||
|
||||
In the legacy simple format, the value stored for a pixel determines the pixel's opacity.
|
||||
This way, with higher *bpp (bit per pixel)*, the edges of the letter can be smoother.
|
||||
The possible *bpp* values are 1, 2, 4 and 8 (higher values mean better quality).
|
||||
|
||||
The *format* property also affects the amount of memory needed to store a
|
||||
font. For example, *format = LV_FONT_GLYPH_FORMAT_A4* makes a font nearly four times larger
|
||||
compared to *format = LV_FONT_GLYPH_FORMAT_A1*.
|
||||
|
||||
Unicode support
|
||||
***************
|
||||
|
||||
LVGL supports **UTF-8** encoded Unicode characters. Your editor needs to
|
||||
be configured to save your code/text as UTF-8 (usually this the default)
|
||||
and be sure that, :c:macro:`LV_TXT_ENC` is set to :c:macro:`LV_TXT_ENC_UTF8` in
|
||||
*lv_conf.h*. (This is the default value)
|
||||
|
||||
To test it try
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_obj_t * label1 = lv_label_create(lv_screen_active(), NULL);
|
||||
lv_label_set_text(label1, LV_SYMBOL_OK);
|
||||
|
||||
If all works well, a ✓ character should be displayed.
|
||||
|
||||
Built-in fonts
|
||||
**************
|
||||
|
||||
There are several built-in fonts in different sizes, which can be
|
||||
enabled in ``lv_conf.h`` with *LV_FONT\_…* defines.
|
||||
|
||||
Normal fonts
|
||||
------------
|
||||
|
||||
Containing all the ASCII characters, the degree symbol (U+00B0), the
|
||||
bullet symbol (U+2022) and the built-in symbols (see below).
|
||||
|
||||
- :c:macro:`LV_FONT_MONTSERRAT_12`: 12 px font
|
||||
- :c:macro:`LV_FONT_MONTSERRAT_14`: 14 px font
|
||||
- :c:macro:`LV_FONT_MONTSERRAT_16`: 16 px font
|
||||
- :c:macro:`LV_FONT_MONTSERRAT_18`: 18 px font
|
||||
- :c:macro:`LV_FONT_MONTSERRAT_20`: 20 px font
|
||||
- :c:macro:`LV_FONT_MONTSERRAT_22`: 22 px font
|
||||
- :c:macro:`LV_FONT_MONTSERRAT_24`: 24 px font
|
||||
- :c:macro:`LV_FONT_MONTSERRAT_26`: 26 px font
|
||||
- :c:macro:`LV_FONT_MONTSERRAT_28`: 28 px font
|
||||
- :c:macro:`LV_FONT_MONTSERRAT_30`: 30 px font
|
||||
- :c:macro:`LV_FONT_MONTSERRAT_32`: 32 px font
|
||||
- :c:macro:`LV_FONT_MONTSERRAT_34`: 34 px font
|
||||
- :c:macro:`LV_FONT_MONTSERRAT_36`: 36 px font
|
||||
- :c:macro:`LV_FONT_MONTSERRAT_38`: 38 px font
|
||||
- :c:macro:`LV_FONT_MONTSERRAT_40`: 40 px font
|
||||
- :c:macro:`LV_FONT_MONTSERRAT_42`: 42 px font
|
||||
- :c:macro:`LV_FONT_MONTSERRAT_44`: 44 px font
|
||||
- :c:macro:`LV_FONT_MONTSERRAT_46`: 46 px font
|
||||
- :c:macro:`LV_FONT_MONTSERRAT_48`: 48 px font
|
||||
|
||||
Special fonts
|
||||
-------------
|
||||
|
||||
- :c:macro:`LV_FONT_MONTSERRAT_28_COMPRESSED`: Same as normal 28 px font but stored as a :ref:`fonts_compressed` with 3 bpp
|
||||
- :c:macro:`LV_FONT_DEJAVU_16_PERSIAN_HEBREW`: 16 px font with normal range + Hebrew, Arabic, Persian letters and all their forms
|
||||
- :c:macro:`LV_FONT_SIMSUN_16_CJK`: 16 px font with normal range plus 1000 of the most common CJK radicals
|
||||
- :c:macro:`LV_FONT_UNSCII_8`: 8 px pixel perfect font with only ASCII characters
|
||||
- :c:macro:`LV_FONT_UNSCII_16`: 16 px pixel perfect font with only ASCII characters
|
||||
|
||||
The built-in fonts are **global variables** with names like
|
||||
:cpp:var:`lv_font_montserrat_16` for a 16 px height font. To use them in a
|
||||
style, just add a pointer to a font variable like shown above.
|
||||
|
||||
The built-in fonts with *bpp = 4* contain the ASCII characters and use
|
||||
the `Montserrat <https://fonts.google.com/specimen/Montserrat>`__ font.
|
||||
|
||||
In addition to the ASCII range, the following symbols are also added to
|
||||
the built-in fonts from the `FontAwesome <https://fontawesome.com/>`__
|
||||
font.
|
||||
|
||||
.. _fonts_symbols:
|
||||
|
||||
.. image:: /misc/symbols.png
|
||||
|
||||
The symbols can be used singly as:
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_label_set_text(my_label, LV_SYMBOL_OK);
|
||||
|
||||
Or together with strings (compile time string concatenation):
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_label_set_text(my_label, LV_SYMBOL_OK "Apply");
|
||||
|
||||
Or more symbols together:
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_label_set_text(my_label, LV_SYMBOL_OK LV_SYMBOL_WIFI LV_SYMBOL_PLAY);
|
||||
|
||||
Special features
|
||||
****************
|
||||
|
||||
Bidirectional support
|
||||
---------------------
|
||||
|
||||
Most languages use a Left-to-Right (LTR for short) writing direction,
|
||||
however some languages (such as Hebrew, Persian or Arabic) use
|
||||
Right-to-Left (RTL for short) direction.
|
||||
|
||||
LVGL not only supports RTL texts but supports mixed (a.k.a.
|
||||
bidirectional, BiDi) text rendering too. Some examples:
|
||||
|
||||
.. image:: /misc/bidi.png
|
||||
|
||||
BiDi support is enabled by :c:macro:`LV_USE_BIDI` in *lv_conf.h*
|
||||
|
||||
All texts have a base direction (LTR or RTL) which determines some
|
||||
rendering rules and the default alignment of the text (Left or Right).
|
||||
However, in LVGL, the base direction is not only applied to labels. It's
|
||||
a general property which can be set for every object. If not set then it
|
||||
will be inherited from the parent. This means it's enough to set the
|
||||
base direction of a screen and every object will inherit it.
|
||||
|
||||
The default base direction for screens can be set by
|
||||
:c:macro:`LV_BIDI_BASE_DIR_DEF` in *lv_conf.h* and other objects inherit the
|
||||
base direction from their parent.
|
||||
|
||||
To set an object's base direction use :cpp:expr:`lv_obj_set_style_base_dir(obj, base_dir, selector)`.
|
||||
The possible base directions are:
|
||||
|
||||
- :cpp:enumerator:`LV_BASE_DIR_LTR`: Left to Right base direction
|
||||
- :cpp:enumerator:`LV_BASE_DIR_RTL`: Right to Left base direction
|
||||
- :cpp:enumerator:`LV_BASE_DIR_AUTO`: Auto detect base direction
|
||||
|
||||
This list summarizes the effect of RTL base direction on objects:
|
||||
|
||||
- Create objects by default on the right
|
||||
- ``lv_tabview``: Displays tabs from right to left
|
||||
- ``lv_checkbox``: Shows the box on the right
|
||||
- ``lv_buttonmatrix``: Shows buttons from right to left
|
||||
- ``lv_list``: Shows icons on the right
|
||||
- ``lv_dropdown``: Aligns options to the right
|
||||
- The texts in ``lv_table``, ``lv_buttonmatrix``, ``lv_keyboard``, ``lv_tabview``, ``lv_dropdown``, ``lv_roller`` are "BiDi processed" to be displayed correctly
|
||||
|
||||
Arabic and Persian support
|
||||
--------------------------
|
||||
|
||||
There are some special rules to display Arabic and Persian characters:
|
||||
the *form* of a character depends on its position in the text. A
|
||||
different form of the same letter needs to be used when it is isolated,
|
||||
at start, middle or end positions. Besides these, some conjunction rules
|
||||
should also be taken into account.
|
||||
|
||||
LVGL supports these rules if :c:macro:`LV_USE_ARABIC_PERSIAN_CHARS` is enabled.
|
||||
|
||||
However, there are some limitations:
|
||||
|
||||
- Only displaying text is supported (e.g. on labels), text inputs (e.g. text area) don't support this feature.
|
||||
- Static text (i.e. const) is not processed. E.g. texts set by :cpp:func:`lv_label_set_text` will be "Arabic processed" but :cpp:func:`lv_label_set_text_static` won't.
|
||||
- Text get functions (e.g. :cpp:func:`lv_label_get_text`) will return the processed text.
|
||||
|
||||
Subpixel rendering
|
||||
------------------
|
||||
|
||||
Subpixel rendering allows for tripling the horizontal resolution by
|
||||
rendering anti-aliased edges on Red, Green and Blue channels instead of
|
||||
at pixel level granularity. This takes advantage of the position of
|
||||
physical color channels of each pixel, resulting in higher quality
|
||||
letter anti-aliasing. Learn more
|
||||
`here <https://en.wikipedia.org/wiki/Subpixel_rendering>`__.
|
||||
|
||||
For subpixel rendering, the fonts need to be generated with special
|
||||
settings:
|
||||
|
||||
- In the online converter tick the ``Subpixel`` box
|
||||
- In the command line tool use ``--lcd`` flag. Note that the generated font needs about three times more memory.
|
||||
|
||||
Subpixel rendering works only if the color channels of the pixels have a
|
||||
horizontal layout. That is the R, G, B channels are next to each other
|
||||
and not above each other. The order of color channels also needs to
|
||||
match with the library settings. By default, LVGL assumes ``RGB`` order,
|
||||
however this can be swapped by setting :c:macro:`LV_SUBPX_BGR` ``1`` in
|
||||
*lv_conf.h*.
|
||||
|
||||
.. _fonts_compressed:
|
||||
|
||||
Compressed fonts
|
||||
----------------
|
||||
|
||||
The bitmaps of fonts can be compressed by
|
||||
|
||||
- ticking the ``Compressed`` check box in the online converter
|
||||
- not passing the ``--no-compress`` flag to the offline converter (compression is applied by default)
|
||||
|
||||
Compression is more effective with larger fonts and higher bpp. However,
|
||||
it's about 30% slower to render compressed fonts. Therefore, it's
|
||||
recommended to compress only the largest fonts of a user interface,
|
||||
because
|
||||
|
||||
- they need the most memory
|
||||
- they can be compressed better
|
||||
- and probably they are used less frequently then the medium-sized fonts, so the performance cost is smaller.
|
||||
|
||||
Kerning
|
||||
-------
|
||||
|
||||
Fonts may provide kerning information to adjust the spacing between specific
|
||||
characters.
|
||||
|
||||
- The online converter generates kerning tables.
|
||||
- The offline converter generates kerning tables unless ``--no-kerning`` is
|
||||
specified.
|
||||
- FreeType integration does not currently support kerning.
|
||||
- The Tiny TTF font engine supports GPOS and Kern tables.
|
||||
|
||||
To configure kerning at runtime, use :cpp:func:`lv_font_set_kerning`.
|
||||
|
||||
.. _add_font:
|
||||
|
||||
Add a new font
|
||||
**************
|
||||
|
||||
There are several ways to add a new font to your project:
|
||||
|
||||
1. The simplest method is to use the `Online font converter <https://lvgl.io/tools/fontconverter>`__.
|
||||
Just set the parameters, click the *Convert* button, copy the font to your project
|
||||
and use it. **Be sure to carefully read the steps provided on that site
|
||||
or you will get an error while converting.**
|
||||
2. Use the `Offline font converter <https://github.com/lvgl/lv_font_conv>`__.
|
||||
(Requires Node.js to be installed)
|
||||
3. If you want to create something like the built-in
|
||||
fonts (Montserrat font and symbols) but in a different size and/or
|
||||
ranges, you can use the ``built_in_font_gen.py`` script in
|
||||
``lvgl/scripts/built_in_font`` folder. (This requires Python and
|
||||
``lv_font_conv`` to be installed)
|
||||
|
||||
To declare a font in a file, use :cpp:expr:`LV_FONT_DECLARE(my_font_name)`.
|
||||
|
||||
To make fonts globally available (like the built-in fonts), add them to
|
||||
:c:macro:`LV_FONT_CUSTOM_DECLARE` in *lv_conf.h*.
|
||||
|
||||
Add new symbols
|
||||
***************
|
||||
|
||||
The built-in symbols are created from the `FontAwesome <https://fontawesome.com/>`__ font.
|
||||
|
||||
1. Search for a symbol on https://fontawesome.com. For example the
|
||||
`USB symbol <https://fontawesome.com/icons/usb?style=brands>`__. Copy its
|
||||
Unicode ID which is ``0xf287`` in this case.
|
||||
2. Open the `Online font converter <https://lvgl.io/tools/fontconverter>`__.
|
||||
Add `FontAwesome.woff <https://lvgl.io/assets/others/FontAwesome5-Solid+Brands+Regular.woff>`__.
|
||||
3. Set the parameters such as Name, Size, BPP. You'll use this name to
|
||||
declare and use the font in your code.
|
||||
4. Add the Unicode ID of the symbol to the range field. E.g.\ ``0xf287``
|
||||
for the USB symbol. More symbols can be enumerated with ``,``.
|
||||
5. Convert the font and copy the generated source code to your project.
|
||||
Make sure to compile the .c file of your font.
|
||||
6. Declare the font using ``extern lv_font_t my_font_name;`` or simply
|
||||
use :cpp:expr:`LV_FONT_DECLARE(my_font_name)`.
|
||||
|
||||
**Using the symbol**
|
||||
|
||||
1. Convert the Unicode value to UTF8, for example on
|
||||
`this site <http://www.ltg.ed.ac.uk/~richard/utf-8.cgi?input=f287&mode=hex>`__.
|
||||
For ``0xf287`` the *Hex UTF-8 bytes* are ``EF 8A 87``.
|
||||
2. Create a ``define`` string from the UTF8 values: ``#define MY_USB_SYMBOL "\xEF\x8A\x87"``
|
||||
3. Create a label and set the text. Eg. :cpp:expr:`lv_label_set_text(label, MY_USB_SYMBOL)`
|
||||
|
||||
:note: :cpp:expr:`lv_label_set_text(label, MY_USB_SYMBOL)` searches for this
|
||||
symbol in the font defined in ``style.text.font`` properties. To use the
|
||||
symbol you may need to change it. Eg ``style.text.font = my_font_name``
|
||||
|
||||
Load a font at run-time
|
||||
***********************
|
||||
|
||||
:cpp:func:`lv_binfont_create` can be used to load a font from a file. The font needs
|
||||
to have a special binary format. (Not TTF or WOFF). Use
|
||||
`lv_font_conv <https://github.com/lvgl/lv_font_conv/>`__ with the
|
||||
``--format bin`` option to generate an LVGL compatible font file.
|
||||
|
||||
:note: To load a font :ref:`LVGL's filesystem <overview_file_system>`
|
||||
needs to be enabled and a driver must be added.
|
||||
|
||||
Example
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_font_t *my_font = lv_binfont_create("X:/path/to/my_font.bin");
|
||||
if(my_font == NULL) return;
|
||||
|
||||
/*Use the font*/
|
||||
|
||||
/*Free the font if not required anymore*/
|
||||
lv_binfont_destroy(my_font);
|
||||
|
||||
Load a font from a memory buffer at run-time
|
||||
******************************************
|
||||
|
||||
:cpp:func:`lv_binfont_create_from_buffer` can be used to load a font from a memory buffer.
|
||||
This function may be useful to load a font from an external file system, which is not
|
||||
supported by LVGL. The font needs to be in the same format as if it were loaded from a file.
|
||||
|
||||
:note: To load a font from a buffer :ref:`LVGL's filesystem <overview_file_system>`
|
||||
needs to be enabled and the MEMFS driver must be added.
|
||||
|
||||
Example
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_font_t *my_font;
|
||||
uint8_t *buf;
|
||||
uint32_t bufsize;
|
||||
|
||||
/*Read font file into the buffer from the external file system*/
|
||||
...
|
||||
|
||||
/*Load font from the buffer*/
|
||||
my_font = lv_binfont_create_from_buffer((void *)buf, buf));
|
||||
if(my_font == NULL) return;
|
||||
/*Use the font*/
|
||||
|
||||
/*Free the font if not required anymore*/
|
||||
lv_binfont_destroy(my_font);
|
||||
|
||||
Use a BDF font
|
||||
**************
|
||||
|
||||
Small displays with low resolution don't look pretty with automatically rendered fonts. A bitmap font provides
|
||||
the solution, but it's necessary to convert the bitmap font (BDF) to a TTF.
|
||||
|
||||
Convert BDF to TTF
|
||||
------------------
|
||||
|
||||
BDF are bitmap fonts where fonts are not described in outlines but in pixels. BDF files can be used but
|
||||
they must be converted into the TTF format via mkttf. This tool uses potrace to generate outlines from
|
||||
the bitmap information. The bitmap itself will be embedded into the TTF as well. `lv_font_conv <https://github.com/lvgl/lv_font_conv/>`__ uses
|
||||
the embedded bitmap but it also needs the outlines. One could think you can use a fake MS Bitmap
|
||||
only sfnt (ttf) (TTF without outlines) created by fontforge but this will not work.
|
||||
|
||||
Install imagemagick, python3, python3-fontforge and potrace
|
||||
|
||||
On Ubuntu Systems, just type
|
||||
|
||||
.. code:: bash
|
||||
|
||||
sudo apt install imagemagick python3-fontforge potrace
|
||||
|
||||
Clone mkttf
|
||||
|
||||
.. code:: bash
|
||||
|
||||
git clone https://github.com/Tblue/mkttf
|
||||
|
||||
Read the mkttf docs.
|
||||
|
||||
Former versions of imagemagick needs the imagemagick call in front of convert, identify and so on.
|
||||
But newer versions don't. So you might probably change 2 lines in potrace-wrapper.sh.
|
||||
Open potrace-wrapper.sh and remove imagemagick from line 55 and line 64.
|
||||
|
||||
line 55
|
||||
|
||||
.. code:: bash
|
||||
|
||||
wh=($(identify -format '%[width]pt %[height]pt' "${input?}"))
|
||||
|
||||
line 64
|
||||
|
||||
.. code:: bash
|
||||
|
||||
convert "${input?}" -sample '1000%' - \
|
||||
|
||||
It might be necessary to change the mkttf.py script.
|
||||
|
||||
line 1
|
||||
|
||||
.. code:: bash
|
||||
|
||||
#!/usr/bin/env python3
|
||||
|
||||
Example for a 12px font
|
||||
-----------------------
|
||||
|
||||
.. code:: bash
|
||||
|
||||
cd mkttf
|
||||
./mkttf.py ./TerminusMedium-12-12.bdf
|
||||
Importing bitmaps from 0 additional fonts...
|
||||
Importing font `./TerminusMedium-12-12.bdf' into glyph background...
|
||||
Processing glyphs...
|
||||
Saving TTF file...
|
||||
Saving SFD file...
|
||||
Done!
|
||||
|
||||
The TTF TerminusMedium-001.000.ttf has been created from ./TerminusMedium-12-12.bdf.
|
||||
|
||||
Create font for lvgl
|
||||
|
||||
.. code:: bash
|
||||
|
||||
lv_font_conv --bpp 1 --size 12 --no-compress --font TerminusMedium-001.000.ttf --range 0x20-0x7e,0xa1-0xff --format lvgl -o terminus_1bpp_12px.c
|
||||
|
||||
:note: use 1bpp because we don't use anti-aliasing. It doesn't look sharp on displays with a low resolution.
|
||||
|
||||
Add a new font engine
|
||||
*********************
|
||||
|
||||
LVGL's font interface is designed to be very flexible but, even so, you
|
||||
can add your own font engine in place of LVGL's internal one. For
|
||||
example, you can use `FreeType <https://www.freetype.org/>`__ to
|
||||
real-time render glyphs from TTF fonts or use an external flash to store
|
||||
the font's bitmap and read them when the library needs them.
|
||||
|
||||
A ready to use FreeType can be found in
|
||||
`lv_freetype <https://github.com/lvgl/lv_lib_freetype>`__ repository.
|
||||
|
||||
To do this, a custom :cpp:type:`lv_font_t` variable needs to be created:
|
||||
|
||||
.. code:: c
|
||||
|
||||
/*Describe the properties of a font*/
|
||||
lv_font_t my_font;
|
||||
my_font.get_glyph_dsc = my_get_glyph_dsc_cb; /*Set a callback to get info about glyphs*/
|
||||
my_font.get_glyph_bitmap = my_get_glyph_bitmap_cb; /*Set a callback to get bitmap of a glyph*/
|
||||
my_font.line_height = height; /*The real line height where any text fits*/
|
||||
my_font.base_line = base_line; /*Base line measured from the top of line_height*/
|
||||
my_font.dsc = something_required; /*Store any implementation specific data here*/
|
||||
my_font.user_data = user_data; /*Optionally some extra user data*/
|
||||
|
||||
...
|
||||
|
||||
/* Get info about glyph of `unicode_letter` in `font` font.
|
||||
* Store the result in `dsc_out`.
|
||||
* The next letter (`unicode_letter_next`) might be used to calculate the width required by this glyph (kerning)
|
||||
*/
|
||||
bool my_get_glyph_dsc_cb(const lv_font_t * font, lv_font_glyph_dsc_t * dsc_out, uint32_t unicode_letter, uint32_t unicode_letter_next)
|
||||
{
|
||||
/*Your code here*/
|
||||
|
||||
/* Store the result.
|
||||
* For example ...
|
||||
*/
|
||||
dsc_out->adv_w = 12; /*Horizontal space required by the glyph in [px]*/
|
||||
dsc_out->box_h = 8; /*Height of the bitmap in [px]*/
|
||||
dsc_out->box_w = 6; /*Width of the bitmap in [px]*/
|
||||
dsc_out->ofs_x = 0; /*X offset of the bitmap in [pf]*/
|
||||
dsc_out->ofs_y = 3; /*Y offset of the bitmap measured from the as line*/
|
||||
dsc_out->format= LV_FONT_GLYPH_FORMAT_A2;
|
||||
|
||||
return true; /*true: glyph found; false: glyph was not found*/
|
||||
}
|
||||
|
||||
|
||||
/* Get the bitmap of `unicode_letter` from `font`. */
|
||||
const uint8_t * my_get_glyph_bitmap_cb(const lv_font_t * font, uint32_t unicode_letter)
|
||||
{
|
||||
/* Your code here */
|
||||
|
||||
/* The bitmap should be a continuous bitstream where
|
||||
* each pixel is represented by `bpp` bits */
|
||||
|
||||
return bitmap; /*Or NULL if not found*/
|
||||
}
|
||||
|
||||
Use font fallback
|
||||
*****************
|
||||
|
||||
You can specify ``fallback`` in :cpp:type:`lv_font_t` to provide fallback to the
|
||||
font. When the font fails to find glyph to a letter, it will try to let
|
||||
font from ``fallback`` to handle.
|
||||
|
||||
``fallback`` can be chained, so it will try to solve until there is no ``fallback`` set.
|
||||
|
||||
.. code:: c
|
||||
|
||||
/* Roboto font doesn't have support for CJK glyphs */
|
||||
lv_font_t *roboto = my_font_load_function();
|
||||
/* Droid Sans Fallback has more glyphs but its typeface doesn't look good as Roboto */
|
||||
lv_font_t *droid_sans_fallback = my_font_load_function();
|
||||
/* So now we can display Roboto for supported characters while having wider characters set support */
|
||||
roboto->fallback = droid_sans_fallback;
|
||||
|
||||
.. _fonts_api:
|
||||
|
||||
API
|
||||
***
|
||||
254
libraries/lvgl/docs/overview/fs.rst
Normal file
254
libraries/lvgl/docs/overview/fs.rst
Normal file
@@ -0,0 +1,254 @@
|
||||
.. _overview_file_system:
|
||||
|
||||
===========
|
||||
File system
|
||||
===========
|
||||
|
||||
LVGL has a 'File system' abstraction module that enables you to attach
|
||||
any type of file system. A file system is identified by an assigned
|
||||
drive letter. For example, if an SD card is associated with the letter
|
||||
``'S'``, a file can be reached using ``"S:path/to/file.txt"``.
|
||||
|
||||
.. note::
|
||||
|
||||
If you want to skip the drive prefix from the path, you can use the :c:macro:`LV_FS_DEFAULT_DRIVE_LETTER` config parameter.
|
||||
|
||||
Ready to use drivers
|
||||
********************
|
||||
|
||||
LVGL contains prepared drivers for the API of POSIX, standard C,
|
||||
Windows, and `FATFS <http://elm-chan.org/fsw/ff/00index_e.html>`__.
|
||||
Learn more :ref:`here <libs_filesystem>`.
|
||||
|
||||
Adding a driver
|
||||
***************
|
||||
|
||||
Registering a driver
|
||||
--------------------
|
||||
|
||||
To add a driver, a :cpp:type:`lv_fs_drv_t` needs to be initialized like below.
|
||||
The :cpp:type:`lv_fs_drv_t` needs to be static, global or dynamically allocated
|
||||
and not a local variable.
|
||||
|
||||
.. code:: c
|
||||
|
||||
static lv_fs_drv_t drv; /*Needs to be static or global*/
|
||||
lv_fs_drv_init(&drv); /*Basic initialization*/
|
||||
|
||||
drv.letter = 'S'; /*An uppercase letter to identify the drive */
|
||||
drv.cache_size = my_cache_size; /*Cache size for reading in bytes. 0 to not cache.*/
|
||||
|
||||
drv.ready_cb = my_ready_cb; /*Callback to tell if the drive is ready to use */
|
||||
drv.open_cb = my_open_cb; /*Callback to open a file */
|
||||
drv.close_cb = my_close_cb; /*Callback to close a file */
|
||||
drv.read_cb = my_read_cb; /*Callback to read a file */
|
||||
drv.write_cb = my_write_cb; /*Callback to write a file */
|
||||
drv.seek_cb = my_seek_cb; /*Callback to seek in a file (Move cursor) */
|
||||
drv.tell_cb = my_tell_cb; /*Callback to tell the cursor position */
|
||||
|
||||
drv.dir_open_cb = my_dir_open_cb; /*Callback to open directory to read its content */
|
||||
drv.dir_read_cb = my_dir_read_cb; /*Callback to read a directory's content */
|
||||
drv.dir_close_cb = my_dir_close_cb; /*Callback to close a directory */
|
||||
|
||||
drv.user_data = my_user_data; /*Any custom data if required*/
|
||||
|
||||
lv_fs_drv_register(&drv); /*Finally register the drive*/
|
||||
|
||||
Any of the callbacks can be ``NULL`` to indicate that operation is not
|
||||
supported.
|
||||
|
||||
Implementing the callbacks
|
||||
--------------------------
|
||||
|
||||
Open callback
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
The prototype of ``open_cb`` looks like this:
|
||||
|
||||
.. code:: c
|
||||
|
||||
void * (*open_cb)(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode);
|
||||
|
||||
``path`` is the path after the drive letter (e.g. "S:path/to/file.txt" -> "path/to/file.txt").
|
||||
``mode`` can be :cpp:enumerator:`LV_FS_MODE_WR` or :cpp:enumerator:`LV_FS_MODE_RD` to open for writes or reads.
|
||||
|
||||
The return value is a pointer to a *file object* that describes the
|
||||
opened file or ``NULL`` if there were any issues (e.g. the file wasn't
|
||||
found). The returned file object will be passed to other file system
|
||||
related callbacks. (see below)
|
||||
|
||||
Other callbacks
|
||||
---------------
|
||||
|
||||
The other callbacks are quite similar. For example ``write_cb`` looks
|
||||
like this:
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_fs_res_t (*write_cb)(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw);
|
||||
|
||||
For ``file_p``, LVGL passes the return value of ``open_cb``, ``buf`` is
|
||||
the data to write, ``btw`` is the Bytes To Write, ``bw`` is the actually
|
||||
written bytes.
|
||||
|
||||
For a template of these callbacks see
|
||||
`lv_fs_template.c <https://github.com/lvgl/lvgl/blob/master/examples/porting/lv_port_fs_template.c>`__.
|
||||
|
||||
Usage example
|
||||
*************
|
||||
|
||||
The example below shows how to read from a file:
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_fs_file_t f;
|
||||
lv_fs_res_t res;
|
||||
res = lv_fs_open(&f, "S:folder/file.txt", LV_FS_MODE_RD);
|
||||
if(res != LV_FS_RES_OK) my_error_handling();
|
||||
|
||||
uint32_t read_num;
|
||||
uint8_t buf[8];
|
||||
res = lv_fs_read(&f, buf, 8, &read_num);
|
||||
if(res != LV_FS_RES_OK || read_num != 8) my_error_handling();
|
||||
|
||||
lv_fs_close(&f);
|
||||
|
||||
The mode in :cpp:func:`lv_fs_open` can be :cpp:enumerator:`LV_FS_MODE_WR` to open for writes
|
||||
only or :cpp:enumerator:`LV_FS_MODE_RD` ``|`` :cpp:enumerator:`LV_FS_MODE_WR` for both
|
||||
|
||||
This example shows how to read a directory's content. It's up to the
|
||||
driver how to mark directories in the result but it can be a good
|
||||
practice to insert a ``'/'`` in front of each directory name.
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_fs_dir_t dir;
|
||||
lv_fs_res_t res;
|
||||
res = lv_fs_dir_open(&dir, "S:/folder");
|
||||
if(res != LV_FS_RES_OK) my_error_handling();
|
||||
|
||||
char fn[256];
|
||||
while(1) {
|
||||
res = lv_fs_dir_read(&dir, fn, sizeof(fn));
|
||||
if(res != LV_FS_RES_OK) {
|
||||
my_error_handling();
|
||||
break;
|
||||
}
|
||||
|
||||
/*fn is empty, if not more files to read*/
|
||||
if(strlen(fn) == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
printf("%s\n", fn);
|
||||
}
|
||||
|
||||
lv_fs_dir_close(&dir);
|
||||
|
||||
Use drives for images
|
||||
*********************
|
||||
|
||||
:ref:`Image <lv_image>` objects can be opened from files too (besides
|
||||
variables stored in the compiled program).
|
||||
|
||||
To use files in image widgets the following callbacks are required:
|
||||
|
||||
- open
|
||||
- close
|
||||
- read
|
||||
- seek
|
||||
- tell
|
||||
|
||||
.. _overview_file_system_cache:
|
||||
|
||||
Optional file buffering/caching
|
||||
*******************************
|
||||
|
||||
Files will buffer their reads if the corresponding ``LV_FS_*_CACHE_SIZE``
|
||||
config option is set to a value greater than zero. Each open file will
|
||||
buffer up to that many bytes to reduce the number of FS driver calls.
|
||||
|
||||
Generally speaking, file buffering can be optimized for different kinds
|
||||
of access patterns. The one implemented here is optimal for reading large
|
||||
files in chunks, which is what the image decoder does.
|
||||
It has the potential to call the driver's ``read`` fewer
|
||||
times than ``lv_fs_read`` is called. In the best case where the cache size is
|
||||
\>= the size of the file, ``read`` will only be called once. This strategy is good
|
||||
for linear reading of large files but less helpful for short random reads across a file bigger than the buffer
|
||||
since data will be buffered that will be discarded after the next seek and read.
|
||||
The cache should be sufficiently large or disabled in that case. Another case where the cache should be disabled
|
||||
is if the file contents are expected to change by an external factor like with special OS files.
|
||||
|
||||
The implementation is documented below. Note that the FS functions make calls
|
||||
to other driver FS functions when the cache is enabled. i.e., ``lv_fs_read`` may call the driver's ``seek``
|
||||
so the driver needs to implement more callbacks when the cache is enabled.
|
||||
|
||||
``lv_fs_read`` :sub:`(behavior when the cache is enabled)`
|
||||
-------------------------------------------------
|
||||
|
||||
.. mermaid::
|
||||
:zoom:
|
||||
|
||||
%%{init: {'theme':'neutral'}}%%
|
||||
flowchart LR
|
||||
A["call lv_fs_read and
|
||||
the cache is enabled"] --> B{{"is there cached data
|
||||
at the file position?"}}
|
||||
B -->|yes| C{{"does the cache have
|
||||
all required bytes available?"}}
|
||||
C -->|yes| D["copy all required bytes from
|
||||
the cache to the destination
|
||||
buffer"]
|
||||
C -->|no| F["copy the available
|
||||
required bytes
|
||||
until the end of the cache
|
||||
into the destination buffer"]
|
||||
--> G["seek the real file to the end
|
||||
of what the cache had available"]
|
||||
--> H{{"is the number of remaining bytes
|
||||
larger than the size of the whole cache?"}}
|
||||
H -->|yes| I["read the remaining bytes
|
||||
from the real file to the
|
||||
destination buffer"]
|
||||
H -->|no| J["eagerly read the real file
|
||||
to fill the whole cache
|
||||
or as many bytes as the
|
||||
read call can"]
|
||||
--> O["copy the required bytes
|
||||
to the destination buffer"]
|
||||
B -->|no| K["seek the real file to
|
||||
the file position"]
|
||||
--> L{{"is the number of required
|
||||
bytes greater than the
|
||||
size of the entire cache?"}}
|
||||
L -->|yes| M["read the real file to
|
||||
the destination buffer"]
|
||||
L -->|no| N["eagerly read the real file
|
||||
to fill the whole cache
|
||||
or as many bytes as the
|
||||
read call can"]
|
||||
--> P["copy the required bytes
|
||||
to the destination buffer"]
|
||||
|
||||
``lv_fs_write`` :sub:`(behavior when the cache is enabled)`
|
||||
--------------------------------------------------
|
||||
|
||||
The part of the cache that coincides with the written content
|
||||
will be updated to reflect the written content.
|
||||
|
||||
``lv_fs_seek`` :sub:`(behavior when the cache is enabled)`
|
||||
-------------------------------------------------
|
||||
|
||||
The driver's ``seek`` will not actually be called unless the ``whence``
|
||||
is ``LV_FS_SEEK_END``, in which case ``seek`` and ``tell`` will be called
|
||||
to determine where the end of the file is.
|
||||
|
||||
``lv_fs_tell`` :sub:`(behavior when the cache is enabled)`
|
||||
-------------------------------------------------
|
||||
|
||||
The driver's ``tell`` will not actually be called.
|
||||
|
||||
.. _overview_file_system_api:
|
||||
|
||||
API
|
||||
***
|
||||
642
libraries/lvgl/docs/overview/image.rst
Normal file
642
libraries/lvgl/docs/overview/image.rst
Normal file
@@ -0,0 +1,642 @@
|
||||
.. _overview_image:
|
||||
|
||||
======
|
||||
Images
|
||||
======
|
||||
|
||||
An image can be a file or a variable which stores the bitmap itself and
|
||||
some metadata.
|
||||
|
||||
Store images
|
||||
************
|
||||
|
||||
You can store images in two places
|
||||
|
||||
- as a variable in internal memory (RAM or ROM)
|
||||
- as a file
|
||||
|
||||
.. _overview_image_variables:
|
||||
|
||||
Variables
|
||||
---------
|
||||
|
||||
Images stored internally in a variable are composed mainly of an
|
||||
:cpp:struct:`lv_image_dsc_t` structure with the following fields:
|
||||
|
||||
- **header**:
|
||||
|
||||
- *cf*: Color format. See :ref:`below <overview_image_color_formats>`
|
||||
- *w*: width in pixels (<= 2048)
|
||||
- *h*: height in pixels (<= 2048)
|
||||
- *always zero*: 3 bits which need to be always zero
|
||||
- *reserved*: reserved for future use
|
||||
- **data**: pointer to an array where the image itself is stored
|
||||
- **data_size**: length of ``data`` in bytes
|
||||
|
||||
These are usually stored within a project as C files. They are linked
|
||||
into the resulting executable like any other constant data.
|
||||
|
||||
.. _overview_image_files:
|
||||
|
||||
Files
|
||||
-----
|
||||
|
||||
To deal with files you need to add a storage *Drive* to LVGL. In short,
|
||||
a *Drive* is a collection of functions (*open*, *read*, *close*, etc.)
|
||||
registered in LVGL to make file operations. You can add an interface to
|
||||
a standard file system (FAT32 on SD card) or you create your simple file
|
||||
system to read data from an SPI Flash memory. In every case, a *Drive*
|
||||
is just an abstraction to read and/or write data to memory. See the
|
||||
:ref:`File system <overview_file_system>` section to learn more.
|
||||
|
||||
Images stored as files are not linked into the resulting executable, and
|
||||
must be read into RAM before being drawn. As a result, they are not as
|
||||
resource-friendly as images linked at compile time. However, they are
|
||||
easier to replace without needing to rebuild the main program.
|
||||
|
||||
.. _overview_image_color_formats:
|
||||
|
||||
Color formats
|
||||
*************
|
||||
|
||||
Various built-in color formats are supported:
|
||||
|
||||
- :cpp:enumerator:`LV_COLOR_FORMAT_NATIVE`: Simply stores the RGB colors (in whatever color depth LVGL is configured for).
|
||||
- :cpp:enumerator:`LV_COLOR_FORMAT_NATIVE_WITH_ALPHA`: Like :cpp:enumerator:`LV_COLOR_FORMAT_NATIVE` but it also adds an alpha (transparency) byte for every pixel.
|
||||
- :cpp:enumerator:`LV_COLOR_FORMAT_I1`, :cpp:enumerator:`LV_COLOR_FORMAT_I2`, :cpp:enumerator:`LV_COLOR_FORMAT_I4`, :cpp:enumerator:`LV_COLOR_FORMAT_I8`:
|
||||
Uses a palette with 2, 4, 16 or 256 colors and stores each pixel in 1, 2, 4 or 8 bits.
|
||||
- :cpp:enumerator:`LV_COLOR_FORMAT_A1`, :cpp:enumerator:`LV_COLOR_FORMAT_A2`, :cpp:enumerator:`LV_COLOR_FORMAT_A4`, :cpp:enumerator:`LV_COLOR_FORMAT_A8`:
|
||||
**Only stores the Alpha value with 1, 2, 4 or 8 bits.** The pixels take the color of ``style.img_recolor`` and
|
||||
the set opacity. The source image has to be an alpha channel. This is
|
||||
ideal for bitmaps similar to fonts where the whole image is one color
|
||||
that can be altered.
|
||||
|
||||
The bytes of :cpp:enumerator:`LV_COLOR_FORMAT_NATIVE` images are stored in the following order.
|
||||
|
||||
- 32-bit color depth:
|
||||
- **Byte 0**: Blue
|
||||
- **Byte 1**: Green
|
||||
- **Byte 2**: Red
|
||||
- **Byte 3**: Alpha (only with :cpp:enumerator:`LV_COLOR_FORMAT_NATIVE_WITH_ALPHA`)
|
||||
- 16-bit color depth:
|
||||
- **Byte 0**: Green 3 lower bit, Blue 5 bit
|
||||
- **Byte 1**: Red 5 bit, Green 3 higher bit
|
||||
- **Byte 2**: Alpha byte (only with :cpp:enumerator:`LV_COLOR_FORMAT_NATIVE_WITH_ALPHA`)
|
||||
- 8-bit color depth:
|
||||
- **Byte 0**: Red 3 bit, Green 3 bit, Blue 2 bit
|
||||
- **Byte 2**: Alpha byte (only with :cpp:enumerator:`LV_COLOR_FORMAT_NATIVE_WITH_ALPHA`)
|
||||
|
||||
You can store images in a *Raw* format to indicate that it's not encoded
|
||||
with one of the built-in color formats and an external :ref:`Image decoder <overview_image_decoder>`
|
||||
needs to be used to decode the image.
|
||||
|
||||
- :cpp:enumerator:`LV_COLOR_FORMAT_RAW`: Indicates a basic raw image (e.g. a PNG or JPG image).
|
||||
- :cpp:enumerator:`LV_COLOR_FORMAT_RAW_ALPHA`: Indicates that an image has alpha and an alpha byte is added for every pixel.
|
||||
|
||||
Add and use images
|
||||
******************
|
||||
|
||||
You can add images to LVGL in two ways:
|
||||
|
||||
- using the online converter
|
||||
- manually create images
|
||||
|
||||
Online converter
|
||||
----------------
|
||||
|
||||
The online Image converter is available here:
|
||||
https://lvgl.io/tools/imageconverter
|
||||
|
||||
Adding an image to LVGL via the online converter is easy.
|
||||
|
||||
1. You need to select a *BMP*, *PNG* or *JPG* image first.
|
||||
2. Give the image a name that will be used within LVGL.
|
||||
3. Select the :ref:`Color format <overview_image_color_formats>`.
|
||||
4. Select the type of image you want. Choosing a binary will generate a
|
||||
``.bin`` file that must be stored separately and read using the :ref:`file support <overview_image_files>`.
|
||||
Choosing a variable will generate a standard C file that can be linked into your project.
|
||||
5. Hit the *Convert* button. Once the conversion is finished, your
|
||||
browser will automatically download the resulting file.
|
||||
|
||||
In the generated C arrays (variables), bitmaps for all the color depths
|
||||
(1, 8, 16 or 32) are included in the C file, but only the color depth
|
||||
that matches :c:macro:`LV_COLOR_DEPTH` in *lv_conf.h* will actually be linked
|
||||
into the resulting executable.
|
||||
|
||||
In the case of binary files, you need to specify the color format you
|
||||
want:
|
||||
|
||||
- RGB332 for 8-bit color depth
|
||||
- RGB565 for 16-bit color depth
|
||||
- RGB565 Swap for 16-bit color depth (two bytes are swapped)
|
||||
- RGB888 for 32-bit color depth
|
||||
|
||||
Manually create an image
|
||||
------------------------
|
||||
|
||||
If you are generating an image at run-time, you can craft an image
|
||||
variable to display it using LVGL. For example:
|
||||
|
||||
.. code:: c
|
||||
|
||||
uint8_t my_img_data[] = {0x00, 0x01, 0x02, ...};
|
||||
|
||||
static lv_image_dsc_t my_img_dsc = {
|
||||
.header.always_zero = 0,
|
||||
.header.w = 80,
|
||||
.header.h = 60,
|
||||
.data_size = 80 * 60 * LV_COLOR_DEPTH / 8,
|
||||
.header.cf = LV_COLOR_FORMAT_NATIVE, /*Set the color format*/
|
||||
.data = my_img_data,
|
||||
};
|
||||
|
||||
Another (possibly simpler) option to create and display an image at
|
||||
run-time is to use the :ref:`Canvas <lv_canvas>` object.
|
||||
|
||||
Use images
|
||||
----------
|
||||
|
||||
The simplest way to use an image in LVGL is to display it with an
|
||||
:ref:`lv_image` object:
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_obj_t * icon = lv_image_create(lv_screen_active(), NULL);
|
||||
|
||||
/*From variable*/
|
||||
lv_image_set_src(icon, &my_icon_dsc);
|
||||
|
||||
/*From file*/
|
||||
lv_image_set_src(icon, "S:my_icon.bin");
|
||||
|
||||
If the image was converted with the online converter, you should use
|
||||
:cpp:expr:`LV_IMAGE_DECLARE(my_icon_dsc)` to declare the image in the file where
|
||||
you want to use it.
|
||||
|
||||
.. _overview_image_decoder:
|
||||
|
||||
Image decoder
|
||||
*************
|
||||
|
||||
As you can see in the :ref:`overview_image_color_formats` section, LVGL
|
||||
supports several built-in image formats. In many cases, these will be
|
||||
all you need. LVGL doesn't directly support, however, generic image
|
||||
formats like PNG or JPG.
|
||||
|
||||
To handle non-built-in image formats, you need to use external libraries
|
||||
and attach them to LVGL via the *Image decoder* interface.
|
||||
|
||||
An image decoder consists of 4 callbacks:
|
||||
|
||||
- **info** get some basic info about the image (width, height and color format).
|
||||
- **open** open an image:
|
||||
- store a decoded image
|
||||
- set it to ``NULL`` to indicate the image can be read line-by-line.
|
||||
- **get_area** if *open* didn't fully open an image this function should give back part of image as decoded data.
|
||||
- **close** close an opened image, free the allocated resources.
|
||||
|
||||
You can add any number of image decoders. When an image needs to be
|
||||
drawn, the library will try all the registered image decoders until it
|
||||
finds one which can open the image, i.e. one which knows that format.
|
||||
|
||||
The following formats are understood by the built-in decoder:
|
||||
- ``LV_COLOR_FORMAT_I1``
|
||||
- ``LV_COLOR_FORMAT_I2``
|
||||
- ``LV_COLOR_FORMAT_I4``
|
||||
- ``LV_COLOR_FORMAT_I8``
|
||||
- ``LV_COLOR_FORMAT_RGB888``
|
||||
- ``LV_COLOR_FORMAT_XRGB8888``
|
||||
- ``LV_COLOR_FORMAT_ARGB8888``
|
||||
- ``LV_COLOR_FORMAT_RGB565``
|
||||
- ``LV_COLOR_FORMAT_RGB565A8``
|
||||
|
||||
|
||||
Custom image formats
|
||||
--------------------
|
||||
|
||||
The easiest way to create a custom image is to use the online image
|
||||
converter and select ``Raw`` or ``Raw with alpha`` format.
|
||||
It will just take every byte of the
|
||||
binary file you uploaded and write it as an image "bitmap". You then
|
||||
need to attach an image decoder that will parse that bitmap and generate
|
||||
the real, renderable bitmap.
|
||||
|
||||
``header.cf`` will be :cpp:enumerator:`LV_COLOR_FORMAT_RAW`, :cpp:enumerator:`LV_COLOR_FORMAT_RAW_ALPHA`
|
||||
accordingly. You should choose the correct format according to your needs:
|
||||
a fully opaque image, using an alpha channel.
|
||||
|
||||
After decoding, the *raw* formats are considered *True color* by the
|
||||
library. In other words, the image decoder must decode the *Raw* images
|
||||
to *True color* according to the format described in the :ref:`overview_image_color_formats` section.
|
||||
|
||||
|
||||
Register an image decoder
|
||||
-------------------------
|
||||
|
||||
Here's an example of getting LVGL to work with PNG images.
|
||||
|
||||
First, you need to create a new image decoder and set some functions to
|
||||
open/close the PNG files. It should look like this:
|
||||
|
||||
.. code:: c
|
||||
|
||||
/*Create a new decoder and register functions */
|
||||
lv_image_decoder_t * dec = lv_image_decoder_create();
|
||||
lv_image_decoder_set_info_cb(dec, decoder_info);
|
||||
lv_image_decoder_set_open_cb(dec, decoder_open);
|
||||
lv_image_decoder_set_get_area_cb(dec, decoder_get_area);
|
||||
lv_image_decoder_set_close_cb(dec, decoder_close);
|
||||
|
||||
|
||||
/**
|
||||
* Get info about a PNG image
|
||||
* @param decoder pointer to the decoder where this function belongs
|
||||
* @param src can be file name or pointer to a C array
|
||||
* @param header image information is set in header parameter
|
||||
* @return LV_RESULT_OK: no error; LV_RESULT_INVALID: can't get the info
|
||||
*/
|
||||
static lv_result_t decoder_info(lv_image_decoder_t * decoder, const void * src, lv_image_header_t * header)
|
||||
{
|
||||
/*Check whether the type `src` is known by the decoder*/
|
||||
if(is_png(src) == false) return LV_RESULT_INVALID;
|
||||
|
||||
/* Read the PNG header and find `width` and `height` */
|
||||
...
|
||||
|
||||
header->cf = LV_COLOR_FORMAT_ARGB8888;
|
||||
header->w = width;
|
||||
header->h = height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a PNG image and decode it into dsc.decoded
|
||||
* @param decoder pointer to the decoder where this function belongs
|
||||
* @param dsc image descriptor
|
||||
* @return LV_RESULT_OK: no error; LV_RESULT_INVALID: can't open the image
|
||||
*/
|
||||
static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
|
||||
{
|
||||
(void) decoder; /*Unused*/
|
||||
|
||||
/*Check whether the type `src` is known by the decoder*/
|
||||
if(is_png(dsc->src) == false) return LV_RESULT_INVALID;
|
||||
|
||||
/*Decode and store the image. If `dsc->decoded` is `NULL`, the `decoder_get_area` function will be called to get the image data line-by-line*/
|
||||
dsc->decoded = my_png_decoder(dsc->src);
|
||||
|
||||
/*Change the color format if decoded image format is different than original format. For PNG it's usually decoded to ARGB8888 format*/
|
||||
dsc->decoded.header.cf = LV_COLOR_FORMAT_...
|
||||
|
||||
/*Call a binary image decoder function if required. It's not required if `my_png_decoder` opened the image in true color format.*/
|
||||
lv_result_t res = lv_bin_decoder_open(decoder, dsc);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode an area of image
|
||||
* @param decoder pointer to the decoder where this function belongs
|
||||
* @param dsc image decoder descriptor
|
||||
* @param full_area input parameter. the full area to decode after enough subsequent calls
|
||||
* @param decoded_area input+output parameter. set the values to `LV_COORD_MIN` for the first call and to reset decoding.
|
||||
* the decoded area is stored here after each call.
|
||||
* @return LV_RESULT_OK: ok; LV_RESULT_INVALID: failed or there is nothing left to decode
|
||||
*/
|
||||
static lv_result_t decoder_get_area(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc,
|
||||
const lv_area_t * full_area, lv_area_t * decoded_area)
|
||||
{
|
||||
/**
|
||||
* If `dsc->decoded` is always set in `decoder_open` then `decoder_get_area` does not need to be implemented.
|
||||
* If `dsc->decoded` is only sometimes set or never set in `decoder_open` then `decoder_get_area` is used to
|
||||
* incrementally decode the image by calling it repeatedly until it returns `LV_RESULT_INVALID`.
|
||||
* In the example below the image is decoded line-by-line but the decoded area can have any shape and size
|
||||
* depending on the requirements and capabilities of the image decoder.
|
||||
*/
|
||||
|
||||
my_decoder_data_t * my_decoder_data = dsc->user_data;
|
||||
|
||||
/* if `decoded_area` has a field set to `LV_COORD_MIN` then reset decoding */
|
||||
if(decoded_area->y1 == LV_COORD_MIN) {
|
||||
decoded_area->x1 = full_area->x1;
|
||||
decoded_area->x2 = full_area->x2;
|
||||
decoded_area->y1 = full_area->y1;
|
||||
decoded_area->y2 = decoded_area->y1; /* decode line-by-line, starting with the first line */
|
||||
|
||||
/* create a draw buf the size of one line */
|
||||
bool reshape_success = NULL != lv_draw_buf_reshape(my_decoder_data->partial,
|
||||
dsc->decoded.header.cf,
|
||||
lv_area_get_width(full_area),
|
||||
1,
|
||||
LV_STRIDE_AUTO);
|
||||
if(!reshape_success) {
|
||||
lv_draw_buf_destroy(my_decoder_data->partial);
|
||||
my_decoder_data->partial = lv_draw_buf_create(lv_area_get_width(full_area),
|
||||
1,
|
||||
dsc->decoded.header.cf,
|
||||
LV_STRIDE_AUTO);
|
||||
|
||||
my_png_decode_line_reset(full_area);
|
||||
}
|
||||
}
|
||||
/* otherwise decoding is already in progress. decode the next line */
|
||||
else {
|
||||
/* all lines have already been decoded. indicate completion by returning `LV_RESULT_INVALID` */
|
||||
if (decoded_area->y1 >= full_area->y2) return LV_RESULT_INVALID;
|
||||
decoded_area->y1++;
|
||||
decoded_area->y2++;
|
||||
}
|
||||
|
||||
my_png_decode_line(my_decoder_data->partial);
|
||||
|
||||
return LV_RESULT_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close PNG image and free data
|
||||
* @param decoder pointer to the decoder where this function belongs
|
||||
* @param dsc image decoder descriptor
|
||||
* @return LV_RESULT_OK: no error; LV_RESULT_INVALID: can't open the image
|
||||
*/
|
||||
static void decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
|
||||
{
|
||||
/*Free all allocated data*/
|
||||
my_png_cleanup();
|
||||
|
||||
my_decoder_data_t * my_decoder_data = dsc->user_data;
|
||||
lv_draw_buf_destroy(my_decoder_data->partial);
|
||||
|
||||
/*Call the built-in close function if the built-in open/get_area was used*/
|
||||
lv_bin_decoder_close(decoder, dsc);
|
||||
|
||||
}
|
||||
|
||||
So in summary:
|
||||
|
||||
- In ``decoder_info``, you should collect some basic information about the image and store it in ``header``.
|
||||
- In ``decoder_open``, you should try to open the image source pointed by
|
||||
``dsc->src``. Its type is already in ``dsc->src_type == LV_IMG_SRC_FILE/VARIABLE``.
|
||||
If this format/type is not supported by the decoder, return :cpp:enumerator:`LV_RESULT_INVALID`.
|
||||
However, if you can open the image, a pointer to the decoded image should be
|
||||
set in ``dsc->decoded``. If the format is known, but you don't want to
|
||||
decode the entire image (e.g. no memory for it), set ``dsc->decoded = NULL`` and
|
||||
use ``decoder_get_area`` to get the image area pixels.
|
||||
- In ``decoder_close`` you should free all allocated resources.
|
||||
- ``decoder_get_area`` is optional. In this case you should decode the whole image In
|
||||
``decoder_open`` function and store image data in ``dsc->decoded``.
|
||||
Decoding the whole image requires extra memory and some computational overhead.
|
||||
|
||||
|
||||
Manually use an image decoder
|
||||
-----------------------------
|
||||
|
||||
LVGL will use registered image decoders automatically if you try and
|
||||
draw a raw image (i.e. using the ``lv_image`` object) but you can use them
|
||||
manually too. Create an :cpp:type:`lv_image_decoder_dsc_t` variable to describe
|
||||
the decoding session and call :cpp:func:`lv_image_decoder_open`.
|
||||
|
||||
The ``color`` parameter is used only with ``LV_COLOR_FORMAT_A1/2/4/8``
|
||||
images to tell color of the image.
|
||||
|
||||
.. code:: c
|
||||
|
||||
|
||||
lv_result_t res;
|
||||
lv_image_decoder_dsc_t dsc;
|
||||
lv_image_decoder_args_t args = { 0 }; /*Custom decoder behavior via args*/
|
||||
res = lv_image_decoder_open(&dsc, &my_img_dsc, &args);
|
||||
|
||||
if(res == LV_RESULT_OK) {
|
||||
/*Do something with `dsc->decoded`. You can copy out the decoded image by `lv_draw_buf_dup(dsc.decoded)`*/
|
||||
lv_image_decoder_close(&dsc);
|
||||
}
|
||||
|
||||
|
||||
Image post-processing
|
||||
---------------------
|
||||
|
||||
Considering that some hardware has special requirements for image formats,
|
||||
such as alpha premultiplication and stride alignment, most image decoders (such as PNG decoders)
|
||||
may not directly output image data that meets hardware requirements.
|
||||
|
||||
For this reason, LVGL provides a solution for image post-processing.
|
||||
First, call a custom post-processing function after ``lv_image_decoder_open`` to adjust the data in the image cache,
|
||||
and then mark the processing status in ``cache_entry->process_state`` (to avoid repeated post-processing).
|
||||
|
||||
See the detailed code below:
|
||||
|
||||
- Stride alignment and premultiply post-processing example:
|
||||
|
||||
.. code:: c
|
||||
|
||||
/* Define post-processing state */
|
||||
typedef enum {
|
||||
IMAGE_PROCESS_STATE_NONE = 0,
|
||||
IMAGE_PROCESS_STATE_STRIDE_ALIGNED = 1 << 0,
|
||||
IMAGE_PROCESS_STATE_PREMULTIPLIED_ALPHA = 1 << 1,
|
||||
} image_process_state_t;
|
||||
|
||||
lv_result_t my_image_post_process(lv_image_decoder_dsc_t * dsc)
|
||||
{
|
||||
lv_color_format_t color_format = dsc->header.cf;
|
||||
lv_result_t res = LV_RESULT_OK;
|
||||
|
||||
if(color_format == LV_COLOR_FORMAT_ARGB8888) {
|
||||
lv_cache_lock();
|
||||
lv_cache_entry_t * entry = dsc->cache_entry;
|
||||
|
||||
if(!(entry->process_state & IMAGE_PROCESS_STATE_PREMULTIPLIED_ALPHA)) {
|
||||
lv_draw_buf_premultiply(dsc->decoded);
|
||||
LV_LOG_USER("premultiplied alpha OK");
|
||||
|
||||
entry->process_state |= IMAGE_PROCESS_STATE_PREMULTIPLIED_ALPHA;
|
||||
}
|
||||
|
||||
if(!(entry->process_state & IMAGE_PROCESS_STATE_STRIDE_ALIGNED)) {
|
||||
uint32_t stride_expect = lv_draw_buf_width_to_stride(decoded->header.w, decoded->header.cf);
|
||||
if(decoded->header.stride != stride_expect) {
|
||||
LV_LOG_WARN("Stride mismatch");
|
||||
lv_draw_buf_t * aligned = lv_draw_buf_adjust_stride(decoded, stride_expect);
|
||||
if(aligned == NULL) {
|
||||
LV_LOG_ERROR("No memory for Stride adjust.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
decoded = aligned;
|
||||
}
|
||||
|
||||
entry->process_state |= IMAGE_PROCESS_STATE_STRIDE_ALIGNED;
|
||||
}
|
||||
|
||||
alloc_failed:
|
||||
lv_cache_unlock();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
- GPU draw unit example:
|
||||
|
||||
.. code:: c
|
||||
|
||||
void gpu_draw_image(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * draw_dsc, const lv_area_t * coords)
|
||||
{
|
||||
...
|
||||
lv_image_decoder_dsc_t decoder_dsc;
|
||||
lv_result_t res = lv_image_decoder_open(&decoder_dsc, draw_dsc->src, NULL);
|
||||
if(res != LV_RESULT_OK) {
|
||||
LV_LOG_ERROR("Failed to open image");
|
||||
return;
|
||||
}
|
||||
|
||||
res = my_image_post_process(&decoder_dsc);
|
||||
if(res != LV_RESULT_OK) {
|
||||
LV_LOG_ERROR("Failed to post-process image");
|
||||
return;
|
||||
}
|
||||
...
|
||||
}
|
||||
|
||||
.. _overview_image_caching:
|
||||
|
||||
Image caching
|
||||
*************
|
||||
|
||||
Sometimes it takes a lot of time to open an image. Continuously decoding
|
||||
a PNG/JPEG image or loading images from a slow external memory would be
|
||||
inefficient and detrimental to the user experience.
|
||||
|
||||
Therefore, LVGL caches image data. Caching means some
|
||||
images will be left open, hence LVGL can quickly access them from
|
||||
``dsc->decoded`` instead of needing to decode them again.
|
||||
|
||||
Of course, caching images is resource intensive as it uses more RAM to
|
||||
store the decoded image. LVGL tries to optimize the process as much as
|
||||
possible (see below), but you will still need to evaluate if this would
|
||||
be beneficial for your platform or not. Image caching may not be worth
|
||||
it if you have a deeply embedded target which decodes small images from
|
||||
a relatively fast storage medium.
|
||||
|
||||
Cache size
|
||||
----------
|
||||
|
||||
The size of cache (in bytes) can be defined with
|
||||
:c:macro:`LV_CACHE_DEF_SIZE` in *lv_conf.h*. The default value is 0, so
|
||||
no image is cached.
|
||||
|
||||
The size of cache can be changed at run-time with
|
||||
:cpp:expr:`lv_cache_set_max_size(size_t size)`,
|
||||
and get with :cpp:expr:`lv_cache_get_max_size()`.
|
||||
|
||||
Value of images
|
||||
---------------
|
||||
|
||||
When you use more images than available cache size, LVGL can't cache all the
|
||||
images. Instead, the library will close one of the cached images to free
|
||||
space.
|
||||
|
||||
To decide which image to close, LVGL uses a measurement it previously
|
||||
made of how long it took to open the image. Cache entries that hold
|
||||
slower-to-open images are considered more valuable and are kept in the
|
||||
cache as long as possible.
|
||||
|
||||
If you want or need to override LVGL's measurement, you can manually set
|
||||
the *weight* value in the cache entry in
|
||||
``cache_entry->weight = time_ms`` to give a higher or lower value. (Leave
|
||||
it unchanged to let LVGL control it.)
|
||||
|
||||
Every cache entry has a *"life"* value. Every time an image is opened
|
||||
through the cache, the *life* value of all entries is increased by their
|
||||
*weight* values to make them older.
|
||||
When a cached image is used, its *usage_count* value is increased
|
||||
to make it more alive.
|
||||
|
||||
If there is no more space in the cache, the entry with *usage_count == 0*
|
||||
and lowest life value will be dropped.
|
||||
|
||||
Memory usage
|
||||
------------
|
||||
|
||||
Note that a cached image might continuously consume memory. For example,
|
||||
if three PNG images are cached, they will consume memory while they are
|
||||
open.
|
||||
|
||||
Therefore, it's the user's responsibility to be sure there is enough RAM
|
||||
to cache even the largest images at the same time.
|
||||
|
||||
Clean the cache
|
||||
---------------
|
||||
|
||||
Let's say you have loaded a PNG image into a :cpp:struct:`lv_image_dsc_t` ``my_png``
|
||||
variable and use it in an ``lv_image`` object. If the image is already
|
||||
cached and you then change the underlying PNG file, you need to notify
|
||||
LVGL to cache the image again. Otherwise, there is no easy way of
|
||||
detecting that the underlying file changed and LVGL will still draw the
|
||||
old image from cache.
|
||||
|
||||
To do this, use :cpp:expr:`lv_cache_invalidate(lv_cache_find(&my_png, LV_CACHE_SRC_TYPE_PTR, 0, 0));`.
|
||||
|
||||
Custom cache algorithm
|
||||
----------------------
|
||||
|
||||
If you want to implement your own cache algorithm, you can refer to the
|
||||
following code to replace the LVGL built-in cache manager:
|
||||
|
||||
.. code:: c
|
||||
|
||||
static lv_cache_entry_t * my_cache_add_cb(size_t size)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
static lv_cache_entry_t * my_cache_find_cb(const void * src, lv_cache_src_type_t src_type, uint32_t param1, uint32_t param2)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
static void my_cache_invalidate_cb(lv_cache_entry_t * entry)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
static const void * my_cache_get_data_cb(lv_cache_entry_t * entry)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
static void my_cache_release_cb(lv_cache_entry_t * entry)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
static void my_cache_set_max_size_cb(size_t new_size)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
static void my_cache_empty_cb(void)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
void my_cache_init(void)
|
||||
{
|
||||
/*Initialize new cache manager.*/
|
||||
lv_cache_manager_t my_manager;
|
||||
my_manager.add_cb = my_cache_add_cb;
|
||||
my_manager.find_cb = my_cache_find_cb;
|
||||
my_manager.invalidate_cb = my_cache_invalidate_cb;
|
||||
my_manager.get_data_cb = my_cache_get_data_cb;
|
||||
my_manager.release_cb = my_cache_release_cb;
|
||||
my_manager.set_max_size_cb = my_cache_set_max_size_cb;
|
||||
my_manager.empty_cb = my_cache_empty_cb;
|
||||
|
||||
/*Replace existing cache manager with the new one.*/
|
||||
lv_cache_lock();
|
||||
lv_cache_set_manager(&my_manager);
|
||||
lv_cache_unlock();
|
||||
}
|
||||
|
||||
.. _overview_image_api:
|
||||
|
||||
API
|
||||
***
|
||||
206
libraries/lvgl/docs/overview/indev.rst
Normal file
206
libraries/lvgl/docs/overview/indev.rst
Normal file
@@ -0,0 +1,206 @@
|
||||
.. _indev:
|
||||
|
||||
=============
|
||||
Input devices
|
||||
=============
|
||||
|
||||
An input device usually means:
|
||||
|
||||
- Pointer-like input device like touchpad or mouse
|
||||
- Keypads like a normal keyboard or simple numeric keypad
|
||||
- Encoders with left/right turn and push options
|
||||
- External hardware buttons which are assigned to specific points on the screen
|
||||
|
||||
:important: Before reading further, please read the `Porting </porting/indev>`__ section of Input devices
|
||||
|
||||
Pointers
|
||||
********
|
||||
|
||||
.. _indev_cursor:
|
||||
|
||||
Cursor
|
||||
------
|
||||
|
||||
Pointer input devices (like a mouse) can have a cursor.
|
||||
|
||||
.. code:: c
|
||||
|
||||
...
|
||||
lv_indev_t * mouse_indev = lv_indev_create();
|
||||
...
|
||||
LV_IMAGE_DECLARE(mouse_cursor_icon); /*Declare the image source.*/
|
||||
lv_obj_t * cursor_obj = lv_image_create(lv_screen_active()); /*Create an image object for the cursor */
|
||||
lv_image_set_src(cursor_obj, &mouse_cursor_icon); /*Set the image source*/
|
||||
lv_indev_set_cursor(mouse_indev, cursor_obj); /*Connect the image object to the driver*/
|
||||
|
||||
Note that the cursor object should have
|
||||
:cpp:expr:`lv_obj_remove_flag(cursor_obj, LV_OBJ_FLAG_CLICKABLE)`. For images,
|
||||
*clicking* is disabled by default.
|
||||
|
||||
.. _indev_gestures:
|
||||
|
||||
Gestures
|
||||
--------
|
||||
|
||||
Pointer input devices can detect basic gestures. By default, most of the
|
||||
widgets send the gestures to its parent, so finally the gestures can be
|
||||
detected on the screen object in a form of an :cpp:enumerator:`LV_EVENT_GESTURE`
|
||||
event. For example:
|
||||
|
||||
.. code:: c
|
||||
|
||||
void my_event(lv_event_t * e)
|
||||
{
|
||||
lv_obj_t * screen = lv_event_get_current_target(e);
|
||||
lv_dir_t dir = lv_indev_get_gesture_dir(lv_indev_active());
|
||||
switch(dir) {
|
||||
case LV_DIR_LEFT:
|
||||
...
|
||||
break;
|
||||
case LV_DIR_RIGHT:
|
||||
...
|
||||
break;
|
||||
case LV_DIR_TOP:
|
||||
...
|
||||
break;
|
||||
case LV_DIR_BOTTOM:
|
||||
...
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
lv_obj_add_event_cb(screen1, my_event, LV_EVENT_GESTURE, NULL);
|
||||
|
||||
To prevent passing the gesture event to the parent from an object use
|
||||
:cpp:expr:`lv_obj_remove_flag(obj, LV_OBJ_FLAG_GESTURE_BUBBLE)`.
|
||||
|
||||
Note that, gestures are not triggered if an object is being scrolled.
|
||||
|
||||
If you did some action on a gesture you can call
|
||||
:cpp:expr:`lv_indev_wait_release(lv_indev_active())` in the event handler to
|
||||
prevent LVGL sending further input device related events.
|
||||
|
||||
.. _indev_keypad_and_encoder:
|
||||
|
||||
Keypad and encoder
|
||||
******************
|
||||
|
||||
You can fully control the user interface without a touchpad or mouse by
|
||||
using a keypad or encoder(s). It works similar to the *TAB* key on the
|
||||
PC to select an element in an application or a web page.
|
||||
|
||||
.. _indev_groups:
|
||||
|
||||
Groups
|
||||
------
|
||||
|
||||
Objects you want to control with a keypad or encoder need to be added to
|
||||
a *Group*. In every group there is exactly one focused object which
|
||||
receives the pressed keys or the encoder actions. For example, if a
|
||||
:ref:`Text area <lv_textarea>` is focused and you press some letter
|
||||
on a keyboard, the keys will be sent and inserted into the text area.
|
||||
Similarly, if a :ref:`Slider <lv_slider>` is focused and you press
|
||||
the left or right arrows, the slider's value will be changed.
|
||||
|
||||
You need to associate an input device with a group. An input device can
|
||||
send key events to only one group but a group can receive data from more
|
||||
than one input device.
|
||||
|
||||
To create a group use :cpp:expr:`lv_group_t * g = lv_group_create()` and to add
|
||||
an object to the group use :cpp:expr:`lv_group_add_obj(g, obj)`.
|
||||
|
||||
To associate a group with an input device use
|
||||
:cpp:expr:`lv_indev_set_group(indev, g)`.
|
||||
|
||||
.. _indev_keys:
|
||||
|
||||
Keys
|
||||
^^^^
|
||||
|
||||
There are some predefined keys which have special meaning:
|
||||
|
||||
- :cpp:enumerator:`LV_KEY_NEXT`: Focus on the next object
|
||||
- :cpp:enumerator:`LV_KEY_PREV`: Focus on the previous object
|
||||
- :cpp:enumerator:`LV_KEY_ENTER`: Triggers :cpp:enumerator:`LV_EVENT_PRESSED`, :cpp:enumerator:`LV_EVENT_CLICKED`, or :cpp:enumerator:`LV_EVENT_LONG_PRESSED` etc. events
|
||||
- :cpp:enumerator:`LV_KEY_UP`: Increase value or move upwards
|
||||
- :cpp:enumerator:`LV_KEY_DOWN`: Decrease value or move downwards
|
||||
- :cpp:enumerator:`LV_KEY_RIGHT`: Increase value or move to the right
|
||||
- :cpp:enumerator:`LV_KEY_LEFT`: Decrease value or move to the left
|
||||
- :cpp:enumerator:`LV_KEY_ESC`: Close or exit (E.g. close a :ref:`Drop down list <lv_dropdown>`)
|
||||
- :cpp:enumerator:`LV_KEY_DEL`: Delete (E.g. a character on the right in a :ref:`Text area <lv_textarea>`)
|
||||
- :cpp:enumerator:`LV_KEY_BACKSPACE`: Delete a character on the left (E.g. in a :ref:`Text area <lv_textarea>`)
|
||||
- :cpp:enumerator:`LV_KEY_HOME`: Go to the beginning/top (E.g. in a :ref:`Text area <lv_textarea>`)
|
||||
- :cpp:enumerator:`LV_KEY_END`: Go to the end (E.g. in a :ref:`Text area <lv_textarea>`)
|
||||
|
||||
The most important special keys in your :cpp:func:`read_cb` function are:
|
||||
|
||||
- :cpp:enumerator:`LV_KEY_NEXT`
|
||||
- :cpp:enumerator:`LV_KEY_PREV`
|
||||
- :cpp:enumerator:`LV_KEY_ENTER`
|
||||
- :cpp:enumerator:`LV_KEY_UP`
|
||||
- :cpp:enumerator:`LV_KEY_DOWN`
|
||||
- :cpp:enumerator:`LV_KEY_LEFT`
|
||||
- :cpp:enumerator:`LV_KEY_RIGHT`
|
||||
|
||||
You should translate some of your keys to these special keys to support navigation
|
||||
in a group and interact with selected objects.
|
||||
|
||||
Usually, it's enough to use only :cpp:enumerator:`LV_KEY_LEFT` and :cpp:enumerator:`LV_KEY_RIGHT` because most
|
||||
objects can be fully controlled with them.
|
||||
|
||||
With an encoder you should use only :cpp:enumerator:`LV_KEY_LEFT`, :cpp:enumerator:`LV_KEY_RIGHT`,
|
||||
and :cpp:enumerator:`LV_KEY_ENTER`.
|
||||
|
||||
Edit and navigate mode
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Since a keypad has plenty of keys, it's easy to navigate between objects
|
||||
and edit them using the keypad. But encoders have a limited number of
|
||||
"keys" and hence it is difficult to navigate using the default options.
|
||||
*Navigate* and *Edit* modes are used to avoid this problem with
|
||||
encoders.
|
||||
|
||||
In *Navigate* mode, an encoder's :cpp:enumerator:`LV_KEY_LEFT` or :cpp:enumerator:`LV_KEY_RIGHT` is translated to
|
||||
:cpp:enumerator:`LV_KEY_NEXT` or :cpp:enumerator:`LV_KEY_PREV`. Therefore, the next or previous object will be
|
||||
selected by turning the encoder. Pressing :cpp:enumerator:`LV_KEY_ENTER` will change
|
||||
to *Edit* mode.
|
||||
|
||||
In *Edit* mode, :cpp:enumerator:`LV_KEY_NEXT` and :cpp:enumerator:`LV_KEY_PREV` is usually used to modify an
|
||||
object. Depending on the object's type, a short or long press of
|
||||
:cpp:enumerator:`LV_KEY_ENTER` changes back to *Navigate* mode. Usually, an object
|
||||
which cannot be pressed (like a :ref:`Slider <lv_slider>`) leaves
|
||||
*Edit* mode upon a short click. But with objects where a short click has
|
||||
meaning (e.g. :ref:`Button <lv_button>`), a long press is required.
|
||||
|
||||
Default group
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
Interactive widgets - such as buttons, checkboxes, sliders, etc. - can
|
||||
be automatically added to a default group. Just create a group with
|
||||
:cpp:expr:`lv_group_t * g = lv_group_create()` and set the default group with
|
||||
:cpp:expr:`lv_group_set_default(g)`
|
||||
|
||||
Don't forget to assign one or more input devices to the default group
|
||||
with :cpp:expr:`lv_indev_set_group(my_indev, g)`.
|
||||
|
||||
Styling
|
||||
-------
|
||||
|
||||
If an object is focused either by clicking it via touchpad or focused
|
||||
via an encoder or keypad it goes to the :cpp:enumerator:`LV_STATE_FOCUSED` state.
|
||||
Hence, focused styles will be applied to it.
|
||||
|
||||
If an object switches to edit mode it enters the
|
||||
:cpp:expr:`LV_STATE_FOCUSED | LV_STATE_EDITED` states so these style properties
|
||||
will be shown.
|
||||
|
||||
For a more detailed description read the
|
||||
`Style <https://docs.lvgl.io/master/overview/style.html>`__ section.
|
||||
|
||||
|
||||
.. _indev_api:
|
||||
|
||||
API
|
||||
***
|
||||
29
libraries/lvgl/docs/overview/index.rst
Normal file
29
libraries/lvgl/docs/overview/index.rst
Normal file
@@ -0,0 +1,29 @@
|
||||
.. _overview:
|
||||
|
||||
========
|
||||
Overview
|
||||
========
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
obj
|
||||
coord
|
||||
style
|
||||
style-props
|
||||
scroll
|
||||
layer
|
||||
event
|
||||
indev
|
||||
display
|
||||
color
|
||||
font
|
||||
image
|
||||
fs
|
||||
animations
|
||||
timer
|
||||
profiler
|
||||
renderers/index
|
||||
new_widget
|
||||
vg_lite_tvg
|
||||
153
libraries/lvgl/docs/overview/layer.rst
Normal file
153
libraries/lvgl/docs/overview/layer.rst
Normal file
@@ -0,0 +1,153 @@
|
||||
.. _layers:
|
||||
|
||||
======
|
||||
Layers
|
||||
======
|
||||
|
||||
In LVGL "layers" can be interpreted in various ways:
|
||||
|
||||
1. The order of widget creation naturally creates a layering of widgets
|
||||
2. Permanent screen-sized layers can be also used
|
||||
3. For some draw operations LVGL renders a widget and all its children into a buffer (a.k.a. layer) first
|
||||
|
||||
.. _layers_creation:
|
||||
|
||||
Order of creation
|
||||
*****************
|
||||
|
||||
By default, LVGL draws new objects on top of old objects.
|
||||
|
||||
For example, assume we add a button to a parent object named button1 and
|
||||
then another button named button2. Then button1 (along with its child
|
||||
object(s)) will be in the background and can be covered by button2 and
|
||||
its children.
|
||||
|
||||
.. image:: /misc/layers.png
|
||||
|
||||
.. code:: c
|
||||
|
||||
/*Create a screen*/
|
||||
lv_obj_t * scr = lv_obj_create(NULL, NULL);
|
||||
lv_screen_load(scr); /*Load the screen*/
|
||||
|
||||
/*Create 2 buttons*/
|
||||
lv_obj_t * btn1 = lv_button_create(scr, NULL); /*Create a button on the screen*/
|
||||
lv_button_set_fit(btn1, true, true); /*Enable automatically setting the size according to content*/
|
||||
lv_obj_set_pos(btn1, 60, 40); /*Set the position of the button*/
|
||||
|
||||
lv_obj_t * btn2 = lv_button_create(scr, btn1); /*Copy the first button*/
|
||||
lv_obj_set_pos(btn2, 180, 80); /*Set the position of the button*/
|
||||
|
||||
/*Add labels to the buttons*/
|
||||
lv_obj_t * label1 = lv_label_create(btn1, NULL); /*Create a label on the first button*/
|
||||
lv_label_set_text(label1, "Button 1"); /*Set the text of the label*/
|
||||
|
||||
lv_obj_t * label2 = lv_label_create(btn2, NULL); /*Create a label on the second button*/
|
||||
lv_label_set_text(label2, "Button 2"); /*Set the text of the label*/
|
||||
|
||||
/*Delete the second label*/
|
||||
lv_obj_delete(label2);
|
||||
|
||||
.. _layers_order:
|
||||
|
||||
Change order
|
||||
------------
|
||||
|
||||
There are four explicit ways to bring an object to the foreground:
|
||||
|
||||
- Use :cpp:expr:`lv_obj_move_foreground(obj)` to bring an object to the foreground.
|
||||
Similarly, use :cpp:expr:`lv_obj_move_background(obj)` to move it to the background.
|
||||
- Use :cpp:expr:`lv_obj_move_to_index(obj, idx)` to move an object to a given index in the order of children.
|
||||
|
||||
- ``0``: background
|
||||
- ``child_num - 1``: foreground
|
||||
- ``< 0``: count from the top, to move forward (up): :cpp:expr:`lv_obj_move_to_index(obj, lv_obj_get_index(obj) - 1)`
|
||||
|
||||
- Use :cpp:expr:`lv_obj_swap(obj1, obj2)` to swap the relative layer position of two objects.
|
||||
- When :cpp:expr:`lv_obj_set_parent(obj, new_parent)` is used, ``obj`` will be on the foreground of the ``new_parent``.
|
||||
|
||||
Screen-like layers
|
||||
******************
|
||||
.. _layers_top_and_sys:
|
||||
|
||||
Top and sys layers
|
||||
------------------
|
||||
|
||||
LVGL uses two special layers named ``layer_top`` and ``layer_sys``. Both
|
||||
are visible and common on all screens of a display. **They are not,
|
||||
however, shared among multiple physical displays.** The ``layer_top`` is
|
||||
always on top of the default screen (:cpp:func:`lv_screen_active`), and
|
||||
``layer_sys`` is on top of ``layer_top``.
|
||||
|
||||
The get these layers use :cpp:func:`lv_layer_top` and :cpp:func:`lv_layer_sys`.
|
||||
|
||||
These layers work like any other widget, meaning the can be styles, scrolled,
|
||||
and any kind of widgets can be created on them.
|
||||
|
||||
The ``layer_top`` can be used by the user to create some content visible
|
||||
everywhere. For example, a menu bar, a pop-up, etc. If the ``click``
|
||||
attribute is enabled, then ``layer_top`` will absorb all user clicks and
|
||||
acts as a modal.
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_obj_add_flag(lv_layer_top(), LV_OBJ_FLAG_CLICKABLE);
|
||||
|
||||
The ``layer_sys`` is also used for similar purposes in LVGL. For
|
||||
example, it places the mouse cursor above all layers to be sure it's
|
||||
always visible.
|
||||
|
||||
.. _layers_bottom:
|
||||
|
||||
Bottom layers
|
||||
-------------
|
||||
|
||||
Similarly top and sys. layer bottom layer is also screen size but
|
||||
it's located below the active screen. It's visible only if the active screen's
|
||||
background opacity is < 255.
|
||||
|
||||
The get the bottom layer use :cpp:func:`lv_layer_bottom`.
|
||||
|
||||
Draw layers
|
||||
***********
|
||||
|
||||
Some style properties make LVGL to allocate a buffer and render a widget and its children there first. Later that layer will be merged to the screen or its parent layer after applying some transformations or other modifications.
|
||||
|
||||
Simple layer
|
||||
------------
|
||||
|
||||
The following style properties trigger the creation of a "Simple layer":
|
||||
|
||||
- ``opa_layered``
|
||||
- ``bitmap_mask_src``
|
||||
- ``blend_mode``
|
||||
|
||||
|
||||
In this case widget will be sliced into ``LV_DRAW_SW_LAYER_SIMPLE_BUF_SIZE`` sized chunks.
|
||||
|
||||
If there is no memory for a new chunk, LVGL will try allocating layer when another chunk is rendered and freed.
|
||||
|
||||
|
||||
Transformed layer
|
||||
---------------
|
||||
|
||||
When the widget is transformed a larger part of the widget needs to rendered to provide enough data for transformation. LVGL tries to render as small area of the widget as possible, but due to the nature of transformations no slicing is possible in this case.
|
||||
|
||||
|
||||
The following style properties trigger the creation of a "Transform layer":
|
||||
|
||||
- ``transform_scale_x``
|
||||
- ``transform_scale_y``
|
||||
- ``transform_skew_x``
|
||||
- ``transform_skew_y``
|
||||
- ``transform_rotate``
|
||||
|
||||
Clip corner
|
||||
-----------
|
||||
|
||||
The ``clip_corner`` style property also makes LVGL to create a 2 layers with radius height for the top and bottom part of the widget.
|
||||
|
||||
.. _layers_api:
|
||||
|
||||
API
|
||||
***
|
||||
5
libraries/lvgl/docs/overview/new_widget.rst
Normal file
5
libraries/lvgl/docs/overview/new_widget.rst
Normal file
@@ -0,0 +1,5 @@
|
||||
.. _new_widget:
|
||||
|
||||
==========
|
||||
New widget
|
||||
==========
|
||||
348
libraries/lvgl/docs/overview/obj.rst
Normal file
348
libraries/lvgl/docs/overview/obj.rst
Normal file
@@ -0,0 +1,348 @@
|
||||
.. _objects:
|
||||
|
||||
=======
|
||||
Objects
|
||||
=======
|
||||
|
||||
In LVGL the **basic building blocks** of a user interface are the
|
||||
objects, also called *Widgets*. For example a
|
||||
:ref:`Button <lv_button>`, :ref:`Label <lv_label>`,
|
||||
:ref:`Image <lv_image>`, :ref:`List <lv_list>`,
|
||||
:ref:`Chart <lv_chart>` or :ref:`Text area <lv_textarea>`.
|
||||
|
||||
You can see all the :ref:`Object types <widgets>` here.
|
||||
|
||||
All objects are referenced using an :cpp:type:`lv_obj_t` pointer as a handle.
|
||||
This pointer can later be used to set or get the attributes of the
|
||||
object.
|
||||
|
||||
.. _objects_attributes:
|
||||
|
||||
Attributes
|
||||
**********
|
||||
|
||||
Basic attributes
|
||||
----------------
|
||||
|
||||
All object types share some basic attributes:
|
||||
|
||||
- Position
|
||||
- Size
|
||||
- Parent
|
||||
- Styles
|
||||
- Event handlers
|
||||
- Etc
|
||||
|
||||
You can set/get these attributes with ``lv_obj_set_...`` and
|
||||
``lv_obj_get_...`` functions. For example:
|
||||
|
||||
.. code:: c
|
||||
|
||||
/*Set basic object attributes*/
|
||||
lv_obj_set_size(btn1, 100, 50); /*Set a button's size*/
|
||||
lv_obj_set_pos(btn1, 20,30); /*Set a button's position*/
|
||||
|
||||
To see all the available functions visit the :ref:`Base object's documentation <lv_obj>`.
|
||||
|
||||
Specific attributes
|
||||
-------------------
|
||||
|
||||
The object types have special attributes too. For example, a slider has
|
||||
|
||||
- Minimum and maximum values
|
||||
- Current value
|
||||
|
||||
For these special attributes, every object type may have unique API
|
||||
functions. For example for a slider:
|
||||
|
||||
.. code:: c
|
||||
|
||||
/*Set slider specific attributes*/
|
||||
lv_slider_set_range(slider1, 0, 100); /*Set the min. and max. values*/
|
||||
lv_slider_set_value(slider1, 40, LV_ANIM_ON); /*Set the current value (position)*/
|
||||
|
||||
The API of the widgets is described in their
|
||||
:ref:`Documentation <widgets>` but you can also check the respective
|
||||
header files (e.g. *widgets/lv_slider.h*)
|
||||
|
||||
.. _objects_working_mechanisms:
|
||||
|
||||
Working mechanisms
|
||||
******************
|
||||
|
||||
Parent-child structure
|
||||
----------------------
|
||||
|
||||
A parent object can be considered as the container of its children.
|
||||
Every object has exactly one parent object (except screens), but a
|
||||
parent can have any number of children. There is no limitation for the
|
||||
type of the parent but there are objects which are typically a parent
|
||||
(e.g. button) or a child (e.g. label).
|
||||
|
||||
Moving together
|
||||
---------------
|
||||
|
||||
If the position of a parent changes, the children will move along with
|
||||
it. Therefore, all positions are relative to the parent.
|
||||
|
||||
.. image:: /misc/par_child1.png
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_obj_t * parent = lv_obj_create(lv_screen_active()); /*Create a parent object on the current screen*/
|
||||
lv_obj_set_size(parent, 100, 80); /*Set the size of the parent*/
|
||||
|
||||
lv_obj_t * obj1 = lv_obj_create(parent); /*Create an object on the previously created parent object*/
|
||||
lv_obj_set_pos(obj1, 10, 10); /*Set the position of the new object*/
|
||||
|
||||
Modify the position of the parent:
|
||||
|
||||
.. image:: /misc/par_child2.png
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_obj_set_pos(parent, 50, 50); /*Move the parent. The child will move with it.*/
|
||||
|
||||
(For simplicity the adjusting of colors of the objects is not shown in
|
||||
the example.)
|
||||
|
||||
Visibility only on the parent
|
||||
-----------------------------
|
||||
|
||||
If a child is partially or fully outside its parent then the parts
|
||||
outside will not be visible.
|
||||
|
||||
.. image:: /misc/par_child3.png
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_obj_set_x(obj1, -30); /*Move the child a little bit off the parent*/
|
||||
|
||||
This behavior can be overwritten with
|
||||
:cpp:expr:`lv_obj_add_flag(obj, LV_OBJ_FLAG_OVERFLOW_VISIBLE)` which allow the
|
||||
children to be drawn out of the parent.
|
||||
|
||||
Create and delete objects
|
||||
-------------------------
|
||||
|
||||
In LVGL, objects can be created and deleted dynamically at run time. It
|
||||
means only the currently created (existing) objects consume RAM.
|
||||
|
||||
This allows for the creation of a screen just when a button is clicked
|
||||
to open it, and for deletion of screens when a new screen is loaded.
|
||||
|
||||
UIs can be created based on the current environment of the device. For
|
||||
example one can create meters, charts, bars and sliders based on the
|
||||
currently attached sensors.
|
||||
|
||||
Every widget has its own **create** function with a prototype like this:
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_obj_t * lv_<widget>_create(lv_obj_t * parent, <other parameters if any>);
|
||||
|
||||
Typically, the create functions only have a *parent* parameter telling
|
||||
them on which object to create the new widget.
|
||||
|
||||
The return value is a pointer to the created object with :cpp:type:`lv_obj_t` ``*``
|
||||
type.
|
||||
|
||||
There is a common **delete** function for all object types. It deletes
|
||||
the object and all of its children.
|
||||
|
||||
.. code:: c
|
||||
|
||||
void lv_obj_delete(lv_obj_t * obj);
|
||||
|
||||
:cpp:func:`lv_obj_delete` will delete the object immediately. If for any reason you
|
||||
can't delete the object immediately you can use
|
||||
:cpp:expr:`lv_obj_delete_async(obj)` which will perform the deletion on the next
|
||||
call of :cpp:func:`lv_timer_handler`. This is useful e.g. if you want to
|
||||
delete the parent of an object in the child's :cpp:enumerator:`LV_EVENT_DELETE`
|
||||
handler.
|
||||
|
||||
You can remove all the children of an object (but not the object itself)
|
||||
using :cpp:expr:`lv_obj_clean(obj)`.
|
||||
|
||||
You can use :cpp:expr:`lv_obj_delete_delayed(obj, 1000)` to delete an object after
|
||||
some time. The delay is expressed in milliseconds.
|
||||
|
||||
Sometimes you're not sure whether an object was deleted and you need some way to
|
||||
check if it's still "alive". Anytime before the object is deleted, you can use
|
||||
cpp:expr:`lv_obj_null_on_delete(&obj)` to cause your object pointer to be set to ``NULL``
|
||||
when the object is deleted.
|
||||
|
||||
Make sure the pointer variable itself stays valid until the object is deleted. Here
|
||||
is an example:
|
||||
|
||||
.. code:: c
|
||||
|
||||
void some_timer_callback(lv_timer_t * t)
|
||||
{
|
||||
static lv_obj_t * my_label;
|
||||
if(my_label == NULL) {
|
||||
my_label = lv_label_create(lv_screen_active());
|
||||
lv_obj_delete_delayed(my_label, 1000);
|
||||
lv_obj_null_on_delete(&my_label);
|
||||
}
|
||||
else {
|
||||
lv_obj_set_x(my_label, lv_obj_get_x(my_label) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
.. _objects_screens:
|
||||
|
||||
Screens
|
||||
*******
|
||||
|
||||
Create screens
|
||||
--------------
|
||||
|
||||
The screens are special objects which have no parent object. So they can
|
||||
be created like:
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_obj_t * scr1 = lv_obj_create(NULL);
|
||||
|
||||
Screens can be created with any object type. For example, a
|
||||
:ref:`Base object <lv_obj>` or an image to make a wallpaper.
|
||||
|
||||
Get the active screen
|
||||
---------------------
|
||||
|
||||
There is always an active screen on each display. By default, the
|
||||
library creates and loads a "Base object" as a screen for each display.
|
||||
|
||||
To get the currently active screen use the :cpp:func:`lv_screen_active` function.
|
||||
|
||||
.. _objects_load_screens:
|
||||
|
||||
Load screens
|
||||
------------
|
||||
|
||||
To load a new screen, use :cpp:expr:`lv_screen_load(scr1)`.
|
||||
|
||||
Layers
|
||||
------
|
||||
|
||||
There are two automatically generated layers:
|
||||
|
||||
- top layer
|
||||
- system layer
|
||||
|
||||
They are independent of the screens and they will be shown on every
|
||||
screen. The *top layer* is above every object on the screen and the
|
||||
*system layer* is above the *top layer*. You can add any pop-up windows
|
||||
to the *top layer* freely. But, the *system layer* is restricted to
|
||||
system-level things (e.g. mouse cursor will be placed there with
|
||||
:cpp:func:`lv_indev_set_cursor`).
|
||||
|
||||
The :cpp:func:`lv_layer_top` and :cpp:func:`lv_layer_sys` functions return pointers
|
||||
to the top and system layers respectively.
|
||||
|
||||
Read the :ref:`Layer overview <layers>` section to learn more
|
||||
about layers.
|
||||
|
||||
Load screen with animation
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
A new screen can be loaded with animation by using
|
||||
:cpp:expr:`lv_screen_load_anim(scr, transition_type, time, delay, auto_del)`. The
|
||||
following transition types exist:
|
||||
|
||||
- :cpp:enumerator:`LV_SCR_LOAD_ANIM_NONE`: Switch immediately after ``delay`` milliseconds
|
||||
- :cpp:enumerator:`LV_SCR_LOAD_ANIM_OVER_LEFT`, :cpp:enumerator:`LV_SCR_LOAD_ANIM_OVER_RIGHT`, :cpp:enumerator:`LV_SCR_LOAD_ANIM_OVER_TOP` and :cpp:enumerator:`LV_SCR_LOAD_ANIM_OVER_BOTTOM`: Move the new screen over the current towards the given direction
|
||||
- :cpp:enumerator:`LV_SCR_LOAD_ANIM_OUT_LEFT`, :cpp:enumerator:`LV_SCR_LOAD_ANIM_OUT_RIGHT`, :cpp:enumerator:`LV_SCR_LOAD_ANIM_OUT_TOP` and :cpp:enumerator:`LV_SCR_LOAD_ANIM_OUT_BOTTOM`: Move out the old screen over the current towards the given direction
|
||||
- :cpp:enumerator:`LV_SCR_LOAD_ANIM_MOVE_LEFT`, :cpp:enumerator:`LV_SCR_LOAD_ANIM_MOVE_RIGHT`, :cpp:enumerator:`LV_SCR_LOAD_ANIM_MOVE_TOP` and :cpp:enumerator:`LV_SCR_LOAD_ANIM_MOVE_BOTTOM`: Move both the current and new screens towards the given direction
|
||||
- :cpp:enumerator:`LV_SCR_LOAD_ANIM_FADE_IN` and :cpp:enumerator:`LV_SCR_LOAD_ANIM_FADE_OUT`: Fade the new screen over the old screen, or vice versa
|
||||
|
||||
Setting ``auto_del`` to ``true`` will automatically delete the old
|
||||
screen when the animation is finished.
|
||||
|
||||
The new screen will become active (returned by :cpp:func:`lv_screen_active`) when
|
||||
the animation starts after ``delay`` time. All inputs are disabled
|
||||
during the screen animation.
|
||||
|
||||
Handling multiple displays
|
||||
--------------------------
|
||||
|
||||
Screens are created on the currently selected *default display*. The
|
||||
*default display* is the last registered display with
|
||||
:cpp:func:`lv_display_create`. You can also explicitly select a new default
|
||||
display using :cpp:expr:`lv_display_set_default(disp)`.
|
||||
|
||||
:cpp:func:`lv_screen_active`, :cpp:func:`lv_screen_load` and :cpp:func:`lv_screen_load_anim` operate
|
||||
on the default display.
|
||||
|
||||
Visit :ref:`display_multi_display_support` to learn more.
|
||||
|
||||
.. _objects_parts:
|
||||
|
||||
Parts
|
||||
*****
|
||||
|
||||
The widgets are built from multiple parts. For example a
|
||||
:ref:`Base object <lv_obj>` uses the main and scrollbar parts but a
|
||||
:ref:`Slider <lv_slider>` uses the main, indicator and knob parts.
|
||||
Parts are similar to *pseudo-elements* in CSS.
|
||||
|
||||
The following predefined parts exist in LVGL:
|
||||
|
||||
- :cpp:enumerator:`LV_PART_MAIN`: A background like rectangle
|
||||
- :cpp:enumerator:`LV_PART_SCROLLBAR`: The scrollbar(s)
|
||||
- :cpp:enumerator:`LV_PART_INDICATOR`: Indicator, e.g. for slider, bar, switch, or the tick box of the checkbox
|
||||
- :cpp:enumerator:`LV_PART_KNOB`: Like a handle to grab to adjust the value
|
||||
- :cpp:enumerator:`LV_PART_SELECTED`: Indicate the currently selected option or section
|
||||
- :cpp:enumerator:`LV_PART_ITEMS`: Used if the widget has multiple similar elements (e.g. table cells)
|
||||
- :cpp:enumerator:`LV_PART_CURSOR`: Mark a specific place e.g. text area's or chart's cursor
|
||||
- :cpp:enumerator:`LV_PART_CUSTOM_FIRST`: Custom parts can be added from here.
|
||||
|
||||
The main purpose of parts is to allow styling the "components" of the
|
||||
widgets. They are described in more detail in the
|
||||
:ref:`Style overview <styles>` section.
|
||||
|
||||
.. _objects_states:
|
||||
|
||||
States
|
||||
******
|
||||
|
||||
The object can be in a combination of the following states:
|
||||
|
||||
- :cpp:enumerator:`LV_STATE_DEFAULT`: Normal, released state
|
||||
- :cpp:enumerator:`LV_STATE_CHECKED`: Toggled or checked state
|
||||
- :cpp:enumerator:`LV_STATE_FOCUSED`: Focused via keypad or encoder or clicked via touchpad/mouse
|
||||
- :cpp:enumerator:`LV_STATE_FOCUS_KEY`: Focused via keypad or encoder but not via touchpad/mouse
|
||||
- :cpp:enumerator:`LV_STATE_EDITED`: Edit by an encoder
|
||||
- :cpp:enumerator:`LV_STATE_HOVERED`: Hovered by mouse (not supported now)
|
||||
- :cpp:enumerator:`LV_STATE_PRESSED`: Being pressed
|
||||
- :cpp:enumerator:`LV_STATE_SCROLLED`: Being scrolled
|
||||
- :cpp:enumerator:`LV_STATE_DISABLED`: Disabled state
|
||||
- :cpp:enumerator:`LV_STATE_USER_1`: Custom state
|
||||
- :cpp:enumerator:`LV_STATE_USER_2`: Custom state
|
||||
- :cpp:enumerator:`LV_STATE_USER_3`: Custom state
|
||||
- :cpp:enumerator:`LV_STATE_USER_4`: Custom state
|
||||
|
||||
The states are usually automatically changed by the library as the user
|
||||
interacts with an object (presses, releases, focuses, etc.). However,
|
||||
the states can be changed manually too. To set or clear given state (but
|
||||
leave the other states untouched) use
|
||||
``lv_obj_add/remove_state(obj, LV_STATE_...)`` In both cases OR-ed state
|
||||
values can be used as well. E.g.
|
||||
:cpp:expr:`lv_obj_add_state(obj, part, LV_STATE_PRESSED | LV_PRESSED_CHECKED)`.
|
||||
|
||||
To learn more about the states read the related section of the
|
||||
:ref:`Style overview <styles>`.
|
||||
|
||||
.. _objects_snapshot:
|
||||
|
||||
Snapshot
|
||||
********
|
||||
|
||||
A snapshot image can be generated for an object together with its
|
||||
children. Check details in :ref:`snapshot`.
|
||||
|
||||
.. _objects_api:
|
||||
|
||||
API
|
||||
***
|
||||
242
libraries/lvgl/docs/overview/profiler.rst
Normal file
242
libraries/lvgl/docs/overview/profiler.rst
Normal file
@@ -0,0 +1,242 @@
|
||||
.. _profiler:
|
||||
|
||||
========
|
||||
Profiler
|
||||
========
|
||||
|
||||
As the complexity of the application increases, performance issues such as low FPS and frequent cache misses
|
||||
causing lag may arise. LVGL has internally set up some hooks for performance measurement to help developers
|
||||
analyze and locate performance issues.
|
||||
|
||||
.. _profiler_introduction:
|
||||
|
||||
Introduction
|
||||
************
|
||||
|
||||
LVGL has a built-in trace system to track and record the timestamps of important events that occur during runtime,
|
||||
such as rendering events and user input events. These event timestamps serve as important metrics for performance analysis.
|
||||
|
||||
The trace system has a configurable record buffer that stores the names of event functions and their timestamps.
|
||||
When the buffer is full, the trace system prints the log information through the provided user interface.
|
||||
|
||||
The output trace logs are formatted according to Android's `systrace <https://developer.android.com/topic/performance/tracing>`_
|
||||
format and can be visualized using `Perfetto <https://ui.perfetto.dev>`_.
|
||||
|
||||
.. _profiler_usage:
|
||||
|
||||
Usage
|
||||
*****
|
||||
|
||||
Configure profiler
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
To enable the profiler, set :c:macro:`LV_USE_PROFILER` in ``lv_conf.h`` and configure the following options:
|
||||
|
||||
1. Enable the built-in profiler functionality by setting :c:macro:`LV_USE_PROFILER_BUILTIN`.
|
||||
|
||||
2. Buffer configuration: Set the value of :c:macro:`LV_PROFILER_BUILTIN_BUF_SIZE` to configure the buffer size. A larger buffer can store more trace event information, reducing interference with rendering. However, it also results in higher memory consumption.
|
||||
|
||||
3. Timestamp configuration: LVGL uses the :cpp:func:`lv_tick_get` function with a precision of 1ms by default to obtain timestamps when events occur. Therefore, it cannot accurately measure intervals below 1ms. If your system environment can provide higher precision (e.g., 1us), you can configure the profiler as follows:
|
||||
|
||||
- Recommended configuration in **UNIX** environments:
|
||||
|
||||
.. code:: c
|
||||
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
|
||||
static uint32_t my_get_tick_us_cb(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
|
||||
}
|
||||
|
||||
static int my_get_tid_cb(void)
|
||||
{
|
||||
return (int)syscall(SYS_gettid);
|
||||
}
|
||||
|
||||
static int my_get_cpu_cb(void)
|
||||
{
|
||||
int cpu_id = 0;
|
||||
syscall(SYS_getcpu, &cpu_id, NULL);
|
||||
return cpu_id;
|
||||
}
|
||||
|
||||
void my_profiler_init(void)
|
||||
{
|
||||
lv_profiler_builtin_config_t config;
|
||||
lv_profiler_builtin_config_init(&config);
|
||||
config.tick_per_sec = 1000000; /* One second is equal to 1000000 microseconds */
|
||||
config.tick_get_cb = my_get_tick_us_cb;
|
||||
config.tid_get_cb = my_get_tid_cb;
|
||||
config.cpu_get_cb = my_get_cpu_cb;
|
||||
lv_profiler_builtin_init(&config);
|
||||
}
|
||||
|
||||
- Recommended configuration in **Arduino** environments:
|
||||
|
||||
.. code:: c
|
||||
|
||||
void my_profiler_init(void)
|
||||
{
|
||||
lv_profiler_builtin_config_t config;
|
||||
lv_profiler_builtin_config_init(&config);
|
||||
config.tick_per_sec = 1000000; /* One second is equal to 1000000 microseconds */
|
||||
config.tick_get_cb = micros; /* Use the microsecond time stamp provided by Arduino */
|
||||
lv_profiler_builtin_init(&config);
|
||||
}
|
||||
|
||||
4. Log output configuration: LVGL uses the :cpp:func:`LV_LOG` interface by default to output trace information. If you want to use another interface to output log information (e.g., file stream), you can redirect the log output using the following code:
|
||||
|
||||
.. code:: c
|
||||
|
||||
static void my_log_print_cb(const char * buf)
|
||||
{
|
||||
printf("%s", buf);
|
||||
}
|
||||
|
||||
void my_profiler_init(void)
|
||||
{
|
||||
lv_profiler_builtin_config_t config;
|
||||
lv_profiler_builtin_config_init(&config);
|
||||
... /* other configurations */
|
||||
config.flush_cb = my_log_print_cb;
|
||||
lv_profiler_builtin_init(&config);
|
||||
}
|
||||
|
||||
Run the test scenario
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Run the UI scenario that you want to measure, such as scrolling a scrollable page up and down or entering/exiting an application.
|
||||
|
||||
Process the logs
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
Save the output log as `my_trace.txt`, use `trace_filter.py` for filtering and preprocessing:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
./lvgl/scripts/trace_filter.py my_trace.txt
|
||||
|
||||
or
|
||||
|
||||
.. code:: bash
|
||||
|
||||
python3 ./lvgl/scripts/trace_filter.py my_trace.txt
|
||||
|
||||
You will obtain a processed text file named `trace.systrace`, which roughly contains the following content:
|
||||
|
||||
.. code:: text
|
||||
|
||||
# tracer: nop
|
||||
#
|
||||
LVGL-1 [0] 2892.002993: tracing_mark_write: B|1|lv_timer_handler
|
||||
LVGL-1 [0] 2892.002993: tracing_mark_write: B|1|_lv_display_refr_timer
|
||||
LVGL-1 [0] 2892.003459: tracing_mark_write: B|1|refr_invalid_areas
|
||||
LVGL-1 [0] 2892.003461: tracing_mark_write: B|1|lv_draw_rect
|
||||
LVGL-1 [0] 2892.003550: tracing_mark_write: E|1|lv_draw_rect
|
||||
LVGL-1 [0] 2892.003552: tracing_mark_write: B|1|lv_draw_rect
|
||||
LVGL-1 [0] 2892.003556: tracing_mark_write: E|1|lv_draw_rect
|
||||
LVGL-1 [0] 2892.003560: tracing_mark_write: B|1|lv_draw_rect
|
||||
LVGL-1 [0] 2892.003573: tracing_mark_write: E|1|lv_draw_rect
|
||||
...
|
||||
|
||||
Import the processed `trace.systrace` file into `Perfetto <https://ui.perfetto.dev>`_ and wait for it to be parsed.
|
||||
|
||||
Performance analysis
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If the log parsing is successful, you will see the following screen:
|
||||
|
||||
.. image:: /misc/perfetto_ui.png
|
||||
|
||||
In the Perfetto UI, use the :kbd:`A` or :kbd:`D` keys to pan the timeline horizontally
|
||||
and the :kbd:`W` or :kbd:`S` keys to zoom in or out on the timeline.
|
||||
Use the mouse to move the focus and click on functions on the timeline to observe their execution time.
|
||||
|
||||
Add Measurement Point
|
||||
*********************
|
||||
|
||||
Users can add their own measured functions:
|
||||
|
||||
.. code:: c
|
||||
|
||||
void my_function_1(void)
|
||||
{
|
||||
LV_PROFILER_BEGIN;
|
||||
do_something();
|
||||
LV_PROFILER_END;
|
||||
}
|
||||
|
||||
void my_function_2(void)
|
||||
{
|
||||
LV_PROFILER_BEGIN_TAG("do_something_1");
|
||||
do_something_1();
|
||||
LV_PROFILER_END_TAG("do_something_1");
|
||||
|
||||
LV_PROFILER_BEGIN_TAG("do_something_2");
|
||||
do_something_2();
|
||||
LV_PROFILER_END_TAG("do_something_2");
|
||||
}
|
||||
|
||||
.. _profiler_custom_implementation:
|
||||
|
||||
Custom profiler implementation
|
||||
******************************
|
||||
|
||||
If you wish to use a profiler method provided by your operating system, you can modify the following configurations in ``lv_conf.h``:
|
||||
|
||||
- :c:macro:`LV_PROFILER_INCLUDE`: Provides a header file for the profiler function.
|
||||
- :c:macro:`LV_PROFILER_BEGIN`: Profiler start point function.
|
||||
- :c:macro:`LV_PROFILER_END`: Profiler end point function.
|
||||
- :c:macro:`LV_PROFILER_BEGIN_TAG`: Profiler start point function with custom tag.
|
||||
- :c:macro:`LV_PROFILER_END_TAG`: Profiler end point function with custom tag.
|
||||
|
||||
|
||||
Taking `NuttX <https://github.com/apache/nuttx>`_ RTOS as an example:
|
||||
|
||||
.. code:: c
|
||||
|
||||
#define LV_PROFILER_INCLUDE "nuttx/sched_note.h"
|
||||
#define LV_PROFILER_BEGIN sched_note_begin(NOTE_TAG_ALWAYS)
|
||||
#define LV_PROFILER_END sched_note_end(NOTE_TAG_ALWAYS)
|
||||
#define LV_PROFILER_BEGIN_TAG(str) sched_note_beginex(NOTE_TAG_ALWAYS, str)
|
||||
#define LV_PROFILER_END_TAG(str) sched_note_endex(NOTE_TAG_ALWAYS, str)
|
||||
|
||||
.. _profiler_faq:
|
||||
|
||||
FAQ
|
||||
***
|
||||
|
||||
Perfetto log parsing fails
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Please check the completeness of the logs. If the logs are incomplete, it may be due to the following reasons:
|
||||
|
||||
1. Serial port reception errors caused by a high baud rate. You need to reduce the baud rate.
|
||||
2. Data corruption caused by other thread logs inserted during the printing of trace logs. You need to disable the log output of other threads or refer to the configuration above to use a separate log output interface.
|
||||
3. Make sure that the string passed in by :c:macro:`LV_PROFILER_BEGIN_TAG/END_TAG` is not a local variable on the stack or a string in shared memory, because currently only the string address is recorded and the content is not copied.
|
||||
|
||||
Function execution time displayed as 0s in Perfetto
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If the function execution time is lower than the precision of the timestamps, this situation can occur. You can refer to the configuration instructions above to use a higher precision timestamp.
|
||||
|
||||
Significant stuttering occurs during profiling
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
When the buffer used to store trace events becomes full, the profiler will output all the data in the buffer, which can cause UI blocking and stuttering during the output. You can optimize this by taking the following measures:
|
||||
|
||||
1. Increase the value of :c:macro:`LV_PROFILER_BUILTIN_BUF_SIZE`. A larger buffer can reduce the frequency of log printing, but it also consumes more memory.
|
||||
2. Optimize the execution time of log printing functions, such as increasing the serial port baud rate or improving file writing speed.
|
||||
|
||||
Trace logs are not being output
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If the trace logs are not automatically printed when the buffer is not full, you can try the following methods to force the log output:
|
||||
|
||||
1. Reduce the value of :c:macro:`LV_PROFILER_BUILTIN_BUF_SIZE` to fill the buffer more quickly and trigger automatic printing.
|
||||
2. Manually call or use a timer to call the :cpp:func:`lv_profiler_builtin_flush` function to force the log output.
|
||||
65
libraries/lvgl/docs/overview/renderers/arm2d.rst
Normal file
65
libraries/lvgl/docs/overview/renderers/arm2d.rst
Normal file
@@ -0,0 +1,65 @@
|
||||
.. _arm2d:
|
||||
|
||||
==========
|
||||
Arm-2D GPU
|
||||
==========
|
||||
|
||||
Arm-2D is not a GPU but **an abstraction layer for 2D GPUs dedicated to
|
||||
Microcontrollers**. It supports all Cortex-M processors ranging from
|
||||
Cortex-M0 to the latest Cortex-M85.
|
||||
|
||||
Arm-2D accelerates LVGL9 with two modes: **Synchronous Mode** and
|
||||
**Asynchronous Mode**.
|
||||
- When **Helium** and **ACI (Arm Custom Instruction)** are available, it is recommend
|
||||
to use **Synchronous Mode** to accelerate LVGL.
|
||||
- When Arm-2D backed 2D-GPUs are available, for example, **DMAC-350 based 2D
|
||||
GPUs**, it is recommend to use **Asynchronous Mode** to accelerate LVGL.
|
||||
|
||||
Arm-2D is an open-source project on GitHub. For more, please refer to:
|
||||
https://github.com/ARM-software/Arm-2D.
|
||||
|
||||
How to Use
|
||||
**********
|
||||
|
||||
In general:
|
||||
- you can set the macro :c:macro:`LV_USE_DRAW_ARM2D_SYNC` to ``1`` and
|
||||
:c:macro:`LV_DRAW_SW_ASM` to ``LV_DRAW_SW_ASM_HELIUM`` in ``lv_conf.h`` to
|
||||
enable Arm-2D synchronous acceleration for LVGL.
|
||||
- You can set
|
||||
the macro :c:macro:`LV_USE_DRAW_ARM2D_ASYNC` to ``1`` in ``lv_conf.h`` to enable
|
||||
Arm-2D Asynchronous acceleration for LVGL.
|
||||
|
||||
If you are using
|
||||
`CMSIS-Pack <https://github.com/lvgl/lvgl/tree/master/env_support/cmsis-pack>`__
|
||||
to deploy the LVGL. You don't have to define the macro
|
||||
:c:macro:`LV_USE_DRAW_ARM2D_SYNC` manually, instead the lv_conf_cmsis.h will
|
||||
check the environment and set the :c:macro:`LV_USE_DRAW_ARM2D_SYNC` accordingly.
|
||||
|
||||
Design Considerations
|
||||
*********************
|
||||
|
||||
As mentioned before, Arm-2D is an abstraction layer for 2D GPU; hence if
|
||||
there is no accelerator or dedicated instruction set (such as Helium or
|
||||
ACI) available for Arm-2D, it provides negligible performance boost for
|
||||
LVGL (sometimes worse) for regular Cortex-M processors.
|
||||
|
||||
**We highly recommend you enable Arm-2D acceleration for LVGL** when:
|
||||
|
||||
- The target processors are **Cortex-M55**, **Cortex-M52** and **Cortex-M85**
|
||||
- The target processors support
|
||||
`Helium <https://developer.arm.com/documentation/102102/0103/?lang=en>`__.
|
||||
- The device vendor provides an arm-2d compliant driver for their
|
||||
proprietary 2D accelerators and/or ACI (Arm Customized Instruction).
|
||||
- The target device contains
|
||||
`DMAC-350 <https://community.arm.com/arm-community-blogs/b/internet-of-things-blog/posts/arm-corelink-dma-350-next-generation-direct-memory-access-for-endpoint-ai>`__
|
||||
|
||||
Examples
|
||||
********
|
||||
|
||||
- `A Cortex-M55 (supports Helium) based MDK Project, PC emulation is
|
||||
available. <https://github.com/lvgl/lv_port_an547_cm55_sim>`__
|
||||
|
||||
API
|
||||
***
|
||||
|
||||
:ref:`lv_gpu_arm2d`
|
||||
14
libraries/lvgl/docs/overview/renderers/index.rst
Normal file
14
libraries/lvgl/docs/overview/renderers/index.rst
Normal file
@@ -0,0 +1,14 @@
|
||||
==================
|
||||
Renderers and GPUs
|
||||
==================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
sw
|
||||
sdl
|
||||
arm2d
|
||||
pxp
|
||||
stm32_dma2d
|
||||
vglite
|
||||
vg_lite
|
||||
14
libraries/lvgl/docs/overview/renderers/pxp.rst
Normal file
14
libraries/lvgl/docs/overview/renderers/pxp.rst
Normal file
@@ -0,0 +1,14 @@
|
||||
===========
|
||||
NXP PXP GPU
|
||||
===========
|
||||
|
||||
API
|
||||
---
|
||||
|
||||
:ref:`lv_draw_pxp`
|
||||
|
||||
:ref:`lv_draw_pxp_blend`
|
||||
|
||||
:ref:`lv_gpu_nxp_pxp`
|
||||
|
||||
:ref:`lv_gpu_nxp_pxp_osa`
|
||||
26
libraries/lvgl/docs/overview/renderers/sdl.rst
Normal file
26
libraries/lvgl/docs/overview/renderers/sdl.rst
Normal file
@@ -0,0 +1,26 @@
|
||||
============
|
||||
SDL renderer
|
||||
============
|
||||
|
||||
API
|
||||
---
|
||||
|
||||
:ref:`lv_draw_sdl`
|
||||
|
||||
:ref:`lv_draw_sdl_composite`
|
||||
|
||||
:ref:`lv_draw_sdl_img`
|
||||
|
||||
:ref:`lv_draw_sdl_layer`
|
||||
|
||||
:ref:`lv_draw_sdl_mask`
|
||||
|
||||
:ref:`lv_draw_sdl_priv`
|
||||
|
||||
:ref:`lv_draw_sdl_rect`
|
||||
|
||||
:ref:`lv_draw_sdl_stack_blur`
|
||||
|
||||
:ref:`lv_draw_sdl_texture_cache`
|
||||
|
||||
:ref:`lv_draw_sdl_utils`
|
||||
8
libraries/lvgl/docs/overview/renderers/stm32_dma2d.rst
Normal file
8
libraries/lvgl/docs/overview/renderers/stm32_dma2d.rst
Normal file
@@ -0,0 +1,8 @@
|
||||
=========
|
||||
DMA2D GPU
|
||||
=========
|
||||
|
||||
API
|
||||
---
|
||||
|
||||
:ref:`lv_gpu_stm32_dma2d`
|
||||
14
libraries/lvgl/docs/overview/renderers/sw.rst
Normal file
14
libraries/lvgl/docs/overview/renderers/sw.rst
Normal file
@@ -0,0 +1,14 @@
|
||||
=================
|
||||
Software renderer
|
||||
=================
|
||||
|
||||
API
|
||||
---
|
||||
|
||||
:ref:`lv_draw_sw`
|
||||
|
||||
:ref:`lv_draw_sw_blend`
|
||||
|
||||
:ref:`lv_draw_sw_dither`
|
||||
|
||||
:ref:`lv_draw_sw_gradient`
|
||||
61
libraries/lvgl/docs/overview/renderers/vg_lite.rst
Normal file
61
libraries/lvgl/docs/overview/renderers/vg_lite.rst
Normal file
@@ -0,0 +1,61 @@
|
||||
===================
|
||||
VG-Lite General GPU
|
||||
===================
|
||||
|
||||
This is a generic VG-Lite rendering backend implementation that is designed to utilize
|
||||
`VeriSilicon <https://verisilicon.com/>`_'s generic API to operate GPU hardware as much as possible.
|
||||
|
||||
Even with different chip manufacturers, as long as they use the same version of VG-Lite API as the rendering backend,
|
||||
LVGL rendering acceleration can be supported without the need for LVGL adaptation work.
|
||||
|
||||
Configuration
|
||||
*************
|
||||
|
||||
1. Set :c:macro:`LV_USE_DRAW_VG_LITE` to 1 in ``lv_conf.h`` to enabled the VG-Lite rendering backend.
|
||||
Make sure that your hardware has been adapted to the VG-Lite API and that the absolute path to ``vg_lite.h``, which can be directly referenced by lvgl, has been exposed.
|
||||
|
||||
2. Confirm the GPU initialization method, there are two ways:
|
||||
- The SDK calls the GPU initialization function on its own during system startup, and the GPU is available when LVGL starts; set :c:macro:`LV_VG_LITE_USE_GPU_INIT` to 0.
|
||||
- LVGL actively calls the GPU initialization function, and the SDK needs to implement the public function `gpu_init()`.
|
||||
LVGL will call it to complete the GPU hardware initialization during startup; set :c:macro:`LV_VG_LITE_USE_GPU_INIT` to 1.
|
||||
|
||||
3. Set the :c:macro:`LV_VG_LITE_USE_ASSERT` configuration to enable GPU call parameter checking.
|
||||
Due to the complexity of the parameters used in GPU calls, incorrect parameters can result in abnormal GPU hardware operation, such as forgetting to add an end symbol
|
||||
to the path or not meeting the alignment requirements for buffer stride.
|
||||
To quickly resolve such issues, strict parameter checking has been added before each VG-Lite call, including buffer stride validation and matrix invertibility check.
|
||||
When an error parameter is detected, an assertion will occur to print out the error parameter, allowing the user to promptly make corrections and reduce the time wasted on hardware simulation.
|
||||
Please note that enabling this check will decrease runtime performance. It is recommended to enable it in Debug mode and disable it in the Release version.
|
||||
|
||||
4. Set the :c:macro:`LV_VG_LITE_FLUSH_MAX_COUNT` configuration to specify the flush method.
|
||||
VG-Lite uses two sets of command buffer buffers to render instructions, and utilizing this mechanism well can greatly improve drawing efficiency.
|
||||
Currently, two buffering methods are supported:
|
||||
- Set :c:macro:`LV_VG_LITE_FLUSH_MAX_COUNT` to zero (recommended). The rendering backend will obtain the GPU's working status every time it writes rendering instructions to the command buffer.
|
||||
When the GPU is idle, it will immediately call ``vg_lite_flush`` to notify the GPU to start rendering and swap the command buffer. When the GPU is busy, it will continue to fill the command buffer cache with rendering instructions.
|
||||
The underlying driver will automatically determine if the command buffer has been filled. When it is about to be filled, it will forcibly wait for the unfinished drawing tasks to end and swap the command buffer.
|
||||
This method can effectively improve GPU utilization, especially in scenarios where rendering text, as the GPU's drawing time and the CPU's data preparation time are very close, allowing the CPU and GPU to run in parallel.
|
||||
- Set :c:macro:`LV_VG_LITE_FLUSH_MAX_COUNT` to a value greater than zero, such as 8. After writing 8 rendering instructions to the command buffer, the rendering backend
|
||||
will call ``vg_lite_flush`` to notify the GPU to start rendering and swap the command buffer.
|
||||
|
||||
5. Set the :c:macro:`LV_VG_LITE_USE_BOX_SHADOW` configuration to use GPU rendering for shadows.
|
||||
In fact, GPU hardware does not actually support shadow rendering. However, through experimentation, it has been found that a similar shadow effect
|
||||
can be achieved by using multiple layers of borders with different levels of transparency.
|
||||
It is recommended to enable this configuration in scenarios where the shadow quality requirements are not high, as it can significantly improve rendering efficiency.
|
||||
|
||||
6. Set the :c:macro:`LV_VG_LITE_GRAD_CACHE_CNT` configuration to specify the number of gradient cache entries.
|
||||
Gradient drawing includes linear gradients and radial gradients. Using a cache can effectively reduce the number of times the gradient image is created and improve drawing efficiency.
|
||||
Each individual gradient consumes around 4K of GPU memory pool. If there are many gradients used in the interface, you can try increasing the number of gradient cache entries.
|
||||
If the VG-Lite API returns the :c:macro:`VG_LITE_OUT_OF_RESOURCES` error, you can try increasing the size of the GPU memory pool or reducing the number of gradient cache entries.
|
||||
|
||||
7. Set the :c:macro:`LV_VG_LITE_STROKE_CACHE_CNT` configuration to specify the number of stroke path caches.
|
||||
When the stroke parameters do not change, the previously generated stroke parameters are automatically retrieved from the cache to improve rendering performance.
|
||||
The memory occupied by the stroke is strongly related to the path length. If the VG-Lite API returns the :c:macro:`VG_LITE_OUT_OF_RESOURCES` error,
|
||||
you can try increasing the size of the GPU memory pool or reducing the number of stroke cache entries.
|
||||
|
||||
NOTE: VG-Lite rendering backend does not support multi-threaded calls, please make sure :c:macro:`LV_USE_OS` is always configured as :c:macro:`LV_OS_NONE`.
|
||||
|
||||
VG-Lite Simulator
|
||||
*****************
|
||||
|
||||
LVGL integrates a VG-Lite simulator based on ThorVG.
|
||||
Its purpose is to simplify the debugging of VG-Lite adaptation and reduce the time of debugging and locating problems on hardware devices.
|
||||
For detailed instructions, see `VG-Lite Simulator </overview/vg_lite_tvg>`__.
|
||||
17
libraries/lvgl/docs/overview/renderers/vglite.rst
Normal file
17
libraries/lvgl/docs/overview/renderers/vglite.rst
Normal file
@@ -0,0 +1,17 @@
|
||||
==============
|
||||
NXP VGLite GPU
|
||||
==============
|
||||
|
||||
API
|
||||
---
|
||||
|
||||
:ref:`lv_draw_vglite`
|
||||
|
||||
:ref:`lv_draw_vglite_arc`
|
||||
|
||||
:ref:`lv_draw_vglite_blend`
|
||||
|
||||
:ref:`lv_draw_vglite_line`
|
||||
|
||||
:ref:`lv_draw_vglite_rect`
|
||||
|
||||
290
libraries/lvgl/docs/overview/scroll.rst
Normal file
290
libraries/lvgl/docs/overview/scroll.rst
Normal file
@@ -0,0 +1,290 @@
|
||||
.. _scroll:
|
||||
|
||||
======
|
||||
Scroll
|
||||
======
|
||||
|
||||
Overview
|
||||
********
|
||||
|
||||
In LVGL scrolling works very intuitively: if an object is outside its
|
||||
parent content area (the size without padding), the parent becomes
|
||||
scrollable and scrollbar(s) will appear. That's it.
|
||||
|
||||
Any object can be scrollable including ``lv_obj``, ``lv_image``,
|
||||
``lv_button``, ``lv_meter``, etc
|
||||
|
||||
The object can either be scrolled horizontally or vertically in one
|
||||
stroke; diagonal scrolling is not possible.
|
||||
|
||||
Scrollbar
|
||||
---------
|
||||
|
||||
Mode
|
||||
^^^^
|
||||
|
||||
Scrollbars are displayed according to a configured ``mode``. The
|
||||
following ``mode``\ (s) exist:
|
||||
|
||||
- :cpp:enumerator:`LV_SCROLLBAR_MODE_OFF`: Never show the scrollbars
|
||||
- :cpp:enumerator:`LV_SCROLLBAR_MODE_ON`: Always show the scrollbars
|
||||
- :cpp:enumerator:`LV_SCROLLBAR_MODE_ACTIVE`: Show scroll bars while an object is being scrolled
|
||||
- :cpp:enumerator:`LV_SCROLLBAR_MODE_AUTO`: Show scroll bars when the content is large enough to be scrolled
|
||||
|
||||
``lv_obj_set_scrollbar_mode(obj, LV_SCROLLBAR_MODE_...)`` sets the scrollbar mode on an object.
|
||||
|
||||
Styling
|
||||
^^^^^^^
|
||||
|
||||
The scrollbars have their own dedicated part, called
|
||||
:cpp:enumerator:`LV_PART_SCROLLBAR`. For example a scrollbar can turn to red like
|
||||
this:
|
||||
|
||||
.. code:: c
|
||||
|
||||
static lv_style_t style_red;
|
||||
lv_style_init(&style_red);
|
||||
lv_style_set_bg_color(&style_red, lv_color_red());
|
||||
|
||||
...
|
||||
|
||||
lv_obj_add_style(obj, &style_red, LV_PART_SCROLLBAR);
|
||||
|
||||
An object goes to the :cpp:enumerator:`LV_STATE_SCROLLED` state while it's being
|
||||
scrolled. This allows adding different styles to the scrollbar or the
|
||||
object itself when scrolled. This code makes the scrollbar blue when the
|
||||
object is scrolled:
|
||||
|
||||
.. code:: c
|
||||
|
||||
static lv_style_t style_blue;
|
||||
lv_style_init(&style_blue);
|
||||
lv_style_set_bg_color(&style_blue, lv_color_blue());
|
||||
|
||||
...
|
||||
|
||||
lv_obj_add_style(obj, &style_blue, LV_STATE_SCROLLED | LV_PART_SCROLLBAR);
|
||||
|
||||
If the base direction of the :cpp:enumerator:`LV_PART_SCROLLBAR` is RTL
|
||||
(:c:macro:`LV_BASE_DIR_RTL`) the vertical scrollbar will be placed on the left.
|
||||
Note that, the ``base_dir`` style property is inherited. Therefore, it
|
||||
can be set directly on the :cpp:enumerator:`LV_PART_SCROLLBAR` part of an object or on
|
||||
the object's or any parent's main part to make a scrollbar inherit the
|
||||
base direction.
|
||||
|
||||
``pad_left/right/top/bottom`` sets the spacing around the scrollbars and
|
||||
``width`` sets the scrollbar's width.
|
||||
|
||||
.. _scroll_events:
|
||||
|
||||
Events
|
||||
------
|
||||
|
||||
The following events are related to scrolling:
|
||||
|
||||
- :cpp:enumerator:`LV_EVENT_SCROLL_BEGIN`: Scrolling begins. The event parameter is
|
||||
``NULL`` or an ``lv_anim_t *`` with a scroll animation descriptor that can be modified if required.
|
||||
- :cpp:enumerator:`LV_EVENT_SCROLL_END`: Scrolling ends.
|
||||
- :cpp:enumerator:`LV_EVENT_SCROLL`: Scroll happened. Triggered on every position change. Scroll events
|
||||
|
||||
Basic example
|
||||
*************
|
||||
|
||||
TODO
|
||||
|
||||
Features of scrolling
|
||||
*********************
|
||||
|
||||
Besides, managing "normal" scrolling there are many interesting and
|
||||
useful additional features.
|
||||
|
||||
Scrollable
|
||||
----------
|
||||
|
||||
It's possible to make an object non-scrollable with
|
||||
:cpp:expr:`lv_obj_remove_flag(obj, LV_OBJ_FLAG_SCROLLABLE)`.
|
||||
|
||||
Non-scrollable objects can still propagate the scrolling (chain) to
|
||||
their parents.
|
||||
|
||||
The direction in which scrolling happens can be controlled by ``lv_obj_set_scroll_dir(obj, LV_DIR_...)``.
|
||||
|
||||
The following values are possible for the direction:
|
||||
|
||||
- :cpp:enumerator:`LV_DIR_TOP`: only scroll up
|
||||
- :cpp:enumerator:`LV_DIR_LEFT`: only scroll left
|
||||
- :cpp:enumerator:`LV_DIR_BOTTOM`: only scroll down
|
||||
- :cpp:enumerator:`LV_DIR_RIGHT`: only scroll right
|
||||
- :cpp:enumerator:`LV_DIR_HOR`: only scroll horizontally
|
||||
- :cpp:enumerator:`LV_DIR_VER`: only scroll vertically
|
||||
- :cpp:enumerator:`LV_DIR_ALL`: scroll any directions
|
||||
|
||||
OR-ed values are also possible. E.g. :cpp:expr:`LV_DIR_TOP | LV_DIR_LEFT`.
|
||||
|
||||
Scroll chain
|
||||
------------
|
||||
|
||||
If an object can't be scrolled further (e.g. its content has reached the
|
||||
bottom-most position) additional scrolling is propagated to its parent.
|
||||
If the parent can be scrolled in that direction than it will be scrolled
|
||||
instead. It continues propagating to the grandparent and
|
||||
grand-grandparents as well.
|
||||
|
||||
The propagation on scrolling is called "scroll chaining" and it can be
|
||||
enabled/disabled with ``LV_OBJ_FLAG_SCROLL_CHAIN_HOR/VER`` flag. If
|
||||
chaining is disabled the propagation stops on the object and the
|
||||
parent(s) won't be scrolled.
|
||||
|
||||
Scroll momentum
|
||||
---------------
|
||||
|
||||
When the user scrolls an object and releases it, LVGL can emulate
|
||||
inertial momentum for the scrolling. It's like the object was thrown and
|
||||
scrolling slows down smoothly.
|
||||
|
||||
The scroll momentum can be enabled/disabled with the
|
||||
:cpp:enumerator:`LV_OBJ_FLAG_SCROLL_MOMENTUM` flag.
|
||||
|
||||
Elastic scroll
|
||||
--------------
|
||||
|
||||
Normally an object can't be scrolled past the extremities of its
|
||||
content. That is the top side of the content can't be below the top side
|
||||
of the object.
|
||||
|
||||
However, with :cpp:enumerator:`LV_OBJ_FLAG_SCROLL_ELASTIC` a fancy effect is added
|
||||
when the user "over-scrolls" the content. The scrolling slows down, and
|
||||
the content can be scrolled inside the object. When the object is
|
||||
released the content scrolled in it will be animated back to the valid
|
||||
position.
|
||||
|
||||
Snapping
|
||||
--------
|
||||
|
||||
The children of an object can be snapped according to specific rules
|
||||
when scrolling ends. Children can be made snappable individually with
|
||||
the :cpp:enumerator:`LV_OBJ_FLAG_SNAPPABLE` flag.
|
||||
|
||||
An object can align snapped children in four ways:
|
||||
|
||||
- :cpp:enumerator:`LV_SCROLL_SNAP_NONE`: Snapping is disabled. (default)
|
||||
- :cpp:enumerator:`LV_SCROLL_SNAP_START`: Align the children to the left/top side of a scrolled object
|
||||
- :cpp:enumerator:`LV_SCROLL_SNAP_END`: Align the children to the right/bottom side of a scrolled object
|
||||
- :cpp:enumerator:`LV_SCROLL_SNAP_CENTER`: Align the children to the center of a scrolled object
|
||||
|
||||
Snap alignment is set with
|
||||
``lv_obj_set_scroll_snap_x/y(obj, LV_SCROLL_SNAP_...)``:
|
||||
|
||||
Under the hood the following happens:
|
||||
|
||||
1. User scrolls an object and releases the screen
|
||||
2. LVGL calculates where the scroll would end considering scroll momentum
|
||||
3. LVGL finds the nearest scroll point
|
||||
4. LVGL scrolls to the snap point with an animation
|
||||
|
||||
Scroll one
|
||||
----------
|
||||
|
||||
The "scroll one" feature tells LVGL to allow scrolling only one
|
||||
snappable child at a time. This requires making the children snappable
|
||||
and setting a scroll snap alignment different from
|
||||
:cpp:enumerator:`LV_SCROLL_SNAP_NONE`.
|
||||
|
||||
This feature can be enabled by the :cpp:enumerator:`LV_OBJ_FLAG_SCROLL_ONE` flag.
|
||||
|
||||
Scroll on focus
|
||||
---------------
|
||||
|
||||
Imagine that there a lot of objects in a group that are on a scrollable
|
||||
object. Pressing the "Tab" button focuses the next object but it might
|
||||
be outside the visible area of the scrollable object. If the "scroll on
|
||||
focus" feature is enabled LVGL will automatically scroll objects to
|
||||
bring their children into view. The scrolling happens recursively
|
||||
therefore even nested scrollable objects are handled properly. The
|
||||
object will be scrolled into view even if it's on a different page of a
|
||||
tabview.
|
||||
|
||||
Scroll manually
|
||||
***************
|
||||
|
||||
The following API functions allow manual scrolling of objects:
|
||||
|
||||
- ``lv_obj_scroll_by(obj, x, y, LV_ANIM_ON/OFF)`` scroll by ``x`` and ``y`` values
|
||||
- ``lv_obj_scroll_to(obj, x, y, LV_ANIM_ON/OFF)`` scroll to bring the given coordinate to the top left corner
|
||||
- ``lv_obj_scroll_to_x(obj, x, LV_ANIM_ON/OFF)`` scroll to bring the given coordinate to the left side
|
||||
- ``lv_obj_scroll_to_y(obj, y, LV_ANIM_ON/OFF)`` scroll to bring the given coordinate to the top side
|
||||
|
||||
From time to time you may need to retrieve the scroll position of an
|
||||
element, either to restore it later, or to display dynamically some
|
||||
elements according to the current scroll. Here is an example to see how
|
||||
to combine scroll event and store the scroll top position.
|
||||
|
||||
.. code:: c
|
||||
|
||||
static int scroll_value = 0;
|
||||
|
||||
static void store_scroll_value_event_cb(lv_event_t* e) {
|
||||
lv_obj_t* screen = lv_event_get_target(e);
|
||||
scroll_value = lv_obj_get_scroll_top(screen);
|
||||
printf("%d pixels are scrolled out on the top\n", scroll_value);
|
||||
}
|
||||
|
||||
lv_obj_t* container = lv_obj_create(NULL);
|
||||
lv_obj_add_event_cb(container, store_scroll_value_event_cb, LV_EVENT_SCROLL, NULL);
|
||||
|
||||
Scroll coordinates can be retrieved from different axes with these
|
||||
functions:
|
||||
|
||||
- ``lv_obj_get_scroll_x(obj)`` Get the ``x`` coordinate of object
|
||||
- ``lv_obj_get_scroll_y(obj)`` Get the ``y`` coordinate of object
|
||||
- ``lv_obj_get_scroll_top(obj)`` Get the scroll coordinate from the top
|
||||
- ``lv_obj_get_scroll_bottom(obj)`` Get the scroll coordinate from the bottom
|
||||
- ``lv_obj_get_scroll_left(obj)`` Get the scroll coordinate from the left
|
||||
- ``lv_obj_get_scroll_right(obj)`` Get the scroll coordinate from the right
|
||||
|
||||
|
||||
Self size
|
||||
*********
|
||||
|
||||
Self size is a property of an object. Normally, the user shouldn't use
|
||||
this parameter but if a custom widget is created it might be useful.
|
||||
|
||||
In short, self size establishes the size of an object's content. To
|
||||
understand it better take the example of a table. Let's say it has 10
|
||||
rows each with 50 px height. So the total height of the content is 500
|
||||
px. In other words the "self height" is 500 px. If the user sets only
|
||||
200 px height for the table LVGL will see that the self size is larger
|
||||
and make the table scrollable.
|
||||
|
||||
This means not only the children can make an object scrollable but a
|
||||
larger self size will too.
|
||||
|
||||
LVGL uses the :cpp:enumerator:`LV_EVENT_GET_SELF_SIZE` event to get the self size of
|
||||
an object. Here is an example to see how to handle the event:
|
||||
|
||||
.. code:: c
|
||||
|
||||
if(event_code == LV_EVENT_GET_SELF_SIZE) {
|
||||
lv_point_t * p = lv_event_get_param(e);
|
||||
|
||||
//If x or y < 0 then it doesn't need to be calculated now
|
||||
if(p->x >= 0) {
|
||||
p->x = 200; //Set or calculate the self width
|
||||
}
|
||||
|
||||
if(p->y >= 0) {
|
||||
p->y = 50; //Set or calculate the self height
|
||||
}
|
||||
}
|
||||
|
||||
.. _scroll_example:
|
||||
|
||||
Examples
|
||||
********
|
||||
|
||||
.. include:: ../examples/scroll/index.rst
|
||||
|
||||
.. _scroll_api:
|
||||
|
||||
API
|
||||
***
|
||||
1033
libraries/lvgl/docs/overview/style-props.md
Normal file
1033
libraries/lvgl/docs/overview/style-props.md
Normal file
File diff suppressed because it is too large
Load Diff
1613
libraries/lvgl/docs/overview/style-props.rst
Normal file
1613
libraries/lvgl/docs/overview/style-props.rst
Normal file
File diff suppressed because it is too large
Load Diff
537
libraries/lvgl/docs/overview/style.rst
Normal file
537
libraries/lvgl/docs/overview/style.rst
Normal file
@@ -0,0 +1,537 @@
|
||||
.. _styles:
|
||||
|
||||
======
|
||||
Styles
|
||||
======
|
||||
|
||||
*Styles* are used to set the appearance of objects. Styles in lvgl are
|
||||
heavily inspired by CSS. The concept in a nutshell is as follows: - A
|
||||
style is an :cpp:type:`lv_style_t` variable which can hold properties like
|
||||
border width, text color and so on. It's similar to a ``class`` in CSS.
|
||||
|
||||
- Styles can be assigned to objects to change their appearance. Upon
|
||||
assignment, the target part (*pseudo-element* in CSS) and target state
|
||||
(*pseudo class*) can be specified. For example one can add
|
||||
``style_blue`` to the knob of a slider when it's in pressed state.
|
||||
- The same style can be used by any number of objects.
|
||||
- Styles can be cascaded which means multiple styles may be assigned to an object and
|
||||
each style can have different properties. Therefore, not all properties
|
||||
have to be specified in a style. LVGL will search for a property until a
|
||||
style defines it or use a default if it's not specified by any of the
|
||||
styles. For example ``style_btn`` can result in a default gray button
|
||||
and ``style_btn_red`` can add only a ``background-color=red`` to
|
||||
overwrite the background color.
|
||||
- The most recently added style has higher precedence. This means if a property
|
||||
is specified in two styles the newest style in the object will be used.
|
||||
- Some properties (e.g. text color) can be inherited from a parent(s) if it's not specified in an object.
|
||||
- Objects can also have local styles with higher precedence than "normal" styles.
|
||||
- Unlike CSS (where pseudo-classes describe different states, e.g. ``:focus``),
|
||||
in LVGL a property is assigned to a given state.
|
||||
- Transitions can be applied when the object changes state.
|
||||
|
||||
.. _styles_states:
|
||||
|
||||
States
|
||||
******
|
||||
|
||||
The objects can be in the combination of the following states:
|
||||
|
||||
- :cpp:enumerator:`LV_STATE_DEFAULT`: (0x0000) Normal, released state
|
||||
- :cpp:enumerator:`LV_STATE_CHECKED`: (0x0001) Toggled or checked state
|
||||
- :cpp:enumerator:`LV_STATE_FOCUSED`: (0x0002) Focused via keypad or encoder or clicked via touchpad/mouse
|
||||
- :cpp:enumerator:`LV_STATE_FOCUS_KEY`: (0x0004) Focused via keypad or encoder but not via touchpad/mouse
|
||||
- :cpp:enumerator:`LV_STATE_EDITED`: (0x0008) Edit by an encoder
|
||||
- :cpp:enumerator:`LV_STATE_HOVERED`: (0x0010) Hovered by mouse
|
||||
- :cpp:enumerator:`LV_STATE_PRESSED`: (0x0020) Being pressed
|
||||
- :cpp:enumerator:`LV_STATE_SCROLLED`: (0x0040) Being scrolled
|
||||
- :cpp:enumerator:`LV_STATE_DISABLED`: (0x0080) Disabled state
|
||||
- :cpp:enumerator:`LV_STATE_USER_1`: (0x1000) Custom state
|
||||
- :cpp:enumerator:`LV_STATE_USER_2`: (0x2000) Custom state
|
||||
- :cpp:enumerator:`LV_STATE_USER_3`: (0x4000) Custom state
|
||||
- :cpp:enumerator:`LV_STATE_USER_4`: (0x8000) Custom state
|
||||
|
||||
An object can be in a combination of states such as being focused and
|
||||
pressed at the same time. This is represented as :cpp:expr:`LV_STATE_FOCUSED | LV_STATE_PRESSED`.
|
||||
|
||||
A style can be added to any state or state combination. For example,
|
||||
setting a different background color for the default and pressed states.
|
||||
If a property is not defined in a state the best matching state's
|
||||
property will be used. Typically this means the property with
|
||||
:cpp:enumerator:`LV_STATE_DEFAULT` is used.˛ If the property is not set even for the
|
||||
default state the default value will be used. (See later)
|
||||
|
||||
But what does the "best matching state's property" really mean? States
|
||||
have a precedence which is shown by their value (see in the above list).
|
||||
A higher value means higher precedence. To determine which state's
|
||||
property to use let's take an example. Imagine the background color is
|
||||
defined like this:
|
||||
|
||||
- :cpp:enumerator:`LV_STATE_DEFAULT`: white
|
||||
- :cpp:enumerator:`LV_STATE_PRESSED`: gray
|
||||
- :cpp:enumerator:`LV_STATE_FOCUSED`: red
|
||||
|
||||
1. Initially the object is in the default state, so it's a simple case:
|
||||
the property is perfectly defined in the object's current state as
|
||||
white.
|
||||
2. When the object is pressed there are 2 related properties: default
|
||||
with white (default is related to every state) and pressed with gray.
|
||||
The pressed state has 0x0020 precedence which is higher than the
|
||||
default state's 0x0000 precedence, so gray color will be used.
|
||||
3. When the object is focused the same thing happens as in pressed state
|
||||
and red color will be used. (Focused state has higher precedence than
|
||||
default state).
|
||||
4. When the object is focused and pressed both gray and red would work,
|
||||
but the pressed state has higher precedence than focused so gray
|
||||
color will be used.
|
||||
5. It's possible to set e.g. rose color for :cpp:expr:`LV_STATE_PRESSED | LV_STATE_FOCUSED`.
|
||||
In this case, this combined state has 0x0020 + 0x0002 = 0x0022 precedence, which is higher than
|
||||
the pressed state's precedence so rose color would be used.
|
||||
6. When the object is in the checked state there is no property to set
|
||||
the background color for this state. So for lack of a better option,
|
||||
the object remains white from the default state's property.
|
||||
|
||||
Some practical notes:
|
||||
|
||||
- The precedence (value) of states is quite intuitive, and it's something the
|
||||
user would expect naturally. E.g. if an object is focused the user will still
|
||||
want to see if it's pressed, therefore the pressed state has a higher
|
||||
precedence. If the focused state had a higher precedence it would overwrite
|
||||
the pressed color.
|
||||
- If you want to set a property for all states (e.g. red background color)
|
||||
just set it for the default state. If the object can't find a property
|
||||
for its current state it will fall back to the default state's property.
|
||||
- Use ORed states to describe the properties for complex cases. (E.g.
|
||||
pressed + checked + focused)
|
||||
- It might be a good idea to use different
|
||||
style elements for different states. For example, finding background
|
||||
colors for released, pressed, checked + pressed, focused, focused +
|
||||
pressed, focused + pressed + checked, etc. states is quite difficult.
|
||||
Instead, for example, use the background color for pressed and checked
|
||||
states and indicate the focused state with a different border color.
|
||||
|
||||
.. _styles_cascading:
|
||||
|
||||
Cascading styles
|
||||
****************
|
||||
|
||||
It's not required to set all the properties in one style. It's possible
|
||||
to add more styles to an object and have the latter added style modify
|
||||
or extend appearance. For example, create a general gray button style
|
||||
and create a new one for red buttons where only the new background color
|
||||
is set.
|
||||
|
||||
This is much like in CSS when used classes are listed like
|
||||
``<div class=".btn .btn-red">``.
|
||||
|
||||
Styles added later have precedence over ones set earlier. So in the
|
||||
gray/red button example above, the normal button style should be added
|
||||
first and the red style second. However, the precedence of the states
|
||||
are still taken into account. So let's examine the following case:
|
||||
|
||||
- the basic button style defines dark-gray color for the default state and
|
||||
light-gray color for the pressed state
|
||||
- the red button style defines the background color as red only in the default state
|
||||
|
||||
In this case, when the button is released (it's in default state) it
|
||||
will be red because a perfect match is found in the most recently added
|
||||
style (red). When the button is pressed the light-gray color is a better
|
||||
match because it describes the current state perfectly, so the button
|
||||
will be light-gray.
|
||||
|
||||
.. _styles_inheritance:
|
||||
|
||||
Inheritance
|
||||
***********
|
||||
|
||||
Some properties (typically those related to text) can be inherited from
|
||||
the parent object's styles. Inheritance is applied only if the given
|
||||
property is not set in the object's styles (even in default state). In
|
||||
this case, if the property is inheritable, the property's value will be
|
||||
searched in the parents until an object specifies a value for the
|
||||
property. The parents will use their own state to determine the value.
|
||||
So if a button is pressed, and the text color comes from here, the
|
||||
pressed text color will be used.
|
||||
|
||||
.. _styles_parts:
|
||||
|
||||
Parts
|
||||
*****
|
||||
|
||||
Objects can be composed of *parts* which may each have their own styles.
|
||||
|
||||
The following predefined parts exist in LVGL:
|
||||
|
||||
- :cpp:enumerator:`LV_PART_MAIN`: A background like rectangle
|
||||
- :cpp:enumerator:`LV_PART_SCROLLBAR`: The scrollbar(s)
|
||||
- :cpp:enumerator:`LV_PART_INDICATOR`: Indicator, e.g. for slider, bar, switch, or the tick box of the checkbox
|
||||
- :cpp:enumerator:`LV_PART_KNOB`: Like a handle to grab to adjust a value
|
||||
- :cpp:enumerator:`LV_PART_SELECTED`: Indicate the currently selected option or section
|
||||
- :cpp:enumerator:`LV_PART_ITEMS`: Used if the widget has multiple similar elements (e.g. table cells)
|
||||
- :cpp:enumerator:`LV_PART_CURSOR`: Mark a specific place e.g. text area's or chart's cursor
|
||||
- :cpp:enumerator:`LV_PART_CUSTOM_FIRST`: Custom part identifiers can be added starting from here.
|
||||
|
||||
For example a :ref:`Slider <lv_slider>` has three parts:
|
||||
|
||||
- Background
|
||||
- Indicator
|
||||
- Knob
|
||||
|
||||
This means all three parts of the slider can have their own styles. See
|
||||
later how to add styles to objects and parts.
|
||||
|
||||
.. _styles_initialize:
|
||||
|
||||
Initialize styles and set/get properties
|
||||
****************************************
|
||||
|
||||
Styles are stored in :cpp:type:`lv_style_t` variables. Style variables should be
|
||||
``static``, global or dynamically allocated. In other words they cannot
|
||||
be local variables in functions which are destroyed when the function
|
||||
exits. Before using a style it should be initialized with
|
||||
:cpp:expr:`lv_style_init(&my_style)`. After initializing a style, properties can
|
||||
be added or changed.
|
||||
|
||||
Property set functions looks like this:
|
||||
``lv_style_set_<property_name>(&style, <value>);`` For example:
|
||||
|
||||
.. code:: c
|
||||
|
||||
static lv_style_t style_btn;
|
||||
lv_style_init(&style_btn);
|
||||
lv_style_set_bg_color(&style_btn, lv_color_hex(0x115588));
|
||||
lv_style_set_bg_opa(&style_btn, LV_OPA_50);
|
||||
lv_style_set_border_width(&style_btn, 2);
|
||||
lv_style_set_border_color(&style_btn, lv_color_black());
|
||||
|
||||
static lv_style_t style_btn_red;
|
||||
lv_style_init(&style_btn_red);
|
||||
lv_style_set_bg_color(&style_btn_red, lv_palette_main(LV_PALETTE_RED));
|
||||
lv_style_set_bg_opa(&style_btn_red, LV_OPA_COVER);
|
||||
|
||||
To remove a property use:
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_style_remove_prop(&style, LV_STYLE_BG_COLOR);
|
||||
|
||||
To get a property's value from a style:
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_style_value_t v;
|
||||
lv_result_t res = lv_style_get_prop(&style, LV_STYLE_BG_COLOR, &v);
|
||||
if(res == LV_RESULT_OK) { /*Found*/
|
||||
do_something(v.color);
|
||||
}
|
||||
|
||||
:cpp:union:`lv_style_value_t` has 3 fields:
|
||||
|
||||
- :cpp:member:`num`: for integer, boolean and opacity properties
|
||||
- :cpp:member:`color`: for color properties
|
||||
- :cpp:member:`ptr`: for pointer properties
|
||||
|
||||
To reset a style (free all its data) use:
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_style_reset(&style);
|
||||
|
||||
Styles can be built as ``const`` too to save RAM:
|
||||
|
||||
.. code:: c
|
||||
|
||||
const lv_style_const_prop_t style1_props[] = {
|
||||
LV_STYLE_CONST_WIDTH(50),
|
||||
LV_STYLE_CONST_HEIGHT(50),
|
||||
LV_STYLE_CONST_PROPS_END
|
||||
};
|
||||
|
||||
LV_STYLE_CONST_INIT(style1, style1_props);
|
||||
|
||||
Later ``const`` style can be used like any other style but (obviously)
|
||||
new properties cannot be added.
|
||||
|
||||
.. _styles_add_remove:
|
||||
|
||||
Add and remove styles to a widget
|
||||
*********************************
|
||||
|
||||
A style on its own is not that useful. It must be assigned to an object
|
||||
to take effect.
|
||||
|
||||
Add styles
|
||||
----------
|
||||
|
||||
To add a style to an object use
|
||||
``lv_obj_add_style(obj, &style, <selector>)``. ``<selector>`` is an
|
||||
OR-ed value of parts and state to which the style should be added. Some
|
||||
examples:
|
||||
|
||||
- :cpp:expr:`LV_PART_MAIN | LV_STATE_DEFAULT`
|
||||
- :cpp:enumerator:`LV_STATE_PRESSED`: The main part in pressed state. :cpp:enumerator:`LV_PART_MAIN` can be omitted
|
||||
- :cpp:enumerator:`LV_PART_SCROLLBAR`: The scrollbar part in the default state. :cpp:enumerator:`LV_STATE_DEFAULT` can be omitted.
|
||||
- :cpp:expr:`LV_PART_SCROLLBAR | LV_STATE_SCROLLED`: The scrollbar part when the object is being scrolled
|
||||
- :cpp:expr:`LV_PART_INDICATOR | LV_STATE_PRESSED | LV_STATE_CHECKED` The indicator part when the object is pressed and checked at the same time.
|
||||
|
||||
Using :cpp:func:`lv_obj_add_style`:
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_obj_add_style(btn, &style_btn, 0); /*Default button style*/
|
||||
lv_obj_add_style(btn, &btn_red, LV_STATE_PRESSED); /*Overwrite only some colors to red when pressed*/
|
||||
|
||||
Replace styles
|
||||
--------------
|
||||
|
||||
To replace a specific style of an object use
|
||||
:cpp:expr:`lv_obj_replace_style(obj, old_style, new_style, selector)`. This
|
||||
function will only replace ``old_style`` with ``new_style`` if the
|
||||
``selector`` matches the ``selector`` used in ``lv_obj_add_style``. Both
|
||||
styles, i.e. ``old_style`` and ``new_style``, must not be ``NULL`` (for
|
||||
adding and removing separate functions exist). If the combination of
|
||||
``old_style`` and ``selector`` exists multiple times in ``obj``\ 's
|
||||
styles, all occurrences will be replaced. The return value of the
|
||||
function indicates whether at least one successful replacement took
|
||||
place.
|
||||
|
||||
Using :cpp:func:`lv_obj_replace_style`:
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_obj_add_style(btn, &style_btn, 0); /*Add a button style*/
|
||||
lv_obj_replace_style(btn, &style_btn, &new_style_btn, 0); /*Replace the button style with a different one*/
|
||||
|
||||
Remove styles
|
||||
-------------
|
||||
|
||||
To remove all styles from an object use :cpp:expr:`lv_obj_remove_style_all(obj)`.
|
||||
|
||||
To remove specific styles use
|
||||
:cpp:expr:`lv_obj_remove_style(obj, style, selector)`. This function will remove
|
||||
``style`` only if the ``selector`` matches with the ``selector`` used in
|
||||
:cpp:func:`lv_obj_add_style`. ``style`` can be ``NULL`` to check only the
|
||||
``selector`` and remove all matching styles. The ``selector`` can use
|
||||
the :cpp:enumerator:`LV_STATE_ANY` and :cpp:enumerator:`LV_PART_ANY` values to remove the style from
|
||||
any state or part.
|
||||
|
||||
Report style changes
|
||||
--------------------
|
||||
|
||||
If a style which is already assigned to an object changes (i.e. a
|
||||
property is added or changed), the objects using that style should be
|
||||
notified. There are 3 options to do this:
|
||||
|
||||
1. If you know that the changed properties can be applied by a simple redraw
|
||||
(e.g. color or opacity changes) just call :cpp:expr:`lv_obj_invalidate(obj)`
|
||||
or :cpp:expr:`lv_obj_invalidate(lv_screen_active())`.
|
||||
2. If more complex style properties were changed or added, and you know which
|
||||
object(s) are affected by that style call :cpp:expr:`lv_obj_refresh_style(obj, part, property)`.
|
||||
To refresh all parts and properties use :cpp:expr:`lv_obj_refresh_style(obj, LV_PART_ANY, LV_STYLE_PROP_ANY)`.
|
||||
3. To make LVGL check all objects to see if they use a style and refresh them
|
||||
when needed, call :cpp:expr:`lv_obj_report_style_change(&style)`. If ``style``
|
||||
is ``NULL`` all objects will be notified about a style change.
|
||||
|
||||
Get a property's value on an object
|
||||
-----------------------------------
|
||||
|
||||
To get a final value of property
|
||||
|
||||
- considering cascading, inheritance, local styles and transitions (see below)
|
||||
- property get functions like this can be used: ``lv_obj_get_style_<property_name>(obj, <part>)``.
|
||||
These functions use the object's current state and if no better candidate exists they return a default value.
|
||||
For example:
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_color_t color = lv_obj_get_style_bg_color(btn, LV_PART_MAIN);
|
||||
|
||||
.. _styles_local:
|
||||
|
||||
Local styles
|
||||
************
|
||||
|
||||
In addition to "normal" styles, objects can also store local styles.
|
||||
This concept is similar to inline styles in CSS
|
||||
(e.g. ``<div style="color:red">``) with some modification.
|
||||
|
||||
Local styles are like normal styles, but they can't be shared among
|
||||
other objects. If used, local styles are allocated automatically, and
|
||||
freed when the object is deleted. They are useful to add local
|
||||
customization to an object.
|
||||
|
||||
Unlike in CSS, LVGL local styles can be assigned to states
|
||||
(*pseudo-classes*) and parts (*pseudo-elements*).
|
||||
|
||||
To set a local property use functions like
|
||||
``lv_obj_set_style_<property_name>(obj, <value>, <selector>);`` For example:
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_obj_set_style_bg_color(slider, lv_color_red(), LV_PART_INDICATOR | LV_STATE_FOCUSED);
|
||||
|
||||
.. _styles_properties:
|
||||
|
||||
Properties
|
||||
**********
|
||||
|
||||
For the full list of style properties click
|
||||
:ref:`here <style_properties>`.
|
||||
|
||||
Typical background properties
|
||||
-----------------------------
|
||||
|
||||
In the documentation of the widgets you will see sentences like "The
|
||||
widget uses the typical background properties". These "typical
|
||||
background properties" are the ones related to:
|
||||
|
||||
- Background
|
||||
- Border
|
||||
- Outline
|
||||
- Shadow
|
||||
- Padding
|
||||
- Width and height transformation
|
||||
- X and Y translation
|
||||
|
||||
.. _styles_transitions:
|
||||
|
||||
Transitions
|
||||
***********
|
||||
|
||||
By default, when an object changes state (e.g. it's pressed) the new
|
||||
properties from the new state are set immediately. However, with
|
||||
transitions it's possible to play an animation on state change. For
|
||||
example, on pressing a button its background color can be animated to
|
||||
the pressed color over 300 ms.
|
||||
|
||||
The parameters of the transitions are stored in the styles. It's
|
||||
possible to set
|
||||
|
||||
- the time of the transition
|
||||
- the delay before starting the transition
|
||||
- the animation path (also known as the timing or easing function)
|
||||
- the properties to animate
|
||||
|
||||
The transition properties can be defined for each state. For example,
|
||||
setting a 500 ms transition time in the default state means that when
|
||||
the object goes to the default state a 500 ms transition time is
|
||||
applied. Setting a 100 ms transition time in the pressed state causes a
|
||||
100 ms transition when going to the pressed state. This example
|
||||
configuration results in going to the pressed state quickly and then
|
||||
going back to default slowly.
|
||||
|
||||
To describe a transition an :cpp:struct:`lv_transition_dsc_t` variable needs to be
|
||||
initialized and added to a style:
|
||||
|
||||
.. code:: c
|
||||
|
||||
/*Only its pointer is saved so must static, global or dynamically allocated */
|
||||
static const lv_style_prop_t trans_props[] = {
|
||||
LV_STYLE_BG_OPA, LV_STYLE_BG_COLOR,
|
||||
0, /*End marker*/
|
||||
};
|
||||
|
||||
static lv_style_transition_dsc_t trans1;
|
||||
lv_style_transition_dsc_init(&trans1, trans_props, lv_anim_path_ease_out, duration_ms, delay_ms);
|
||||
|
||||
lv_style_set_transition(&style1, &trans1);
|
||||
|
||||
.. _styles_opacity_blend_modes_transformations:
|
||||
|
||||
Opacity, Blend modes and Transformations
|
||||
****************************************
|
||||
|
||||
If the ``opa``, ``blend_mode``, ``transform_angle``, or
|
||||
``transform_zoom`` properties are set to their non-default value LVGL
|
||||
creates a snapshot about the widget and all its children in order to
|
||||
blend the whole widget with the set opacity, blend mode and
|
||||
transformation properties.
|
||||
|
||||
These properties have this effect only on the ``MAIN`` part of the
|
||||
widget.
|
||||
|
||||
The created snapshot is called "intermediate layer" or simply "layer".
|
||||
If only ``opa`` and/or ``blend_mode`` is set to a non-default value LVGL
|
||||
can build the layer from smaller chunks. The size of these chunks can be
|
||||
configured by the following properties in ``lv_conf.h``:
|
||||
|
||||
- :cpp:enumerator:`LV_LAYER_SIMPLE_BUF_SIZE`: [bytes] the optimal target buffer size. LVGL will try to allocate this size of memory.
|
||||
- :cpp:enumerator:`LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE`: [bytes] used if :cpp:enumerator:`LV_LAYER_SIMPLE_BUF_SIZE` couldn't be allocated.
|
||||
|
||||
If transformation properties were also used the layer cannot be
|
||||
rendered in chunks, but one larger memory needs to be allocated. The
|
||||
required memory depends on the angle, zoom and pivot parameters, and the
|
||||
size of the area to redraw, but it's never larger than the size of the
|
||||
widget (including the extra draw size used for shadow, outline, etc).
|
||||
|
||||
If the widget can fully cover the area to redraw, LVGL creates an RGB
|
||||
layer (which is faster to render and uses less memory). If the opposite
|
||||
case ARGB rendering needs to be used. A widget might not cover its area
|
||||
if it has radius, ``bg_opa != 255``, has shadow, outline, etc.
|
||||
|
||||
The click area of the widget is also transformed accordingly.
|
||||
|
||||
.. _styles_color_filter:
|
||||
|
||||
Color filter
|
||||
************
|
||||
|
||||
TODO
|
||||
|
||||
.. _styles_themes:
|
||||
|
||||
Themes
|
||||
******
|
||||
|
||||
Themes are a collection of styles. If there is an active theme LVGL
|
||||
applies it on every created widget. This will give a default appearance
|
||||
to the UI which can then be modified by adding further styles.
|
||||
|
||||
Every display can have a different theme. For example, you could have a
|
||||
colorful theme on a TFT and monochrome theme on a secondary monochrome
|
||||
display.
|
||||
|
||||
To set a theme for a display, two steps are required:
|
||||
|
||||
1. Initialize a theme
|
||||
2. Assign the initialized theme to a display.
|
||||
|
||||
Theme initialization functions can have different prototypes. This
|
||||
example shows how to set the "default" theme:
|
||||
|
||||
.. code:: c
|
||||
|
||||
lv_theme_t * th = lv_theme_default_init(display, /*Use the DPI, size, etc from this display*/
|
||||
LV_COLOR_PALETTE_BLUE, LV_COLOR_PALETTE_CYAN, /*Primary and secondary palette*/
|
||||
false, /*Light or dark mode*/
|
||||
&lv_font_montserrat_10, &lv_font_montserrat_14, &lv_font_montserrat_18); /*Small, normal, large fonts*/
|
||||
|
||||
lv_display_set_theme(display, th); /*Assign the theme to the display*/
|
||||
|
||||
The included themes are enabled in ``lv_conf.h``. If the default theme
|
||||
is enabled by :c:macro:`LV_USE_THEME_DEFAULT` LVGL automatically initializes
|
||||
and sets it when a display is created.
|
||||
|
||||
Extending themes
|
||||
----------------
|
||||
|
||||
Built-in themes can be extended. If a custom theme is created, a parent
|
||||
theme can be selected. The parent theme's styles will be added before
|
||||
the custom theme's styles. Any number of themes can be chained this way.
|
||||
E.g. default theme -> custom theme -> dark theme.
|
||||
|
||||
:cpp:expr:`lv_theme_set_parent(new_theme, base_theme)` extends the
|
||||
``base_theme`` with the ``new_theme``.
|
||||
|
||||
There is an example for it below.
|
||||
|
||||
.. _styles_example:
|
||||
|
||||
Examples
|
||||
********
|
||||
|
||||
.. include:: ../examples/styles/index.rst
|
||||
|
||||
.. _styles_api:
|
||||
|
||||
API
|
||||
***
|
||||
146
libraries/lvgl/docs/overview/timer.rst
Normal file
146
libraries/lvgl/docs/overview/timer.rst
Normal file
@@ -0,0 +1,146 @@
|
||||
.. _timer:
|
||||
|
||||
======
|
||||
Timers
|
||||
======
|
||||
|
||||
LVGL has a built-in timer system. You can register a function to have it
|
||||
be called periodically. The timers are handled and called in
|
||||
:cpp:func:`lv_timer_handler`, which needs to be called every few milliseconds.
|
||||
See `Porting </porting/timer-handler>`__ for more information.
|
||||
|
||||
Timers are non-preemptive, which means a timer cannot interrupt another
|
||||
timer. Therefore, you can call any LVGL related function in a timer.
|
||||
|
||||
Create a timer
|
||||
**************
|
||||
|
||||
To create a new timer, use
|
||||
:cpp:expr:`lv_timer_create(timer_cb, period_ms, user_data)`. It will create an
|
||||
:cpp:type:`lv_timer_t` ``*`` variable, which can be used later to modify the
|
||||
parameters of the timer. :cpp:func:`lv_timer_create_basic` can also be used.
|
||||
This allows you to create a new timer without specifying any parameters.
|
||||
|
||||
A timer callback should have a ``void (*lv_timer_cb_t)(lv_timer_t *)``
|
||||
prototype.
|
||||
|
||||
For example:
|
||||
|
||||
.. code:: c
|
||||
|
||||
void my_timer(lv_timer_t * timer)
|
||||
{
|
||||
/*Use the user_data*/
|
||||
uint32_t * user_data = timer->user_data;
|
||||
printf("my_timer called with user data: %d\n", *user_data);
|
||||
|
||||
/*Do something with LVGL*/
|
||||
if(something_happened) {
|
||||
something_happened = false;
|
||||
lv_button_create(lv_screen_active(), NULL);
|
||||
}
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
static uint32_t user_data = 10;
|
||||
lv_timer_t * timer = lv_timer_create(my_timer, 500, &user_data);
|
||||
|
||||
Ready and Reset
|
||||
***************
|
||||
|
||||
:cpp:expr:`lv_timer_ready(timer)` makes a timer run on the next call of
|
||||
:cpp:func:`lv_timer_handler`.
|
||||
|
||||
:cpp:expr:`lv_timer_reset(timer)` resets the period of a timer. It will be
|
||||
called again after the defined period of milliseconds has elapsed.
|
||||
|
||||
Set parameters
|
||||
**************
|
||||
|
||||
You can modify some timer parameters later:
|
||||
|
||||
- :cpp:expr:`lv_timer_set_cb(timer, new_cb)`
|
||||
- :cpp:expr:`lv_timer_set_period(timer, new_period)`
|
||||
|
||||
Repeat count
|
||||
************
|
||||
|
||||
You can make a timer repeat only a given number of times with
|
||||
:cpp:expr:`lv_timer_set_repeat_count(timer, count)`. The timer will
|
||||
automatically be deleted after it's called the defined number of times.
|
||||
Set the count to ``-1`` to repeat indefinitely.
|
||||
|
||||
Enable and Disable
|
||||
******************
|
||||
|
||||
You can enable or disable a timer with :cpp:expr:`lv_timer_enable(en)`.
|
||||
|
||||
Pause and Resume
|
||||
****************
|
||||
|
||||
:cpp:expr:`lv_timer_pause(timer)` pauses the specified timer.
|
||||
|
||||
:cpp:expr:`lv_timer_resume(timer)` resumes the specified timer.
|
||||
|
||||
Measure idle time
|
||||
*****************
|
||||
|
||||
You can get the idle percentage time of :cpp:func:`lv_timer_handler` with
|
||||
:cpp:func:`lv_timer_get_idle`. Note that, it doesn't measure the idle time of
|
||||
the overall system, only :cpp:func:`lv_timer_handler`. It can be misleading if
|
||||
you use an operating system and call :cpp:func:`lv_timer_handler` in a timer, as
|
||||
it won't actually measure the time the OS spends in an idle thread.
|
||||
|
||||
Timer handler resume callback
|
||||
*****************************
|
||||
|
||||
When the `lv_timer_handler` is stopped, if you want to pay attention to the wake-up
|
||||
timing of the `lv_timer_handler`, you can set a resume callback using
|
||||
:cpp:expr:`lv_timer_handler_set_resume_cb(cb, user_data)`.
|
||||
The callback should have a ``void (*lv_timer_handler_resume_cb_t)(void*)`` prototype.
|
||||
|
||||
Asynchronous calls
|
||||
******************
|
||||
|
||||
In some cases, you can't perform an action immediately. For example, you
|
||||
can't delete an object because something else is still using it, or you
|
||||
don't want to block the execution now. For these cases,
|
||||
:cpp:expr:`lv_async_call(my_function, data_p)` can be used to call
|
||||
``my_function`` on the next invocation of :cpp:func:`lv_timer_handler`.
|
||||
``data_p`` will be passed to the function when it's called. Note that
|
||||
only the data pointer is saved, so you need to ensure that the variable
|
||||
will be "alive" while the function is called. It can be *static*, global
|
||||
or dynamically allocated data. If you want to cancel an asynchronous
|
||||
call, call :cpp:expr:`lv_async_call_cancel(my_function, data_p)`, which will
|
||||
clear all asynchronous calls matching ``my_function`` and ``data_p``.
|
||||
|
||||
For example:
|
||||
|
||||
.. code:: c
|
||||
|
||||
void my_screen_clean_up(void * scr)
|
||||
{
|
||||
/*Free some resources related to `scr`*/
|
||||
|
||||
/*Finally delete the screen*/
|
||||
lv_obj_delete(scr);
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
/*Do something with the object on the current screen*/
|
||||
|
||||
/*Delete screen on next call of `lv_timer_handler`, not right now.*/
|
||||
lv_async_call(my_screen_clean_up, lv_screen_active());
|
||||
|
||||
/*The screen is still valid so you can do other things with it*/
|
||||
|
||||
If you just want to delete an object and don't need to clean anything up
|
||||
in ``my_screen_cleanup`` you could just use :cpp:func:`lv_obj_delete_async` which
|
||||
will delete the object on the next call to :cpp:func:`lv_timer_handler`.
|
||||
|
||||
.. _timer_api:
|
||||
|
||||
API
|
||||
***
|
||||
27
libraries/lvgl/docs/overview/vg_lite_tvg.rst
Normal file
27
libraries/lvgl/docs/overview/vg_lite_tvg.rst
Normal file
@@ -0,0 +1,27 @@
|
||||
.. _vg_lite_tvg:
|
||||
|
||||
=================
|
||||
VG-Lite Simulator
|
||||
=================
|
||||
|
||||
LVGL integrates a VG-Lite simulator based on ThorVG.
|
||||
Its purpose is to simplify the debugging of VG-Lite adaptation and reduce the time of debugging and locating problems on hardware devices.
|
||||
|
||||
It has been integrated into the CI automated compilation and testing process to ensure that the VG-Lite rendering backend can be fully tested after each PR modification.
|
||||
|
||||
How it works
|
||||
************
|
||||
|
||||
Sort out the APIs in the ``vg_lite.h`` header file provided by the vendor, re-implement the APIs using `ThorVG <https://github.com/thorvg/thorvg>`_,
|
||||
and simulate the same rendering images as the real hardware on the simulator.
|
||||
|
||||
Configuration
|
||||
*************
|
||||
|
||||
1. Enable VG-Lite rendering backend, see `VG-Lite Rendering Backend </overview/renderers/vg_lite>`__.
|
||||
|
||||
2. Enable ThorVG and turn on the configuration :c:macro:`LV_USE_THORVG_INTERNAL` or :c:macro:`LV_USE_THORVG_EXTERNAL`.
|
||||
It is recommended to use the internal ThorVG library to ensure uniform rendering results.
|
||||
|
||||
3. Enable :c:macro:`LV_USE_VG_LITE_THORVG` and set :c:macro:`LV_DRAW_BUF_ALIGN` to 64. The rest of the options can remain default.
|
||||
Make sure :c:macro:`LV_VG_LITE_USE_GPU_INIT` is enabled, because the thorvg drawing context needs to be initialized before it can be used.
|
||||
Reference in New Issue
Block a user