add some code
1021
managed_components/lvgl__lvgl/docs/src/CHANGELOG.rst
Normal file
60
managed_components/lvgl__lvgl/docs/src/_ext/link_roles.py
Normal 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'}
|
||||
133
managed_components/lvgl__lvgl/docs/src/_ext/lv_example.py
Normal 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 </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> 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> View on GitHub': self.github_path(example_path, 'py'),
|
||||
'<i class="fa fa-play"></i> 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,
|
||||
}
|
||||
287
managed_components/lvgl__lvgl/docs/src/_static/css/custom.css
Normal 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 </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
|
||||
* 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
|
||||
}
|
||||
|
||||
5
managed_components/lvgl__lvgl/docs/src/_static/css/fontawesome.min.css
vendored
Normal file
BIN
managed_components/lvgl__lvgl/docs/src/_static/images/align.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 86 KiB |
BIN
managed_components/lvgl__lvgl/docs/src/_static/images/bidi.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 127 KiB |
BIN
managed_components/lvgl__lvgl/docs/src/_static/images/home_1.png
Normal file
|
After Width: | Height: | Size: 9.6 KiB |
BIN
managed_components/lvgl__lvgl/docs/src/_static/images/home_2.png
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
BIN
managed_components/lvgl__lvgl/docs/src/_static/images/home_3.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
managed_components/lvgl__lvgl/docs/src/_static/images/home_4.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
managed_components/lvgl__lvgl/docs/src/_static/images/home_5.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
managed_components/lvgl__lvgl/docs/src/_static/images/home_6.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 37 KiB |
BIN
managed_components/lvgl__lvgl/docs/src/_static/images/layers.png
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 8.5 KiB |
|
After Width: | Height: | Size: 973 B |
|
After Width: | Height: | Size: 993 B |
|
After Width: | Height: | Size: 990 B |
|
After Width: | Height: | Size: 135 KiB |
|
After Width: | Height: | Size: 9.7 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 110 KiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 133 KiB |
BIN
managed_components/lvgl__lvgl/docs/src/_static/images/scale.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 8.5 KiB |
|
After Width: | Height: | Size: 8.7 KiB |
|
After Width: | Height: | Size: 76 KiB |
57
managed_components/lvgl__lvgl/docs/src/_static/js/custom.js
Normal 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);
|
||||
});
|
||||
})
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -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 %}
|
||||
82
managed_components/lvgl__lvgl/docs/src/_templates/page.html
Normal 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 %}
|
||||
631
managed_components/lvgl__lvgl/docs/src/conf.py
Normal 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')
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
70
managed_components/lvgl__lvgl/docs/src/contributing/dco.rst
Normal 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.
|
||||
@@ -0,0 +1,14 @@
|
||||
.. _contributing:
|
||||
|
||||
============
|
||||
Contributing
|
||||
============
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
introduction
|
||||
ways_to_contribute
|
||||
pull_requests
|
||||
dco
|
||||
coding_style
|
||||
@@ -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!
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
***
|
||||
|
||||
@@ -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
|
||||
***
|
||||
@@ -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
|
||||
***
|
||||
@@ -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
|
||||
***
|
||||
|
||||
@@ -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
|
||||
***
|
||||
|
||||
@@ -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
|
||||
***
|
||||
@@ -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
|
||||
@@ -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
|
||||
***
|
||||
|
||||
@@ -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.
|
||||
@@ -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
|
||||
***
|
||||
@@ -0,0 +1,11 @@
|
||||
.. _observer:
|
||||
|
||||
========
|
||||
Observer
|
||||
========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
observer
|
||||
observer_examples
|
||||
@@ -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
|
||||
***
|
||||
@@ -0,0 +1,8 @@
|
||||
.. _observer examples:
|
||||
|
||||
========
|
||||
Examples
|
||||
========
|
||||
|
||||
.. include:: ../../../examples/others/observer/index.rst
|
||||
|
||||
@@ -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
|
||||
***
|
||||
|
||||
@@ -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
|
||||
***
|
||||
@@ -0,0 +1,7 @@
|
||||
.. _xml_animations:
|
||||
|
||||
==========
|
||||
Animations
|
||||
==========
|
||||
|
||||
TODO
|
||||
@@ -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);
|
||||
@@ -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>
|
||||
@@ -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
|
||||
***
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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>
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)`.
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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.
|
||||
@@ -0,0 +1,7 @@
|
||||
.. _xml_translations:
|
||||
|
||||
============
|
||||
Translations
|
||||
============
|
||||
|
||||
TODO
|
||||
@@ -0,0 +1,7 @@
|
||||
.. _xml_view:
|
||||
|
||||
=========
|
||||
View
|
||||
=========
|
||||
|
||||
TODO
|
||||
@@ -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.
|
||||
@@ -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, it’s 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
|
||||
***
|
||||
@@ -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
|
||||
***
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
***
|
||||
@@ -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
|
||||
***
|
||||
@@ -0,0 +1,12 @@
|
||||
.. _layouts:
|
||||
|
||||
=======
|
||||
Layouts
|
||||
=======
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
flex
|
||||
grid
|
||||
@@ -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
|
||||
***
|
||||
@@ -0,0 +1,11 @@
|
||||
.. _styles:
|
||||
|
||||
======
|
||||
Styles
|
||||
======
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
styles
|
||||
style-properties
|
||||
@@ -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_
|
||||
@@ -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.
|
||||
@@ -0,0 +1,13 @@
|
||||
.. _debugging:
|
||||
|
||||
=========
|
||||
Debugging
|
||||
=========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
gdb_plugin
|
||||
log
|
||||
profiler
|
||||
vg_lite_tvg
|
||||
@@ -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
|
||||
***
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
20
managed_components/lvgl__lvgl/docs/src/details/index.rst
Normal 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
|
||||
@@ -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
|
||||
|
||||