add some code

This commit is contained in:
2025-09-05 13:25:11 +08:00
parent 9ff0a99e7a
commit 3cf1229a85
8911 changed files with 2535396 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,60 @@
# based on http://protips.readthedocs.io/link-roles.html
#from __future__ import print_function, unicode_literals
import os
import re
import subprocess
from collections import namedtuple
from docutils import nodes
from sphinx.transforms.post_transforms import SphinxPostTransform
URL_BASE = {
"zh_CN": "https://lvgl.100ask.net/"
}
class translation_link(nodes.Element):
"""Node for "link_to_translation" role."""
# Linking to translation is done at the "writing" stage to avoid issues with the info being cached between builders
def link_to_translation(name, rawtext, text, lineno, inliner, options={}, content=[]):
node = translation_link()
node['expr'] = (rawtext, text, options)
return [node], []
class TranslationLinkNodeTransform(SphinxPostTransform):
# Transform needs to happen early to ensure the new reference node is also transformed
default_priority = 0
def run(self, **kwargs):
# Only output relative links if building HTML
for node in self.document.traverse(translation_link):
if 'html' in self.app.builder.name:
rawtext, text, options = node['expr']
(language, link_text) = text.split(':')
env = self.document.settings.env
docname = env.docname
# doc_path = env.doc2path(docname, False)
if "LVGL_URLPATH" not in os.environ:
os.environ['LVGL_URLPATH'] = 'master'
urlpath = os.getenv('LVGL_URLPATH')+'/'
return_path = URL_BASE.get(language, "") + urlpath
url = '{}.html'.format(os.path.join(return_path, docname))
node.replace_self(nodes.reference(rawtext, link_text, refuri=url, **options))
else:
node.replace_self([])
def setup(app):
# link to the current documentation file in specific language version
app.add_role('link_to_translation', link_to_translation)
app.add_node(translation_link)
app.add_post_transform(TranslationLinkNodeTransform)
return {'parallel_read_safe': True, 'parallel_write_safe': True, 'version': '0.5'}

View File

@@ -0,0 +1,133 @@
import os
from docutils import nodes
from docutils.parsers.rst import Directive, directives
# from docutils.parsers.rst.directives.images import Image
# from sphinx.directives.code import LiteralInclude
def excluded_list(argument):
return argument.split(',')
class LvExample(Directive):
required_arguments = 1
option_spec = {
'excluded_languages': excluded_list,
'language': directives.unchanged,
'description': directives.unchanged
}
def get_example_code_path(self, example_path, language):
base_path = os.path.dirname(__file__)
examples_path = os.path.abspath(os.path.join(base_path, '..', 'examples'))
example_path = os.path.join(examples_path, example_path + '.' + language)
return example_path
def human_language_name(self, language):
if language == 'py':
return 'MicroPython'
elif language == 'c':
return 'C'
else:
return language
def github_path(self, example_path, language):
env = self.state.document.settings.env
return f"https://github.com/lvgl/lvgl/blob/{env.config.repo_commit_hash}/examples/{example_path}.{language}"
def embed_code(self, example_file, example_path, language, buttons={}):
toggle = nodes.container('', literal_block=False, classes=['toggle'])
header = nodes.container('', literal_block=False, classes=['header'])
toggle.append(header)
try:
with open(example_file, 'rb') as f:
contents = f.read().decode('utf-8')
except FileNotFoundError:
print('File Not Found', example_file)
contents = 'Error encountered while trying to open ' + example_file
literal_list = nodes.literal_block(contents, contents)
literal_list['language'] = language
toggle.append(literal_list)
paragraph_node = nodes.raw(text=f"<p>{self.human_language_name(language)} code &nbsp;</p>", format='html')
for text, url in buttons.items():
paragraph_node.append(nodes.raw(text=f"<a class='lv-example-link-button' onclick=\"event.stopPropagation();\" href='{url}'>{text}</a>", format='html'))
header.append(paragraph_node)
return toggle
def run(self):
example_path = self.arguments[0]
example_name = os.path.split(example_path)[1]
excluded_languages = self.options.get('excluded_languages', [])
node_list = []
env = self.state.document.settings.env
iframe_html = ""
c_path = self.get_example_code_path(example_path, 'c')
py_path = self.get_example_code_path(example_path, 'py')
if os.path.exists(c_path):
c_code = self.embed_code(c_path, example_path, 'c', buttons={
'<i class="fa fa-github"></i>&nbsp;View on GitHub': self.github_path(example_path, 'c')
})
else:
c_code = None
if os.path.exists(py_path):
py_code = self.embed_code(py_path, example_path, 'py', buttons={
'<i class="fa fa-github"></i>&nbsp;View on GitHub': self.github_path(example_path, 'py'),
'<i class="fa fa-play"></i>&nbsp;MicroPython Simulator': f"https://sim.lvgl.io/v{env.config.version}/micropython/ports/javascript/index.html?script_startup=https://raw.githubusercontent.com/lvgl/lvgl/{env.config.repo_commit_hash}/examples/header.py&script=https://raw.githubusercontent.com/lvgl/lvgl/{env.config.repo_commit_hash}/examples/{example_path}.py"
})
else:
py_code = None
if 'c' not in excluded_languages:
if env.app.tags.has('html'):
iframe_html = f"<div class='lv-example' data-real-src='/{env.config.version}/_static/built_lv_examples/index.html?example={example_name}&w=320&h=240'></div>"
description_html = f"<div class='lv-example-description'>{self.options.get('description', '')}</div>"
layout_node = nodes.raw(text=f"<div class='lv-example-container'>{iframe_html}{description_html}</div>", format='html')
node_list.append(layout_node)
if 'c' not in excluded_languages and c_code is not None:
node_list.append(c_code)
if 'py' not in excluded_languages and py_code is not None:
node_list.append(py_code)
trailing_node = nodes.raw(text=f"<hr/>", format='html')
node_list.append(trailing_node)
return node_list
def setup(app):
app.add_directive("lv_example", LvExample)
# Direct [View on GitHub] links in examples to use current
# branch (stored in LVGL_GITCOMMIT environment variable) instead
# of the current commit hash as was being done previously.
# Default to 'master' if Sphinx is being run outside of `build.py`.
# Resulting example link:
# [https://github.com/lvgl/lvgl/blob/master/examples/anim/lv_example_anim_1.c].
# [https://github.com/lvgl/lvgl/blob/v8.4.0/examples/anim/lv_example_anim_1.c].
# [https://github.com/lvgl/lvgl/blob/v9.2.0/examples/anim/lv_example_anim_1.c].
if 'LVGL_GITCOMMIT' in os.environ:
git_commit = os.environ['LVGL_GITCOMMIT']
else:
git_commit = 'master'
app.add_config_value("repo_commit_hash", git_commit, "env")
# if 'repo_commit_hash' in app.config._options:
# print(f"repo_commit_hash from lv_example.py: [{app.config._options['repo_commit_hash']}]")
# else:
# print("repo_commit_hash not found in [app.config._options] at this time.")
return {
'version': '0.1',
'parallel_read_safe': True,
'parallel_write_safe': True,
}

View File

@@ -0,0 +1,287 @@
table, th, td {
border: 1px solid #bbb;
padding: 10px;
}
span.pre {
padding-right:8px;
}
span.pre {
padding-right: 8px;
}
span.pre:first-child {
padding-right: 0px;
}
code.sig-name {
/*margin-left:8px;*/
}
/*-------------------------------------------------------------------------
* `home-img` class is (at this writing) exclusively used for the large
* buttons on the landing page. The rules below provide their hover
* movement behavior.
*-------------------------------------------------------------------------
*/
.home-img {
width: 32%;
transition: transform .3s ease-out;
}
.home-img:hover {
transform: translate(0, -10px);
}
/*-------------------------------------------------------------------------
* All `wy-...` and `rst-content` classes are classes from
* `sphinx_rtd_theme` `layout.html` template.
*-------------------------------------------------------------------------
* `wy-side-nav-search` class adorns the <div> element in the upper left
* corner of each page that contains:
* - the LVGL logo
* - documentation-version selector drop-down (matches LVGL release versions)
* - search TextBox
*/
.wy-side-nav-search {
background-color: #f5f5f5;
}
.wy-side-nav-search > div.version {
color: #333;
display: none; /*replaced by dropdown*/
}
/* `rst-content` class marks the <div> element that contains the whole
* visible part of the right panel where the page content is shown. */
/*Let `code` wrap*/
.rst-content code, .rst-content tt, code {
white-space: normal;
}
.wy-nav-content {
padding: 1.618em 3.236em;
height: 100%;
max-width: 1920px;
margin: auto
}
/*-------------------------------------------------------------------------
* `lv_example` Sections
*-------------------------------------------------------------------------
* The below contains a documented example to show the relationships of
* the rules below to their selected elements within the example itself.
*-------------------------------------------------------------------------
* `lv_example` sections are the output of `./docs/src/_ext/lv_example.py`
* custom Sphinx extension. These are generated when the `.rst` source
* file contains a pattern like this:
*
* A very simple *hello world* label // Sphinx+docutils (.rst parser) generates
* --------------------------------- // the <section> element and title from this.
*
* .. lv_example:: get_started/lv_example_get_started_1 // `lv_example.py` generates the
* :language: c // rest of the <section> content
* // below from this.
*-------------------------------------------------------------------------
* Visible layout consists of a <section> element containing:
* - <h3> element with title
* - <div> element containing live running web-assembly example of LVGL UI
* - <div> element containing [> Show C Code] [G View on GitHub] buttons
* and the C code below them.
*
* <section id="a-very-simple-hello-world-label"> // Contains whole example, buttons and code.
* <h3>A very simple <em>hello world</em> label...</h3> // The title itself.
* <div class="lv-example-container"> // Container for running example.
* <div class="lv-example" data-real-src="..." data-is-loaded="true">
* <iframe src="..."></iframe> // Running web-assembly example.
* </div>
* <div class="lv-example-description"></div> // Whole area to the right of running example.
* </div>
* <div class="toggle docutils container"> // Contains buttons + code;
* <div class="header docutils container" ...> // Contains buttons + code; `open` class is added to this to element "open" the code <div> element.
* <p>C code &nbsp;</p> // [C code] "button"; JS code in `page.html` template arms this "button" with its events when `$(document).ready` event fires.
* <a class="lv-example-link-button" ...> // [View on GitHub] "button"; provides hyperlink to the example code on GitHub.
* <i class="fa fa-github"></i> // "GitHub Cat" icon
* &nbsp;View on GitHub // text of "button"
* </a>
* </div>
* <div class="highlight-c notranslate" ...>
* <div class="highlight">
* <pre>
* ... // Highlighted C code
* </pre>
* </div>
* </div>
* </div>
* <hr>
* </section>
*/
.toggle .header {
display: block;
clear: both;
cursor: pointer;
font-weight: bold;
}
/* Selects <div class="header docutils container" ...> element.
* Places "disclosure triangle" icon + non-breaking space to its left.*/
.toggle .header:before {
font-family: FontAwesome, "Lato", "proxima-nova", "Helvetica Neue", Arial, sans-serif;
content: "\f0da \00a0 Show "; /* \f0da = "closed" disclosure triangle; \f00a0 = non-breaking space */
display: inline-block;
font-size: 1.1em;
}
/* Selects <div class="header docutils container" ...> element when it also contains
* `open` class, which class is toggled by JS code in `page.html` template. */
.toggle .header.open:before {
content: "\f0d7 \00a0 Hide "; /* \f0d7 = "open" disclosure triangle; \f00a0 = non-breaking space */
}
/* Selects the [> C code] "button". */
.header p {
display: inline-block;
font-size: 1.1em;
margin-bottom: 8px;
}
/* Selects web-assembly example <div> and child <iframe> elements. */
.lv-example, .lv-example > iframe {
border: none;
outline: none;
padding: 0;
display: block;
width: 320px;
height: 240px;
flex: none;
position: relative;
}
/* Selects only the <iframe> elements. */
.lv-example > iframe {
position: absolute;
top: 0;
left: 0;
}
/* Selects outer example <div> container elements. */
.lv-example-container {
display: flex;
padding-bottom: 16px;
}
/* Selects <div class="lv-example-description"></div>
* which is the whole area to the right of running example. */
.lv-example-description {
flex: 1 1 auto;
margin-left: 1rem;
}
/* Selects [View on GitHub] "button". */
.lv-example-link-button {
display: inline-block;
padding: 4px 8px;
border-radius: 4px;
background-color: #2980b9;
color: white;
margin: 0 4px;
}
/* Selects [View on GitHub] "button" when in `:hover` state. */
.lv-example-link-button:hover {
color: white;
filter: brightness(120%);
}
/* Selects [View on GitHub] "button" when in `:visited` state. */
.lv-example-link-button:visited {
color: white;
}
/*-------------------------------------------------------------------------
* This doesn't select anything at this writing (29-Mar-2025).
*-------------------------------------------------------------------------
*/
dl.cpp.unexpanded dd {
display: none;
}
/*-------------------------------------------------------------------------
* The `lv-api-...` classes do not select anything at this writing (29-Mar-2025).
*-------------------------------------------------------------------------
*/
.lv-api-expansion-button {
padding: 4px;
}
/* There are no elements that these select at this writing (29-Mar-2025). */
.lv-api-expansion-button::before {
font-family: FontAwesome, "Lato", "proxima-nova", "Helvetica Neue", Arial, sans-serif;
display: inline-block;
font-size: 1.1em;
cursor: pointer;
}
/* There are no elements that these select at this writing (29-Mar-2025). */
.unexpanded .lv-api-expansion-button::before {
content: "\f0da \00a0";
}
.expanded .lv-api-expansion-button::before {
content: "\f0d7 \00a0";
}
/* Selects all <div> elements with "body" class.
* There are no elements that this selects at this writing (29-Mar-2025). */
div.body {
min-width: 360px;
max-width: 1920px;
}
/* Selects CPP-expression content <span> elements generated from
* :cpp:expr:`...` Interpreted-Text-Role expressions in the `.rst` source files. */
.cpp-expr {
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", Courier, monospace;
color: #e74c3c;
padding: 2px 5px;
border: 1px solid #e1e4e5;
font-size: 75%;
}
/* These replace colors present in `pygments.css` which is used in code highlighting.
* These are too dark to be readlable in DARK mode. They include:
* .highlight .nf -- function names
* .highlight .nl -- code labels
* .descname .n -- API documentation function names
* .highlight .p -- Punctuation
* .highlight -- Plain text in a `.. code-block:: none` block
* The first 2 were created by lightening the `pygments.css` colors without changing their
* angle on the color wheel. The added attribute "conditional" also limits this change to
* DARK MODE only instead of both light and dark modes.
*/
/* Name.Function */
html[data-theme="dark"] .highlight .nf {
color: #ccd285;
}
/* Name.Label */
html[data-theme="dark"] .highlight .nl {
color: #0043e2;
}
/* Name */
html[data-theme="dark"] .descname .n {
color: #0a44de;
}
/* Punctuation */
html[data-theme="dark"] .highlight .p {
color: #5c7c72
}
/* Plain text. */
html[data-theme="dark"] .highlight {
background: #ffffff; color: #bfbfbf
}

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 973 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 993 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 990 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

View File

@@ -0,0 +1,57 @@
/* API collapsing */
document.addEventListener('DOMContentLoaded', (event) => {
document.querySelectorAll("dl.cpp").forEach(cppListing => {
const dt = cppListing.querySelector("dt");
let shouldBeExpanded = false;
if(dt.id == document.location.hash.substring(1))
shouldBeExpanded = true;
cppListing.classList.add(shouldBeExpanded ? "expanded" : "unexpanded");
const button = document.createElement("span");
button.classList.add("lv-api-expansion-button");
button.addEventListener("click", () => {
cppListing.classList.toggle("unexpanded");
cppListing.classList.toggle("expanded");
});
dt.insertBefore(button, dt.firstChild);
});
fetch('https://lvgl.io/home-banner.txt') // Replace with your URL
.then(response => {
// Check if the request was successful
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
// Read the response as text
return response.text();
})
.then(data => {
const section = document.querySelector('.wy-nav-content-wrap');
//Add a div
const newDiv = document.createElement('div');
newDiv.style="background-image: linear-gradient(45deg, black, #5e5e5e); color: white; border-bottom: 4px solid #e10010; padding-inline:3em"
section.insertBefore(newDiv, section.firstChild);
//Add a p to the div
const newP = document.createElement('p');
newP.style="padding-block:12px; margin-block:0px;align-content: center;align-items: center;"
newP.innerHTML = data
newDiv.insertBefore(newP, newDiv.firstChild);
const children = newDiv.querySelectorAll('*');
// Iterate over each child
children.forEach(child => {
// Check if the child has an id
if (child.id) {
// Prepend 'docs-' to the id
child.id = 'docs-' + child.id;
}
})
}) .catch(error => {
console.error('Fetch error: ' + error.message);
});
})

View File

@@ -0,0 +1,29 @@
/*https://www.w3schools.com/howto/howto_html_include.asp*/
function includeHTML() {
var z, i, elmnt, file, xhttp;
/*loop through a collection of all HTML elements:*/
z = document.getElementsByTagName("*");
for (i = 0; i < z.length; i++) {
elmnt = z[i];
/*search for elements with a certain attribute:*/
file = elmnt.getAttribute("include-html");
if (file) {
/*make an HTTP request using the attribute value as the file name:*/
xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4) {
if (this.status == 200) {elmnt.innerHTML = this.responseText;}
if (this.status == 404) {elmnt.innerHTML = "Page not found.";}
/*remove the attribute, and call this function once more:*/
elmnt.removeAttribute("w3-include-html");
includeHTML();
}
}
xhttp.open("GET", file, true);
xhttp.send();
/*exit the function:*/
return;
}
}
};

View File

@@ -0,0 +1,31 @@
{% extends "!layout.html" %}
{%- block extrahead %}
{{ super() }}
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-78811084-3"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-78811084-3', { 'anonymize_ip': true });
</script>
{% endblock %}
{% block footer %}
{{ super() }}
<div class="footer">This page uses <a href="https://analytics.google.com/">
Google Analytics</a> to collect statistics. You can disable it by blocking
the JavaScript coming from www.google-analytics.com.
<script type="text/javascript">
(function() {
var ga = document.createElement('script');
ga.src = ('https:' == document.location.protocol ?
'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
ga.setAttribute('async', 'true');
document.documentElement.firstChild.appendChild(ga);
})();
</script>
</div>
{% endblock %}

View File

@@ -0,0 +1,82 @@
{% extends "!page.html" %}
{% block footer %}
<style>
.wy-side-nav-search > div[role="search"] {
color: black;
}
</style>
<script type="text/javascript">
$(document).ready(function() {
$(".toggle > *").hide();
$(".toggle .header").show();
$(".toggle .header").click(function() {
$(this).parent().children().not(".header").toggle(400);
$(this).parent().children(".header").toggleClass("open");
})
});
</script>
<script type="text/javascript">
function add_version_selector()
{
return fetch("https://raw.githubusercontent.com/lvgl/docs_compiled/gh-pages/versionlist.txt")
.then(res => res.text())
.then(text => {
const versions = text.split("\n").filter(version => version.trim().length > 0);
let p = document.getElementById("rtd-search-form").parentElement;
p.innerHTML = `
<select name="versions" id="versions" onchange="ver_sel()" style="border-radius:5px; margin-bottom:15px">
${versions.map(version => {
let versionName = "";
if(version == "master") versionName = "master (latest)";
else versionName = "v" + ((version.indexOf(".") != -1) ? version : (version + " (latest minor)"));
return `<option value="${version}">${versionName}</option>`;
})}
</select>` + p.innerHTML;
});
}
function ver_sel()
{
var x = document.getElementById("versions").value;
window.location.href = window.location.protocol + "//" + window.location.host + "/" + x + "/";
}
document.addEventListener('DOMContentLoaded', (event) => {
add_version_selector().then(() => {
var value = window.location.pathname.split('/')[1];
document.getElementById("versions").value = value;
});
})
document.addEventListener('DOMContentLoaded', (event) => {
function onIntersection(entries) {
entries.forEach(entry => {
let currentlyLoaded = entry.target.getAttribute("data-is-loaded") == "true";
let shouldBeLoaded = entry.intersectionRatio > 0;
if(currentlyLoaded != shouldBeLoaded) {
entry.target.setAttribute("data-is-loaded", shouldBeLoaded);
if(shouldBeLoaded) {
let iframe = document.createElement("iframe");
iframe.src = entry.target.getAttribute("data-real-src");
entry.target.appendChild(iframe);
} else {
let iframe = entry.target.querySelector("iframe");
iframe.parentNode.removeChild(iframe);
}
}
});
}
const config = {
rootMargin: '600px 0px',
threshold: 0.01
};
let observer = new IntersectionObserver(onIntersection, config);
document.querySelectorAll(".lv-example").forEach(iframe => {
observer.observe(iframe);
});
});
</script>
{% endblock %}

View File

@@ -0,0 +1,631 @@
# Configuration file for the Sphinx documentation builder.
# Created by sphinx-quickstart on Wed Jun 12 16:38:40 2019.
#
# This file is execfile()'d with the current directory set to its
# containing directory.
#
# All configuration values have a default; some values are included
# below that are commented out and some of these serve to show the
# default. Note that not all possible configuration values are present.
#
# For the full list of built-in configuration values, see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# The major sections below each reflect a major section of that web page,
# and they are ordered in the same sequence so it is clear what config
# items go with what.
#
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath() to make it absolute, as shown here.
import os
import sys
from sphinx.builders.html import StandaloneHTMLBuilder
base_path = os.path.abspath(os.path.dirname(__file__))
# Add path to import link_roles.py and lv_example.py
sys.path.insert(0, os.path.abspath('./_ext'))
# Add path to import lvgl_version.py. Remember this is
# running under `sphinx-build` environment, not `build.py`.
sys.path.insert(0, base_path)
from lvgl_version import lvgl_version #NoQA
# *************************************************************************
# Project Information
# *************************************************************************
project = 'LVGL'
copyright = '2024-%Y, LVGL Kft'
author = 'LVGL Community'
if __name__ == '__main__':
version_src_path = os.path.join(base_path, '../../lv_version.h')
else:
version_src_path = os.path.join(base_path, 'lv_version.h')
version = lvgl_version(version_src_path)
release = version
# Notes about `version` here:
# ---------------------------
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# A short X.Y version is extracted from `lv_version.h` using a cross-platform compatible
# Python function in lvgl_version.py, and passed in on `sphinx-build` command line.
#
# 22-Apr-2025 while the `-D version=...` on the command line works (as long as quotes
# are not placed around the version), having it added after `sphinx-build` has
# executed this script is not soon enough because we need the version in some
# strings below. So we need to get it here from `lv_version.h` in order to do that.
# *************************************************************************
# General Configuration
# *************************************************************************
# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or custom extensions.
#
# As of 6-Jan-2025, `link_roles` is being commented out because it is being
# replaced by a manually-installed translation link in ./docs/index.rst.
extensions = [
'sphinx_rtd_theme',
'sphinx.ext.autodoc',
'sphinx.ext.intersphinx',
'sphinx.ext.todo',
'breathe',
'sphinx_sitemap',
'lv_example',
'sphinx_design',
'sphinx_rtd_dark_mode',
# 'link_roles',
'sphinxcontrib.mermaid',
]
needs_extensions = {
'sphinxcontrib.mermaid': '0.9.2'
}
# If 'SPHINX_REREDIRECTS_STANDDOWN' environment variable exists and
# is set to a value not equal to '0', then do not add 'sphinx_reredirects'
# to extensions. This gives someone testing/editing/debugging documentation
# build the possibility of skipping adding redirects in the local environment
# if desired.
add_redirects = True
if 'SPHINX_REREDIRECTS_STANDDOWN' in os.environ:
if os.environ.get('SPHINX_REREDIRECTS_STANDDOWN') != '0':
print("sphinx_reredirects standing down as requested.")
add_redirects = False
if add_redirects:
extensions.append('sphinx_reredirects')
del add_redirects
# -------------------------------------------------------------------------
# Options for Highlighting
# -------------------------------------------------------------------------
# The default language to highlight source code in. The default is 'python'.
# The value should be a valid Pygments lexer name, see Showing code examples
# for more details.
highlight_language = 'c'
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# -------------------------------------------------------------------------
# Options for Internationalisation
# -------------------------------------------------------------------------
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = 'en'
# -------------------------------------------------------------------------
# Options for Markup
# -------------------------------------------------------------------------
default_role = 'literal'
# keep_warnings = False # True causes Sphinx warnings to be added to documents.
primary_domain = 'c' # Default: 'py'
# -------------------------------------------------------------------------
# Options for Source Files
# -------------------------------------------------------------------------
# List of glob-style patterns, relative to source directory, that
# match files and directories to ignore when looking for source files.
# These patterns also effect html_static_path and html_extra_path.
exclude_patterns = ['build', 'doxygen', 'Thumbs.db', '.DS_Store',
'README.md', 'README_*', 'lv_examples', 'out_html', 'env', '_ext', 'examples']
# The master toctree document. (Root of TOC tree.)
master_doc = 'index'
# The suffix(es) of source filenames.
# You can specify multiple suffixes as a dictionary per
# https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-source_suffix
source_suffix = {'.rst': 'restructuredtext'}
# -------------------------------------------------------------------------
# Options for Smart Quotes
# -------------------------------------------------------------------------
# Enabling smart quotes action to convert `--` to en dashes and `---` to em
# dashes. Converting quotation marks and ellipses is NOT done because the
# default `smartquotes_action` 'qDe' is changed to just 'D' below, which
# accomplishes the dash conversions as desired.
#
# For list of all possible smartquotes_action values, see:
# https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-smartquotes_action
smartquotes = True
smartquotes_action = 'D'
smartquotes_excludes = {'builders': ['man', 'text', 'json', 'xml', 'latex']}
# -------------------------------------------------------------------------
# Options for Templating
# -------------------------------------------------------------------------
# A list of paths that contain extra templates (or templates that overwrite
# builtin/theme-specific templates). Relative paths are taken as relative
# to the configuration directory.
templates_path = ['_templates']
# -------------------------------------------------------------------------
# Options for Warning Control
# -------------------------------------------------------------------------
# suppress_warnings = ['app.add_directive', etc.]
# *************************************************************************
# Builder Options
# *************************************************************************
# -------------------------------------------------------------------------
# Options for HTML Builder
# -------------------------------------------------------------------------
# The theme for HTML output. See https://www.sphinx-doc.org/en/master/usage/theming.html
html_theme = 'sphinx_rtd_theme'
# Theme options are theme-specific and customize the look and feel of a
# theme further. For a list of options available for each theme, see the
# theme documentation.
#
# Various other builders are derived from the HTML output, and also make use
# of these options.
# Note: 'display_version' option is now obsolete in the current (08-Oct-2024)
# version of sphinx-rtd-theme (upgraded for Sphinx v8.x). The removed line
# is preserved by commenting it out in case it is ever needed again.
html_theme_options = {
# 'display_version': True,
'prev_next_buttons_location': 'both',
'style_external_links': False,
# 'vcs_pageview_mode': '',
# 'style_nav_header_background': 'white',
# Toc options
'sticky_navigation': True,
'navigation_depth': 4,
'includehidden': False,
'titles_only': False,
'collapse_navigation': False,
'logo_only': True,
}
# For site map generation
if "LVGL_URLPATH" not in os.environ:
os.environ['LVGL_URLPATH'] = 'master'
_branch = os.getenv('LVGL_URLPATH')
html_baseurl = f"https://docs.lvgl.io/{_branch}/"
#lvgl_github_url = f"https://github.com/lvgl/lvgl/blob/{os.environ['LVGL_GITCOMMIT']}/docs"
#extlinks = {'github_link_base': (github_url + '%s', github_url)}
if "LVGL_GITCOMMIT" not in os.environ:
os.environ['LVGL_GITCOMMIT'] = 'master'
_git_commit_ref = os.getenv('LVGL_GITCOMMIT')
# These keys are used "bare" as template variables in:
# - sphinx_rtd_theme theme template: breadcrumbs.html
# - furo theme template: edit-this-page.html
# - furo theme template: view-this-page.html
html_context = {
'display_github': True,
'github_user': 'lvgl',
'github_repo': 'lvgl',
'github_version': _git_commit_ref,
'conf_py_path': '/docs/src/'
}
html_logo = '_static/images/logo_lvgl.png'
html_favicon = '_static/images/favicon.png'
html_css_files = [
'css/custom.css',
'css/fontawesome.min.css'
]
html_js_files = [
'js/custom.js',
'js/include_html.js'
]
html_static_path = ['_static']
html_last_updated_fmt = '' # Empty string uses default format: '%b %d, %Y'
html_last_updated_use_utc = False # False = use generating system's local date, not GMT.
html_permalinks = True # Default = True, add link anchor for each heading and description environment.
html_sidebars = {
'**': [
'relations.html', # needs 'show_related': True theme option to display
'searchbox.html',
]
}
# html_domain_indices
# html_use_index = True # Default = True
# html_split_index
# html_copy_source = True # Default = True; if True, reStructuredText sources are included in HTML build as _sources/docname.
# html_show_sourcelink = True # Default = True
# html_sourcelink_suffix = '.txt' # Default = '.txt'
# html_use_opensearch = ''
# html_file_suffix = '.html' # Default = '.html'
# html_link_suffix = html_file_suffix
html_show_copyright = True # Default = True; shows copyright notice in footer.
# html_show_search_summary = True # Default = True
# html_show_sphinx = True # Default = True; adds "Created using Sphinx" to footer.
# html_output_encoding = 'utf-8' # Default = 'utf-8'
# html_compact_lists = True # Default = True
# html_secnumber_suffix = '. ' # Default = '. '; set to ' ' to suppress final dot on section numbers.
# html_search_language = language # Default = language; language to be used for generating the HTML full-text search index.
# html_search_options
# Link images that have been resized with a scale option (scale, width, or height)
# to their original full-resolution image. This will not overwrite any link given
# by the target option on the the image directive, if present.
#
# Tip
#
# To disable this feature on a per-image basis, add the no-scaled-link class to the image directive:
#
# .. image:: sphinx.png
# :scale: 50%
# :class: no-scaled-link
# html_scaled_image_link
# html_math_renderer = 'mathjax' # Default = 'mathjax'
# Change image search sequence: image files are searched in the order in which they appear here.
# See https://www.sphinx-doc.org/en/master/usage/builders/index.html#sphinx.builders.html.StandaloneHTMLBuilder.supported_image_types
StandaloneHTMLBuilder.supported_image_types = [
'image/svg+xml',
'image/gif', #prefer gif over png
'image/png',
'image/jpeg'
]
# -------------------------------------------------------------------------
# Options for HTMLHelp Builder
# -------------------------------------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = 'LVGLdoc'
# -------------------------------------------------------------------------
# Options for LaTeX Builder
# -------------------------------------------------------------------------
latex_engine = 'xelatex'
latex_use_xindy = False
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
'inputenc': '',
'utf8extra': '',
'classoptions': ',openany,oneside',
'babel': '\\usepackage{babel}',
'passoptionstopackages': r'''
\PassOptionsToPackage{bookmarksdepth=5}{hyperref}% depth of pdf bookmarks
''',
'preamble': r'''
\usepackage{fontspec}
\setmonofont{DejaVu Sans Mono}
\usepackage{silence}
\WarningsOff*
''',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'LVGL.tex', 'LVGL v' + version,
author, 'manual'),
]
# -------------------------------------------------------------------------
# Options for Manual Page Builder
# -------------------------------------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'lvgl', 'LVGL v' + version,
[author], 3)
]
# -------------------------------------------------------------------------
# Options for Texinfo Builder
# -------------------------------------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'LVGL', 'LVGL v' + version,
author, 'Contributors of LVGL', 'One line description of project.',
'Miscellaneous'),
]
# *************************************************************************
# Domain Options
# *************************************************************************
# -------------------------------------------------------------------------
# Options for the C Domain
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------
# Options for the CPP Domain
# -------------------------------------------------------------------------
# *************************************************************************
# Configuration for Sphinx Extensions
# *************************************************************************
# -------------------------------------------------------------------------
# Options for sphinx_rtd_dark_mode extension
# -------------------------------------------------------------------------
default_dark_mode = False
# -------------------------------------------------------------------------
# Options for lv_example extension
# -------------------------------------------------------------------------
repo_commit_hash = _git_commit_ref
# -------------------------------------------------------------------------
# Options for sphinx.ext.todo extension
# -------------------------------------------------------------------------
# If true, `todo` and `todoList` directives produce output, else they produce nothing.
# See https://www.sphinx-doc.org/en/master/usage/extensions/todo.html
todo_include_todos = True
# -------------------------------------------------------------------------
# Options for sphinx_sitemap extension
# -------------------------------------------------------------------------
# See https://sphinx-sitemap.readthedocs.io/en/latest/index.html
sitemap_url_scheme = "{link}"
# -------------------------------------------------------------------------
# Options for breathe extension
# -------------------------------------------------------------------------
# See https://breathe.readthedocs.io/en/latest/
breathe_projects = {
"lvgl": "xml/",
}
breathe_default_project = "lvgl"
# breathe_debug_trace_directives = True
# -------------------------------------------------------------------------
# Options for sphinx_reredirects
# -------------------------------------------------------------------------
# The below generates .HTML page redirects for pages that have been moved.
# Browsers are redirected via `<meta http-equiv="refresh" content="0; url=new_url">`.
redirects = {
"CONTRIBUTING": "contributing/index.html" ,
"get-started/index": "../intro/getting_started.html" ,
"get-started/os/nuttx": "../../details/integration/os/nuttx.html" ,
"get-started/platforms/arduino": "../../details/integration/framework/arduino.html" ,
"get-started/platforms/espressif": "../../details/integration/chip/espressif.html" ,
"get-started/platforms/pc-simulator": "../../details/integration/ide/pc-simulator.html" ,
"get-started/quick-overview": "../intro/getting_started.html" ,
"integration/bindings/api_json": "../../details/integration/bindings/api_json.html" ,
"integration/bindings/cpp": "../../details/integration/bindings/cpp.html" ,
"integration/bindings/index": "../../details/integration/bindings/index.html" ,
"integration/bindings/javascript": "../../details/integration/bindings/javascript.html" ,
"integration/bindings/micropython": "../../details/integration/bindings/micropython.html" ,
"integration/bindings/pikascript": "../../details/integration/bindings/pikascript.html" ,
"integration/building/cmake": "../../details/integration/building/cmake.html" ,
"integration/building/index": "../../details/integration/building/index.html" ,
"integration/building/make": "../../details/integration/building/make.html" ,
"integration/chip/arm": "../../details/integration/chip/arm.html" ,
"integration/chip/espressif": "../../details/integration/chip/espressif.html" ,
"integration/chip/index": "../../details/integration/chip/index.html" ,
"integration/chip/nxp": "../../details/integration/chip/nxp.html" ,
"integration/chip/renesas": "../../details/integration/chip/renesas/index.html" ,
"integration/chip/stm32": "../../details/integration/chip/stm32.html" ,
"integration/driver/X11": "../../details/integration/driver/X11.html" ,
"integration/driver/display/fbdev": "../../../details/integration/driver/display/fbdev.html" ,
"integration/driver/display/gen_mipi": "../../../details/integration/driver/display/gen_mipi.html" ,
"integration/driver/display/ili9341": "../../../details/integration/driver/display/ili9341.html" ,
"integration/driver/display/index": "../../../details/integration/driver/display/index.html" ,
"integration/driver/display/lcd_stm32_guide": "../../../details/integration/driver/display/lcd_stm32_guide.html",
"integration/driver/display/renesas_glcdc": "../../../details/integration/driver/display/renesas_glcdc.html" ,
"integration/driver/display/st7735": "../../../details/integration/driver/display/st7735.html" ,
"integration/driver/display/st7789": "../../../details/integration/driver/display/st7789.html" ,
"integration/driver/display/st7796": "../../../details/integration/driver/display/st7796.html" ,
"integration/driver/display/st_ltdc": "../../../details/integration/driver/display/st_ltdc.html" ,
"integration/driver/index": "../../details/integration/driver/index.html" ,
"integration/driver/libinput": "../../details/integration/driver/libinput.html" ,
"integration/driver/opengles": "../../details/integration/driver/opengles.html" ,
"integration/driver/touchpad/evdev": "../../../details/integration/driver/touchpad/evdev.html" ,
"integration/driver/touchpad/ft6x36": "../../../details/integration/driver/touchpad/ft6x36.html" ,
"integration/driver/touchpad/index": "../../../details/integration/driver/touchpad/index.html" ,
"integration/driver/wayland": "../../details/integration/driver/wayland.html" ,
"integration/driver/windows": "../../details/integration/driver/windows.html" ,
"integration/framework/arduino": "../../details/integration/framework/arduino.html" ,
"integration/framework/index": "../../details/integration/framework/index.html" ,
"integration/framework/platformio": "../../details/integration/framework/platformio.html" ,
"integration/framework/tasmota-berry": "../../details/integration/framework/tasmota-berry.html" ,
"integration/ide/index": "../../details/integration/ide/index.html" ,
"integration/ide/mdk": "../../details/integration/ide/mdk.html" ,
"integration/ide/pc-simulator": "../../details/integration/ide/pc-simulator.html" ,
"integration/index": "../details/integration/index.html" ,
"integration/os/freertos": "../../details/integration/os/freertos.html" ,
"integration/os/index": "../../details/integration/os/index.html" ,
"integration/os/mqx": "../../details/integration/os/mqx.html" ,
"integration/os/nuttx": "../../details/integration/os/nuttx.html" ,
"integration/os/px5": "../../details/integration/os/px5.html" ,
"integration/os/qnx": "../../details/integration/os/qnx.html" ,
"integration/os/rt-thread": "../../details/integration/os/rt-thread.html" ,
"integration/os/yocto/core_components": "../../../details/integration/os/yocto/core_components.html" ,
"integration/os/yocto/index": "../../../details/integration/os/yocto/index.html" ,
"integration/os/yocto/lvgl_recipe": "../../../details/integration/os/yocto/lvgl_recipe.html" ,
"integration/os/yocto/terms_and_variables": "../../../details/integration/os/yocto/terms_and_variables.html" ,
"integration/os/zephyr": "../../details/integration/os/zephyr.html" ,
"layouts/flex": "../details/common-widget-features/layouts/flex.html" ,
"layouts/grid": "../details/common-widget-features/layouts/grid.html" ,
"layouts/index": "../details/common-widget-features/layouts/index.html" ,
"libs/arduino_esp_littlefs": "../details/libs/arduino_esp_littlefs.html" ,
"libs/arduino_sd": "../details/libs/arduino_sd.html" ,
"libs/barcode": "../details/libs/barcode.html" ,
"libs/bmp": "../details/libs/bmp.html" ,
"libs/ffmpeg": "../details/libs/ffmpeg.html" ,
"libs/freetype": "../details/libs/freetype.html" ,
"libs/fs": "../details/libs/fs.html" ,
"libs/gif": "../details/libs/gif.html" ,
"libs/index": "../details/libs/index.html" ,
"libs/lfs": "../details/libs/lfs.html" ,
"libs/libjpeg_turbo": "../details/libs/libjpeg_turbo.html" ,
"libs/libpng": "../details/libs/libpng.html" ,
"libs/lodepng": "../details/libs/lodepng.html" ,
"libs/qrcode": "../details/libs/qrcode.html" ,
"libs/rle": "../details/libs/rle.html" ,
"libs/rlottie": "../details/libs/rlottie.html" ,
"libs/svg": "../details/libs/svg.html" ,
"libs/tiny_ttf": "../details/libs/tiny_ttf.html" ,
"libs/tjpgd": "../details/libs/tjpgd.html" ,
"others/file_explorer": "../details/auxiliary-modules/file_explorer.html" ,
"others/font_manager": "../details/auxiliary-modules/font_manager.html" ,
"others/fragment": "../details/auxiliary-modules/fragment.html" ,
"others/gridnav": "../details/auxiliary-modules/gridnav.html" ,
"others/ime_pinyin": "../details/auxiliary-modules/ime_pinyin.html" ,
"others/imgfont": "../details/auxiliary-modules/imgfont.html" ,
"others/index": "../details/auxiliary-modules/index.html" ,
"others/monkey": "../details/auxiliary-modules/monkey.html" ,
"others/obj_id": "../details/auxiliary-modules/obj_id.html" ,
"others/obj_property": "../details/auxiliary-modules/obj_property.html" ,
"others/observer": "../details/auxiliary-modules/observer.html" ,
"others/snapshot": "../details/auxiliary-modules/snapshot.html" ,
"overview/animations": "../details/main-modules/animation.html" ,
"overview/color": "../details/main-modules/color.html" ,
"overview/coord": "../details/common-widget-features/coord.html" ,
"overview/debugging/gdb_plugin": "../../details/debugging/gdb_plugin.html" ,
"overview/debugging/index": "../../details/debugging/index.html" ,
"overview/debugging/log": "../../details/debugging/log.html" ,
"overview/debugging/profiler": "../../details/debugging/profiler.html" ,
"overview/debugging/vg_lite_tvg": "../../details/debugging/vg_lite_tvg.html" ,
"overview/display": "../details/main-modules/display/index.html" ,
"overview/event": "../details/common-widget-features/event.html" ,
"overview/font": "../details/main-modules/font.html" ,
"overview/fs": "../details/main-modules/fs.html" ,
"overview/image": "../details/main-modules/image.html" ,
"overview/indev": "../details/main-modules/indev.html" ,
"overview/index": "../details/main-modules/index.html" ,
"overview/layer": "../details/common-widget-features/layer.html" ,
"overview/new_widget": "../details/widgets/new_widget.html" ,
"overview/obj": "../details/common-widget-features/obj.html" ,
"overview/renderers/arm2d": "../../details/integration/renderers/arm2d.html" ,
"overview/renderers/index": "../../details/integration/renderers/index.html" ,
"overview/renderers/nema_gfx": "../../details/integration/renderers/nema_gfx.html" ,
"overview/renderers/pxp": "../../details/integration/renderers/nxp_pxp.html" ,
"overview/renderers/sdl": "../../details/integration/renderers/sdl.html" ,
"overview/renderers/stm32_dma2d": "../../details/integration/renderers/stm32_dma2d.html" ,
"overview/renderers/sw": "../../details/integration/renderers/sw.html" ,
"overview/renderers/vg_lite": "../../details/integration/renderers/vg_lite.html" ,
"overview/renderers/vglite": "../../details/integration/renderers/nxp_vglite_gpu.html" ,
"overview/scroll": "../details/common-widget-features/scroll.html" ,
"overview/style": "../details/common-widget-features/styles/styles.html" ,
"overview/style-props": "../details/common-widget-features/styles/style-properties.html" ,
"overview/timer": "../details/main-modules/timer.html" ,
"porting/display": "../details/main-modules/display/index.html" ,
"porting/draw": "../details/main-modules/draw.html" ,
"porting/indev": "../details/main-modules/indev.html" ,
"porting/index": "../details/integration/adding-lvgl-to-your-project/index.html" ,
"porting/os": "../details/integration/adding-lvgl-to-your-project/threading.html",
"porting/project": "../details/integration/adding-lvgl-to-your-project/connecting_lvgl.html",
"porting/sleep": "../details/integration/adding-lvgl-to-your-project/threading.html",
"porting/tick": "../details/integration/adding-lvgl-to-your-project/connecting_lvgl.html",
"porting/timer_handler": "../details/integration/adding-lvgl-to-your-project/timer_handler.html",
"widgets/animimg": "../details/widgets/animimg.html" ,
"widgets/arc": "../details/widgets/arc.html" ,
"widgets/bar": "../details/widgets/bar.html" ,
"widgets/button": "../details/widgets/button.html" ,
"widgets/buttonmatrix": "../details/widgets/buttonmatrix.html" ,
"widgets/calendar": "../details/widgets/calendar.html" ,
"widgets/canvas": "../details/widgets/canvas.html" ,
"widgets/chart": "../details/widgets/chart.html" ,
"widgets/checkbox": "../details/widgets/checkbox.html" ,
"widgets/dropdown": "../details/widgets/dropdown.html" ,
"widgets/image": "../details/widgets/image.html" ,
"widgets/imagebutton": "../details/widgets/imagebutton.html" ,
"widgets/index": "../details/widgets/index.html" ,
"widgets/keyboard": "../details/widgets/keyboard.html" ,
"widgets/label": "../details/widgets/label.html" ,
"widgets/led": "../details/widgets/led.html" ,
"widgets/line": "../details/widgets/line.html" ,
"widgets/list": "../details/widgets/list.html" ,
"widgets/lottie": "../details/widgets/lottie.html" ,
"widgets/menu": "../details/widgets/menu.html" ,
"widgets/msgbox": "../details/widgets/msgbox.html" ,
"widgets/obj": "../details/widgets/base_widget.html" ,
"widgets/roller": "../details/widgets/roller.html" ,
"widgets/scale": "../details/widgets/scale.html" ,
"widgets/slider": "../details/widgets/slider.html" ,
"widgets/span": "../details/widgets/spangroup.html" ,
"widgets/spinbox": "../details/widgets/spinbox.html" ,
"widgets/spinner": "../details/widgets/spinner.html" ,
"widgets/switch": "../details/widgets/switch.html" ,
"widgets/table": "../details/widgets/table.html" ,
"widgets/tabview": "../details/widgets/tabview.html" ,
"widgets/textarea": "../details/widgets/textarea.html" ,
"widgets/tileview": "../details/widgets/tileview.html" ,
"widgets/win": "../details/widgets/win.html" ,
"details/widgets/span": "../../details/widgets/spangroup.html" ,
}
# Example configuration for intersphinx: refer to the Python standard library.
def setup(app):
pass
# app.add_config_value('recommonmark_config', {
# 'enable_eval_rst': True,
# 'enable_auto_toc_tree': 'True',
# }, True)
# app.add_transform(AutoStructify)
# app.add_css_file('css/custom.css')
# app.add_css_file('css/fontawesome.min.css')

View File

@@ -0,0 +1,401 @@
.. _coding-style:
============
Coding Style
============
File Template
*************
Use `misc/lv_templ.c <https://github.com/lvgl/lvgl/blob/master/src/misc/lv_templ.c>`__
and `misc/lv_templ.h <https://github.com/lvgl/lvgl/blob/master/src/misc/lv_templ.h>`__
Naming Conventions
******************
- Words are separated by '\_'
- In variable and function names use only lower case letters
(e.g. *height_tmp*)
- In enums and defines use only upper case letters
(e.g. *MAX_LINE_NUM*)
- Global names (API):
- start with *lv*
- followed by module name: *button*, *label*, *style* etc.
- followed by the action (for functions): *set*, *get*, etc.
- closed with the subject: *name*, *size*, *state* etc.
- Typedefs
- prefer ``typedef struct`` and ``typedef enum`` instead of
``struct name`` and ``enum name``
- always end ``typedef struct`` and ``typedef enum`` type names with
``_t``
- Abbreviations:
- The following abbreviations are used and allowed:
- ``dsc`` descriptor
- ``param`` parameter
- ``indev`` input device
- ``anim`` animation
- ``buf`` buffer
- ``str`` string
- ``min/max`` minimum/maximum
- ``alloc`` allocate
- ``ctrl`` control
- ``pos`` position
- Avoid adding new abbreviations
Coding Guide
************
- Editor:
- Set editor to use 4 spaces for tab indentations (instead of tab characters).
- Exception: the **Kconfig** file and any make files require leading tab characters
on child items.
- Functions:
- Write functions that use the single-responsibility principle.
- Make functions ``static`` when not part of that object's public API (where possible).
- Variables:
- One line, one declaration (BAD: char x, y;).
- Use ``<stdint.h>`` (*uint8_t*, *int32_t* etc).
- Declare variables where needed (not all at function start).
- Use the smallest required scope.
- Variables in a file (outside functions) are always *static*.
- Do not use global variables (use functions to set/get static variables).
Comments
********
Before every function prototype in ``.h`` files, include a Doxygen-formatted comment
like this:
.. code-block:: c
/**
* Brief description. Add a blank line + additional paragraphs when more detail is needed.
* @param parent brief description of argument. Additional detail can appear
* on subsequent lines. List of accepted values:
* - value one
* - value two
* - value three
* @return brief description of return value.
*/
type_name_t * lv_function_name(lv_obj_t * parent);
The normal comment prefix ``/**`` causes the comment to document the code member
*after* the comment. When documenting a code member that is *before* the
comment, such as a struct member, use ``/**<`` like this:
.. code-block:: c
/**
* Brief description of struct
*
* When more detail is needed, add a blank line then the detail.
*/
typedef struct {
char *text; /**< Brief description of this member */
uint16_t length; /**< Brief description of this member */
} lv_example_type_t;
- When commenting code, use block comments like this ``/* Description */``,
not end-of-line comments like this ``// Description``.
- Include a space after the ``/*`` or ``/**<`` and before the ``*/`` for improved readability.
- Write readable code to avoid descriptive comments like: ``x++; /* Add 1 to x */``.
- The code should show clearly what you are doing.
- You should write **why** you did it: ``x++; /* Point to closing '\0' of string */``
- Short "code summaries" of a few lines are accepted: ``/* Calculate new coordinates */``
- In comments use back-quotes (\`...\`) when referring to a code element, such as a variable, type,
or struct name: ``/* Update value of `x_act` */``
- When adding or modifying comments, priorities are (in order of importance):
1. clarity (the ease with which other programmers can understand your intention),
2. readability (the ease with which other programmers can read your comments),
3. brevity (the quality of using few words when speaking or writing).
- Blank lines within comments are desirable when they improve clarity and readability.
- Remember, when you are writing source code, you are not just teaching the computer
what to do, but also teaching other programmers how it works, not only users of the
API, but also future maintainers of your source code. Comments add information
about what you were thinking when the code was written, and **why** you did things
that way---information that cannot be conveyed by the source code alone.
Doxygen Comment Specifics
-------------------------
Doxygen is the first program in a chain that generates the online LVGL API
documentation from the files in the LVGL repository. Doxygen detects files it should
pay attention to by them having a ``@file`` command inside a Doxygen comment. Doxygen
comments begin with a leading ``/**``. It ignores comments that do not have exactly
two ``*``.
The following is an illustration of an API function prototype with documentation
illustrating most of the Doxygen commands used in LVGL.
.. code-block:: c
/**
* Set alignment of Widgets placed in containers with LV_STYLE_FLEX_FLOW style.
*
* The values for the `..._place` arguments come from the `lv_flex_align_t`
* enumeration and have the same meanings as they do for flex containers in CSS.
* @param obj pointer to flex container. It must have
* `LV_STYLE_FLEX_FLOW` style or nothing will happen.
* @param main_place where to place items on main axis (in their track).
* (Any value of `lv_flex_align_t`.)
* @param cross_place where to place item in track on cross axis.
* - `LV_FLEX_ALIGN_START`
* - `LV_FLEX_ALIGN_END`
* - `LV_FLEX_ALIGN_CENTER`
* @param track_cross_place where to place tracks in cross direction.
* (Any value of `lv_flex_align_t`.)
* Example for a title bar layout:
* @code{.c}
* lv_obj_set_flex_align(title_bar, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
* @endcode
* @see
* - https://css-tricks.com/snippets/css/a-guide-to-flexbox/
* - see `lv_obj_set_flex_grow()` for additional information.
*/
void lv_obj_set_flex_align(lv_obj_t * widget, lv_flex_align_t main_place, lv_flex_align_t cross_place,
lv_flex_align_t track_cross_place);
- Always start Doxygen comment with a brief description of the code element it documents.
- When more detail is needed, add a blank line below the brief description and add
additional information that may be needed by LVGL API users, including preconditions
for calling the function. Doxygen needs the blank line to separate "brief" from
"detail" portions of the description.
- Describe function parameters with the ``@param`` command. When a function writes
to the address contained by a pointer parameter, if not already obvious (e.g. when
the parameter name contains the word "out"), include the direction in the command
for clarity:
``@param[out] param_name description``.
- Describe return values with the ``@return`` command.
- Add at least 2 spaces after Doxygen commands for improved readability.
- Use back-quotes (\`...\`) around code elements (variables, type names, function names). For type
names and function names, Doxygen generates a hyperlink to that code member's
documentation (when it exists) with or without the single back-quotes.
- Append empty "()" to function names. Doxygen will not generate a hyperlink to the
function's documentation without this.
- Use proper grammar for clarity. Descriptions of parameters do not need periods
after them unless they are full sentences.
- Align edges of text around lists of parameters for ease of reading.
- Lists (e.g. of accepted parameter values) can be created by using the '-' character.
If the list needs to be numbered, numbers can also be used.
- Place example code in a code block by surrounding it with ``@code{.c}`` and ``@endcode`` commands.
Doxygen doesn't need the ``{.c}`` part, but the downstream software does.
- Refer reader to additional information using the ``@see`` command. Doxygen adds a
"See also" paragraph. The text following the ``@see`` command will be indented.
- If you create a new pair of ``.c`` and ``.h`` files (e.g. for a new driver), ensure
a Doxygen comment like this is at the top of each new file. Doxygen will not parse
the file without the ``@file`` command being present.
.. code-block:: c
/**
* @file filename.c
*
*/
Supported Doxygen Commands
--------------------------
- ``@file``
tells Doxygen to parse this file and also supplies documentation about
the file itself when applicable (everything following it in the same comment).
- ``@param name description``
documents ``name`` as a function parameter, and ``description`` is the text that
follows it until Doxygen encounters a blank line or another Doxygen command.
- ``@return description``
documents the return value until Doxygen encounters a blank line or another Doxygen command.
- ``@code{.c}/@endcode``
surrounds code that should be placed in a code block. While Doxygen knows to use C
color-coding of code blocks in a .C file, the downstream part of the documentation
generation sequence does not, so the ``{.c}`` appendage to the ``@code`` command
is necessary.
- ``@note text``
starts a paragraph where a note can be entered. The note ends with a blank line,
the end of the comment, or another Doxygen command that starts a new section.
If the note contains more than one paragraph, additional paragraphs can be added
by using additional ``@note`` commands. At this writing, ``@par`` commands do not
add additional paragraphs to notes as indicated in the Doxygen documentation.
- ``@see text``
generates a "See also" pagraph in a highlighted section, helpful when additional
information about a topic can be found elsewhere.
API Conventions
***************
To support the auto-generation of bindings, the LVGL C API must
follow some coding conventions:
- Use ``enum``\ s instead of macros. If inevitable to use ``define``\ s
export them with :cpp:expr:`LV_EXPORT_CONST_INT(defined_value)` right after the ``define``.
- In function arguments use ``type name[]`` declaration for array parameters instead of :cpp:expr:`type * name`
- Use typed pointers instead of :cpp:expr:`void *` pointers
- Widget constructor must follow the ``lv_<widget_name>_create(lv_obj_t * parent)`` pattern.
- Widget members function must start with ``lv_<widget_name>`` and should receive :cpp:expr:`lv_obj_t *` as first
argument which is a pointer to Widget object itself.
- ``struct`` APIs should follow the widgets' conventions. That is to receive a pointer to the ``struct`` as the
first argument, and the prefix of the ``struct`` name should be used as the prefix of the
function name as well (e.g. :cpp:expr:`lv_display_set_default(lv_display_t * disp)`)
- Functions and ``struct``\ s which are not part of the public API must begin with underscore in order to mark them as "private".
- Argument must be named in H files as well.
- Do not ``malloc`` into a static or global variables. Instead declare the variable in ``lv_global_t``
structure in ``lv_global.h`` and mark the variable with :cpp:expr:`(LV_GLOBAL_DEFAULT()->variable)` when it's used.
- To register and use callbacks one of the following needs to be followed.
- Pass a pointer to a ``struct`` as the first argument of both the registration function and the callback. That
``struct`` must contain ``void * user_data`` field.
- The last argument of the registration function must be ``void * user_data`` and the same ``user_data``
needs to be passed as the last argument of the callback.
To learn more refer to the documentation of `MicroPython <integration/bindings/micropython>`__.
Formatting
**********
Here is example to show bracket placement and use of white space:
.. code-block:: c
/**
* Set new text for a label. Memory will be allocated by label to store text.
*
* @param label pointer to label Widget
* @param text '\0' terminated character string.
* NULL to refresh with current text.
*/
void lv_label_set_text(lv_obj_t * label, const char * text)
{ /* Main brackets of functions in new line */
if(label == NULL) return; /* No bracket only if command is inline with if statement */
lv_obj_inv(label);
lv_label_ext_t * ext = lv_obj_get_ext(label);
/* Comment before a section */
if(text == ext->txt || text == NULL) { /* Bracket of statements starts on same line */
lv_label_refr_text(label);
return;
}
...
}
You can use **astyle** to format the code. Run ``code-format.py`` from
the ``scripts`` folder.
Includes
********
Various subsystems of LVGL can be enabled or disabled by setting a macro in
``lv_conf.h`` to 1 or 0 respectively. The code files that contain the logic for
those subsystems are often arranged so that there is an ``#if <ENABLING_MACRO_NAME>``
directive near the top of the file, and its matching ``#endif`` is at the end of the
file. If you add or modify such a subsystem in LVGL, whenever possible, the only
``#include`` that should be above those conditional directives should be just enough
to include the enabling/disabling macro. Specifically:
- in the ``.c`` file: the ``#include`` that includes the header with the closest
correspondence to that ``.c`` file
- in the mated ``.h`` file: ``#include "lv_conf_internal.h"``
which, itself includes ``lv_conf.h``. See examples at
`lv_freetype.c <https://github.com/lvgl/lvgl/blob/master/src/libs/freetype/lv_freetype.c>`__,
`lv_freetype_private.h <https://github.com/lvgl/lvgl/blob/master/src/libs/freetype/lv_freetype_private.h>`__ and
`lv_freetype.h <https://github.com/lvgl/lvgl/blob/master/src/libs/freetype/lv_freetype.h>`__.
pre-commit
**********
`pre-commit <https://pre-commit.com/>`__ is a multi-language package
manager for pre-commit hooks. See the `installation
guide <https://pre-commit.com/#installation>`__ to get pre-commit python
package installed into your development machine.
Once you have ``pre-commit`` installed you will need to `set up the git
hook scripts <https://pre-commit.com/#3-install-the-git-hook-scripts>`__
with:
.. code-block:: console
pre-commit install
now ``pre-commit`` will run automatically on ``git commit``!
Hooks
*****
The ``format-source`` local hook (see ``.pre-commit-config.yaml``) runs
**astyle** on all the staged source and header files (that are not
excluded, see ``exclude`` key of the hook configuration) before entering
the commit message, if any file gets formatted by **astyle** you will
need to add the change to the staging area and run ``git commit`` again.
The ``trailing-whitespace`` hook fixes trailing whitespaces on all of
the files.
Skipping hooks
**************
If you want to skip any particular hook you can do so with:
.. code-block:: console
SKIP=name-of-the-hook git commit
Testing hooks
*************
It is not necessary to do a commit to test the hooks, you can test hooks
by adding the files into the staging area and run:
.. code:: console
pre-commit run name-of-the-hook

View File

@@ -0,0 +1,70 @@
.. _contributing_dco:
=======================================
Developer Certification of Origin (DCO)
=======================================
Overview
********
To ensure all licensing criteria are met for every repository of the
LVGL project, we apply a process called DCO (Developer's Certificate of
Origin).
The text of DCO can be read here: https://developercertificate.org/.
By contributing to any repositories of the LVGL project you agree that
your contribution complies with the DCO.
If your contribution fulfills the requirements of the DCO, no further
action is needed. If you are unsure feel free to ask us in a comment,
e.g. in your submitted :ref:`Pull Request <contributing_pull_requests>`.
Accepted Licenses and Copyright Notices
***************************************
To make the DCO easier to digest, here are some practical guides about
specific cases:
Your own work
-------------
The simplest case is when the contribution is solely your own work. In
this case you can just send a Pull Request without worrying about any
licensing issues.
Using code from an online source
--------------------------------
If the code you would like to add is based on an article, post or
comment on a website (e.g. StackOverflow) the license and/or rules of
that site should be followed.
For example in case of StackOverflow a notice like this can be used:
.. code-block::
/* The original version of this code-snippet was published on StackOverflow.
* Post: http://stackoverflow.com/questions/12345
* Author: http://stackoverflow.com/users/12345/username
* The following parts of the snippet were changed:
* - Check this or that
* - Optimize performance here and there
*/
... code snippet here ...
Using MIT-licensed code
-----------------------
As LVGL is MIT licensed, other MIT licensed code can be integrated
without issues. The MIT license requires a copyright notice be added to
the derived work. Any derivative work based on MIT licensed code must
copy the original work's license file or text.
Use GPL-licensed code
---------------------
The GPL license is not compatible with the MIT license. Therefore, LVGL
cannot accept GPL licensed code.

View File

@@ -0,0 +1,14 @@
.. _contributing:
============
Contributing
============
.. toctree::
:maxdepth: 1
introduction
ways_to_contribute
pull_requests
dco
coding_style

View File

@@ -0,0 +1,18 @@
.. _contributing_intro:
============
Introduction
============
Join LVGL's community and leave your footprint in the library!
There are a lot of ways to contribute to LVGL even if you are new to the
library or even new to programming.
It might be scary to make the first step but you have nothing to be
afraid of. A friendly and helpful community is waiting for you. Get to
know like-minded people and make something great together.
So let's find which contribution option fits you the best and helps you
join the development of LVGL!

View File

@@ -0,0 +1,129 @@
.. _contributing_pull_requests:
=============
Pull Requests
=============
Merging new code into the ``lvgl/lvgl`` and other
repositories happens via *Pull Requests* (PR for short). A PR is a
notification like "Hey, I made some updates to your project. Here are
the changes, you can add them if you want." To do this you need a copy
(called fork) of the original project under your account, make some
changes there, and notify the original repository about your updates.
You can see what it looks like on GitHub for LVGL here:
https://github.com/lvgl/lvgl/pulls.
To add your changes you can edit files online on GitHub and send a new
Pull request from there (recommended for small changes) or add the
updates in your favorite editor/IDE and use ``git`` to publish the changes
(recommended for more complex updates).
From GitHub
***********
1. Navigate to the file you want to edit.
2. Click the Edit button in the top right-hand corner.
3. Add your changes to the file.
4. Add a commit message at the bottom of the page.
5. Click the *Propose changes* button.
From Your Local Workstation
***************************
These instructions describe the main ``lvgl`` repository but it works the
same way any remote Git repository.
1. Fork the `lvgl repository <https://github.com/lvgl/lvgl>`__. To do this click the
"Fork" button in the top right corner. It will "copy" the ``lvgl``
repository to your GitHub account (``https://github.com/<YOUR_NAME>?tab=repositories``)
2. Clone your forked repository.
3. Add your changes. You can create a *feature branch* from the ``master`` branch for the updates: ``git checkout -b <new-feature-branch-name>``
4. Commit and push your changes to your forked ``lvgl`` repository.
5. Create a PR on GitHub from the page of your forked ``lvgl`` repository (``https://github.com/<YOUR_NAME>/lvgl``) by
clicking the *"New pull request"* button. Don't forget to select the branch where you added your changes.
6. Set the base branch where you want to merge your update. In the ``lvgl`` repo both fixes
and new features should be directed to the ``master`` branch.
7. Describe what is in the update. Example code is welcome if applicable.
8. If you need to make more changes, just update your forked ``lvgl`` repo with new commits.
They will automatically appear in the PR.
.. _contributing_commit_message_format:
Commit Message Format
*********************
The commit messages format is inspired by `Angular Commit
Format <https://github.com/angular/angular/blob/main/CONTRIBUTING.md#commit>`__.
The following structure should be used:
.. code-block:: text
<type>(<scope>): <subject>
<--- blank line
<body>
<--- blank line
<footer>
Possible ``<type>``\ s:
- ``fix`` bugfix in LVGL source code
- ``feat`` new feature
- ``arch`` architectural changes
- ``perf`` changes that affect performance
- ``example`` anything related to examples (including fixes and new examples)
- ``docs`` anything related to documentation (including fixes, formatting, and new pages)
- ``test`` anything related to tests (new and updated tests or CI actions)
- ``chore`` any minor formatting or style changes that would make the changelog noisy
``<scope>`` is the name of the module, file, or subsystem affected by the
commit. It's usually one word and can be chosen freely. For example
``img``, ``layout``, ``txt``, ``anim``. The scope can be omitted.
``<subject>`` contains a short description of the change following these guidelines:
- use the imperative mood: e.g. present tense "change", not "changed" nor "changes";
- don't capitalize the first letter;
- no period (``.``) at the end;
- max 90 characters.
``<body>`` optional and can be used to describe the details of this
change.
``<footer>`` shall contain
- the words "BREAKING CHANGE" if the changes break the API
- reference to the GitHub issue or Pull Request if applicable.
(See `Linking a pull rquest to an issue <https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/using-keywords-in-issues-and-pull-requests#linking-a-pull-request-to-an-issue>`__
for details.)
Some examples:
.. code-block:: text
fix(image): update size when a new source is set
.. code-block:: text
fix(bar): fix memory leak
The animations weren't deleted in the destructor.
Fixes: #1234
.. code-block:: text
feat: add span widget
The span widget allows mixing different font sizes, colors and styles.
It's similar to HTML <span>
.. code-block:: text
docs(porting): fix typo

View File

@@ -0,0 +1,23 @@
.. _contributing_ways:
==================
Ways to Contribute
==================
- **Spread the Word**: Share your LVGL experience with friends or on social media to boost its visibility.
- **Star LVGL** Give us a star on `GitHub <https://github.com/lvgl/lvgl>`__! It helps a lot to make LVGL more appealing for newcomers.
- **Report a Bug**: Open a `GitHub Issue <https://github.com/lvgl/lvgl/issues>`__ if something is not working.
- **Join Our** `Forum <https://forum.lvgl.io/>`__ : Meet fellow developers and discuss questions.
- **Tell Us Your Ideas**: If you feel something is missing from LVGL, we would love to hear about it in a `GitHub Issue <https://github.com/lvgl/lvgl/issues>`__
- **Develop Features**: Help to design or develop a feature. See below.
Mid- and large-scale issues are discussed in `Feature Planning <https://github.com/lvgl/lvgl/issues/new?assignees=&labels=&projects=&template=feat-planning.yml>`__ issues.
An issue can be developed when all the questions in the issue template are answered and there are no objection from any core member.
We are using GitHub labels to show the state and attributes of the issues and Pull requests.
If you are looking for contribution opportunities you can `Filter for these labels <https://github.com/lvgl/lvgl/labels>`__ :
- ``Simple``: Good choice to get started with an LVGL contribution
- ``PR needed``: We investigated the issue but it still needs to be implemented
- ``Review needed``: A Pull request is opened and it needs review/testing

View File

@@ -0,0 +1,226 @@
.. _file_explorer:
=============
File Explorer
=============
``lv_file_explorer`` provides a UI enabling the end user to browse the contents of a
file system. Its main area is called the "Browsing Area" and provides the list of
files contained in the currently-viewed directory.
When enabled, there is also a "Quick-Access" panel on the left, which provides a
convenient way to reach parts of the file system that are frequently accessed.
Available "Quick-Access" destinations are:
- File System,
- HOME,
- Video,
- Pictures,
- Music, and
- Documents.
You specify what paths these lead to during ``lv_file_explorer``\ 's initialization.
``lv_file_explorer`` only provides the file browsing and events caused by user
activity (e.g. clicking a file), but does not provide the actual file operations.
Client code must hook various events and decide what to do when they are emitted
(e.g. a click or double-click on a file). The actions taken might to open the file,
display it, send it to some other part of the application, etc..
``lv_file_explorer`` passes the full path and name of file that was clicked to the
event callback functions. What happens next is up to the application designer.
``lv_file_explorer`` uses the :ref:`lv_table` Widget for the "Browsing Area", and the
:ref:`lv_list` Widget for the "Quick-Access" panel when it is enabled. Thus,
:c:macro:`LV_USE_TABLE` macro must be set to a non-zero value in ``lv_conf.h`` in
order to use ``lv_file_explorer``, and and :c:macro:`LV_USE_LIST` must be set to a
non-zero value to use the "Quick-Access" panel.
.. note::
In order to use File Explorer, :ref:`file_system` has to be set up and
know about all the drive letters you use when passing paths to File System
(described below).
Prerequisites
*************
If you haven't already done so, you will need to learn about the LVGL :ref:`File
System abstraction <file_system>`, since it must be set up and be functional
for File Explorer to work.
.. _file_explorer_usage:
Usage
*****
Set :c:macro:`LV_USE_FILE_EXPLORER` to a non-zero value in ``lv_conf.h``.
First use :cpp:expr:`lv_file_explorer_create(lv_screen_active())` to create a File
Explorer. The default size is the screen size. After that, you can
customize the style like any Widget.
The size of the ``current_path`` buffer is set by :c:macro:`LV_FILE_EXPLORER_PATH_MAX_LEN`
in ``lv_conf.h``.
The object hierarchy of a freshly-created File Explorer looks like this:
- ``File Explorer``: occupies full area of parent Widget, typically a Screen (Flex-Flow COLUMN)
- ``Container``: occupies full area of File Explorer (Flex grow 1)
- ``Quick-Access Panel``:
- ``Device List``: grows to accommodate children
- ``File System``: button
- ``Places List``: grows to accommodate children
- ``HOME``: button
- ``Video``: button
- ``Pictures``: button
- ``Music``: button
- ``Documents``: button
- ``Browser Panel``:
- ``Header``: 14% of ``Browser Panel`` height
- ``Current Path``: label
- ``File Table``: with 1 column, 86% of ``Browser Panel`` height
- Fields:
- ``home_dir`` = NULL
- ``video_dir`` = NULL
- ``pictures_dir`` = NULL
- ``music_dir`` = NULL
- ``docs_dir`` = NULL
- ``fs_dir`` = NULL
- ``current_path`` = [empty buffer]
- ``sel_fn`` (selected file)
- ``sort`` (default :cpp:enumerator:`LV_EXPLORER_SORT_NONE`)
Accessing the Parts
-------------------
This list of functions provides access to the parts shown in diagram above:
- :cpp:expr:`lv_file_explorer_get_selected_file_name(explorer)` (pointer
to NUL-terminated string containing file-path user selected; typically used inside
an :cpp:enumerator:`LV_EVENT_CLICKED` event)
- :cpp:expr:`lv_file_explorer_get_current_path(explorer)` (pointer to ``current_path`` ``char`` buffer)
- :cpp:expr:`lv_file_explorer_get_file_table(explorer)` (pointer to ``File Table`` :ref:`lv_table` Widget)
- :cpp:expr:`lv_file_explorer_get_header(explorer)` (pointer to ``Header`` :ref:`base_widget` Widget)
- :cpp:expr:`lv_file_explorer_get_path_label(explorer)` (pointer to ``Current Path Label`` :ref:`lv_label` Widget)
- :cpp:expr:`lv_file_explorer_get_quick_access_area(explorer)` (pointer to ``Quick-Access Panel`` :ref:`base_widget`)
- :cpp:expr:`lv_file_explorer_get_places_list(explorer)` (pointer to ``Places List`` :ref:`lv_list` Widget)
- :cpp:expr:`lv_file_explorer_get_device_list(explorer)` (pointer to ``Device List`` :ref:`lv_list` Widget)
Quick-Access Panel
------------------
The ``Quick-Access Panel`` behaves like a typical navigation panel and appears on the
left, while the ``Browser Panel`` appears on the right
This panel is optional. If you set :c:macro:`LV_FILE_EXPLORER_QUICK_ACCESS` to ``0``
in ``lv_conf.h``, the ``Quick-Access Panel`` will not be created. This saves only a
little bit of memory.
Soon after the File Explorer is created, you typically use
:cpp:expr:`lv_file_explorer_set_quick_access_path(explorer, LV_EXPLORER_XXX_DIR, "path")`
to set the path that will be navigated to when the buttons in the ``Quick-Access Panel``
are clicked, which is currently a fixed list. The corresponding values you will need
to pass as the 2nd argument are the following:
- :cpp:enumerator:`LV_EXPLORER_HOME_DIR`
- :cpp:enumerator:`LV_EXPLORER_MUSIC_DIR`
- :cpp:enumerator:`LV_EXPLORER_PICTURES_DIR`
- :cpp:enumerator:`LV_EXPLORER_VIDEO_DIR`
- :cpp:enumerator:`LV_EXPLORER_DOCS_DIR`
- :cpp:enumerator:`LV_EXPLORER_FS_DIR`
.. _file_explorer_sort:
Sort
----
You can use
:cpp:expr:`lv_file_explorer_set_sort(explorer, LV_EXPLORER_SORT_XX)` to set
the sorting method.
These are the possible sorting methods:
- :cpp:enumerator:`LV_EXPLORER_SORT_NONE` (default)
- :cpp:enumerator:`LV_EXPLORER_SORT_KIND`
:cpp:expr:`lv_file_explorer_get_sort(explorer)` returns the current sorting method.
.. _file_explorer_events:
Events
******
- :cpp:enumerator:`LV_EVENT_READY` Sent when a directory is opened, which can happen:
- when the File Explorer is initially opened,
- after a user clicks on a ``Quick-Access Panel`` navigation button, and
- after the user clicks on a directory displayed in the ``Browser Panel``.
You can use it to, for example, customize the file sort.
- :cpp:enumerator:`LV_EVENT_VALUE_CHANGED` Sent once when any item (file) in the
``Brwoser Panel``\ 's file list is clicked.
- :cpp:enumerator:`LV_EVENT_CLICKED` Sent twice when an item in the ``Browser Panel``
is clicked: once as a result of the input-device :cpp:enumerator:`LV_EVENT_RELEASED`
event and a second as a result of the input device :cpp:enumerator:`LV_EVENT_CLICKED`
event. This applies to files, directories, and the "< Back" item in the ``Browser Panel``.
In these events you can use :cpp:func:`lv_file_explorer_get_current_path` to get the
current path and :cpp:func:`lv_file_explorer_get_selected_file_name` to get the name
of the currently selected file in the event processing function. For example:
.. code-block:: c
static void file_explorer_event_handler(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
if(code == LV_EVENT_VALUE_CHANGED) {
char * cur_path = lv_file_explorer_get_current_path(widget);
char * sel_fn = lv_file_explorer_get_selected_file_name(widget);
LV_LOG_USER("%s%s", cur_path, sel_fn);
}
}
You can also save the obtained **path** and **file** name into an array
through functions such as :cpp:func:`strcpy` and :cpp:func:`strcat` for later use.
.. _file_explorer_example:
Example
*******
.. include:: ../../examples/others/file_explorer/index.rst
.. _file_explorer_api:
API
***

View File

@@ -0,0 +1,137 @@
.. _font_manager:
============
Font Manager
============
The font manager is a secondary encapsulation of the LVGL font engine,
which facilitates the use and management of fonts for applications.
The font management functions currently provided include:
- Font resource reference counting (reduces repeated creation of font resources).
- Font resource concatenation (font fallback).
- Font resource recycling mechanism (buffers recently deleted font resources to
reduce the time overhead of repeated creation).
.. _font_manager_usage:
Usage
*****
Enable FreeType and Font Manager in ``lv_conf.h`` by setting the
:c:macro:`LV_USE_FONT_MANAGER` macros to non-zero
values, and configure :c:macro:`LV_FONT_MANAGER_NAME_MAX_LEN` to set the maximum
length of the font name.
Initialize Font Manager
-----------------------
Use :cpp:func:`lv_font_manager_create` to create a font manager, where the
:cpp:func:`recycle_cache_size` parameter is used to set the number of font recycling
caches, which can improve font creation efficiency.
Use :cpp:func:`lv_font_manager_add_src_static` to add a mapping between font names
and font resources to tell the font manager how to access the font resources.
Note that if the font resource description structure is not statically allocated
(for example, allocated from a local variable), use :cpp:func:`lv_font_manager_add_src` to add the resource.
This function will copy the contents of the structure itself.
Use :cpp:func:`lv_font_manager_remove_src` to remove the font resource mapping.
It should be noted that the ``src`` parameter must strictly correspond to ``class_p``.
``class_p`` will affect the way the font manager interprets src. If an incompatible parameter is passed, the program may fail.
For currently supported font classes, please refer to the example code.
.. code-block:: c
static lv_font_manager_t * g_font_manager = NULL;
void font_manager_init(void)
{
/* Create font manager, with 8 fonts recycling buffers */
g_font_manager = lv_font_manager_create(8);
/* Add font path mapping to font manager */
lv_font_manager_add_src_static(g_font_manager, "Lato-Regular", "./lvgl/examples/libs/freetype/Lato-Regular.ttf", &lv_freetype_font_class);
char path[] = "/path/to/myfont.ttf";
lv_font_manager_add_src(g_font_manager, "MyFont", path, &lv_freetype_font_class);
}
Create Font from Font Manager
-----------------------------
The parameters will be passed to the font creation function of the font backend,
such as :cpp:func:`lv_freetype_font_create` and :cpp:func:`lv_tiny_ttf_create_file`.
The font backend will select the supported parameters by itself and ignore the unsupported parameters.
The ``font_family`` parameter can be filled with the names of multiple fonts
(separated by ``,``) to achieve font concatenation (when the corresponding glyph is
not found in a font file, it will automatically search from the next concatenated
font).
.. code-block:: c
static lv_font_t * g_font = NULL;
/* Create font from font manager */
lv_font_t * g_font = lv_font_manager_create_font(g_font_manager,
"Lato-Regular,MyFont",
LV_FREETYPE_FONT_RENDER_MODE_BITMAP,
24,
LV_FREETYPE_FONT_STYLE_NORMAL,
LV_FONT_KERNING_NONE);
/* Handle error case */
if(g_font == NULL) {
g_font = (lv_font_t *)LV_FONT_DEFAULT;
}
/* Create label with the font */
lv_obj_t * label = lv_label_create(lv_screen_active());
lv_obj_set_style_text_font(label, g_font, 0);
lv_label_set_text(label, "Hello World!");
Delete Font
-----------
Use :cpp:func:`lv_font_manager_delete_font` to delete the font when it is no longer needed.
The font manager will mark the font resource as a recyclable font so that it has the
possibility of being more quickly created next time.
Note that you need to delete any Widgets that used the font first, and then
delete the font to avoid accessing wild pointers.
.. code-block:: c
/* Delete label and font */
lv_obj_delete(label);
lv_font_manager_delete_font(g_font_manager, g_font);
g_font = NULL;
Delete Font Manager
-------------------
Use :cpp:func:`lv_font_manager_delete` to destroy the entire font manager. It should
be noted that before destruction, it is necessary to ensure that the application has
deleted all fonts using :cpp:func:`lv_font_manager_delete_font`. The font manager
will check the reference status of all allocated fonts. If there are still fonts
being referenced, the font manager will fail to be destroyed and the function will return false.
.. _font_manager_example:
Example
*******
.. include:: ../../examples/others/font_manager/index.rst
.. _font_manager_api:
API
***

View File

@@ -0,0 +1,92 @@
.. _fragment:
========
Fragment
========
Fragment is a concept copied from
`Android <https://developer.android.com/guide/fragments>`__.
It represents a reusable portion of your app's UI. A fragment defines
and manages its own layout, has its own lifecycle, and can handle its
own events. Like Android's Fragment that must be hosted by an activity
or another fragment, Fragment in LVGL needs to be hosted by a Widget,
or another fragment. The fragment's view hierarchy becomes part of, or
attaches to, the host's view hierarchy.
Such concept also has some similarities to `UiViewController on
iOS <https://developer.apple.com/documentation/uikit/uiviewcontroller>`__.
Fragment Manager is a manager holding references to fragments attached
to it, and has an internal stack to achieve forward and backwards navigation. You can use
fragment manager to build a navigation stack, or a multi-pane application
easily.
.. _fragment_usage:
Usage
*****
Enable :c:macro:`LV_USE_FRAGMENT` in ``lv_conf.h``.
Create Fragment Class
---------------------
.. code-block:: c
struct sample_fragment_t {
/* IMPORTANT: don't miss this part */
lv_fragment_t base;
/* States, object references and data fields for this fragment */
const char *title;
};
const lv_fragment_class_t sample_cls = {
/* Initialize something needed */
.constructor_cb = sample_fragment_ctor,
/* Create view objects */
.create_obj_cb = sample_fragment_create_obj,
/* IMPORTANT: size of your fragment struct */
.instance_size = sizeof(struct sample_fragment_t),
};
Use ``lv_fragment_manager``
---------------------------
.. code-block:: c
/* Create fragment instance, and Widgets will be added to container */
lv_fragment_manager_t *manager = lv_fragment_manager_create(container, NULL);
/* Replace current fragment with instance of sample_cls, and init_argument is user defined pointer */
lv_fragment_manager_replace(manager, &sample_cls, init_argument);
Fragment Based Navigation
-------------------------
.. code-block:: c
/* Add one instance into manager stack. View object of current fragment will be destroyed,
* but instances created in class constructor will be kept.
*/
lv_fragment_manager_push(manager, &sample_cls, NULL);
/* Remove the top most fragment from the stack, and bring back previous one. */
lv_fragment_manager_pop(manager);
.. _fragment_example:
Example
*******
.. include:: ../../examples/others/fragment/index.rst
.. _fragment_api:
API
***

View File

@@ -0,0 +1,93 @@
.. _gridnav:
===============
Grid navigation
===============
Grid navigation (gridnav for short) is a feature that moves focus among a set
of child Widgets via arrow-key presses.
If the child Widgets are arranged into a grid-like layout then the up, down,
left and right arrows move focus to the nearest sibling in the
respective direction.
It doesn't matter how the children are positioned, as only the current x
and y coordinates are considered. This means that gridnav works with
manually positioned children, as well as :ref:`flex` and
:ref:`grid` layouts.
Gridnav also works if the children are arranged into a single row or
column. This is useful, for example, to simplify navigation among items in a
:ref:`List Widget <lv_list>`.
Gridnav assumes that the Widget to which gridnav is added is part of a
:ref:`group <indev_groups>`. This way, if the Widget with
gridnav has focus, the arrow key presses are automatically forwarded to
the Widget so that gridnav can process the arrow keys.
To move the focus to the next widget of the group use
:cpp:enumerator:`LV_KEY_NEXT` or :cpp:enumerator:`LV_KEY_PREV`.
Optionally you can also use :cpp:func:`lv_group_focus_next`
or :cpp:func:`lv_group_focus_prev` or the ``TAB``
key on keyboard as usual.
If the container is scrollable and the focused child is out of the view,
gridnav will automatically scroll the child into view.
.. _gridnav_usage:
Usage
*****
To add gridnav behavior to any Widget (e.g. one serving as a container for
child Widgets that the end user will navigate among using arrow keys) use
:cpp:expr:`lv_gridnav_add(container, flags)`.
The ``flags`` argument controls the navigation behavior:
- :cpp:enumerator:`LV_GRIDNAV_CTRL_NONE`: Default settings
- :cpp:enumerator:`LV_GRIDNAV_CTRL_ROLLOVER`: If there is no next/previous Widget in a
direction, the focus goes to the Widget in the next/previous row (on
left/right keys) or first/last row (on up/down keys)
- :cpp:enumerator:`LV_GRIDNAV_CTRL_SCROLL_FIRST`: If an arrow is pressed and the focused
Widget can be scrolled in that direction then it will be scrolled instead of
going to the next/previous Widget. If there is no more room for scrolling the
next/previous Widget will receive focus normally.
- :cpp:enumerator:`LV_GRIDNAV_CTRL_HORIZONTAL_MOVE_ONLY`: Only use the left/right keys
for grid navigation. Up/down key events will be sent to the Widget that has focus.
- :cpp:enumerator:`LV_GRIDNAV_CTRL_VERTICAL_MOVE_ONLY`: Only use the up/down keys
for grid navigation. Left/right key events will be sent to the Widget that has focus.
While the above behaviors can be combined by bit-wise OR-ing the above values together,
:cpp:enumerator:`LV_GRIDNAV_CTRL_HORIZONTAL_MOVE_ONLY` and :cpp:enumerator:`LV_GRIDNAV_CTRL_VERTICAL_MOVE_ONLY`
should not be used together.
:cpp:expr:`lv_gridnav_remove(container)` Removes gridnav behavior from a Widget.
Focusable Widgets
*****************
A Widget needs to be clickable or click focusable
(:cpp:enumerator:`LV_OBJ_FLAG_CLICKABLE` or :cpp:enumerator:`LV_OBJ_FLAG_CLICK_FOCUSABLE`) and not
hidden (:cpp:enumerator:`LV_OBJ_FLAG_HIDDEN`) to receive focus via gridnav.
.. _gridnav_example:
Examples
********
.. include:: ../../examples/others/gridnav/index.rst
.. _gridnav_api:
API
***

View File

@@ -0,0 +1,132 @@
.. _ime_pinyin:
==========
Pinyin IME
==========
Pinyin IME provides an API to provide Chinese Pinyin input method (Chinese
input) for a Keyboard Widget, which supports both 26-key and 9-key input modes.
You can think of ``lv_ime_pinyin`` as a Pinyin input method plug-in for
the Keyboard Widget.
Normally, an environment where :ref:`lv_keyboard` can
run can also run ``lv_ime_pinyin``. There are two main influencing
factors: the size of the font file and the size of the dictionary.
.. _ime_pinyin_usage:
Usage
*****
Enable :c:macro:`LV_USE_IME_PINYIN` in ``lv_conf.h``.
First use :cpp:expr:`lv_ime_pinyin_create(lv_screen_active())` to create a Pinyin
input-method plug-in, then use
:cpp:expr:`lv_ime_pinyin_set_keyboard(pinyin_ime, kb)` to add the Keyboard Widget
you created to the Pinyin input method plug-in. You can use
:cpp:expr:`lv_ime_pinyin_set_dict(pinyin_ime, your_dict)` to use a custom
dictionary. If you don't want to use the built-in dictionary,
you can disable :c:macro:`LV_IME_PINYIN_USE_DEFAULT_DICT` in ``lv_conf.h``,
which can save a lot of memory space.
The built-in thesaurus is customized based on the
**LV_FONT_SOURCE_HAN_SANS_SC_16_CJK** font library, which currently has more
than 1,000 of the most common CJK radicals, so it is recommended to use a
custom font and thesaurus.
In the process of using the Pinyin input method plug-in, you can change
the Keyboard and dictionary at any time.
Custom Dictionary
*****************
If you don't want to use the built-in Pinyin dictionary, or if you feel that the
built-in phonetic dictionary consumes too much memory, you can use a custom dictionary.
Customizing the dictionary is very simple.
First, set :c:macro:`LV_IME_PINYIN_USE_DEFAULT_DICT` to ``0`` in ``lv_conf.h``
Then, write a dictionary in the following format.
Dictionary format
-----------------
The arrangement order of each pinyin syllable is very important. If you
need to customize your own thesaurus according to the Hanyu Pinyin
syllable table, you can read
`here <https://baike.baidu.com/item/%E6%B1%89%E8%AF%AD%E6%8B%BC%E9%9F%B3%E9%9F%B3%E8%8A%82/9167981>`__
to learn about the Hanyu Pinyin syllables and the syllable table.
Then, write your own dictionary according to the following format:
.. code-block:: c
lv_100ask_pinyin_dict_t your_pinyin_dict[] = {
{ "a", "啊阿呵吖" },
{ "ai", "埃挨哎唉哀皑蔼矮碍爱隘癌艾" },
{ "an", "按安暗岸俺案鞍氨胺厂广庵揞犴铵桉谙鹌埯黯" },
{ "ang", "昂肮盎仰" },
{ "ao", "凹敖熬翱袄傲奥懊澳" },
{ "ba", "芭捌叭吧笆八疤巴拔跋靶把坝霸罢爸扒耙" },
{ "bai", "白摆佰败拜柏百稗伯" },
/* ...... */
{ "zuo", "昨左佐做作坐座撮琢柞"},
{NULL, NULL}
**The last item** must be ``{null, null}``, or it will not work
properly.
.. _ime_pinyin_apply_new_dictionary:
Applying a new dictionary
-------------------------
After writing a dictionary according to the above dictionary format, you
only need to call this function to set up and use your dictionary:
.. code-block:: c
lv_obj_t * pinyin_ime = lv_100ask_pinyin_ime_create(lv_screen_active());
lv_100ask_pinyin_ime_set_dict(pinyin_ime, your_pinyin_dict);
.. _ime_pinyin_modes:
Modes
*****
lv_ime_pinyin has the following modes:
- :cpp:enumerator:`LV_IME_PINYIN_MODE_K26`: Pinyin 26-key input mode
- :cpp:enumerator:`LV_IME_PINYIN_MODE_K9`: Pinyin 9-key input mode
- :cpp:enumerator:`LV_IME_PINYIN_MODE_K9_NUMBER`: Numeric keypad mode
The Keyboard's ``TEXT``-mode layout contains buttons to change mode.
To set the mode manually, use :cpp:expr:`lv_ime_pinyin_set_mode(pinyin_ime, mode)`.
The default mode is :cpp:enumerator:`LV_IME_PINYIN_MODE_K26`.
.. _ime_pinyin_example:
Example
*******
.. include:: ../../examples/others/ime/index.rst
.. _ime_pinyin_api:
API
***

View File

@@ -0,0 +1,53 @@
.. _lv_imgfont:
==========
Image font
==========
Draw image in **label** or **span** obj with :cpp:type:`lv_imgfont`. This is often used to
display Unicode emoji icons in text.
Supported image formats: determined by enabled LVGL :ref:`image decoders <overview_image_decoder>`.
.. _lv_imgfont_usage:
Usage
*****
Enable :c:macro:`LV_USE_IMGFONT` in ``lv_conf.h``.
To create a new *imgfont*:
.. code-block:: c
static lv_font_t * imgfont;
...
imgfont = lv_imgfont_create(height, path_cb, user_data);
- ``height`` Font size.
- ``path_cb`` A function to get the image path of a character.
Pass ``NULL`` if no image should be shown, but the character itself.
- ``user_data`` Pointer to user data.
To use the *imgfont* in a label, reference it:
:cpp:expr:`lv_obj_set_style_text_font(label, imgfont, LV_PART_MAIN)`
To destroy the *imgfont* that is no longer used, use :cpp:expr:`lv_imgfont_destroy(imgfont)`.
.. _lv_imgfont_example:
Example
*******
.. include:: ../../examples/others/imgfont/index.rst
.. _lv_imgfont_api:
API
***

View File

@@ -0,0 +1,22 @@
.. _auxiliary_modules:
=================
Auxiliary Modules
=================
.. toctree::
:maxdepth: 2
file_explorer
font_manager
fragment
gridnav
ime_pinyin
imgfont
monkey
obj_id
obj_property
observer/index
snapshot
test
xml/index

View File

@@ -0,0 +1,62 @@
.. _monkey:
======
Monkey
======
The Monkey module provides LVGL applications with a simple monkey test. Monkey
Testing is a technique where the user tests the application or system by providing
random inputs and checking the behavior or seeing whether the aplication or system
will crash. This module provides this service as simulated random input to stress
test an LVGL application.
.. _monkey_usage:
Usage
*****
First, enable :c:macro:`LV_USE_MONKEY` in ``lv_conf.h``.
Next, declare a variable (it can be local) of type :c:type:`lv_monkey_config_t` to
define the configuration structure, initialize it using
:cpp:expr:`lv_monkey_config_init(cfg)` then set its ``type`` member to the desired
type of :ref:`input device <indev>`, and set the ``min`` and ``max`` values for its
``period_range`` and ``input_range`` members to set the time ranges (in milliseconds)
and input ranges the Monkey module will use to generate random input at random times.
Next, call :cpp:expr:`lv_monkey_create(cfg)` to create the Monkey. It returns a
pointer to the ``lv_monkey_t`` created.
Finally call :cpp:expr:`lv_monkey_set_enable(monkey, true)` to enable Monkey.
If you want to pause the monkey, call
:cpp:expr:`lv_monkey_set_enable(monkey, false)`. To delete it, call
:cpp:expr:`lv_monkey_delete(monkey)`.
Note that ``input_range`` has different meanings depending on the ``type`` input device:
- :cpp:enumerator:`LV_INDEV_TYPE_POINTER`: No effect, click randomly within the pixels of the screen resolution.
- :cpp:enumerator:`LV_INDEV_TYPE_ENCODER`: The minimum and maximum values of ``enc_diff``.
- :cpp:enumerator:`LV_INDEV_TYPE_BUTTON`: The minimum and maximum values of ``btn_id``.
Use :cpp:func:`lv_monkey_get_indev` to get the input device, and use
:cpp:func:`lv_indev_set_button_points` to map the key ID to the coordinates.
- :cpp:enumerator:`LV_INDEV_TYPE_KEYPAD`: No effect, Send random :ref:`indev_keys`.
.. _monkey_example:
Example
*******
.. include:: ../../examples/others/monkey/index.rst
.. _monkey_api:
API
***

View File

@@ -0,0 +1,197 @@
.. _widget_id:
=========
Widget ID
=========
Widgets can optionally have identifiers added to their functionality if needed for
the application. Exactly how that happens is designed to be flexible, and can morph
with the needs of the application. It can even be a timestamp or other data current
at the time the Widget was created.
.. _widget_id_usage:
Usage
*****
Enable Widget ID functionality by setting :c:macro:`LV_USE_OBJ_ID` to ``1`` in ``lv_conf.h``.
Once enabled, several things change:
- each Widget will now have a ``void *`` field called ``id``;
- these two API functions become available:
- :cpp:expr:`lv_obj_get_id(widget)`,
- :cpp:expr:`lv_obj_find_by_id(widget, id)`;
- several more Widget-ID-related API functions become available if
:c:macro:`LV_USE_OBJ_ID_BUILTIN` is non-zero (more on this below);
- two additional configuration macros both :c:macro:`LV_OBJ_ID_AUTO_ASSIGN` and
:c:macro:`LV_USE_OBJ_ID_BUILTIN` now have meaning.
:c:macro:`LV_OBJ_ID_AUTO_ASSIGN`
--------------------------------
This macro in ``lv_conf.h`` defaults to whatever value :c:macro:`LV_USE_OBJ_ID`
equates to. You can change this if you wish. Either way, if it equates to a
non-zero value, it causes two things to happen:
- :cpp:expr:`lv_obj_assign_id(class_p, widget)` will be called at the end of each
Widget's creation, and
- :cpp:expr:`lv_obj_free_id(widget)` will be called at the end of the sequence when
each Widget is deleted.
Because of this timing, custom versions of these functions can be used according to
the below, and they can even be used like "event hooks" to implement a trace
operation that occurs when each Widget is created and deleted.
:cpp:expr:`lv_obj_assign_id(class_p, widget)`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This function (whether provided by LVGL or by you --- more on this below) is
responsible for assigning a value to the Widget's ``id`` field, and possibly do
other things, depending on the implementation.
:cpp:expr:`lv_obj_free_id(widget)`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This function (whether provided by LVGL or by you --- more on this below) is
responsible for doing the clean-up of any resources allocated by
:cpp:func:`lv_obj_assign_id()`
:c:macro:`LV_USE_OBJ_ID_BUILTIN`
--------------------------------
This macro in ``lv_conf.h`` equates to ``1`` by default. You can change this if you
wish. When it equates to a non-zero value the following function implementations are
provided by LVGL:
- :cpp:expr:`lv_obj_assign_id(class_p, widget)`
- :cpp:expr:`lv_obj_free_id(widget)`
- :cpp:expr:`lv_obj_set_id(widget, id)`
- :cpp:expr:`lv_obj_stringify_id(widget, buf, len)`
- :cpp:expr:`lv_obj_id_compare(id1, id2)`
These supply the default implementation for Widget IDs, namely that for each Widget
created, :cpp:expr:`lv_obj_stringify_id(widget, buf, len)` will produce a unique
string for it. Example: if the following 6 Widgets are created in this sequence:
- Screen
- Label
- Button
- Label
- Label
- Image
the strings produced by :cpp:expr:`lv_obj_stringify_id(widget, buf, len)` would be
- obj1
- label1
- btn1
- label2
- label3
- image1
respectively.
.. _widget_id_custom_generator:
Using a custom ID generator
---------------------------
If you wish, you can provide custom implementations for several Widget-ID related
functions. You do this by first setting :c:macro:`LV_USE_OBJ_ID_BUILTIN` to `0` in
``lv_conf.h``.
You will then need to provide implementations for the following functions (and link
them with LVGL):
.. code-block:: c
const char * lv_obj_stringify_id(lv_obj_t * widget, char * buf, uint32_t len);
int lv_obj_id_compare(const void * id1, const void * id2);
If :c:macro:`LV_OBJ_ID_AUTO_ASSIGN` equates to a non-zero value (or if you otherwise
simply need to use them), you will also need to provide implementations for:
.. code-block:: c
void lv_obj_assign_id(const lv_obj_class_t * class_p, lv_obj_t * widget);
void lv_obj_free_id(lv_obj_t * widget);
If :c:macro:`LV_BUILD_TEST` equates to a non-zero value and you are including LVGL
test code in your compile (or if you otherwise simply need to use them), you
will also need to provide an implementation for:
.. code-block:: c
void lv_obj_set_id(lv_obj_t * widget, void * id);
Examples of implementations of these functions exist in ``lv_obj_id_builtin.c``, but
you are free to use a different design if needed.
:cpp:func:`lv_obj_stringify_id` converts the passed ``widget`` to a string
representation (typically incorporating the ``id`` field) and writes it into the
buffer provided in its ``buf`` argument.
:cpp:func:`lv_obj_id_compare` compares 2 ``void * id`` values and returns ``0`` when
they are considered equal, and non-zero otherwise.
If :c:macro:`LV_OBJ_ID_AUTO_ASSIGN` equates to a non-zero value,
:cpp:func:`lv_obj_assign_id` is called when a Widget is created. It is responsible
for assigning a value to the Widget's ``id`` field. A pointer to the Widget's final
class is passed in its ``class_p`` argument in case it is needed for determining the
value for the ``id`` field, or for other possible needs related to your design for
Widget IDs. Note that this pointer may be different than :cpp:expr:`widget->class_p`
which is the class of the Widget currently being created.
If :c:macro:`LV_OBJ_ID_AUTO_ASSIGN` equates to a non-zero value,
:cpp:func:`lv_obj_free_id` is called when a Widget is deleted. It needs to perform
the clean-up for any resources allocated by :cpp:func:`lv_obj_assign_id`.
Dumping a Widget Tree
---------------------
Regardless of the state of any of the above macros, the function
:cpp:expr:`lv_obj_dump_tree(widget)` provides a "dump" of the Widget Tree for the
specified Widget (that Widget plus all its children recursively) using the
currently-configured method used by the :c:macro:`LV_LOG_USER` macro. If NULL is
passed instead of a pointer to a "root" Widget, the dump will include the Widget Tree
for all :ref:`Screens`, for all :ref:`Displays <display>` in the system.
For :c:macro:`LV_LOG_USER` to produce output, the following needs to be true in
``lv_conf.h``:
- :c:macro:`LV_USE_LOG` must equate to a non-zero value
- :c:macro:`LV_LOG_LEVEL` <= :c:macro:`LV_LOG_LEVEL_USER`
It will recursively walk through all that Widget's children (starting with the Widget
itself) and print the Widget's parent's address, the Widget's address, and if
:c:macro:`LV_USE_OBJ_ID` equates to a non-zero value, will also print the output of
:cpp:func:`lv_obj_stringify_id` for that Widget.
This can be useful in the event of a UI crash. From that log you can examine the
state of the Widget Tree when :cpp:expr:`lv_obj_dump_tree(widget)` was called.
For example, if a pointer to a deleted Widget is stored in a Timer's
:cpp:expr:`timer->user_data` field when the timer event callback is called, attempted
use of that pointer will likely cause a crash because the pointer is not valid any
more. However, a timely dump of the Widget Tree right before that point will show
that the Widget no longer exists.
Find child by ID
----------------
.. warning::
``lv_obj_find_by_id(widget, id)`` is deprecated. To find a widget use ``obj_name``.
:cpp:expr:`lv_obj_find_by_id(widget, id)` will perform a recursive walk through
``widget``\ 's children and return the first child encountered having the given ID.

View File

@@ -0,0 +1,278 @@
.. _widget_property:
=================
Widget Properties
=================
Widget Properties provides a way to greatly reduce the size of the interface between
LVGL and whatever logic layer is just above it, to get and set the most important
properties of Widgets. It's intended use is to:
- simplify (decreasing development time) writing bindings for LVGL in another
language, such as:
- Micropython,
- Lua,
- Python,
- Perl,
- .NET
- make it possible to control the UI (or parts of it, e.g. animation) via external
input, without modifying firmware, such as:
- an external text file (YAML, JSON, XML, custom)
- any external input source (e.g. serial)
While using it consumes more program space and more CPU overhead while setting and
getting Widget properties, it is designed so minimize that additional CPU overhead.
What is a Widget Property?
**************************
A Widget's properties are the combined set of :ref:`styles` plus additional properties
that are unique to each type of Widget, that determine what the Widget looks like and
how it behaves. Examples: size, position, color, are properties of all Widgets
whereas text, long-mode, selection-start, and selection-end, are properties unique to
Label Widgets. A Widget's :ref:`local styles <style_local>` are also valid
properties in this context.
The non-style Widget properties available for a given Widget are implemented at the
top of that Widget's primary ``.c`` file as a ``const`` id-to-function-pointer lookup
array, like this example for the Label Widget:
.. code:: c
#if LV_USE_OBJ_PROPERTY
static const lv_property_ops_t properties[] = {
{
.id = LV_PROPERTY_LABEL_TEXT,
.setter = lv_label_set_text,
.getter = lv_label_get_text,
},
{
.id = LV_PROPERTY_LABEL_LONG_MODE,
.setter = lv_label_set_long_mode,
.getter = lv_label_get_long_mode,
},
{
.id = LV_PROPERTY_LABEL_TEXT_SELECTION_START,
.setter = lv_label_set_text_selection_start,
.getter = lv_label_get_text_selection_start,
},
{
.id = LV_PROPERTY_LABEL_TEXT_SELECTION_END,
.setter = lv_label_set_text_selection_end,
.getter = lv_label_get_text_selection_end,
},
};
#endif
This array is attached to the ``properties`` field of the Widget's class, so all
Widgets of the same type share the same id-to-function-pointer lookup array.
Some properties are read-only. When this is the case, only the ``getter`` field in
the corresponding array element will be initialized with a function pointer.
Example: an object's child-Widget count or scroll position must be controlled via
other types of input, but their values are readable through this API.
.. _widget_property_usage:
Usage
*****
By default, this feature of LVGL is turned off. It can be turned on by configuring
:c:macro:`LV_USE_OBJ_PROPERTY` to ``1`` in ``lv_conf.h``.
The 3 functions that then become available are:
- :cpp:type:`lv_result_t` :cpp:expr:`lv_obj_set_property(widget, lv_property_t * value)`
Sets specified property of Widget.
- :cpp:type:`lv_property_t` :cpp:expr:`lv_obj_get_property(widget, lv_prop_id_t id)`
Reads property value from Widget.
- :cpp:type:`lv_result_t` :cpp:expr:`lv_obj_set_properties(widget, lv_property_t * values, count)`
Sets multiple Widget properties from an array of :cpp:type:`lv_property_t`.
An ``lv_prop_id_t`` is a :ref:`widget_property_id`, whereas an ``lv_property_t`` is a
struct that pairs a :ref:`widget_property_id` with a :ref:`widget_property_value`.
The following is an example of an array that could be used as the ``values`` argument
in :cpp:func:`lv_obj_set_properties`:
.. code-block:: c
lv_property_t values[] = {
{ .id = LV_PROPERTY_IMAGE_SRC, .ptr = &img_demo_widgets_avatar, },
{ .id = LV_PROPERTY_IMAGE_PIVOT, .ptr = &pivot_50, },
{ .id = LV_PROPERTY_IMAGE_SCALE, .num = 128, },
{ .id = LV_PROPERTY_OBJ_FLAG_CLICKABLE, .num = 1, },
{ .id = LV_STYLE_IMAGE_OPA, .num = 128, },
{ .id = LV_STYLE_BG_COLOR, .color = (lv_color_t){.red = 0x11, .green = 0x22, .blue = 0x33}, },
}
Alternately, :cpp:expr:`lv_obj_set_property(widget, value)` could be called using
this array's individual ``value`` elements inside a loop.
.. _widget_property_id:
Property ID
-----------
:cpp:type:`lv_prop_id_t` identifies which property to get/set. It is an enum value
defined in the primary ``.h`` file for the Widget in question. Because the actual
names are "assembled" by a preprocessor string-concatenation macro and are thus
hard to visualize, you can also find the names in the Widget's primary ``.c`` file in
the ``properties[]`` array initializing the ``.id`` fields in the array. For example,
``LV_PROPERTY_LABEL_TEXT`` is one found in ``lv_label.c``, and the properties
available to all Widgets are found near the top of the ``lv_obj.c`` file.
That array is attached to the Widget's class, enabling "getter" and "setter" functions
to be looked up for each type of Widget where Widget properties has been implemented.
(Note: this is done internally so you don't have to.)
If the property you need to set or get using this API is not implemented yet, you can
add your own Widget property ID following same rules and using one of two helper
macros in the ``enum`` in the Widget's primary ``.h`` file. In both cases, the
"assembled" value is a 32-bit value:
- :c:macro:`LV_PROPERTY_ID` (for single values -- see :ref:`Single Values` below)`;
bits ``<31:28>`` contain the property's value type and bits ``<27:0>`` contain the
property ID.
- :c:macro:`LV_PROPERTY_ID2` (for paired values -- see :ref:`Paired Values` below)`;
bits ``<31:28>`` contain the type for the property's 1st value, bits ``<27:24>``
contain the type for the 2nd value, and bits ``<23:0>`` contain the property ID.
Just make sure the ID is unique across all Widgets.
Note that :cpp:type:`lv_style_prop_t` (enumerator values beginning with ``LV_PROPERTY_STYLE_...``)
are also valid property IDs, and can be used to set or get a Widget's style values.
.. _widget_property_value:
Property Value
--------------
:cpp:type:`lv_property_t` is a struct that begins with an ``id`` field whose meaning
is the same as property ID described above, paired with a value, which is a union of
all possible property types including integer, pointer and color. The ``value``
field is also capable of carrying the different value types for styles. It does this
by being a union of all the different types that might be needed. The list of
"union-ed" fields at this writing are:
.. _single values:
Single Values
~~~~~~~~~~~~~
.. code-block:: c
int32_t num; /**< Signed integer number (enums or "normal" numbers) */
uint32_t num_u; /**< Unsigned integer number (opacity, Booleans) */
bool enable; /**< Booleans */
const void * ptr; /**< Constant pointers (font, cone text, etc.) */
lv_color_t color; /**< Colors */
lv_value_precise_t precise; /**< float or int for precise value */
lv_point_t point; /**< Point, contains two int32_t */
struct {
/**
* Note that place struct member `style` at first place is intended.
* `style` shares same memory with `num`, `ptr`, `color`.
* So we set the style value directly without using `prop.style.num`.
*
* E.g.
*
* static const lv_property_t obj_pos_x = {
* .id = LV_PROPERTY_STYLE_X,
* .num = 123,
* .selector = LV_STATE_PRESSED,
* }
*
* instead of:
* static const lv_property_t obj_pos_x = {
* .id = LV_PROPERTY_STYLE_X,
* .style.num = 123, // note this line.
* .selector = LV_STATE_PRESSED,
* }
*/
lv_style_value_t style; /**< Make sure it's the first element in struct. */
uint32_t selector; /**< Style selector, lv_part_t | lv_state_t */
};
.. _paired values:
Paired Values
~~~~~~~~~~~~~
.. code-block:: c
/**
* For some properties like slider range, it contains two simple (4-byte) values
* so we can use `arg1.num` and `arg2.num` to set the argument.
*/
struct {
union {
int32_t num;
uint32_t num_u;
bool enable;
const void * ptr;
lv_color_t color;
lv_value_precise_t precise;
} arg1, arg2;
};
You can find the current :cpp:type:`lv_property_t` struct in the
`lv_obj_property.h <https://github.com/lvgl/lvgl/blob/master/src/core/lv_obj_property.h>`__ file.
Property ID Lookup by Name
--------------------------
Setting configuration macro :c:macro:`LV_USE_OBJ_PROPERTY_NAME` to ``1`` enables the
following functions to look up property IDs by passing property name (a string):
- :cpp:type:`lv_prop_id_t` :cpp:expr:`lv_obj_property_get_id(widget, name)`
Gets property ID by recursively searching for ``name`` in Widget's class hierarchy,
and if still not found, then searches style properties.
- :cpp:type:`lv_prop_id_t` :cpp:expr:`lv_obj_class_property_get_id(class_p, name)`
Gets property ID by doing a non-recursive search for ``name`` directly in Widget
class properties.
- :cpp:type:`lv_prop_id_t` :cpp:expr:`lv_style_property_get_id(name)`
Gets style property ID by name.
The latter two functions are useful when you already know ``name`` is among the
properties of a specific Widget class, or is a style name, since a property name may
exist in both lists. Because of the search sequence in
:cpp:expr:`lv_obj_property_get_id(widget, name)`, if a name does exist in both lists,
then using this function forces the name in the Widget's class hierarchy properties
to have precedence over the style name.
You can tell which names are available by looking in the ``.c`` files in the
``./src/widgets/property/`` directory. Note that to support binary name searches,
these arrays are generated so that they are guaranteed to be in alphabetical order.
If you need to add a property that is not present, it is recommended to add it in the
``enum`` near the top of the Widget's primary ``.h`` file, and re-generate these
lists using ``./scripts/properties.py`` to ensure alphabetical ordering is preserved.
Additional Notes
****************
For the ``lv_property_t * value`` argument of the :cpp:func:`lv_obj_set_property`
function, the language used to call that function (e.g. in a static or
dynamically-loaded library) may need additional code to convert values from their
local data type (e.g. dict, table, etc.) to a C struct before passing it to the
:cpp:func:`lv_obj_set_property` function.
API
***

View File

@@ -0,0 +1,11 @@
.. _observer:
========
Observer
========
.. toctree::
:maxdepth: 2
observer
observer_examples

View File

@@ -0,0 +1,546 @@
.. _observer_how_to_use:
==========
How to Use
==========
.. _observer_overview:
Overview
********
.. _observer pattern: https://en.wikipedia.org/wiki/Observer_pattern
The ``lv_observer`` module is an implemention of the `Observer Pattern`_.
This implementation consists of:
:Subjects: (in global memory or heap) are "logic packages", each containing the
value being "observed" and its type (integer (``int32_t``), a string, a
pointer, an :cpp:type:`lv_color_t`, or a group);
:Observers: (zero or more per Subject, always dynamically-allocated) are always
attached to exactly one Subject, and provide user-defined notifications
each the time Subject's value changes.
A Subject and its Observers can be used in various ways:
1. Simply subscribe to a Subject and get notified when the Subject's value changes.
2. Subscribe to a group Subject (connects a group of Subjects) to get notified when
any of the Subjects' values change in the group.
3. Bind Widgets to Subjects to automatically match the Widget's value with the
Subject (e.g. a Label's text or an Arc's value).
.. _observer_usage:
Usage
*****
Using Observer first requires :c:macro:`LV_USE_OBSERVER` be configured to ``1``.
(It is ``1`` by default, and can be set to ``0`` to save some program space if you
will not be using Observer.)
A typical use case looks like this:
.. code-block:: c
// Any typical global variable
lv_subject_t my_subject;
/*-------
* main.c
*-------*/
extern lv_subject_t my_subject;
void main(void)
{
// Initialize Subject as integer with the default value of 10.
lv_subject_init_int(&my_subject, 10);
some_module_init();
}
/*--------------
* some_module.c
*--------------*/
extern lv_subject_t some_subject;
// Will be called when Subject's value changes
static void some_observer_cb(lv_observer_t * observer, lv_subject_t * subject)
{
int32_t v = lv_subject_get_int(subject);
do_something(v);
}
void some_module_init(void)
{
// Subscribe to Subject as an Observer.
lv_subject_add_observer(&some_subject, some_observer_cb, NULL);
}
/*--------------
* some_system.c
*--------------*/
extern lv_subject_t some_subject;
void some_event(void)
{
// The below call sets Subject's value to 30 and notifies current Observers.
lv_subject_set_int(&some_subject, 30);
}
.. _observer_subject:
Subject
-------
Subject Initialization
~~~~~~~~~~~~~~~~~~~~~~
Subjects have to be static or global variables, or dynamically-allocated
:cpp:type:`lv_subject_t` objects. Reason: their content must remain valid through
the life of the Subject.
To initialize a Subject use ``lv_subject_init_<type>(&subject, params, init_value)``.
The following initialization functions exist, one for each of the Subject types:
:Integer: void :cpp:expr:`lv_subject_init_int(subject, int_value)`
:String: void :cpp:expr:`lv_subject_init_string(subject, buf, prev_buf, buf_size, initial_string)`
:Pointer: void :cpp:expr:`lv_subject_init_pointer(subject, ptr)`
:Color: void :cpp:expr:`lv_subject_init_color(subject, color)`
:Group: void :cpp:expr:`lv_subject_init_group(group_subject, subject_list[], count)`
Setting a Subject's Value
~~~~~~~~~~~~~~~~~~~~~~~~~
The following functions are used to update a Subject's value:
:Integer: void :cpp:expr:`lv_subject_set_int(subject, int_value)`
:String: void :cpp:expr:`lv_subject_copy_string(subject, buf)`
:Pointer: void :cpp:expr:`lv_subject_set_pointer(subject, ptr)`
:Color: void :cpp:expr:`lv_subject_set_color(subject, color)`
At the end of each of these calls, if the new value differs from the previous value,
a notification is sent to all current Observers.
Getting a Subject's Value
~~~~~~~~~~~~~~~~~~~~~~~~~
The following functions are used to get a Subject's current value:
:Integer: int32_t :cpp:expr:`lv_subject_get_int(subject)`
:String: const char * :cpp:expr:`lv_subject_get_string(subject)`
:Pointer: const void * :cpp:expr:`lv_subject_get_pointer(subject)`
:Color: lv_color_t :cpp:expr:`lv_subject_get_color(subject)`
Getting a Subject's Previous Value
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The following functions are used to get a Subject's previous value:
:Integer: int32_t :cpp:expr:`lv_subject_get_previous_int(subject)`
:String: const char * :cpp:expr:`lv_subject_get_previous_string(subject)`
:Pointer: const void * :cpp:expr:`lv_subject_get_previous_pointer(subject)`
:Color: lv_color_t :cpp:expr:`lv_subject_get_previous_color(subject)`
.. _observer_observer:
Observer
--------
Subscribing to a Subject
~~~~~~~~~~~~~~~~~~~~~~~~
The action of subscribing to a Subject:
- dynamically allocates an Observer object,
- attaches it to the Subject,
- performs an initial notification to the Observer (allowing the Observer to
update itself with the Subject's current value), and
- returns a pointer to the newly-created Observer.
Thereafter the Observer will receive a notification each time the Subject's value
changes, as long as that Observer remains attached (subscribed) to that Subject.
Notifications are performed by calling the callback function provided when
subscribing to the Subject.
Simple Subscription
~~~~~~~~~~~~~~~~~~~
To subscribe to a Subject one of the ``lv_subject_add_observer...()`` functions are
used. Alternately, if you want to bind a Subject's value to a Widget's property, one
of the ``lv_<widget_type>_bind_...()`` functions can be used. The former are covered
below. The latter are covered in the :ref:`observer_widget_binding` section.
For the most basic use case, subscribe to a Subject by using the following function:
lv_observer_t * observer = :cpp:expr:`lv_subject_add_observer(&some_subject, some_observer_cb, user_data)`
where the Observer's notification callback should look like this:
.. code-block:: c
static void some_observer_cb(lv_observer_t * observer, lv_subject_t * subject)
{
...
}
This function returns a pointer to the newly-created Observer.
When using this method of subscribing, it is the responsibility of the user to call
:cpp:expr:`lv_observer_remove(observer)` when the Observer is no longer needed, which
both unsubscribes it from the Subject and deletes it from the LVGL heap.
Subscribing While Associating Observer with a Non-Widget Object
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The function subscribes to a Subject additionally associates the Observer with a
pointer to any type of object, a copy of which is saved in the Observer's ``target``
field. This function should be used when the pointer *does not* point to a Widget.
lv_observer_t * observer = :cpp:expr:`lv_subject_add_observer_with_target(&some_subject, some_observer_cb, some_pointer, user_data)`
A copy of the passed pointer can be retrieved by calling
:cpp:expr:`lv_observer_get_target(observer)`, e.g. inside the callback function.
When using this method of subscribing, it is the responsibility of the user to call
:cpp:expr:`lv_observer_remove(observer)` when the Observer is no longer needed, which
both unsubscribes it from the Subject and deletes it from the LVGL heap.
Subscribing While Associating Observer with a Widget
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The function below assocates a Widget with an Observer while subscribing to a
Subject. A copy of the pointer to that Widget is saved in the Observer's ``target``
field. This works exactly like the above method except that when the Widget is
deleted, the Observer thus created will be automatically unsubscribed from the
Subject and deleted from the LVGL heap. Note this is different from
:ref:`observer_widget_binding`.
lv_observer_t * observer = :cpp:expr:`lv_subject_add_observer_obj(&some_subject, some_observer_cb, widget, user_data)`
Any number of Observers can be created and be associated with a Widget this way.
A copy of the pointer to the Widget can be retrieved by calling
:cpp:expr:`lv_observer_get_target_obj(observer)`, e.g. inside the callback function.
Note that this function returns the stored pointer as a ``lv_obj_t *`` type, as
opposed to the ``void *`` type returned by
:cpp:expr:`lv_observer_get_target_obj(observer)`.
(:cpp:expr:`lv_observer_get_target(observer)` can still be used if you need that
pointer as a ``void *`` type for any reason, but in practice, this would be rare.)
Unsubscribing from a Subject
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When an Observer and its associated events are no longer needed, you can remove it
from memory using any of the methods below, depending on the effect you want to create:
- If Widget needs to be deleted, simply delete the Widget, which will automatically
gracefully remove the Observer (and its events) from the Subject, while deleting
the Widget.
- If Widget does NOT need to be deleted:
- :cpp:expr:`lv_obj_remove_from_subject(widget, subject)` deletes all Observers
associated with ``widget``. ``subject`` can be NULL to unsubscribe the Widget
from all associated Subjects.
- :cpp:expr:`lv_subject_deinit(subject)` gracefully disconnects ``subject`` from
all associated Observers and Widget events. This includes subscriptions made
using any of the :ref:`observer_widget_binding` functions covered below.
- :cpp:expr:`lv_observer_remove(observer)` deletes that specific Observer and
gracefully disconnects it from its ``subject`` and any associated Widgets,
where ``observer`` is the return value any of the above
``lv_subject_add_observer_...()`` functions.
.. _observer_subject_groups:
Subject Groups
--------------
When something in your system relies on more than one value (i.e. it needs to be
notified when any of a SET of two or more values changes), it can be made an
Observer of a Subject Group.
Let us consider an example of an instrument which measures either voltage or current.
To display the measured value on a label, 3 things are required:
1. What is being measured (current or voltage)?
2. What is the measured value?
3. What is the range or unit ("mV", "V", "mA", "A")?
When any of these 3 input values change, the label needs to be updated, and it needs
to know all 3 values to compose its text.
To handle this you can create an array from the addresses of all the Subjects that
are relied upon, and pass that array as a parameter when you initialize a Subject
with GROUP type.
.. code-block:: c
static lv_subject_t * subject_list[3] = {&subject_1, &subject_2, &subject_3};
lv_subject_init_group(&subject_all, subject_list, 3); /* Last argument is number of elements. */
Observers are then added to Subject Groups (e.g. ``subject_all``) in the usual way.
When this is done, a change to the value of any of the Subjects in the group triggers
a notification to all Observers subscribed to the Subject Group (e.g. ``subject_all``).
As an example, the above scenario with Voltage/Current measurement might look like this:
.. code-block:: c
lv_obj_t * label = lv_label_create(lv_screen_active());
lv_subject_t subject_mode; // Voltage or Current
lv_subject_t subject_value; // Measured value
lv_subject_t subject_unit; // The unit
lv_subject_t subject_all; // Subject group that connects the above 3 Subjects
lv_subject_t * subject_list[3] = {&subject_mode, &subject_value, &subject_unit}; // The elements of the group
lv_subject_init_int(&subject_mode, 0); // Let's say 0 is Voltage, 1 is Current
lv_subject_init_int(&subject_value, 0);
lv_subject_init_pointer(&subject_unit, "V");
lv_subject_init_group(&subject_all, subject_list, 3);
lv_subject_add_observer_obj(&subject_all, all_observer_cb, label, NULL);
...
static void all_observer_cb(lv_observer_t * observer, lv_subject_t * subject)
{
lv_obj_t * label = lv_observer_get_target(observer);
lv_subject_t * subject_mode = lv_subject_get_group_element(subject, 0);
lv_subject_t * subject_value = lv_subject_get_group_element(subject, 1);
lv_subject_t * subject_unit = lv_subject_get_group_element(subject, 2);
int32_t mode = lv_subject_get_int(subject_mode);
int32_t value = lv_subject_get_int(subject_value);
const char * unit = lv_subject_get_pointer(subject_unit);
lv_label_set_text_fmt(label, "%s: %d %s", mode ? "Current" : "Voltage", value, unit);
}
.. _observer_widget_binding:
Widget Binding
--------------
The following methods of subscribing to an integer-type Subject associate the
Observer with ONE of a Widget's properties as thought that property itself were the
Observer. Any of the following Widget properties can be thus bound to an Subject's
integer value:
- flag (or OR-ed combination of flags) from from the ``LV_OBJ_FLAG_...`` enumeration values;
- state (or OR-ed combination of states) from the ``LV_STATE_...`` enumeration values;
- text value for Label Widgets;
- integer value for these Widget types:
- Arc
- Drop-Down
- Roller
- Slider
Any number of Observers can be created for a single Widget, each bound to ONE of
the above properties.
For all of the ``lv_..._bind_...()`` functions covered below, they are similar to
:cpp:expr:`lv_subject_add_observer_obj(&some_subject, some_observer_cb, widget, user_data)`
in that they create an Observer and associates the Widget with it. What is different
is that updates to the Widget's property thus bound are handled internally -- the
user *does not supply callback functions* for any of these subscribing methods -- the
callback methods are supplied by the Observer subsystem.
.. note::
While the examples below show saving a reference to the created Observer objects
for the various ``lv_..._bind_...()`` functions, it is not necessary to do so
unless you need them for some purpose, because the created Observer objects will
be automatically deleted when the Widget is deleted.
Any Widget Type
~~~~~~~~~~~~~~~
Flags
^^^^^
The following methods of subscribing to an integer Subject affect a Widget's flag (or
OR-ed combination of flags). When the subscribing occurs, and each time the
Subject's value is changed thereafter, the Subject's value is compared with the
specified reference value, and the specified flag(s) is (are):
- SET when the Subject's integer value fulfills the indicated condition, and
- CLEARED otherwise.
Here are the functions that carry out this method of subscribing to a Subject. The
``flags`` argument can contain a single, or bit-wise OR-ed combination of any of the
``LV_OBJ_FLAG_...`` enumeration values.
:equal: :cpp:expr:`lv_obj_bind_flag_if_eq(widget, &subject, flags, ref_value)`
:not equal: :cpp:expr:`lv_obj_bind_flag_if_not_eq(widget, &subject, flags, ref_value)`
:greater than: :cpp:expr:`lv_obj_bind_flag_if_gt(widget, &subject, flags, ref_value)`
:greater than or equal: :cpp:expr:`lv_obj_bind_flag_if_ge(widget, &subject, flags, ref_value)`
:less than: :cpp:expr:`lv_obj_bind_flag_if_lt(widget, &subject, flags, ref_value)`
:less than or equal: :cpp:expr:`lv_obj_bind_flag_if_le(widget, &subject, flags, ref_value)`
States
^^^^^^
The following methods of subscribing to an integer Subject affect a Widget's states
(or OR-ed combination of states). When the subscribing occurs, and each time the
Subject's value is changed thereafter, the Subject's value is compared with the
specified reference value, and the specified state(s) is (are):
- SET when the Subject's integer value fulfills the indicated condition, and
- CLEARED otherwise.
Here are the functions that carry out this method of subscribing to a Subject. The
``states`` argument can contain a single, or bit-wise OR-ed combination of any of the
``LV_STATE_...`` enumeration values.
:equal: :cpp:expr:`lv_obj_bind_state_if_eq(widget, &subject, states, ref_value)`
:not equal: :cpp:expr:`lv_obj_bind_state_if_not_eq(widget, &subject, states, ref_value)`
:greater than: :cpp:expr:`lv_obj_bind_state_if_gt(widget, &subject, states, ref_value)`
:greater than or equal: :cpp:expr:`lv_obj_bind_state_if_ge(widget, &subject, states, ref_value)`
:less than: :cpp:expr:`lv_obj_bind_state_if_lt(widget, &subject, states, ref_value)`
:less than or equal: :cpp:expr:`lv_obj_bind_state_if_le(widget, &subject, states, ref_value)`
Checked State
^^^^^^^^^^^^^
The following method of subscribing to an integer Subject affects a Widget's
:cpp:enumerator:`LV_STATE_CHECKED` state. When the subscribing occurs, and each time
the Subject's value is changed thereafter, the Subject's value is compared to a
reference value of ``0``, and the :cpp:enumerator:`LV_STATE_CHECKED` state is:
- CLEARED when the Subject's value is 0, and
- SET when the Subject's integer value is non-zero.
Note that this is a two-way binding (Subject <===> Widget) so direct (or
programmatic) interaction with the Widget that causes its
:cpp:enumerator:`LV_STATE_CHECKED` state to be SET or CLEARED also causes the
Subject's value to be set to ``1`` or ``0`` respectively.
- :cpp:expr:`lv_obj_bind_checked(widget, &subject)`
Label Widgets
~~~~~~~~~~~~~
.. |deg| unicode:: U+000B0 .. DEGREE SIGN
This method of subscribing to an integer Subject affects a Label Widget's
``text``. The Subject can be an STRING, POINTER or INTEGER type.
When the subscribing occurs, and each time the Subject's value is changed thereafter,
the Subject's value is used to update the Label's text as follows:
:string Subject: Subject's string is used to directly update the Label's text.
:pointer Subject: If NULL is passed as the ``format_string`` argument when
subscribing, the Subject's pointer value is assumed to point to a
NUL-terminated string. and is used to directly update the Label's
text. See :ref:`observer_format_string` for other options.
:integer Subject: Subject's integer value is used with the ``format_string`` argument.
See See :ref:`observer_format_string` for details.
Note that this is a one-way binding (Subject ===> Widget).
- :cpp:expr:`lv_label_bind_text(label, &subject, format_string)`
.. _observer_format_string:
The ``format_string`` Argument
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The ``format_string`` argument is optional and if provided, must contain exactly 1
printf-like format specifier and be one of the following:
:string or pointer Subject: "%s" to format the new pointer value as a string or "%p"
to format the pointer as a pointer (typically the
pointer's address value is spelled out with 4, 8 or 16
hexadecimal characters depending on the platform).
:integer Subject: "%d" format specifier (``"%" PRIdxx`` --- a
cross-platform equivalent where ``xx`` can be ``8``,
``16``, ``32`` or ``64``, depending on the platform).
If NULL is passed for the ``format_string`` argument:
:string or pointer Subject: Updates expect the pointer to point to a NUL-terminated string.
:integer Subject: The Label will display an empty string (i.e. nothing).
**Example:** "%d |deg|\C"
Arc Widgets
~~~~~~~~~~~
This method of subscribing to an integer Subject affects an Arc Widget's integer
value directly. Note that this is a two-way binding (Subject <===> Widget) so an end
user's direct interaction with the Arc Widget updates the Subject's value and vice
versa. (Requires :c:macro:`LV_USE_ARC` to be configured to ``1``.)
- :cpp:expr:`lv_arc_bind_value(arc, &subject)`
Slider Widgets
~~~~~~~~~~~~~~
This method of subscribing to an integer Subject affects a Slider Widget's integer
value directly. Note that this is a two-way binding (Subject <===> Widget) so an end
user's direct interaction with the Slider Widget updates the Subject's value and vice
versa. (Requires :c:macro:`LV_USE_SLIDER` to be configured to ``1``.)
- :cpp:expr:`lv_slider_bind_value(slider, &subject)`
Roller Widgets
~~~~~~~~~~~~~~
This method of subscribing to an integer Subject affects a Roller Widget's integer
value directly. Note that this is a two-way binding (Subject <===> Widget) so an end
user's direct interaction with the Slider Widget updates the Subject's value and vice
versa. (Requires :c:macro:`LV_USE_ROLLER` to be configured to ``1``.)
- :cpp:expr:`lv_roller_bind_value(roller, &subject)`
Drop-Down Widgets
~~~~~~~~~~~~~~~~~
This method of subscribing to an integer Subject affects a Drop-Down Widget's integer
value directly. Note that this is a two-way binding (Subject <===> Widget) so an end
user's direct interaction with the Drop-Down Widget updates the Subject's value and
vice versa. (Requires :c:macro:`LV_USE_DROPDOWN` to be configured to ``1``.)
- :cpp:expr:`lv_dropdown_bind_value(dropdown, &subject)`
.. _observer_api:
API
***

View File

@@ -0,0 +1,8 @@
.. _observer examples:
========
Examples
========
.. include:: ../../../examples/others/observer/index.rst

View File

@@ -0,0 +1,86 @@
.. _snapshot:
========
Snapshot
========
Snapshot provides an API to take a snapshot image for an LVGL Widget together
with its children. The image will look exactly like the Widget on the display.
.. _snapshot_usage:
Usage
*****
Simply call function :cpp:expr:`lv_snapshot_take(widget, color_format)` to generate
the image descriptor which can be used as an Image Widget's image source using
:cpp:func:`lv_image_set_src`.
Note, only following color formats are supported at this time:
- :cpp:enumerator:`LV_COLOR_FORMAT_RGB565`
- :cpp:enumerator:`LV_COLOR_FORMAT_RGB888`
- :cpp:enumerator:`LV_COLOR_FORMAT_XRGB8888`
- :cpp:enumerator:`LV_COLOR_FORMAT_ARGB8888`
Freeing the Image
-----------------
The memory :cpp:func:`lv_snapshot_take` uses is dynamically allocated using
:cpp:func:`lv_draw_buf_create`. Use :cpp:func:`lv_draw_buf_destroy` to free the
memory it allocated.
The snapshot image which is the draw buffer returned by :cpp:func:`lv_snapshot_take`
normally won't be added to the cache because it can be drawn directly. So you don't need
to invalidate the cache by calling :cpp:func:`lv_image_cache_drop` before destroying
the draw buffer.
The below code snippet demonstrates correct use of :cpp:func:`lv_snapshot_take`:
.. code-block:: c
void update_snapshot(lv_obj_t * widget, lv_obj_t * img_snapshot)
{
lv_draw_buf_t* snapshot = (void*)lv_image_get_src(img_snapshot);
if(snapshot) {
lv_draw_buf_destroy(snapshot);
}
snapshot = lv_snapshot_take(widget, LV_COLOR_FORMAT_ARGB8888);
lv_image_set_src(img_snapshot, snapshot);
}
Using an Existing Buffer
------------------------
If the snapshot needs to be updated repeatedly, or if the caller provides the draw
buffer, use :cpp:expr:`lv_snapshot_take_to_draw_buf(widget, color_format, draw_buf)`.
In this case, the caller is responsible for creating and destroying the draw buffer.
If snapshot is generated successfully, the image descriptor is updated,
the image data will be stored to the provided ``draw_buf``, and the function will
return :cpp:enumerator:`LV_RESULT_OK`.
Note that snapshot may fail if the provided buffer is not large enough, which can
happen if the Widget's size changes. It's recommended to use
:cpp:expr:`lv_snapshot_reshape_draw_buf(widget, draw_buf)` to first ensure the buffer
is large enough, and if it fails, destroy the existing draw buffer and call
`lv_snapshot_take` directly.
.. _snapshot_example:
Example
*******
.. include:: ../../examples/others/snapshot/index.rst
.. _snapshot_api:
API
***

View File

@@ -0,0 +1,163 @@
.. _test:
==========
UI Testing
==========
Overview
********
The Test module provides functions to emulate clicks, key presses, encoder turns, time passing, and
compare the UI with reference images.
These functions can be easily used in any test framework (such as Unity, GoogleTest, etc.), and
assertions can be performed to check if, for example:
- A widget's value is different from the expected value after emulating user inputs.
- The values are incorrect after some time has passed.
- The screen's content is different from the reference image.
- Some event functions are not triggered.
- Etc.
Note that it is assumed the tests are performed on a desktop or server environment,
where there are no memory constraints.
Usage
*****
The Test module can be enabled by configuring ``LV_USE_TEST`` to a non-zero value,
and it consists of the following components:
- Helpers
- Display emulation
- Input device emulation
- Screenshot comparison
Helpers
-------
Time
~~~~
To emulate elapsed time, two functions can be used:
1. :cpp:expr:`lv_test_wait(ms)`: Emulates that ``ms`` milliseconds have elapsed, but it also calls ``lv_timer_handler`` after each millisecond.
This is useful to check if events (e.g., long press, long press repeat) and timers were triggered correctly over time.
2. :cpp:expr:`lv_test_fast_forward(ms)`: Jumps ``ms`` milliseconds ahead and calls ``lv_timer_handler`` only once at the end.
:cpp:expr:`lv_refr_now(NULL)` is called at the end of both functions to ensure that animations and
widget coordinates are recalculated.
:cpp:expr:`lv_refr_now(NULL)` can also be called manually to force LVGL to refresh the emulated display.
Memory Usage
~~~~~~~~~~~~
If ``LV_USE_STDLIB_MALLOC`` is set to ``LV_STDLIB_BUILTIN``, memory usage and memory leaks can be monitored.
.. code-block:: c
size_t mem1 = lv_test_get_free_mem();
<create and delete items>
size_t mem2 = lv_test_get_free_mem();
if(mem1 != mem2) fail();
It might make sense to create and delete items in a loop many times and add a small tolerance
to the memory leakage test. This might be needed due to potential memory fragmentation. Empirically,
a tolerance of 32 bytes is recommended.
.. code-block:: c
if(LV_ABS((int64_t)mem2 - (int64_t)mem1) > 32) fail();
Display Emulation
-----------------
By calling :cpp:expr:`lv_test_display_create(hor_res, ver_res)`, a dummy display can be created.
It functions like any other normal display, but its content exists only in memory.
When creating this display, the horizontal and vertical resolutions must be passed. Internally,
a framebuffer will be allocated for this size, and ``XRGB8888`` color format will be set.
The resolution and color format can be changed at any time by calling :cpp:func:`lv_display_set_resolution` and
:cpp:func:`lv_display_set_color_format`.
Input Device Emulation
----------------------
By calling :cpp:func:`lv_test_indev_create_all`, three test input devices will be created:
1. A pointer (for touch or mouse)
2. A keypad
3. An encoder
For example, this is how a scroll gesture can be emulated:
.. code-block:: c
lv_test_mouse_move_to(20, 30);
lv_test_mouse_press();
lv_test_wait(20);
lv_test_mouse_move_by(0, 100);
lv_test_wait(20);
lv_test_mouse_release();
lv_test_wait(20);
It is recommended to add :cpp:func:`lv_test_wait` after user actions to ensure that
the new state and coordinates are read and applied from the input device.
After that, the user can check if the given widget was really scrolled
by getting the Y coordinate of a child.
.. code-block:: c
int32_t y_start = lv_obj_get_y(child);
<scroll emulation>
int32_t y_end = lv_obj_get_y(child);
if(y_start + 100 != y_end) fail();
Please refer to :ref:`lv_test_indev_h` for the list of supported input device emulation functions.
Screenshot Comparison
---------------------
``bool lv_test_screenshot_compare(const char * fn_ref)`` is a useful function
to compare the content of the emulated display with reference PNG images.
This function works in a practical way:
- If the folder(s) referenced in ``fn_ref`` do not exist, they will be created automatically.
- If the reference image is not found, it will be created automatically from the rendered screen.
- If the comparison fails, an ``<image_name>_err.png`` file will be created with the rendered content next to the reference image.
- If the comparison fails, the X and Y coordinates of the first divergent pixel, along with the actual and expected colors, will also be printed.
The reference PNG images should have a **32-bit color format** and match the display size.
The test display's content will be converted to ``XRGB8888`` to simplify comparison with the reference images.
The conversion is supported from the following formats (i.e., the test display should have a color
format in this list):
- :cpp:enumerator:`LV_COLOR_FORMAT_XRGB8888`
- :cpp:enumerator:`LV_COLOR_FORMAT_ARGB8888`
- :cpp:enumerator:`LV_COLOR_FORMAT_ARGB8888_PREMULTIPLIED`
- :cpp:enumerator:`LV_COLOR_FORMAT_RGB888`
- :cpp:enumerator:`LV_COLOR_FORMAT_RGB565`
- :cpp:enumerator:`LV_COLOR_FORMAT_RGB565_SWAPPED`
- :cpp:enumerator:`LV_COLOR_FORMAT_L8`
- :cpp:enumerator:`LV_COLOR_FORMAT_AL88`
- :cpp:enumerator:`LV_COLOR_FORMAT_I1`
To read and decode PNG images and to store the converted rendered image, a few MBs of RAM are dynamically allocated using the standard ``malloc``
(not :cpp:expr:`lv_malloc`).
The screenshot comparison uses `lodepng` which is built-in to LVGL and just needs to be enabled with
``LV_USE_LODEPNG``.
To avoid making the entire Test module dependent on `lodepng`, screenshot comparison can be individually enabled by
``LV_USE_TEST_SCREENSHOT_COMPARE``.
API
***

View File

@@ -0,0 +1,7 @@
.. _xml_animations:
==========
Animations
==========
TODO

View File

@@ -0,0 +1,285 @@
.. _xml_api:
===
API
===
The ``<api>`` tag can be a child of ``<widget>`` and ``<components>`` tags, although
each supports slightly different features.
Properties
**********
Inside ``<prop>`` elements, ``<param>`` elements can be defined to describe the arguments.
For **Widgets**, all properties are optional.
If a property is not set on an instance of a Widget, it simply won't be applied,
and the created Widget's default value for that property will be used (e.g., ``text``
for a label's text).
For **Components**, all properties are mandatory; however, default values can be defined
to be used when a property is not set.
If a property has only one parameter (which is usually the case), a shorthand syntax
can be applied as shown below.
For example:
.. code-block:: xml
<api>
<prop name="range" default="0 100" help="Set the range.">
<param name="range_min" type="int" help="Sets the minimum value."/>
<param name="range_max" type="int" help="Sets the maximum value."/>
</prop>
<prop name="title" type="string" help="The title of the slider"/>
</api>
When a property is used, all parameters are set as a single attribute value. For example:
.. code-block:: xml
<my_slider range="-100 100" title="Room 1"/>
For **Widgets**, each property corresponds to a setter function.
The ``name`` in ``<prop>`` is used to build the name of the setter function like this:
.. code-block:: c
<widget_name>_set_<prop_name>(lv_obj_t * obj, <param1_type> <param1_name>, <param2_type> <param2_name>, ...);
For **Components**, the exported code contains only a single ``create`` function
to which all the properties are passed:
.. code-block:: c
<component_name>_create(lv_obj_t * parent, <param1_type> <param1_name>, <param2_type> <param2_name>, ...);
``<prop>`` elements have an optional ``<postponed>`` boolean attribute.
By default, it is ``false``, but if set to ``true``, the given property will be
applied after all children are created. A practical example is setting the current
tab of a tab view, which cannot be set before the tabs are created. This feature is
not supported yet.
``<enumdef>``
*************
This tag is used only with Widgets. It is used to define new enum types for a given
Widget. It should contain ``<enum>`` elements to define possible options.
Example:
.. code-block:: xml
<!-- my_widget.xml -->
<api>
<enumdef name="my_widget_mode" help="Possible modes">
<enum name="normal" help="Normal mode" value="0x10"/>
<enum name="inverted" help="Inverted mode"/>
</enumdef>
<prop name="mode" help="Set Widget mode">
<param name="mode" type="enum:my_widget_mode"/>
</prop>
</api>
Note that the enum values are not important because:
1. When the code is exported, the enum names will be used, and the compiler generates
its own value for each enumerator symbol.
2. When loaded from XML, the Widget's XML parser should convert the enum names to C
enum fields.
``<element>``
*************
``<element>`` tags also apply only to Widgets. Elements are used to describe
sub-Widgets or internal parts of Widgets. Examples include the list of a dropdown,
the tabs of a tab view, or the data series of a chart.
Elements can have ``<arg>`` and ``<prop>`` definitions. ``<arg>`` elements are
mandatory (default values are supported) as they are used to create the element,
whereas ``<prop>`` elements are optional as they are mapped to setter functions.
An element in a ``<view>`` can be referenced like this: ``<widget_name-element_name>``.
Note that the ``-`` separates two names inside that tag name: the Widget name and the
element name. ``-`` is not allowed in Widget and element names. Only ``_`` can be
used to separate words in tag names.
Example:
.. code-block:: xml
<my_chart-super_series color="0xff0000"/>
An important attribute of elements is ``access``. The possible values are:
- ``add``: Create any number of elements dynamically (e.g., chart series).
- ``get``: Get a pointer to an implicitly created Widget or any data (e.g., list of a Drop-Down List).
- ``set``: Select specific parts of the Widget with indexes (e.g., table cells).
Elements with ``access="add"`` or ``access="get"`` can have a custom data type
defined using ``type="my_data"``. In these cases, no children can be added. If the
``type`` is ``lv_obj``, the element can have children.
It is not yet possible to describe the ``<view>`` of elements in XML; only the API can be defined.
The actual implementation needs to be done in C.
``access="add"``
----------------
The element is explicitly created with an ``add`` function, e.g., ``lv_tabview_add_tab(obj, "Title");``.
``<arg>`` elements defined as direct children of the ``<element>`` are passed to the
``add`` function as arguments.
Example:
.. code-block:: xml
<!-- my_widget.xml -->
<api>
<element name="indicator" type="lv_obj" help="The indicator of my_widget" access="add">
<arg name="color" type="color" help="Help for color"/>
<arg name="max_value" type="int" help="Help for max_value"/>
<prop name="value" help="Set a new value for the indicator">
<param name="value" type="int" help="Help for value"/>
</prop>
</element>
</api>
<view extends="obj">
<button name="btn1"/>
</view>
In a view it can be used like this:
.. code-block:: xml
<!-- complex_widget.xml -->
<view>
<lv_label text="Title"/>
<my_widget width="100px" y="40px">
<my_widget-indicator name="indic1" color="0xff0000" max_value="120" value="30"/>
</my_widget>
</view>
From the API definition the following functions are generated:
.. code-block:: c
lv_obj_t * my_widget_add_indicator(lv_obj_t * parent, lv_color_t color, int32_t max_value);
void my_widget_set_indicator_value(lv_obj_t * obj, int32_t value);
And this is the related C file where the indicator is created:
.. code-block:: c
lv_obj_t * indic1 = my_widget_add_indicator(parent, color, max_value);
lv_my_widget_set_indicator_value(indic1, value);
``access="get"``
----------------
If the element is created internally and implicitly, it can be retrieved with a
function like ``lv_dropdown_get_list(obj);``.
``<arg>`` elements are passed to the ``get`` function as arguments.
Example:
.. code-block:: xml
<!-- my_widget.xml -->
<api>
<element name="control_button" type="lv_obj" help="A control button of my_widget" access="get">
<arg name="index" type="int" help="Zero-based index of the control button"/>
<prop name="title">
<param name="text" type="string"/>
</prop>
</element>
</api>
In a view:
.. code-block:: xml
<!-- complex_widget.xml -->
<view>
<my_widget width="100px">
<my_widget-control_button name="btn1" index="3" title="Hello"/>
</my_widget>
</view>
Generated API:
.. code-block:: c
lv_obj_t * my_widget_get_control_button(lv_obj_t * parent, int32_t index);
void my_widget_set_control_button_title(lv_obj_t * obj, const char * text);
And this is a C file where the control button is retrieved:
.. code-block:: c
lv_obj_t * btn1 = lvmy_widget_get_control_button(parent, index);
my_widget_set_control_button_title(btn1, text);
``access="set"``
----------------
The "set" value is used when elements are created automatically but need to be selected in API calls,
e.g., ``lv_table_set_cell_value(table, row, col, "text");``.
Example:
.. code-block:: xml
<!-- my_widget.xml -->
<api>
<element name="item" type="lv_obj" help="An item on my_widget" access="set">
<arg name="index" type="int" help="The zero-based index of the item"/>
<prop name="icon" help="Set the icon of an item">
<param name="icon_src" type="img_src" help="The image to set as an icon."/>
</prop>
<prop name="color" help="Set the color">
<param name="color" type="color" help="The color to set for the item."/>
</prop>
</element>
</api>
In a view:
.. code-block:: xml
<!-- complex_widget.xml -->
<view>
<my_widget width="100px">
<my_widget-item index="3" icon_src="image1" color="0xff0000"/>
</my_widget>
</view>
This is the generated header file:
.. code-block:: c
void my_widget_set_item_icon(lv_obj_t * parent, int32_t index, const void * icon_src);
void my_widget_set_item_color(lv_obj_t * parent, int32_t index, lv_color_t color);
And this is the related C file where the item properties are set:
.. code-block:: c
my_widget_set_item_icon(parent, index, image1);
my_widget_set_item_color(parent, index, color);

View File

@@ -0,0 +1,189 @@
.. _xml_component_library:
=================
Component Library
=================
Overview
********
.. |nbsp| unicode:: U+000A0 .. NO-BREAK SPACE
:trim:
The collection of Components, Widgets, Screens, Images, Fonts, etc., is called a
Component Library.
A Component Library can be fully self-sufficient, but it can also reference data from
other Component Libraries.
LVGL itself is a Component Library that supplies the built-in Widgets, data types,
etc., so typically Component Libraries use at least the core LVGL data. You can
find the XML files that describe the LVGL Widgets
`here <https://github.com/lvgl/lvgl/tree/master/xmls>`__.
A project always has at least 2 Component Libraries: that of LVGL as mentioned
above, and its own where the Screens, Components, and Widgets of the project are
defined. A project may include additional Component Libraries.
Structure
*********
A typical structure for a Component Library looks like this:
.. code-block:: none
name_of_the_component_library
├── globals.xml
├── components
│ ├── component1.xml
│ ├── component2.xml
│ └── other_folder
│ ├── component3.xml
│ └── component4.xml
├── widgets
│ ├── widget1
│ │ ├── widget1.xml
│ │ ├── widget1.c
│ │ ├── widget1.h
│ │ ├── widget1_gen.c
│ │ ├── widget1_gen.h
│ │ ├── widget1_private_gen.h
│ │ └── widget1_xml_parser.c
│ └── widget2
│ └── ...same as widget1...
├── screens
│ ├── screen1.xml
│ └── screen2.xml
├── fonts
│ ├── font1.ttf
│ └── font2.ttf
└── images
├── image1.png
└── image2.png
Visibility
**********
The content of all ``globals.xml`` files is part of a common global scope, and
any Components, Widgets or Screens defined therein can be used in all .XML files.
Styles, constants, and other data defined in the XML file of Components, Widgets, or Screens
are local to that XML file.
Thus, there are two namespaces:
1. **local namespace** within the given XML file of Components, Widgets and Screens.
2. **global namespace** created from the data in the ``globals.xml`` file from each
Component Library included.
To find names referenced in XML files, the local namespace is checked first, and if
a name is not found there, then the global namespace is checked.
The names of defined Components, Widgets and Screens become part of the global
namespace and must be unique therein. This ensures that each Component has a unique
name.
All data belonging to the LVGL core Component Library is prefixed by ``lv_``
(e.g., ``lv_label``, ``lv_font_default``).
A custom Component can be prefixed with ``watch_``, ``small_``, ``light_``, or
anything else the developer deems appropriate.
LVGL's UI |nbsp| Editor will show an error if there is a name conflict.
``globals.xml``
***************
A single ``globals.xml`` file should be created in the root directory of each
Component Library. The definitions in it do not belong to any specific Widget but
are available throughout the entire UI, Widgets, and all XML files. The valid tags
in it are:
:<config>: Can specify name and help.
:<api>: Used with ``<enumdefs>`` to show possible values for Widget or Component attributes.
:<subjects>: List of :ref:`Subjects <observer_subject>`. Can be considered the API of a Component Library.
:<consts>: Globally available constants.
:<styles>: Globally available styles.
:<fonts>: Globally available fonts.
:<images>: Globally available images.
:<const_variants>: See below.
:<style_variants>: See below.
The ``globals.xml`` must be the only ``globals.xml`` file within the Component
Library's folder tree.
From each ``globals.xml`` file, a ``<config.name>.h`` file is generated, which is
included by all generated header files --- not only in the sub-folders where
``globals.xml`` is created, but in all exported .C and .H files. This ensures that
constants, fonts, and other global data are available for all Widgets and new Widgets.
Variants
--------
``<const_variant>`` can be used by constants to create variants that can be selected at compile time.
This can be used to select a different display size, color scheme, etc.
``<style_variant>`` can be used by styles only, to modify styles at runtime. To
select the current style variant, an integer :ref:`Subject <observer_subject>` (i.e.
a Subject containing an integer value in the Observer Pattern implemented in
``lv_observer.c/.h``) ``<style_variant.name>_variant`` is created. Styles can
subscribe to this, and the style properties can be changed according to the selected
variant's integer value. (See `observer` for details about how to do this.)
All possible variants should be defined in ``globals.xml``.
Example
-------
A ``globals.xml`` file of a Component Library might look like this:
.. code-block:: xml
<globals>
<config name="mylib" help="This is my great Component Library"/>
<const_variants>
<const_variant name="size" help="Select the size">
<case name="small" help="Assets for 320x240 Screen"/>
<case name="large" help="Assets for 1280x768 Screen"/>
</const_variant>
</const_variants>
<style_variants>
<style_variant name="color" help="Select the color of the UI">
<case name="red" help="Select a red theme"/>
<case name="blue" help="Select a blue theme"/>
</style_variant>
</style_variants>
<api>
<enumdef name="mode">
<enum name="slow"/>
<enum name="fast"/>
</enumdef>
</api>
<consts>
<px name="small_unit" value="8"/>
<px name="large_unit" value="16"/>
</consts>
<styles>
<style name="card" bg_color="0xeee" radius="#small_unit" padding="12px"/>
</styles>
<images>
<file name="arrow_left" src="A:/images/arrow_left.png"/>
</images>
<fonts>
<tiny_ttf name="big" src="A:/fonts/arial.ttf" size="28"/>
</fonts>
</globals>

View File

@@ -0,0 +1,163 @@
.. _xml_components:
==========
Components
==========
Overview
********
``<component>`` the following child elements:
- ``<consts>``,
- ``<api>``,
- ``<styles>``, and
- ``<view>``.
Unlike Widgets (which are always compiled into the application), Components can either:
1. be loaded at runtime from XML, or
2. be exported to C code and compiled with the application.
Usage from Exported Code
************************
From each Component XML file, a C and H file is exported with a single function inside:
.. code-block:: c
lv_obj_t * component_name_create(lv_obj_t * parent, ...api properties...);
where 'component_name' (in the function above) is replaced by the Component's XML
file name.
When a Component is used in another Component's XML code and the code is exported,
this ``create`` function will be called. This means that Components do not have a
detailed set/get API but can be created with a fixed set of parameters.
If the user needs to access or modify values dynamically, it is recommended to use a
:ref:`Subject <observer_subject>`.
The user can also call these ``..._create()`` functions at any time from application code.
Usage from XML
**************
Registration
------------
Once a Component is created (e.g., ``my_button``), it can be registered by calling either:
- ``lv_xml_component_register_from_file("A:lvgl/examples/others/xml/my_button.xml");``
- ``lv_xml_component_register_from_data("my_button", xml_data_of_my_button);``
These registration functions process the XML data and store relevant information internally.
This is required to make LVGL recognize the Component by name.
Note that the "A:" in the above path is a file system "driver identifier letter" from
:ref:`file_system` and used accordingly. See that documentation for details.
When loaded from a file, the file name is used as the Component name.
Instantiation
-------------
After registration, a new instance of any registered Component can be created with:
.. code-block:: c
lv_obj_t * obj = lv_xml_create(lv_screen_active(), "my_button", NULL);
The created Widget is a normal LVGL Widget that can be used like any other manually-created Widget.
The last parameter can be ``NULL`` or an attribute list, like this:
.. code-block:: c
/* Can be local */
char * my_button_attrs[] = {
"x", "10",
"y", "-10",
"align", "bottom_left",
"btn_text", "New button",
NULL, NULL,
};
lv_obj_t * btn1 = lv_xml_create(lv_screen_active(), "my_button", my_button_attrs);
Parameters
**********
The properties of child elements can be adjusted, such as:
.. code-block:: xml
<lv_label x="10" text="Hello"/>
These parameters can be set to a fixed value. However, with the help of ``<prop>``
elements inside the ``<api>`` tag, they can also be passed when an instance is
created. Only the whole property can be passed, but not individual ``<param>``
elements.
Additionally, when a Component is created, it can use the extended Widget's attributes
(see ``<view extends="...">`` in the code examples below).
This means that Components inherit the API of the extended Widget as well.
The following example demonstrates parameter passing and the use of the
``text`` label property on a Component:
.. code-block:: xml
<!-- h3.xml -->
<component>
<view extends="lv_label"/>
</component>
.. code-block:: xml
<!-- red_button.xml -->
<component>
<api>
<prop type="string" name="btn_text" default="None"/>
</api>
<view extends="lv_button" style_radius="0" style_bg_color="0xff0000">
<h3 text="Some text"/>
<h3 text="$btn_text" y="40"/>
</view>
</component>
.. code-block:: c
lv_xml_component_register_from_file("A:path/to/h3.xml");
lv_xml_component_register_from_file("A:path/to/red_button.xml");
/* Creates a button with "None" text */
lv_xml_create(lv_screen_active(), "red_button", NULL);
/* Use attributes to set the button text */
const char * attrs[] = {
"btn_text", "Click here",
NULL, NULL,
};
lv_xml_create(lv_screen_active(), "red_button", attrs);
Example
*******
.. include:: ../../../examples/others/xml/index.rst
API
***

View File

@@ -0,0 +1,73 @@
.. _xml_consts:
=========
Constants
=========
Overview
********
Constants can be defined to replace any value with a selected type or to be used as special values.
The supported types are:
- ``color``
- ``px``
- ``percentage``
- ``string``
- ``opa``
- ``bool``
Usage
*****
.. code-block:: xml
<consts>
<color name="color1" value="0xff0000" help="Primary color"/>
<px name="pad_xs" value="8" help="Small padding"/>
</consts>
Constants can be used in:
- Style properties
- Widget and Component properties
And they can be used like this:
.. code-block:: xml
<styles>
<style name="style1" bg_color="#color1"/>
</styles>
Variants
********
Constants support a ``<variant>`` attribute to change the constants at compile time. For example:
.. code-block:: xml
<consts>
<px name="pad" value="8" help="General padding">
<variant name="size" case="small" value="4"/>
<variant name="size" case="large" value="12"/>
</px>
</consts>
from which the following C code can be exported:
.. code-block:: c
#if SIZE == SMALL
#define PAD 4
#elif SIZE == LARGE
#define PAD 12
#else
#define PAD 8
#endif
Where ``SMALL`` and ``LARGE`` are just preprocessor defines with incremental values.

View File

@@ -0,0 +1,59 @@
.. _xml_events:
======
Events
======
Overview
********
``<lv_event>`` tags can be added as a child of any Widget to react to user inputs or other value changes.
Right now, only a single event type is supported to call user-defined callbacks.
Usage
*****
Call function
-------------
User-defined functions can be called like this:
.. code-block:: xml
<view>
<lv_button width="200" height="100">
<lv_event-call_function callback="my_callback_1" trigger="clicked" user_data="some_text"/>
<lv_label text="Hello"/>
</lv_button>
</view>
When the XML is loaded at runtime, the callback name needs to be mapped to a function by using:
:cpp:expr:`lv_xml_register_event_cb("my_callback_1", an_event_handler)`.
The callback should have the standard LVGL event callback signature:
``void an_event_handler(lv_event_t * e);``
In the exported C code, it is assumed that there is a function with the exact name
specified as the callback name. For example, ``callback="my_callback_1"`` will be
exported as:
.. code-block:: c
void my_callback_1(lv_event_t * e); /* At the beginning of the exported file */
lv_obj_add_event_cb(obj, my_callback_1, LV_EVENT_CLICKED, "some_text");
For triggers, all LVGL event types are supported with straightforward mapping:
.. What is a trigger?
- :cpp:enumerator:`LV_EVENT_ALL`: ``"all"``
- :cpp:enumerator:`LV_EVENT_CLICKED`: ``"clicked"``
- :cpp:enumerator:`LV_EVENT_PRESSED`: ``"pressed"``
- etc.
The ``user_data`` is optional. If omitted, ``NULL`` will be passed.

View File

@@ -0,0 +1,109 @@
.. _xml_fonts:
=====
Fonts
=====
Overview
********
A ``<fonts>`` section can be added in ``globals.xml`` files.
Later, it might be supported in Components and Widgets to define local fonts and keep
the global space cleaner.
Usage
*****
The following section creates a mapping between font names and their paths with various attributes:
.. code-block:: xml
<fonts>
<bin as_file="false" name="medium" src="path/to/file.ttf" range="0x20-0x7f" symbols="°" size="24"/>
<tiny_ttf as_file="true" name="big" src_path="path/to/file.ttf" range="0x20-0x7f" symbols="auto" size="48"/>
<freetype name="chinese" src_path="file.ttf" size="48" custom_freetype_attribute="abc"/>
</fonts>
In ``<styles>`` and ``<view>``, fonts can then be referenced by their name, e.g.,
.. code-block:: xml
<style name="style1" text_font="medium"/>
The tag name determines how the font is loaded. Currently, only ``tiny_ttf as_file="true"`` is supported.
- ``bin``:
- If ``as_file="true"``: Converts the font file to ``bin`` (see `lv_font_conv`)
which will be loaded by ``lv_binfont_create()``.
- If ``as_file="false"`` (default): On export, the font file will be converted to a
C array LVGL font that can be used directly by LVGL.
- ``tiny_ttf``:
- If ``as_file="true"``: Can be loaded directly by ``lv_tiny_ttf_create_file()``.
- If ``as_file="false"`` (default): The font file will be converted to a raw C
array on export that will be loaded by ``lv_tiny_ttf_create_data()``.
- ``freetype``: The file can be loaded directly by ``lv_freetype_font_create()``.
For simplicity, if ``as_file="false"``, fonts will be loaded as files in the preview.
Setting ``as_file="false"`` affects only the C export.
If the UI is created from XML at runtime and a ``globals.xml`` is parsed, the
``as_file="false"`` tags are skipped because it is assumed that the user manually
creates the mapping. This is because the XML parser cannot automatically map an LVGL
font definition like:
.. code-block:: c
lv_font_t my_font_24;
to
.. code-block:: xml
<bin name="my_font_24"/>
Exported Code
-------------
When C code is exported, global ``const lv_font_t * <font_name>`` variables are
created, and in the initialization function of the Component Library (e.g.,
``my_lib_init_gen()``), the actual font is assigned.
.. Note: :cpp:expr: role cannot be used here because it doesn't know how to parse
the ampersand and angle brackets. An alternate approach could be to make the
arguments "style1_p, font_name", but leaving the ampersand there seems more
appropriate due to that IS the normal way to pass a style as an argument.
So it was made into a literal string instead to avoid the parsing error.
In ``lv_style_set_text_font(&style1, <font_name>)``, the created font is referenced.
Constants
---------
Constants can also be used with fonts.
.. code-block:: xml
<consts>
<int name="font_size" value="32">
<variant name="size" case="small" value="24"/>
</int>
</consts>
<fonts>
<bin name="medium" src_path="file.ttf" range="0x20-0x7f" symbols="°" size="#font_size"/>
</fonts>
Default Font
------------
``"lv_font_default"`` can be used to access ``LV_FONT_DEFAULT``. Other built-in fonts
are not exposed by default.

View File

@@ -0,0 +1,88 @@
.. _xml_images:
======
Images
======
Overview
********
.. |nbsp| unicode:: U+000A0 .. NO-BREAK SPACE
:trim:
An ``<images>`` section can be added to ``globals.xml`` files. If present, it
describes how to map images with names.
(Later, it might also be possible in Components and Widgets to define local images to
keep the global space cleaner.)
Currently ``<file>`` is the only supported child element, and ``<convert>`` is not
yet implemented.
Usage
*****
.. code-block:: xml
<images>
<file name="avatar" src_path="avatar1.png">
<convert path="raw/avatar.svg" width="100px" color_format="L8"/>
</file>
<data name="logo" src_path="logo1.png" color-format="rgb565" memory="RAM2">
<convert path="https://foo.com/image.png" width="50%" height="80%"
color_format="RGB565"/>
</data>
</images>
- ``<file>`` means that the image source is used as a file path:
- ``<data>`` means that the image is converted to a C array on export.
In both cases in the exported C code global ``const void * <image_name>`` variables are created and in the
initialization function of the Component Library (e.g. ``my_lib_init_gen()``) either the path or
the pointer to the converted :cpp:type:`lv_image_dsc_t` pointers are assigned to that variable.
In :cpp:expr:`lv_image_set_src(image, image_name)` ``image_name`` is used
instead of the path or :cpp:type:`lv_image_dsc_t` pointer.
For simplicity, in the UI |nbsp| Editor preview, images are always loaded as files.
If the UI is created from XML at runtime and a ``globals.xml`` is parsed, the ``<data>`` tags are skipped
because it is assumed that the user manually created the mapping. This is because the XML parser cannot
automatically map an image like:
.. code-block:: c
lv_image_dsc_t my_logo;
to
.. code-block:: xml
<data name="my_logo"/>
Constants
---------
Constants can be used with images as well.
.. code-block:: xml
<consts>
<int name="icon_size" value="32">
<variant name="size" case="small" value="16"/>
</int>
</consts>
<images>
<data name="icon_apply" src_path="apply.png">
<convert path="raw/apply.png" width="#icon_size"/>
</data>
</images>

View File

@@ -0,0 +1,31 @@
.. _xml_component:
====================
XML - Declarative UI
====================
.. toctree::
:maxdepth: 1
intro
component_library
project
syntax
components
screens
widgets
preview
api
styles
consts
view
fonts
images
events
subjects
animations
translations

View File

@@ -0,0 +1,217 @@
.. _xml_intro:
============
Introduction
============
Overview
********
.. |nbsp| unicode:: U+000A0 .. NO-BREAK SPACE
:trim:
LVGL is capable of loading UI elements written in XML. The XML file can be written by hand, but
it's highly recommended to use LVGL's UI |nbsp| Editor to write the XML files. The UI |nbsp| Editor provides
features like:
- Instant preview the XML files
- Autocomplete and Syntax highlighting
- Online preview for collaboration and testing
- `Figma <https://www.figma.com/>`__ integration to easily reimplement the designs
.. warning::
The UI |nbsp| Editor and the XML loader are still under development and not production ready.
Consider them as an open beta, or experimental features.
Describing the UI in XML in a declarative manner offers several advantages:
- XML files can be loaded at runtime (e.g., from an SD card) to change the application build.
- XML is simpler to write than C, enabling people with different skill sets to create LVGL UIs.
- XML is textual data, making it easy to parse and manipulate with a large number of programming and scripting languages.
- XML can be used to generate LVGL code in any language.
- XML helps to separate the view from the internal logic.
Currently supported features:
- Load XML Components at runtime from file or any type of input
- Nest Components and Widgets to any depth
- Dynamically create instances of XML Components in C
- Register images and font that can be accessed by name later in the XML data (only from
file, no C file is generated for images and fonts)
- Constants are working for Widget- and style-properties
- Parameters can be defined, passed and used for Components
- Most of the built-in Widgets are supported, even the complex ones (``label``, ``slider``,
``bar``, ``button``, ``chart``, ``scale``, ``button matrix``, ``table``, etc.)
- Style sheets and local styles can be assigned to parts and states supporting
almost all style properties
Limitations:
- Screens are not supported yet (only Components)
- Events are not supported yet.
- Animations are not supported yet.
- Observer pattern Subjects are not supported yet.
- The documentation is not complete yet.
Concept
*******
The XML files are Component-oriented. To be more specific, they are ``Component-Library`` oriented.
That is, they are structured in a way to make it easy to create reusable Component Libraries.
For example, a company can have a Component Library for the basic Widgets for all its products
(smart home, smart watch, smart oven, etc.), and create other industry-specific Libraries
(smart-home specific, smart-watch specific, etc.) containing only a few extra Widgets.
These Component Libraries are independent, can be reused across many products, and
can be freely versioned and managed.
Imagine a Component Library as a collection of XML files, images, fonts, and other
assets stored in a git repository, which can be a submodule in many projects.
If someone finds a bug in the Component Library, they can just fix it in just one
place and push it back to the git repository so that other projects can be updated
from it.
The built-in Widgets of LVGL are also considered a ``Component Library`` which is
always available.
A UI |nbsp| Editor project can have any number of Component Libraries and will always have
at least 2:
- LVGL's built-in Widgets, and
- XML-based definitions of Screen contents, along with other project-specific Components.
Widgets, Components, and Screens
********************************
It is important to distinguish between :dfn:`Widgets` and :dfn:`Components`.
Widgets
-------
:dfn:`Widgets` are the core building blocks of the UI and are not meant to be loaded at runtime
but rather compiled into the application. The main characteristics of Widgets are:
- In XML, they start with a ``<widget>`` root element.
- They are similar to LVGL's built-in Widgets.
- They are built using ``lv_obj_class`` objects.
- They have custom and complex logic inside.
- They cannot be loaded from XML at runtime because the custom code cannot be loaded.
- They have a large API with ``set/get/add`` functions.
- They can themselves contain Widgets as children (e.g., ``Tabview``'s tabs, ``Dropdown``'s lists).
Any handwritten Widget can be accessed from XML by:
1. Defining its API in an XML file.
2. Writing and registering an XML parser for it.
`See some examples here. <https://github.com/lvgl/lvgl/tree/master/src/others/xml/parsers>`__
Components
----------
:dfn:`Components` are built from other Components and Widgets, and can be loaded at runtime.
The main characteristics of Components are:
- In XML, they start with a ``<component>`` root element.
- They are built in XML only and cannot have custom C code.
- They can be loaded from XML at runtime as they describe only visual aspects of the UI.
- They are built from Widgets or other Components.
- They can be used for styling Widgets.
- They can contain (as children) Widgets or other Components.
- They can have a simple API to pass properties to their children (e.g., ``btn_text`` to a Label's text).
Regardless of whether the XML was written manually or by the UI |nbsp| Editor, the XML files
of Components can be registered in LVGL, and after that, instances can be created.
In other words, LVGL can just read the XML files, "learn" the Components from them, and
thereafter create Components as part of a :ref:`Screen's <screens>` :ref:`Widget Tree
<basic_data_flow>` according to their structure.
:dfn:`Screens` are similar to Components:
- In XML, they start with a ``<screen>`` root element.
- They are built from Widgets or other Components to describe the :ref:`Screen <screens>`.
- They can be loaded from XML at runtime as they describe only visual aspects of the UI.
- They do not have an API.
- They can be referenced in Screen load events.
Syntax Teaser
*************
Each Widget or Component XML file describes a single Widget or Component. The root
element for Widgets, Components, and Screens are ``<widget>``, ``<component>`` and
``<screen>`` respectively. Other than that, the contained XML elements are almost
identical. This is a high-level overview of the most important XML elements that
will be children of these root elements:
:<api>: Describes the properties that can be ``set`` for a Widget or Component.
Properties can be referenced ysubg ``$``. For Widgets, custom enums can
also be defined with the ``<enumdef>`` tag.
:<consts>: Specifies constants (local to the Widget or Component) for colors, sizes,
and other values. Constant values can be referenced using ``#``.
:<styles>: Describes style (``lv_style_t``) objects that can be referenced (and
shared) by Widgets and Components later.
:<view>: Specifies the appearance of the Widget or Component by describing the
children and their properties.
This is a simple example illustrating what an LVGL XML Component looks like.
Note that only the basic features are shown here.
.. code-block:: xml
<component>
<consts>
<px name="size" value="100"/>
<color name="orange" value="0xffa020"/>
</consts>
<api>
<prop name="btn_text" default="Apply" type="string"/>
</api>
<styles>
<style name="blue" bg_color="0x0000ff" radius="2"/>
<style name="red" bg_color="0xff0000"/>
</styles>
<view extends="lv_button" width="#size" styles="blue red:pressed">
<my_h3 text="$btn_text" align="center" color="#orange" style_text_color:checked="0x00ff00"/>
</view>
</component>
Usage Teaser
************
LVGL's UI |nbsp| Editor can be used in two different ways.
Export C and H Files
--------------------
The Widgets, Components, images, fonts, etc., can be converted to .C/.H files with
plain LVGL code. The exported code works the same way as if it was written by the
user. In this case, the XML files are not required anymore unless modifications may
be made later. The XML files were used only during editing/implementing the Widgets
and Components to save recompilation time and optionally leverage other useful
UI |nbsp| Editor features.
Load the UI from XML
--------------------
Although the Widgets' code always needs to be exported in C and compiled into the
application (just like the built-in LVGL Widgets are also part of the application), the Components'
XML can be loaded and any number of instances can be created at runtime. In the simplest case,
a Component can be registered with :cpp:expr:`lv_xml_component_register_from_file(path)` and
an instance can be created with :cpp:expr:`lv_obj_t * obj = lv_xml_create(parent, "my_button", NULL)`.

View File

@@ -0,0 +1,56 @@
.. _xml_preview:
=======
Preview
=======
Overview
********
.. |nbsp| unicode:: U+000A0 .. NO-BREAK SPACE
:trim:
In ``<component>`` and ``<widget>``, it is possible to define ``<preview>`` tags.
These are **not** exported to code and are **not** loaded from XML.
They are used only by the UI |nbsp| Editor to describe the context of the Component.
For example, you might want to:
- Change the background of the Editor's preview to dark.
- Center the Component.
- Set margins.
- Change the size of the preview.
Usage
*****
You can think of a ``<preview>`` tag as an ``lv_obj`` where the following properties can be used:
- ``width``, ``height``
- Any local style properties, for example, ``style_bg_color="0x333"``
- ``flex`` and ``flex_flow``
It is also possible to define multiple previews, and in the UI |nbsp| Editor, you can
select one of them.
Example
*******
.. code-block:: xml
<component>
<previews>
<preview name="small_dark" width="content" height="100" style_bg_color="0x333" style_pad_all="32"/>
<preview name="centered" width="200" height="100" flex="row center"/>
<preview name="large_light" width="1980" height="1080" style_bg_color="0xeeeeee"/>
</previews>
<view>
...
</view>
</component>

View File

@@ -0,0 +1,55 @@
.. _xml_project:
=======
Project
=======
Overview
********
.. |nbsp| unicode:: U+000A0 .. NO-BREAK SPACE
:trim:
A single ``project.xml`` file should be created for each project where the following
content is specified:
:<folders>: Specifies the path to Component Libraries. LVGL's base Widgets are
always loaded automatically.
:<targets>: Describes various hardware configurations, allowing the UI |nbsp| Editor
to check if the UI is out of resources and to select different previews
for each Screen according to the specified displays.
Example
*******
.. code-block:: xml
<project>
<folders>
<folder path="../widget_lib1"/>
<folder path="/home/user/work/ui_libs/modern"/>
<folder path="https://github.com/user/repo"/>
</folders>
<targets>
<renesas-RA8D1-EK gpu="true"/>
<target name="small">
<display width="320" height="240" color_format="RGB565"/>
<memory name="int_ram" size="128K"/>
<memory name="ext_ram" size="2M"/>
<memory name="int_flash" size="512K"/>
<memory name="ext_flash" size="32M"/>
</target>
<target name="large">
<display width="1024" height="768" color_format="XRGB8888"/>
<memory name="int_ram" size="128K"/>
<memory name="ext_ram" size="2M"/>
<memory name="int_flash" size="512K"/>
<memory name="ext_flash" size="32M"/>
</target>
</targets>
</project>

View File

@@ -0,0 +1,63 @@
.. _xml_screens:
=======
Screens
=======
Overview
********
.. |nbsp| unicode:: U+000A0 .. NO-BREAK SPACE
:trim:
Screens work very similarly to Components. Both can be:
- Loaded from XML
- Contain Widgets and Components as children
- Have ``<styles>``
- Have ``<consts>``
- Have a ``<view>``
However, Screens **cannot** have an ``<api>``.
Usage
*****
Each XML file describes a :ref:`Screen <screens>`. The name of the XML file will
also be the name of the Screen.
In the ``project.xml`` file, multiple ``<display>`` elements can be defined. In the
UI |nbsp| Editor, when a Screen is being developed, the user can select from all the
defined displays in the Preview, and the Screen will be shown with the given
resolution and color depth.
This is useful for verifying responsive designs.
Example
*******
.. code-block:: xml
<screen>
<consts>
<string name="title" value="Main menu"/>
</consts>
<styles>
<style name="dark" bg_color="0x333"/>
</styles>
<view>
<header label="#title"/>
<selector_container styles="dark">
<button text="Weather" icon="cloudy"/>
<button text="Messages" icon="envelope"/>
<button text="Settings" icon="cogwheel"/>
<button text="About" icon="questionmark"/>
</selector_container>
</view>
</screen>

View File

@@ -0,0 +1,127 @@
.. _xml_styles:
======
Styles
======
Overview
********
In XML files, both style sheets (:cpp:expr:`lv_style_t`) and local styles can be used.
Style variants are also supported to change style properties at runtime.
Style Sheets
************
In the ``<styles>`` section, styles and their properties can be defined like this:
.. code-block:: xml
<style name="red"
help="What is this style about?"
border_width="2px"
border_color="0xff0000"/>
Styles can be referenced like this in the ``<view>``:
.. code-block:: xml
<view>
<slider styles="main red:indicator red:knob:focused"/>
</view>
As shown in the example, parts and states are appended after a ``:`` to the style's name.
Local Styles
************
Local styles can be used directly in a Widget, for example:
.. code-block:: xml
<lv_label style_bg_opa="200" style_bg_opa:disabled="100"/>
Gradients
*********
Before the ``<styles>`` tag, the ``<gradients>`` tag can be used to describe various
gradients, which can later be referenced in styles.
When a gradient is created, it can be referenced by its name, like:
.. code-block:: xml
<style bg_grad="grad1"/>
or
.. code-block:: xml
<lv_button style_bg_grad="grad1"/>
Horizontal or Vertical Gradient
-------------------------------
To define a simple ``<horizontal>`` or ``<vertical>`` gradients:
.. code-block:: xml
<gradients>
<horizontal name="grad1">
<stop color="#ff0000" offset="20%" opa="40%"/>
<stop color="#00ff00" offset="128" opa="100%"/>
</horizontal>
</gradients>
Linear Gradient
---------------
To define a skewed gradient from two points:
.. code-block:: xml
<gradients>
<linear name="grad1" start="50 50" end="100 80">
<stop color="#ff0000" offset="20%" opa="100%"/>
<stop color="#00ff00" offset="240" opa="100%"/>
</linear>
</gradients>
Radial Gradient
---------------
To define a radial gradient:
.. code-block:: xml
<gradients>
<radial name="grad1" center="100 50%" edge="200 50" focal_center="50 80%" focal_edge="55 80%">
<stop color="#ff0000" opa="100%"/>
<stop color="#00ff00" opa="100%"/>
</radial>
</gradients>
Conical Gradient
----------------
To define a conical gradient:
.. code-block:: xml
<gradients>
<conical name="grad1" center="80 50%" angle="45 270">
<stop color="#ff0000" opa="100%"/>
<stop color="#00ff00" opa="100%"/>
</conical>
</gradients>

View File

@@ -0,0 +1,53 @@
.. _xml_subjects:
========
Subjects
========
To connect values of the Widget internally or to external data, :ref:`Subjects
<observer_subject>` can be used. For example, an internally connected value could be
a slider's value mapped to a label. Externally connected data could be the current
number of users shown on a label.
To handle internal connections, local Subjects can be created like this:
.. code-block:: xml
<subjects>
<int name="a" value="20"/>
<string name="b" value="Hello"/>
<group name="a_and_b" value="a b"/>
</subjects>
These Subjects can be used in Widget APIs like:
.. code-block:: xml
<view>
<label bind_text="a 'Progress: %d'"/>
</view>
When generating code, the Subjects are saved in the Widget's data and are used like this:
.. code-block:: c
lv_subject_init_int(&my_widget->subject_a, 20);
lv_subject_init_string(&my_widget->subject_b, "Hello");
my_widget->subject_a_and_b_list = lv_malloc(sizeof(lv_subject_t *) * 2);
my_widget->subject_a_and_b_list[0] = &my_widget->subject_a;
my_widget->subject_a_and_b_list[1] = &my_widget->subject_b;
lv_subject_init_group(&my_widget->subject_a_and_b, my_widget->subject_a_and_b_list);
If the connection is more complex and not supported out of the box, it can be handled from code.
External Subjects are defined in the API of the Widget:
.. code-block:: xml
<api>
<prop name="bind_value" help="">
<param name="subject" type="subject" help=""/>
<param name="max_value" type="int" help="Just another parameter, e.g., to limit the value"/>
</prop>
</api>

View File

@@ -0,0 +1,171 @@
.. _xml_syntax:
======
Syntax
======
Naming conventions
******************
.. |nbsp| unicode:: U+000A0 .. NO-BREAK SPACE
:trim:
- A standard XML syntax is used.
- Lowercase letters with ``_`` separation are used for attribute names.
- Tag names follow the usual variable-name rules: they must start with a letter or
``'_'`` and the rest of the name may be comprised of letters, ``'_'`` and digits.
- The LVGL API is followed as much as possible, e.g., ``align="center"``, ``bg_color="0xff0000"``.
- For colors, all these syntaxes are supported (similar to CSS colors): ``0x112233``,
``#112233``, ``112233``, ``0x123``, ``#123``, ``123``. Note that like CSS,
``0x123``, ``#123`` and ``123`` all mean ``#112233``.
- ``params`` can be referenced with ``$``.
- ``consts`` can be referenced with ``#``.
- ``styles`` can be attached to states and/or parts like this:
``styles="style1 style2:pressed style3:focused:scrollbar"``.
- Local styles (i.e. styles that are stored within the Component and thus not shared
by any other Components) can be used like this:
``<lv_label style_text_color="0xff0000" style_text_color:checked="0x00ff00"/>``.
Types
*****
All of the types can be used as API property types, but only a subset of them can be
used as constant and :ref:`Subject <observer_subject>` types.
Simple types
------------
The following simple built-in types are supported:
:bool: a ``true`` or ``false``.
:int: Integer number in the range of roughly -2B to 2B by default.
(Same as ``int32_t`` in C.)
:px: Simple pixel units. The unit ``px`` can be omitted.
:%: Percentage. ``%`` must be appended to the value as the unit.
(Means the same as :cpp:expr:`lv_pct()`.)
:content: Means ``LV_SIZE_CONTENT``.
:string: Simple NUL-terminated string. When multiple strings are used in a
property or string array, ``'`` should be used. E.g. ``foo="'a' 'b'"``.
:color: A color stored as 24-bit RGB (:cpp:expr:`lv_color_t`).
:opa: Opacity value in the range of 0 to 255 or 0 to 100%. Like CSS,
percentage values must be be followed by '%'.
:lv_obj: Pointer to a Widget (:cpp:expr:`lv_obj_t *`).
:point: A point with ``x`` and ``y`` values (:cpp:expr:`lv_point_t`).
:arglist: List all parameters as arguments. Supports only ``int`` and
``string``. E.g. ``foo="1 23 'hello' 'a'"``.
Name-based types
----------------
In XML files, fonts, images, styles, etc., are not used by pointer but by string
names. For example, a style is defined like ``<style name="red" bg_color="0xff0000"/>``.
Later, they can be referenced by their names.
This means that the actual values need to be bound to the names when the UI is loaded
from XML, otherwise, LVGL wouldn't know what a name means.
Most of these connections are done automatically (e.g., for styles, fonts, images,
animations, gradients, etc.), but others need to be connected manually (e.g., event
callbacks where the callback itself is available only in the code).
For fonts and images, the connections are created automatically if the source is a file.
If the font or image is compiled into the application (as a C array), the user needs
to specify which variable a given name refers to.
To create these connections, functions like
- ``lv_xml_register_image(&ctx, name, pointer)``
- ``lv_xml_register_font(&ctx, name, pointer)``
- ``lv_xml_register_event_cb(&ctx, name, callback)``
- etc.
can be used. Later, a pointer to the object can be retrieved by
- ``lv_xml_get_image(&ctx, name)``
- ``lv_xml_get_font(&ctx, name)``
- ``lv_xml_get_event_cb(&ctx, name)``
- etc.
:style: Name of a style. :cpp:expr:`lv_xml_get_style_by_name(&ctx, name)` returns an :cpp:expr:`lv_style_t *`.
:font: Name of a font. :cpp:expr:`lv_xml_get_font(&ctx, name)` returns an :cpp:expr:`lv_font_t *`.
:image: Name of an image. :cpp:expr:`lv_xml_get_image(&ctx, name)` returns an :cpp:expr:`const void *`,
which can be :cpp:expr:`lv_image_dsc_t *` or a NUL-terminated string path to a file.
:animation: Name of an animation descriptor. :cpp:expr:`lv_xml_get_anim(&ctx, name)` returns an :cpp:expr:`lv_anim_t *`.
:subject: Name of a :ref:`Subject <observer_subject>`. :cpp:expr:`lv_xml_get_subject(&ctx, name)` returns an :cpp:expr:`lv_subject_t *`.
:grad: Name of a gradient. :cpp:expr:`lv_xml_get_grad(&ctx, name)` returns an :cpp:expr:`lv_grad_dsc_t *`.
:event_cb: Name of an event callback. :cpp:expr:`lv_xml_get_event_cb(&ctx, name)` returns an :cpp:expr:`lv_event_cb_t`.
Arrays
------
An array of any type can be defined in four ways:
:int[N]: An integer array with ``N`` elements.
:string[...NULL]: An array terminated with a ``NULL`` element. ``NULL`` can be
replaced by any value, e.g., ``grid_template_last``.
:string[5]: An array that must have exactly 5 elements.
:string[]: No ``NULL`` termination and no count parameter, used when the
number of elements is not known or delivered via another
mechanism, such as via a function parameter.
Enums
-----
``<enumdef>`` can be used in the ``<api>`` tags to create custom enums for
**Widgets**. This is not supported for Components.
For example:
.. code-block:: xml
<api>
<enumdef name="my_widget_mode" help="Possible modes" help-zh="Chinese help">
<enum name="normal" help="Normal mode" help-zh="Normal mode in Chinese" value="0x10"/>
<enum name="inverted" help="Inverted mode"/>
</enumdef>
<prop name="mode" help="help">
<param name="mode" type="enum:my_widget_mode" help="help"/>
</prop>
</api>
When used as a type, a ``+`` suffix means multiple values can be selected and ORed.
For example: ``type="axis+"``. In this case, the options should be separated by
``|``, for example: ``axis=primary_x|secondary_y``.
Compound types
--------------
Types can be compound, meaning multiple options/types are possible. For example, for
width: ``type="px|%|content"``.
Limiting accepted values
------------------------
It is also possible to limit the possible options the user can select from an enum.
For example:
- Enums: ``type="dir(top bottom)"``
- Colors: ``type="color(0xff0000 0x00ff00 0x0000ff)"``
- Strings: ``type="string('Ok' 'Cancel')``
These are checked in the UI |nbsp| Editor, and if an invalid option is selected, it
will be highlighted as an error.

View File

@@ -0,0 +1,7 @@
.. _xml_translations:
============
Translations
============
TODO

View File

@@ -0,0 +1,7 @@
.. _xml_view:
=========
View
=========
TODO

View File

@@ -0,0 +1,120 @@
.. _xml_widgets:
=======
Widgets
=======
Overview
********
.. |nbsp| unicode:: U+000A0 .. NO-BREAK SPACE
:trim:
Widgets are written in C and compiled into the application.
They can be referenced from Components, and their API can be used via the exposed attributes
(e.g., label text or slider value).
Using the UI |nbsp| Editor, all the following .C/.H files can be exported from the XML of the Widgets:
:<widget_name>_gen.h: Contains the generated API implementation of the widget
(overwritten on each code export).
:<widget_name>_private_gen.h: Contains private API and the data for the widget
(overwritten on each code export).
:<widget_name>_gen.c: Contains the internals of the Widget (overwritten on
each code export).
:<widget_name>.h: Includes ``<widget_name>_gen.h`` and allows the user to
define custom APIs. Only a skeleton is exported once.
:<widget_name>.c: Contains hooks from ``<widget_name>_gen.c`` and allows
the user to write custom code. Only a skeleton is
exported once.
:<widget_name>_xml_parser.c: Processes the XML strings and calls the required
functions according to the set attributes. Only a
skeleton is exported once.
Usage
*****
XML Parser
----------
To make the Widgets accessible from XML, an XML parser needs to be created and
registered for each Widget. The XML parser for the label Widget looks like this:
.. code-block:: c
void * lv_xml_label_create(lv_xml_parser_state_t * state, const char ** attrs)
{
/* Create the label */
void * obj = lv_label_create(lv_xml_state_get_parent(state));
return obj;
}
void lv_xml_label_apply(lv_xml_parser_state_t * state, const char ** attrs)
{
void * obj = lv_xml_state_get_item(state);
/* Apply the common properties, e.g., width, height, styles, flags, etc. */
lv_xml_obj_apply(state, attrs);
/* Process the label-specific attributes */
for(int i = 0; attrs[i]; i += 2) {
const char * name = attrs[i];
const char * value = attrs[i + 1];
if(lv_streq("text", name)) lv_label_set_text(obj, value);
if(lv_streq("long_mode", name)) lv_label_set_long_mode(obj, long_mode_text_to_enum(value));
}
}
/* Helper to convert the string to enum values */
static lv_label_long_mode_t long_mode_text_to_enum(const char * txt)
{
if(lv_streq("wrap", txt)) return LV_LABEL_LONG_WRAP;
if(lv_streq("scroll", txt)) return LV_LABEL_LONG_SCROLL;
LV_LOG_WARN("%s is an unknown value for label's long_mode", txt);
return 0; /* Return 0 in the absence of a better option. */
}
A Widget XML processor can be registered like this:
.. code-block:: c
lv_xml_widget_register("lv_label", lv_xml_label_create, lv_xml_label_apply);
After registration, a Widget can be created like this:
.. code-block:: c
const char * attrs[] = {
"text", "Click here",
"align", "center",
NULL, NULL,
};
lv_xml_create(lv_screen_active(), "lv_label", attrs);
LVGL automatically registers its built-in Widgets, so only custom Widgets need to be
registered manually.
Adding Custom Code
------------------
``<widget_name>.c`` contains three hooks:
- **Constructor hook**: Called when the Widget and all its children are created. Any
modifications can be done on the children here.
- **Destructor hook**: Called when the Widget is deleted. All manually allocated
memory needs to be freed here.
- **Event hook**: Called at the beginning of the Widget's event callback to perform
any custom action.
In this C file, the ``set`` functions for each API ``<prop>`` also need to be
implemented. The declaration of these functions is automatically exported in
``<widget_name>_gen.h``.
Besides these, any custom code and functions can be freely implemented in this file.

View File

@@ -0,0 +1,682 @@
.. _widget_basics:
=============
Widget Basics
=============
What is a Widget?
*****************
A Widget is the **basic building block** of the LVGL user interface.
Examples of Widgets: :ref:`Base Widget (and Screen) <base_widget>`,
:ref:`Button <lv_button>`, :ref:`Label <lv_label>`,
:ref:`Image <lv_image>`, :ref:`List <lv_list>`,
:ref:`Chart <lv_chart>` and :ref:`Text Area <lv_textarea>`.
See :ref:`widgets` to see all Widget types.
All Widgets are referenced using an :cpp:type:`lv_obj_t` pointer as a handle.
This pointer can later be used to read or change the Widget's attributes.
.. _widget_attributes:
Attributes
**********
Basic attributes
----------------
All Widget types share some basic attributes:
- Position
- Size
- Parent
- Styles
- Events it emits
- Flags like *Clickable*, *Scollable*, etc.
- Etc.
You can set/get these attributes with ``lv_obj_set_...`` and
``lv_obj_get_...`` functions. For example:
.. code-block:: c
/* Set basic Widget attributes */
lv_obj_set_size(btn1, 100, 50); /* Set a button's size */
lv_obj_set_pos(btn1, 20,30); /* Set a button's position */
For complete details on position, size, coordinates and layouts, see :ref:`coord`.
Widget-specific attributes
--------------------------
The Widget types have special attributes as well. For example, a slider has
- Minimum and maximum values
- Current value
For these special attributes, every Widget type may have unique API
functions. For example for a slider:
.. code-block:: c
/* Set slider specific attributes */
lv_slider_set_range(slider1, 0, 100); /* Set the min. and max. values */
lv_slider_set_value(slider1, 40, LV_ANIM_ON); /* Set the current value (position) */
The API of the widgets is described in their
:ref:`Documentation <widgets>` but you can also check the respective
header files (e.g. *widgets/lv_slider.h*)
.. _lv_obj_parents_and_children:
Parents and children
--------------------
A Widget's parent is set when the widget is created --- the parent is passed to the
creation function.
To get a Widget's current parent, use :cpp:expr:`lv_obj_get_parent(widget)`.
You can move the Widget to a new parent with :cpp:expr:`lv_obj_set_parent(widget, new_parent)`.
To get a specific child of a parent use :cpp:expr:`lv_obj_get_child(parent, idx)`.
Some examples for ``idx``:
- ``0`` get the child created first
- ``1`` get the child created second
- ``-1`` get the child created last
You can iterate through a parent Widget's children like this:
.. code-block:: c
uint32_t i;
for(i = 0; i < lv_obj_get_child_count(parent); i++) {
lv_obj_t * child = lv_obj_get_child(parent, i);
/* Do something with child. */
}
:cpp:expr:`lv_obj_get_index(widget)` returns the index of the Widget in its parent.
It is equivalent to the number of older children in the parent.
You can bring a Widget to the foreground or send it to the background with
:cpp:expr:`lv_obj_move_foreground(widget)` and :cpp:expr:`lv_obj_move_background(widget)`.
You can change the index of a Widget in its parent using :cpp:expr:`lv_obj_move_to_index(widget, index)`.
You can swap the position of two Widgets with :cpp:expr:`lv_obj_swap(widget1, widget2)`.
To get a Widget's Screen (highest-level parent) use :cpp:expr:`lv_obj_get_screen(widget)`.
.. _widget_working_mechanisms:
Working Mechanisms
******************
Parent-child structure
----------------------
A parent Widget can be considered as the container of its children. Every Widget has
exactly one parent Widget (except Screens), but a parent Widget can have any number
of children. There is no limitation for the type of the parent but there are Widgets
which are typically a parent (e.g. button) or a child (e.g. label).
Moving together
---------------
If the position of a parent changes, the children will move along with
it. Therefore, all positions are relative to the parent.
.. image:: /_static/images/par_child1.png
.. code-block:: c
lv_obj_t * parent = lv_obj_create(lv_screen_active()); /* Create a parent Widget on current Screen */
lv_obj_set_size(parent, 100, 80); /* Set size of parent */
lv_obj_t * widget1 = lv_obj_create(parent); /* Create a Widget on previously created parent Widget */
lv_obj_set_pos(widget1, 10, 10); /* Set position of new Widget */
Modify the position of the parent:
.. image:: /_static/images/par_child2.png
.. code-block:: c
lv_obj_set_pos(parent, 50, 50); /* Move the parent. The child will move with it. */
(For simplicity the adjusting of colors of the Widgets is not shown in
the example.)
Visibility only on the parent
-----------------------------
If a child is partially or fully outside its parent then the parts
outside will not be visible.
.. image:: /_static/images/par_child3.png
.. code-block:: c
lv_obj_set_x(widget1, -30); /* Move the child a little bit off the parent */
This behavior can be overwritten with
:cpp:expr:`lv_obj_add_flag(widget, LV_OBJ_FLAG_OVERFLOW_VISIBLE)` which allow the
children to be drawn out of the parent. 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.
.. code-block:: c
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*/
}
Creating and deleting Widgets
-----------------------------
In LVGL, Widgets can be created and deleted dynamically at run time. It
means only the currently created (existing) Widgets consume RAM.
This allows for the creation of a Screen just when a button is clicked
to open it, and for deletion of Screens when a new Screen is loaded.
UIs can be created based on the current environment of the device. For
example one can create meters, charts, bars and sliders based on the
currently attached sensors.
Every widget has its own **create** function with a prototype like this:
.. code-block:: c
lv_obj_t * lv_<widget>_create(lv_obj_t * parent, <other parameters if any>);
Typically, the create functions only have a ``parent`` parameter telling
them on which Widget to create the new Widget.
The return value is a pointer to the created Widget with :cpp:type:`lv_obj_t` ``*``
type.
There is a common **delete** function for all Widget types. It deletes
the Widget and all of its children.
.. code-block:: c
void lv_obj_delete(lv_obj_t * widget);
:cpp:func:`lv_obj_delete` will delete the Widget immediately. If for any reason you
can't delete the Widget immediately you can use
:cpp:expr:`lv_obj_delete_async(widget)` which will perform the deletion on the next
call of :cpp:func:`lv_timer_handler`. This is useful e.g. if you want to
delete the parent of a Widget in the child's :cpp:enumerator:`LV_EVENT_DELETE`
handler.
You can remove all the children of a Widget (but not the Widget itself)
using :cpp:expr:`lv_obj_clean(widget)`.
You can use :cpp:expr:`lv_obj_delete_delayed(widget, 1000)` to delete a Widget after
some time. The delay is expressed in milliseconds.
Sometimes you're not sure whether a Widget was deleted and you need some way to
check if it's still "alive". Anytime before the Widget is deleted, you can use
cpp:expr:`lv_obj_null_on_delete(&widget)` to cause your Widget pointer to be set to ``NULL``
when the Widget is deleted.
Make sure the pointer variable itself stays valid until the Widget is deleted. Here
is an example:
.. code:: c
void some_timer_callback(lv_timer_t * t)
{
static lv_obj_t * my_label;
if(my_label == NULL) {
my_label = lv_label_create(lv_screen_active());
lv_obj_delete_delayed(my_label, 1000);
lv_obj_null_on_delete(&my_label);
}
else {
lv_obj_set_x(my_label, lv_obj_get_x(my_label) + 1);
}
}
.. _screens:
Screens
*******
What are Screens?
-----------------
Not to be confused with a :ref:`display`, Screens are simply any Widget created
without a parent (i.e. passing NULL for the ``parent`` argument during creation). As
such, they form the "root" of a Widget Tree.
Normally the Base Widget is used for this purpose since it has all the features most
Screens need. But an :ref:`lv_image` Widget can also be used to create a wallpaper
background for the Widget Tree.
All Screens:
- are automatically attached to the :ref:`default_display` current when the Screen
was created;
- automatically occupy the full area of the associated display;
- cannot be moved, i.e. functions such as :cpp:func:`lv_obj_set_pos` and
:cpp:func:`lv_obj_set_size` cannot be used on screens.
Each :ref:`display` object can have multiple screens associated with it, but not vice
versa. Thus the relationship::
Display
|
--- (one or more)
/|\
Screen Widgets (root of a Widget Tree)
|
O (zero or more)
/|\
Child Widgets
Creating Screens
----------------
Screens are created like this:
.. code-block:: c
lv_obj_t * scr1 = lv_obj_create(NULL);
Screens can be deleted with :cpp:expr:`lv_obj_delete(scr)`, but be sure you do not
delete the :ref:`active_screen`.
.. _active_screen:
Active Screen
-------------
While each :ref:`display` object can have any number of Screens Widgets associated
with it, only one of those Screens is considered "Active" at any given time. That
Screen is referred to as the Display's "Active Screen". For this reason, only one
Screen and its child Widgets will ever be shown on a display at one time.
When each :ref:`display` object was created, a default screen was created with it and
set as its "Active Screen".
To get a pointer to the "Active Screen", call :cpp:func:`lv_screen_active`.
To set a Screen to be the "Active Screen", call :cpp:func:`lv_screen_load` or
:cpp:func:`lv_screen_load_anim`.
.. _loading_screens:
Loading Screens
---------------
To load a new screen, use :cpp:expr:`lv_screen_load(scr1)`. This sets ``scr1`` as
the Active Screen.
Load Screen with Extended Options
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
There is a way to load screens that gives you 2 additional (extended) options,
allowing the caller to specify:
- an optional transition method, and
- an option to gracefully delete the screen that was being displayed.
:cpp:expr:`lv_screen_load_anim(scr, transition_type, time, delay, auto_del)`. The
following transition types exist:
- :cpp:enumerator:`LV_SCR_LOAD_ANIM_NONE`: Switch immediately after ``delay`` milliseconds
- :cpp:enumerator:`LV_SCR_LOAD_ANIM_OVER_LEFT`, :cpp:enumerator:`LV_SCR_LOAD_ANIM_OVER_RIGHT`, :cpp:enumerator:`LV_SCR_LOAD_ANIM_OVER_TOP` and :cpp:enumerator:`LV_SCR_LOAD_ANIM_OVER_BOTTOM`: Move the new screen over the current towards the given direction
- :cpp:enumerator:`LV_SCR_LOAD_ANIM_OUT_LEFT`, :cpp:enumerator:`LV_SCR_LOAD_ANIM_OUT_RIGHT`, :cpp:enumerator:`LV_SCR_LOAD_ANIM_OUT_TOP` and :cpp:enumerator:`LV_SCR_LOAD_ANIM_OUT_BOTTOM`: Move out the old screen over the current towards the given direction
- :cpp:enumerator:`LV_SCR_LOAD_ANIM_MOVE_LEFT`, :cpp:enumerator:`LV_SCR_LOAD_ANIM_MOVE_RIGHT`, :cpp:enumerator:`LV_SCR_LOAD_ANIM_MOVE_TOP` and :cpp:enumerator:`LV_SCR_LOAD_ANIM_MOVE_BOTTOM`: Move both the current and new screens towards the given direction
- :cpp:enumerator:`LV_SCR_LOAD_ANIM_FADE_IN` and :cpp:enumerator:`LV_SCR_LOAD_ANIM_FADE_OUT`: Fade the new screen over the old screen, or vice versa
Setting ``auto_del`` to ``true`` will automatically delete the old
screen when the animation (if any) is finished.
The new screen will become active (returned by :cpp:func:`lv_screen_active`) when
the animation starts after ``delay`` time. All inputs are disabled
during the screen animation.
.. _layers_overview:
Layers
------
When an ``lv_display_t`` object is created, 4 Screens (layers) are created and
attached to it.
1. Bottom Layer
2. Active Screen
3. Top Layer
4. System Layer
1, 3 and 4 are independent of the :ref:`active_screen` and they will be shown (if
they contain anything that is visible) regardless of which screen is the Active Screen.
See :ref:`display_screen_layers` and :ref:`transparent_screens` for more information.
.. _widget_parts:
Parts
*****
The widgets are built from multiple parts. For example a
:ref:`Base Widget <base_widget>` uses the main and scrollbar parts but a
:ref:`Slider <lv_slider>` uses the main, indicator and knob parts.
Parts are similar to *pseudo-elements* in CSS.
The following predefined parts exist in LVGL:
- :cpp:enumerator:`LV_PART_MAIN`: A background like rectangle
- :cpp:enumerator:`LV_PART_SCROLLBAR`: The scrollbar(s)
- :cpp:enumerator:`LV_PART_INDICATOR`: Indicator, e.g. for slider, bar, switch, or the tick box of the checkbox
- :cpp:enumerator:`LV_PART_KNOB`: Like a handle to grab to adjust the value
- :cpp:enumerator:`LV_PART_SELECTED`: Indicate the currently selected option or section
- :cpp:enumerator:`LV_PART_ITEMS`: Used if the widget has multiple similar elements (e.g. table cells)
- :cpp:enumerator:`LV_PART_CURSOR`: Mark a specific place e.g. text area's or chart's cursor
- :cpp:enumerator:`LV_PART_CUSTOM_FIRST`: Custom parts can be added from here.
The main purpose of parts is to allow styling the "components" of the
widgets. They are described in more detail in the
:ref:`Style overview <styles>` section.
.. _widget_states:
States
******
The Widget can be in a combination of the following states:
- :cpp:enumerator:`LV_STATE_DEFAULT`: Normal, released state
- :cpp:enumerator:`LV_STATE_CHECKED`: Toggled or checked state
- :cpp:enumerator:`LV_STATE_FOCUSED`: Focused via keypad or encoder or clicked via touchpad/mouse
- :cpp:enumerator:`LV_STATE_FOCUS_KEY`: Focused via keypad or encoder but not via touchpad/mouse
- :cpp:enumerator:`LV_STATE_EDITED`: Edit by an encoder
- :cpp:enumerator:`LV_STATE_HOVERED`: Hovered by mouse (not supported now)
- :cpp:enumerator:`LV_STATE_PRESSED`: Being pressed
- :cpp:enumerator:`LV_STATE_SCROLLED`: Being scrolled
- :cpp:enumerator:`LV_STATE_DISABLED`: Disabled state
- :cpp:enumerator:`LV_STATE_USER_1`: Custom state
- :cpp:enumerator:`LV_STATE_USER_2`: Custom state
- :cpp:enumerator:`LV_STATE_USER_3`: Custom state
- :cpp:enumerator:`LV_STATE_USER_4`: Custom state
The states are usually automatically changed by the library as the user
interacts with a Widget (presses, releases, focuses, etc.). However,
the states can be changed manually as well. To set or clear given state (but
leave the other states untouched) use
:cpp:expr:`lv_obj_add_state(widget, LV_STATE_...)` and
:cpp:expr:`lv_obj_remove_state(widget, LV_STATE_...)`. In both cases OR-ed state
values can be used as well. E.g.
:cpp:expr:`lv_obj_add_state(widget, part, LV_STATE_PRESSED | LV_PRESSED_CHECKED)`.
To learn more about the states, read the related section of
:ref:`styles_overview`.
.. _lv_obj_flags:
Flags
*****
There are some Widget attributes which can be enabled/disabled by
:cpp:expr:`lv_obj_add_flag(widget, LV_OBJ_FLAG_...)` and
:cpp:expr:`lv_obj_remove_flag(widget, LV_OBJ_FLAG_...)`.
- :cpp:enumerator:`LV_OBJ_FLAG_HIDDEN` Make the Widget hidden. (Like it wasn't there at all)
- :cpp:enumerator:`LV_OBJ_FLAG_CLICKABLE` Make the Widget clickable by input devices
- :cpp:enumerator:`LV_OBJ_FLAG_CLICK_FOCUSABLE` Add focused state to the Widget when clicked
- :cpp:enumerator:`LV_OBJ_FLAG_CHECKABLE` Toggle checked state when the Widget is clicked
- :cpp:enumerator:`LV_OBJ_FLAG_SCROLLABLE` Make the Widget scrollable
- :cpp:enumerator:`LV_OBJ_FLAG_SCROLL_ELASTIC` Allow scrolling inside but with slower speed
- :cpp:enumerator:`LV_OBJ_FLAG_SCROLL_MOMENTUM` Make the Widget scroll further when "thrown"
- :cpp:enumerator:`LV_OBJ_FLAG_SCROLL_ONE` Allow scrolling only one snappable children
- :cpp:enumerator:`LV_OBJ_FLAG_SCROLL_CHAIN_HOR` Allow propagating the horizontal scroll to a parent
- :cpp:enumerator:`LV_OBJ_FLAG_SCROLL_CHAIN_VER` Allow propagating the vertical scroll to a parent
- :cpp:enumerator:`LV_OBJ_FLAG_SCROLL_CHAIN` Simple packaging for (:cpp:expr:`LV_OBJ_FLAG_SCROLL_CHAIN_HOR | LV_OBJ_FLAG_SCROLL_CHAIN_VER`)
- :cpp:enumerator:`LV_OBJ_FLAG_SCROLL_ON_FOCUS` Automatically scroll Widget to make it visible when focused
- :cpp:enumerator:`LV_OBJ_FLAG_SCROLL_WITH_ARROW` Allow scrolling the focused Widget with arrow keys
- :cpp:enumerator:`LV_OBJ_FLAG_SNAPPABLE` If scroll snap is enabled on the parent it can snap to this Widget
- :cpp:enumerator:`LV_OBJ_FLAG_PRESS_LOCK` Keep the Widget pressed even if the press slid from the Widget
- :cpp:enumerator:`LV_OBJ_FLAG_EVENT_BUBBLE` Propagate the events to the parent as well
- :cpp:enumerator:`LV_OBJ_FLAG_GESTURE_BUBBLE` Propagate the gestures to the parent
- :cpp:enumerator:`LV_OBJ_FLAG_ADV_HITTEST` Allow performing more accurate hit (click) test. E.g. accounting for rounded corners
- :cpp:enumerator:`LV_OBJ_FLAG_IGNORE_LAYOUT` Make the Widget not positioned by the layouts
- :cpp:enumerator:`LV_OBJ_FLAG_FLOATING` Do not scroll the Widget when the parent scrolls and ignore layout
- :cpp:enumerator:`LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS` Enable sending ``LV_EVENT_DRAW_TASK_ADDED`` events
- :cpp:enumerator:`LV_OBJ_FLAG_OVERFLOW_VISIBLE` Do not clip the children's content to the parent's boundary
- :cpp:enumerator:`LV_OBJ_FLAG_FLEX_IN_NEW_TRACK` Start a new flex track on this item
- :cpp:enumerator:`LV_OBJ_FLAG_LAYOUT_1` Custom flag, free to use by layouts
- :cpp:enumerator:`LV_OBJ_FLAG_LAYOUT_2` Custom flag, free to use by layouts
- :cpp:enumerator:`LV_OBJ_FLAG_WIDGET_1` Custom flag, free to use by widget
- :cpp:enumerator:`LV_OBJ_FLAG_WIDGET_2` Custom flag, free to use by widget
- :cpp:enumerator:`LV_OBJ_FLAG_USER_1` Custom flag, free to use by user
- :cpp:enumerator:`LV_OBJ_FLAG_USER_2` Custom flag, free to use by user
- :cpp:enumerator:`LV_OBJ_FLAG_USER_3` Custom flag, free to use by user
- :cpp:enumerator:`LV_OBJ_FLAG_USER_4` Custom flag, free to use by user
Some examples:
.. code-block:: c
/* Hide on Widget */
lv_obj_add_flag(widget, LV_OBJ_FLAG_HIDDEN);
/* Make a Widget non-clickable */
lv_obj_remove_flag(widget, LV_OBJ_FLAG_CLICKABLE);
.. _lv_obj_events:
Base-Widget Events
******************
.. _widget_events:
Events from Input Devices
-------------------------
- :cpp:enumerator:`LV_EVENT_PRESSED` Widget has been pressed.
- :cpp:enumerator:`LV_EVENT_PRESSING` Widget is being pressed (sent continuously while pressing).
- :cpp:enumerator:`LV_EVENT_PRESS_LOST` Widget is still being pressed but slid cursor/finger off Widget.
- :cpp:enumerator:`LV_EVENT_SHORT_CLICKED` Widget was pressed for a short period of time, then released. Not sent if scrolled.
- :cpp:enumerator:`LV_EVENT_SINGLE_CLICKED` Sent for first short click within a small distance and short time.
- :cpp:enumerator:`LV_EVENT_DOUBLE_CLICKED` Sent for second short click within small distance and short time.
- :cpp:enumerator:`LV_EVENT_TRIPLE_CLICKED` Sent for third short click within small distance and short time.
- :cpp:enumerator:`LV_EVENT_LONG_PRESSED` Object has been pressed for at least `long_press_time`. Not sent if scrolled.
- :cpp:enumerator:`LV_EVENT_LONG_PRESSED_REPEAT` Sent after `long_press_time` in every `long_press_repeat_time` ms. Not sent if scrolled.
- :cpp:enumerator:`LV_EVENT_CLICKED` Sent on release if not scrolled (regardless to long press).
- :cpp:enumerator:`LV_EVENT_RELEASED` Sent in every cases when Widget has been released.
- :cpp:enumerator:`LV_EVENT_SCROLL_BEGIN` Scrolling begins. The event parameter is a pointer to the animation of the scroll. Can be modified.
- :cpp:enumerator:`LV_EVENT_SCROLL_THROW_BEGIN` Received when scrolling begins.
- :cpp:enumerator:`LV_EVENT_SCROLL_END` Scrolling ended.
- :cpp:enumerator:`LV_EVENT_SCROLL` Scrolling
- :cpp:enumerator:`LV_EVENT_GESTURE` A gesture is detected. Get gesture with `lv_indev_get_gesture_dir(lv_indev_active());`
- :cpp:enumerator:`LV_EVENT_KEY` A key is sent to Widget. Get key with `lv_indev_get_key(lv_indev_active());`
- :cpp:enumerator:`LV_EVENT_FOCUSED` Widget received focus,
- :cpp:enumerator:`LV_EVENT_DEFOCUSED` Widget's focus has been lost.
- :cpp:enumerator:`LV_EVENT_LEAVE` Widget's focus has been lost but is still selected.
- :cpp:enumerator:`LV_EVENT_HIT_TEST` Perform advanced hit-testing.
Special Events
--------------
- :cpp:enumerator:`LV_EVENT_VALUE_CHANGED` when the :cpp:enumerator:`LV_OBJ_FLAG_CHECKABLE` flag is
enabled and the Widget was clicked (on transition to/from the checked state)
Drawing Events
--------------
- :cpp:enumerator:`LV_EVENT_DRAW_MAIN` Performing drawing of main part
- :cpp:enumerator:`LV_EVENT_DRAW_MAIN_BEGIN` Starting drawing of main part
- :cpp:enumerator:`LV_EVENT_DRAW_MAIN_END` Finishing drawing of main part
- :cpp:enumerator:`LV_EVENT_DRAW_POST` Perform the post draw phase (when all children are drawn)
- :cpp:enumerator:`LV_EVENT_DRAW_POST_BEGIN` Starting the post draw phase (when all children are drawn)
- :cpp:enumerator:`LV_EVENT_DRAW_POST_END` Finishing the post draw phase (when all children are drawn)
Other Events
------------
- :cpp:enumerator:`LV_EVENT_DELETE` Object is being deleted
- :cpp:enumerator:`LV_EVENT_CHILD_CHANGED` Child was removed, added, or its size, position were changed
- :cpp:enumerator:`LV_EVENT_CHILD_CREATED` Child was created, always bubbles up to all parents
- :cpp:enumerator:`LV_EVENT_CHILD_DELETED` Child was deleted, always bubbles up to all parents
- :cpp:enumerator:`LV_EVENT_SIZE_CHANGED` Object coordinates/size have changed
- :cpp:enumerator:`LV_EVENT_STYLE_CHANGED` Object's style has changed
- :cpp:enumerator:`LV_EVENT_LAYOUT_CHANGED` A child's position has changed due to a layout recalculation (when container has flex or grid layout style)
- :cpp:enumerator:`LV_EVENT_GET_SELF_SIZE` Get internal size of a widget
.. admonition:: Further Reading
Learn more about :ref:`events`.
.. _lv_obj_keys:
Keys
****
If :cpp:enumerator:`LV_OBJ_FLAG_CHECKABLE` is enabled, :cpp:enumerator:`LV_KEY_RIGHT` and
:cpp:enumerator:`LV_KEY_UP` make the Widget checked, and :cpp:enumerator:`LV_KEY_LEFT` and
:cpp:enumerator:`LV_KEY_DOWN` make it unchecked.
If :cpp:enumerator:`LV_OBJ_FLAG_SCROLLABLE` is enabled, but the Widget is not editable
(as declared by the widget class), the arrow keys (:cpp:enumerator:`LV_KEY_UP`,
:cpp:enumerator:`LV_KEY_DOWN`, :cpp:enumerator:`LV_KEY_LEFT`, :cpp:enumerator:`LV_KEY_RIGHT`) scroll the Widget.
If the Widget can only scroll vertically, :cpp:enumerator:`LV_KEY_LEFT` and
:cpp:enumerator:`LV_KEY_RIGHT` will scroll up/down instead, making it compatible with
an encoder input device. See :ref:`Input devices overview <indev>` for
more on encoder behaviors and the edit mode.
.. admonition:: Further Reading
Learn more about :ref:`indev_keys`.
.. _widget_names:
Names
*****
When a widget is created, its reference can be stored in an :cpp:expr:`lv_obj_t *` pointer
variable. To use this widget in multiple places in the code, the variable can be passed
as a function parameter or made a global variable. However, this approach has some drawbacks:
- Using global variables is not clean and generally not recommended.
- It's not scalable. Passing references to 20 widgets as function parameters is not ideal.
- It's hard to track whether a widget still exists or has been deleted.
Setting names
-------------
To address these issues, LVGL introduces a powerful widget naming system that can be enabled
by setting ``LV_USE_OBJ_NAME`` in ``lv_conf.h``.
A custom name can be assigned using :cpp:expr:`lv_obj_set_name(obj, "name")` or
:cpp:expr:`lv_obj_set_name_static(obj, "name")`. The "static" variant means the passed name
must remain valid while the widget exists, as only the pointer is stored. Otherwise, LVGL will
allocate memory to store a copy of the name.
If a name ends with ``#``, LVGL will automatically replace it with an index based on the
number of siblings with the same base name. If no name is provided, the default is
``<widget_type>_#``.
Below is an example showing how manually and automatically assigned names are resolved:
- Main ``lv_obj`` container named ``"cont"``: "cont"
- ``lv_obj`` container named ``"header"``: "header"
- ``lv_label`` with no name: "lv_label_0"
- ``lv_label`` named ``"title"``: "title"
- ``lv_label`` with no name: "lv_label_1" (It's the third label, but custom-named widgets are not counted)
- ``lv_obj`` container named ``"buttons"``:
- ``lv_button`` with no name: "lv_button_0"
- ``lv_button`` named ``"second_button"``: "second_button"
- ``lv_button`` with no name: "lv_button_1"
- ``lv_button`` named ``lv_button_#``: "lv_button_2"
- ``lv_button`` named ``mybtn_#``: "mybtn_0"
- ``lv_button`` with no name: "lv_button_2"
- ``lv_button`` named ``mybtn_#``: "mybtn_1"
- ``lv_button`` named ``mybtn_#``: "mybtn_2"
- ``lv_button`` named ``mybtn_#``: "mybtn_3"
Finding widgets
---------------
Widgets can be found by name in two ways:
1. **Get a direct child by name** using :cpp:func:`lv_obj_get_child_by_name(parent, "child_name")`.
Example:
``lv_obj_get_child_by_name(header, "title")``
You can also use a "path" to find nested children:
``lv_obj_get_child_by_name(cont, "buttons/mybtn_2")``
2. **Find a descendant at any level** using :cpp:func:`lv_obj_find_by_name(parent, "child_name")`.
Example:
``lv_obj_find_by_name(cont, "mybtn_1")``
Note that ``"mybtn_1"`` is a child of ``"buttons"``, not directly of ``"cont"``.
This is useful when you want to ignore hierarchy and search by name alone.
Since both functions start searching from a specific parent, its possible to have multiple widget
subtrees with identical names under different parents.
For example, if ``my_listitem_create(parent)`` creates a widget named ``"list_item_#"``
with children like ``"icon"``, ``"title"``, ``"ok_button"``, and ``"lv_label_0"``,
and it's called 10 times, a specific ``"ok_button"`` can be found like this:
.. code-block:: c
lv_obj_t * item = lv_obj_find_by_name(lv_screen_active(), "list_item_5");
lv_obj_t * ok_btn = lv_obj_find_by_name(item, "ok_button");
// Or
lv_obj_t * ok_btn = lv_obj_get_child_by_name(some_list_container, "list_item_5/ok_button");
Names are resolved **when they are retrieved**, not when they are set.
This means the indices always reflect the current state of the widget tree
at the time the name is used.
.. _widget_snapshot:
Snapshot
********
A snapshot image can be generated for a Widget together with its
children. Check details in :ref:`snapshot`.
Example
*******
.. include:: ../../examples/widgets/obj/index.rst
.. _lv_obj_api:
API
***

View File

@@ -0,0 +1,624 @@
.. _coord:
============================
Positions, Sizes and Layouts
============================
Overview
********
Similar to many other parts of LVGL, the concept of setting the
coordinates was inspired by CSS. LVGL has by no means a complete
implementation of CSS but a comparable subset is implemented (sometimes
with minor adjustments).
In short this means:
- Explicitly set coordinates are stored in styles (position, size, layouts, etc.)
- support min-width, max-width, min-height, max-height
- have pixel, percentage, and "content" units
- x=0; y=0 coordinate means the top-left corner of the parent plus the left/top padding plus border width
- width/height means the full size, the "content area" is smaller with padding and border width
- a subset of flexbox and grid layouts are supported
.. _coord_units:
Length Units
************
When passing "length units" (a.k.a. "distance units" or "size units") as arguments to
functions that modify position, size, etc., to make layout of your UI convenient, you
have a choice of several different types of units you can use.
:pixels: Specify size as pixels: an integer value <
:c:macro:`LV_COORD_MAX` always means pixels. E.g.
:cpp:expr:`lv_obj_set_x(btn, 10)`.
:percentage: Specify size as a percentage of the size of the Widget's
parent or of itself, depending on the property.
:cpp:expr:`lv_pct(value)` converts ``value`` to a percentage.
E.g. :cpp:expr:`lv_obj_set_width(btn, lv_pct(50))`. If you want
to avoid the overhead of the call to :cpp:func:`lv_pct`, you can
also use the macro :cpp:expr:`LV_PCT(x)` to mean the same thing.
Note that when you use this feature, your value is *stored as a
percent* so that if/when the size of the parent container (or
other positioning factor) changes, this style value dynamically
retains its meaning.
:contained content: Specify size as a function of the Widget's children. The macro
:c:macro:`LV_SIZE_CONTENT`: passed as a size value has special
meaning: it means to set the width and/or height of a Widget
just large enough to include all of its children. This is
similar to ``auto`` in CSS. E.g.
:cpp:expr:`lv_obj_set_width(btn, LV_SIZE_CONTENT)`.
:inches: Specify size as 1/160-th portion of an inch as if it were pixels
on a 160-DPI display, even though a display may have a different
DPI. Use :cpp:expr:`lv_dpx(n)` or :cpp:expr:`LV_DPX(n)` to do
this. Examples:
+----+-----+----------------------------+
| n | DPI | Computed Pixels |
+====+=====+============================+
| 40 | 320 | 80 pixels to make 1/4 inch |
+----+-----+----------------------------+
| 40 | 160 | 40 pixels to make 1/4 inch |
+----+-----+----------------------------+
| 40 | 130 | 33 pixels to make 1/4 inch |
+----+-----+----------------------------+
| 80 | 130 | 66 pixels to make 1/2 inch |
+----+-----+----------------------------+
See DPI under :ref:`display_attributes`.
.. _boxing_model:
Boxing Model
************
LVGL follows CSS's `border-box <https://developer.mozilla.org/en-US/docs/Web/CSS/box-sizing>`__
model. A Widget's "box" is built from the following parts:
:bounding box: the width/height of the elements.
:border width: the width of the border.
:padding: space between the sides of the Widget and its children.
:margin: space outside of the Widget (considered only by some layouts)
:content: the content area which is the size of the bounding box reduced by the border width and padding.
.. image:: /_static/images/boxmodel.png
:alt: The box models of LVGL: The content area is smaller than the bounding box with the padding and border width
The border is drawn inside the bounding box. Inside the border LVGL
keeps a "padding margin" when placing a Widget's children.
The outline is drawn outside the bounding box.
.. _coord_notes:
Important Notes
***************
This section describes special cases in which LVGL's behavior might be
unexpected.
.. _coord_postponed_coordinate_calculation:
Postponed coordinate calculation
--------------------------------
LVGL doesn't recalculate all the coordinate changes immediately. This is
done to improve performance. Instead, the Widgets are marked as "dirty"
and before redrawing the screen LVGL checks if there are any "dirty"
Widgets. If so it refreshes their position, size and layout.
In other words, if you need to get the coordinate of a Widget and the
coordinates were just changed, LVGL needs to be forced to recalculate
the coordinates. To do this call :cpp:func:`lv_obj_update_layout`.
The size and position might depend on the parent or layout. Therefore
:cpp:func:`lv_obj_update_layout` recalculates the coordinates of all Widgets on
the screen of ``obj``.
.. _coord_removing styles:
Removing styles
---------------
As it's described in the :ref:`coord_using_styles` section,
coordinates can also be set via style properties. To be more precise,
under the hood every style coordinate related property is stored as a
style property. If you use :cpp:expr:`lv_obj_set_x(widget, 20)` LVGL saves ``x=20``
in the local style of the Widget.
This is an internal mechanism and doesn't matter much as you use LVGL.
However, there is one case in which you need to be aware of the
implementation. If the style(s) of a Widget are removed by
.. code-block:: c
lv_obj_remove_style_all(widget)
or
.. code-block:: c
lv_obj_remove_style(widget, NULL, LV_PART_MAIN);
the earlier set coordinates will be removed as well.
For example:
.. code-block:: c
/* The size of obj1 will be set back to the default in the end */
lv_obj_set_size(widget1, 200, 100); /* Now obj1 has 200;100 size */
lv_obj_remove_style_all(widget1); /* It removes the set sizes */
/* widget2 will have 200;100 size in the end */
lv_obj_remove_style_all(widget2);
lv_obj_set_size(widget2, 200, 100);
.. _positioning_widgets:
Positioning Widgets
*******************
Direct
------
To simply set the x and y coordinates of a Widget use:
.. code-block:: c
lv_obj_set_x(widget, 10); /* Separate... */
lv_obj_set_y(widget, 20);
lv_obj_set_pos(widget, 10, 20); /* Or in one function */
By default, the x and y coordinates are measured from the top left
corner of the parent's content area. For example if the parent has five
pixels of padding on every side the above code will place ``obj`` at
(15, 25) because the content area starts after the padding.
Percentage values are calculated from the parent's content area size.
.. code-block:: c
lv_obj_set_x(btn, lv_pct(10)); //x = 10 % of parent content area width
Alignment
---------
Inside parent widget
~~~~~~~~~~~~~~~~~~~~
In many cases it is more convenient to tell LVGL to align your object relative to
an "anchor" in its parent *other* than its upper left corner. To establish
that "anchor", call :cpp:expr:`lv_obj_set_align(widget, LV_ALIGN_...)`. After
that call, that "anchor" will be remembered until another one is established.
In other words, every futire x and y setting for that Widget will be relative to the
that "anchor".
Example: Position Widget (10,20) px relative to the center of its parent:
.. code-block:: c
lv_obj_set_align(widget, LV_ALIGN_CENTER);
lv_obj_set_pos(widget, 10, 20);
/* Or combine the above in one function... */
lv_obj_align(widget, LV_ALIGN_CENTER, 10, 20);
9 convenient "anchors" can be used with these functions:
- :cpp:enumerator:`LV_ALIGN_TOP_LEFT`
- :cpp:enumerator:`LV_ALIGN_TOP_MID`
- :cpp:enumerator:`LV_ALIGN_TOP_RIGHT`
- :cpp:enumerator:`LV_ALIGN_BOTTOM_LEFT`
- :cpp:enumerator:`LV_ALIGN_BOTTOM_MID`
- :cpp:enumerator:`LV_ALIGN_BOTTOM_RIGHT`
- :cpp:enumerator:`LV_ALIGN_LEFT_MID`
- :cpp:enumerator:`LV_ALIGN_RIGHT_MID`
- :cpp:enumerator:`LV_ALIGN_CENTER`
See illustration below to visualize what these mean.
It's quite common to align a child to the center of its parent,
therefore a dedicated function exists:
.. code-block:: c
lv_obj_center(widget);
//Has the same effect
lv_obj_align(widget, LV_ALIGN_CENTER, 0, 0);
If the parent's size changes, the set alignment and position of the
children is updated automatically.
Relative to another Widget
~~~~~~~~~~~~~~~~~~~~~~~~~~
Alternately, you can choose an "anchor" on another Widget.
.. code-block:: c
lv_obj_align_to(widget, reference_widget, align, x, y);
where ``align`` can be done of the following:
- :cpp:enumerator:`LV_ALIGN_OUT_TOP_LEFT`
- :cpp:enumerator:`LV_ALIGN_OUT_TOP_MID`
- :cpp:enumerator:`LV_ALIGN_OUT_TOP_RIGHT`
- :cpp:enumerator:`LV_ALIGN_OUT_BOTTOM_LEFT`
- :cpp:enumerator:`LV_ALIGN_OUT_BOTTOM_MID`
- :cpp:enumerator:`LV_ALIGN_OUT_BOTTOM_RIGHT`
- :cpp:enumerator:`LV_ALIGN_OUT_LEFT_TOP`
- :cpp:enumerator:`LV_ALIGN_OUT_LEFT_MID`
- :cpp:enumerator:`LV_ALIGN_OUT_LEFT_BOTTOM`
- :cpp:enumerator:`LV_ALIGN_OUT_RIGHT_TOP`
- :cpp:enumerator:`LV_ALIGN_OUT_RIGHT_MID`
- :cpp:enumerator:`LV_ALIGN_OUT_RIGHT_BOTTOM`
Example: to horizontally center a label 10 pixels above a button:
.. code-block:: c
lv_obj_align_to(label, btn, LV_ALIGN_OUT_TOP_MID, 0, -10);
Note that, unlike with :cpp:func:`lv_obj_align`, :cpp:func:`lv_obj_align_to`
does not remember the "anchor" used, and so will not automatically reposition
the aligned widget if the reference widget later moves.
The following illustration shows the meaning of each "anchor" mentioned above.
.. image:: /_static/images/align.png
.. _coord_size:
Size
****
Sizing the simple way
---------------------
The width and the height of a Widget can be set easily as well:
.. code-block:: c
lv_obj_set_width(widget, 200); /* Separate... */
lv_obj_set_height(widget, 100);
lv_obj_set_size(widget, 200, 100); /* Or in one function */
Percentage values are calculated based on the parent's content area
size. For example to set the Widget's height to the screen height:
.. code-block:: c
lv_obj_set_height(widget, lv_pct(100));
The size settings support a special value: :c:macro:`LV_SIZE_CONTENT`. It means
the Widget's size in the respective direction will be set to the size of
its children. Note that only children on the right and bottom sides will
be considered and children on the top and left remain cropped. This
limitation makes the behavior more predictable.
Widgets with :cpp:enumerator:`LV_OBJ_FLAG_HIDDEN` or :cpp:enumerator:`LV_OBJ_FLAG_FLOATING` will be
ignored by the :c:macro:`LV_SIZE_CONTENT` calculation.
The above functions set the size of a Widget's bounding box but the
size of the content area can be set as well. This means a Widget's
bounding box will be enlarged with the addition of padding.
.. code-block:: c
lv_obj_set_content_width(widget, 50); /* The actual width: padding left + 50 + padding right */
lv_obj_set_content_height(widget, 30); /* The actual width: padding top + 30 + padding bottom */
The size of the bounding box and the content area can be retrieved with
the following functions:
.. code-block:: c
int32_t w = lv_obj_get_width(widget);
int32_t h = lv_obj_get_height(widget);
int32_t content_w = lv_obj_get_content_width(widget);
int32_t content_h = lv_obj_get_content_height(widget);
.. _extending_click_area:
Extending the click area
------------------------
By default, Widgets can be clicked only within their bounding area. However,
especially with small Widgets, it can be helpful to make a Widget's "clickable" area
larger. You can do this with :cpp:expr:`lv_obj_set_ext_click_area(widget, size)`.
.. _coord_using_styles:
Using styles
************
Under the hood the position, size and alignment properties are style
properties. The above described "simple functions" hide the style
related code for the sake of simplicity and set the position, size, and
alignment properties in the :ref:`local styles <style_local>` of the Widget.
However, using styles to set the coordinates has some great advantages:
- It makes it easy to set the width/height/etc. for several Widgets
together. E.g. make all the sliders 100x10 pixels sized.
- It also makes possible to modify the values in one place.
- The values can be partially overwritten by other styles. For example
``style_btn`` makes the Widget ``100x50`` by default but adding
``style_full_width`` overwrites only the width of the Widget.
- The Widget can have different position or size depending on state.
E.g. 100 px wide in :cpp:enumerator:`LV_STATE_DEFAULT` but 120 px
in :cpp:enumerator:`LV_STATE_PRESSED`.
- Style transitions can be used to make the coordinate changes smooth.
Here are some examples to set a Widget's size using a style:
.. code-block:: c
static lv_style_t style;
lv_style_init(&style);
lv_style_set_width(&style, 100);
lv_obj_t * btn = lv_button_create(lv_screen_active());
lv_obj_add_style(btn, &style, LV_PART_MAIN);
As you will see below there are some other great features of size and
position setting. However, to keep the LVGL API lean, only the most
common coordinate setting features have a "simple" version and the more
complex features can be used via styles.
.. _coord_translation:
Translation
***********
Let's say the there are 3 buttons next to each other. Their position is
set as described above. Now you want to move a button up a little when
it's pressed.
One way to achieve this is by setting a new Y coordinate for the pressed
state:
.. code-block:: c
static lv_style_t style_normal;
lv_style_init(&style_normal);
lv_style_set_y(&style_normal, 100);
static lv_style_t style_pressed;
lv_style_init(&style_pressed);
lv_style_set_y(&style_pressed, 80);
lv_obj_add_style(btn1, &style_normal, LV_STATE_DEFAULT);
lv_obj_add_style(btn1, &style_pressed, LV_STATE_PRESSED);
lv_obj_add_style(btn2, &style_normal, LV_STATE_DEFAULT);
lv_obj_add_style(btn2, &style_pressed, LV_STATE_PRESSED);
lv_obj_add_style(btn3, &style_normal, LV_STATE_DEFAULT);
lv_obj_add_style(btn3, &style_pressed, LV_STATE_PRESSED);
This works, but it's not really flexible because the pressed coordinate
is hard-coded. If the buttons are not at y=100, ``style_pressed`` won't
work as expected. Translations can be used to solve this:
.. code-block:: c
static lv_style_t style_normal;
lv_style_init(&style_normal);
lv_style_set_y(&style_normal, 100);
static lv_style_t style_pressed;
lv_style_init(&style_pressed);
lv_style_set_translate_y(&style_pressed, -20);
lv_obj_add_style(btn1, &style_normal, LV_STATE_DEFAULT);
lv_obj_add_style(btn1, &style_pressed, LV_STATE_PRESSED);
lv_obj_add_style(btn2, &style_normal, LV_STATE_DEFAULT);
lv_obj_add_style(btn2, &style_pressed, LV_STATE_PRESSED);
lv_obj_add_style(btn3, &style_normal, LV_STATE_DEFAULT);
lv_obj_add_style(btn3, &style_pressed, LV_STATE_PRESSED);
Translation is applied from the current position of the Widget.
Percentage values can be used in translations as well. The percentage is
relative to the size of the Widget (and not to the size of the parent).
For example :cpp:expr:`lv_pct(50)` will move the Widget with half of its
width/height.
The translation is applied after the layouts are calculated. Therefore,
even laid out Widgets' position can be translated.
The translation actually moves the Widget. That means it makes the
scrollbars and :c:macro:`LV_SIZE_CONTENT` sized Widgets react to the position
change.
.. _coord_transformation:
Transformation
**************
Similarly to position, a Widget's size can be changed relative to the
current size as well. The transformed width and height are added on both
sides of the Widget. This means a 10 px transformed width makes the
Widget 2x10 pixels wider.
Unlike position translation, the size transformation doesn't make the
Widget "really" larger. In other words scrollbars, layouts, and
:c:macro:`LV_SIZE_CONTENT` will not react to the transformed size. Hence, size
transformation is "only" a visual effect.
This code enlarges a button when it's pressed:
.. code-block:: c
static lv_style_t style_pressed;
lv_style_init(&style_pressed);
lv_style_set_transform_width(&style_pressed, 10);
lv_style_set_transform_height(&style_pressed, 10);
lv_obj_add_style(btn, &style_pressed, LV_STATE_PRESSED);
.. _coord_min_max_size:
Min and Max size
----------------
Similarly to CSS, LVGL also supports ``min-width``, ``max-width``,
``min-height`` and ``max-height``. These are limits preventing a
Widget's size from becoming smaller/larger than these values. They are
especially useful if the size is set by percentage or
:c:macro:`LV_SIZE_CONTENT`.
.. code-block:: c
static lv_style_t style_max_height;
lv_style_init(&style_max_height);
lv_style_set_max_height(&style_max_height, 200);
lv_obj_set_height(widget, lv_pct(100));
lv_obj_add_style(widget, &style_max_height, LV_STATE_DEFAULT); //Limit the height to 200 px
Percentage values can be used as well which are relative to the size of
the parent's content area.
.. code-block:: c
static lv_style_t style_max_height;
lv_style_init(&style_max_height);
lv_style_set_max_height(&style_max_height, lv_pct(50));
lv_obj_set_height(widget, lv_pct(100));
lv_obj_add_style(widget, &style_max_height, LV_STATE_DEFAULT); //Limit the height to half parent height
.. _coord_layout:
Layout
******
Layout overview
---------------
Layouts can update the position and size of a Widget's children. They
can be used to automatically arrange the children into a line or column,
or in much more complicated forms.
The position and size set by the layout overwrites the "normal" x, y,
width, and height settings.
There is only one function that is the same for every layout:
:cpp:func:`lv_obj_set_layout` ``(widget, <LAYOUT_NAME>)`` sets the layout on a Widget.
For further settings of the parent and children see the documentation of
the given layout.
Built-in layouts
----------------
LVGL comes with two very powerful layouts:
* Flexbox: arrange Widgets into rows or columns, with support for wrapping and expanding items.
* Grid: arrange Widgets into fixed positions in 2D table.
Both are heavily inspired by the CSS layouts with the same name.
Layouts are described in detail in their own section of documentation.
Flags
-----
There are some flags that can be used on Widgets to affect how they
behave with layouts:
- :cpp:enumerator:`LV_OBJ_FLAG_HIDDEN` Hidden Widgets are ignored in layout calculations.
- :cpp:enumerator:`LV_OBJ_FLAG_IGNORE_LAYOUT` The Widget is simply ignored by the layouts. Its coordinates can be set as usual.
- :cpp:enumerator:`LV_OBJ_FLAG_FLOATING` Same as :cpp:enumerator:`LV_OBJ_FLAG_IGNORE_LAYOUT` but the Widget with :cpp:enumerator:`LV_OBJ_FLAG_FLOATING` will be ignored in :c:macro:`LV_SIZE_CONTENT` calculations.
These flags can be added/removed with :cpp:expr:`lv_obj_add_flag(widget, FLAG)` and :cpp:expr:`lv_obj_remove_flag(widget, FLAG)`
Adding new layouts
------------------
LVGL can be freely extended by a custom layout like this:
.. code-block:: c
uint32_t MY_LAYOUT;
...
MY_LAYOUT = lv_layout_register(my_layout_update, &user_data);
...
void my_layout_update(lv_obj_t * widget, void * user_data)
{
/* Will be called automatically if it's required to reposition/resize the children of "obj" */
}
Custom style properties can be added which can be retrieved and used in
the update callback. For example:
.. code-block:: c
uint32_t MY_PROP;
...
LV_STYLE_MY_PROP = lv_style_register_prop();
...
static inline void lv_style_set_my_prop(lv_style_t * style, uint32_t value)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_style_set_prop(style, LV_STYLE_MY_PROP, v);
}
.. _coord_example:
Examples
********
.. _coord_api:
API
***

View File

@@ -0,0 +1,300 @@
.. _events:
======
Events
======
Events are triggered in LVGL when something happens which might be
interesting to the user, e.g. when a Widget:
- is clicked
- is scrolled
- has its value changed
- is redrawn, etc.
Besides Widgets, events can registered from displays and input devices as well.
It is not detailed below, but you can do this by changing the prefix of the functions
from ``lv_obj_`` to ``lv_display_`` or ``lv_indev_``.
.. _adding_events_to_a_widget:
Adding Events to a Widget
*************************
The user can assign callback functions to a widget to process events.
In practice, it looks like this:
.. code-block:: c
lv_obj_t * btn = lv_button_create(lv_screen_active());
lv_obj_add_event_cb(btn, my_event_cb, LV_EVENT_CLICKED, user_data); /* Assign an event callback */
...
static void my_event_cb(lv_event_t * event)
{
printf("Clicked\n");
}
In the example :cpp:enumerator:`LV_EVENT_CLICKED` means that only the click event will
call ``my_event_cb``. See the :ref:`list of event codes <events_codes>` for
all the options. :cpp:enumerator:`LV_EVENT_ALL` can be used to receive all events.
The last parameter of :cpp:func:`lv_obj_add_event_cb` is a pointer to any custom
data that will be available in the event. NULL may be passed for this argument if
there is no need to use that data when the event is processed. You can retrieve the
pointer passed when setting the callback function like this:
.. code-block:: c
my_user_data_t * user_data;
...
user_data = lv_event_get_user_data(e);
More events can be added to a Widget, like this:
.. code-block:: c
lv_obj_add_event_cb(widget, my_event_cb_1, LV_EVENT_CLICKED, NULL);
lv_obj_add_event_cb(widget, my_event_cb_2, LV_EVENT_PRESSED, NULL);
lv_obj_add_event_cb(widget, my_event_cb_3, LV_EVENT_ALL, NULL); /* No filtering, receive all events */
Even the same event callback can be used on a Widget with different
``user_data``. For example:
.. code-block:: c
lv_obj_add_event_cb(widget, increment_on_click, LV_EVENT_CLICKED, &num1);
lv_obj_add_event_cb(widget, increment_on_click, LV_EVENT_CLICKED, &num2);
The events will be called in the order as they were added.
Other Widgets can use the same *event callback*.
In the very same way, events can be attached to input devices and displays like this:
.. code-block:: c
lv_display_add_event_cb(disp, event_cb, LV_EVENT_RESOLUTION_CHANGED, NULL);
lv_indev_add_event_cb(indev, event_cb, LV_EVENT_CLICKED, NULL);
Removing Event(s) from Widgets
******************************
.. code-block:: c
uint32_t i;
uint32_t event_cnt = lv_obj_get_event_count(widget);
for(i = 0; i < event_cnt; i++) {
lv_event_dsc_t * event_dsc = lv_obj_get_event_dsc(widget, i);
if(lv_event_dsc_get_cb(event_dsc) == some_event_cb) {
lv_obj_remove_event(widget, i);
break;
}
}
.. _events_codes:
Event Codes
***********
The event codes can be grouped into these categories: - Input device
events - Drawing events - Other events - Special events - Custom events
All Widgets (such as Buttons/Labels/Sliders etc.) regardless their type
receive the *Input device*, *Drawing* and *Other* events.
However, the *Special events* are specific to a particular widget type.
See the :ref:`widgets' documentation <widgets>` to learn when they
are sent,
*Custom events* are added by the user and are never sent by LVGL.
The following event codes exist:
Input Device Events
-------------------
- :cpp:enumerator:`LV_EVENT_PRESSED`: Widget has been pressed
- :cpp:enumerator:`LV_EVENT_PRESSING`: Widget is being pressed (called continuously while pressing)
- :cpp:enumerator:`LV_EVENT_PRESS_LOST`: Widget is still being pressed but slid cursor/finger off Widget
- :cpp:enumerator:`LV_EVENT_SHORT_CLICKED`: Widget was pressed for a short period of time, and then released without scrolling.
- :cpp:enumerator:`LV_EVENT_SINGLE_CLICKED`: Widget was pressed for a short period of time, and then released without scrolling, for the first time in a click streak. A click streak refers to multiple short clicks within a short period of time and a small distance.
- :cpp:enumerator:`LV_EVENT_DOUBLE_CLICKED`: Widget was pressed for a short period of time, and then released without scrolling, for the second time in a click streak.
- :cpp:enumerator:`LV_EVENT_TRIPLE_CLICKED`: Widget was pressed for a short period of time, and then released without scrolling, for the third time in a click streak.
- :cpp:enumerator:`LV_EVENT_LONG_PRESSED`: Widget has been pressed for at least `long_press_time`. Not called if scrolled.
- :cpp:enumerator:`LV_EVENT_LONG_PRESSED_REPEAT`: Called after `long_press_time` in every `long_press_repeat_time` ms. Not called if scrolled.
- :cpp:enumerator:`LV_EVENT_CLICKED`: Called on release if not scrolled (regardless of long press)
- :cpp:enumerator:`LV_EVENT_RELEASED`: Called in every cases when Widget has been released
- :cpp:enumerator:`LV_EVENT_SCROLL_BEGIN`: Scrolling begins. The event parameter is a pointer to the animation of the scroll. Can be modified
- :cpp:enumerator:`LV_EVENT_SCROLL_THROW_BEGIN`:
- :cpp:enumerator:`LV_EVENT_SCROLL_END`: Scrolling ends
- :cpp:enumerator:`LV_EVENT_SCROLL`: Scrolling
- :cpp:enumerator:`LV_EVENT_GESTURE`: A gesture is detected. Get the gesture with :cpp:expr:`lv_indev_get_gesture_dir(lv_indev_active())`
- :cpp:enumerator:`LV_EVENT_KEY`: A key is sent to Widget. Get the key with :cpp:expr:`lv_indev_get_key(lv_indev_active())`
- :cpp:enumerator:`LV_EVENT_FOCUSED`: Widget received focus
- :cpp:enumerator:`LV_EVENT_DEFOCUSED`: Widget is defocused
- :cpp:enumerator:`LV_EVENT_LEAVE`: Widget is defocused but still selected
- :cpp:enumerator:`LV_EVENT_HIT_TEST`: Perform advanced hit-testing
- :cpp:enumerator:`LV_EVENT_INDEV_RESET`: Indev has been reset
- :cpp:enumerator:`LV_EVENT_HOVER_OVER`: Indev hover over Widget
- :cpp:enumerator:`LV_EVENT_HOVER_LEAVE`: Indev hover leave Widget
Drawing Events
--------------
- :cpp:enumerator:`LV_EVENT_COVER_CHECK`: Check if Widget fully covers an area. The event parameter is :cpp:type:`lv_cover_check_info_t` ``*``.
- :cpp:enumerator:`LV_EVENT_REFR_EXT_DRAW_SIZE`: Get the required extra draw area around Widget (e.g. for shadow). The event parameter is :cpp:type:`int32_t` ``*`` to store the size.
- :cpp:enumerator:`LV_EVENT_DRAW_MAIN_BEGIN`: Starting the main drawing phase
- :cpp:enumerator:`LV_EVENT_DRAW_MAIN`: Perform the main drawing
- :cpp:enumerator:`LV_EVENT_DRAW_MAIN_END`: Finishing the main drawing phase
- :cpp:enumerator:`LV_EVENT_DRAW_POST_BEGIN`: Starting the post draw phase (when all children are drawn)
- :cpp:enumerator:`LV_EVENT_DRAW_POST`: Perform the post draw phase (when all children are drawn)
- :cpp:enumerator:`LV_EVENT_DRAW_POST_END`: Finishing the post draw phase (when all children are drawn)
- :cpp:enumerator:`LV_EVENT_DRAW_TASK_ADDED`: Adding a draw task
.. attention::
In drawing-event callback functions the rendering
sequence has already begun, and during this period, making changes to any
Widget's attributes, styles, or creating/deleting widgets is forbidden. If you attempt to do so,
LVGL will generate an assertion failure with a message
indicating that invalidating an area is not allowed during rendering.
Special Events
--------------
- :cpp:enumerator:`LV_EVENT_VALUE_CHANGED`: Widget's value has changed (i.e. slider moved)
- :cpp:enumerator:`LV_EVENT_INSERT`: A text is inserted to Widget. The event data is ``char *`` being inserted.
- :cpp:enumerator:`LV_EVENT_REFRESH`: Notify Widget to refresh something on it (for the user)
- :cpp:enumerator:`LV_EVENT_READY`: A process has finished
- :cpp:enumerator:`LV_EVENT_CANCEL`: A process has been cancelled
Other Events
------------
- :cpp:enumerator:`LV_EVENT_CREATE`: Widget is being created
- :cpp:enumerator:`LV_EVENT_DELETE`: Widget is being deleted
- :cpp:enumerator:`LV_EVENT_CHILD_CHANGED`: Child was removed, added, or its size, position were changed
- :cpp:enumerator:`LV_EVENT_CHILD_CREATED`: Child was created, always bubbles up to all parents
- :cpp:enumerator:`LV_EVENT_CHILD_DELETED`: Child was deleted, always bubbles up to all parents
- :cpp:enumerator:`LV_EVENT_SCREEN_UNLOAD_START`: A screen unload started, fired immediately when scr_load is called
- :cpp:enumerator:`LV_EVENT_SCREEN_LOAD_START`: A screen load started, fired when the screen change delay is expired
- :cpp:enumerator:`LV_EVENT_SCREEN_LOADED`: A screen was loaded
- :cpp:enumerator:`LV_EVENT_SCREEN_UNLOADED`: A screen was unloaded
- :cpp:enumerator:`LV_EVENT_SIZE_CHANGED`: Widget coordinates/size have changed
- :cpp:enumerator:`LV_EVENT_STYLE_CHANGED`: Widget's style has changed
- :cpp:enumerator:`LV_EVENT_LAYOUT_CHANGED`: The children position has changed due to a layout recalculation
- :cpp:enumerator:`LV_EVENT_GET_SELF_SIZE`: Get the internal size of a widget
Display Events
--------------
.. include:: ../main-modules/display/display_events_list.txt
Custom Events
-------------
Any number of custom event codes can be registered by
``uint32_t MY_EVENT_1 =`` :cpp:func:`lv_event_register_id`
They can be sent to any Widget with
:cpp:expr:`lv_obj_send_event(widget, MY_EVENT_1, &some_data)`
Refresh Event
-------------
:cpp:enumerator:`LV_EVENT_REFRESH` is a special event because it's designed to let the
user notify a Widget to refresh itself. Some examples:
- notify a label to refresh its text according to one or more variables (e.g. current time)
- refresh a label when the language changes
- enable a button if some conditions are met (e.g. the correct PIN is entered)
- add/remove styles to/from a Widget if a limit is exceeded, etc
Sending Events Manually
***********************
To manually send events to a Widget, use
``lv_obj_send_event(widget, <EVENT_CODE>, &some_data)``.
For example, this can be used to manually close a message box by
simulating a button press (although there are simpler ways to do this):
.. code-block:: c
/* Simulate the press of the first button (indexes start from zero) */
uint32_t btn_id = 0;
lv_obj_send_event(mbox, LV_EVENT_VALUE_CHANGED, &btn_id);
The same works for display and input devices with
``lv_display_send_event(widget, <EVENT_CODE>, &some_data)`` and
``lv_indev_send_event(widget, <EVENT_CODE>, &some_data)``.
Fields of lv_event_t
********************
:cpp:type:`lv_event_t` is the only parameter passed to the event callback and it
contains all data about the event. The following values can be gotten from it:
- :cpp:expr:`lv_event_get_code(e)`: get the event code
- :cpp:expr:`lv_event_get_current_target(e)`: get Widget to which an event was sent. I.e. the Widget whose event handler is being called.
- :cpp:expr:`lv_event_get_target(e)`: get Widget that originally triggered the event (different from :cpp:func:`lv_event_get_target` if :ref:`event bubbling <event_bubbling>` is enabled)
- :cpp:expr:`lv_event_get_user_data(e)`: get the pointer passed as the last parameter of :cpp:func:`lv_obj_add_event_cb`.
- :cpp:expr:`lv_event_get_param(e)`: get the parameter passed as the last parameter of :cpp:func:`lv_obj_send_event_cb`
.. _event_bubbling:
Event Bubbling
**************
If :cpp:expr:`lv_obj_add_flag(widget, LV_OBJ_FLAG_EVENT_BUBBLE)` is enabled all
events will be sent to a Widget's parent as well. If the parent also has
:cpp:enumerator:`LV_OBJ_FLAG_EVENT_BUBBLE` enabled the event will be sent to its
parent, and so on.
The *target* parameter of the event is always the current target Widget,
not the original Widget. To get the original target call
:cpp:expr:`lv_event_get_target_obj(e)` in the event handler.
.. _events_examples:
Examples
********
.. include:: ../../examples/event/index.rst
.. _events_api:
API
***
.. API equals:
lv_cover_check_info_t
lv_display_add_event_cb
lv_event_get_code
lv_event_get_target_obj
lv_event_get_user_data
lv_event_register_id
LV_EVENT_RENDER_START
lv_event_t
lv_indev_active
lv_indev_add_event_cb
lv_indev_get_gesture_dir
lv_indev_get_key
lv_obj_add_event_cb
lv_obj_add_flag
lv_obj_get_event_count
lv_obj_remove_event
lv_obj_send_event

View File

@@ -0,0 +1,18 @@
.. _common_widget_features:
======================
Common Widget Features
======================
The following details apply to all types of Widgets.
.. toctree::
:maxdepth: 3
basics
coordinates
layers
styles/index
events
layouts/index
scrolling

View File

@@ -0,0 +1,87 @@
.. _layers:
======
Layers
======
When the term "layer" is used in LVGL documentation, it may refer to one of several
things:
1. for Widgets, the :ref:`layers_creation` creates a natural layering of Widgets;
2. in the context of pixel rendering (drawing), there are :ref:`draw_layers`;
3. permanent :ref:`display_screen_layers` are part of each :ref:`display` object.
#1 is covered below. #2 and #3 are covered in :ref:`draw_layers` and
:ref:`display_screen_layers` respectively.
.. _layers_creation:
Order of Creation
*****************
By default, LVGL draws new Widgets on top of old Widgets.
For example, assume we add a button to a parent Widget named button1 and
then another button named button2. Then button1 (along with its child
Widget(s)) will be in the background and can be covered by button2 and
its children.
.. image:: /_static/images/layers.png
.. code-block:: c
/* Create a screen */
lv_obj_t * scr = lv_obj_create(NULL);
lv_screen_load(scr); /* Load the screen */
/* Create 2 buttons */
lv_obj_t * btn1 = lv_button_create(scr); /* Create the first button on the screen */
lv_obj_set_pos(btn1, 60, 40); /* Set the position of the first button */
lv_obj_t * btn2 = lv_button_create(scr); /* Create the second button on the screen */
lv_obj_set_pos(btn2, 180, 80); /* Set the position of the second button */
/* Add labels to the buttons */
lv_obj_t * label1 = lv_label_create(btn1); /* Create a label on the first button */
lv_label_set_text(label1, "Button 1"); /* Set the text of the label */
lv_obj_t * label2 = lv_label_create(btn2); /* Create a label on the second button */
lv_label_set_text(label2, "Button 2"); /* Set the text of the label */
/* Delete the second label */
lv_obj_delete(label2);
.. _layers_order:
Changing Order
--------------
There are four explicit ways to bring a Widget to the foreground:
- Use :cpp:expr:`lv_obj_move_foreground(widget)` to bring a Widget to the foreground.
Similarly, use :cpp:expr:`lv_obj_move_background(widget)` to move it to the background.
- Use :cpp:expr:`lv_obj_move_to_index(widget, idx)` to move a Widget to a given index in the order of children.
- ``0``: background
- ``child_num - 1``: foreground
- ``< 0``: count from the top, to move forward (up): :cpp:expr:`lv_obj_move_to_index(widget, lv_obj_get_index(widget) - 1)`
- Use :cpp:expr:`lv_obj_swap(widget1, widget2)` to swap the relative layer position of two Widgets.
- When :cpp:expr:`lv_obj_set_parent(widget, new_parent)` is used, ``widget`` will be on the foreground of ``new_parent``.
.. _layers_api:
API
***
.. API equals:
lv_obj_move_foreground
lv_obj_move_to_index
lv_obj_swap
lv_obj_set_parent

View File

@@ -0,0 +1,231 @@
.. _flex:
====
Flex
====
Overview
********
The Flexbox (or Flex for short) is a subset of `CSS Flexbox`_ behaviors.
It can arrange items (child Widgets) into rows or columns (tracks), handle wrapping,
adjust the spacing between items and tracks, handle *grow* to make
item(s) fill remaining space with respect to min/max width and
height.
To make a Widget a Flex container call
:cpp:expr:`lv_obj_set_layout(widget, LV_LAYOUT_FLEX)`.
Note that the Flex layout feature of LVGL needs to be globally enabled
with :c:macro:`LV_USE_FLEX` in ``lv_conf.h``.
Terms
*****
- **tracks**: rows or columns
- **main direction**: row or column, the direction in which multiple items are
placed first
- **cross direction**: the direction perpendicular to the **main direction**
- **wrap**: if there is no more space in the track, a new track is started
- **grow**: if set on an item it will "grow" to fill the remaining space in
the track. The available space will be distributed among items
respective to their grow value (larger value means more space)
- **gap**: the space between rows and columns or the items on a track
See `CSS Flexbox`_ for illustrations showing the meanings of these terms.
Simple Interface
****************
Use the following functions to set and control the Flex layout on any parent Widget.
.. note::
The parent Widget must be a Flex container for these styles to take effect.
The functions below cause the parent Widget to become a Flex container if it is
not already.
.. _flex_flow:
Flex flow
---------
:cpp:expr:`lv_obj_set_flex_flow(widget, flex_flow)`
The possible values for ``flex_flow`` are:
- :cpp:enumerator:`LV_FLEX_FLOW_ROW`: Place the children in a row without wrapping
- :cpp:enumerator:`LV_FLEX_FLOW_COLUMN`: Place the children in a column without wrapping
- :cpp:enumerator:`LV_FLEX_FLOW_ROW_WRAP`: Place the children in a row with wrapping
- :cpp:enumerator:`LV_FLEX_FLOW_COLUMN_WRAP`: Place the children in a column with wrapping
- :cpp:enumerator:`LV_FLEX_FLOW_ROW_REVERSE`: Place the children in a row without wrapping but in reversed order
- :cpp:enumerator:`LV_FLEX_FLOW_COLUMN_REVERSE`: Place the children in a column without wrapping but in reversed order
- :cpp:enumerator:`LV_FLEX_FLOW_ROW_WRAP_REVERSE`: Place the children in a row with wrapping but in reversed order
- :cpp:enumerator:`LV_FLEX_FLOW_COLUMN_WRAP_REVERSE`: Place the children in a column with wrapping but in reversed order
These values cause the Widget's layout behavior to model `CSS Flexbox`_ behavior
by combining flex-direction_ and flex-wrap_ as defined under flex-flow_.
.. _flex_align:
Flex align
----------
To manage placement of children use
:cpp:expr:`lv_obj_set_flex_align(widget, main_place, cross_place, track_cross_place)`
which makes the parent Widget model the Flex-container behavior defined `here
<justify-content_>`_.
- ``main_place`` determines how to distribute the items in their track
on the main axis. E.g. flush the items to the right on
:cpp:enumerator:`LV_FLEX_FLOW_ROW_WRAP`. (It's called
justify-content_ in CSS.)
- ``cross_place`` determines how to distribute the items in their track
on the cross axis. E.g. if the items have different height, align them
against the bottom of the track. (It's called align-items_ in CSS.)
- ``track_cross_place`` determines how to distribute the tracks (It's
called align-content_ in CSS.)
The possible values are:
- :cpp:enumerator:`LV_FLEX_ALIGN_START`: means left when direction is horizontal, top when vertical (default)
- :cpp:enumerator:`LV_FLEX_ALIGN_END`: means right when direction is horizontal, bottom when vertical
- :cpp:enumerator:`LV_FLEX_ALIGN_CENTER`: simply center with respect to direction
- :cpp:enumerator:`LV_FLEX_ALIGN_SPACE_EVENLY`: items are distributed so
that the spacing between any two items (and the space to the edges) is
equal. Does not apply to ``track_cross_place``.
- :cpp:enumerator:`LV_FLEX_ALIGN_SPACE_AROUND`: items are evenly
distributed in the track with equal space around them. Note that
visually the spaces are not equal since all the items have equal space
on both sides. This makes the space between items double the space
between edge items and the container's edge. Does not apply to
``track_cross_place``.
- :cpp:enumerator:`LV_FLEX_ALIGN_SPACE_BETWEEN`: items are evenly distributed in
the track with no space before and after first and last items. Does not apply
to ``track_cross_place``.
See justify-content_, align-items_ and align-content_ for illustrations of these values.
.. _flex_grow:
Flex grow
---------
Flex grow can be used to make one or more children fill available space in the track.
When more than one child Widget have non-zero grow values, all available space will
be distributed in proportion to their respective grow values. For example, if there
is 400 px space remaining and 3 child Widgets with non-zero grow values:
- ``A`` with grow = 1
- ``B`` with grow = 1
- ``C`` with grow = 2
``A`` and ``B`` will occupy 100 px, and ``C`` will occupy 200 px.
Flex grow can be set on a child Widget with
:cpp:expr:`lv_obj_set_flex_grow(child, value)`. ``value`` needs to be >=
1 or 0 to disable grow on the child.
See flex-grow_ for an illustration of this behavior.
.. _flex_style:
Style Interface
***************
All Flex-related values are style properties under the hood so you
can use them as you would any other style property.
The following flex related style properties exist:
- :cpp:enumerator:`FLEX_FLOW`
- :cpp:enumerator:`FLEX_MAIN_PLACE`
- :cpp:enumerator:`FLEX_CROSS_PLACE`
- :cpp:enumerator:`FLEX_TRACK_PLACE`
- :cpp:enumerator:`FLEX_GROW`
.. _flex_padding:
Internal padding
----------------
To modify the minimum space flexbox inserts between Widgets, the
following functions can be used to set the flex container padding style:
- :cpp:func:`lv_style_set_pad_row` sets padding between rows.
- :cpp:func:`lv_style_set_pad_column` sets padding between columns.
These can, for example, be used if you do not want any padding between
Widgets: :cpp:expr:`lv_style_set_pad_column(&row_container_style, 0)`
.. _flex_other:
Other Features
**************
RTL
---
If the base direction of the container is set the
:cpp:enumerator:`LV_BASE_DIR_RTL` the meaning of
:cpp:enumerator:`LV_FLEX_ALIGN_START` and
:cpp:enumerator:`LV_FLEX_ALIGN_END` is swapped on ``ROW`` layouts. I.e.
``START`` will mean right.
The items on ``ROW`` layouts, and tracks of ``COLUMN`` layouts will be
placed from right to left.
New track
---------
You can force Flex to put an item into a new line with
:cpp:expr:`lv_obj_add_flag(child, LV_OBJ_FLAG_FLEX_IN_NEW_TRACK)`.
.. admonition:: Further Reading
Learn more about `CSS Flexbox`_.
.. _flex_examples:
Examples
********
.. include:: ../../../examples/layouts/flex/index.rst
.. Hyperlinks
.. _css flexbox: https://css-tricks.com/snippets/css/a-guide-to-flexbox/
.. _flex-direction: https://css-tricks.com/snippets/css/a-guide-to-flexbox/#aa-flex-direction
.. _flex-wrap: https://css-tricks.com/snippets/css/a-guide-to-flexbox/#aa-flex-wrap
.. _flex-flow: https://css-tricks.com/snippets/css/a-guide-to-flexbox/#aa-flex-flow
.. _justify-content: https://css-tricks.com/snippets/css/a-guide-to-flexbox/#aa-justify-content
.. _align-items: https://css-tricks.com/snippets/css/a-guide-to-flexbox/#aa-align-items
.. _align-content: https://css-tricks.com/snippets/css/a-guide-to-flexbox/#aa-align-content
.. _flex-grow: https://css-tricks.com/snippets/css/a-guide-to-flexbox/#aa-flex-grow
.. _flex_api:
API
***

View File

@@ -0,0 +1,227 @@
.. _grid:
====
Grid
====
Overview
********
The Grid layout is a subset of `CSS Grid`_ layout.
It can arrange items (child Widgets) into a 2D "table" that has rows and columns
(tracks). An item can span multiple columns or rows. The
track's size can be set in pixels, to the largest item
(:c:macro:`LV_GRID_CONTENT`), or to a fraction of the available free space
(i.e. `Free [FR] Units <fr units_>`_) to distribute free space proportionally.
To make a Widget a Grid container call :cpp:expr:`lv_obj_set_layout(widget, LV_LAYOUT_GRID)`.
Note that the Grid layout feature of LVGL needs to be globally enabled
with :c:macro:`LV_USE_GRID` in ``lv_conf.h``.
Terms
*****
- **tracks**: rows or columns
- **free (FR) units**: if a track's size is set in ``FR units`` it will grow
to fill the remaining space in the parent Widget (container), in proportion with
other tracks that have non-zero FR-unit values.
- **gap**: the space between rows and columns or the items on a track
Simple Interface
****************
With the following functions you can cause any parent Widget to have Grid-layout behaviors.
.. note::
As with Flex containers, the parent Widget must be a Grid container for these
styles to take effect. The functions below cause the parent Widget to become a
Grid container if it is not already.
.. _grid_descriptors:
Grid descriptors
----------------
First you need to describe the size of rows and columns. It can be done
by declaring 2 arrays and the track sizes in them. The last element must
be :c:macro:`LV_GRID_TEMPLATE_LAST`.
For example:
.. code-block:: c
static int32_t column_dsc[] = {100, 400, LV_GRID_TEMPLATE_LAST}; /* 2 columns with 100- and 400-px width */
static int32_t row_dsc[] = {100, 100, 100, LV_GRID_TEMPLATE_LAST}; /* 3 100-px tall rows */
To set the descriptors on a parent use
:cpp:expr:`lv_obj_set_grid_dsc_array(widget, col_dsc, row_dsc)`.
Besides settings the sizes in pixels, you can use two special
values:
- :c:macro:`LV_GRID_CONTENT` sets size to fit the largest child on this track
- :cpp:expr:`LV_GRID_FR(X)` determines what portion of the remaining space
should be used by this track. Larger values means larger space.
.. _grid_items:
Grid items
----------
By default, a Widget's children are not added to the grid. They need to be
added manually to a cell.
To do this call
:cpp:expr:`lv_obj_set_grid_cell(child, column_align, column_pos, column_span, row_align, row_pos, row_span)`.
``column_align`` and ``row_align`` determine how to align the child Widget
in its cell. Possible values are:
- :cpp:enumerator:`LV_GRID_ALIGN_START`: means left when direction is horizontal and top when vertical (default)
- :cpp:enumerator:`LV_GRID_ALIGN_END`: means right when direction is horizontal and bottom when vertical
- :cpp:enumerator:`LV_GRID_ALIGN_CENTER`: simply center ``column_pos`` and ``row_pos``
means the zero-based index of the cell in which the item should be placed.
``column_span`` and ``row_span`` means how many tracks should be occupied
from the start cell. Must be ``>= 1``.
.. _grid_align:
Grid align
----------
If there is some empty space, items (Widgets) in Grid tracks can be aligned in several ways:
- :cpp:enumerator:`LV_GRID_ALIGN_START`: means left when direction is horizontal and top when vertical. (default)
- :cpp:enumerator:`LV_GRID_ALIGN_END`: means right when direction is horizontal and bottom when vertical
- :cpp:enumerator:`LV_GRID_ALIGN_CENTER`: simply center
- :cpp:enumerator:`LV_GRID_ALIGN_SPACE_EVENLY`: items are distributed so that the spacing
between any two items (and the space to the edges) is equal. Not applies to ``track_cross_place``.
- :cpp:enumerator:`LV_GRID_ALIGN_SPACE_AROUND`: items are
evenly distributed in the track with equal space around them. Note that
visually the spaces aren't equal, since all the items have equal space
on both sides. This makes the space between items double the space
between edge items and the container's edge. Does not apply to ``track_cross_place``.
- :cpp:enumerator:`LV_GRID_ALIGN_SPACE_BETWEEN`: items are
evenly distributed in the track with first and last items next to container's edges.
Does not apply to ``track_cross_place``.
To set the track's alignment use
:cpp:expr:`lv_obj_set_grid_align(widget, column_align, row_align)`.
.. _grid_subgrid:
Sub grid
--------
If you set the column and/or row grid descriptors of a widget to ``NULL`` it will use
the grid descriptor(s) from it's parent.
For example if you create a grid item that spans columns 2..6 columns and rows 1..3
of the grid, the grid item will occupy 5 columns and 4 rows with the corresponding
track size from the parent Grid container.
This way even if a wrapper item is used in the grid, it can be made "transparent"
from the grid's point of view.
Limitations:
- The sub-grid is resolved only to a depth of 1 level. That is, a grid can have a
sub-grid child, but that sub-grid cannot have another sub-grid.
- ``LV_GRID_CONTENT`` tracks on the grid are not handled in the sub-grid, only in its
own grid.
The sub-grid feature works the same as in CSS. For further information, see
`CSS Subgrid`_.
.. _grid_style:
Style Interface
***************
All the Grid-related values are style properties under the hood so you
can use them as you would any other style property.
The following Grid-related style properties exist:
- :cpp:enumerator:`GRID_COLUMN_DSC_ARRAY`
- :cpp:enumerator:`GRID_ROW_DSC_ARRAY`
- :cpp:enumerator:`GRID_COLUMN_ALIGN`
- :cpp:enumerator:`GRID_ROW_ALIGN`
- :cpp:enumerator:`GRID_CELL_X_ALIGN`
- :cpp:enumerator:`GRID_CELL_COLUMN_POS`
- :cpp:enumerator:`GRID_CELL_COLUMN_SPAN`
- :cpp:enumerator:`GRID_CELL_Y_ALIGN`
- :cpp:enumerator:`GRID_CELL_ROW_POS`
- :cpp:enumerator:`GRID_CELL_ROW_SPAN`
.. _grid_padding:
Internal padding
----------------
To modify the minimum space Grid inserts between Widgets, the following
properties can be set on the Grid container style:
- :cpp:func:`lv_style_set_pad_row` sets padding between rows.
- :cpp:func:`lv_style_set_pad_column` sets padding between columns.
.. _grid_other:
Other features
**************
RTL
---
If the base direction of the container is set to :cpp:enumerator:`LV_BASE_DIR_RTL`,
the meaning of :cpp:enumerator:`LV_GRID_ALIGN_START` and :cpp:enumerator:`LV_GRID_ALIGN_END` is
swapped. I.e. ``START`` will mean right-most.
The columns will be placed from right to left.
.. admonition:: Further Reading
- Learn more about `CSS Grid`_ layout.
- Learn more about `CSS Subgrid`_ layout.
.. _grid_examples:
Example
*******
.. include:: ../../../examples/layouts/grid/index.rst
.. Hyperlinks
.. _css grid: https://css-tricks.com/snippets/css/complete-guide-grid/
.. _fr units: https://css-tricks.com/introduction-fr-css-unit/
.. _css subgrid: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout/Subgrid
.. _grid_api:
API
***

View File

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

View File

@@ -0,0 +1,328 @@
.. _scrolling:
=========
Scrolling
=========
Overview
********
In LVGL scrolling works very intuitively: if a Widget is outside its
parent content area (the size without padding), the parent becomes
scrollable and scrollbar(s) will appear. That's it.
Any Widget can be scrollable including :ref:`base_widget`, ``lv_image``,
``lv_button``, ``lv_meter``, etc
The Widget can either be scrolled horizontally or vertically in one
stroke; diagonal scrolling is not possible.
Scrollbar
---------
Mode
~~~~
Scrollbars are displayed according to the configured ``scrollbar-mode``. The
following modes are available:
- :cpp:enumerator:`LV_SCROLLBAR_MODE_OFF`: Never show the scrollbars
- :cpp:enumerator:`LV_SCROLLBAR_MODE_ON`: Always show the scrollbars
- :cpp:enumerator:`LV_SCROLLBAR_MODE_ACTIVE`: Show scroll bars while a Widget is being scrolled
- :cpp:enumerator:`LV_SCROLLBAR_MODE_AUTO`: Show scroll bars when the content is large enough to be scrolled
:cpp:expr:`lv_obj_set_scrollbar_mode(widget, LV_SCROLLBAR_MODE_...)` sets the scrollbar mode on a Widget.
Styling
~~~~~~~
A Scrollbar is a dedicated part of a Widget, called
:cpp:enumerator:`LV_PART_SCROLLBAR`. For example, a scrollbar can turn to red like
this:
.. code-block:: c
static lv_style_t style_red;
lv_style_init(&style_red);
lv_style_set_bg_color(&style_red, lv_color_red());
...
lv_obj_add_style(widget, &style_red, LV_PART_SCROLLBAR);
A Widget goes to the :cpp:enumerator:`LV_STATE_SCROLLED` state while it's being
scrolled. This allows adding different styles to the Widget that will be effective
while it is being scrolled. For example, this code makes the scrollbar blue while
the Widget is being scrolled:
.. code-block:: c
static lv_style_t style_blue;
lv_style_init(&style_blue);
lv_style_set_bg_color(&style_blue, lv_color_blue());
...
lv_obj_add_style(widget, &style_blue, LV_STATE_SCROLLED | LV_PART_SCROLLBAR);
If the base direction of the :cpp:enumerator:`LV_PART_SCROLLBAR` is RTL
(:c:macro:`LV_BASE_DIR_RTL`) the vertical scrollbar will be placed on the left.
Note that, the ``base_dir`` style property is inherited. Therefore, it
can be set directly on the :cpp:enumerator:`LV_PART_SCROLLBAR` part of a Widget, or
on the Widget's LV_PART_MAIN part, or that of any of its parents, to make a scrollbar
inherit the base direction.
``pad_left/right/top/bottom`` sets the spacing around the scrollbars,
``width`` sets the scrollbar's width and ``length`` sets the scrollbar's length:
If `length` is not set or left at `0` the scrollbar's length will be set automatically according to the length of the content.
.. code-block:: c
static lv_style_t style_scrollbar;
lv_style_init(&style_scrollbar);
lv_style_set_pad_left(&style_scrollbar, 2);
lv_style_set_pad_right(&style_scrollbar, 2);
lv_style_set_pad_top(&style_scrollbar, 2);
lv_style_set_pad_bottom(&style_scrollbar, 2);
lv_style_set_width(&style_scrollbar, 10);
lv_style_set_length(&style_scrollbar, 50);
...
lv_obj_add_style(widget, &style_scrollbar, LV_PART_SCROLLBAR);
The minimum length of the scrollbar is fixed to 10, while its maximum length is limited by the
Widget's height or width, depending on whether the scrollbar is vertical or horizontal. Any length value
set outside of these limits will automatically result in a length fixed to either limit.
.. _scroll_events:
Scrolling Events
----------------
The following events are emitted as part of scrolling:
- :cpp:enumerator:`LV_EVENT_SCROLL_BEGIN`: Signals that scrolling has begun. The
event parameter is ``NULL`` or an ``lv_anim_t *`` with a scroll animation
descriptor that can be modified if required.
- :cpp:enumerator:`LV_EVENT_SCROLL_END`: Signals that scrolling has ended.
- :cpp:enumerator:`LV_EVENT_SCROLL`: Signals that the scrolling position changed;
triggered on every position change.
Features of Scrolling
*********************
Besides, managing "normal" scrolling there are many interesting and
useful additional features.
Scrollable
----------
It is possible to make a Widget non-scrollable with
:cpp:expr:`lv_obj_remove_flag(widget, LV_OBJ_FLAG_SCROLLABLE)`.
Non-scrollable Widgets can still propagate the scrolling (chain) to
their parents.
The direction in which scrolling happens can be controlled by
:cpp:expr:`lv_obj_set_scroll_dir(widget, LV_DIR_...)`.
The following values can be used for the direction:
- :cpp:enumerator:`LV_DIR_TOP`: only scroll up
- :cpp:enumerator:`LV_DIR_LEFT`: only scroll left
- :cpp:enumerator:`LV_DIR_BOTTOM`: only scroll down
- :cpp:enumerator:`LV_DIR_RIGHT`: only scroll right
- :cpp:enumerator:`LV_DIR_HOR`: only scroll horizontally
- :cpp:enumerator:`LV_DIR_VER`: only scroll vertically
- :cpp:enumerator:`LV_DIR_ALL`: scroll any directions
OR-ed values are also possible. E.g. :cpp:expr:`LV_DIR_TOP | LV_DIR_LEFT`.
Scroll chaining
---------------
If a Widget can't be scrolled further (e.g. its content has reached the
bottom-most position), additional scrolling is propagated to its parent.
If the parent can be scrolled in that direction than it will be scrolled
instead. It continues propagating up the Widget's parent hierarchy up to
the :ref:`Screen <screens>`.
The propagation on scrolling is called "scroll chaining" and it can be
enabled/disabled with ``LV_OBJ_FLAG_SCROLL_CHAIN_HOR/VER`` flag. If
chaining is disabled the propagation stops on the Widget and the
parent(s) won't be scrolled.
Scroll momentum
---------------
When the user scrolls a Widget and releases it, LVGL can emulate
inertial momentum for the scrolling. It's like the Widget was "thrown"
and scrolling slows down smoothly.
Scroll momentum can be enabled/disabled with the
:cpp:enumerator:`LV_OBJ_FLAG_SCROLL_MOMENTUM` flag.
Elastic scroll
--------------
Normally a Widget can't be scrolled past the extremities of its
content. That is, the top side of the content can't be below the top side
of the Widget, and vice versa for the bottom side.
However, with :cpp:enumerator:`LV_OBJ_FLAG_SCROLL_ELASTIC` a fancy effect is added
when the user "over-scrolls" the content. The scrolling slows down, and
the content can be scrolled inside the Widget. When the Widget is
released the content scrolled in it is animated back to the closest valid
position.
Snapping
--------
The children of a Widget can be snapped according to specific rules
when scrolling ends. Children can be made snappable individually with
the :cpp:enumerator:`LV_OBJ_FLAG_SNAPPABLE` flag.
A Widget can align snapped children in four ways:
- :cpp:enumerator:`LV_SCROLL_SNAP_NONE`: Snapping is disabled. (default)
- :cpp:enumerator:`LV_SCROLL_SNAP_START`: Align the children to the left/top side of a scrolled Widget
- :cpp:enumerator:`LV_SCROLL_SNAP_END`: Align the children to the right/bottom side of a scrolled Widget
- :cpp:enumerator:`LV_SCROLL_SNAP_CENTER`: Align the children to the center of a scrolled Widget
Snap alignment is set with
:cpp:expr:`lv_obj_set_scroll_snap_x(widget, LV_SCROLL_SNAP_...)` and
:cpp:expr:`lv_obj_set_scroll_snap_y(widget, LV_SCROLL_SNAP_...)`.
This is what happens under the hood:
1. user scrolls and releases a Widget;
2. LVGL calculates where the scroll would end considering scroll momentum;
3. LVGL finds the nearest scroll point;
4. LVGL scrolls to the snap point with an animation.
Scroll one
----------
The "scroll one" feature tells LVGL to allow scrolling only one
snappable child at a time. This requires making the children snappable
and setting scroll snap alignment to something other than
:cpp:enumerator:`LV_SCROLL_SNAP_NONE`.
This feature can be enabled by the :cpp:enumerator:`LV_OBJ_FLAG_SCROLL_ONE` flag.
Scroll on focus
---------------
Imagine that there are a lot of Widgets in a group that are on a scrollable
Widget. Pressing the "Tab" button moves focus to the next Widget but it might
be outside the visible area of the scrollable Widget. If the "scroll on
focus" feature is enabled LVGL will automatically scroll Widgets to
bring the child Widget with focus into view. The scrolling happens recursively
therefore even nested scrollable Widgets are handled properly. The
Widget will be scrolled into view even if it is on a different page of a
tabview.
Scrolling Programmatically
**************************
The following API functions allow programmatic scrolling of Widgets:
- ``lv_obj_scroll_by(widget, x, y, LV_ANIM_ON/OFF)`` scroll by ``x`` and ``y`` values
- ``lv_obj_scroll_to(widget, x, y, LV_ANIM_ON/OFF)`` scroll to bring the given coordinate to the top left corner
- ``lv_obj_scroll_to_x(widget, x, LV_ANIM_ON/OFF)`` scroll to bring the given coordinate to the left side
- ``lv_obj_scroll_to_y(widget, y, LV_ANIM_ON/OFF)`` scroll to bring the given coordinate to the top side
From time to time you may need to retrieve the *scroll position* of a
scrollable Widget, either to restore it later, or to dynamically display some
elements according to its current scroll position. Here is an example to illustrate
how to combine scroll event and store the scroll-top position.
.. code-block:: c
static int scroll_value = 0;
static void store_scroll_top_value_event_cb(lv_event_t* e) {
lv_obj_t * scr = lv_event_get_target(e);
scroll_value = lv_obj_get_scroll_top(scr);
printf("%d pixels are scrolled above top edge of display.\n", scroll_value);
}
lv_obj_t * scr = lv_obj_create(NULL);
lv_obj_add_event_cb(scr, store_scroll_top_value_event_cb, LV_EVENT_SCROLL, NULL);
Scroll coordinates can be retrieved from different axes with these functions:
- :cpp:expr:`lv_obj_get_scroll_x(widget)` Pixels scrolled past left edge of Widget's view window.
- :cpp:expr:`lv_obj_get_scroll_y(widget)` Pixels scrolled past top of Widget's view window.
- :cpp:expr:`lv_obj_get_scroll_top(widget)` Identical to :cpp:expr:`lv_obj_get_scroll_y(widget)`
- :cpp:expr:`lv_obj_get_scroll_bottom(widget)` Pixels scrolled past bottom of Widget's view window.
- :cpp:expr:`lv_obj_get_scroll_left(widget)` Identical to :cpp:expr:`lv_obj_get_scroll_x(widget)`.
- :cpp:expr:`lv_obj_get_scroll_right(widget)` Pixels scrolled past right edge of Widget's view window.
Setting scroll position can be done with these functions:
- :cpp:expr:`lv_obj_scroll_by(widget, dx, dy, anim_enable)` Scroll by given amount of pixels.
- :cpp:expr:`lv_obj_scroll_by_bounded(widget, dx, dy, animation_enable)` Scroll by given amount of pixels.
- :cpp:expr:`lv_obj_scroll_to(widget, x, y, animation_enable)` Scroll to given coordinate on Widget.
- :cpp:expr:`lv_obj_scroll_to_x(widget, x, animation_enable)` Scroll to X coordinate on Widget.
- :cpp:expr:`lv_obj_scroll_to_y(widget, y, animation_enable)` Scroll to Y coordinate on Widget.
- :cpp:expr:`lv_obj_scroll_to_view(widget, animation_enable)` Scroll ``obj``'s parent Widget until ``obj`` becomes visible.
- :cpp:expr:`lv_obj_scroll_to_view_recursive(widget, animation_enable)` Scroll ``obj``'s parent Widgets recursively until ``obj`` becomes visible.
Self Size
*********
Self size is a property of a Widget. Normally, the user shouldn't use
this parameter but if a custom widget is created it might be useful.
In short, self size establishes the size of a Widget's content. To
understand it better take the example of a table. Let's say it has 10
rows each with 50 px height. So the total height of the content is 500
px. In other words the "self height" is 500 px. If the user sets only
200 px height for the table LVGL will see that the self size is larger
and make the table scrollable.
This means not only the children can make a Widget scrollable but a
larger self size will as well.
LVGL uses the :cpp:enumerator:`LV_EVENT_GET_SELF_SIZE` event to get the self size of
a Widget. Here is an example to see how to handle the event:
.. code-block:: c
if(event_code == LV_EVENT_GET_SELF_SIZE) {
lv_point_t * p = lv_event_get_param(e);
/* If x or y < 0 then it doesn't need to be calculated now. */
if(p->x >= 0) {
p->x = 200; /* Set or calculate self width. */
}
if(p->y >= 0) {
p->y = 50; /* Set or calculate self height. */
}
}
.. _scroll_example:
Examples
********
.. include:: ../../examples/scroll/index.rst
.. _scroll_api:
API
***

View File

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

View File

@@ -0,0 +1,598 @@
.. _styles_overview:
===============
Styles Overview
===============
Styles are used to set the appearance of Widgets. Styles in LVGL are
heavily inspired by CSS. The concept in a nutshell is that a
style is an :cpp:type:`lv_style_t` variable which can hold properties like
border width, font, text color and so on. It's similar to a ``class`` in CSS.
- Styles can be assigned to Widgets to change their appearance. Upon
assignment, the target part (pseudo-element_ in CSS) and target state
(pseudo-class_ in CSS) can be specified. For example one can add
``style_blue`` to the knob of a slider when it's in pressed state.
- The same style can be used by any number of Widgets.
- Styles can be cascaded which means multiple styles may be assigned to a Widget and
each style can have different properties. Therefore, not all properties
have to be specified in a style. LVGL will search for a property until a
style defines it or use a default value if it's not specified by any of the
styles. For example ``style_btn`` can result in a default gray button
and ``style_btn_red`` can add only a ``background-color=red`` to
overwrite the background color.
- The most recently added style has higher precedence. This means if a property
is specified in two styles the newest style in the Widget will be used.
- Some properties (e.g. text color) can be inherited from a parent(s) if it's not specified in a Widget.
- Widgets can also have :ref:`local styles <style_local>` with higher precedence than "normal" styles.
- Unlike CSS (where pseudo-classes_ describe different states, e.g. ``:focus``),
in LVGL a property is assigned to a given state.
- Transitions can be applied when the Widget changes state.
.. _style_states:
States
******
The Widgets can be in the combination of the following states:
- :cpp:enumerator:`LV_STATE_DEFAULT`: (0x0000) Normal, released state
- :cpp:enumerator:`LV_STATE_CHECKED`: (0x0001) Toggled or checked state
- :cpp:enumerator:`LV_STATE_FOCUSED`: (0x0002) Focused via keypad or encoder or clicked via touchpad/mouse
- :cpp:enumerator:`LV_STATE_FOCUS_KEY`: (0x0004) Focused via keypad or encoder but not via touchpad/mouse
- :cpp:enumerator:`LV_STATE_EDITED`: (0x0008) Edit by an encoder
- :cpp:enumerator:`LV_STATE_HOVERED`: (0x0010) Hovered by mouse
- :cpp:enumerator:`LV_STATE_PRESSED`: (0x0020) Being pressed
- :cpp:enumerator:`LV_STATE_SCROLLED`: (0x0040) Being scrolled
- :cpp:enumerator:`LV_STATE_DISABLED`: (0x0080) Disabled state
- :cpp:enumerator:`LV_STATE_USER_1`: (0x1000) Custom state
- :cpp:enumerator:`LV_STATE_USER_2`: (0x2000) Custom state
- :cpp:enumerator:`LV_STATE_USER_3`: (0x4000) Custom state
- :cpp:enumerator:`LV_STATE_USER_4`: (0x8000) Custom state
A Widget can be in a combination of states such as being focused and
pressed at the same time. This is represented as :cpp:expr:`LV_STATE_FOCUSED | LV_STATE_PRESSED`.
A style can be added to any state or state combination. For example,
setting a different background color for the default and pressed states.
If a property is not defined in a state the best matching state's
property will be used. Typically this means the property with
:cpp:enumerator:`LV_STATE_DEFAULT` is used.˛ If the property is not set even for the
default state the default value will be used. (See later)
Since :cpp:enumerator:`LV_PART_MAIN` and :cpp:enumerator:`LV_STATE_DEFAULT` both
have zero values, you can simply pass ``0`` as the ``selector`` argument instead of
``LV_PART_MAIN | LV_STATE_DEFAULT`` as a shortcut when adding styles to an object.
What does the "best matching state's property" mean?
----------------------------------------------------
States have a precedence which is shown by their value (see in the above list).
A higher value means higher precedence. To determine which state's
property to use let's take an example. Imagine the background color is
defined like this:
- :cpp:enumerator:`LV_STATE_DEFAULT`: white
- :cpp:enumerator:`LV_STATE_PRESSED`: gray
- :cpp:enumerator:`LV_STATE_FOCUSED`: red
1. Initially the Widget is in the default state, so it's a simple case:
the property is perfectly defined in the Widget's current state as
white.
2. When the Widget is pressed there are 2 related properties: default
with white (default is related to every state) and pressed with gray.
The pressed state has 0x0020 precedence which is higher than the
default state's 0x0000 precedence, so gray color will be used.
3. When the Widget has focus the same thing happens as in pressed state
and red color will be used. (Focused state has higher precedence than
default state).
4. When the Widget has focus and pressed both gray and red would work,
but the pressed state has higher precedence than focused so gray
color will be used.
5. It's possible to set e.g. rose color for :cpp:expr:`LV_STATE_PRESSED | LV_STATE_FOCUSED`.
In this case, this combined state has 0x0020 + 0x0002 = 0x0022 precedence, which is higher than
the pressed state's precedence so rose color would be used.
6. When the Widget is in the checked state there is no property to set
the background color for this state. So for lack of a better option,
the Widget remains white from the default state's property.
Some practical notes:
- The precedence (value) of states is quite intuitive, and it's something the
user would expect naturally. Example: if a Widget has focus the user will still
want to see if it's pressed, therefore the pressed state has a higher
precedence. If the focused state had a higher precedence it would overwrite
the pressed color.
- If you want to set a property for all states (e.g. red background color)
just set it for the default state. If the Widget can't find a property
for its current state it will fall back to the default state's property.
- Use ORed states to describe the properties for complex cases (e.g.
pressed + checked + focused).
- It might be a good idea to use different
style elements for different states. For example, finding background
colors for released, pressed, checked + pressed, focused, focused +
pressed, focused + pressed + checked, etc. states is quite difficult.
Instead, for example, use the background color for pressed and checked
states and indicate the focused state with a different border color.
.. _style_cascading:
Cascading Styles
****************
It's not required to set all the properties in one style. It's possible
to add more styles to a Widget and have the latter added style modify
or extend appearance. For example, create a general gray button style
and create a new one for red buttons where only the new background color
is set.
This is much like in CSS when used classes are listed like
``<div class=".btn .btn-red">``.
Styles added later have precedence over ones set earlier. So in the
gray/red button example above, the normal button style should be added
first and the red style second. However, the precedence of the states
are still taken into account. So let's examine the following case:
- the basic button style defines dark-gray color for the default state and
light-gray color for the pressed state
- the red button style defines the background color as red only in the default state
In this case, when the button is released (it's in default state) it
will be red because a perfect match is found in the most recently added
style (red). When the button is pressed the light-gray color is a better
match because it describes the current state perfectly, so the button
will be light-gray.
.. _style_inheritance:
Inheritance
***********
Some properties (typically those related to text) can be inherited from
the parent Widget's styles. Inheritance is applied only if the given
property is not set in the Widget's styles (even in default state). In
this case, if the property is inheritable, the property's value will be
searched up the parent hierarchy until a Widget specifies a value for the
property. The parents will use their own state to determine the value.
So if a button is pressed, and the text color comes from a parent, the
pressed text color will be used.
.. _style_parts:
Parts
*****
Widgets can be composed of *parts* which may each have their own styles.
The following predefined parts exist in LVGL:
- :cpp:enumerator:`LV_PART_MAIN`: (0x000000) A background like rectangle
- :cpp:enumerator:`LV_PART_SCROLLBAR`: (0x010000) The scrollbar(s)
- :cpp:enumerator:`LV_PART_INDICATOR`: (0x020000) Indicator, e.g. for slider, bar, switch, or the tick box of the checkbox
- :cpp:enumerator:`LV_PART_KNOB`: (0x030000) Like a handle to grab to adjust a value
- :cpp:enumerator:`LV_PART_SELECTED`: (0x040000) Indicate the currently selected option or section
- :cpp:enumerator:`LV_PART_ITEMS`: (0x050000) Used if the widget has multiple similar elements (e.g. table cells)
- :cpp:enumerator:`LV_PART_CURSOR`: (0x060000) Mark a specific place e.g. Text Area's or chart's cursor
- :cpp:enumerator:`LV_PART_CUSTOM_FIRST`: (0x080000) Custom part identifiers can be added starting from here.
- :cpp:enumerator:`LV_PART_ANY`: (0x0F0000) Special value can be used in some functions to target all parts.
For example a :ref:`Slider <lv_slider>` has three parts:
- Main (background)
- Indicator
- Knob
This means all three parts of the slider can have their own styles. See
later how to add styles to Widgets and parts.
Since :cpp:enumerator:`LV_PART_MAIN` and :cpp:enumerator:`LV_STATE_DEFAULT` both
have zero values, you can simply pass ``0`` as the ``selector`` argument instead of
``LV_PART_MAIN | LV_STATE_DEFAULT`` as a shortcut when adding styles to an object.
.. _style_initialize:
Initialize Styles and Set/Get Properties
****************************************
Styles are stored in :cpp:type:`lv_style_t` variables. Style variables should be
``static``, global or dynamically allocated. In other words they cannot
be local variables in functions which are destroyed when the function
exits. Before using a style it should be initialized with
:cpp:expr:`lv_style_init(&my_style)`. After initializing a style, properties can
be added or changed.
Property set functions looks like this:
``lv_style_set_<property_name>(&style, <value>);`` For example:
.. code-block:: c
static lv_style_t style_btn;
lv_style_init(&style_btn);
lv_style_set_bg_color(&style_btn, lv_color_hex(0x115588));
lv_style_set_bg_opa(&style_btn, LV_OPA_50);
lv_style_set_border_width(&style_btn, 2);
lv_style_set_border_color(&style_btn, lv_color_black());
static lv_style_t style_btn_red;
lv_style_init(&style_btn_red);
lv_style_set_bg_color(&style_btn_red, lv_palette_main(LV_PALETTE_RED));
lv_style_set_bg_opa(&style_btn_red, LV_OPA_COVER);
To remove a property use:
.. code-block:: c
lv_style_remove_prop(&style, LV_STYLE_BG_COLOR);
To get a property's value from a style:
.. code-block:: c
lv_style_value_t v;
lv_result_t res = lv_style_get_prop(&style, LV_STYLE_BG_COLOR, &v);
if(res == LV_RESULT_OK) { /* Found */
do_something(v.color);
}
:cpp:union:`lv_style_value_t` has 3 fields, only one of which will apply, depending
on the type of property it is applied to:
- :cpp:member:`num`: for integer, boolean and opacity properties
- :cpp:member:`color`: for color properties
- :cpp:member:`ptr`: for pointer properties
To reset a style (free all its data) use:
.. code-block:: c
lv_style_reset(&style);
Styles can be built as ``const`` as well to save RAM:
.. code-block:: c
const lv_style_const_prop_t style1_props[] = {
LV_STYLE_CONST_WIDTH(50),
LV_STYLE_CONST_HEIGHT(50),
LV_STYLE_CONST_PROPS_END
};
LV_STYLE_CONST_INIT(style1, style1_props);
Later ``const`` style can be used like any other style but (obviously)
new properties cannot be added.
.. _style_add_remove:
Add and remove styles to a widget
*********************************
A style on its own has no effect until it is added (assigned) to a Widget.
Add styles
----------
To add a style to a Widget use
``lv_obj_add_style(widget, &style, <selector>)``. ``<selector>`` is an
OR-ed value of parts and state to which the style should be added. Some
examples:
- :cpp:expr:`LV_PART_MAIN | LV_STATE_DEFAULT`
- :cpp:enumerator:`LV_STATE_PRESSED`: The main part in pressed state. :cpp:enumerator:`LV_PART_MAIN` can be omitted
- :cpp:enumerator:`LV_PART_SCROLLBAR`: The scrollbar part in the default state. :cpp:enumerator:`LV_STATE_DEFAULT` can be omitted.
- :cpp:expr:`LV_PART_SCROLLBAR | LV_STATE_SCROLLED`: The scrollbar part when the Widget is being scrolled
- :cpp:expr:`LV_PART_INDICATOR | LV_STATE_PRESSED | LV_STATE_CHECKED` The indicator part when the Widget is pressed and checked at the same time.
Using :cpp:func:`lv_obj_add_style`:
.. code-block:: c
lv_obj_add_style(btn, &style_btn, 0); /* Default button style */
lv_obj_add_style(btn, &btn_red, LV_STATE_PRESSED); /* Overwrite only some colors to red when pressed */
Replace styles
--------------
To replace a specific style of a Widget use
:cpp:expr:`lv_obj_replace_style(widget, old_style, new_style, selector)`. This
function will only replace ``old_style`` with ``new_style`` if the
``selector`` matches the ``selector`` used in ``lv_obj_add_style``. Both
``old_style`` and ``new_style`` must not be ``NULL``. Separate functions exist for
adding and removing styles. If the combination of
``old_style`` and ``selector`` exists multiple times in ``obj``\ 's
styles, all occurrences will be replaced. The return value of the
function indicates whether at least one successful replacement took
place.
Using :cpp:func:`lv_obj_replace_style`:
.. code-block:: c
lv_obj_add_style(btn, &style_btn, 0); /* Add a button style */
lv_obj_replace_style(btn, &style_btn, &new_style_btn, 0); /* Replace the button style with a different one */
Remove styles
-------------
To remove all styles from a Widget use :cpp:expr:`lv_obj_remove_style_all(widget)`.
To remove specific styles use
:cpp:expr:`lv_obj_remove_style(widget, style, selector)`. This function will remove
``style`` only if the ``selector`` matches with the ``selector`` used in
:cpp:func:`lv_obj_add_style`. ``style`` can be ``NULL`` to check only the
``selector`` and remove all matching styles. The ``selector`` can use
the :cpp:enumerator:`LV_STATE_ANY` and :cpp:enumerator:`LV_PART_ANY` values to remove the style from
any state or part.
Reporting style changes
-----------------------
If a style which is already assigned to a Widget changes (i.e. a
property is added or changed), the Widgets using that style should be
notified. There are 3 options to do this:
1. If you know that the changed properties can be applied by a simple redraw
(e.g. color or opacity changes) just call :cpp:expr:`lv_obj_invalidate(widget)`
or :cpp:expr:`lv_obj_invalidate(lv_screen_active())`.
2. If more complex style properties were changed or added, and you know which
Widget(s) are affected by that style call :cpp:expr:`lv_obj_refresh_style(widget, part, property)`.
To refresh all parts and properties use :cpp:expr:`lv_obj_refresh_style(widget, LV_PART_ANY, LV_STYLE_PROP_ANY)`.
3. To make LVGL check all Widgets to see if they use a style and refresh them
when needed, call :cpp:expr:`lv_obj_report_style_change(&style)`. If ``style``
is ``NULL`` all Widgets will be notified about a style change.
Get a style property's value on a Widget
----------------------------------------
To get the final value of a style's property considering
- cascading,
- inheritance,
- local styles and transitions (see below)
property "get" functions like this can be used: ``lv_obj_get_style_<property_name>(widget, <part>)``.
These functions use the Widget's current state and if no better candidate exists they return the default value.
For example:
.. code-block:: c
lv_color_t color = lv_obj_get_style_bg_color(btn, LV_PART_MAIN);
.. _style_local:
Local Styles
************
In addition to "normal" styles, Widgets can also store local styles.
This concept is similar to inline styles in CSS
(e.g. ``<div style="color:red">``) with some modification.
Local styles are like normal styles, but they can't be shared among
other Widgets. If used, local styles are allocated automatically, and
freed when the Widget is deleted. They are useful to add local
customization to a Widget.
Unlike in CSS, LVGL local styles can be assigned to states
(pseudo-classes_) and parts (pseudo-elements_).
To set a local property use functions like
``lv_obj_set_style_<property_name>(widget, <value>, <selector>);`` For example:
.. code-block:: c
lv_obj_set_style_bg_color(slider, lv_color_red(), LV_PART_INDICATOR | LV_STATE_FOCUSED);
.. _style_properties_overview:
Style Properties Overview
*************************
For the full list of style properties click :ref:`here <style_properties>`.
.. _typical bg props:
Typical background properties
-----------------------------
In documentation of widgets you will see sentences like "The
_____ Widget uses the typical background style properties". These "typical
background properties" are the properties being referred to:
- Background
- Border
- Outline
- Shadow
- Padding
- Width and height transformation
- X and Y translation
See :ref:`boxing_model` for the meanings of these terms.
.. _style_transitions:
Transitions
***********
By default, when a Widget changes state (e.g. it's pressed) the new
properties from the new state are set immediately. However, with
transitions it's possible to play an animation on state change. For
example, on pressing a button its background color can be animated to
the pressed color over 300 ms.
The parameters of the transitions are stored in the styles. It's
possible to set
- the time of the transition
- the delay before starting the transition
- the animation path (also known as the timing or easing function)
- the properties to animate
The transition properties can be defined for each state. For example,
setting a 500 ms transition time in the default state means that when
the Widget goes to the default state a 500 ms transition time is
applied. Setting a 100 ms transition time in the pressed state causes a
100 ms transition when going to the pressed state. This example
configuration results in going to the pressed state quickly and then
going back to default slowly.
To describe a transition an :cpp:struct:`lv_transition_dsc_t` variable needs to be
initialized and added to a style:
.. code-block:: c
/* Only its pointer is saved so must static, global or dynamically allocated */
static const lv_style_prop_t trans_props[] = {
LV_STYLE_BG_OPA, LV_STYLE_BG_COLOR,
0, /* End marker */
};
static lv_style_transition_dsc_t trans1;
lv_style_transition_dsc_init(&trans1, trans_props, lv_anim_path_ease_out, duration_ms, delay_ms);
lv_style_set_transition(&style1, &trans1);
.. _style_opacity_blend_modes_transformations:
Opacity, Blend Modes and Transformations
****************************************
If the ``opa``, ``blend_mode``, ``transform_angle``, or
``transform_zoom`` properties are set to a non-default value LVGL
creates a snapshot of the widget and its children in order to
blend the whole widget with the set opacity, blend mode and
transformation properties.
These properties have this effect only on the ``MAIN`` part of the
widget.
The created snapshot is called "intermediate layer" or simply "layer".
If only ``opa`` and/or ``blend_mode`` is set to a non-default value LVGL
can build the layer from smaller chunks. The size of these chunks can be
configured by the following properties in ``lv_conf.h``:
- :cpp:enumerator:`LV_LAYER_SIMPLE_BUF_SIZE`: [bytes] the optimal target buffer size. LVGL will try to allocate this size of memory.
- :cpp:enumerator:`LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE`: [bytes] used if :cpp:enumerator:`LV_LAYER_SIMPLE_BUF_SIZE` couldn't be allocated.
If transformation properties were also used the layer cannot be
rendered in chunks, but one larger memory block needs to be allocated. The
required memory depends on the angle, zoom and pivot parameters, and the
size of the area to redraw, but it's never larger than the size of the
widget (including the extra draw size used for shadow, outline, etc).
If the widget can fully cover the area to redraw, LVGL creates an RGB
layer (which is faster to render and uses less memory). If the opposite
case ARGB rendering needs to be used, a widget might not cover its area
if it has radius, ``bg_opa < 255``, has shadow, outline, etc.
The click area of the widget is also transformed accordingly.
.. _style_color_filter:
Color Filter
************
TODO
.. _style_themes:
Themes
******
Themes are a collection of styles. If there is an active theme LVGL
applies it to every newly-created widget. This will give a default appearance
to the UI which can then be modified by adding further styles.
Every display can have a different theme. For example, you could have a
colorful theme on a TFT and monochrome theme on a secondary monochrome
display.
To set a theme for a display, two steps are required:
1. Initialize a theme
2. Assign the initialized theme to a display.
Theme initialization functions can have different prototypes. This
example shows how to set the "default" theme:
.. code-block:: c
lv_theme_t * th = lv_theme_default_init(display, /* Use DPI, size, etc. from this display */
LV_COLOR_PALETTE_BLUE, /* Primary and secondary palette */
LV_COLOR_PALETTE_CYAN,
false, /* Dark theme? False = light theme. */
&lv_font_montserrat_10, /* Small, normal, large fonts */
&lv_font_montserrat_14,
&lv_font_montserrat_18);
lv_display_set_theme(display, th); /* Assign theme to display */
The included themes are enabled in ``lv_conf.h``. If the default theme
is enabled by :c:macro:`LV_USE_THEME_DEFAULT` LVGL automatically initializes
and sets it when a display is created.
Extending themes
----------------
Built-in themes can be extended. If a custom theme is created, a parent
theme can be selected. The parent theme's styles will be added before
the custom theme's styles. Any number of themes can be chained this way.
E.g. default theme -> custom theme -> dark theme.
:cpp:expr:`lv_theme_set_parent(new_theme, base_theme)` extends the
``base_theme`` with the ``new_theme``.
There is an example of this below.
.. _styles_example:
Examples
********
.. include:: ../../../examples/styles/index.rst
.. Hyperlinks
.. _pseudo-elements:
.. _pseudo-element: https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Selectors#pseudo-classes_and_pseudo-elements
.. _pseudo-classes:
.. _pseudo-class: https://developer.mozilla.org/en-US/docs/Glossary/Pseudo-class
.. _styles_api:
API
***
.. API equals: lv_style_init, lv_style_t, lv_obj_add_style, LV_PART_MAIN, LV_STATE_DEFAULT
.. API startswith: lv_style_set_, lv_obj_set_

View File

@@ -0,0 +1,91 @@
.. _gdb_plugin:
===========
GDB Plug-In
===========
Debugging LVGL with GDB
-----------------------
To facilitate debugging LVGL with GDB, a GDB plugin is provided. This plugin
can be found in the ``lvgl/scripts/gdb`` directory. The GDB plugin can be used
with any target where GDB is available. For example, you can use it to debug a
device connected to a PC via JLink, which provides a GDB server. Additionally,
if your device crashes and you have a core dump, you can use GDB to analyze the
core dump. To load the LVGL GDB plugin within GDB's command line, type the
following command:
``source lvgl/scripts/gdb/gdbinit.py``
Example of usage:
.. code:: bash
(gdb) source lvgl/scripts/gdb/gdbinit.py
(gdb) dump obj -L 2
obj@0x60700000dd10 (0,0,799,599)
tabview@0x608000204ca0 (0,0,799,599)
obj@0x607000025da0 (0,0,799,69)
obj@0x607000025e80 (0,70,799,599)
obj@0x60700002bd70 (743,543,791,591)
btn@0x60700002c7f0 (747,547,787,587)
keyboard@0x60d0000f7040 (0,300,799,599)
dropdown-list@0x608000205420 (0,0,129,129)
label@0x60d0000f7ba0 (22,22,56,39)
(gdb)
The plugin provides the following commands.
- ``dump obj``: Dump the object tree.
- ``info style``: Show the object's style.
- ``info draw_unit``: Display all current drawing unit information.
Dump obj tree
-------------
``dump obj``: Dump the object tree.
``dump obj -L 2``: Dump the object tree with a depth of 2.
``dump obj -a 0x60700000dd10``: Dump the object tree starting from the specified address.
Show obj's style
----------------
This command can dump the object's local style, since style value is a union, it's displayed in all possible formats.
``info style address_of_obj``: Show the object's style.
Example:
.. code:: bash
(gdb) info style 0x60700000dd10
32 = {num = 90, ptr = 0x5a, color = {blue = 90 'Z', green = 0 '\000', red = 0 '\000'}}
158 = {num = 32767, ptr = 0x7fff, color = {blue = 255 '\377', green = 127 '\177', red = 0 '\000'}}
(gdb) p lv_global->disp_default->act_scr
$4 = (lv_obj_t *) 0x60700000dd10
(gdb) info style $4
32 = {num = 90, ptr = 0x5a, color = {blue = 90 'Z', green = 0 '\000', red = 0 '\000'}}
158 = {num = 32767, ptr = 0x7fff, color = {blue = 255 '\377', green = 127 '\177', red = 0 '\000'}}
(gdb)
Connect to Debugger
-------------------
This command provides the ability to connect and debug GDB Python Script using IDE.
Connect to ``PyCharm`` / ``VSCode`` / ``Eclipse(not support yet)``
``debugger -t pycharm``
``debugger -t vscode``
``debugger -t eclipse``
How to use it specifically, search ``pydevd_pycharm`` / ``debugpy`` for details.

View File

@@ -0,0 +1,13 @@
.. _debugging:
=========
Debugging
=========
.. toctree::
:maxdepth: 2
gdb_plugin
log
profiler
vg_lite_tvg

View File

@@ -0,0 +1,73 @@
.. _logging:
=======
Logging
=======
LVGL has a built-in *Log* module to inform the user about what is
happening in the library.
Log Level
*********
To enable logging, set :c:macro:`LV_USE_LOG` in ``lv_conf.h`` and set
:c:macro:`LV_LOG_LEVEL` to one of the following values:
- :c:macro:`LV_LOG_LEVEL_TRACE`: A lot of logs to give detailed information
- :c:macro:`LV_LOG_LEVEL_INFO`: Log important events
- :c:macro:`LV_LOG_LEVEL_WARN`: Log if something unwanted happened but didn't cause a problem
- :c:macro:`LV_LOG_LEVEL_ERROR`: Only critical issues, where the system may fail
- :c:macro:`LV_LOG_LEVEL_USER`: Only user messages
- :c:macro:`LV_LOG_LEVEL_NONE`: Do not log anything
The events which have a higher level than the set log level will be logged
as well. E.g. if you :c:macro:`LV_LOG_LEVEL_WARN`, errors will be also logged.
Printing Logs
*************
Logging with printf
-------------------
If your system supports ``printf``, you just need to enable
:c:macro:`LV_LOG_PRINTF` in ``lv_conf.h`` to send the logs with ``printf``.
Custom log function
-------------------
If you can't use ``printf`` or want to use a custom function to log, you
can register a "logger" callback with :cpp:func:`lv_log_register_print_cb`.
For example:
.. code-block:: c
void my_log_cb(lv_log_level_t level, const char * buf)
{
serial_send(buf, strlen(buf));
}
...
lv_log_register_print_cb(my_log_cb);
Add Logs
********
You can also use the log module via the
``LV_LOG_TRACE/INFO/WARN/ERROR/USER(text)`` or ``LV_LOG(text)``
functions. Here:
- ``LV_LOG_TRACE/INFO/WARN/ERROR/USER(text)`` append the following information to your ``text``
- Log Level
- \__FILE\_\_
- \__LINE\_\_
- \__func\_\_
- ``LV_LOG(text)`` is similar to ``LV_LOG_USER`` but has no extra information attached.
API
***

View File

@@ -0,0 +1,250 @@
.. _profiler:
========
Profiler
========
As the complexity of the application increases, performance issues such as low FPS and frequent cache misses
causing lag may arise. LVGL has internally set up some hooks for performance measurement to help developers
analyze and locate performance issues.
.. _profiler_introduction:
Introduction
************
LVGL has a built-in trace system to track and record the timestamps of important events that occur during runtime,
such as rendering events and user input events. These event timestamps serve as important metrics for performance analysis.
The trace system has a configurable record buffer that stores the names of event functions and their timestamps.
When the buffer is full, the trace system prints the log information through the provided user interface.
The output trace logs are formatted according to Android's `systrace <https://developer.android.com/topic/performance/tracing>`_
format and can be visualized using `Perfetto <https://ui.perfetto.dev>`_.
.. _profiler_usage:
Usage
*****
Configure profiler
^^^^^^^^^^^^^^^^^^
To enable the profiler, set :c:macro:`LV_USE_PROFILER` in ``lv_conf.h`` and configure the following options:
1. Enable the built-in profiler functionality by setting :c:macro:`LV_USE_PROFILER_BUILTIN`.
2. Buffer configuration: Set the value of :c:macro:`LV_PROFILER_BUILTIN_BUF_SIZE` to configure the buffer size. A larger buffer can store more trace event information, reducing interference with rendering. However, it also results in higher memory consumption.
3. Timestamp configuration: LVGL uses the :cpp:func:`lv_tick_get` function with a precision of 1ms by default to obtain timestamps when events occur. Therefore, it cannot accurately measure intervals below 1ms. If your system environment can provide higher precision (e.g., 1us), you can configure the profiler as follows:
- Recommended configuration in **UNIX** environments:
.. code-block:: c
#include <sys/syscall.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
static uint64_t my_get_tick_cb(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec * 1000000000 + ts.tv_nsec;
}
static int my_get_tid_cb(void)
{
return (int)syscall(SYS_gettid);
}
static int my_get_cpu_cb(void)
{
int cpu_id = 0;
syscall(SYS_getcpu, &cpu_id, NULL);
return cpu_id;
}
void my_profiler_init(void)
{
lv_profiler_builtin_config_t config;
lv_profiler_builtin_config_init(&config);
config.tick_per_sec = 1000000000; /* One second is equal to 1000000000 nanoseconds */
config.tick_get_cb = my_get_tick_cb;
config.tid_get_cb = my_get_tid_cb;
config.cpu_get_cb = my_get_cpu_cb;
lv_profiler_builtin_init(&config);
}
- Recommended configuration in **Arduino** environments:
.. code-block:: c
static uint64_t my_get_tick_cb(void)
{
/* Use the microsecond time stamp provided by Arduino */
return micros();
}
void my_profiler_init(void)
{
lv_profiler_builtin_config_t config;
lv_profiler_builtin_config_init(&config);
config.tick_per_sec = 1000000; /* One second is equal to 1000000 microseconds */
config.tick_get_cb = my_get_tick_cb;
lv_profiler_builtin_init(&config);
}
4. Log output configuration: LVGL uses the :cpp:func:`LV_LOG` interface by default to output trace information. If you want to use another interface to output log information (e.g., file stream), you can redirect the log output using the following code:
.. code-block:: c
static void my_log_print_cb(const char * buf)
{
printf("%s", buf);
}
void my_profiler_init(void)
{
lv_profiler_builtin_config_t config;
lv_profiler_builtin_config_init(&config);
... /* other configurations */
config.flush_cb = my_log_print_cb;
lv_profiler_builtin_init(&config);
}
Run the test scenario
^^^^^^^^^^^^^^^^^^^^^
Run the UI scenario that you want to measure, such as scrolling a scrollable page up and down or entering/exiting an application.
Process the logs
^^^^^^^^^^^^^^^^
Save the output log as `my_trace.txt`, use `trace_filter.py` for filtering and preprocessing:
.. code-block:: bash
./lvgl/scripts/trace_filter.py my_trace.txt
or
.. code-block:: bash
python3 ./lvgl/scripts/trace_filter.py my_trace.txt
You will obtain a processed text file named `trace.systrace`, which roughly contains the following content:
.. code-block:: text
# tracer: nop
#
LVGL-1 [0] 2892.002993: tracing_mark_write: B|1|lv_timer_handler
LVGL-1 [0] 2892.002993: tracing_mark_write: B|1|_lv_display_refr_timer
LVGL-1 [0] 2892.003459: tracing_mark_write: B|1|refr_invalid_areas
LVGL-1 [0] 2892.003461: tracing_mark_write: B|1|lv_draw_rect
LVGL-1 [0] 2892.003550: tracing_mark_write: E|1|lv_draw_rect
LVGL-1 [0] 2892.003552: tracing_mark_write: B|1|lv_draw_rect
LVGL-1 [0] 2892.003556: tracing_mark_write: E|1|lv_draw_rect
LVGL-1 [0] 2892.003560: tracing_mark_write: B|1|lv_draw_rect
LVGL-1 [0] 2892.003573: tracing_mark_write: E|1|lv_draw_rect
...
Import the processed `trace.systrace` file into `Perfetto <https://ui.perfetto.dev>`_ and wait for it to be parsed.
Performance analysis
^^^^^^^^^^^^^^^^^^^^
If the log parsing is successful, you will see the following screen:
.. image:: /_static/images/perfetto_ui.png
In the Perfetto UI, use the :kbd:`A` or :kbd:`D` keys to pan the timeline horizontally
and the :kbd:`W` or :kbd:`S` keys to zoom in or out on the timeline.
Use the mouse to move the focus and click on functions on the timeline to observe their execution time.
Add Measurement Point
*********************
Users can add their own measured functions:
.. code-block:: c
void my_function_1(void)
{
LV_PROFILER_BEGIN;
do_something();
LV_PROFILER_END;
}
void my_function_2(void)
{
LV_PROFILER_BEGIN_TAG("do_something_1");
do_something_1();
LV_PROFILER_END_TAG("do_something_1");
LV_PROFILER_BEGIN_TAG("do_something_2");
do_something_2();
LV_PROFILER_END_TAG("do_something_2");
}
.. _profiler_custom_implementation:
Custom profiler implementation
******************************
If you wish to use a profiler method provided by your operating system, you can modify the following configurations in ``lv_conf.h``:
- :c:macro:`LV_PROFILER_INCLUDE`: Provides a header file for the profiler function.
- :c:macro:`LV_PROFILER_BEGIN`: Profiler start point function.
- :c:macro:`LV_PROFILER_END`: Profiler end point function.
- :c:macro:`LV_PROFILER_BEGIN_TAG`: Profiler start point function with custom tag.
- :c:macro:`LV_PROFILER_END_TAG`: Profiler end point function with custom tag.
Taking `NuttX <https://github.com/apache/nuttx>`_ RTOS as an example:
.. code-block:: c
#define LV_PROFILER_INCLUDE "nuttx/sched_note.h"
#define LV_PROFILER_BEGIN sched_note_begin(NOTE_TAG_ALWAYS)
#define LV_PROFILER_END sched_note_end(NOTE_TAG_ALWAYS)
#define LV_PROFILER_BEGIN_TAG(str) sched_note_beginex(NOTE_TAG_ALWAYS, str)
#define LV_PROFILER_END_TAG(str) sched_note_endex(NOTE_TAG_ALWAYS, str)
.. _profiler_faq:
FAQ
***
Perfetto log parsing fails
^^^^^^^^^^^^^^^^^^^^^^^^^^
Please check the completeness of the logs. If the logs are incomplete, it may be due to the following reasons:
1. Serial port reception errors caused by a high baud rate. You need to reduce the baud rate.
2. Data corruption caused by other thread logs inserted during the printing of trace logs. You need to disable the log output of other threads or refer to the configuration above to use a separate log output interface.
3. Make sure that the string passed in by :c:macro:`LV_PROFILER_BEGIN_TAG` or :c:macro:`LV_PROFILER_END_TAG` is not a local variable on the stack or a string in shared memory, because currently only the string address is recorded and the content is not copied.
Function execution time displayed as 0s in Perfetto
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If the function execution time is lower than the precision of the timestamps, this situation can occur. You can refer to the configuration instructions above to use a higher precision timestamp.
Significant stuttering occurs during profiling
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When the buffer used to store trace events becomes full, the profiler will output all the data in the buffer, which can cause UI blocking and stuttering during the output. You can optimize this by taking the following measures:
1. Increase the value of :c:macro:`LV_PROFILER_BUILTIN_BUF_SIZE`. A larger buffer can reduce the frequency of log printing, but it also consumes more memory.
2. Optimize the execution time of log printing functions, such as increasing the serial port baud rate or improving file writing speed.
Trace logs are not being output
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If the trace logs are not automatically printed when the buffer is not full, you can try the following methods to force the log output:
1. Reduce the value of :c:macro:`LV_PROFILER_BUILTIN_BUF_SIZE` to fill the buffer more quickly and trigger automatic printing.
2. Manually call or use a timer to call the :cpp:func:`lv_profiler_builtin_flush` function to force the log output.

View File

@@ -0,0 +1,27 @@
.. _vg_lite_tvg:
=================
VG-Lite Simulator
=================
LVGL integrates a VG-Lite simulator based on ThorVG.
Its purpose is to simplify the debugging of VG-Lite adaptation and reduce the time of debugging and locating problems on hardware devices.
It has been integrated into the CI automated compilation and testing process to ensure that the VG-Lite rendering backend can be fully tested after each PR modification.
How It Works
************
Sort out the APIs in the ``vg_lite.h`` header file provided by the vendor, re-implement the APIs using `ThorVG <https://github.com/thorvg/thorvg>`_,
and simulate the same rendering images as the real hardware on the simulator.
Configuration
*************
1. Enable VG-Lite rendering backend, see :ref:`vg_lite`.
2. Enable ThorVG and turn on the configuration :c:macro:`LV_USE_THORVG_INTERNAL` or :c:macro:`LV_USE_THORVG_EXTERNAL`.
It is recommended to use the internal ThorVG library to ensure uniform rendering results.
3. Enable :c:macro:`LV_USE_VG_LITE_THORVG` and set :c:macro:`LV_DRAW_BUF_ALIGN` to 64. The rest of the options can remain default.
Make sure :c:macro:`LV_VG_LITE_USE_GPU_INIT` is enabled, because the thorvg drawing context needs to be initialized before it can be used.

View File

@@ -0,0 +1,20 @@
.. _reference:
=======
Details
=======
.. toctree::
:maxdepth: 2
../examples
integration/index
common-widget-features/index
widgets/index
main-modules/index
auxiliary-modules/index
libs/index
debugging/index
../contributing/index
../CHANGELOG
../API/index

View File

@@ -0,0 +1,28 @@
.. _building_lvgl:
=============
Building LVGL
=============
Make and CMake
**************
LVGL also supports ``make`` and ``CMake`` build systems out of the box.
To add LVGL to your Makefile based build system add these lines to your
main Makefile:
.. code-block:: make
LVGL_DIR_NAME ?= lvgl #The name of the lvgl folder (change this if you have renamed it)
LVGL_DIR ?= ${shell pwd} #The path where the lvgl folder is
include $(LVGL_DIR)/$(LVGL_DIR_NAME)/lvgl.mk
For integration with CMake take a look this section of the
:ref:`Documentation <integrating_lvgl_cmake>`.
Managed builds
**************
TODO

Some files were not shown because too many files have changed in this diff Show More