feat(docs): reorganize docs (#7136)
This commit is contained in:
578
docs/details/base-widget/coord.rst
Normal file
578
docs/details/base-widget/coord.rst
Normal file
@@ -0,0 +1,578 @@
|
||||
.. _coord:
|
||||
|
||||
============================
|
||||
Positions, Sizes and Layouts
|
||||
============================
|
||||
|
||||
|
||||
Overview
|
||||
********
|
||||
|
||||
Similar 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 (position, size, 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_units:
|
||||
|
||||
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 Widget 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
|
||||
Widget 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. A Widget'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 Widget and its children.
|
||||
- margin: space outside of the Widget (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 a Widget'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 Widgets are marked as "dirty"
|
||||
and before redrawing the screen LVGL checks if there are any "dirty"
|
||||
Widgets. If so it refreshes their position, size and layout.
|
||||
|
||||
In other words, if you need to get the coordinate of a Widget 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 Widgets 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(widget, 20)` LVGL saves ``x=20``
|
||||
in the local style of the Widget.
|
||||
|
||||
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 a Widget are removed by
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
lv_obj_remove_style_all(widget)
|
||||
|
||||
or
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
lv_obj_remove_style(widget, NULL, LV_PART_MAIN);
|
||||
|
||||
the earlier set coordinates will be removed as well.
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
/* The size of obj1 will be set back to the default in the end */
|
||||
lv_obj_set_size(widget1, 200, 100); /* Now obj1 has 200;100 size */
|
||||
lv_obj_remove_style_all(widget1); /* It removes the set sizes */
|
||||
|
||||
|
||||
/* widget2 will have 200;100 size in the end */
|
||||
lv_obj_remove_style_all(widget2);
|
||||
lv_obj_set_size(widget2, 200, 100);
|
||||
|
||||
|
||||
|
||||
.. _positioning_widgets:
|
||||
|
||||
Positioning Widgets
|
||||
*******************
|
||||
|
||||
|
||||
Direct
|
||||
------
|
||||
|
||||
To simply set the x and y coordinates of a Widget use:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
lv_obj_set_x(widget, 10); /* Separate... */
|
||||
lv_obj_set_y(widget, 20);
|
||||
lv_obj_set_pos(widget, 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-block:: c
|
||||
|
||||
lv_obj_set_x(btn, lv_pct(10)); //x = 10 % of parent content area width
|
||||
|
||||
|
||||
Alignment
|
||||
---------
|
||||
|
||||
|
||||
Inside parent widget
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
In many cases it is more convenient to tell LVGL to align your object relative to
|
||||
an "anchor point" in its parent *other* than its upper left corner. To establish
|
||||
that "anchor point", call :cpp:expr:`lv_obj_set_align(widget, LV_ALIGN_...)`. After
|
||||
that call, that "anchor point" will be remembered until another one is established.
|
||||
In other words, every futire x and y setting for that Widget will be relative to the
|
||||
that "anchor point".
|
||||
|
||||
Example: Position Widget (10,20) px relative to the center of its parent:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
lv_obj_set_align(widget, LV_ALIGN_CENTER);
|
||||
lv_obj_set_pos(widget, 10, 20);
|
||||
|
||||
/* Or combine the above in one function... */
|
||||
lv_obj_align(widget, LV_ALIGN_CENTER, 10, 20);
|
||||
|
||||
9 convenient "anchor points" can be used with these functions:
|
||||
|
||||
- :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`
|
||||
|
||||
See illustration below to visualize what these mean.
|
||||
|
||||
It's quite common to align a child to the center of its parent,
|
||||
therefore a dedicated function exists:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
lv_obj_center(widget);
|
||||
|
||||
//Has the same effect
|
||||
lv_obj_align(widget, LV_ALIGN_CENTER, 0, 0);
|
||||
|
||||
If the parent's size changes, the set alignment and position of the
|
||||
children is updated automatically.
|
||||
|
||||
|
||||
Relative to another Widget
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Alternately, you can choose an "anchor point" on another Widget.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
lv_obj_align_to(widget, reference_widget, align, x, y);
|
||||
|
||||
where ``align`` can be done of the following:
|
||||
|
||||
- :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`
|
||||
|
||||
Example: to horizontally center a label 10 pixels above a button:
|
||||
|
||||
.. code-block:: 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`
|
||||
does not remember the "anchor point" used, and so will not automatically reposition
|
||||
the aligned widget if the reference widget later moves.
|
||||
|
||||
The following illustration shows the meaning of each "anchor point" mentioned above.
|
||||
|
||||
.. image:: /misc/align.png
|
||||
|
||||
|
||||
|
||||
.. _coord_size:
|
||||
|
||||
Size
|
||||
****
|
||||
|
||||
Sizing the simple way
|
||||
---------------------
|
||||
|
||||
The width and the height of a Widget can be set easily as well:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
lv_obj_set_width(widget, 200); /* Separate... */
|
||||
lv_obj_set_height(widget, 100);
|
||||
lv_obj_set_size(widget, 200, 100); /* Or in one function */
|
||||
|
||||
Percentage values are calculated based on the parent's content area
|
||||
size. For example to set the Widget's height to the screen height:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
lv_obj_set_height(widget, lv_pct(100));
|
||||
|
||||
The size settings support a special value: :c:macro:`LV_SIZE_CONTENT`. It means
|
||||
the Widget'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.
|
||||
|
||||
Widgets 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 a Widget's bounding box but the
|
||||
size of the content area can be set as well. This means a Widget's
|
||||
bounding box will be enlarged with the addition of padding.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
lv_obj_set_content_width(widget, 50); /* The actual width: padding left + 50 + padding right */
|
||||
lv_obj_set_content_height(widget, 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-block:: c
|
||||
|
||||
int32_t w = lv_obj_get_width(widget);
|
||||
int32_t h = lv_obj_get_height(widget);
|
||||
int32_t content_w = lv_obj_get_content_width(widget);
|
||||
int32_t content_h = lv_obj_get_content_height(widget);
|
||||
|
||||
|
||||
.. _extending_click_area:
|
||||
|
||||
Extending the click area
|
||||
------------------------
|
||||
|
||||
By default, Widgets can be clicked only within their bounding area. However,
|
||||
especially with small Widgets, it can be helpful to make a Widget's "clickable" area
|
||||
larger. You can do this with :cpp:expr:`lv_obj_set_ext_click_area(widget, size)`.
|
||||
|
||||
|
||||
|
||||
.. _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 Widget.
|
||||
|
||||
However, using styles to set the coordinates has some great advantages:
|
||||
|
||||
- It makes it easy to set the width/height/etc. for several Widgets
|
||||
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 Widget ``100x50`` by default but adding
|
||||
``style_full_width`` overwrites only the width of the Widget.
|
||||
- The Widget 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 a Widget's size using a style:
|
||||
|
||||
.. code-block:: 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-block:: 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-block:: 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 Widget.
|
||||
|
||||
Percentage values can be used in translations as well. The percentage is
|
||||
relative to the size of the Widget (and not to the size of the parent).
|
||||
For example :cpp:expr:`lv_pct(50)` will move the Widget with half of its
|
||||
width/height.
|
||||
|
||||
The translation is applied after the layouts are calculated. Therefore,
|
||||
even laid out Widgets' position can be translated.
|
||||
|
||||
The translation actually moves the Widget. That means it makes the
|
||||
scrollbars and :c:macro:`LV_SIZE_CONTENT` sized Widgets react to the position
|
||||
change.
|
||||
|
||||
.. _coord_transformation:
|
||||
|
||||
Transformation
|
||||
**************
|
||||
|
||||
Similarly to position, a Widget's size can be changed relative to the
|
||||
current size as well. The transformed width and height are added on both
|
||||
sides of the Widget. This means a 10 px transformed width makes the
|
||||
Widget 2x10 pixels wider.
|
||||
|
||||
Unlike position translation, the size transformation doesn't make the
|
||||
Widget "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-block:: 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 a
|
||||
Widget'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-block:: 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(widget, lv_pct(100));
|
||||
lv_obj_add_style(widget, &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-block:: 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(widget, lv_pct(100));
|
||||
lv_obj_add_style(widget, &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 a Widget'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` ``(widget, <LAYOUT_NAME>)`` sets the layout on a Widget.
|
||||
For further settings of the parent and children see the documentation of
|
||||
the given layout.
|
||||
|
||||
|
||||
Built-in layouts
|
||||
----------------
|
||||
|
||||
LVGL comes with two very powerful layouts:
|
||||
|
||||
* Flexbox: arrange Widgets into rows or columns, with support for wrapping and expanding items.
|
||||
* Grid: arrange Widgets 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 Widgets to affect how they
|
||||
behave with layouts:
|
||||
|
||||
- :cpp:enumerator:`LV_OBJ_FLAG_HIDDEN` Hidden Widgets are ignored in layout calculations.
|
||||
- :cpp:enumerator:`LV_OBJ_FLAG_IGNORE_LAYOUT` The Widget 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 Widget 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(widget, FLAG)` and :cpp:expr:`lv_obj_remove_flag(widget, FLAG)`
|
||||
|
||||
|
||||
Adding new layouts
|
||||
------------------
|
||||
|
||||
LVGL can be freely extended by a custom layout like this:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
uint32_t MY_LAYOUT;
|
||||
|
||||
...
|
||||
|
||||
MY_LAYOUT = lv_layout_register(my_layout_update, &user_data);
|
||||
|
||||
...
|
||||
|
||||
void my_layout_update(lv_obj_t * widget, 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-block:: 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
|
||||
***
|
||||
281
docs/details/base-widget/event.rst
Normal file
281
docs/details/base-widget/event.rst
Normal file
@@ -0,0 +1,281 @@
|
||||
.. _events:
|
||||
|
||||
======
|
||||
Events
|
||||
======
|
||||
|
||||
Events are triggered in LVGL when something happens which might be
|
||||
interesting to the user, e.g. when a Widget:
|
||||
|
||||
- is clicked
|
||||
- is scrolled
|
||||
- has its value changed
|
||||
- is redrawn, etc.
|
||||
|
||||
Besides Widgets, events can registered from displays and input devices as well.
|
||||
It is not detailed below, but you can do this by changing the prefix of the functions
|
||||
from ``lv_obj_`` to ``lv_display_`` or ``lv_indev_``.
|
||||
|
||||
|
||||
.. _adding_events_to_a_widget:
|
||||
|
||||
Adding Events to a Widget
|
||||
*************************
|
||||
|
||||
The user can assign callback functions to a widget to process events.
|
||||
In practice, it looks like this:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
lv_obj_t * btn = lv_button_create(lv_screen_active());
|
||||
lv_obj_add_event_cb(btn, my_event_cb, LV_EVENT_CLICKED, user_data); /* 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. NULL may be passed for this argument if
|
||||
there is no need to use that data when the event is processed. You can retrieve the
|
||||
pointer passed when setting the callback function like this:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
my_user_data_t * user_data;
|
||||
...
|
||||
user_data = lv_event_get_user_data(e);
|
||||
|
||||
More events can be added to a Widget, like this:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
lv_obj_add_event_cb(widget, my_event_cb_1, LV_EVENT_CLICKED, NULL);
|
||||
lv_obj_add_event_cb(widget, my_event_cb_2, LV_EVENT_PRESSED, NULL);
|
||||
lv_obj_add_event_cb(widget, my_event_cb_3, LV_EVENT_ALL, NULL); /* No filtering, receive all events */
|
||||
|
||||
Even the same event callback can be used on a Widget with different
|
||||
``user_data``. For example:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
lv_obj_add_event_cb(widget, increment_on_click, LV_EVENT_CLICKED, &num1);
|
||||
lv_obj_add_event_cb(widget, increment_on_click, LV_EVENT_CLICKED, &num2);
|
||||
|
||||
The events will be called in the order as they were added.
|
||||
|
||||
Other Widgets can use the same *event callback*.
|
||||
|
||||
In the very same way, events can be attached to input devices and displays like this:
|
||||
|
||||
.. code-block:: 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);
|
||||
|
||||
|
||||
Removing Event(s) from Widgets
|
||||
******************************
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
uint32_t i;
|
||||
uint32_t event_cnt = lv_obj_get_event_count(widget);
|
||||
for(i = 0; i < event_cnt; i++) {
|
||||
lv_event_dsc_t * event_dsc = lv_obj_get_event_dsc(widget, i);
|
||||
if(lv_event_dsc_get_cb(event_dsc) == some_event_cb) {
|
||||
lv_obj_remove_event(widget, 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 Widgets (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`: Widget has been pressed
|
||||
- :cpp:enumerator:`LV_EVENT_PRESSING`: Widget is being pressed (called continuously while pressing)
|
||||
- :cpp:enumerator:`LV_EVENT_PRESS_LOST`: Widget is still being pressed but slid cursor/finger off Widget
|
||||
- :cpp:enumerator:`LV_EVENT_SHORT_CLICKED`: Widget was pressed for a short period of time, and then released without scrolling.
|
||||
- :cpp:enumerator:`LV_EVENT_SINGLE_CLICKED`: Widget was pressed for a short period of time, and then released without scrolling, for the first time in a click streak. A click streak refers to multiple short clicks within a short period of time and a small distance.
|
||||
- :cpp:enumerator:`LV_EVENT_DOUBLE_CLICKED`: Widget was pressed for a short period of time, and then released without scrolling, for the second time in a click streak.
|
||||
- :cpp:enumerator:`LV_EVENT_TRIPLE_CLICKED`: Widget was pressed for a short period of time, and then released without scrolling, for the third time in a click streak.
|
||||
- :cpp:enumerator:`LV_EVENT_LONG_PRESSED`: Widget 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 of long press)
|
||||
- :cpp:enumerator:`LV_EVENT_RELEASED`: Called in every cases when Widget 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 Widget. Get the key with :cpp:expr:`lv_indev_get_key(lv_indev_active())`
|
||||
- :cpp:enumerator:`LV_EVENT_FOCUSED`: Widget received focus
|
||||
- :cpp:enumerator:`LV_EVENT_DEFOCUSED`: Widget is defocused
|
||||
- :cpp:enumerator:`LV_EVENT_LEAVE`: Widget 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 Widget
|
||||
- :cpp:enumerator:`LV_EVENT_HOVER_LEAVE`: Indev hover leave Widget
|
||||
|
||||
Drawing Events
|
||||
--------------
|
||||
|
||||
- :cpp:enumerator:`LV_EVENT_COVER_CHECK`: Check if Widget 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 Widget (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`: Widget's value has changed (i.e. slider moved)
|
||||
- :cpp:enumerator:`LV_EVENT_INSERT`: A text is inserted to Widget. The event data is ``char `*`` being inserted.
|
||||
- :cpp:enumerator:`LV_EVENT_REFRESH`: Notify Widget 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`: Widget is being created
|
||||
- :cpp:enumerator:`LV_EVENT_DELETE`: Widget 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`: Widget coordinates/size have changed
|
||||
- :cpp:enumerator:`LV_EVENT_STYLE_CHANGED`: Widget'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 number of custom event codes can be registered by
|
||||
``uint32_t MY_EVENT_1 =`` :cpp:func:`lv_event_register_id`
|
||||
|
||||
They can be sent to any Widget with
|
||||
:cpp:expr:`lv_obj_send_event(widget, MY_EVENT_1, &some_data)`
|
||||
|
||||
|
||||
Refresh Event
|
||||
-------------
|
||||
|
||||
:cpp:enumerator:`LV_EVENT_REFRESH` is a special event because it's designed to let the
|
||||
user notify a Widget 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 a Widget if a limit is exceeded, etc
|
||||
|
||||
|
||||
Sending Events Manually
|
||||
***********************
|
||||
|
||||
To manually send events to a Widget, use
|
||||
``lv_obj_send_event(widget, <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-block:: c
|
||||
|
||||
/* Simulate the press of the first button (indexes start from zero) */
|
||||
uint32_t btn_id = 0;
|
||||
lv_obj_send_event(mbox, LV_EVENT_VALUE_CHANGED, &btn_id);
|
||||
|
||||
The same works for display and input devices with
|
||||
``lv_display_send_event(widget, <EVENT_CODE>, &some_data)`` and
|
||||
``lv_indev_send_event(widget, <EVENT_CODE>, &some_data)``.
|
||||
|
||||
|
||||
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 Widget to which an event was sent. I.e. the Widget whose event handler is being called.
|
||||
- :cpp:expr:`lv_event_get_target(e)`: get Widget that originally triggered the event (different from :cpp:func:`lv_event_get_target` if :ref:`event bubbling <event_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_obj_send_event`
|
||||
|
||||
.. _event_bubbling:
|
||||
|
||||
|
||||
Event Bubbling
|
||||
**************
|
||||
|
||||
If :cpp:expr:`lv_obj_add_flag(widget, LV_OBJ_FLAG_EVENT_BUBBLE)` is enabled all
|
||||
events will be sent to a Widget's parent as well. 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 Widget,
|
||||
not the original Widget. 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
|
||||
***
|
||||
18
docs/details/base-widget/index.rst
Normal file
18
docs/details/base-widget/index.rst
Normal file
@@ -0,0 +1,18 @@
|
||||
.. _base_widget_overview:
|
||||
|
||||
===========
|
||||
Base Widget
|
||||
===========
|
||||
|
||||
The following details apply to all types of Widgets.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
|
||||
obj
|
||||
coord
|
||||
layer
|
||||
styles/index
|
||||
event
|
||||
layouts/index
|
||||
scroll
|
||||
126
docs/details/base-widget/layer.rst
Normal file
126
docs/details/base-widget/layer.rst
Normal file
@@ -0,0 +1,126 @@
|
||||
.. _layers:
|
||||
|
||||
======
|
||||
Layers
|
||||
======
|
||||
|
||||
When the term "layer" is used in LVGL documentation, it may refer to one of several
|
||||
things:
|
||||
|
||||
1. for Widgets, the :ref:`layers_creation` creates a natural layering of Widgets;
|
||||
2. in the context of pixel rendering (drawing), there are :ref:`draw_layers`;
|
||||
3. permanent :ref:`screen_layers` are part of each :ref:`display` object, and
|
||||
are covered :ref:`here <screen_layers>`
|
||||
|
||||
#1 and #2 are covered below.
|
||||
|
||||
|
||||
|
||||
.. _layers_creation:
|
||||
|
||||
Order of Creation
|
||||
*****************
|
||||
|
||||
By default, LVGL draws new Widgets on top of old Widgets.
|
||||
|
||||
For example, assume we add a button to a parent Widget named button1 and
|
||||
then another button named button2. Then button1 (along with its child
|
||||
Widget(s)) will be in the background and can be covered by button2 and
|
||||
its children.
|
||||
|
||||
.. image:: /misc/layers.png
|
||||
|
||||
.. code-block:: 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:
|
||||
|
||||
Changing Order
|
||||
--------------
|
||||
|
||||
There are four explicit ways to bring a Widget to the foreground:
|
||||
|
||||
- Use :cpp:expr:`lv_obj_move_foreground(widget)` to bring a Widget to the foreground.
|
||||
Similarly, use :cpp:expr:`lv_obj_move_background(widget)` to move it to the background.
|
||||
- Use :cpp:expr:`lv_obj_move_to_index(widget, idx)` to move a Widget 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(widget, lv_obj_get_index(widget) - 1)`
|
||||
|
||||
- Use :cpp:expr:`lv_obj_swap(widget1, widget2)` to swap the relative layer position of two Widgets.
|
||||
- When :cpp:expr:`lv_obj_set_parent(widget, new_parent)` is used, ``widget`` will be on the foreground of ``new_parent``.
|
||||
|
||||
|
||||
.. _draw_layers:
|
||||
|
||||
Draw Layers
|
||||
***********
|
||||
|
||||
Some style properties cause 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 the 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 the layer after
|
||||
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 causes LVGL to create a 2 layers with radius
|
||||
height for the top and bottom parts of the Widget.
|
||||
|
||||
|
||||
.. _layers_api:
|
||||
|
||||
API
|
||||
***
|
||||
178
docs/details/base-widget/layouts/flex.rst
Normal file
178
docs/details/base-widget/layouts/flex.rst
Normal file
@@ -0,0 +1,178 @@
|
||||
.. _flex:
|
||||
|
||||
====
|
||||
Flex
|
||||
====
|
||||
|
||||
Overview
|
||||
********
|
||||
|
||||
The Flexbox (or Flex for short) is a subset of `CSS Flexbox <https://css-tricks.com/snippets/css/a-guide-to-flexbox/>`__.
|
||||
|
||||
It can arrange items into rows or columns (tracks), handle wrapping,
|
||||
adjust the spacing between the items and tracks, handle *grow* to make
|
||||
the item(s) fill the remaining space with respect to min/max width and
|
||||
height.
|
||||
|
||||
To make a Widget flex container call
|
||||
:cpp:expr:`lv_obj_set_layout(widget, LV_LAYOUT_FLEX)`.
|
||||
|
||||
Note that the flex layout feature of LVGL needs to be globally enabled
|
||||
with :c:macro:`LV_USE_FLEX` in ``lv_conf.h``.
|
||||
|
||||
Terms
|
||||
*****
|
||||
|
||||
- **tracks**: the rows or columns
|
||||
- **main direction**: row or column, the direction in which the items are
|
||||
placed
|
||||
- **cross direction**: perpendicular to the main direction
|
||||
- **wrap**: if there is no more space in the track a new track is started
|
||||
- **grow**: if set on an item it will grow to fill the remaining space on
|
||||
the track. The available space will be distributed among items
|
||||
respective to their grow value (larger value means more space)
|
||||
- **gap**: the space between the rows and columns or the items on a track
|
||||
|
||||
Simple interface
|
||||
****************
|
||||
|
||||
With the following functions you can set a Flex layout on any parent.
|
||||
|
||||
.. _flex_flow:
|
||||
|
||||
Flex flow
|
||||
---------
|
||||
|
||||
:cpp:expr:`lv_obj_set_flex_flow(widget, flex_flow)`
|
||||
|
||||
The possible values for ``flex_flow`` are:
|
||||
|
||||
- :cpp:enumerator:`LV_FLEX_FLOW_ROW`: Place the children in a row without wrapping
|
||||
- :cpp:enumerator:`LV_FLEX_FLOW_COLUMN`: Place the children in a column without wrapping
|
||||
- :cpp:enumerator:`LV_FLEX_FLOW_ROW_WRAP`: Place the children in a row with wrapping
|
||||
- :cpp:enumerator:`LV_FLEX_FLOW_COLUMN_WRAP`: Place the children in a column with wrapping
|
||||
- :cpp:enumerator:`LV_FLEX_FLOW_ROW_REVERSE`: Place the children in a row without wrapping but in reversed order
|
||||
- :cpp:enumerator:`LV_FLEX_FLOW_COLUMN_REVERSE`: Place the children in a column without wrapping but in reversed order
|
||||
- :cpp:enumerator:`LV_FLEX_FLOW_ROW_WRAP_REVERSE`: Place the children in a row with wrapping but in reversed order
|
||||
- :cpp:enumerator:`LV_FLEX_FLOW_COLUMN_WRAP_REVERSE`: Place the children in a column with wrapping but in reversed order
|
||||
|
||||
.. _flex_align:
|
||||
|
||||
Flex align
|
||||
----------
|
||||
|
||||
To manage the placement of the children use
|
||||
:cpp:expr:`lv_obj_set_flex_align(widget, main_place, cross_place, track_cross_place)`
|
||||
|
||||
- ``main_place`` determines how to distribute the items in their track
|
||||
on the main axis. E.g. flush the items to the right on :cpp:enumerator:`LV_FLEX_FLOW_ROW_WRAP`. (It's called
|
||||
``justify-content`` in CSS)
|
||||
- ``cross_place`` determines how to distribute the items in their track
|
||||
on the cross axis. E.g. if the items have different height place them
|
||||
to the bottom of the track. (It's called ``align-items`` in CSS)
|
||||
- ``track_cross_place`` determines how to distribute the tracks (It's
|
||||
called ``align-content`` in CSS)
|
||||
|
||||
The possible values are:
|
||||
|
||||
- :cpp:enumerator:`LV_FLEX_ALIGN_START`: means left on a horizontally and top vertically (default)
|
||||
- :cpp:enumerator:`LV_FLEX_ALIGN_END`: means right on a horizontally and bottom vertically
|
||||
- :cpp:enumerator:`LV_FLEX_ALIGN_CENTER`: simply center
|
||||
- :cpp:enumerator:`LV_FLEX_ALIGN_SPACE_EVENLY`: items are distributed so
|
||||
that the spacing between any two items (and the space to the edges) is
|
||||
equal. Does not apply to ``track_cross_place``.
|
||||
- :cpp:enumerator:`LV_FLEX_ALIGN_SPACE_AROUND`: items are evenly
|
||||
distributed in the track with equal space around them. Note that
|
||||
visually the spaces aren't equal, since all the items have equal space
|
||||
on both sides. The first item will have one unit of space against the
|
||||
container edge, but two units of space between the next item because
|
||||
that next item has its own spacing that applies. Not applies to
|
||||
``track_cross_place``.
|
||||
- :cpp:enumerator:`LV_FLEX_ALIGN_SPACE_BETWEEN`: items are evenly distributed in
|
||||
the track: first item is on the start line, last item on the end line. Not applies to ``track_cross_place``.
|
||||
|
||||
.. _flex_grow:
|
||||
|
||||
Flex grow
|
||||
---------
|
||||
|
||||
Flex grow can be used to make one or more children fill the available
|
||||
space on the track. When more children have grow parameters, the
|
||||
available space will be distributed proportionally to the grow values.
|
||||
For example, there is 400 px remaining space and 4 Widgets with grow:
|
||||
|
||||
- ``A`` with grow = 1
|
||||
- ``B`` with grow = 1
|
||||
- ``C`` with grow = 2
|
||||
|
||||
``A`` and ``B`` will have 100 px size, and ``C`` will have 200 px size.
|
||||
|
||||
Flex grow can be set on a child with
|
||||
:cpp:expr:`lv_obj_set_flex_grow(child, value)`. ``value`` needs to be >
|
||||
1 or 0 to disable grow on the child.
|
||||
|
||||
.. _flex_style:
|
||||
|
||||
Style interface
|
||||
***************
|
||||
|
||||
All the Flex-related values are style properties under the hood and you
|
||||
can use them similarly to any other style property.
|
||||
|
||||
The following flex related style properties exist:
|
||||
|
||||
- :cpp:enumerator:`FLEX_FLOW`
|
||||
- :cpp:enumerator:`FLEX_MAIN_PLACE`
|
||||
- :cpp:enumerator:`FLEX_CROSS_PLACE`
|
||||
- :cpp:enumerator:`FLEX_TRACK_PLACE`
|
||||
- :cpp:enumerator:`FLEX_GROW`
|
||||
|
||||
.. _flex_padding:
|
||||
|
||||
Internal padding
|
||||
----------------
|
||||
|
||||
To modify the minimum space flexbox inserts between Widgets, the
|
||||
following properties can be set on the flex container style:
|
||||
|
||||
- ``pad_row`` Sets the padding between the rows.
|
||||
|
||||
- ``pad_column`` Sets the padding between the columns.
|
||||
|
||||
These can for example be used if you don't want any padding between your
|
||||
Widgets: :cpp:expr:`lv_style_set_pad_column(&row_container_style,0)`
|
||||
|
||||
.. _flex_other:
|
||||
|
||||
Other features
|
||||
**************
|
||||
|
||||
RTL
|
||||
---
|
||||
|
||||
If the base direction of the container is set the
|
||||
:cpp:enumerator:`LV_BASE_DIR_RTL` the meaning of
|
||||
:cpp:enumerator:`LV_FLEX_ALIGN_START` and
|
||||
:cpp:enumerator:`LV_FLEX_ALIGN_END` is swapped on ``ROW`` layouts. I.e.
|
||||
``START`` will mean right.
|
||||
|
||||
The items on ``ROW`` layouts, and tracks of ``COLUMN`` layouts will be
|
||||
placed from right to left.
|
||||
|
||||
New track
|
||||
---------
|
||||
|
||||
You can force Flex to put an item into a new line with
|
||||
:cpp:expr:`lv_obj_add_flag(child, LV_OBJ_FLAG_FLEX_IN_NEW_TRACK)`.
|
||||
|
||||
.. _flex_example:
|
||||
|
||||
Example
|
||||
*******
|
||||
|
||||
.. include:: ../../../examples/layouts/flex/index.rst
|
||||
|
||||
.. _flex_api:
|
||||
|
||||
API
|
||||
***
|
||||
184
docs/details/base-widget/layouts/grid.rst
Normal file
184
docs/details/base-widget/layouts/grid.rst
Normal file
@@ -0,0 +1,184 @@
|
||||
.. _grid:
|
||||
|
||||
====
|
||||
Grid
|
||||
====
|
||||
|
||||
Overview
|
||||
********
|
||||
|
||||
The Grid layout is a subset of `CSS Grid <https://css-tricks.com/snippets/css/complete-guide-grid/>`__.
|
||||
|
||||
It can arrange items into a 2D "table" that has rows or columns
|
||||
(tracks). The item can span through multiple columns or rows. The
|
||||
track's size can be set in pixel, to the largest item
|
||||
(:c:macro:`LV_GRID_CONTENT`) or in "Free unit" (FR) to distribute the free
|
||||
space proportionally.
|
||||
|
||||
To make a Widget a grid container call :cpp:expr:`lv_obj_set_layout(widget, LV_LAYOUT_GRID)`.
|
||||
|
||||
Note that the grid layout feature of LVGL needs to be globally enabled
|
||||
with :c:macro:`LV_USE_GRID` in ``lv_conf.h``.
|
||||
|
||||
Terms
|
||||
*****
|
||||
|
||||
- **tracks**: the rows or columns
|
||||
- **free unit (FR)**: if set on track's size is set in ``FR`` it will grow
|
||||
to fill the remaining space on the parent.
|
||||
- **gap**: the space between the rows and columns or the items on a track
|
||||
|
||||
Simple interface
|
||||
****************
|
||||
|
||||
With the following functions you can easily set a Grid layout on any
|
||||
parent.
|
||||
|
||||
.. _grid_descriptors:
|
||||
|
||||
Grid descriptors
|
||||
----------------
|
||||
|
||||
First you need to describe the size of rows and columns. It can be done
|
||||
by declaring 2 arrays and the track sizes in them. The last element must
|
||||
be :c:macro:`LV_GRID_TEMPLATE_LAST`.
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
static int32_t column_dsc[] = {100, 400, LV_GRID_TEMPLATE_LAST}; /* 2 columns with 100 and 400 ps width */
|
||||
static int32_t row_dsc[] = {100, 100, 100, LV_GRID_TEMPLATE_LAST}; /* 3 100 px tall rows */
|
||||
|
||||
To set the descriptors on a parent use
|
||||
:cpp:expr:`lv_obj_set_grid_dsc_array(widget, col_dsc, row_dsc)`.
|
||||
|
||||
Besides simple settings the size in pixel you can use two special
|
||||
values:
|
||||
|
||||
- :c:macro:`LV_GRID_CONTENT` set the size to fit the largest child on this track
|
||||
- :cpp:expr:`LV_GRID_FR(X)` tell what portion of the remaining space
|
||||
should be used by this track. Larger value means larger space.
|
||||
|
||||
.. _grid_items:
|
||||
|
||||
Grid items
|
||||
----------
|
||||
|
||||
By default, the children are not added to the grid. They need to be
|
||||
added manually to a cell.
|
||||
|
||||
To do this call
|
||||
:cpp:expr:`lv_obj_set_grid_cell(child, column_align, column_pos, column_span, row_align, row_pos, row_span)`.
|
||||
|
||||
``column_align`` and ``row_align`` determine how to align the children
|
||||
in its cell. The possible values are:
|
||||
|
||||
- :cpp:enumerator:`LV_GRID_ALIGN_START`: means left on a horizontally and top vertically (default)
|
||||
- :cpp:enumerator:`LV_GRID_ALIGN_END`: means right on a horizontally and bottom vertically
|
||||
- :cpp:enumerator:`LV_GRID_ALIGN_CENTER`: simply center ``column_pos`` and ``row_pos``
|
||||
means the zero based index of the cell into the item should be placed.
|
||||
|
||||
``column_span`` and ``row_span`` means how many tracks should the item
|
||||
involve from the start cell. Must be ``>= 1``.
|
||||
|
||||
.. _grid_align:
|
||||
|
||||
Grid align
|
||||
----------
|
||||
|
||||
If there are some empty space the track can be aligned several ways:
|
||||
|
||||
- :cpp:enumerator:`LV_GRID_ALIGN_START`: means left on a horizontally and top vertically. (default)
|
||||
- :cpp:enumerator:`LV_GRID_ALIGN_END`: means right on a horizontally and bottom vertically
|
||||
- :cpp:enumerator:`LV_GRID_ALIGN_CENTER`: simply center
|
||||
- :cpp:enumerator:`LV_GRID_ALIGN_SPACE_EVENLY`: items are distributed so that the spacing
|
||||
between any two items (and the space to the edges) is equal. Not applies to ``track_cross_place``.
|
||||
- :cpp:enumerator:`LV_GRID_ALIGN_SPACE_AROUND`: items are
|
||||
evenly distributed in the track with equal space around them. Note that
|
||||
visually the spaces aren't equal, since all the items have equal space
|
||||
on both sides. The first item will have one unit of space against the
|
||||
container edge, but two units of space between the next item because
|
||||
that next item has its own spacing that applies. Not applies to ``track_cross_place``.
|
||||
- :cpp:enumerator:`LV_GRID_ALIGN_SPACE_BETWEEN`: items are
|
||||
evenly distributed in the track: first item is on the start line, last
|
||||
item on the end line. Not applies to ``track_cross_place``.
|
||||
|
||||
To set the track's alignment use
|
||||
:cpp:expr:`lv_obj_set_grid_align(widget, column_align, row_align)`.
|
||||
|
||||
.. _grid_subgrid:
|
||||
|
||||
Sub grid
|
||||
--------
|
||||
|
||||
If you set the column and/or row grid descriptors of a widget to ``NULL`` it will use the grid descriptor(s) from it's parent.
|
||||
For example if you create a grid item on 2..6 columns and 1..3 rows of the parent,
|
||||
the grid item will see 5 columns and 4 rows with the corresponding track size from the parent.
|
||||
|
||||
This way even if a wrapper item is used on the grid and can be made "transparent" from the grid's point of view.
|
||||
|
||||
Limitations:
|
||||
|
||||
- The sub grid is resolved only in one level depth. That is a grid can have a sub grid children, but a sub grid can't have another sub grid.
|
||||
- ``LV_GRID_CONTENT`` tracks on the are not handled in the sub grid, only in the its own grid.
|
||||
|
||||
The sub grid feature works the same as in CSS. For further reference see `this description <https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout/Subgrid>`__.
|
||||
|
||||
.. _grid_style:
|
||||
|
||||
Style interface
|
||||
***************
|
||||
|
||||
All the Grid related values are style properties under the hood and you
|
||||
can use them similarly to any other style properties.
|
||||
|
||||
The following Grid related style properties exist:
|
||||
|
||||
- :cpp:enumerator:`GRID_COLUMN_DSC_ARRAY`
|
||||
- :cpp:enumerator:`GRID_ROW_DSC_ARRAY`
|
||||
- :cpp:enumerator:`GRID_COLUMN_ALIGN`
|
||||
- :cpp:enumerator:`GRID_ROW_ALIGN`
|
||||
- :cpp:enumerator:`GRID_CELL_X_ALIGN`
|
||||
- :cpp:enumerator:`GRID_CELL_COLUMN_POS`
|
||||
- :cpp:enumerator:`GRID_CELL_COLUMN_SPAN`
|
||||
- :cpp:enumerator:`GRID_CELL_Y_ALIGN`
|
||||
- :cpp:enumerator:`GRID_CELL_ROW_POS`
|
||||
- :cpp:enumerator:`GRID_CELL_ROW_SPAN`
|
||||
|
||||
.. _grid_padding:
|
||||
|
||||
Internal padding
|
||||
----------------
|
||||
|
||||
To modify the minimum space Grid inserts between Widgets, the following
|
||||
properties can be set on the Grid container style:
|
||||
|
||||
- ``pad_row`` Sets the padding between the rows.
|
||||
- ``pad_column`` Sets the padding between the columns.
|
||||
|
||||
.. _grid_other:
|
||||
|
||||
Other features
|
||||
**************
|
||||
|
||||
RTL
|
||||
---
|
||||
|
||||
If the base direction of the container is set to :cpp:enumerator:`LV_BASE_DIR_RTL`,
|
||||
the meaning of :cpp:enumerator:`LV_GRID_ALIGN_START` and :cpp:enumerator:`LV_GRID_ALIGN_END` is
|
||||
swapped. I.e. ``START`` will mean right-most.
|
||||
|
||||
The columns will be placed from right to left.
|
||||
|
||||
.. _grid_examples:
|
||||
|
||||
Example
|
||||
*******
|
||||
|
||||
.. include:: ../../../examples/layouts/grid/index.rst
|
||||
|
||||
.. _grid_api:
|
||||
|
||||
API
|
||||
***
|
||||
12
docs/details/base-widget/layouts/index.rst
Normal file
12
docs/details/base-widget/layouts/index.rst
Normal file
@@ -0,0 +1,12 @@
|
||||
.. _layouts:
|
||||
|
||||
=======
|
||||
Layouts
|
||||
=======
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
flex
|
||||
grid
|
||||
633
docs/details/base-widget/obj.rst
Normal file
633
docs/details/base-widget/obj.rst
Normal file
@@ -0,0 +1,633 @@
|
||||
.. _widget_basics:
|
||||
|
||||
=============
|
||||
Widget Basics
|
||||
=============
|
||||
|
||||
|
||||
|
||||
What is a Widget?
|
||||
*****************
|
||||
A Widget is the **basic building block** of the LVGL user interface.
|
||||
|
||||
Examples of Widgets: :ref:`Base Widget (and Screen) <base_widget>`,
|
||||
:ref:`Button <lv_button>`, :ref:`Label <lv_label>`,
|
||||
:ref:`Image <lv_image>`, :ref:`List <lv_list>`,
|
||||
:ref:`Chart <lv_chart>` and :ref:`Text Area <lv_textarea>`.
|
||||
|
||||
See :ref:`widgets` to see all Widget types.
|
||||
|
||||
All Widgets are referenced using an :cpp:type:`lv_obj_t` pointer as a handle.
|
||||
This pointer can later be used to read or change the Widget's attributes.
|
||||
|
||||
|
||||
|
||||
.. _base_widget:
|
||||
|
||||
Base Widget
|
||||
***********
|
||||
The most fundamental of all Widgets is the Base Widget, on which all other widgets
|
||||
are based. From an Object-Oriented perspective, think of the Base Widget as the
|
||||
Widget class from which all other Widgets inherit.
|
||||
|
||||
The functions and functionalities of the Base Widget can be used with
|
||||
other widgets as well. For example :cpp:expr:`lv_obj_set_width(slider, 100)`.
|
||||
|
||||
The Base Widget can be used directly as a simple widget. While it is a simple
|
||||
rectangle, it has a large number of features shared with all Widgets, detailed
|
||||
below and in subsequent pages. In HTML terms, think of it as a ``<div>``.
|
||||
|
||||
|
||||
|
||||
.. _widget_attributes:
|
||||
|
||||
Attributes
|
||||
**********
|
||||
|
||||
|
||||
Basic attributes
|
||||
----------------
|
||||
|
||||
All Widget types share some basic attributes:
|
||||
|
||||
- Position
|
||||
- Size
|
||||
- Parent
|
||||
- Styles
|
||||
- Events it emits
|
||||
- Flags like *Clickable*, *Scollable*, etc.
|
||||
- Etc.
|
||||
|
||||
You can set/get these attributes with ``lv_obj_set_...`` and
|
||||
``lv_obj_get_...`` functions. For example:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
/* Set basic Widget 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 */
|
||||
|
||||
For complete details on position, size, coordinates and layouts, see :ref:`coord`.
|
||||
|
||||
|
||||
Widget-specific attributes
|
||||
--------------------------
|
||||
|
||||
The Widget types have special attributes as well. For example, a slider has
|
||||
|
||||
- Minimum and maximum values
|
||||
- Current value
|
||||
|
||||
For these special attributes, every Widget type may have unique API
|
||||
functions. For example for a slider:
|
||||
|
||||
.. code-block:: 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*)
|
||||
|
||||
|
||||
.. _lv_obj_parents_and_children:
|
||||
|
||||
Parents and children
|
||||
--------------------
|
||||
|
||||
A Widget's parent is set when the widget is created --- the parent is passed to the
|
||||
creation function.
|
||||
|
||||
To get a Widget's current parent, use :cpp:expr:`lv_obj_get_parent(widget)`.
|
||||
|
||||
You can move the Widget to a new parent with :cpp:expr:`lv_obj_set_parent(widget, new_parent)`.
|
||||
|
||||
To get a specific child of a parent use :cpp:expr:`lv_obj_get_child(parent, idx)`.
|
||||
Some examples for ``idx``:
|
||||
|
||||
- ``0`` get the child created first
|
||||
- ``1`` get the child created second
|
||||
- ``-1`` get the child created last
|
||||
|
||||
You can iterate through a parent Widget's children like this:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
uint32_t i;
|
||||
for(i = 0; i < lv_obj_get_child_count(parent); i++) {
|
||||
lv_obj_t * child = lv_obj_get_child(parent, i);
|
||||
/* Do something with child. */
|
||||
}
|
||||
|
||||
:cpp:expr:`lv_obj_get_index(widget)` returns the index of the Widget in its parent.
|
||||
It is equivalent to the number of older children in the parent.
|
||||
|
||||
You can bring a Widget to the foreground or send it to the background with
|
||||
:cpp:expr:`lv_obj_move_foreground(widget)` and :cpp:expr:`lv_obj_move_background(widget)`.
|
||||
|
||||
You can change the index of a Widget in its parent using :cpp:expr:`lv_obj_move_to_index(widget, index)`.
|
||||
|
||||
You can swap the position of two Widgets with :cpp:expr:`lv_obj_swap(widget1, widget2)`.
|
||||
|
||||
To get a Widget's Screen (highest-level parent) use :cpp:expr:`lv_obj_get_screen(widget)`.
|
||||
|
||||
|
||||
|
||||
.. _widget_working_mechanisms:
|
||||
|
||||
Working Mechanisms
|
||||
******************
|
||||
|
||||
Parent-child structure
|
||||
----------------------
|
||||
|
||||
A parent Widget can be considered as the container of its children. Every Widget has
|
||||
exactly one parent Widget (except Screens), but a parent Widget can have any number
|
||||
of children. There is no limitation for the type of the parent but there are Widgets
|
||||
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-block:: c
|
||||
|
||||
lv_obj_t * parent = lv_obj_create(lv_screen_active()); /* Create a parent Widget on current screen */
|
||||
lv_obj_set_size(parent, 100, 80); /* Set size of parent */
|
||||
|
||||
lv_obj_t * obj1 = lv_obj_create(parent); /* Create a Widget on previously created parent Widget */
|
||||
lv_obj_set_pos(widget1, 10, 10); /* Set position of new Widget */
|
||||
|
||||
Modify the position of the parent:
|
||||
|
||||
.. image:: /misc/par_child2.png
|
||||
|
||||
.. code-block:: 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 Widgets 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-block:: c
|
||||
|
||||
lv_obj_set_x(widget1, -30); /* Move the child a little bit off the parent */
|
||||
|
||||
This behavior can be overwritten with
|
||||
:cpp:expr:`lv_obj_add_flag(widget, LV_OBJ_FLAG_OVERFLOW_VISIBLE)` which allow the
|
||||
children to be drawn out of the parent.
|
||||
|
||||
Create and delete Widgets
|
||||
-------------------------
|
||||
|
||||
In LVGL, Widgets can be created and deleted dynamically at run time. It
|
||||
means only the currently created (existing) Widgets 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-block:: 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 Widget to create the new Widget.
|
||||
|
||||
The return value is a pointer to the created Widget with :cpp:type:`lv_obj_t` ``*``
|
||||
type.
|
||||
|
||||
There is a common **delete** function for all Widget types. It deletes
|
||||
the Widget and all of its children.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
void lv_obj_delete(lv_obj_t * widget);
|
||||
|
||||
:cpp:func:`lv_obj_delete` will delete the Widget immediately. If for any reason you
|
||||
can't delete the Widget immediately you can use
|
||||
:cpp:expr:`lv_obj_delete_async(widget)` 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 a Widget in the child's :cpp:enumerator:`LV_EVENT_DELETE`
|
||||
handler.
|
||||
|
||||
You can remove all the children of a Widget (but not the Widget itself)
|
||||
using :cpp:expr:`lv_obj_clean(widget)`.
|
||||
|
||||
You can use :cpp:expr:`lv_obj_delete_delayed(widget, 1000)` to delete a Widget after
|
||||
some time. The delay is expressed in milliseconds.
|
||||
|
||||
Sometimes you're not sure whether a Widget was deleted and you need some way to
|
||||
check if it's still "alive". Anytime before the Widget is deleted, you can use
|
||||
cpp:expr:`lv_obj_null_on_delete(&widget)` to cause your Widget pointer to be set to ``NULL``
|
||||
when the Widget is deleted.
|
||||
|
||||
Make sure the pointer variable itself stays valid until the Widget 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.. _screens:
|
||||
|
||||
Screens
|
||||
*******
|
||||
|
||||
What are Screens?
|
||||
-----------------
|
||||
|
||||
Not to be confused with a :ref:`display`, Screens are simply any Widget created
|
||||
without a parent (i.e. passing NULL for the ``parent`` argument during creation). As
|
||||
such, they form the "root" of a Widget Tree.
|
||||
|
||||
Normally the Base Widget is used for this purpose since it has all the features most
|
||||
Screens need. But an :ref:`lv_image` Widget can also be used to create a wallpaper
|
||||
background for the Widget Tree.
|
||||
|
||||
All Screens:
|
||||
|
||||
- are automatically attached to the :ref:`default_display` current when the Screen
|
||||
was created;
|
||||
- automatically occupy the full area of the associated display;
|
||||
- cannot be moved, i.e. functions such as :cpp:func:`lv_obj_set_pos` and
|
||||
:cpp:func:`lv_obj_set_size` cannot be used on screens.
|
||||
|
||||
Each :ref:`display` object can have multiple screens associated with it, but not vice
|
||||
versa. Thus the relationship::
|
||||
|
||||
Display
|
||||
|
|
||||
--- (one or more)
|
||||
/|\
|
||||
Screen Widgets (root of a Widget Tree)
|
||||
|
|
||||
O (zero or more)
|
||||
/|\
|
||||
Child Widgets
|
||||
|
||||
|
||||
Creating Screens
|
||||
----------------
|
||||
|
||||
Screens are created like this:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
lv_obj_t * scr1 = lv_obj_create(NULL);
|
||||
|
||||
Screens can be deleted with :cpp:expr:`lv_obj_delete(scr)`, but be sure you do not
|
||||
delete the :ref:`active_screen`.
|
||||
|
||||
|
||||
.. _active_screen:
|
||||
|
||||
Active Screen
|
||||
-------------
|
||||
While each :ref:`display` object can have any number of Screens Widgets associated
|
||||
with it, only one of those Screens is considered "Active" at any given time. That
|
||||
Screen is referred to as the Display's "Active Screen". For this reason, only one
|
||||
Screen and its child Widgets will ever be shown on a display at one time.
|
||||
|
||||
When each :ref:`display` object was created, a default screen was created with it and
|
||||
set as its "Active Screen".
|
||||
|
||||
To get a pointer to the "Active Screen", call :cpp:func:`lv_screen_active`.
|
||||
|
||||
To set a Screen to be the "Active Screen", call :cpp:func:`lv_screen_load` or
|
||||
:cpp:func:`lv_screen_load_anim`.
|
||||
|
||||
|
||||
.. _loading_screens:
|
||||
|
||||
Loading Screens
|
||||
---------------
|
||||
|
||||
To load a new screen, use :cpp:expr:`lv_screen_load(scr1)`. This sets ``scr1`` as
|
||||
the Active Screen.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
.. _layers_overview:
|
||||
|
||||
Layers
|
||||
------
|
||||
|
||||
When an ``lv_display_t`` object is created, 4 Screens (layers) are created and
|
||||
attached to it.
|
||||
|
||||
1. Bottom Layer
|
||||
2. Active Screen
|
||||
3. Top Layer
|
||||
4. System Layer
|
||||
|
||||
1, 3 and 4 are independent of the :ref:`active_screen` and they will be shown (if
|
||||
they contain anything that is visible) regardless of which screen is the Active
|
||||
Screen. See :ref:`screen_layers` for more information.
|
||||
|
||||
|
||||
.. _transparent_screens:
|
||||
|
||||
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`` will be visible. If the bottom layer's
|
||||
opacity is also not :cpp:enumerator:`LV_OPA_COVER` LVGL will have no solid background
|
||||
to draw.
|
||||
|
||||
This configuration (transparent Screen) could be useful to create, for example,
|
||||
on-screen display (OSD) menus where a video is played on a different hardware layer
|
||||
of the display panel, and a menu is overlaid on a higher layer.
|
||||
|
||||
To properly render a UI on a transparent Screen the Display's color format needs to
|
||||
be set to one with an alpha channel (for example LV_COLOR_FORMAT_ARGB8888).
|
||||
|
||||
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 a color format with alpha channel. E.g.
|
||||
:cpp:expr:`lv_display_set_color_format(disp, LV_COLOR_FORMAT_ARGB8888)`
|
||||
|
||||
|
||||
|
||||
.. _widget_parts:
|
||||
|
||||
Parts
|
||||
*****
|
||||
|
||||
The widgets are built from multiple parts. For example a
|
||||
:ref:`Base Widget <base_widget>` 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.
|
||||
|
||||
|
||||
|
||||
.. _widget_states:
|
||||
|
||||
States
|
||||
******
|
||||
|
||||
The Widget 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 a Widget (presses, releases, focuses, etc.). However,
|
||||
the states can be changed manually as well. To set or clear given state (but
|
||||
leave the other states untouched) use
|
||||
:cpp:expr:`lv_obj_add_state(widget, LV_STATE_...)` and
|
||||
:cpp:expr:`lv_obj_remove_state(widget, LV_STATE_...)`. In both cases OR-ed state
|
||||
values can be used as well. E.g.
|
||||
:cpp:expr:`lv_obj_add_state(widget, part, LV_STATE_PRESSED | LV_PRESSED_CHECKED)`.
|
||||
|
||||
To learn more about the states read the related section of the
|
||||
:ref:`Style overview <styles>`.
|
||||
|
||||
|
||||
|
||||
.. _lv_obj_flags:
|
||||
|
||||
Flags
|
||||
*****
|
||||
|
||||
There are some Widget attributes which can be enabled/disabled by
|
||||
:cpp:expr:`lv_obj_add_flag(widget, LV_OBJ_FLAG_...)` and
|
||||
:cpp:expr:`lv_obj_remove_flag(widget, LV_OBJ_FLAG_...)`.
|
||||
|
||||
- :cpp:enumerator:`LV_OBJ_FLAG_HIDDEN` Make the Widget hidden. (Like it wasn't there at all)
|
||||
- :cpp:enumerator:`LV_OBJ_FLAG_CLICKABLE` Make the Widget clickable by input devices
|
||||
- :cpp:enumerator:`LV_OBJ_FLAG_CLICK_FOCUSABLE` Add focused state to the Widget when clicked
|
||||
- :cpp:enumerator:`LV_OBJ_FLAG_CHECKABLE` Toggle checked state when the Widget is clicked
|
||||
- :cpp:enumerator:`LV_OBJ_FLAG_SCROLLABLE` Make the Widget scrollable
|
||||
- :cpp:enumerator:`LV_OBJ_FLAG_SCROLL_ELASTIC` Allow scrolling inside but with slower speed
|
||||
- :cpp:enumerator:`LV_OBJ_FLAG_SCROLL_MOMENTUM` Make the Widget scroll further when "thrown"
|
||||
- :cpp:enumerator:`LV_OBJ_FLAG_SCROLL_ONE` Allow scrolling only one snappable children
|
||||
- :cpp:enumerator:`LV_OBJ_FLAG_SCROLL_CHAIN_HOR` Allow propagating the horizontal scroll to a parent
|
||||
- :cpp:enumerator:`LV_OBJ_FLAG_SCROLL_CHAIN_VER` Allow propagating the vertical scroll to a parent
|
||||
- :cpp:enumerator:`LV_OBJ_FLAG_SCROLL_CHAIN` Simple packaging for (:cpp:expr:`LV_OBJ_FLAG_SCROLL_CHAIN_HOR | LV_OBJ_FLAG_SCROLL_CHAIN_VER`)
|
||||
- :cpp:enumerator:`LV_OBJ_FLAG_SCROLL_ON_FOCUS` Automatically scroll Widget to make it visible when focused
|
||||
- :cpp:enumerator:`LV_OBJ_FLAG_SCROLL_WITH_ARROW` Allow scrolling the focused Widget with arrow keys
|
||||
- :cpp:enumerator:`LV_OBJ_FLAG_SNAPPABLE` If scroll snap is enabled on the parent it can snap to this Widget
|
||||
- :cpp:enumerator:`LV_OBJ_FLAG_PRESS_LOCK` Keep the Widget pressed even if the press slid from the Widget
|
||||
- :cpp:enumerator:`LV_OBJ_FLAG_EVENT_BUBBLE` Propagate the events to the parent as well
|
||||
- :cpp:enumerator:`LV_OBJ_FLAG_GESTURE_BUBBLE` Propagate the gestures to the parent
|
||||
- :cpp:enumerator:`LV_OBJ_FLAG_ADV_HITTEST` Allow performing more accurate hit (click) test. E.g. accounting for rounded corners
|
||||
- :cpp:enumerator:`LV_OBJ_FLAG_IGNORE_LAYOUT` Make the Widget not positioned by the layouts
|
||||
- :cpp:enumerator:`LV_OBJ_FLAG_FLOATING` Do not scroll the Widget when the parent scrolls and ignore layout
|
||||
- :cpp:enumerator:`LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS` Enable sending ``LV_EVENT_DRAW_TASK_ADDED`` events
|
||||
- :cpp:enumerator:`LV_OBJ_FLAG_OVERFLOW_VISIBLE` Do not clip the children's content to the parent's boundary
|
||||
- :cpp:enumerator:`LV_OBJ_FLAG_FLEX_IN_NEW_TRACK` Start a new flex track on this item
|
||||
- :cpp:enumerator:`LV_OBJ_FLAG_LAYOUT_1` Custom flag, free to use by layouts
|
||||
- :cpp:enumerator:`LV_OBJ_FLAG_LAYOUT_2` Custom flag, free to use by layouts
|
||||
- :cpp:enumerator:`LV_OBJ_FLAG_WIDGET_1` Custom flag, free to use by widget
|
||||
- :cpp:enumerator:`LV_OBJ_FLAG_WIDGET_2` Custom flag, free to use by widget
|
||||
- :cpp:enumerator:`LV_OBJ_FLAG_USER_1` Custom flag, free to use by user
|
||||
- :cpp:enumerator:`LV_OBJ_FLAG_USER_2` Custom flag, free to use by user
|
||||
- :cpp:enumerator:`LV_OBJ_FLAG_USER_3` Custom flag, free to use by user
|
||||
- :cpp:enumerator:`LV_OBJ_FLAG_USER_4` Custom flag, free to use by user
|
||||
|
||||
Some examples:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
/* Hide on Widget */
|
||||
lv_obj_add_flag(widget, LV_OBJ_FLAG_HIDDEN);
|
||||
|
||||
/* Make a Widget non-clickable */
|
||||
lv_obj_remove_flag(widget, LV_OBJ_FLAG_CLICKABLE);
|
||||
|
||||
|
||||
|
||||
.. _lv_obj_events:
|
||||
|
||||
Base-Widget Events
|
||||
******************
|
||||
|
||||
.. _widget_events:
|
||||
|
||||
Events from Input Devices
|
||||
-------------------------
|
||||
- :cpp:enumerator:`LV_EVENT_PRESSED` Widget has been pressed.
|
||||
- :cpp:enumerator:`LV_EVENT_PRESSING` Widget is being pressed (sent continuously while pressing).
|
||||
- :cpp:enumerator:`LV_EVENT_PRESS_LOST` Widget is still being pressed but slid cursor/finger off Widget.
|
||||
- :cpp:enumerator:`LV_EVENT_SHORT_CLICKED` Widget was pressed for a short period of time, then released. Not sent if scrolled.
|
||||
- :cpp:enumerator:`LV_EVENT_SINGLE_CLICKED` Sent for first short click within a small distance and short time.
|
||||
- :cpp:enumerator:`LV_EVENT_DOUBLE_CLICKED` Sent for second short click within small distance and short time.
|
||||
- :cpp:enumerator:`LV_EVENT_TRIPLE_CLICKED` Sent for third short click within small distance and short time.
|
||||
- :cpp:enumerator:`LV_EVENT_LONG_PRESSED` Object has been pressed for at least `long_press_time`. Not sent if scrolled.
|
||||
- :cpp:enumerator:`LV_EVENT_LONG_PRESSED_REPEAT` Sent after `long_press_time` in every `long_press_repeat_time` ms. Not sent if scrolled.
|
||||
- :cpp:enumerator:`LV_EVENT_CLICKED` Sent on release if not scrolled (regardless to long press).
|
||||
- :cpp:enumerator:`LV_EVENT_RELEASED` Sent in every cases when Widget 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` Received when scrolling begins.
|
||||
- :cpp:enumerator:`LV_EVENT_SCROLL_END` Scrolling ended.
|
||||
- :cpp:enumerator:`LV_EVENT_SCROLL` Scrolling
|
||||
- :cpp:enumerator:`LV_EVENT_GESTURE` A gesture is detected. Get gesture with `lv_indev_get_gesture_dir(lv_indev_active());`
|
||||
- :cpp:enumerator:`LV_EVENT_KEY` A key is sent to Widget. Get key with `lv_indev_get_key(lv_indev_active());`
|
||||
- :cpp:enumerator:`LV_EVENT_FOCUSED` Widget received focus,
|
||||
- :cpp:enumerator:`LV_EVENT_DEFOCUSED` Widget's focus has been lost.
|
||||
- :cpp:enumerator:`LV_EVENT_LEAVE` Widget's focus has been lost but is still selected.
|
||||
- :cpp:enumerator:`LV_EVENT_HIT_TEST` Perform advanced hit-testing.
|
||||
|
||||
Special Events
|
||||
--------------
|
||||
- :cpp:enumerator:`LV_EVENT_VALUE_CHANGED` when the :cpp:enumerator:`LV_OBJ_FLAG_CHECKABLE` flag is
|
||||
enabled and the Widget was clicked (on transition to/from the checked state)
|
||||
|
||||
Drawing Events
|
||||
--------------
|
||||
- :cpp:enumerator:`LV_EVENT_DRAW_MAIN` Performing drawing of main part
|
||||
- :cpp:enumerator:`LV_EVENT_DRAW_MAIN_BEGIN` Starting drawing of main part
|
||||
- :cpp:enumerator:`LV_EVENT_DRAW_MAIN_END` Finishing drawing of main part
|
||||
- :cpp:enumerator:`LV_EVENT_DRAW_POST` Perform the post draw phase (when all children are drawn)
|
||||
- :cpp:enumerator:`LV_EVENT_DRAW_POST_BEGIN` Starting 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)
|
||||
|
||||
Other Events
|
||||
------------
|
||||
- :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_SIZE_CHANGED` Object coordinates/size have changed
|
||||
- :cpp:enumerator:`LV_EVENT_STYLE_CHANGED` Object's style has changed
|
||||
- :cpp:enumerator:`LV_EVENT_LAYOUT_CHANGED` A child's position has changed due to a layout recalculation (when container has flex or grid layout style)
|
||||
- :cpp:enumerator:`LV_EVENT_GET_SELF_SIZE` Get internal size of a widget
|
||||
|
||||
.. admonition:: Further Reading
|
||||
|
||||
Learn more about :ref:`events`.
|
||||
|
||||
|
||||
|
||||
.. _lv_obj_keys:
|
||||
|
||||
Keys
|
||||
****
|
||||
|
||||
If :cpp:enumerator:`LV_OBJ_FLAG_CHECKABLE` is enabled, :cpp:enumerator:`LV_KEY_RIGHT` and
|
||||
:cpp:enumerator:`LV_KEY_UP` make the Widget checked, and :cpp:enumerator:`LV_KEY_LEFT` and
|
||||
:cpp:enumerator:`LV_KEY_DOWN` make it unchecked.
|
||||
|
||||
If :cpp:enumerator:`LV_OBJ_FLAG_SCROLLABLE` is enabled, but the Widget is not editable
|
||||
(as declared by the widget class), the arrow keys (:cpp:enumerator:`LV_KEY_UP`,
|
||||
:cpp:enumerator:`LV_KEY_DOWN`, :cpp:enumerator:`LV_KEY_LEFT`, :cpp:enumerator:`LV_KEY_RIGHT`) scroll the Widget.
|
||||
If the Widget can only scroll vertically, :cpp:enumerator:`LV_KEY_LEFT` and
|
||||
:cpp:enumerator:`LV_KEY_RIGHT` will scroll up/down instead, making it compatible with
|
||||
an encoder input device. See :ref:`Input devices overview <indev>` for
|
||||
more on encoder behaviors and the edit mode.
|
||||
|
||||
.. admonition:: Further Reading
|
||||
|
||||
Learn more about :ref:`indev_keys`.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.. _widget_snapshot:
|
||||
|
||||
Snapshot
|
||||
********
|
||||
|
||||
A snapshot image can be generated for a Widget together with its
|
||||
children. Check details in :ref:`snapshot`.
|
||||
|
||||
|
||||
|
||||
Example
|
||||
*******
|
||||
|
||||
.. include:: ../../examples/widgets/obj/index.rst
|
||||
|
||||
|
||||
|
||||
.. _lv_obj_api:
|
||||
|
||||
API
|
||||
***
|
||||
294
docs/details/base-widget/scroll.rst
Normal file
294
docs/details/base-widget/scroll.rst
Normal file
@@ -0,0 +1,294 @@
|
||||
.. _scrolling:
|
||||
|
||||
=========
|
||||
Scrolling
|
||||
=========
|
||||
|
||||
|
||||
Overview
|
||||
********
|
||||
|
||||
In LVGL scrolling works very intuitively: if a Widget is outside its
|
||||
parent content area (the size without padding), the parent becomes
|
||||
scrollable and scrollbar(s) will appear. That's it.
|
||||
|
||||
Any Widget can be scrollable including :ref:`base_widget`, ``lv_image``,
|
||||
``lv_button``, ``lv_meter``, etc
|
||||
|
||||
The Widget 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 a Widget is being scrolled
|
||||
- :cpp:enumerator:`LV_SCROLLBAR_MODE_AUTO`: Show scroll bars when the content is large enough to be scrolled
|
||||
|
||||
:cpp:expr:`lv_obj_set_scrollbar_mode(widget, LV_SCROLLBAR_MODE_...)` sets the scrollbar mode on a Widget.
|
||||
|
||||
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-block:: 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(widget, &style_red, LV_PART_SCROLLBAR);
|
||||
|
||||
A Widget goes to the :cpp:enumerator:`LV_STATE_SCROLLED` state while it's being
|
||||
scrolled. This allows adding different styles to the scrollbar or the
|
||||
Widget itself when scrolled. This code makes the scrollbar blue when the
|
||||
Widget is scrolled:
|
||||
|
||||
.. code-block:: 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(widget, &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 a Widget or on
|
||||
the Widget'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:
|
||||
|
||||
Scrolling 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
|
||||
|
||||
|
||||
Features of scrolling
|
||||
*********************
|
||||
|
||||
Besides, managing "normal" scrolling there are many interesting and
|
||||
useful additional features.
|
||||
|
||||
Scrollable
|
||||
----------
|
||||
|
||||
It's possible to make a Widget non-scrollable with
|
||||
:cpp:expr:`lv_obj_remove_flag(widget, LV_OBJ_FLAG_SCROLLABLE)`.
|
||||
|
||||
Non-scrollable Widgets can still propagate the scrolling (chain) to
|
||||
their parents.
|
||||
|
||||
The direction in which scrolling happens can be controlled by
|
||||
:cpp:expr:`lv_obj_set_scroll_dir(widget, 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 a Widget 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 Widget and the
|
||||
parent(s) won't be scrolled.
|
||||
|
||||
Scroll momentum
|
||||
---------------
|
||||
|
||||
When the user scrolls a Widget and releases it, LVGL can emulate
|
||||
inertial momentum for the scrolling. It's like the Widget 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 a Widget 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 Widget.
|
||||
|
||||
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 Widget. When the Widget is
|
||||
released the content scrolled in it will be animated back to the valid
|
||||
position.
|
||||
|
||||
Snapping
|
||||
--------
|
||||
|
||||
The children of a Widget 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.
|
||||
|
||||
A Widget 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 Widget
|
||||
- :cpp:enumerator:`LV_SCROLL_SNAP_END`: Align the children to the right/bottom side of a scrolled Widget
|
||||
- :cpp:enumerator:`LV_SCROLL_SNAP_CENTER`: Align the children to the center of a scrolled Widget
|
||||
|
||||
Snap alignment is set with
|
||||
:cpp:expr:`lv_obj_set_scroll_snap_x(widget, LV_SCROLL_SNAP_...)` and
|
||||
:cpp:expr:`lv_obj_set_scroll_snap_y(widget, LV_SCROLL_SNAP_...)`.
|
||||
|
||||
Under the hood the following happens:
|
||||
|
||||
1. User scrolls a Widget 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 Widgets in a group that are on a scrollable
|
||||
Widget. Pressing the "Tab" button focuses the next Widget but it might
|
||||
be outside the visible area of the scrollable Widget. If the "scroll on
|
||||
focus" feature is enabled LVGL will automatically scroll Widgets to
|
||||
bring their children into view. The scrolling happens recursively
|
||||
therefore even nested scrollable Widgets are handled properly. The
|
||||
Widget 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 Widgets:
|
||||
|
||||
- ``lv_obj_scroll_by(widget, x, y, LV_ANIM_ON/OFF)`` scroll by ``x`` and ``y`` values
|
||||
- ``lv_obj_scroll_to(widget, x, y, LV_ANIM_ON/OFF)`` scroll to bring the given coordinate to the top left corner
|
||||
- ``lv_obj_scroll_to_x(widget, x, LV_ANIM_ON/OFF)`` scroll to bring the given coordinate to the left side
|
||||
- ``lv_obj_scroll_to_y(widget, 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-block:: 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(widget)`` Get the ``x`` coordinate of Widget
|
||||
- ``lv_obj_get_scroll_y(widget)`` Get the ``y`` coordinate of Widget
|
||||
- ``lv_obj_get_scroll_top(widget)`` Get the scroll coordinate from the top
|
||||
- ``lv_obj_get_scroll_bottom(widget)`` Get the scroll coordinate from the bottom
|
||||
- ``lv_obj_get_scroll_left(widget)`` Get the scroll coordinate from the left
|
||||
- ``lv_obj_get_scroll_right(widget)`` Get the scroll coordinate from the right
|
||||
|
||||
|
||||
Self size
|
||||
*********
|
||||
|
||||
Self size is a property of a Widget. 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 a Widget'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 a Widget scrollable but a
|
||||
larger self size will as well.
|
||||
|
||||
LVGL uses the :cpp:enumerator:`LV_EVENT_GET_SELF_SIZE` event to get the self size of
|
||||
a Widget. Here is an example to see how to handle the event:
|
||||
|
||||
.. code-block:: 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
|
||||
***
|
||||
11
docs/details/base-widget/styles/index.rst
Normal file
11
docs/details/base-widget/styles/index.rst
Normal file
@@ -0,0 +1,11 @@
|
||||
.. _styles:
|
||||
|
||||
======
|
||||
Styles
|
||||
======
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
style
|
||||
style-properties
|
||||
1657
docs/details/base-widget/styles/style-properties.rst
Normal file
1657
docs/details/base-widget/styles/style-properties.rst
Normal file
File diff suppressed because it is too large
Load Diff
538
docs/details/base-widget/styles/style.rst
Normal file
538
docs/details/base-widget/styles/style.rst
Normal file
@@ -0,0 +1,538 @@
|
||||
.. _style_details:
|
||||
|
||||
=============
|
||||
Style Details
|
||||
=============
|
||||
|
||||
*Styles* are used to set the appearance of Widgets. Styles in lvgl are
|
||||
heavily inspired by CSS. The concept in a nutshell is that a
|
||||
style is an :cpp:type:`lv_style_t` variable which can hold properties like
|
||||
border width, font, text color and so on. It's similar to a ``class`` in CSS.
|
||||
|
||||
- Styles can be assigned to Widgets 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 Widgets.
|
||||
- Styles can be cascaded which means multiple styles may be assigned to a Widget 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 Widget will be used.
|
||||
- Some properties (e.g. text color) can be inherited from a parent(s) if it's not specified in a Widget.
|
||||
- Widgets 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 Widget changes state.
|
||||
|
||||
.. _styles_states:
|
||||
|
||||
States
|
||||
******
|
||||
|
||||
The Widgets 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
|
||||
|
||||
A Widget 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 Widget is in the default state, so it's a simple case:
|
||||
the property is perfectly defined in the Widget's current state as
|
||||
white.
|
||||
2. When the Widget 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 Widget has focus 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 Widget has focus 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 Widget 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 Widget 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 a Widget has focus 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 Widget 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 a Widget 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 Widget's styles. Inheritance is applied only if the given
|
||||
property is not set in the Widget'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 a Widget 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
|
||||
*****
|
||||
|
||||
Widgets 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 Widgets 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-block:: 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-block:: c
|
||||
|
||||
lv_style_remove_prop(&style, LV_STYLE_BG_COLOR);
|
||||
|
||||
To get a property's value from a style:
|
||||
|
||||
.. code-block:: 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-block:: c
|
||||
|
||||
lv_style_reset(&style);
|
||||
|
||||
Styles can be built as ``const`` as well to save RAM:
|
||||
|
||||
.. code-block:: 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 a Widget
|
||||
to take effect.
|
||||
|
||||
Add styles
|
||||
----------
|
||||
|
||||
To add a style to a Widget use
|
||||
``lv_obj_add_style(widget, &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 Widget is being scrolled
|
||||
- :cpp:expr:`LV_PART_INDICATOR | LV_STATE_PRESSED | LV_STATE_CHECKED` The indicator part when the Widget is pressed and checked at the same time.
|
||||
|
||||
Using :cpp:func:`lv_obj_add_style`:
|
||||
|
||||
.. code-block:: 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 a Widget use
|
||||
:cpp:expr:`lv_obj_replace_style(widget, 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-block:: 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 a Widget use :cpp:expr:`lv_obj_remove_style_all(widget)`.
|
||||
|
||||
To remove specific styles use
|
||||
:cpp:expr:`lv_obj_remove_style(widget, 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 a Widget changes (i.e. a
|
||||
property is added or changed), the Widgets 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(widget)`
|
||||
or :cpp:expr:`lv_obj_invalidate(lv_screen_active())`.
|
||||
2. If more complex style properties were changed or added, and you know which
|
||||
Widget(s) are affected by that style call :cpp:expr:`lv_obj_refresh_style(widget, part, property)`.
|
||||
To refresh all parts and properties use :cpp:expr:`lv_obj_refresh_style(widget, LV_PART_ANY, LV_STYLE_PROP_ANY)`.
|
||||
3. To make LVGL check all Widgets 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 Widgets will be notified about a style change.
|
||||
|
||||
Get a property's value on a Widget
|
||||
-----------------------------------
|
||||
|
||||
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>(widget, <part>)``.
|
||||
These functions use the Widget's current state and if no better candidate exists they return a default value.
|
||||
For example:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
lv_color_t color = lv_obj_get_style_bg_color(btn, LV_PART_MAIN);
|
||||
|
||||
.. _styles_local:
|
||||
|
||||
Local styles
|
||||
************
|
||||
|
||||
In addition to "normal" styles, Widgets 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 Widgets. If used, local styles are allocated automatically, and
|
||||
freed when the Widget is deleted. They are useful to add local
|
||||
customization to a Widget.
|
||||
|
||||
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>(widget, <value>, <selector>);`` For example:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
lv_obj_set_style_bg_color(slider, lv_color_red(), LV_PART_INDICATOR | LV_STATE_FOCUSED);
|
||||
|
||||
|
||||
.. _style_properties_overview:
|
||||
|
||||
Style Properties Overview
|
||||
*************************
|
||||
|
||||
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 a Widget 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 Widget 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-block:: 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-block:: 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
|
||||
***
|
||||
Reference in New Issue
Block a user