Widget Basics
What is a Widget?
A Widget is the basic building block of the LVGL user interface.
Examples of Widgets: Base Widget (and Screen), Button, Label, Image, List, Chart and Text Area.
See Widgets to see all Widget types.
All Widgets are referenced using an lv_obj_t
pointer as a handle.
This pointer can later be used to read or change the Widget's attributes.
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 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>
.
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:
/* 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 Positions, Sizes and Layouts.
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:
/* 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 Documentation but you can also check the respective header files (e.g. widgets/lv_slider.h)
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 lv_obj_get_parent(widget).
You can move the Widget to a new parent with lv_obj_set_parent(widget, new_parent).
To get a specific child of a parent use lv_obj_get_child(parent, idx).
Some examples for idx
:
0
get the child created first1
get the child created second-1
get the child created last
You can iterate through a parent Widget's children like this:
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. */
}
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 lv_obj_move_foreground(widget) and lv_obj_move_background(widget).
You can change the index of a Widget in its parent using lv_obj_move_to_index(widget, index).
You can swap the position of two Widgets with lv_obj_swap(widget1, widget2).
To get a Widget's Screen (highest-level parent) use lv_obj_get_screen(widget).
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.
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:
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.
lv_obj_set_x(widget1, -30); /* Move the child a little bit off the parent */
This behavior can be overwritten with lv_obj_add_flag(widget, LV_OBJ_FLAG_OVERFLOW_VISIBLE) which allow the children to be drawn out of the parent. In addition to this, you must register the following event callback (this was not required in previous versions).
Note: ext_width
should be the maximum absolute width the children will be
drawn within.
static void ext_draw_size_event_cb(lv_event_t * e)
{
lv_event_set_ext_draw_size(e, 30); /*Set 30px extra draw area around the widget*/
}
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:
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 lv_obj_t
*
type.
There is a common delete function for all Widget types. It deletes the Widget and all of its children.
void lv_obj_delete(lv_obj_t * widget);
lv_obj_delete()
will delete the Widget immediately. If for any reason you
can't delete the Widget immediately you can use
lv_obj_delete_async(widget) which will perform the deletion on the next
call of lv_timer_handler()
. This is useful e.g. if you want to
delete the parent of a Widget in the child's LV_EVENT_DELETE
handler.
You can remove all the children of a Widget (but not the Widget itself) using lv_obj_clean(widget).
You can use 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:
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
What are Screens?
Not to be confused with a Display (lv_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 Image (lv_image) Widget can also be used to create a wallpaper background for the Widget Tree.
All Screens:
are automatically attached to the 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
lv_obj_set_pos()
andlv_obj_set_size()
cannot be used on screens.
Each Display (lv_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:
lv_obj_t * scr1 = lv_obj_create(NULL);
Screens can be deleted with lv_obj_delete(scr), but be sure you do not delete the Active Screen.
Active Screen
While each Display (lv_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 Display (lv_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 lv_screen_active()
.
To set a Screen to be the "Active Screen", call lv_screen_load()
or
lv_screen_load_anim()
.
Loading Screens
To load a new screen, use 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 lv_screen_load_anim(scr, transition_type, time, delay, auto_del). The following transition types exist:
LV_SCR_LOAD_ANIM_NONE
: Switch immediately afterdelay
millisecondsLV_SCR_LOAD_ANIM_OVER_LEFT
,LV_SCR_LOAD_ANIM_OVER_RIGHT
,LV_SCR_LOAD_ANIM_OVER_TOP
andLV_SCR_LOAD_ANIM_OVER_BOTTOM
: Move the new screen over the current towards the given directionLV_SCR_LOAD_ANIM_OUT_LEFT
,LV_SCR_LOAD_ANIM_OUT_RIGHT
,LV_SCR_LOAD_ANIM_OUT_TOP
andLV_SCR_LOAD_ANIM_OUT_BOTTOM
: Move out the old screen over the current towards the given directionLV_SCR_LOAD_ANIM_MOVE_LEFT
,LV_SCR_LOAD_ANIM_MOVE_RIGHT
,LV_SCR_LOAD_ANIM_MOVE_TOP
andLV_SCR_LOAD_ANIM_MOVE_BOTTOM
: Move both the current and new screens towards the given directionLV_SCR_LOAD_ANIM_FADE_IN
andLV_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 lv_screen_active()
) when
the animation starts after delay
time. All inputs are disabled
during the screen animation.
Layers
When an lv_display_t
object is created, 4 Screens (layers) are created and
attached to it.
Bottom Layer
Active Screen
Top Layer
System Layer
1, 3 and 4 are independent of the Active Screen and they will be shown (if they contain anything that is visible) regardless of which screen is the Active Screen. See Screen Layers for more information.
Transparent Screens
Usually, the opacity of the Screen is 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 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: lv_obj_set_style_bg_opa(lv_screen_active(), LV_OPA_TRANSP, LV_PART_MAIN)Set the bottom layer's
bg_opa
to transparent: lv_obj_set_style_bg_opa(lv_layer_bottom(), LV_OPA_TRANSP, LV_PART_MAIN)Set a color format with alpha channel. E.g. lv_display_set_color_format(disp, LV_COLOR_FORMAT_ARGB8888)
Parts
The widgets are built from multiple parts. For example a Base Widget uses the main and scrollbar parts but a Slider uses the main, indicator and knob parts. Parts are similar to pseudo-elements in CSS.
The following predefined parts exist in LVGL:
LV_PART_MAIN
: A background like rectangleLV_PART_SCROLLBAR
: The scrollbar(s)LV_PART_INDICATOR
: Indicator, e.g. for slider, bar, switch, or the tick box of the checkboxLV_PART_KNOB
: Like a handle to grab to adjust the valueLV_PART_SELECTED
: Indicate the currently selected option or sectionLV_PART_ITEMS
: Used if the widget has multiple similar elements (e.g. table cells)LV_PART_CURSOR
: Mark a specific place e.g. text area's or chart's cursorLV_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 Style overview section.
States
The Widget can be in a combination of the following states:
LV_STATE_DEFAULT
: Normal, released stateLV_STATE_CHECKED
: Toggled or checked stateLV_STATE_FOCUSED
: Focused via keypad or encoder or clicked via touchpad/mouseLV_STATE_FOCUS_KEY
: Focused via keypad or encoder but not via touchpad/mouseLV_STATE_EDITED
: Edit by an encoderLV_STATE_HOVERED
: Hovered by mouse (not supported now)LV_STATE_PRESSED
: Being pressedLV_STATE_SCROLLED
: Being scrolledLV_STATE_DISABLED
: Disabled stateLV_STATE_USER_1
: Custom stateLV_STATE_USER_2
: Custom stateLV_STATE_USER_3
: Custom stateLV_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 lv_obj_add_state(widget, LV_STATE_...) and lv_obj_remove_state(widget, LV_STATE_...). In both cases OR-ed state values can be used as well. E.g. lv_obj_add_state(widget, part, LV_STATE_PRESSED | LV_PRESSED_CHECKED).
To learn more about the states read the related section of the Style overview.
Flags
There are some Widget attributes which can be enabled/disabled by lv_obj_add_flag(widget, LV_OBJ_FLAG_...) and lv_obj_remove_flag(widget, LV_OBJ_FLAG_...).
LV_OBJ_FLAG_HIDDEN
Make the Widget hidden. (Like it wasn't there at all)LV_OBJ_FLAG_CLICKABLE
Make the Widget clickable by input devicesLV_OBJ_FLAG_CLICK_FOCUSABLE
Add focused state to the Widget when clickedLV_OBJ_FLAG_CHECKABLE
Toggle checked state when the Widget is clickedLV_OBJ_FLAG_SCROLLABLE
Make the Widget scrollableLV_OBJ_FLAG_SCROLL_ELASTIC
Allow scrolling inside but with slower speedLV_OBJ_FLAG_SCROLL_MOMENTUM
Make the Widget scroll further when "thrown"LV_OBJ_FLAG_SCROLL_ONE
Allow scrolling only one snappable childrenLV_OBJ_FLAG_SCROLL_CHAIN_HOR
Allow propagating the horizontal scroll to a parentLV_OBJ_FLAG_SCROLL_CHAIN_VER
Allow propagating the vertical scroll to a parentLV_OBJ_FLAG_SCROLL_CHAIN
Simple packaging for (LV_OBJ_FLAG_SCROLL_CHAIN_HOR | LV_OBJ_FLAG_SCROLL_CHAIN_VER)LV_OBJ_FLAG_SCROLL_ON_FOCUS
Automatically scroll Widget to make it visible when focusedLV_OBJ_FLAG_SCROLL_WITH_ARROW
Allow scrolling the focused Widget with arrow keysLV_OBJ_FLAG_SNAPPABLE
If scroll snap is enabled on the parent it can snap to this WidgetLV_OBJ_FLAG_PRESS_LOCK
Keep the Widget pressed even if the press slid from the WidgetLV_OBJ_FLAG_EVENT_BUBBLE
Propagate the events to the parent as wellLV_OBJ_FLAG_GESTURE_BUBBLE
Propagate the gestures to the parentLV_OBJ_FLAG_ADV_HITTEST
Allow performing more accurate hit (click) test. E.g. accounting for rounded cornersLV_OBJ_FLAG_IGNORE_LAYOUT
Make the Widget not positioned by the layoutsLV_OBJ_FLAG_FLOATING
Do not scroll the Widget when the parent scrolls and ignore layoutLV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS
Enable sendingLV_EVENT_DRAW_TASK_ADDED
eventsLV_OBJ_FLAG_OVERFLOW_VISIBLE
Do not clip the children's content to the parent's boundaryLV_OBJ_FLAG_FLEX_IN_NEW_TRACK
Start a new flex track on this itemLV_OBJ_FLAG_LAYOUT_1
Custom flag, free to use by layoutsLV_OBJ_FLAG_LAYOUT_2
Custom flag, free to use by layoutsLV_OBJ_FLAG_WIDGET_1
Custom flag, free to use by widgetLV_OBJ_FLAG_WIDGET_2
Custom flag, free to use by widgetLV_OBJ_FLAG_USER_1
Custom flag, free to use by userLV_OBJ_FLAG_USER_2
Custom flag, free to use by userLV_OBJ_FLAG_USER_3
Custom flag, free to use by userLV_OBJ_FLAG_USER_4
Custom flag, free to use by user
Some examples:
/* 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);
Base-Widget Events
Events from Input Devices
LV_EVENT_PRESSED
Widget has been pressed.LV_EVENT_PRESSING
Widget is being pressed (sent continuously while pressing).LV_EVENT_PRESS_LOST
Widget is still being pressed but slid cursor/finger off Widget.LV_EVENT_SHORT_CLICKED
Widget was pressed for a short period of time, then released. Not sent if scrolled.LV_EVENT_SINGLE_CLICKED
Sent for first short click within a small distance and short time.LV_EVENT_DOUBLE_CLICKED
Sent for second short click within small distance and short time.LV_EVENT_TRIPLE_CLICKED
Sent for third short click within small distance and short time.LV_EVENT_LONG_PRESSED
Object has been pressed for at least long_press_time. Not sent if scrolled.LV_EVENT_LONG_PRESSED_REPEAT
Sent after long_press_time in every long_press_repeat_time ms. Not sent if scrolled.LV_EVENT_CLICKED
Sent on release if not scrolled (regardless to long press).LV_EVENT_RELEASED
Sent in every cases when Widget has been released.LV_EVENT_SCROLL_BEGIN
Scrolling begins. The event parameter is a pointer to the animation of the scroll. Can be modified.LV_EVENT_SCROLL_THROW_BEGIN
Received when scrolling begins.LV_EVENT_SCROLL_END
Scrolling ended.LV_EVENT_SCROLL
ScrollingLV_EVENT_GESTURE
A gesture is detected. Get gesture with lv_indev_get_gesture_dir(lv_indev_active());LV_EVENT_KEY
A key is sent to Widget. Get key with lv_indev_get_key(lv_indev_active());LV_EVENT_FOCUSED
Widget received focus,LV_EVENT_DEFOCUSED
Widget's focus has been lost.LV_EVENT_LEAVE
Widget's focus has been lost but is still selected.LV_EVENT_HIT_TEST
Perform advanced hit-testing.
Special Events
LV_EVENT_VALUE_CHANGED
when theLV_OBJ_FLAG_CHECKABLE
flag is enabled and the Widget was clicked (on transition to/from the checked state)
Drawing Events
LV_EVENT_DRAW_MAIN
Performing drawing of main partLV_EVENT_DRAW_MAIN_BEGIN
Starting drawing of main partLV_EVENT_DRAW_MAIN_END
Finishing drawing of main partLV_EVENT_DRAW_POST
Perform the post draw phase (when all children are drawn)LV_EVENT_DRAW_POST_BEGIN
Starting the post draw phase (when all children are drawn)LV_EVENT_DRAW_POST_END
Finishing the post draw phase (when all children are drawn)
Other Events
LV_EVENT_DELETE
Object is being deletedLV_EVENT_CHILD_CHANGED
Child was removed, added, or its size, position were changedLV_EVENT_CHILD_CREATED
Child was created, always bubbles up to all parentsLV_EVENT_CHILD_DELETED
Child was deleted, always bubbles up to all parentsLV_EVENT_SIZE_CHANGED
Object coordinates/size have changedLV_EVENT_STYLE_CHANGED
Object's style has changedLV_EVENT_LAYOUT_CHANGED
A child's position has changed due to a layout recalculation (when container has flex or grid layout style)LV_EVENT_GET_SELF_SIZE
Get internal size of a widget
Further Reading
Learn more about Events.
Keys
If LV_OBJ_FLAG_CHECKABLE
is enabled, LV_KEY_RIGHT
and
LV_KEY_UP
make the Widget checked, and LV_KEY_LEFT
and
LV_KEY_DOWN
make it unchecked.
If LV_OBJ_FLAG_SCROLLABLE
is enabled, but the Widget is not editable
(as declared by the widget class), the arrow keys (LV_KEY_UP
,
LV_KEY_DOWN
, LV_KEY_LEFT
, LV_KEY_RIGHT
) scroll the Widget.
If the Widget can only scroll vertically, LV_KEY_LEFT
and
LV_KEY_RIGHT
will scroll up/down instead, making it compatible with
an encoder input device. See Input devices overview for
more on encoder behaviors and the edit mode.
Further Reading
Learn more about Keys.
Snapshot
A snapshot image can be generated for a Widget together with its children. Check details in Snapshot.
Example
Base objects with custom styles
C code
View on GitHub#include "../../lv_examples.h"
#if LV_BUILD_EXAMPLES
void lv_example_obj_1(void)
{
lv_obj_t * obj1;
obj1 = lv_obj_create(lv_screen_active());
lv_obj_set_size(obj1, 100, 50);
lv_obj_align(obj1, LV_ALIGN_CENTER, -60, -30);
static lv_style_t style_shadow;
lv_style_init(&style_shadow);
lv_style_set_shadow_width(&style_shadow, 10);
lv_style_set_shadow_spread(&style_shadow, 5);
lv_style_set_shadow_color(&style_shadow, lv_palette_main(LV_PALETTE_BLUE));
lv_obj_t * obj2;
obj2 = lv_obj_create(lv_screen_active());
lv_obj_add_style(obj2, &style_shadow, 0);
lv_obj_align(obj2, LV_ALIGN_CENTER, 60, 30);
}
#endif
Make an object draggable
C code
View on GitHub#include "../../lv_examples.h"
#if LV_BUILD_EXAMPLES
static void drag_event_handler(lv_event_t * e)
{
lv_obj_t * obj = lv_event_get_target(e);
lv_indev_t * indev = lv_indev_active();
if(indev == NULL) return;
lv_point_t vect;
lv_indev_get_vect(indev, &vect);
int32_t x = lv_obj_get_x_aligned(obj) + vect.x;
int32_t y = lv_obj_get_y_aligned(obj) + vect.y;
lv_obj_set_pos(obj, x, y);
}
/**
* Make an object draggable.
*/
void lv_example_obj_2(void)
{
lv_obj_t * obj;
obj = lv_obj_create(lv_screen_active());
lv_obj_set_size(obj, 150, 100);
lv_obj_add_event_cb(obj, drag_event_handler, LV_EVENT_PRESSING, NULL);
lv_obj_t * label = lv_label_create(obj);
lv_label_set_text(label, "Drag me");
lv_obj_center(label);
}
#endif
Transform object using a 3x3 matrix
C code
View on GitHub#include "../../lv_examples.h"
#if LV_BUILD_EXAMPLES
#if LV_DRAW_TRANSFORM_USE_MATRIX
static void timer_cb(lv_timer_t * timer)
{
lv_obj_t * obj = lv_timer_get_user_data(timer);
static float value = 0.1f;
lv_matrix_t matrix;
lv_matrix_identity(&matrix);
lv_matrix_scale(&matrix, value, 1);
lv_matrix_rotate(&matrix, value * 360);
lv_obj_set_transform(obj, &matrix);
value += 0.01f;
if(value > 2.0f) {
lv_obj_reset_transform(obj);
value = 0.1f;
}
}
void lv_example_obj_3(void)
{
lv_obj_t * obj = lv_obj_create(lv_screen_active());
lv_obj_center(obj);
lv_timer_create(timer_cb, 20, obj);
}
#else
void lv_example_obj_3(void)
{
lv_obj_t * label = lv_label_create(lv_screen_active());
lv_label_set_text_static(label, "LV_DRAW_TRANSFORM_USE_MATRIX is not enabled");
lv_obj_center(label);
}
#endif /*LV_DRAW_TRANSFORM_USE_MATRIX*/
#endif /*LV_BUILD_EXAMPLES*/