feat(docs): reorganize docs (#7136)

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

View File

@@ -0,0 +1,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
***

View 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
***

View 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

View 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
***

View 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
***

View 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
***

View File

@@ -0,0 +1,12 @@
.. _layouts:
=======
Layouts
=======
.. toctree::
:maxdepth: 2
flex
grid

View 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
***

View 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
***

View File

@@ -0,0 +1,11 @@
.. _styles:
======
Styles
======
.. toctree::
:maxdepth: 2
style
style-properties

File diff suppressed because it is too large Load Diff

View 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
***