LVGL examples - Introduction

Hi everyone,

I’ve spent a lot of time integrating LVGL into Annex, and I find it a pity that no one has taken the initiative to publish any programs that use it. I understand that the long list of available functions can seem intimidating for newcomers, but with the examples I’m going to provide, you’ll see that it’s actually quite the opposite. Using LVGL is very simple, and the extensive list of functions is there to allow for very fine-tuned customization of the graphical interface. Every element can be customized if you want, but you can also use them in their default appearance if you don’t feel like overcomplicating things.

Let’s say you can start with the default elements, and then, as you become more familiar with LVGL’s functions, you can dive deeper into the details. In any case, LVGL is a truly professional interface—the kind that powers most HMIs you find on the market, from your car’s dashboard to your washing machine’s interface. Its look and feel are similar to Android’s interface, with a set of graphical elements that are really easy to use.

To use LVGL with Annex, you simply need a version of Annex that includes LVGL support—typically recognizable by the term "LVGL" in the name (such as BLE_CAN_LVGL)—and a display supported by Annex that includes a touchscreen. Starting from version 2.00, Annex with LVGL is available for both ESP32 and ESP32-S3 platforms. Almost all TFT displays on the market are supported, along with four different touchscreen controllers, both SPI and I2C.

It is highly recommended to use a module with PSRAM, especially if you plan to use images that need to be loaded into RAM, and to have an SD card for ample storage of images. While Annex with LVGL can run without these two components, there will be limitations, particularly regarding available memory.

A basic configuration can be an ESP32 with an ILI9341 TFT and an XPT2046 touchscreen controller at 320x240 resolution, or, even better, an ST7796 TFT with the same touchscreen controller at 480x320 resolution.

A good starting point is to use one of the so-called CYD (Cheap Yellow Display) modules, which typically feature an ILI9341 or ST7789 at 320x240 with a touchscreen controller. These modules are directly supported by Annex, whether they use a resistive or capacitive controller.

It’s also possible to modify these CYD modules by adding external PSRAM for more memory, thereby removing most limitations.

For higher quality, a configuration based on an ESP32-S3 with 16MB flash and 8MB PSRAM, paired with a supported display and touchscreen controller, is preferable. The ideal setup is an ESP32-S3 module with an LCD screen—various types are available, typically with resolutions from 480x480 up to 800x480 and a capacitive touch controller.

Several such modules are directly supported by Annex and can be selected from the configuration menu.

So, to get started, we'll use an improved basic configuration: an ESP32 module with PSRAM, a TFT ILI9341 display, and an XPT2046 touchscreen controller—typically a CYD (Cheap Yellow Display) with PSRAM. Having PSRAM is essential for supporting all the examples where images are loaded, especially those that consume a lot of memory.

This setup ensures smooth operation with LVGL, as PSRAM provides the extra memory needed for handling complex graphics and large images, which would otherwise be limited by the ESP32's internal RAM. The CYD module is a popular and directly supported choice, making it easy to get started with LVGL and Annex.

If you don't have PSRAM, probably some examples will not work, particularly those that require loading large images or need significant memory for buffers.

However, don't care, you can still use LVGL and Annex for simpler interfaces and projects that do not demand as much memory.

I will write a series of short chapters as posts in this topic, each one dedicated to a specific subject and supported by a few examples.

Please do not post replies here. I kindly ask you to leave your comments in a separate thread that I will create in parallel. The goal is to keep a continuous thread focused on the same subject, without interruptions from discussions.

Chapter 1: How LVGL Works

Understanding LVGL’s Architecture

Here’s a simple breakdown of how LVGL works inside Annex:

How LVGL Works: Data Flow, Display, and Input

1. Data Flow

LVGL manages the flow of information between your application, the display, and input devices.

- You create widgets (like buttons, labels, sliders) in your code. These widgets are organized in a tree structure, with screen as parent and widgets as children.

- Each widget can also have its own children, forming a hierarchical tree structure with a clear parent-child relationship. This means that not only do screens have child widgets, but widgets themselves can act as parents to other

- LVGL takes care of drawing these widgets on the screen and updating them when something changes.

2. Display

LVGL uses a display driver to show graphics on your screen.

- It draws everything into a memory buffer first. When ready, it sends this buffer to your display using a dedicated function.

- Annex set up the display driver by telling LVGL about your screen’s size and how to send data to it.

3. Input

LVGL can handle different types of input devices: touchscreens, buttons, keypads, mice, etc.

- An input driver reads what the user does (like touching the screen) and tells LVGL.

- LVGL checks for new input regularly, processes it, and triggers events (like button clicks) for your widgets.

4. Refresh

- To keep everything running smoothly, a specific LVGL function (LVGL.REFRESH) must be called in your main loop. This function is responsible for updating the GUI, processing input events, handling animations, and refreshing the display as needed. Without calling this function frequently, the interface will not update or respond to user interactions.

How It All Connects (Step by Step)

- (a) You initialize LVGL and Annex transparently set up display and input drivers.

- (b) Your code creates widgets and arranges them on the screen.

- (c) You regularly call LVGL.REFRESH function which:

- Updates the screen by drawing widgets into the buffer and sending it to the display.

- Checks for user input and processes events.

- Handles animations and timers.

This is your first LVGL program:

'(a) Init LVGL
LVGL.INIT WHITE, darkcyan
'(b) Create a button widget
LVGL.BUTTON
"Button"
do
  '(c) Regularly refresh LVGL
  lvgl.refresh
loop
This is what will be shown on the screen:

You’ll notice that when you touch the screen, the cursor arrow will follow your finger.

Note: The text "Welcome to Annex and LVGL!" displayed in the center of the screen is a default widget. You can easily remove it by using the command LVGL.CLEAN right after LVGL.INIT.

In summary:

LVGL is like a manager that connects your code, the display, and input devices. You just create widgets and respond to events—LVGL handles the rest, making sure everything appears and reacts as expected.

The example program provided demonstrates the minimal code required to make LVGL work with Annex.

INIT and THEME

When working with LVGL in Annex to create graphical user interfaces, two fundamental commands are used to set up and customize your environment: LVGL.INIT and LVGL.SET_THEME. These commands allow you to initialize the graphics library, allocate memory, and define the visual style of your application, making it easy to start building embedded GUIs with a consistent and appealing look.

LVGL.INIT

This command initializes the LVGL library, sets up the display, and allocates memory for both the screen buffer and widgets.

Parameters:

LVGL.SET_THEME

This command defines the overall appearance of your application by setting the color scheme, font, dark or light mode, and theme style.

Parameters:

With these two commands, you can quickly initialize your LVGL environment and apply a consistent, visually pleasing style to your entire interface.

This is an updated program that includes a THEME

'Init LVGL
LVGL.INIT BLUE, darkcyan
' set theme colors, font 30px, no dark mode
LVGL.SET_THEME red, orange, 30, 0
'Create some widgets
LVGL.BUTTON
"Button"
do
  'Regularly refresh LVGL
  lvgl.refresh
loop

This is the result:

You'll notice the differences of colors and text size compared with the previous example

In summary:

By using LVGL.INIT to set up memory and display parameters, and LVGL.SET_THEME to define the look and feel, you can quickly and efficiently create visually appealing and functional user interfaces in Annex with LVGL.

Widgets

Widgets (Objects): The Basic Building Blocks of LVGL

Widgets are the basic building blocks of an LVGL user interface. Each widget is a ready-made element you can use to build your touchscreen application, such as buttons, labels, sliders, images, and more. You create widgets in your code and arrange them on the screen to make interactive menus, controls, and displays.

Note: In LVGL, the terms Widget and Object are synonyms. You can refer to a widget as an object and vice versa.

Each widget has its own properties and can also be composed of other widgets in a hierarchical way. This means that a widget can contain other widgets; for example, the Keyboard widget contains several Button widgets, which in turn contain Label objects.

Every widget has properties with default values, but these can be freely modified in your code. These properties include position (X, Y), color, size, borders, and many more.

To access a widget's properties, each widget has a pointer (or handle) that uniquely identifies it. This value is provided by LVGL when you create the widget, and the pointer remains valid as long as the widget exists in LVGL. When a widget is deleted, the pointer is no longer valid.

In Annex, every function that creates a widget returns this pointer. It is important to save it in a variable so you can access and modify the properties of the corresponding widget later.

We will start with a very simple widget: the label.

This widget can be created in Annex using the LVGL.LABEL function.

For example:

label1 =LVGL.LABEL "Hi, I'm a label!"

This will create a label object with the text "Hi, I'm a label!" The corresponding pointer is returned in the variable label1.

By default, the object is created at the top-left corner of the screen (x=0, y=0). You can change the position of the object using the LVGL.SET_POS function together with the object's pointer.

For example:

LVGL.SET_POS label1, 50, 30

This command will move the label object to the position (x=50, y=30) on the screen.

You can now write a small program that creates a label and moves it to a new position on the screen.

For example:

label1 = LVGL.LABEL "Hi, I'm a label!"
LVGL.SET_POS label1, 50, 30

This program will create a label object with the text "Hi, I'm a label!" at the default position (0, 0), then move it to the coordinates (50, 30) on the screen.

By using the pointer, you can also modify other properties of the widget.

For example, you can change the text color of a label using the LVGL.SET_TEXT_COLOR function.

For example:

LVGL.SET_TEXT_COLOR label1, red

This command will set the text color of the label to red.

In this way, you can easily customize various properties of your widgets using their pointer.

So let's try this small program:

LVGL.INIT BLUE, darkcyan
' Set theme colors, font 30px, no dark mode
LVGL.SET_THEME red, orange, 30, 0
LVGL.CLEAN ' clean the screen

' Init LVGL
label1 = LVGL.LABEL "Hi, I'm a label!"
LVGL.SET_POS label1, 50, 30
LVGL.SET_TEXT_COLOR label1, &hFF0000

do
  ' Regularly refresh LVGL
  lvgl.refresh
loop

This program initializes LVGL with a blue foreground and dark cyan background, sets the theme to use red and orange with a 30px font in light mode, and cleans the screen. It then creates a label at the default position, moves it to (50, 30), and sets its text color to red. The main loop ensures that LVGL is regularly refreshed.

Now, if you save the program and run it, you will see the label appear on the screen at the specified position and with the chosen text color.

Additionally, by using the "Immediate" window, you can interact with your program in real time. For example, if you type, in the Immediate window:

LVGL.SET_TEXT label1, "Hello"
you will see the label's text change instantly. This feature allows you to quickly experiment and learn how LVGL commands work, without needing to modify and rerun the entire program each time. It’s a practical way to explore and understand the behavior of your user interface as you develop it.

Button, Events, Flags

Now we will introduce another widget: the button.

This will also allow us to discuss another very important concept in LVGL : events and flags.

First, the button, as you might expect, is an object with text on it that can be clicked.

This widget can be created in Annex using the LVGL.BUTTON function.

For example:

button1 = LVGL.BUTTON "BUTTON1"

This will create a button object with the text "BUTTON1". The corresponding pointer is returned in the variable button1.

As usual, by default, the object is created at the top-left corner of the screen.

Optionally, you can also define its initial position and size by specifying them after the text.

For example:

button1 = LVGL.BUTTON "BUTTON1", 20, 30, 120, 60

This creates a button positioned at x=20, y=30, with a width of 120 pixels and a height of 60 pixels.

Now, if we want to associate an action that will be executed when the button is pressed, we can attach a "CLICKED" event to the button and define a routine that will be executed when the button is pressed.

In Annex, you can use the LVGL.ADD_EVENT function to link the event to your button:

LVGL.ADD_EVENT button1, 4, but1_clicked '4 means CLICKED

but1_clicked:
  wlog "Button was pressed"
return

In this example, LVGL.ADD_EVENT attaches the event with code 4 (which corresponds to the "CLICKED" event) to button1. When the button is pressed, the but1_clicked routine is executed, and the message "Button was pressed" is logged.

Now, by default, the button is temporary—it only stays active while it is being pressed.

However, we can make it behave like a toggle button by setting the CHECKABLE flag.

To do this in Annex, you can use the LVGL.SET_FLAG function with the value 8, which means CHECKABLE:

LVGL.ADD_FLAG button1, 8   ' 8 means CHECKABLE

With the CHECKABLE flag set, the button will remain in a pressed (checked) state after being clicked, and will toggle between checked and unchecked states with each press. This is useful for creating on/off switches or selection buttons in your interface.

Note that when the button is toggled, it becomes orange, as defined in the theme as the secondary color. This visual feedback helps users easily see when the button is in the checked (active) state.

The use of theme colors makes it easy to maintain a consistent and visually appealing interface across your application.

This is the complete program:

LVGL.INIT BLUE, darkcyan
' Set theme colors, font 16px, no dark mode
LVGL.SET_THEME blue, orange, 16, 0
LVGL.CLEAN ' clean the screen

button1 = LVGL.BUTTON "BUTTON1", 20, 30, 120, 60
LVGL.ADD_EVENT button1, 4, but1_clicked '4 means CLICKED
LVGL.ADD_FLAG button1, 8 '8 means CHECKABLE

do
  ' Regularly refresh LVGL
  lvgl.refresh
loop

but1_clicked:
  wlog "Button was pressed"
return

To summarize:

Events allow you to link an action to something that happens, such as a click (as shown in the example above). When an event occurs, the associated routine is executed, making your interface interactive and responsive.

Flags allow you to modify the behavior of objects. For example, by setting the CHECKABLE flag, you can turn a temporary button into a toggle button. Flags provide a flexible way to customize how widgets behave according to your application's needs.

Read - Write to Widgets

Read and write to Widgets

Now let's talk about how to read from and write to widgets. There are several functions available for this, but generally there are two main methods:

- LVGL.GET_VALUE to read a numeric value from a widget

- LVGL.SET_VALUE to write a numeric value to a widget

- LVGL.GET_TEXT$ to read text from a widget

- LVGL.SET_TEXT to write text to a widget

These functions allow you to interact with widgets dynamically in your code, whether you need to display information, update a label, or get user input from a slider or text box.

Next, let's introduce two new widgets to illustrate these concepts with a concrete example. The widgets we will use are the slider and the bar.

The slider widget allows the user to select a numeric value by dragging a handle along a track. The bar widget is used to visually display a value, such as a progress indicator or a level meter.

By combining these two widgets, we can create an interactive example where moving the slider updates the value shown on the bar in real time.

Here is a practical example where moving a slider updates the value shown on a bar in real time:

LVGL.INIT BLUE, darkcyan
LVGL.SET_THEME red, orange, 30, 0
LVGL.CLEAN
' Create a slider at position (20, 50), width 200, height 30
slider1 = LVGL.SLIDER 20, 50, 200, 30
LVGL.SET_RANGE slider1, 0, 100     ' Set slider range from 0 to 100

' Create a bar at position (20, 100), width 200, height 30
bar1 = LVGL.BAR 20, 100, 200, 30
LVGL.SET_VALUE bar1, 0             ' Initialize bar value to 0

' Add an event: when the slider value changes, update the bar
LVGL.ADD_EVENT slider1, 32, slider_changed   ' 32 = VALUE_CHANGED

do
  lvgl.refresh
loop

slider_changed:
  value = LVGL.GET_VALUE(slider1)
  LVGL.SET_VALUE bar1, value
  wlog "Slider value: "; value
return

In this example:

- Moving the slider updates the bar to show the same value.

- The current slider value is also logged using wlog.

- The interface uses the theme and colors defined at the start.

LVGL_slider.gif

Now, let's look at another example using the dropdown and textarea widgets.

The dropdown widget allows the user to select an option from a list, while the textarea widget can be used to display or edit text.

In this example, when you select an item from the dropdown, the selected value will be displayed in the textarea.

LVGL.INIT BLUE, darkcyan
LVGL.SET_THEME blue, orange, 16, 0
LVGL.CLEAN
' Create a dropdown with options at position (10, 10), width 200, height 40
dropdown1 = LVGL.DROPDOWN "Apple\nBanana\nOrange\nGrape", 10, 10, 200, 40

' Create a textarea at position (20, 200), width 200, height 40
textarea1 = LVGL.TEXTAREA "Select a fruit", 20, 200, 200, 40

' Add an event: when the dropdown value changes, update the textarea
LVGL.ADD_EVENT dropdown1, 32, dropdown_changed   ' 32 = CHANGED

do
  lvgl.refresh
loop
dropdown_changed:
  selected$ = LVGL.GET_TEXT$(dropdown1)
  LVGL.SET_TEXT textarea1, "You selected: " + selected$
  wlog "Dropdown selection: "; selected$
return

In this example:

- The dropdown contains a list of fruits.

- When the user selects an item from the dropdown, the textarea updates to display the selected fruit.

- The selection is also logged using wlog.

LVGL_dropdown.gif

This demonstrates how to link widgets together and respond to user input in real time.

Father and Children (Parent and Child Objects)

Chapter: Father and Children (Parent and Child Objects)

In LVGL, every widget (object) is placed inside a parent, also called the “father.” By default, all objects are created on the active screen, but you can organize your interface by grouping objects inside containers or other widgets. This is very useful for building complex and dynamic layouts.

How it works:

- By default, all objects are created on the active screen (LVGL.SCREEN).

- You can set a different parent for new objects using LVGL.DEF_FATHER. All objects created after this command will be children of the specified parent.

- You can also move any object into a new parent at any time using LVGL.SET_PARENT.

- To find out who the parent of an object is, use LVGL.GET_PARENT.

- You can determine the type of any object with LVGL.GET_CLASS_NAME$.

Important:

When you use LVGL.SET_PARENT to move an object into a new parent, the object will be automatically centered in the middle of the parent, and its coordinates will become relative to the center.

You can change the alignment of the object within its parent using LVGL.ALIGN_TO if you want a different positioning.

Example: Organizing Widgets with Parents and Children

LVGL.INIT BLUE, darkcyan
LVGL.SET_THEME blue, orange, 16, 0
LVGL.CLEAN
' Create a base container at (20, 20), size 240x160
base1 = LVGL.BASE 20, 20, 240, 160

' Set base1 as the default parent for new objects
LVGL.DEF_FATHER base1

' These buttons will be children of base1
button1 = LVGL.BUTTON "OK", 10, 0, 80, 40
button2 = LVGL.BUTTON "Cancel", 130, 0, 80, 40

' Restore default parent to the screen
LVGL.DEF_FATHER 0

' Create another button on the main screen
button3 = LVGL.BUTTON "Exit"

' Move button3 into base1 after creation
LVGL.SET_PARENT button3, base1

' Add events to the buttons
LVGL.ADD_EVENT button1, 4, ok_clicked     ' 4 = CLICKED
LVGL.ADD_EVENT button2, 4, cancel_clicked
LVGL.ADD_EVENT button3, 4, exit_clicked

do
  lvgl.refresh
loop
ok_clicked:
  wlog "OK button pressed"
return
cancel_clicked:
  wlog "Cancel button pressed"
return
exit_clicked:
  wlog "Exit button pressed"
  ' Bonus: Show the class name of button3 and its parent
  wlog "button3 is a "; LVGL.GET_CLASS_NAME$(button3)
  wlog "Parent of button3 is a "; LVGL.GET_CLASS_NAME$(LVGL.GET_PARENT(button3))
return

Explanation:

- base1 is created on the screen at (20, 20), size 240x160.

- LVGL.DEF_FATHER base1 makes base1 the parent for button1 and button2.

- LVGL.DEF_FATHER 0 restores the default parent to the active screen.

- button3 ("Exit") is created on the screen, then moved into base1 using LVGL.SET_PARENT.

- LVGL.GET_PARENT can be used to check the parent of any object.

- LVGL.GET_CLASS_NAME$ returns the class name (type) of any object.

- When you move an object with LVGL.SET_PARENT, it is centered in the new parent by default. You can use LVGL.ALIGN_TO to change its alignment if needed.

This system allows you to easily group, organize, and dynamically rearrange your user interface in LVGL.

Controlling a Group of Objects

Controlling a Group of Objects via the Father (Parent)

Once you have grouped several widgets inside a container (the father), you can easily control all the children at once by acting on the parent object. For example, you can hide or show the entire group, and all the children will automatically follow these changes.

Example: Hide and Show the Parent Container

LVGL.INIT BLUE, darkcyan
LVGL.SET_THEME blue, orange, 16, 0
LVGL.CLEAN
' Create a base container at (20, 20), size 240x120
base1 = LVGL.BASE 20, 20, 240, 120

' Set base1 as the default parent for new objects
LVGL.DEF_FATHER base1

' Add some buttons as children of base1
button1 = LVGL.BUTTON "OK", 10, 0, 80, 40
button2 = LVGL.BUTTON "Cancel", 130, 0, 80, 40

' Restore default parent to the screen
LVGL.DEF_FATHER 0

' Create controls to hide/show the group
hide_btn = LVGL.BUTTON "Hide Group", 20, 180
show_btn = LVGL.BUTTON "Show Group", 160, 180

' Add events to the control buttons
LVGL.ADD_EVENT hide_btn, 4, hide_group
LVGL.ADD_EVENT show_btn, 4, show_group

do
  lvgl.refresh
loop
hide_group:
  LVGL.VISIBLE base1, 0
  wlog "The group is now hidden"
return
show_group:
  LVGL.VISIBLE base1, 1
  wlog "The group is now visible"
return

Explanation:

- When you hide or show the parent object (base1) with LVGL.VISIBLE, all its children (the buttons inside) will also be hidden or shown automatically.

- This technique is useful for managing dialogs, popups, or any grouped controls that need to be shown or hidden together with a single command.

LVGL_hide.gif

You can use this approach to easily control the visibility of complex interface sections in LVGL!

Widget States and Parts

Chapter: Widget States and Parts

LVGL widgets are highly flexible and interactive thanks to two fundamental concepts: states and parts.

Widget States

A widget can be in one or more states at the same time.

States reflect the current interaction or status of the widget and can affect its appearance or behavior.

Common states include:

LV_STATE_DEFAULT: The normal, released state.

LV_STATE_CHECKED: Toggled or checked (e.g., a switch or checkbox).

LV_STATE_FOCUSED: The widget is focused.

LV_STATE_PRESSED: The widget is being pressed.

LV_STATE_DISABLED: The widget is disabled and cannot be interacted with.

LV_STATE_SCROLLED: The widget is being scrolled.

States are usually set and cleared automatically by LVGL as the user interacts with the widget (for example, pressing or focusing).

However, you can also set or clear states programmatically if needed.

To add or remove a state in code:

LVGL.ADD_STATE widget, 0x20 'LV_STATE_PRESSED
LVGL.REMOVE_STATE widget, 0x20 'LV_STATE_PRESSED

To check if a widget is in a given state:

IF (LVGL.GET_STATE widget) and 0x20 THEN 'LV_STATE_PRESSED
  wlog "Widget is pressed"
ENDIF

or, a more "elegant" method is

IF LVGL.HAS_STATE(widget, 0x20) THEN 'LV_STATE_PRESSED
  wlog "Widget is pressed"
ENDIF

Widget Parts

Each widget in LVGL is made up of one or more parts.

Parts are like sub-elements of the widget that can be styled or controlled independently, similar to CSS pseudo-elements.

For example:

LV_PART_MAIN: The main background or body of the widget.

LV_PART_SCROLLBAR: The scrollbar(s) for scrollable widgets.

LV_PART_INDICATOR: The indicator (e.g., for sliders, bars, or switches).

LV_PART_KNOB: The knob/handle for sliders or switches.

LV_PART_SELECTED: The currently selected part (e.g., selected option in a list).

LV_PART_ITEMS: Used for widgets with multiple items (e.g., table cells).

LV_PART_CURSOR: The cursor for text areas or charts.

For example, a button usually only has the LV_PART_MAIN part, but a slider has LV_PART_MAIN, LV_PART_INDICATOR, and LV_PART_KNOB.

Why use Parts and States?

You can apply different styles to different parts and states.

For example, you can make the indicator of a slider turn green when pressed, or make a button look different when disabled.

This allows for highly dynamic and visually rich interfaces.

Example: Styling a Button Part in a State

' Define constants (from LVGL documentation)
LV_PART_MAIN = 0x0
LV_STATE_PRESSED = 0x0020
' Set the button's main part to red when pressed
LVGL.SET_BG_COLOR button1, RED, LV_PART_MAIN or LV_STATE_PRESSED

Example 1: Slider Indicator Turns Green When Pressed

' Define constants (from LVGL documentation)
LV_PART_INDICATOR = 0x020000
LV_STATE_PRESSED = 0x0020

' Create a slider at (20, 20), width 200
slider1 = LVGL.SLIDER 20, 20

' Style the indicator to turn green when pressed
LVGL.SET_BG_COLOR slider1, GREEN, LV_PART_INDICATOR or LV_STATE_PRESSED

Explanation:

The slider's indicator (the filled portion) will turn green when pressed.

The LV_PART_INDICATOR or LV_STATE_PRESSED combines the part and state into a single style target.

Example 2: Styling an ARC Widget

The ARC widget is an interactive, circular control similar to a slider, but instead of moving linearly, the value is adjusted by moving a knob or indicator around a circle. It is often used for gauges, progress rings, or any UI where a circular slider is more intuitive.

' Define constants
LV_PART_MAIN = 0x0
LV_PART_INDICATOR = 0x020000
LV_STATE_DEFAULT = 0x0
LV_STATE_PRESSED = 0x0020

' Create an arc at (20, 80), radius 50
arc1 = LVGL.ARC 20, 80, 100, 100
LVGL.SET_RANGE arc1, 0, 100 ' Set range 0-100
LVGL.SET_VALUE arc1, 75 ' Set value to 75%

' Style the main part (background)
LVGL.SET_ARC_COLOR arc1, DARKGREY, LV_PART_MAIN or LV_STATE_DEFAULT

' Style the indicator (progress line)
LVGL.SET_ARC_COLOR arc1, ORANGE, LV_PART_INDICATOR or LV_STATE_DEFAULT
LVGL.SET_ARC_COLOR arc1, RED, LV_PART_INDICATOR or LV_STATE_PRESSED ' Red when pressed

' Optional: Style the knob
LVGL.SET_BG_COLOR arc1, BLUE, LV_PART_KNOB or LV_STATE_DEFAULT

Explanation:

The arc’s background (main part) is styled gray.

The indicator (progress line) is orange by default and turns red when pressed.

The knob (handle) is styled blue.

As an interactive widget, the ARC can be used just like a slider but with a circular movement.

Complete example:

LVGL.INIT BLUE, darkcyan
LVGL.SET_THEME blue, orange, 16, 0
LVGL.CLEAN
' Create a slider and an arc widget
slider1 = LVGL.SLIDER 20, 20, 200, 30
arc1 = LVGL.ARC 20, 80, 100, 100
LVGL.SET_RANGE arc1, 0, 100
LVGL.SET_VALUE arc1, 75

' Define constants for parts and states
LV_PART_MAIN = 0x0
LV_PART_INDICATOR = 0x020000
LV_PART_KNOB = 0x04
LV_STATE_DEFAULT = 0x0
LV_STATE_PRESSED = 0x20

' Style the slider: indicator turns green when pressed
LVGL.SET_BG_COLOR slider1, GREEN, LV_PART_INDICATOR or LV_STATE_PRESSED

' Style the arc widget
LVGL.SET_ARC_COLOR arc1, DARKGREY, LV_PART_MAIN or LV_STATE_DEFAULT
LVGL.SET_ARC_COLOR arc1, ORANGE, LV_PART_INDICATOR or LV_STATE_DEFAULT
LVGL.SET_ARC_COLOR arc1, RED, LV_PART_INDICATOR or LV_STATE_PRESSED
LVGL.SET_BG_COLOR arc1, BLUE, LV_PART_KNOB or LV_STATE_DEFAULT

' Main refresh loop
do
  lvgl.refresh
loop

Summary

States describe what the widget is doing or how it is being interacted with.

Parts are the sub-elements of a widget that can be styled or controlled separately.

By combining parts and states, you can create advanced, interactive, and visually appealing user interfaces in LVGL.

Colors, Opacity, Color Names, and Hex Values

Chapter: Colors, Opacity, Color Names, and Hex Values

LVGL allows you to use a variety of standard color names or hexadecimal RGB values to style your widgets. You can also control the opacity (transparency) of colors for richer UI effects.

Available Color Names

Color NameHex CodeSwatch
BLACK0x000000■■■■■
NAVY0x000080■■■■■
DARKGREEN0x006400■■■■■
DARKCYAN0x008B8B■■■■■
MAROON0x800000■■■■■
PURPLE0x800080■■■■■
OLIVE0x808000■■■■■
LIGHTGREY0xD3D3D3■■■■■
DARKGREY0xA9A9A9■■■■■
BLUE0x0000FF■■■■■
GREEN0x008000■■■■■
CYAN0x00FFFF■■■■■
RED0xFF0000■■■■■
MAGENTA0xFF00FF■■■■■
YELLOW0xFFFF00■■■■■
WHITE0xFFFFFF■■■■■
ORANGE0xFFA500■■■■■
GREENYELLOW0xADFF2F■■■■■
PINK0xFFC0CB■■■■■
BROWN0xA52A2A■■■■■
GOLD0xFFD700■■■■■
SILVER0xC0C0C0■■■■■
SKYBLUE0x87CEEB■■■■■
VIOLET0x8A2BE2■■■■■

Using Colors in LVGL

To set the background color of a widget, you can use either the color name or the hex value:

LVGL.SET_BG_COLOR widget, RED
LVGL.SET_BG_COLOR widget, 0xFFA500 ' Orange (hex value)

The color can be also specified with the R, G, B components values, from 0 to 255

LVGL.SET_BG_COLOR widget, LVGL.RGB(255,0,0) ' Red

Setting Opacity

Opacity values range from 0 (fully transparent) to 255 (fully opaque):

LVGL.SET_BG_OPA widget, 128 ' 50% opacity
LVGL.SET_BG_OPA widget, 255 ' Fully opaque
LVGL.SET_BG_OPA widget, 0 ' Fully transparent

Example: Using Color Names, Hex, and Opacity

LVGL.INIT BLUE, darkcyan
LVGL.SET_THEME blue, orange, 16, 0
LVGL.CLEAN
' Button with a named color
btn1 = LVGL.BUTTON "Red", 20, 20, 120, 40
LVGL.SET_BG_COLOR btn1, RED

' Button with a hex color
btn2 = LVGL.BUTTON "Sky Blue", 160, 20, 120, 40
LVGL.SET_BG_COLOR btn2, 0x87CEEB

' Button with opacity
btn3 = LVGL.BUTTON "50% Opacity", 20, 80, 120, 40
LVGL.SET_BG_COLOR btn3, GREEN
LVGL.SET_BG_OPA btn3, 128

' Button with another named color
btn4 = LVGL.BUTTON "Gold", 160, 80, 120, 40
LVGL.SET_BG_COLOR btn4, GOLD

' Button with less opacity
btn5 = LVGL.BUTTON "10% Opacity", 20, 140, 120, 40
LVGL.SET_BG_COLOR btn5, GREEN
LVGL.SET_BG_OPA btn5, 25

do
  lvgl.refresh
loop

Summary

Use color names for readability and convenience.

Use hexadecimal values for custom colors.

Control opacity to create transparency effects.

Combine these features to create rich and dynamic UI styles in LVGL.

Chapter 2: Alignments in LVGL - Using LVGL.ALIGN_TO

LVGL provides a powerful way to position widgets relative to each other using alignments. The LVGL.ALIGN_TO command lets you align any object to another object with a variety of alignment types and optional pixel offsets.

How LVGL.ALIGN_TO Works

LVGL.ALIGN_TO obj, obj_base, align [, x [, y]]

obj: The object you want to align.

obj_base: The reference object to align to.

align: The alignment type (numeric value, see below).

x: (Optional) Horizontal offset in pixels.

y: (Optional) Vertical offset in pixels.

This command positions obj relative to obj_base using the specified alignment.

You can fine-tune the placement with the optional x and y offsets.

Common Alignment Types

Here are some of the most commonly used alignments (from the lv_align_t table):

There are many more options for aligning inside or outside any side or corner of the reference object.

image.png

Example: Aligning Widgets on top of a Base Container

LVGL.INIT BLUE, darkcyan
LVGL.SET_THEME blue, orange, 14, 0
LVGL.CLEAN
' Create a large base container
base = LVGL.BASE 20, 20, 280, 160

' Add buttons aligned to various positions inside the base
btn_topleft = LVGL.BUTTON "Top Left"
LVGL.ALIGN_TO btn_topleft, base, 1 ' LV_ALIGN_TOP_LEFT

btn_topmid = LVGL.BUTTON "Top Mid"
LVGL.ALIGN_TO btn_topmid, base, 2 ' LV_ALIGN_TOP_MID

btn_bottomright = LVGL.BUTTON "Bottom Right"
LVGL.ALIGN_TO btn_bottomright, base, 6 ' LV_ALIGN_BOTTOM_RIGHT

btn_center = LVGL.BUTTON "Center"
LVGL.ALIGN_TO btn_center, base, 9 ' LV_ALIGN_CENTER

btn_below = LVGL.BUTTON "Below"
LVGL.ALIGN_TO btn_below, base, 14, 0, 10 ' LV_ALIGN_OUT_BOTTOM_MID, 10px below the base

do
  lvgl.refresh
loop
image.png

Example: Aligning Objects Relative to Each Other

You can align objects not only inside a container, but also relative to each other. For instance, you might want to place one button exactly to the right of another, with a specific horizontal gap.

LVGL.INIT BLUE, darkcyan
LVGL.SET_THEME blue, orange, 14, 0
LVGL.CLEAN
' Create a base container for visual reference
base = LVGL.BASE 20, 20, 280, 160

' Create the first button near the left edge of the container
btn_left = LVGL.BUTTON "Left"
LVGL.ALIGN_TO btn_left, base, 1, 10, 40 ' LV_ALIGN_TOP_LEFT, offset 10px right, 40px down

' Create a second button and align it to the right-middle of btn_left with a 20px gap
btn_right = LVGL.BUTTON "Right"
LVGL.ALIGN_TO btn_right, btn_left, 20, 20, 0 ' LV_ALIGN_OUT_RIGHT_MID (20), 20px to the right

' Create a third button and align it just below btn_left with a 10px gap
btn_below = LVGL.BUTTON "Below"
LVGL.ALIGN_TO btn_below, btn_left, 14, 0, 10 ' LV_ALIGN_OUT_BOTTOM_MID (14), 10px below

' Create a fourth button and align it above btn_left with a -10px vertical offset
btn_above = LVGL.BUTTON "Above"
LVGL.ALIGN_TO btn_above, btn_left, 11, 0, -10 ' LV_ALIGN_OUT_TOP_MID (11), 10px above

do
  lvgl.refresh
loop
image.png

Explanation:

btn_left is placed inside the base container with a small offset.

btn_right is aligned directly to the right of btn_left, with a 20px horizontal gap, using LV_ALIGN_OUT_RIGHT_MID.

btn_below is aligned just below btn_left, with a 10px vertical gap, using LV_ALIGN_OUT_BOTTOM_MID.

btn_above is aligned just above btn_left, with a -10px offset, using LV_ALIGN_OUT_TOP_MID.

This approach lets you build custom layouts by chaining alignments between objects, not just within a parent container.

Tips and Notes

LVGL.ALIGN_TO is ideal for arranging widgets inside containers, panels, or any parent object.

You can use LVGL.ALIGN_TO to arrange widgets dynamically at runtime, not just during initialization.

The align parameter uses numeric values (see lv_align_t table in the HELP for the full list).

The optional x and y offsets are applied after alignment, allowing for precise placement.

Using a base container makes it easy to create organized and adaptable layouts; you can use it just as a point of reference for the alignements and then you can even hide it acting like an invisible "grid".

Summary:

LVGL.ALIGN_TO makes it easy to position widgets relative to each other with a single command.

Use different alignment types to create flexible, visually balanced layouts.

Combine alignment with pixel offsets for precision and control.

Combine with pixel offsets for fine-tuned control.

Moving, Positioning, and Sizing Objects in LVGL – Understanding Padding and Margin

Chapter: Moving, Positioning, and Sizing Objects in LVGL – Understanding Padding and Margin

When building user interfaces in LVGL, you need to control where objects appear, how big they are, and how much space they have around and inside them. Let’s see how to do all of this with real code examples.

Example 1: Manual Positioning and Sizing

This example shows how to manually position and size a button inside a container.

LVGL.INIT BLUE, darkcyan
LVGL.SET_THEME blue, orange, 14, 0
LVGL.CLEAN
' Create a base container at (20,20) with size 280x160
base = LVGL.BASE 20, 20, 280, 160

' Set internal padding: 0px on all sides (space inside the base)
LVGL.SET_PAD base, 0, 0, 0, 0

' Set external margin for the container (space outside the base)
LVGL.SET_MARGIN base, 16, 16, 16, 16

' All following objects will be children of 'base'
LVGL.DEF_FATHER base

' Create a button inside the container
btn1 = LVGL.BUTTON "Manual Position"

' Manually set the position to (40,40) inside the container
LVGL.SET_POS btn1, 40, 40

' Set the button size to 120x32 pixels
LVGL.SET_SIZE btn1, 120, 32

do
  lvgl.refresh
loop
image.png

What happens here?

The button appears 40 pixels from the left and 40 pixels from the top edge of the container (not counting the container’s margin).

You control the exact position of the button yourself.

Example 2: Using Padding – Object at (0,0)

This example demonstrates how padding on the container affects the position of its children. The button is placed at (0,0), but appears offset by the container’s padding.

LVGL.INIT BLUE, darkcyan
LVGL.SET_THEME blue, orange, 14, 0
LVGL.CLEAN
' Create a base container at (20,20) with size 280x160
base = LVGL.BASE 20, 20, 280, 160

' Set internal padding: 20px on all sides (space inside the base)
LVGL.SET_PAD base, 20, 20, 20, 20

' Set external margin for the container (space outside the base)
LVGL.SET_MARGIN base, 16, 16, 16, 16

' All following objects will be children of 'base'
LVGL.DEF_FATHER base

' Create a button inside the container
btn1 = LVGL.BUTTON "Padding Offset"

' Set the position to (0,0) -- top-left of the container's content area
LVGL.SET_POS btn1, 0, 0

' Set the button size to 120x32 pixels
LVGL.SET_SIZE btn1, 120, 32

do
  lvgl.refresh
loop
image.png

What happens here?

The button’s position is set to (0,0), but it is visually offset by the container’s 20px padding.

This means the button will appear 20px from the top and left inside the base, without you needing to manually adjust its position.

Padding makes it easy to keep all children spaced evenly from the edges.

What’s the Difference Between Padding and Margin?

Padding is the space inside an object, between its border and its content/children. It pushes the content inward.

Margin is the space outside an object, between the object’s border and other widgets. It pushes the object away from its neighbors.

' Set 20px padding on all sides of a container (content starts 20px from each edge)
LVGL.SET_PAD base, 20, 20, 20, 20

' Set 16px margin on all sides (space outside the container)
LVGL.SET_MARGIN base, 16, 16, 16, 16

Summary Table:

What you want to do Command Example Effect
Move an object LVGL.SET_POS btn, 30, 40 Places btn at (30,40) in parent
Change object size LVGL.SET_SIZE btn, 120, 40 Sets btn to 120x40 pixels
Add space inside object LVGL.SET_PAD base, 20,20,20,20 Content starts 20px from each edge
Add space outside object LVGL.SET_MARGIN base, 16,16,16,16 16px gap between base and neighbors

In Short:

Use LVGL.SET_POS to move objects.

Use LVGL.SET_SIZE, LVGL.SET_WIDTH, and LVGL.SET_HEIGHT to resize them.

Use LVGL.SET_PAD for space inside the object (content area).

Use LVGL.SET_MARGIN for space outside the object (between widgets).

Automatic Widget Positioning with Flex Layout

Chapter: Automatic Widget Positioning with Flex Layout in LVGL

If you want to arrange widgets automatically—without having to set the position of each one—LVGL’s Flex layout is your friend. With Flex, you just add your widgets to a container and let LVGL handle their placement and alignment for you.

How Flex Layout Works

Flex Flow decides the main direction in which widgets are placed (row, column, wrap, etc).

Flex Align controls how widgets are aligned along the main axis and the cross axis.

Padding, Margin, and Pad Gap control the spacing inside and between widgets.

Setting Up Flex Layout

Suppose you want to create a row of buttons, centered and evenly spaced inside a container. Here’s how you do it:

LVGL.INIT BLUE, darkcyan
LVGL.SET_THEME blue, orange, 14, 0
LVGL.CLEAN
' Create a container for the buttons
base = LVGL.BASE 20, 20, 280, 160

' Enable flex layout in row direction (widgets placed left-to-right)
LVGL.SET_FLEX_FLOW base, 0 ' 0 = ROW, see LVGL docs for other options

' Align widgets to the center on both axes
LVGL.SET_FLEX_ALIGN base, 2, 2, 2 ' 2 = CENTER for main, cross, and track_cross

' Set padding inside the container (space from container edge)
LVGL.SET_PAD base, 10, 10, 10, 10

' Set the gap between widgets (space between each button)
LVGL.SET_PAD_GAP base, 12

' Now, make the base the parent for the next widgets
LVGL.DEF_FATHER base

' Add three buttons—they’ll be positioned automatically!
btn1 = LVGL.BUTTON "One"
btn2 = LVGL.BUTTON "Two"
btn3 = LVGL.BUTTON "Three"

do
  lvgl.refresh
loop
image.png

What happens here?

The buttons are laid out in a row, centered in the container.

There’s 16px of padding between the buttons and the container’s edge.

There’s 12px of gap between each button.

No need to set positions for the buttons—they’re arranged automatically!

How Margin and Padding Work with Flex

Padding on the container defines the space between the container’s border and its children (all widgets inside).

Margin on a widget defines the space outside that widget, pushing it away from its siblings or the container edge.

Pad Gap (LVGL.SET_PAD_GAP) sets the space between child widgets in the flex flow direction.

You can combine these for fine-tuned layouts: (type it in the immediate window so see the effect while the program is running)

' Add margin to a specific button (extra space around it)
LVGL.SET_MARGIN btn2, 5, 5, 8, 8 ' Top, bottom, left, right
image.png

Changing Flex Flow and Alignment

Want your widgets in a column instead of a row? Just change the flex flow: (type it in the immediate window)

LVGL.SET_FLEX_FLOW base, 1 ' 1 = COLUMN
image.png

You can also align widgets to the start, end, or stretch them: (type it in the immediate window)

LVGL.SET_FLEX_ALIGN base, 0, 0, 0 ' 0 = START alignment
image.png

Summary Table

What you want to do Command Example Effect
Arrange widgets in a row LVGL.SET_FLEX_FLOW base, 0 Widgets placed left-to-right
Arrange widgets in a column LVGL.SET_FLEX_FLOW base, 1 Widgets placed top-to-bottom
Center widgets LVGL.SET_FLEX_ALIGN base, 2, 2, 2 Widgets centered on both axes
Add space inside container LVGL.SET_PAD base, 16,16,16,16 16px padding from container edge
Add gap between widgets LVGL.SET_PAD_GAP base, 12 12px space between widgets
Add margin to a widget LVGL.SET_MARGIN btn2, 20,20,8,8 Extra space around btn2

In Short:

Use LVGL.SET_FLEX_FLOW and LVGL.SET_FLEX_ALIGN to arrange widgets automatically in rows or columns, and align them as you like.

Use LVGL.SET_PAD for space inside the container, LVGL.SET_PAD_GAP for space between widgets, and LVGL.SET_MARGIN for space around individual widgets.

Flex layout makes your UI responsive and easy to maintain—just add widgets and let LVGL do the rest!

Example: Using Flex Wrap with Many Buttons

Suppose you want to fill a container with many buttons, automatically wrapping to a new line when there’s no more room. You can do this with Flex’s wrap mode.

LVGL.INIT BLUE, darkcyan
LVGL.SET_THEME blue, orange, 14, 0
LVGL.CLEAN
' Create a wide container at (10,10) with size 300x200
base = LVGL.BASE 10, 10, 300, 200

' Enable flex layout in row wrap mode (widgets placed left-to-right, wrap to new line)
LVGL.SET_FLEX_FLOW base, 4 ' 4 = ROW_WRAP

' Align widgets to the start on main axis, center on cross axis
LVGL.SET_FLEX_ALIGN base, 0, 2, 2 ' 0 = START, 2 = CENTER

' Add 8px padding inside the container
LVGL.SET_PAD base, 8, 8, 8, 8

' Set 6px gap between widgets
LVGL.SET_PAD_GAP base, 6

' Make base the parent for the next widgets
LVGL.DEF_FATHER base

' Add 12 buttons using a loop
for i = 1 to 12
btn = LVGL.BUTTON "Btn " + str$(i)
next
do
  lvgl.refresh
loop
image.png

What happens here?

Buttons are placed left-to-right.

When there’s no more space, they wrap to a new line.

Buttons are aligned to the start of each row, and centered vertically in the container.

Padding and gap keep everything neat.

Try Changing the Alignment!

Want all buttons centered on each row? Change the main axis alignment to CENTER:

LVGL.SET_FLEX_ALIGN base, 2, 2, 2 ' All axes CENTER
image.png

Want buttons spaced evenly ? Use SPACE_EVENLY

LVGL.SET_FLEX_ALIGN base, 3, 3, 3 ' 3 = SPACE_EVENLY
image.png

Try END alignment and SPACE_BETWEEN to push buttons to the right and top/bottom:

LVGL.SET_FLEX_ALIGN base, 1, 3, 5 ' 1 = END on main axis, SPACE_BETWEEN
image.png

Summary

Use ROW_WRAP or COLUMN_WRAP flex flow to auto-wrap widgets.

Use a loop to quickly fill your container with widgets.

Play with LVGL.SET_FLEX_ALIGN to see how alignment changes the layout—try START, CENTER, END, and STRETCH!

Combine with padding and gap for a polished, flexible UI.

This makes it super easy to create dynamic, responsive layouts in LVGL—no manual positioning needed!

Customizing Borders

Chapter: Customizing Borders in LVGL

Borders help define the edges of your UI elements and add visual structure to your design. In LVGL, borders are not visible on most widgets (like buttons) until you set a border width greater than zero. Once the width is set, you can adjust the color, sides, and corner radius for a polished look.

Setting Border Width

By default, the border width is zero, so you won’t see any border on most widgets—even if you set the color. Start by setting the border width to make the border visible:

LVGL.INIT white, darkcyan
LVGL.SET_THEME blue, orange, 14, 0
LVGL.CLEAN
' Create a base container
base = LVGL.BASE 20, 20, 280, 120
LVGL.DEF_FATHER base

' Create a button inside the container
btn = LVGL.BUTTON "Border Width"
LVGL.SET_SIZE btn, 120, 48
LVGL.SET_POS btn, 40, 30

' Make the border 4 pixels wide (now visible!)
LVGL.SET_BORDER_WIDTH btn, 4

do
lvgl.refresh
  loop
image.png

Setting Border Color

Once the border is visible, you can set its color using LVGL.SET_BORDER_COLOR:

LVGL.SET_BORDER_COLOR btn, RED
image.png

Setting Border Sides

You can specify which sides of the object show borders using LVGL.SET_BORDER_SIDE. The sides are specified as bit flags:

Combine values for multiple sides (e.g., 0x01 + 0x04 = bottom and left).

' Show border only on bottom and left sides
LVGL.SET_BORDER_SIDE btn, 0x01 + 0x04 ' LV_BORDER_SIDE_BOTTOM | LV_BORDER_SIDE_LEFT
image.png

Setting Corner Radius

Rounded corners add a softer look. Use LVGL.SET_RADIUS to set the corner radius in pixels. You can also use the value 32767 to make fully rounded corners (circle if width equals height).

LVGL.SET_RADIUS btn, 16
image.png
' Or make the button fully circular (if width=height)
LVGL.SET_RADIUS btn, 32767
image.png

Full Example: Custom Borders and Radius

Here’s a complete example demonstrating all border customizations together:

LVGL.INIT white, darkcyan
LVGL.SET_THEME blue, orange, 14, 0
LVGL.CLEAN
' Create a container
base = LVGL.BASE 20, 20, 280, 120
LVGL.DEF_FATHER base

' Create a button
btn = LVGL.BUTTON "Styled Button"
LVGL.SET_SIZE btn, 120, 48
LVGL.SET_POS btn, 40, 30

' Set border width to 4 pixels 
LVGL.SET_BORDER_WIDTH btn, 4

' Set border color to green
LVGL.SET_BORDER_COLOR btn, GREEN

' Show border only on left and right sides
LVGL.SET_BORDER_SIDE btn, 0x04 + 0x08 ' LV_BORDER_SIDE_LEFT | LV_BORDER_SIDE_RIGHT

' Make corners rounded with a radius of 16 pixels
LVGL.SET_RADIUS btn, 16

do
  lvgl.refresh
loop
image.png

Summary Table

Action Command Example Effect
Set border width LVGL.SET_BORDER_WIDTH btn, 4 Border is 4 pixels thick (makes border visible)
Set border color LVGL.SET_BORDER_COLOR btn, RED Red border color
Set border sides (bottom + left) LVGL.SET_BORDER_SIDE btn, 0x01 + 0x04 Border on bottom and left only
Set border sides (left + right) LVGL.SET_BORDER_SIDE btn, 0x04 + 0x08 Border on left and right only
Set corner radius LVGL.SET_RADIUS btn, 16 Rounded corners with 16px radius

In Short:

Always set LVGL.SET_BORDER_WIDTH first to make the border visible.

Use LVGL.SET_BORDER_COLOR to define the border color.

Use LVGL.SET_BORDER_SIDE with the correct bit flags to specify which sides have borders.

Use LVGL.SET_RADIUS to round the corners of your widget.

These functions let you create visually appealing and well-defined UI elements tailored to your design needs.

Using Fonts and Unicode Symbols

Chapter: Using Fonts and Unicode Symbols in LVGL

Fonts in LVGL are collections of bitmaps and metadata that define how text is rendered on your widgets. You can use built-in fonts, load custom fonts at runtime, and display symbols using Unicode codes. By default, the font for each widget is set by the active theme, but you can override it for any object.

Loading Custom Fonts

You can load your own font files (converted to .bin format using the LVGL Online Font Converter) and assign them to a font slot (100–103):

' Load a font from file and assign it to slot 100
LVGL.LOAD_FONT "/myfont100.bin", 100

' Load another font to slot 101
LVGL.LOAD_FONT "/myfont101.bin", 101

Once loaded, these fonts can be used just like built-in fonts.

Setting the Font for an Object

You can set the font of any object with LVGL.SET_FONT.

The font can be a built-in size or a custom-loaded font slot (100–103):

' Set built-in Montserrat 16 font for a label
LVGL.SET_FONT label, 16

' Set loaded font 100 for a button
LVGL.SET_FONT btn, 100

You can also specify a style selector to set the font for a particular state or part.

Available Font Numbers

Note on Default Fonts

If you don’t set a font explicitly, the font is inherited from the current theme. This ensures a consistent look across your UI unless you want to customize it.

Using Unicode Symbols and the unescape$() Function

LVGL supports special symbols using Unicode codes, which can be inserted into strings.

To make this easy, you can use the unescape$() function, which allows you to define strings using C-style escape sequences (like \xEF\x82\x95 for Unicode characters).

For example, to display a phone symbol followed by text:

label = LVGL.LABEL unescape$("\xEF\x82\x95 Phone Call")
image.png

This will show the phone icon (U+F095, commonly used in icon fonts like FontAwesome) followed by the text "Phone Call".

The unescape$() function is especially useful for:

Full Example: Custom Fonts and Unicode Symbols

LVGL.INIT white, darkcyan
LVGL.SET_THEME blue, orange, 14, 0
LVGL.CLEAN
' Load a custom font to slot 100
LVGL.LOAD_FONT "/BeckyTahlia.bin", 100

' Create a label with a built-in font
label1 = LVGL.LABEL "Montserrat 16"
LVGL.SET_FONT label1, 16

' Create a label with a loaded custom font
label2 = LVGL.LABEL "Custom Font"
LVGL.SET_FONT label2, 100
LVGL.SET_POS label2, 10, 40

' Create a label with a Unicode symbol using unescape$()
label3 = LVGL.LABEL unescape$("\xEF\x80\x91 Power")
LVGL.SET_FONT label3, 16
LVGL.SET_POS label3, 10, 100

do
  lvgl.refresh
loop
image.png

Summary Table

Action Command Example Effect
Load a custom font LVGL.LOAD_FONT "/myfont100.bin", 100 Loads font to slot 100
Set built-in font LVGL.SET_FONT label, 16 Sets Montserrat 16 font
Set loaded font LVGL.SET_FONT btn, 100 Uses custom font slot 100
Show Unicode symbol with unescape$() LVGL.LABEL unescape$("\xEF\x82\x95 Phone Call") Shows a phone icon and text

In Short:

- Use LVGL.LOAD_FONT to load custom fonts in .bin format (slots 100–103).

- Use LVGL.SET_FONT to apply a font to any object.

- Font is inherited from the theme by default.

- Use unescape$() to easily insert Unicode symbols or special characters in your text.

- Check the Annex documentation for the full list of the Symbols included

With these features, you can fully control the typography and iconography in your LVGL user interface!

Download BeckyTahlia.bin

Percentage Positioning, Dimensioning, and Transformations

Chapter: Percentage Positioning, Dimensioning, and Transformations in LVGL

LVGL lets you position, size, and transform widgets with great flexibility. This chapter explains how to use percentage-based positioning, set transformation pivots (including absolute coordinates), and apply rotation and scaling to any object.

Positioning and Dimensioning with LVGL.pct()

You can define the position and size of objects in percentage terms using LVGL.pct(). This makes your UI responsive, adapting to different screen sizes.

' Create a button that is 50% of the screen width and 20% of the screen height
btn = LVGL.BUTTON "Transform Me"
LVGL.SET_SIZE btn, LVGL.pct(50), LVGL.pct(20)
LVGL.SET_POS btn, LVGL.pct(25), LVGL.pct(40) ' 25% from left, 40% from top
image.png

This button will always be centered horizontally and positioned a bit above the middle, regardless of the screen size.

Setting the Transformation Pivot Point

To rotate or scale an object around a specific point, use LVGL.SET_TRANSFORM_PIVOT. The pivot point is where the object rotates or scales from. By default, the pivot is the center of the object.

You can set the pivot using absolute coordinates (pixels from the top left corner) or percentage values (relative to the object’s size):

' Set the pivot point to top left corner (absolute coordinates)
LVGL.SET_TRANSFORM_PIVOT obj, 0, 0

' Set the pivot point to center (default, percentage)
LVGL.SET_TRANSFORM_PIVOT obj, LVGL.pct(50), LVGL.pct(50)

' Set the pivot point to right edge, halfway down (absolute coordinates)
' (Assuming the object width = 200 px, height = 100 px)
LVGL.SET_TRANSFORM_PIVOT obj, 200, 50

' Set the pivot point to right edge, halfway down (percentage)
LVGL.SET_TRANSFORM_PIVOT obj, LVGL.pct(100), LVGL.pct(50)

You can also specify a style selector to apply the pivot for a particular state or part of the object.

Rotating Objects with LVGL.SET_TRANSFORM_ROTATION

Rotate any object by a specified angle:

' Rotate the object by 45.8 degrees
LVGL.SET_TRANSFORM_ROTATION btn, 45.8
image.png

You can optionally specify a style selector to apply rotation for a particular state or part of the object.

Scaling Objects with LVGL.SET_TRANSFORM_SCALE

Scale objects using LVGL.SET_TRANSFORM_SCALE. The scale value 256 means normal size, 128 is half size, 512 is double size, etc.

' Scale the object to double its size on X and Y
LVGL.SET_TRANSFORM_SCALE btn, 512, 512
image.png
' Scale the object to half its size on X, normal on Y
LVGL.SET_TRANSFORM_SCALE btn, 128, 256
image.png

You can also specify a style selector to apply scaling for a particular state or part of the object.

Example: Combining Positioning and Transformations

LVGL.INIT white, darkcyan
LVGL.SET_THEME blue, orange, 14, 0
LVGL.CLEAN
' Create a button sized 40% width and 15% height, positioned at 30% left and 40% top
btn = LVGL.BUTTON "Transform Me"
LVGL.SET_SIZE btn, LVGL.pct(40), LVGL.pct(15)
LVGL.SET_POS btn, LVGL.pct(30), LVGL.pct(40)

' Set pivot to center
LVGL.SET_TRANSFORM_PIVOT btn, LVGL.pct(50), LVGL.pct(50)

' Rotate button by 30 degrees
LVGL.SET_TRANSFORM_ROTATION btn, -30

' Scale button to 1.5x width and height (256 normal, so 384 = 1.5x)
LVGL.SET_TRANSFORM_SCALE btn, 384, 384

do
  lvgl.refresh
loop
image.png

Bonus : Button zoomed when pressed:

LVGL.INIT white, darkcyan
LVGL.SET_THEME blue, orange, 14, 0
LVGL.CLEAN
' Create a button sized 40% width and 15% height, positioned at 30% left and 40% top
btn = LVGL.BUTTON "Transform Me"
LVGL.SET_SIZE btn, LVGL.pct(40), LVGL.pct(15)
LVGL.SET_POS btn, LVGL.pct(30), LVGL.pct(40)

' Set pivot to center
LVGL.SET_TRANSFORM_PIVOT btn, LVGL.pct(50), LVGL.pct(50)

' Scale button to 1.5x width and height (256 normal, so 384 = 1.5x) when pressed
LVGL.SET_TRANSFORM_SCALE btn, 384, 384, 0x20 'LV_STATE_PRESSED

do
  lvgl.refresh
loop

Summary Table

Action Command Example Effect
Set size/position in percentage LVGL.SET_SIZE btn, LVGL.pct(50), LVGL.pct(20) Button is 50% wide and 20% tall
Set pivot with absolute coordinates LVGL.SET_TRANSFORM_PIVOT obj, 0, 0 Pivot is top left corner
Set pivot with percentage LVGL.SET_TRANSFORM_PIVOT obj, LVGL.pct(50), LVGL.pct(50) Pivot is center
Set pivot right edge, halfway down (absolute) LVGL.SET_TRANSFORM_PIVOT obj, 200, 50 Pivot at right edge, halfway down (pixels)
Set pivot right edge, halfway down (percentage) LVGL.SET_TRANSFORM_PIVOT obj, LVGL.pct(100), LVGL.pct(50) Pivot at right edge, halfway down (percent)
Rotate object LVGL.SET_TRANSFORM_ROTATION obj, 45.8 Rotated by 45.8°
Scale object LVGL.SET_TRANSFORM_SCALE obj, 512, 512 Double size on X and Y

In Short:

Use LVGL.pct() for responsive positioning and sizing.

Use LVGL.SET_TRANSFORM_PIVOT to set the rotation/scale center for any object—using either absolute coordinates or percentage values.

Use LVGL.SET_TRANSFORM_ROTATION to rotate objects.

Use LVGL.SET_TRANSFORM_SCALE to scale objects, where 256 is normal size.

Object Hierarchy and Identification

Using LVGL Functions for Object Hierarchy and Identification

LVGL provides a set of functions to manage the relationships between objects (parents and children) and to identify object types at runtime. Understanding these functions is essential for effective UI management.

Most widgets are composed of other objects. For example, a Button widget typically contains child objects such as a Label for the button text and sometimes an Image for an icon. Therefore, it is often necessary to access the internal parts of a widget starting from the reference to the main object.

For this reason, the Annex RDS command LVGL.GET_CHILD_COUNT obj returns the number of child objects contained within a parent object. Once you know how many children an object has, you can use LVGL.GET_CHILD obj, idx to retrieve the reference to each internal part or child object by its index.

To facilitate the identification of these child objects, the command LVGL.GET_CLASS_NAME$ obj returns the class name of an object as a string (e.g., "btn" for a button, "label" for a label). This is useful when you want to determine the type of an object dynamically during runtime.

This mechanism is especially useful for customizing and modifying more complex objects such as Tabview, List, and other composite widgets, where accessing and manipulating internal child objects allows for advanced UI customizations and behavior adjustments.

Example: Creating a Button and Inspecting Its Child

LVGL.INIT white, darkcyan
LVGL.SET_THEME blue, orange, 14, 0
LVGL.CLEAN
' Create a button with label "My Label"
btn = LVGL.BUTTON "My Label"

' Log the number of children of the button (expected: 1)
wlog LVGL.GET_CHILD_COUNT(btn)

' Get the first child of the button (the label)
child = LVGL.GET_CHILD(btn, 0)

' Log the class name of the child (expected: "label")
wlog LVGL.GET_CLASS_NAME$(child)

do
lvgl.refresh
loop

Summary Table

CommandDescriptionArgumentsReturn TypeNotes
LVGL.GET_CHILD_COUNT objReturns number of child objectsobj (parent object)IntegerUseful to know how many children exist
LVGL.GET_CHILD obj, idxReturns child object at specified indexobj (parent), idx (child index)Object pointerIndex is zero-based
LVGL.GET_CLASS_NAME$ objReturns class name of the object as a stringobjStringHelps identify object type at runtime

Widgets: Label, Button, Checkbox, Radio and Switch

Introduction to Basic LVGL Widgets in Annex RDS

LVGL, as integrated in Annex RDS, provides a variety of widgets to build interactive and user-friendly graphical interfaces. Among the most commonly used are the Label, Button, Checkbox, Radio, and Switch. These widgets serve as the building blocks for most GUIs, offering both display and user input capabilities.

Below is an introduction to each widget, including a short description, customization details, and practical examples with event handling.

Label

A Label displays text on the screen. It can be used alone or as a child of other widgets (such as buttons).

If the size (width and height) of the label is not specified, it automatically takes the size required to fit the entire text. This may cause the label to extend beyond the screen boundaries if the text is too long.

To control how the label behaves with long text, you can customize it using the command:

LVGL.SET_MODE label, mode

Where mode corresponds to the lv_label_long_mode_t enumeration:

0 : Wrap lines longer than the object width and expand the object height.

1 : Write dots ("...") at the end if the text is too long (ellipsis).

2 : Roll the text back and forth (horizontal scrolling).

3 : Roll the text circularly (continuous scrolling).

4 : Clip the text outside the object's size (text is cut off).

Example demonstrating different label modes and sizing:

LVGL.INIT white, darkcyan
LVGL.SET_THEME blue, orange, 14, 0
LVGL.CLEAN
LVGL.SET_FLEX_FLOW lvgl.screen, 1 'auto arrange items in a column.

lbl_wrap = LVGL.LABEL "Wrapped label text that fits within the widget width"
LVGL.SET_MODE lbl_wrap, 0 ' wrap lines and expand height
LVGL.SET_WIDTH lbl_wrap, 200 'set the dimension otherwise will be out of screen

lbl_ellipsis = LVGL.LABEL "Ellipsis mode label with long text"
LVGL.SET_MODE lbl_ellipsis, 1 ' show dots if too long
LVGL.SET_SIZE lbl_ellipsis, 200, 20 'set the dimension 

lbl_roll_back_forth = LVGL.LABEL "Rolling back and forth label text"
LVGL.SET_MODE lbl_roll_back_forth, 2 ' roll back and forth
LVGL.SET_WIDTH lbl_roll_back_forth, 200 'set the dimension 

lbl_roll_circular = LVGL.LABEL "Circular rolling label text"
LVGL.SET_MODE lbl_roll_circular, 3 ' roll circularly
LVGL.SET_WIDTH lbl_roll_circular, 120 'set the dimension 

lbl_clip = LVGL.LABEL "Clipped label text that will be cut off if too long"
LVGL.SET_MODE lbl_clip, 4 ' clip text
LVGL.SET_WIDTH lbl_clip, 200 'set the dimension 

do
  lvgl.refresh
loop

Button

A Button is a clickable widget, often used to trigger actions or events.

btn = LVGL.BUTTON "Click Me"

LVGL.ADD_EVENT btn, 7, btn_clicked ' 7 = clicked event
. . . .

btn_clicked:
LVGL.SET_TEXT msg_lbl, "Button clicked!"
RETURN

Checkbox

A Checkbox allows the user to select or deselect an option. It can generate events when toggled, enabling your application to react accordingly.

chk = LVGL.CHECKBOX "Accept Terms"

LVGL.ADD_EVENT chk, 0x20, chk_toggled ' 0x20 = value changed event
. . . .

chk_toggled:
LVGL.SET_TEXT msg_lbl, "Checkbox toggled. Current state: " + STR$(LVGL.GET_STATE(chk))
RETURN

Radio

A Radio button allows the user to select one option from a group. Radio buttons can also generate events when their state changes.

rad1 = LVGL.RADIO "Option 1"
rad2 = LVGL.RADIO "Option 2"

LVGL.ADD_EVENT rad1, 0x20, rad1_toggled
LVGL.ADD_EVENT rad2, 0x20, rad2_toggled
. . . .

rad1_toggled:
LVGL.SET_TEXT msg_lbl, "Radio 1 toggled. Current state: " + STR$(LVGL.GET_STATE(rad1))
RETURN
rad2_toggled:
LVGL.SET_TEXT msg_lbl, "Radio 2 toggled. Current state: " + STR$(LVGL.GET_STATE(rad2))
RETURN

Switch

A Switch is a toggle widget representing an ON/OFF state, similar to a physical switch. It supports events to detect state changes.

sw = LVGL.SWITCH()

LVGL.ADD_EVENT sw, 0x20, sw_toggled
. . . .

sw_toggled:
LVGL.SET_TEXT msg_lbl, "Switch toggled. Current state: " + STR$(LVGL.GET_STATE(sw))
RETURN

Complete Example: Widgets with Event Handlers Updating a Message Label

LVGL.INIT white, darkcyan
LVGL.SET_THEME blue, orange, 14, 0
LVGL.CLEAN
LVGL.SET_FLEX_FLOW lvgl.screen, 1  'auto arrange items in a column.

msg_lbl = LVGL.LABEL("Message Label")

btn = LVGL.BUTTON "Click Me"
LVGL.ADD_EVENT btn, 7, btn_clicked

chk = LVGL.CHECKBOX "Accept Terms"
LVGL.ADD_EVENT chk, 0x20, chk_toggled

rad1 = LVGL.RADIO "Option 1"
rad2 = LVGL.RADIO "Option 2"
LVGL.ADD_EVENT rad1, 0x20, rad1_toggled
LVGL.ADD_EVENT rad2, 0x20, rad2_toggled

sw = LVGL.SWITCH
LVGL.ADD_EVENT sw, 0x20, sw_toggled

do
  lvgl.refresh
loop

btn_clicked:
LVGL.SET_TEXT msg_lbl, "Button clicked!"
RETURN
chk_toggled:
LVGL.SET_TEXT msg_lbl, "Checkbox toggled. Value: " + STR$(LVGL.HAS_STATE(chk, 1)) 'checked
RETURN
rad1_toggled:
LVGL.SET_TEXT msg_lbl, "Radio 1 toggled. Value: " + STR$(LVGL.HAS_STATE(rad1, 1)) 'checked 
RETURN
rad2_toggled:
LVGL.SET_TEXT msg_lbl, "Radio 2 toggled. Value: " + STR$(LVGL.HAS_STATE(rad2, 1)) 'checked
RETURN
sw_toggled:
LVGL.SET_TEXT msg_lbl, "Switch toggled. Value: " + STR$(LVGL.HAS_STATE(sw, 1)) 'checked 
RETURN
image.png

Notes on Events

Checkboxes, radio buttons, switches, and buttons all support event handling in Annex RDS. Labels typically do not generate events unless explicitly configured, but you can attach events if needed.

Summary Table

WidgetCreation CommandDescriptionCustomization & Events
LabelLVGL.LABEL "text"Displays static or dynamic textLVGL.SET_MODE: 0=wrap, 1=ellipsis, 2=roll back/forth, 3=roll circular, 4=clip
ButtonLVGL.BUTTON "text"Clickable button with a labelSupports click events
CheckboxLVGL.CHECKBOX "text"Selectable box for optionsSupports toggle events
RadioLVGL.RADIO "text"Single-choice option in a groupSupports toggle events
SwitchLVGL.SWITCH()ON/OFF toggle switchSupports toggle events

Widgets: Slider, Arc, Bar, LED, and Spinner

Introduction to Slider, Arc, Bar, LED, and Spinner Widgets in Annex RDS

This chapter introduces additional LVGL widgets available in Annex RDS: the Slider, Arc, Bar, LED, and Spinner. These widgets provide interactive controls and visual indicators commonly used in graphical user interfaces.

Slider

A Slider is a widget similar to a bar but with a draggable knob allowing the user to select a value within a range. It can be horizontal or vertical and supports events to react to value changes.

' Create and configure a horizontal slider
slider = LVGL.SLIDER()
LVGL.SET_WIDTH slider, 200
LVGL.ADD_EVENT slider, 0x20, slider_changed ' 0x20 = value changed event

' Optional: set slider range and initial value
LVGL.SET_RANGE slider, 0, 100
LVGL.SET_VALUE slider, 50
....

slider_changed:
val = LVGL.GET_VALUE slider
LVGL.SET_TEXT msg_lbl, "Slider value: " + STR$(val)
RETURN

Arc

An Arc widget displays a circular arc that can indicate progress or a value. It can be animated or controlled programmatically.

arc = LVGL.ARC
LVGL.SET_SIZE arc, 150, 150
LVGL.SET_RANGE arc, 0, 100
LVGL.SET_VALUE arc, 50
LVGL.ADD_EVENT arc, 0x20, arc_changed
....

arc_changed:
val = LVGL.GET_VALUE arc
LVGL.SET_TEXT msg_lbl, "Arc value: " + STR$(val)
RETURN

Bar

A Bar is a simple progress bar or indicator that shows a value visually, without a knob.

bar = LVGL.BAR
LVGL.SET_SIZE bar, 200, 20
LVGL.SET_VALUE bar, 40
....

bar_changed:
val = LVGL.GET_VALUE bar
LVGL.SET_TEXT msg_lbl, "Bar value: " + STR$(val)
RETURN

LED

An LED widget simulates an LED indicator that can be turned on or off and colored.

led = LVGL.LED red
LVGL.SET_SIZE led, 30, 30

Timer0 1000, blink_LED
....

blink_LED:
IF LVGL.GET_VALUE(led) <> 255 THEN
  LVGL.SET_VALUE led, 255
ELSE
  LVGL.SET_VALUE led, 0
END IF
RETURN

Spinner

A Spinner widget is an animated indicator, often used to show loading or processing.

spinner = LVGL.SPINNER 1000, 240
LVGL.SET_SIZE spinner, 50, 50

Example: Linking Arc Widget Events to a Bar Widget

LVGL.INIT white, darkcyan
LVGL.SET_THEME blue, orange, 14, 0
LVGL.CLEAN
LVGL.SET_FLEX_FLOW lvgl.screen, 1 'auto align

arc = LVGL.ARC
LVGL.SET_SIZE arc, 150, 150
LVGL.SET_RANGE arc, 0, 100
LVGL.SET_VALUE arc, 50
LVGL.ADD_EVENT arc, 0x20, arc_changed

bar = LVGL.BAR
LVGL.SET_SIZE bar, 200, 20
LVGL.SET_RANGE bar, 0, 100
LVGL.SET_VALUE bar, 50

do
  lvgl.refresh
loop
arc_changed:
value = LVGL.GET_VALUE arc
LVGL.SET_VALUE bar, value
RETURN
image.png

Extended Example: Arc, Bar, and Value Display Label with Event Handling

LVGL.INIT white, darkcyan
LVGL.SET_THEME blue, orange, 14, 0
LVGL.CLEAN
LVGL.SET_FLEX_FLOW lvgl.screen, 1

arc = LVGL.ARC
LVGL.SET_SIZE arc, 150, 150
LVGL.SET_RANGE arc, 0, 100
LVGL.SET_VALUE arc, 0
LVGL.ADD_EVENT arc, 0x20, arc_changed

bar = LVGL.BAR
LVGL.SET_SIZE bar, 200, 20
LVGL.SET_RANGE bar, 0, 100

lbl = LVGL.LABEL "Arc Value: 0%"
LVGL.SET_WIDTH lbl, 200
LVGL.SET_MODE lbl, 1 ' Ellipsis mode

do
  lvgl.refresh
loop
arc_changed:
value = LVGL.GET_VALUE arc
LVGL.SET_VALUE bar, value
LVGL.SET_TEXT lbl, "Arc Value: " + STR$(value) + "%"
RETURN

Example: LED Blinking with Timer0 and Spinner Animation

LVGL.INIT white, darkcyan
LVGL.SET_THEME blue, orange, 14, 0
LVGL.CLEAN
LVGL.SET_FLEX_FLOW lvgl.screen, 1 ' auto align
LVGL.SET_FLEX_ALIGN lvgl.screen, 5, 2, 2 ' auto center and expand
LVGL.SET_PAD lvgl.screen, 10, 10, 10, 10 ' pad at 10px

led = LVGL.LED red

spinner = LVGL.SPINNER 1000, 240

Timer0 1000, blink_LED

do
  lvgl.refresh
loop
blink_LED:
IF LVGL.GET_VALUE(led) <> 255 THEN
  LVGL.SET_VALUE led, 255
ELSE
  LVGL.SET_VALUE led, 0
END IF
RETURN

Summary Table

WidgetCreation CommandDescriptionEvents
SliderLVGL.SLIDERDraggable knob to select valueSupports value changed (0x20)
ArcLVGL.ARCCircular progress/value indicatorSupports value changed (0x20)
BarLVGL.BARProgress bar without knobMay support value changed (0x20)
LEDLVGL.LEDColored on/off indicatorNo events; controlled via timer
SpinnerLVGL.SPINNERAnimated loading indicatorNo events

Introduction to Dropdown, Roller, and Textarea Widgets in Annex RDS

This chapter introduces the Dropdown, Roller, and Textarea widgets available in Annex RDS. These widgets provide advanced user input and selection capabilities for your GUI applications.

Dropdown Widget Example

LVGL.INIT white, darkcyan
LVGL.SET_THEME blue, orange, 14, 0
LVGL.CLEAN
LVGL.SET_FLEX_FLOW lvgl.screen, 1

msg_lbl = LVGL.LABEL("Message Label")

' Create a dropdown with options
dropdown = LVGL.DROPDOWN "Red\nGreen\nBlue"
LVGL.ADD_EVENT dropdown, 0x20, dropdown_changed

do
  lvgl.refresh
loop
dropdown_changed:
selected = LVGL.GET_VALUE(dropdown)
LVGL.SET_TEXT msg_lbl, "Dropdown selected: " + STR$(selected)
RETURN
image.png

Roller Widget Example

LVGL.INIT white, darkcyan
LVGL.SET_THEME blue, orange, 14, 0
LVGL.CLEAN
LVGL.SET_FLEX_FLOW lvgl.screen, 1

msg_lbl = LVGL.LABEL("Message Label")

' Create a roller with options
roller = LVGL.ROLLER "Option 1\nOption 2\nOption 3\nOption 4\nOption 5"
LVGL.ADD_EVENT roller, 0x20, roller_changed

do
  lvgl.refresh
loop
roller_changed:
selected = LVGL.GET_VALUE(roller)
LVGL.SET_TEXT msg_lbl, "Roller selected: " + STR$(selected)
RETURN
image.png

Textarea Widget Example

LVGL.INIT white, darkcyan
LVGL.SET_THEME blue, orange, 14, 0
LVGL.CLEAN
LVGL.SET_FLEX_FLOW lvgl.screen, 1

msg_lbl = LVGL.LABEL("Message Label")

' Create a textarea
textarea = LVGL.TEXTAREA "Enter your text here..."
LVGL.SET_SIZE textarea, 200, 100
LVGL.ADD_EVENT textarea, 0x20, textarea_changed

do
  lvgl.refresh
loop
textarea_changed:
text = LVGL.GET_TEXT(textarea)
LVGL.SET_TEXT msg_lbl, "Textarea content: " + text
RETURN
image.png

Note on Textarea Widget

The Textarea widget is mainly used for entering and modifying multi-line text. Its full capabilities, including integration with the Keyboard widget for text input, will be explained in detail when we introduce the Keyboard widget in a later chapter.

Summary Table

WidgetCreation CommandDescriptionEvents
DropdownLVGL.DROPDOWNSelect an option from a dropdown listSupports value changed (0x20)
RollerLVGL.ROLLERScroll and select from a listSupports value changed (0x20)
TextareaLVGL.TEXTAREAMulti-line text input (details with Keyboard widget)Supports value changed (0x20)

Chapter 3: The LIST Widget in LVGL

The LIST widget in LVGL is a versatile container that organizes items vertically, making it ideal for menus, navigation panels, and content lists. Each item in the list is typically a button or label, but you can add any widget as a list item.

Creating a List

To create a list, use the LVGL.LIST command. You can optionally specify the positon and the size

' Create a list widget 
list = LVGL.LIST

Adding Items to the List

To add an item to the list, use functions like LVGL.ADD_TEXT for labels and LVGL.ADD_BUTTON for interactive buttons. You can include icons in buttons by specifying a Unicode symbol as the first argument, or put a single space (" ") for no icon.

' Add a header label to the list
info1 = LVGL.ADD_TEXT list, "Main Options"

' Add a button with an icon and text
item1 = LVGL.ADD_BUTTON list, "\xEF\x80\x95", "Home"

' Add a button with no icon, just text
item2 = LVGL.ADD_BUTTON list, " ", "Settings"

You can also use unescape$() to embed Unicode icons directly into text for decorative separators or special labels.

Customizing List Items

You can customize each item as you would any other widget.

For example, set the color for a button:

' Set the text color for a button
LVGL.SET_TEXT_COLOR item1, RED

You can also associate an event to trigger any action

'Set a click event on the button
LVGL.ADD_EVENT item1,  7, item1_clicked

Setting List Size and Position

You can set the size and position of the list widget using LVGL.SET_SIZE and LVGL.SET_POS:

LVGL.SET_SIZE list, 200, 300
LVGL.SET_POS list, 50, 50

Or use percentage-based sizing for responsive layouts:

LVGL.SET_SIZE list, LVGL.pct(80), LVGL.pct(60)
LVGL.SET_POS list, LVGL.pct(10), LVGL.pct(20)

Example: Creating a Vertical Menu List with Icons and Decorative Text

LVGL.INIT white, darkcyan
LVGL.SET_THEME blue, orange, 14, 0
LVGL.CLEAN
' Create a vertical list
list = LVGL.LIST
LVGL.SET_SIZE list, LVGL.pct(70), LVGL.pct(80)
LVGL.SET_POS list, LVGL.pct(15), LVGL.pct(10)

' Add a label as a separator
info1 = LVGL.ADD_TEXT list, "Main Options"
' Add items to the list
item1 = LVGL.ADD_BUTTON list, "\xEF\x80\x95", "Home"
item2 = LVGL.ADD_BUTTON list, "\xEF\x80\x93", "Settings"
item3 = LVGL.ADD_BUTTON list, "\xEF\x87\xAB", "Wifi"

' Add a label as a separator, including decorative Unicode icons
info2 = LVGL.ADD_TEXT list, unescape$("\xEF\x80\x81 Music \xEF\x80\x81")
' Add items to the list
item21 = LVGL.ADD_BUTTON list, "\xEF\x81\x8B", "Play"
item22 = LVGL.ADD_BUTTON list, "\xEF\x81\x8C", "Pause"
item23 = LVGL.ADD_BUTTON list, "\xEF\x81\x8D", "Stop"
item24 = LVGL.ADD_BUTTON list, "\xEF\x81\x92", "Eject"

'add a "standard" button at the bottom of the list
but = LVGL.BUTTON "Hello"
LVGL.SET_PARENT but, list
do
  lvgl.refresh
loop
image.png image.png

Summary Table

Action Command Example Effect
Create a list list = LVGL.LIST Creates a vertical list
Add text item LVGL.ADD_TEXT list, "Header" Adds a label to the list
Add button with icon LVGL.ADD_BUTTON list, "\xEF\x80\x95", "Home" Adds a button with icon and text
Add button with no icon LVGL.ADD_BUTTON list, "", "Settings" Adds a button with text only
Add label with Unicode icons LVGL.ADD_TEXT list, unescape$("\xEF\x80\x81 Music \xEF\x80\x81") Adds a label with decorative Unicode
Set list size/position LVGL.SET_SIZE list, 200, 300 List is 200x300 pixels
Set list size/position (percentage) LVGL.SET_SIZE list, LVGL.pct(80), LVGL.pct(60) List is 80% wide, 60% tall

In Short:

LIST widgets are perfect for menus and organized content.

Add text and buttons to the list—buttons can include icons (Unicode symbols) or be text-only.

Use Unicode symbols for icons in buttons or decorative elements in text using unescape$().

Customize items as needed, including setting callbacks for buttons.

Set size and position with pixels or percentages for responsive layouts.

Chapter: Images in LVGL

To use images in your LVGL interfaces, you should be aware that images must be loaded into memory for display. The internal memory is often not sufficient for large images, so it is recommended to use a module with PSRAM (Pseudo Static RAM) if you intend to display several images or large ones.

Displaying Images in LVGL

You can display images in several ways:

Supported File Formats

The following image file formats are supported:

Automatic Decoding in Annex

In Annex, image decoding is automatic and integrated. You only need to provide the filename, and the image will be loaded and displayed directly. There is no need to manually decode the image or handle memory yourself.

Loading an Image

To display an image from a file, use the following command:

img = LVGL.IMAGE "/flower2.png"

Once the image is loaded, you can manipulate it just like any other object in LVGL. You can set its position, size, or apply transformations as needed.

Example: Controlling the rotation and zoom of a transparent image

LVGL.INIT white, 0x003a57
'LVGL.SET_THEME white, orange, 14, 0
lvgl.set_theme 0x107090, 0xff9900,14, 0
LVGL.CLEAN
'background image
back = LVGL.IMAGE "/landscape2.jpg"

img = LVGL.IMAGE "/flower2.png"
LVGL.ALIGN_TO img, LVGL.SCREEN, 9, 0, -30
LVGL.SET_TRANSFORM_PIVOT img, LVGL.PCT(50), LVGL.PCT(50)

slider_rotation = LVGL.SLIDER 20, 170
LVGL.SET_RANGE slider_rotation, 0, 360
LVGL.ADD_EVENT slider_rotation, 0x20, rotate_image

slider_zoom = LVGL.SLIDER 20, 210
LVGL.ADD_EVENT slider_zoom, 0x20, zoom_image

lab1 = LVGL.LABEL "Rotation"
LVGL.ALIGN_TO lab1, slider_rotation, 11

lab2 = LVGL.LABEL "Zoom"
LVGL.ALIGN_TO lab2, slider_zoom, 11

do
  lvgl.refresh
loop

rotate_image:
LVGL.SET_TRANSFORM_ROTATION img, LVGL.GET_VALUE(slider_rotation)
return
zoom_image:
z = LVGL.GET_VALUE(slider_zoom)*2.56+256 
LVGL.SET_TRANSFORM_SCALE img, z, z
return
image.png

How it works

Summary Table

Action Command Example Effect
Load image from file img = LVGL.IMAGE "/flower2.png" Displays image from file
Set image position LVGL.ALIGN_TO img, LVGL.SCREEN, 9, 0, -30 Aligns image to screen center
Set transformation pivot LVGL.SET_TRANSFORM_PIVOT img, LVGL.PCT(50), LVGL.PCT(50) Sets rotation/zoom center to image center
Control rotation LVGL.SET_TRANSFORM_ROTATION img, angle Rotates image to specified angle
Control zoom LVGL.SET_TRANSFORM_SCALE img, scale, scale Scales image by specified factor (256 = normal size)

In Short:

With these features, you can easily enhance your LVGL interfaces with images, icons, and graphics!