add some code
4
managed_components/lvgl__lvgl/docs/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
build/
|
||||
doxygen/
|
||||
intermediate/
|
||||
src/_static/built_lv_examples
|
||||
76
managed_components/lvgl__lvgl/docs/CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our
|
||||
project and our community a harassment-free experience for everyone,
|
||||
regardless of age, body size, disability, ethnicity, gender identity and
|
||||
expression, level of experience, nationality, personal appearance, race,
|
||||
religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
- Using welcoming and inclusive language
|
||||
- Being respectful of differing viewpoints and experiences
|
||||
- Gracefully accepting constructive criticism
|
||||
- Focusing on what is best for the community
|
||||
- Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
- The use of sexualized language or imagery and unwelcome sexual
|
||||
attention or advances
|
||||
- Trolling, insulting/derogatory comments, and personal or political
|
||||
attacks
|
||||
- Public or private harassment
|
||||
- Publishing others\' private information, such as a physical or
|
||||
electronic address, without explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in
|
||||
a professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of
|
||||
acceptable behavior and are expected to take appropriate and fair
|
||||
corrective action in response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit,
|
||||
or reject comments, commits, code, wiki edits, issues, and other
|
||||
contributions that are not aligned to this Code of Conduct, or to ban
|
||||
temporarily or permanently any contributor for other behaviors that they
|
||||
deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public
|
||||
spaces when an individual is representing the project or its community.
|
||||
Examples of representing a project or community include using an
|
||||
official project e-mail address, posting via an official social media
|
||||
account, or acting as an appointed representative at an online or
|
||||
offline event. Representation of a project may be further defined and
|
||||
clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may
|
||||
be reported by contacting the project team using the [contact
|
||||
form](https://lvgl.io/about). All complaints will be reviewed and
|
||||
investigated and will result in a response that is deemed necessary and
|
||||
appropriate to the circumstances. The project team is obligated to
|
||||
maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted
|
||||
separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in
|
||||
good faith may face temporary or permanent repercussions as determined
|
||||
by other members of the project\'s leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor
|
||||
Covenant](http://contributor-covenant.org), version 1.4, available at
|
||||
[http://contributor-covenant.org/version/1/4](http://contributor-covenant.org/version/1/4/)
|
||||
2464
managed_components/lvgl__lvgl/docs/Doxyfile
Normal file
30
managed_components/lvgl__lvgl/docs/Makefile
Normal file
@@ -0,0 +1,30 @@
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line, and also
|
||||
# from the environment for the first two.
|
||||
SPHINXOPTS ?=
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
BUILDDIR = build
|
||||
SOURCEDIR = intermediatex
|
||||
|
||||
# SOURCEDIR can be overridden by LVGL_DOC_BUILD_INTERMEDIATE_DIR
|
||||
LEN := $(shell printf '%s' '$(LVGL_DOC_BUILD_INTERMEDIATE_DIR)' | wc -c)
|
||||
|
||||
ifeq ($(shell test $(LEN) -gt 0; echo $$?),0)
|
||||
SOURCEDIR = $(LVGL_DOC_BUILD_INTERMEDIATE_DIR)
|
||||
else
|
||||
SOURCEDIR = intermediate
|
||||
endif
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
# It can be supplied from an environment variable 'O' or on make command line.
|
||||
%: Makefile
|
||||
$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
311
managed_components/lvgl__lvgl/docs/README.md
Normal file
@@ -0,0 +1,311 @@
|
||||
# Documentation
|
||||
|
||||
|
||||
## Building
|
||||
|
||||
Building the documentation is easy. Here are the requirements:
|
||||
|
||||
* Doxygen
|
||||
* Python >= 3.10
|
||||
* C compiler (gcc, msvc, clang, etc...)
|
||||
|
||||
Once Python is installed
|
||||
|
||||
pip install -r requirements.txt
|
||||
|
||||
will install all the prerequisite packages:
|
||||
|
||||
* Sphinx
|
||||
* breathe
|
||||
* imagesize
|
||||
* importlib-metadata
|
||||
* sphinx-rtd-theme
|
||||
* sphinx-sitemap
|
||||
* sphinxcontrib-applehelp
|
||||
* sphinxcontrib-devhelp
|
||||
* sphinxcontrib-htmlhelp
|
||||
* sphinxcontrib-jsmath
|
||||
* sphinxcontrib-qthelp
|
||||
* sphinxcontrib-serializinghtml
|
||||
* sphinxcontrib-mermaid==0.9.2
|
||||
* sphinx-design
|
||||
* sphinx-rtd-dark-mode
|
||||
* typing-extensions
|
||||
* sphinx-reredirects
|
||||
* dirsync
|
||||
|
||||
Now you are ready to build the documentation:
|
||||
|
||||
python build.py html
|
||||
|
||||
or if you are on a Unix like OS:
|
||||
|
||||
python3 build.py html
|
||||
|
||||
Intermediate files are prepared in `./docs/intermediate/` and the final documentation will appear in `./docs/build/html/`. (Both of these directories can be overridden using environment variables. See documentation in `build.py` for details.)
|
||||
|
||||
If the list of document source files has changed (names or paths):
|
||||
|
||||
python build.py clean html
|
||||
|
||||
Will remove the old intermediate and build files and regenerate new ones matching the new structure.
|
||||
|
||||
To see a list of options available:
|
||||
|
||||
python build.py
|
||||
|
||||
Read the docstring for `build.py` for detailed documentation on each option.
|
||||
|
||||
|
||||
## For Developers
|
||||
|
||||
One of our firm policies is ***EVERYTHING MUST BE DOCUMENTED***.
|
||||
|
||||
The below are some rules to follow when updating any of the `.rst` files located in the `./docs/src/` directory tree.
|
||||
|
||||
|
||||
### What to Name Your `.rst` File
|
||||
|
||||
The directory structure under the `./docs/src/` directory, and the filenames of the `.rst` files govern the eventual URLs that are generated in the HTML output. These directories are organized so as to reflect the nature of the content. Example: the `.rst` files under `./docs/src/intro` contain introductory material—detailed reference material would not go there, but instead in an appropriate subdirectory of `./docs/src/details/`. It is expected that the content and location of any new documents added would be in alignment with this directory structure, and placed and named according to their content. Additionally, to be linked into the eventual generated documentation, the stem of the new filename would need to appear in at least one (normally *only one*) `.. toctree::` directive, normally in an `index.rst` file in the directory where it will appear in the table of contents (TOC).
|
||||
|
||||
Other than that, there are no restrictions on filenames. Previous linking of filenames to generated API links has been removed and replaced by a better scheme. For sake of illustration, let's say you are creating (or enhancing) documentation related to the `lv_scale_t` data type (one of the LVGL Widgets): if you want the doc-build logic to generate appropriate links to LVGL API pages, place an API section at the end of your document (it must be at the end) like this:
|
||||
|
||||
```rst
|
||||
API
|
||||
***
|
||||
```
|
||||
|
||||
and then, if you want the API-link-generation logic to generate hyperlinks to API pages based on an ***exact, case-sensitive string match*** with specific C symbols, follow it with a reStructuredText comment using this syntax:
|
||||
|
||||
```rst
|
||||
.. API equals: lv_scale_t, lv_scale_create
|
||||
```
|
||||
|
||||
What follows the colon is a comma- or space-separated list of exact C symbols documented somewhere in the `lvgl/src/` directory. If the list is long, it can be wrapped to subsequent lines, though continuation lines must be all indented at the same level. The list ends with the first blank line after this pseudo-directive.
|
||||
|
||||
If you instead want the API-link-generation logic to simply include links to code that ***starts with a specific string*** use this syntax instead. The format of the list is the same as for `.. API equals:`:
|
||||
|
||||
```rst
|
||||
.. API startswith: lv_scale, lv_obj_set_style
|
||||
```
|
||||
|
||||
You can also manually link to API pages, in which case the API-link-generation logic will see that you have already added links and will not repeat them.
|
||||
|
||||
```rst
|
||||
:ref:`lv_scale_h`
|
||||
```
|
||||
|
||||
Note that the period before the `h` is replaced with an underscore (`_`). The naming of this reference (`lv_scale_h`) will generate a hyperlink to the documentation extracted by Doxygen from the `lvgl/src/widgets/scale/lv_scale.h` file.
|
||||
|
||||
|
||||
### Text Format
|
||||
|
||||
While with `.md` files, it is important to allow paragraphs to flow off to the right with one long line so that when they are formatted as `.html` files, the paragraphs will word-wrap with the width of the browser, this is not true with reStructuredText (`.rst` files). [Sphinx](https://www.sphinx-doc.org/en/master/) and its underlying [docutils parsing engine](https://docutils.sourceforge.io/docs/) conveniently combine grouped text into a proper paragraph with that word-wrapping behavior. This allows the source text documents to be nicely word-wrapped so that they are more readable in text- and code-editors that do not have wide editing windows. So please wrap the text around column 86 or narrower. Wrapping at *exactly* column 86 is not important, but readability and ease of editing is.
|
||||
|
||||
|
||||
### index.rst Files
|
||||
|
||||
If you create a new directory you MUST have an `index.rst` file in that directory and that index file needs to be pointed to in the `index.rst` file that is located in the parent directory.
|
||||
|
||||
Let's take a look at the `index.rst` file that is located in the `docs/layouts` directory.
|
||||
|
||||
```
|
||||
.. _layouts:
|
||||
|
||||
=======
|
||||
Layouts
|
||||
=======
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
flex
|
||||
grid
|
||||
```
|
||||
|
||||
|
||||
The below explains the parts of this file.
|
||||
|
||||
```
|
||||
.. _layouts: <=== Creates an explicit link target
|
||||
<=== Empty line -- important!
|
||||
=======
|
||||
Layouts <=== Heading seen in documentation
|
||||
=======
|
||||
|
||||
|
||||
.. toctree:: <=== Table of contents
|
||||
:maxdepth: 2 <=== Internal use and needs to always be set this way
|
||||
|
||||
flex <=== .rst files located in directory with index.rst
|
||||
grid
|
||||
```
|
||||
|
||||
The first line is for the purposes of providing a uniquely-named **link target** that can be referenced elsewhere in the documentation.
|
||||
|
||||
.. _{LINK NAME}:
|
||||
|
||||
Note that `{LINK NAME}`:
|
||||
|
||||
- **must** be preceded by a single underscore, and
|
||||
- **must** be followed by at least one blank line for the doc-generation logic to process it correctly.
|
||||
|
||||
Replace `{LINK NAME}` with a link name that is unique among all documents under the `./docs/` directory. It can have multiple words if needed to make it unique or when otherwise appropriate for clarity. If multiple words are used, they can be separated with single spaces, hyphens or underscores. Whatever you use, the `{LINK NAME}` string used to reference it must be identical. `{LINK NAME}` strings are not case sensitive.
|
||||
|
||||
That unique name is then used to provide a link reference elsewhere in the documentation using one of two formats.
|
||||
|
||||
|
||||
|
||||
##### When "link text" should be a title or section heading from the target document:
|
||||
|
||||
```reStructuredText
|
||||
:ref:`{LINK NAME}`
|
||||
```
|
||||
|
||||
This in-line markup (interpreted text using the Sphinx-defined custom `:ref:` role) is then replaced with a hyperlink whose "link text" is the name of the section heading just below the **link target**. For this reason, when using this syntax, `{LINK NAME}` must reference **link target**s that are just above a title or section heading.
|
||||
|
||||
|
||||
|
||||
##### When "link text" should be something else:
|
||||
|
||||
```reStructuredText
|
||||
:ref:`other link text <{LINK NAME}>`
|
||||
```
|
||||
|
||||
This latter syntax enables you to put a **link target** anywhere in an .RST file (not just above a heading) and link to it using this syntax.
|
||||
|
||||
Note: This latter syntax was either added or fixed in Sphinx recently. It did not work in Sphinx 7.3.7.
|
||||
|
||||
|
||||
|
||||
|
||||
### Section Headings
|
||||
|
||||
[Section headings](https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html#sections) are created by underlining (and optionally overlining) the section title with a punctuation character, at least as long as the text. Example:
|
||||
|
||||
```
|
||||
=================
|
||||
This Is a Heading
|
||||
=================
|
||||
```
|
||||
|
||||
reStructuredText does not impose any particular heading levels assigned to certain characters since the structure is determined from the succession of headings. So if you are modifying an existing .RST file, please follow the pattern already in use.
|
||||
|
||||
If you are creating a new .RST file, use this convention:
|
||||
|
||||
```
|
||||
=====
|
||||
Title
|
||||
=====
|
||||
|
||||
Chapter
|
||||
*******
|
||||
|
||||
Section
|
||||
-------
|
||||
|
||||
Sub Section
|
||||
~~~~~~~~~~~
|
||||
|
||||
Sub Sub Section
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
Sub Sub Sub Section
|
||||
'''''''''''''''''''
|
||||
```
|
||||
|
||||
Note that the "underlining" can be longer than the heading title, but if it is shorter, the documentation-generation logic will fail with an error.
|
||||
|
||||
For improved readability in the .RST file, place at least 2 blank lines above section headings.
|
||||
|
||||
|
||||
### Code Blocks
|
||||
|
||||
* Do not use tab characters in code blocks.
|
||||
* Each indentation level use 4 spaces.
|
||||
* Include at least 1 empty line after a code block.
|
||||
* There must be one empty line between the code block directive and the code.
|
||||
* `.. code-block::` is the only directive that should be used. Note carefully that unlike the **link target** directive above, this directive has 2 colons. (The only ReST and sphinx directives that are valid with one colon are **link target**s as shown above.) Lone `::`, `:code:` or `.. code:` should not be used.
|
||||
* If you want to separate code into easier-to-understand sections you can do so with a single empty line.
|
||||
* For syntax highlighting appropriate to the language in the code block, specify the language after the directive. Some examples are:
|
||||
|
||||
- `.. code-block:: c`,
|
||||
- `.. code-block:: cpp`,
|
||||
- `.. code-block:: python`,
|
||||
- `.. code-block:: shell`,
|
||||
- `.. code-block:: kconfig`,
|
||||
- `.. code-block:: json`,
|
||||
- `.. code-block:: yaml`,
|
||||
- `.. code-block:: csharp` (or "cs"),
|
||||
- `.. code-block:: vb.net`,
|
||||
- `.. code-block:: dot` (graphviz),
|
||||
- `.. code-block:: html`,
|
||||
- `.. code-block:: css`,
|
||||
- `.. code-block:: xml`,
|
||||
- `.. code-block:: make`.
|
||||
|
||||
The full set of supported lexers are listed here: https://pygments.org/docs/lexers/ .
|
||||
|
||||
|
||||
### Bulleted Lists
|
||||
|
||||
To create a bulleted list, do the following:
|
||||
|
||||
- First item description
|
||||
- If you want to span multiple lines, indent subsequent
|
||||
lines to align with item text like this.
|
||||
- If you want to include a code block under a list item,
|
||||
it must be intended to align with the list item like this:
|
||||
|
||||
.. code-block: python
|
||||
<=== blank line here is important
|
||||
# this is some code
|
||||
<=== blank line here is important
|
||||
- If you want to have nested bulleted lists, indent each
|
||||
new level to align with its parent list item like this:
|
||||
<=== blank line here is important
|
||||
- level 2 item 1: text
|
||||
- level 2 item 2: text
|
||||
<=== blank line here is important
|
||||
- Last list item. Note that the nested list above is preceded
|
||||
and followed by 1 blank line.
|
||||
|
||||
All lists (including nested lists) **must** be preceded and followed with at least 1 blank line. This is mandatory for the documentation-generation logic to process it correctly.
|
||||
|
||||
|
||||
### Referencing API Documentation
|
||||
|
||||
If you want to reference portions of the LVGL code from the documentation (in .RST files) there are special directives to do this:
|
||||
|
||||
:cpp:func:`lv_init`
|
||||
:c:macro:`LV_USE_FLEX`
|
||||
:cpp:type:`lv_event_t`
|
||||
:cpp:enum:`_lv_event_t`
|
||||
:cpp:enumerator:`LV_EVENT_ALL`
|
||||
:cpp:struct:`lv_image_dsc_t`
|
||||
:cpp:union:`lv_style_value_t`
|
||||
|
||||
There is a special directive when wanting to use a more complex expression. For example when showing the arguments passed to a function.
|
||||
|
||||
:cpp:expr:`lv_obj_set_layout(widget, LV_LAYOUT_FLEX)`
|
||||
:cpp:expr:`lv_slider_set_mode(slider, LV_SLIDER_MODE_...)`
|
||||
|
||||
Arguments that are expressions (more than one word), or contain non-alphanumeric characters will cause the `:cpp:expr:` interpreted-text to fail. Examples:
|
||||
|
||||
:cpp:expr:`lv_obj_set_layout(widget, LV_LAYOUT_FLEX/GRID)` <== arg with > 1 word
|
||||
:cpp:expr:`lv_obj_set_layout(widget, LV_LAYOUT_*)` <== asterisk
|
||||
:cpp:expr:`lv_obj_set_layout(*widget, LV_LAYOUT_FLEX)` <== asterisk
|
||||
:cpp:expr:`lv_obj_set_layout((lv_obj_t *)widget, LV_LAYOUT_FLEX)` <== cast
|
||||
:cpp:expr:`lv_obj_set_layout(&widget, LV_LAYOUT_FLEX);` <== ampersand & semicolon
|
||||
:cpp:expr:`lv_obj_set_layout(widget, ...)` <== lone ellipsis
|
||||
|
||||
For such examples, simply use reStructuredText literal markup like this:
|
||||
|
||||
``lv_obj_set_layout(widget, LV_LAYOUT_FLEX/GRID)``
|
||||
``lv_obj_set_layout(widget, LV_LAYOUT_*)``
|
||||
``lv_obj_set_layout(*widget, LV_LAYOUT_FLEX)``
|
||||
``lv_obj_set_layout((lv_obj_t *)widget, LV_LAYOUT_FLEX)``
|
||||
``lv_obj_set_layout(&widget, LV_LAYOUT_FLEX);``
|
||||
``lv_obj_set_layout(widget, ...)``
|
||||
|
||||
436
managed_components/lvgl__lvgl/docs/README_jp.md
Normal file
@@ -0,0 +1,436 @@
|
||||
<a href="https://github.com/sponsors/lvgl" target="_blank"><img align="left" src="https://lvgl.io/github-assets/sponsor.png" height="32px"></a>
|
||||
|
||||
|
||||
|
||||
<p align="right"><a href="../README.md">English</a> | <a href="./README_zh.md">中文</a> | <a href="./README_pt_BR.md">Português do Brasil</a> | <b>日本語</b></p>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://lvgl.io/github-assets/logo-colored.png" width=300px>
|
||||
</p>
|
||||
|
||||
|
||||
<h1 align="center">Light and Versatile Graphics Library</h1>
|
||||
<br>
|
||||
<div align="center">
|
||||
<img src="https://raw.githubusercontent.com/kisvegabor/test/master/smartwatch_demo.gif">
|
||||
|
||||
<img border="1px" src="https://lvgl.io/github-assets/widgets-demo.gif">
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://lvgl-io.translate.goog/?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja" title="Homepage of LVGL">Website </a></a> |
|
||||
<a href="https://docs-lvgl-io.translate.goog/master/index.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja" title="Detailed documentation with 100+ examples">Docs</a> |
|
||||
<a href="https://forum.lvgl.io" title="Get help and help others">Forum</a> :gb: |
|
||||
<a href="https://lvgl-io.translate.goog/demos?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja" title="Demos running in your browser">Demos</a> |
|
||||
<a href="https://lvgl-io.translate.goog/services?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja" title="Graphics design, UI implementation and consulting">Services</a>
|
||||
</p>
|
||||
|
||||
<br>
|
||||
|
||||
## :ledger: Overview
|
||||
|
||||
**実績**<br>
|
||||
LVGL は、フリー&オープンソースの組み込み用グラフィックスライブラリです。
|
||||
あらゆるMCU、MPU、ディスプレイタイプに対応しており、美しいUI(User Interface)を実現できます。
|
||||
ARM, STM32, NXP, Espressif, Nuvoton, Arduino, RT-Thread, Zephyr, NuttX, Adafruitなど、業界をリードするベンダーやプロジェクトによりサポートされています。
|
||||
|
||||
**機能豊富**<br>
|
||||
モダンで美しいGUIを作成するための機能をすべて備えています。
|
||||
30以上の組み込みウィジェット、強力なスタイルシステム、WEB由来のレイアウトマネージャ、多くの言語をサポートする文字グラフィックシステムなどです。
|
||||
LVGL のシステム要件は、RAM 32KB、Flash 128KB、Cコンパイラ、フレームバッファ、1/10スクリーンサイズのレンダリング用バッファです。
|
||||
|
||||
**UIエディタ**<br>
|
||||
SquareLine Studio は、LVGL用のプロフェッショナル&リーズナブルなドラッグ&ドロップ型のUIエディターです。
|
||||
Windows、Linux、MacOS で動作し、ウェブサイトへの登録なしで試すことができます。
|
||||
|
||||
**サービス**<br>
|
||||
LVGL LLC では、グラフィックデザイン、UI実装、コンサルティングサービスに対する技術サポートが可能です。GUIプロジェクトの開発において何らかのサポートが必要な場合には、お気軽にお問い合わせください。
|
||||
|
||||
|
||||
## :rocket: 特徴
|
||||
|
||||
**フリー & 移植可能**
|
||||
- 外部依存関係がなく、完全に移植可能な Cライブラリ。(C++互換)
|
||||
- 任意の(RT)OS、任意のMCU・MPU用にコンパイル可能。
|
||||
- 電子ペーパー、OLEDディスプレイ、TFTディスプレイ、白黒ディスプレイ、モニターに対応。 [Porting Guide](https://docs-lvgl-io.translate.goog/master/details/integration/adding-lvgl-to-your-project/index.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja)
|
||||
- MITライセンスにより商用利用可能。
|
||||
- システム要件:RAM 32KB、Flash 128KB、フレームバッファ、レンダリング用に1/10以上のスクリーンサイズのバッファ。
|
||||
- OS、外部メモリ、GPUもサポート。
|
||||
|
||||
**ウィジェット、スタイル、レイアウトなど**
|
||||
- 30以上の組み込み [ウィジェット](https://docs-lvgl-io.translate.goog/master/details/widgets/index.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja): ボタン、ラベル、スライダー、グラフ、キーボード、メーター、円弧、表など。
|
||||
- ウィジェットの任意の部分を任意の状態にカスタマイズ可能な豊富なスタイルプロパティを備えた柔軟な [スタイルシステム](https://docs-lvgl-io.translate.goog/master/details/common-widget-features/styles/style.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja)。
|
||||
- [Flexbox](https://docs-lvgl-io.translate.goog/master/details/common-widget-features/layouts/flex.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja) および [グリッド](https://docs-lvgl-io.translate.goog/master/details/common-widget-features/layouts/grid.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja) 風のレイアウトエンジンにより、ウィジェットのサイズと位置を自動的に設定。
|
||||
- テキスト表示(UTF-8対応)は、中国語、日本語、韓国語、タイ語、ヒンディー語、アラビア語、ペルシア語をサポート。
|
||||
- ワードラッピング、カーニング、テキストスクロール、サブピクセルレンダリング、ピンイン-IME中国語入力、テキスト中の絵文字に対応。
|
||||
- アニメーション、アンチエイリアシング、不透明度、スムーズスクロール、シャドウ、画像変換などをサポートするレンダリングエンジン。
|
||||
- マウス、タッチパッド、キーパッド、キーボード、外部ボタン、エンコーダ等の [入力デバイス](https://docs-lvgl-io.translate.goog/master/details/integration/adding-lvgl-to-your-project/index.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja) をサポート。
|
||||
- [マルチディスプレイ](https://docs-lvgl-io.translate.goog/master/details/main-modules/display/overview.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja) 対応。
|
||||
|
||||
**Binding と Build をサポート**
|
||||
- [MicroPython Binding](https://blog-lvgl-io.translate.goog/2019-02-20/micropython-bindings?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja) が LVGL API を公開。
|
||||
- カスタムビルドシステムは使用せず、プロジェクトの他のファイルをビルドするときに、LVGLをビルド可能。
|
||||
- Make と [CMake](https://docs-lvgl-io.translate.goog/master/details/integration/building/cmake.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja) が含まれており、すぐ使えるようにサポート。
|
||||
- [PCのシミュレータで開発したUIコード](https://docs-lvgl-io.translate.goog/master/details/integration/ide/pc-simulator.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja) は、そのまま組込み用ハードウェアでも使用可能。
|
||||
- [Emscripten port](https://github.com/lvgl/lv_web_emscripten) :gb: によりC言語のUIコードをHTMLファイルに変換。
|
||||
|
||||
**ドキュメント, ツール, 技術サービス**
|
||||
- [ドキュメント](https://docs-lvgl-io.translate.goog/master/index.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja)は[100以上の簡単なサンプルプログラム](https://github.com/lvgl/lvgl/tree/master/examples) :gb: 入り 。
|
||||
- [SquareLine Studio](https://squareline.io/) :gb: - UI開発をスピードアップおよび簡素化するためのプロフェッショナルで使いやすいUIエディターソフトウェア。
|
||||
- UI開発をよりシンプルかつ迅速にするための、ユーザーインターフェイスの設計、実装、コンサルティングなどの [技術サービス](https://lvgl-io.translate.goog/services?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja)。
|
||||
|
||||
## :package: パッケージ
|
||||
LVGL は以下で利用可能です。
|
||||
- [Arduino library](https://docs-lvgl-io.translate.goog/master/details/integration/framework/arduino.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja)
|
||||
- [PlatformIO package](https://registry.platformio.org/libraries/lvgl/lvgl) :gb:
|
||||
- [Zephyr library](https://docs-zephyrproject-org.translate.goog/latest/index.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja)
|
||||
- [ESP32 component](https://docs-lvgl-io.translate.goog/master/details/integration/chip/espressif.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja)
|
||||
- [NXP MCUXpresso component](https://www-nxp-com.translate.goog/design/software/embedded-software/lvgl-open-source-graphics-library:LITTLEVGL-OPEN-SOURCE-GRAPHICS-LIBRARY?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja)
|
||||
- [NuttX library](https://docs-lvgl-io.translate.goog/master/details/integration/os/nuttx.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja)
|
||||
- [RT-Thread RTOS](https://docs-lvgl-io.translate.goog/master/details/integration/os/rt-thread.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja)
|
||||
- NXP MCUXpresso library
|
||||
- CMSIS-Pack
|
||||
|
||||
## :robot: サンプルプログラム
|
||||
|
||||
ウィジェット・レイアウト・スタイルのサンプルプログラムを用意しました。
|
||||
C と MicroPython のコードを選べます。
|
||||
オンラインの MicroPythonエディタ へのリンクにより、サンプルプログラムの動作確認・編集もできます。
|
||||
|
||||
その他のサンプルプログラムは [Examples フォルダ](https://github.com/lvgl/lvgl/tree/master/examples) :gb: を確認してください。
|
||||
|
||||
### Button with Click Event
|
||||
|
||||

|
||||
|
||||
<details>
|
||||
<summary>C code</summary>
|
||||
|
||||
```c
|
||||
lv_obj_t * btn = lv_button_create(lv_screen_active()); /*Add a button to the current screen*/
|
||||
lv_obj_center(btn); /*Set its position*/
|
||||
lv_obj_set_size(btn, 100, 50); /*Set its size*/
|
||||
lv_obj_add_event(btn, btn_event_cb, LV_EVENT_CLICKED, NULL); /*Assign a callback to the button*/
|
||||
|
||||
lv_obj_t * label = lv_label_create(btn); /*Add a label to the button*/
|
||||
lv_label_set_text(label, "Button"); /*Set the labels text*/
|
||||
lv_obj_center(label); /*Align the label to the center*/
|
||||
...
|
||||
|
||||
void btn_event_cb(lv_event_t * e)
|
||||
{
|
||||
printf("Clicked\n");
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>MicroPython code | <a href="https://sim.lvgl.io/v8.3/micropython/ports/javascript/index.html?script_startup=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/header.py&script=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/widgets/slider/lv_example_slider_2.py&script_direct=926bde43ec7af0146c486de470c53f11f167491e" target="_blank">Online Simulator</a> :gb:</summary>
|
||||
|
||||
```python
|
||||
def btn_event_cb(e):
|
||||
print("Clicked")
|
||||
|
||||
# Create a Button and a Label
|
||||
btn = lv.btn(lv.screen_active())
|
||||
btn.center()
|
||||
btn.set_size(100, 50)
|
||||
btn.add_event(btn_event_cb, lv.EVENT.CLICKED, None)
|
||||
|
||||
label = lv.label(btn)
|
||||
label.set_text("Button")
|
||||
label.center()
|
||||
```
|
||||
</details>
|
||||
<br>
|
||||
|
||||
### Checkboxes with Layout
|
||||

|
||||
|
||||
<details>
|
||||
<summary>C code</summary>
|
||||
|
||||
```c
|
||||
|
||||
lv_obj_set_flex_flow(lv_screen_active(), LV_FLEX_FLOW_COLUMN);
|
||||
lv_obj_set_flex_align(lv_screen_active(), LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER);
|
||||
|
||||
lv_obj_t * cb;
|
||||
cb = lv_checkbox_create(lv_screen_active());
|
||||
lv_checkbox_set_text(cb, "Apple");
|
||||
lv_obj_add_event(cb, event_handler, LV_EVENT_ALL, NULL);
|
||||
|
||||
cb = lv_checkbox_create(lv_screen_active());
|
||||
lv_checkbox_set_text(cb, "Banana");
|
||||
lv_obj_add_state(cb, LV_STATE_CHECKED);
|
||||
lv_obj_add_event(cb, event_handler, LV_EVENT_ALL, NULL);
|
||||
|
||||
cb = lv_checkbox_create(lv_screen_active());
|
||||
lv_checkbox_set_text(cb, "Lemon");
|
||||
lv_obj_add_state(cb, LV_STATE_DISABLED);
|
||||
lv_obj_add_event(cb, event_handler, LV_EVENT_ALL, NULL);
|
||||
|
||||
cb = lv_checkbox_create(lv_screen_active());
|
||||
lv_obj_add_state(cb, LV_STATE_CHECKED | LV_STATE_DISABLED);
|
||||
lv_checkbox_set_text(cb, "Melon\nand a new line");
|
||||
lv_obj_add_event(cb, event_handler, LV_EVENT_ALL, NULL);
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>MicroPython code | <a href="https://sim.lvgl.io/v8.3/micropython/ports/javascript/index.html?script_startup=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/header.py&script=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/widgets/slider/lv_example_slider_2.py&script_direct=311d37e5f70daf1cb0d2cad24c7f72751b5f1792" target="_blank">Online Simulator</a> :gb:</summary>
|
||||
|
||||
```python
|
||||
def event_handler(e):
|
||||
code = e.get_code()
|
||||
obj = e.get_target_obj()
|
||||
if code == lv.EVENT.VALUE_CHANGED:
|
||||
txt = obj.get_text()
|
||||
if obj.get_state() & lv.STATE.CHECKED:
|
||||
state = "Checked"
|
||||
else:
|
||||
state = "Unchecked"
|
||||
print(txt + ":" + state)
|
||||
|
||||
|
||||
lv.scr_act().set_flex_flow(lv.FLEX_FLOW.COLUMN)
|
||||
lv.scr_act().set_flex_align(lv.FLEX_ALIGN.CENTER, lv.FLEX_ALIGN.START, lv.FLEX_ALIGN.CENTER)
|
||||
|
||||
cb = lv.checkbox(lv.screen_active())
|
||||
cb.set_text("Apple")
|
||||
cb.add_event(event_handler, lv.EVENT.ALL, None)
|
||||
|
||||
cb = lv.checkbox(lv.screen_active())
|
||||
cb.set_text("Banana")
|
||||
cb.add_state(lv.STATE.CHECKED)
|
||||
cb.add_event(event_handler, lv.EVENT.ALL, None)
|
||||
|
||||
cb = lv.checkbox(lv.screen_active())
|
||||
cb.set_text("Lemon")
|
||||
cb.add_state(lv.STATE.DISABLED)
|
||||
cb.add_event(event_handler, lv.EVENT.ALL, None)
|
||||
|
||||
cb = lv.checkbox(lv.screen_active())
|
||||
cb.add_state(lv.STATE.CHECKED | lv.STATE.DISABLED)
|
||||
cb.set_text("Melon")
|
||||
cb.add_event(event_handler, lv.EVENT.ALL, None)
|
||||
```
|
||||
|
||||
</details>
|
||||
<br>
|
||||
|
||||
### Styling a Slider
|
||||

|
||||
|
||||
|
||||
<details>
|
||||
<summary>C code</summary>
|
||||
|
||||
```c
|
||||
lv_obj_t * slider = lv_slider_create(lv_screen_active());
|
||||
lv_slider_set_value(slider, 70, LV_ANIM_OFF);
|
||||
lv_obj_set_size(slider, 300, 20);
|
||||
lv_obj_center(slider);
|
||||
|
||||
/*Add local styles to MAIN part (background rectangle)*/
|
||||
lv_obj_set_style_bg_color(slider, lv_color_hex(0x0F1215), LV_PART_MAIN);
|
||||
lv_obj_set_style_bg_opa(slider, 255, LV_PART_MAIN);
|
||||
lv_obj_set_style_border_color(slider, lv_color_hex(0x333943), LV_PART_MAIN);
|
||||
lv_obj_set_style_border_width(slider, 5, LV_PART_MAIN);
|
||||
lv_obj_set_style_pad_all(slider, 5, LV_PART_MAIN);
|
||||
|
||||
/*Create a reusable style sheet for the INDICATOR part*/
|
||||
static lv_style_t style_indicator;
|
||||
lv_style_init(&style_indicator);
|
||||
lv_style_set_bg_color(&style_indicator, lv_color_hex(0x37B9F5));
|
||||
lv_style_set_bg_grad_color(&style_indicator, lv_color_hex(0x1464F0));
|
||||
lv_style_set_bg_grad_dir(&style_indicator, LV_GRAD_DIR_HOR);
|
||||
lv_style_set_shadow_color(&style_indicator, lv_color_hex(0x37B9F5));
|
||||
lv_style_set_shadow_width(&style_indicator, 15);
|
||||
lv_style_set_shadow_spread(&style_indicator, 5);
|
||||
|
||||
/*Add the style sheet to the slider's INDICATOR part*/
|
||||
lv_obj_add_style(slider, &style_indicator, LV_PART_INDICATOR);
|
||||
|
||||
/*Add the same style to the KNOB part too and locally overwrite some properties*/
|
||||
lv_obj_add_style(slider, &style_indicator, LV_PART_KNOB);
|
||||
|
||||
lv_obj_set_style_outline_color(slider, lv_color_hex(0x0096FF), LV_PART_KNOB);
|
||||
lv_obj_set_style_outline_width(slider, 3, LV_PART_KNOB);
|
||||
lv_obj_set_style_outline_pad(slider, -5, LV_PART_KNOB);
|
||||
lv_obj_set_style_shadow_spread(slider, 2, LV_PART_KNOB);
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>MicroPython code |
|
||||
<a href="https://sim.lvgl.io/v8.3/micropython/ports/javascript/index.html?script_startup=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/header.py&script=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/widgets/slider/lv_example_slider_2.py&script_direct=c431c7b4dfd2cc0dd9c392b74365d5af6ea986f0" target="_blank">Online Simulator</a> :gb:
|
||||
</summary>
|
||||
|
||||
|
||||
```python
|
||||
# Create a slider and add the style
|
||||
slider = lv.slider(lv.screen_active())
|
||||
slider.set_value(70, lv.ANIM.OFF)
|
||||
slider.set_size(300, 20)
|
||||
slider.center()
|
||||
|
||||
# Add local styles to MAIN part (background rectangle)
|
||||
slider.set_style_bg_color(lv.color_hex(0x0F1215), lv.PART.MAIN)
|
||||
slider.set_style_bg_opa(255, lv.PART.MAIN)
|
||||
slider.set_style_border_color(lv.color_hex(0x333943), lv.PART.MAIN)
|
||||
slider.set_style_border_width(5, lv.PART.MAIN)
|
||||
slider.set_style_pad_all(5, lv.PART.MAIN)
|
||||
|
||||
# Create a reusable style sheet for the INDICATOR part
|
||||
style_indicator = lv.style_t()
|
||||
style_indicator.init()
|
||||
style_indicator.set_bg_color(lv.color_hex(0x37B9F5))
|
||||
style_indicator.set_bg_grad_color(lv.color_hex(0x1464F0))
|
||||
style_indicator.set_bg_grad_dir(lv.GRAD_DIR.HOR)
|
||||
style_indicator.set_shadow_color(lv.color_hex(0x37B9F5))
|
||||
style_indicator.set_shadow_width(15)
|
||||
style_indicator.set_shadow_spread(5)
|
||||
|
||||
# Add the style sheet to the slider's INDICATOR part
|
||||
slider.add_style(style_indicator, lv.PART.INDICATOR)
|
||||
slider.add_style(style_indicator, lv.PART.KNOB)
|
||||
|
||||
# Add the same style to the KNOB part too and locally overwrite some properties
|
||||
slider.set_style_outline_color(lv.color_hex(0x0096FF), lv.PART.KNOB)
|
||||
slider.set_style_outline_width(3, lv.PART.KNOB)
|
||||
slider.set_style_outline_pad(-5, lv.PART.KNOB)
|
||||
slider.set_style_shadow_spread(2, lv.PART.KNOB)
|
||||
```
|
||||
</details>
|
||||
<br>
|
||||
|
||||
### English, Hebrew (mixed LRT-RTL) and Chinese texts
|
||||
|
||||

|
||||
|
||||
<details>
|
||||
<summary>C code</summary>
|
||||
|
||||
```c
|
||||
lv_obj_t * ltr_label = lv_label_create(lv_screen_active());
|
||||
lv_label_set_text(ltr_label, "In modern terminology, a microcontroller is similar to a system on a chip (SoC).");
|
||||
lv_obj_set_style_text_font(ltr_label, &lv_font_montserrat_16, 0);
|
||||
lv_obj_set_width(ltr_label, 310);
|
||||
lv_obj_align(ltr_label, LV_ALIGN_TOP_LEFT, 5, 5);
|
||||
|
||||
lv_obj_t * rtl_label = lv_label_create(lv_screen_active());
|
||||
lv_label_set_text(rtl_label,"מעבד, או בשמו המלא יחידת עיבוד מרכזית (באנגלית: CPU - Central Processing Unit).");
|
||||
lv_obj_set_style_base_dir(rtl_label, LV_BASE_DIR_RTL, 0);
|
||||
lv_obj_set_style_text_font(rtl_label, &lv_font_dejavu_16_persian_hebrew, 0);
|
||||
lv_obj_set_width(rtl_label, 310);
|
||||
lv_obj_align(rtl_label, LV_ALIGN_LEFT_MID, 5, 0);
|
||||
|
||||
lv_obj_t * cz_label = lv_label_create(lv_screen_active());
|
||||
lv_label_set_text(cz_label,
|
||||
"嵌入式系统(Embedded System),\n是一种嵌入机械或电气系统内部、具有专一功能和实时计算性能的计算机系统。");
|
||||
lv_obj_set_style_text_font(cz_label, &lv_font_source_han_sans_sc_16_cjk, 0);
|
||||
lv_obj_set_width(cz_label, 310);
|
||||
lv_obj_align(cz_label, LV_ALIGN_BOTTOM_LEFT, 5, -5);
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>MicroPython code | <a href="https://sim.lvgl.io/v8.3/micropython/ports/javascript/index.html?script_startup=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/header.py&script=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/widgets/slider/lv_example_slider_2.py&script_direct=18bb38200a64e10ead1aa17a65c977fc18131842" target="_blank">Online Simulator</a> :gb:</summary>
|
||||
|
||||
```python
|
||||
ltr_label = lv.label(lv.screen_active())
|
||||
ltr_label.set_text("In modern terminology, a microcontroller is similar to a system on a chip (SoC).")
|
||||
ltr_label.set_style_text_font(lv.font_montserrat_16, 0);
|
||||
|
||||
ltr_label.set_width(310)
|
||||
ltr_label.align(lv.ALIGN.TOP_LEFT, 5, 5)
|
||||
|
||||
rtl_label = lv.label(lv.screen_active())
|
||||
rtl_label.set_text("מעבד, או בשמו המלא יחידת עיבוד מרכזית (באנגלית: CPU - Central Processing Unit).")
|
||||
rtl_label.set_style_base_dir(lv.BASE_DIR.RTL, 0)
|
||||
rtl_label.set_style_text_font(lv.font_dejavu_16_persian_hebrew, 0)
|
||||
rtl_label.set_width(310)
|
||||
rtl_label.align(lv.ALIGN.LEFT_MID, 5, 0)
|
||||
|
||||
font_han_sans_16_cjk = lv.font_load("S:../../assets/font/lv_font_source_han_sans_sc_16_cjk.fnt")
|
||||
|
||||
cz_label = lv.label(lv.screen_active())
|
||||
cz_label.set_style_text_font(font_han_sans_16_cjk, 0)
|
||||
cz_label.set_text("嵌入式系统(Embedded System),\n是一种嵌入机械或电气系统内部、具有专一功能和实时计算性能的计算机系统。")
|
||||
cz_label.set_width(310)
|
||||
cz_label.align(lv.ALIGN.BOTTOM_LEFT, 5, -5)
|
||||
|
||||
```
|
||||
</details>
|
||||
|
||||
## :arrow_forward: はじめに
|
||||
LVGLを使い始める時は、以下の順に進める事をおすすめします。
|
||||
|
||||
**LVGLに触れてみましょう**
|
||||
|
||||
1. LVGLの動きを [オンラインデモ](https://lvgl-io.translate.goog/demos?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja) で確認しましょう。 (3分間)
|
||||
2. ドキュメントの [Introduction](https://docs-lvgl-io.translate.goog/master/intro/index.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja) を読みましょう。 (5分間)
|
||||
3. LVGLの基本に慣れるため [Quick overview](https://docs-lvgl-io.translate.goog/master/intro/getting_started.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja) を読みましょう。 (15分間)
|
||||
|
||||
**LVGLを使ってみましょう**
|
||||
|
||||
4. [シミュレータ](https://docs-lvgl-io.translate.goog/master/details/integration/ide/pc-simulator.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja) をセットアップしましょう。 (10 minutes)
|
||||
5. [サンプルプログラム](https://github.com/lvgl/lvgl/tree/master/examples) :gb: を動かしてみましょう。
|
||||
6. [移植ガイド](https://docs-lvgl-io.translate.goog/master/details/integration/adding-lvgl-to-your-project/index.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja) を参考に、LVGLを開発ボードに移植してみましょう。すぐ使える形の [プロジェクト](https://github.com/lvgl?q=lv_port_) :gb: も用意してあります。
|
||||
|
||||
**より詳しく体験してみましょう**
|
||||
|
||||
7. ライブラリの理解を深めるため [Overview](https://docs-lvgl-io.translate.goog/master/intro/getting_started.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja) を読みましょう。 (2~3時間)
|
||||
8. ウィジェットの機能や使い方の詳細は [Widgets](https://docs-lvgl-io.translate.goog/master/details/widgets/index.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja) でご確認ください。
|
||||
|
||||
**助け合いましょう**
|
||||
|
||||
9. 質問がある場合は [Forum](http://forum.lvgl.io/) :gb: で質問して下さい。
|
||||
10. LVGLの改善への協力は大歓迎です。詳細は [Contributing guide](https://docs-lvgl-io.translate.goog/master/contributing/index.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja) をご覧ください。 (15分間)
|
||||
|
||||
**さらに理解を深めましょう**
|
||||
|
||||
11. [SquareLine Studio](https://squareline.io/) :gb: をダウンロードして試用してみましょう。
|
||||
12. 技術的サポートが必要であれば、[技術サービス](https://lvgl.io/services) :gb: に問い合わせて下さい。
|
||||
|
||||
|
||||
## :handshake: 技術サービス
|
||||
[LVGL LLC](https://www.digikey.com/en/design-services-providers/lvgl-kft) は、LVGLライブラリの確かな背景を元に、UI開発のための様々な技術サービスを提供するために設立されました。 UIとグラフィックス業界における15年以上の実績を活かし、UIを次のレベルに引き上げるお手伝いを致します。
|
||||
|
||||
- **グラフィックデザイン** 当社のグラフィックデザイナーは、製品とハードウェアのリソースに合わせて美しくモダンなデザインにするエキスパートです。
|
||||
- **UI実装** お客様または弊社で作成したデザインを元に、UIを実装することも可能です。お客様のハードウェアとLVGLを最大限に活用することをお約束します。
|
||||
LVGLにない機能やウィジェットは、私たちが実装しますのでご安心ください。
|
||||
- **コンサルタント&技術サポート** UI開発において、価格と時間を要する作業でのリスクを減らすため、コンサルティングも含めてサポート致します。
|
||||
- **Board certification** development board または production ready kit を提供している企業に対しては、ボードがLVGLを実行できるようにするためのボード認定を行います。
|
||||
|
||||
|
||||
サンプルは [Demos](https://lvgl-io.translate.goog/demos?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja) をご覧ください。
|
||||
詳しくは [Services page](https://lvgl-io.translate.goog/services?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja) をご覧ください。
|
||||
|
||||
お問い合わせは [問い合わせフォーム](https://lvgl.io/#contact) :gb: より送信して下さい。
|
||||
|
||||
|
||||
## :star2: 協力
|
||||
LVGLはオープンプロジェクトであり、協力は大歓迎です。
|
||||
色々な方法で協力できます。
|
||||
協力方法の例
|
||||
- LVGLを使用した作品やプロジェクトの公表
|
||||
- サンプルプログラムの作成
|
||||
- ドキュメントの改善
|
||||
- バグの修正
|
||||
|
||||
協力方法の詳細については、ドキュメントの [Contributing section](https://docs-lvgl-io.translate.goog/master/contributing/index.html?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja) をご覧ください。
|
||||
|
||||
すでに 300人以上がLVGLに足跡を残しています。いっしょに活動しましょう! :slightly_smiling_face:
|
||||
|
||||
<a href="https://github.com/lvgl/lvgl/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=lvgl/lvgl&max=48" />
|
||||
</a>
|
||||
|
||||
... and many others.
|
||||
453
managed_components/lvgl__lvgl/docs/README_pt_BR.md
Normal file
@@ -0,0 +1,453 @@
|
||||
**NOTA IMPORTANTE** A próxima versão principal (v9.0.0) está sendo desenvolvida na branch master.
|
||||
A última versão estável está disponível na branch [release/v8.3](https://github.com/lvgl/lvgl/tree/release/v8.3).
|
||||
|
||||
---
|
||||
|
||||
<a href="https://github.com/sponsors/lvgl" target="_blank"><img align="left" src="https://lvgl.io/github-assets/sponsor.png" height="32px"></a>
|
||||
|
||||
<p align="right">
|
||||
<a href="../README.md">English</a> |
|
||||
<a href="./README_zh.md">中文</a> |
|
||||
<b>Português do Brasil</b> |
|
||||
<a href="./README_jp.md">日本語</a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<img src="https://lvgl.io/github-assets/logo-colored.png" width=300px>
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
<h1 align="center">LVGL - Biblioteca gráfica leve e versátil</h1>
|
||||
<br>
|
||||
<div align="center">
|
||||
<img src="https://raw.githubusercontent.com/kisvegabor/test/master/smartwatch_demo.gif">
|
||||
|
||||
<img border="1px" src="https://lvgl.io/github-assets/widgets-demo.gif">
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://lvgl.io" title="Página inicial do LVGL">Site</a> |
|
||||
<a href="https://docs.lvgl.io/" title="Documentação detalhada com +100 exemplos">Documentação</a> |
|
||||
<a href="https://forum.lvgl.io" title="Obtenha ajuda e ajude outras pessoas">Fórum</a> |
|
||||
<a href="https://lvgl.io/services" title="Design gráfico, implementações e consultoria de serviços">Serviços</a> |
|
||||
<a href="https://lvgl.io/demos" title="Execute demonstrações no seu navegador">Demonstrações</a>
|
||||
</p>
|
||||
|
||||
<br>
|
||||
|
||||
## :monocle_face: Visão Geral
|
||||
|
||||
**Maduro e popular**
|
||||
|
||||
LVGL é a biblioteca gráfica incorporada gratuita e de código aberto mais popular para criar belas interfaces de usuário para qualquer display do tipo MCU, MPU. Ela é suportada por fornecedores e projetos líderes do setor, como ARM, STM32, NXP, Espressif, Nuvoton, Arduino, RT-Thread, Zephyr, NuttX, Adafruit e muitos outros.
|
||||
|
||||
**Rico em recursos**
|
||||
|
||||
Ela tem todos os recursos para a criação de GUIs modernas e bonitas: mais de 30 widgets integrados, um sistema de design poderoso, gerenciadores de layout inspirados na web e um sistema de tipografia com suporte para vários idiomas. Para integrar o LVGL em sua plataforma, tudo que você precisa é de pelo menos 32kB de RAM e 128kB de Flash, um compilador C, um frame buffer e pelo menos uma tela de tamanho 1/10 para renderização.
|
||||
|
||||
**Editor UI profissional**
|
||||
|
||||
SquareLine Studio é um editor de interface do usuário de (arrasta e solta) profissional para LVGL. Ele roda em Windows, Linux e MacOS também e você pode experimentá-lo sem se registrar no site.
|
||||
|
||||
**Serviços**
|
||||
|
||||
Nossa equipe está pronta para ajudá-lo com design gráfico, implementação de UI e serviços de consultoria. Entre em contato conosco se precisar de algum suporte durante o desenvolvimento de seu próximo projeto de GUI.
|
||||
|
||||
## :rocket: Recursos
|
||||
|
||||
**Gratuito e portátil**
|
||||
|
||||
- Uma biblioteca C totalmente portátil (compatível com C++) sem dependências externas.
|
||||
- Pode ser compilado para qualquer display MCU ou MPU, e qualquer sistema operacional de tempo real (RT-OS).
|
||||
- Suporta monitores monocromáticos, ePaper, OLED ou TFT. [Guia de portabilidade](https://docs.lvgl.io/master/details/integration/adding-lvgl-to-your-project/index.html)
|
||||
- Distribuído sob a licença do MIT, para que você também possa usá-lo facilmente em projetos comerciais.
|
||||
- Precisa de apenas 32 kB de RAM e 128 kB de Flash, um frame buffer e pelo menos uma tela de tamanho 1/10 para renderização.
|
||||
- Sistemas operacionais, memória externa e GPU são suportados, mas não obrigatórios.
|
||||
|
||||
**Widgets, designs, layouts e muito mais**
|
||||
|
||||
- Mais de 30 widgets integrados: botão, etiqueta (label), controle deslizante (slider), gráfico (chart), teclado, medidor (meter), tabelas e muito mais.
|
||||
- Sistema de design flexível com pelo menos 100 propriedades de estilo para personalizar qualquer parte dos widgets.
|
||||
- Mecanismos de layouts Flexbox e Grid para dimensionar e posicionar automaticamente os widgets de maneira responsiva.
|
||||
- Os textos são renderizados com codificação UTF-8, suportando sistemas de escrita CJK (chinês, japonês e coreano), tailandês, hindi, árabe e persa.
|
||||
- Quebra de palavras (word wrapping), espaçamento entre letras (kerning), rolagem de texto (scrolling), renderização subpixel, entrada em chinês Pinyin-IME e emojis.
|
||||
- Mecanismo de renderização que suporta animações, anti-aliasing, opacidade, rolagem suave (smooth scroll), sombras, transformação de imagens, etc.
|
||||
- Suporta mouse, touchpad, teclado, botões externos, dispositivos de entrada codificadores (encoders).
|
||||
- Suporta vários monitores.
|
||||
|
||||
**Suporte de vinculação (binding) e compilação de arquivos**
|
||||
|
||||
- Exposição da API do LVGL com o [Micropython](https://blog.lvgl.io/2019-02-20/micropython-bindings)
|
||||
- Nenhum sistema de compilação personalizado é usado. Você pode construir o LVGL enquanto constrói os outros arquivos do seu projeto.
|
||||
- O suporte para Make e [CMake](https://docs.lvgl.io/master/details/integration/building/cmake.html) já vem incluído.
|
||||
- [Desenvolva no PC](https://docs.lvgl.io/master/details/integration/ide/pc-simulator.html) e use o mesmo código de interface do usuário em hardwares incorporados (embedded hardware).
|
||||
- Converta o código C para um arquivo HTML com o [Emscripten port](https://github.com/lvgl/lv_web_emscripten).
|
||||
|
||||
**Documentação, ferramentas e serviços**
|
||||
|
||||
- Documentação detalhada com [+100 exemplos simples](https://docs.lvgl.io/master/examples.html)
|
||||
- [SquareLine Studio](https://squareline.io) - Um software editor UI profissional e fácil de usar, para acelerar e simplificar o desenvolvimento da interface do usuário.
|
||||
- [Serviços](https://lvgl.io/services) como design de UI, implementação e consultoria para tornar o desenvolvimento de UI mais simples e rápido.
|
||||
|
||||
## :heart: Patrocinador
|
||||
|
||||
Se o LVGL economizou muito tempo e dinheiro ou você apenas se divertiu ao usá-lo, considere Apoiar o desenvolvimento.
|
||||
|
||||
**Como e com o que utilizamos os recursos doados?**
|
||||
Nosso objetivo é fornecer compensação financeira para as pessoas que mais fazem pelo LVGL. Isso significa que não apenas os mantenedores, mas qualquer pessoa que implemente um ótimo recurso deve receber um pagamento com o dinheiro acumulado. Usamos as doações para cobrir nossos custos operacionais, como servidores e serviços relacionados.
|
||||
|
||||
**Como doar?**
|
||||
Usamos o [Open Collective](https://opencollective.com/lvgl), onde você pode enviar facilmente doações únicas ou recorrentes. Você também pode ver todas as nossas despesas de forma transparente.
|
||||
|
||||
**Como receber o pagamento de sua contribuição?**
|
||||
Se alguém implementar ou corrigir um problema rotulado como [Patrocinado](https://github.com/lvgl/lvgl/labels/Sponsored), essa pessoa receberá um pagamento por esse trabalho. Estimamos o tempo necessário, a complexidade e a importância da questão e definimos um preço de acordo. Para entrar, apenas comente sobre um problema patrocinado dizendo "Olá, gostaria de lidar com isso. É assim que estou planejando corrigi-lo/implementá-lo...". Um trabalho é considerado pronto quando é aprovado e mesclado por um mantenedor. Depois disso, você pode enviar uma "despesa" (expense) pela plataforma [opencollective.com](https://opencollective.com/lvgl) e então receberá o pagamento em alguns dias.
|
||||
|
||||
**Organizações que apoiam o projeto LVGL**<br>
|
||||
[](https://opencollective.com/lvgl)
|
||||
|
||||
**Pessoas que apoiam o projeto LVGL**<br>
|
||||
[](https://opencollective.com/lvgl)
|
||||
|
||||
## :package: Pacotes
|
||||
|
||||
LVGL está disponível para:
|
||||
|
||||
- [Arduino library](https://docs.lvgl.io/master/details/integration/framework/arduino.html)
|
||||
- [PlatformIO package](https://registry.platformio.org/libraries/lvgl/lvgl)
|
||||
- [Zephyr library](https://docs.zephyrproject.org/latest/kconfig.html#CONFIG_LVGL)
|
||||
- [ESP32 component](https://docs.lvgl.io/master/details/integration/chip/espressif.html)
|
||||
- [NXP MCUXpresso component](https://www.nxp.com/design/software/embedded-software/lvgl-open-source-graphics-library:LITTLEVGL-OPEN-SOURCE-GRAPHICS-LIBRARY)
|
||||
- [NuttX library](https://docs.lvgl.io/master/details/integration/os/nuttx.html)
|
||||
- [RT-Thread RTOS](https://docs.lvgl.io/master/details/integration/os/rt-thread.html)
|
||||
- NXP MCUXpresso library
|
||||
- CMSIS-Pack
|
||||
|
||||
## :man_technologist: Exemplos
|
||||
|
||||
Veja como criar um botão com um evento de clique em C e MicroPython. Para mais exemplos, veja a pasta [examples](https://github.com/lvgl/lvgl/tree/master/examples).
|
||||
|
||||
### Botão com evento de clique
|
||||
|
||||

|
||||
|
||||
<details>
|
||||
<summary>Código C</summary>
|
||||
|
||||
```c
|
||||
lv_obj_t * btn = lv_button_create(lv_screen_active()); /* Adiciona o botão a tela atual */
|
||||
lv_obj_center(btn); /* Define a posição do botão */
|
||||
lv_obj_set_size(btn, 100, 50); /* Define o tamanho do botão */
|
||||
lv_obj_add_event(btn, btn_event_cb, LV_EVENT_CLICKED, NULL); /* Atribui um retorno de chamada (callback) ao botão */
|
||||
|
||||
lv_obj_t * label = lv_label_create(btn); /* Adiciona um rótulo (label) */
|
||||
lv_label_set_text(label, "Botão"); /* Define um texto para o rótulo (label) */
|
||||
lv_obj_center(label); /* Alinha o texto no centro do botão */
|
||||
...
|
||||
|
||||
void btn_event_cb(lv_event_t * e)
|
||||
{
|
||||
printf("Clicado\n");
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Código MicroPython | <a href="https://sim.lvgl.io/v8.3/micropython/ports/javascript/index.html?script_startup=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/header.py&script=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/widgets/slider/lv_example_slider_2.py&script_direct=926bde43ec7af0146c486de470c53f11f167491e" target="_blank">Simulador online</a></summary>
|
||||
|
||||
```python
|
||||
def btn_event_cb(e):
|
||||
print("Clicado")
|
||||
|
||||
# Cria um botão e um rótulo (label)
|
||||
btn = lv.btn(lv.screen_active())
|
||||
btn.center()
|
||||
btn.set_size(100, 50)
|
||||
btn.add_event(btn_event_cb, lv.EVENT.CLICKED, None)
|
||||
|
||||
label = lv.label(btn)
|
||||
label.set_text("Botão")
|
||||
label.center()
|
||||
```
|
||||
</details>
|
||||
<br>
|
||||
|
||||
### Caixas de seleção (chackboxes) com layout
|
||||

|
||||
|
||||
<details>
|
||||
<summary>Código em C</summary>
|
||||
|
||||
```c
|
||||
|
||||
lv_obj_set_flex_flow(lv_screen_active(), LV_FLEX_FLOW_COLUMN);
|
||||
lv_obj_set_flex_align(lv_screen_active(), LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER);
|
||||
|
||||
lv_obj_t * cb;
|
||||
cb = lv_checkbox_create(lv_screen_active());
|
||||
lv_checkbox_set_text(cb, "Maça");
|
||||
lv_obj_add_event(cb, event_handler, LV_EVENT_ALL, NULL);
|
||||
|
||||
cb = lv_checkbox_create(lv_screen_active());
|
||||
lv_checkbox_set_text(cb, "Banana");
|
||||
lv_obj_add_state(cb, LV_STATE_CHECKED);
|
||||
lv_obj_add_event(cb, event_handler, LV_EVENT_ALL, NULL);
|
||||
|
||||
cb = lv_checkbox_create(lv_screen_active());
|
||||
lv_checkbox_set_text(cb, "Limão");
|
||||
lv_obj_add_state(cb, LV_STATE_DISABLED);
|
||||
lv_obj_add_event(cb, event_handler, LV_EVENT_ALL, NULL);
|
||||
|
||||
cb = lv_checkbox_create(lv_screen_active());
|
||||
lv_obj_add_state(cb, LV_STATE_CHECKED | LV_STATE_DISABLED);
|
||||
lv_checkbox_set_text(cb, "Melão\ne uma nova linha");
|
||||
lv_obj_add_event(cb, event_handler, LV_EVENT_ALL, NULL);
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Código MicroPython | <a href="https://sim.lvgl.io/v8.3/micropython/ports/javascript/index.html?script_startup=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/header.py&script=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/widgets/slider/lv_example_slider_2.py&script_direct=311d37e5f70daf1cb0d2cad24c7f72751b5f1792" target="_blank">Online Simulator</a></summary>
|
||||
|
||||
```python
|
||||
def event_handler(e):
|
||||
code = e.get_code()
|
||||
obj = e.get_target_obj()
|
||||
if code == lv.EVENT.VALUE_CHANGED:
|
||||
txt = obj.get_text()
|
||||
if obj.get_state() & lv.STATE.CHECKED:
|
||||
state = "Marcador"
|
||||
else:
|
||||
state = "Desmarcado"
|
||||
print(txt + ":" + state)
|
||||
|
||||
|
||||
lv.scr_act().set_flex_flow(lv.FLEX_FLOW.COLUMN)
|
||||
lv.scr_act().set_flex_align(lv.FLEX_ALIGN.CENTER, lv.FLEX_ALIGN.START, lv.FLEX_ALIGN.CENTER)
|
||||
|
||||
cb = lv.checkbox(lv.screen_active())
|
||||
cb.set_text("Maça")
|
||||
cb.add_event(event_handler, lv.EVENT.ALL, None)
|
||||
|
||||
cb = lv.checkbox(lv.screen_active())
|
||||
cb.set_text("Banana")
|
||||
cb.add_state(lv.STATE.CHECKED)
|
||||
cb.add_event(event_handler, lv.EVENT.ALL, None)
|
||||
|
||||
cb = lv.checkbox(lv.screen_active())
|
||||
cb.set_text("Limão")
|
||||
cb.add_state(lv.STATE.DISABLED)
|
||||
cb.add_event(event_handler, lv.EVENT.ALL, None)
|
||||
|
||||
cb = lv.checkbox(lv.screen_active())
|
||||
cb.add_state(lv.STATE.CHECKED | lv.STATE.DISABLED)
|
||||
cb.set_text("Melão")
|
||||
cb.add_event(event_handler, lv.EVENT.ALL, None)
|
||||
```
|
||||
|
||||
</details>
|
||||
<br>
|
||||
|
||||
### Estilizando um controle deslizante (slider)
|
||||

|
||||
|
||||
|
||||
<details>
|
||||
<summary>Código C</summary>
|
||||
|
||||
```c
|
||||
lv_obj_t * slider = lv_slider_create(lv_screen_active());
|
||||
lv_slider_set_value(slider, 70, LV_ANIM_OFF);
|
||||
lv_obj_set_size(slider, 300, 20);
|
||||
lv_obj_center(slider);
|
||||
|
||||
/* Adiciona estilos locais à parte MAIN (retângulo de fundo) */
|
||||
lv_obj_set_style_bg_color(slider, lv_color_hex(0x0F1215), LV_PART_MAIN);
|
||||
lv_obj_set_style_bg_opa(slider, 255, LV_PART_MAIN);
|
||||
lv_obj_set_style_border_color(slider, lv_color_hex(0x333943), LV_PART_MAIN);
|
||||
lv_obj_set_style_border_width(slider, 5, LV_PART_MAIN);
|
||||
lv_obj_set_style_pad_all(slider, 5, LV_PART_MAIN);
|
||||
|
||||
/* Crie uma folha de estilo reutilizável para a parte do (INDICADOR) */
|
||||
static lv_style_t style_indicator;
|
||||
lv_style_init(&style_indicator);
|
||||
lv_style_set_bg_color(&style_indicator, lv_color_hex(0x37B9F5));
|
||||
lv_style_set_bg_grad_color(&style_indicator, lv_color_hex(0x1464F0));
|
||||
lv_style_set_bg_grad_dir(&style_indicator, LV_GRAD_DIR_HOR);
|
||||
lv_style_set_shadow_color(&style_indicator, lv_color_hex(0x37B9F5));
|
||||
lv_style_set_shadow_width(&style_indicator, 15);
|
||||
lv_style_set_shadow_spread(&style_indicator, 5);
|
||||
|
||||
/* Adicione a folha de estilo à parte do INDICATOR do controle deslizante (slider) */
|
||||
lv_obj_add_style(slider, &style_indicator, LV_PART_INDICATOR);
|
||||
|
||||
/* Adicione o mesmo estilo à parte do KNOB e sobrescreva localmente algumas propriedades */
|
||||
lv_obj_add_style(slider, &style_indicator, LV_PART_KNOB);
|
||||
|
||||
lv_obj_set_style_outline_color(slider, lv_color_hex(0x0096FF), LV_PART_KNOB);
|
||||
lv_obj_set_style_outline_width(slider, 3, LV_PART_KNOB);
|
||||
lv_obj_set_style_outline_pad(slider, -5, LV_PART_KNOB);
|
||||
lv_obj_set_style_shadow_spread(slider, 2, LV_PART_KNOB);
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Código MicroPython |
|
||||
<a href="https://sim.lvgl.io/v8.3/micropython/ports/javascript/index.html?script_startup=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/header.py&script=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/widgets/slider/lv_example_slider_2.py&script_direct=c431c7b4dfd2cc0dd9c392b74365d5af6ea986f0" target="_blank">Simulador online</a>
|
||||
</summary>
|
||||
|
||||
|
||||
```python
|
||||
# Crie um controle deslizante (slider) e adicione o estilo
|
||||
slider = lv.slider(lv.screen_active())
|
||||
slider.set_value(70, lv.ANIM.OFF)
|
||||
slider.set_size(300, 20)
|
||||
slider.center()
|
||||
|
||||
# Adicione estilos locais à parte MAIN (retângulo de fundo)
|
||||
slider.set_style_bg_color(lv.color_hex(0x0F1215), lv.PART.MAIN)
|
||||
slider.set_style_bg_opa(255, lv.PART.MAIN)
|
||||
slider.set_style_border_color(lv.color_hex(0x333943), lv.PART.MAIN)
|
||||
slider.set_style_border_width(5, lv.PART.MAIN)
|
||||
slider.set_style_pad_all(5, lv.PART.MAIN)
|
||||
|
||||
# Crie uma folha de estilo reutilizável para a parte do INDICATOR
|
||||
style_indicator = lv.style_t()
|
||||
style_indicator.init()
|
||||
style_indicator.set_bg_color(lv.color_hex(0x37B9F5))
|
||||
style_indicator.set_bg_grad_color(lv.color_hex(0x1464F0))
|
||||
style_indicator.set_bg_grad_dir(lv.GRAD_DIR.HOR)
|
||||
style_indicator.set_shadow_color(lv.color_hex(0x37B9F5))
|
||||
style_indicator.set_shadow_width(15)
|
||||
style_indicator.set_shadow_spread(5)
|
||||
|
||||
# Adicione a folha de estilo à parte do INDICATOR do controle deslizante (slider)
|
||||
slider.add_style(style_indicator, lv.PART.INDICATOR)
|
||||
slider.add_style(style_indicator, lv.PART.KNOB)
|
||||
|
||||
# Adicione o mesmo estilo à parte do KNOB e sobrescreva localmente algumas propriedades
|
||||
slider.set_style_outline_color(lv.color_hex(0x0096FF), lv.PART.KNOB)
|
||||
slider.set_style_outline_width(3, lv.PART.KNOB)
|
||||
slider.set_style_outline_pad(-5, lv.PART.KNOB)
|
||||
slider.set_style_shadow_spread(2, lv.PART.KNOB)
|
||||
```
|
||||
|
||||
</details>
|
||||
<br>
|
||||
|
||||
### Textos em inglês, hebraico (LRT-RTL misto) e chinês
|
||||
|
||||

|
||||
|
||||
<details>
|
||||
<summary>Código C</summary>
|
||||
|
||||
```c
|
||||
lv_obj_t * ltr_label = lv_label_create(lv_screen_active());
|
||||
lv_label_set_text(ltr_label, "In modern terminology, a microcontroller is similar to a system on a chip (SoC).");
|
||||
lv_obj_set_style_text_font(ltr_label, &lv_font_montserrat_16, 0);
|
||||
lv_obj_set_width(ltr_label, 310);
|
||||
lv_obj_align(ltr_label, LV_ALIGN_TOP_LEFT, 5, 5);
|
||||
|
||||
lv_obj_t * rtl_label = lv_label_create(lv_screen_active());
|
||||
lv_label_set_text(rtl_label,"מעבד, או בשמו המלא יחידת עיבוד מרכזית (באנגלית: CPU - Central Processing Unit).");
|
||||
lv_obj_set_style_base_dir(rtl_label, LV_BASE_DIR_RTL, 0);
|
||||
lv_obj_set_style_text_font(rtl_label, &lv_font_dejavu_16_persian_hebrew, 0);
|
||||
lv_obj_set_width(rtl_label, 310);
|
||||
lv_obj_align(rtl_label, LV_ALIGN_LEFT_MID, 5, 0);
|
||||
|
||||
lv_obj_t * cz_label = lv_label_create(lv_screen_active());
|
||||
lv_label_set_text(cz_label,
|
||||
"嵌入式系统(Embedded System),\n是一种嵌入机械或电气系统内部、具有专一功能和实时计算性能的计算机系统。");
|
||||
lv_obj_set_style_text_font(cz_label, &lv_font_source_han_sans_sc_16_cjk, 0);
|
||||
lv_obj_set_width(cz_label, 310);
|
||||
lv_obj_align(cz_label, LV_ALIGN_BOTTOM_LEFT, 5, -5);
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Código MicroPython | <a href="https://sim.lvgl.io/v8.3/micropython/ports/javascript/index.html?script_startup=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/header.py&script=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/widgets/slider/lv_example_slider_2.py&script_direct=18bb38200a64e10ead1aa17a65c977fc18131842" target="_blank">Simulador online</a></summary>
|
||||
|
||||
```python
|
||||
ltr_label = lv.label(lv.screen_active())
|
||||
ltr_label.set_text("In modern terminology, a microcontroller is similar to a system on a chip (SoC).")
|
||||
ltr_label.set_style_text_font(lv.font_montserrat_16, 0);
|
||||
|
||||
ltr_label.set_width(310)
|
||||
ltr_label.align(lv.ALIGN.TOP_LEFT, 5, 5)
|
||||
|
||||
rtl_label = lv.label(lv.screen_active())
|
||||
rtl_label.set_text("מעבד, או בשמו המלא יחידת עיבוד מרכזית (באנגלית: CPU - Central Processing Unit).")
|
||||
rtl_label.set_style_base_dir(lv.BASE_DIR.RTL, 0)
|
||||
rtl_label.set_style_text_font(lv.font_dejavu_16_persian_hebrew, 0)
|
||||
rtl_label.set_width(310)
|
||||
rtl_label.align(lv.ALIGN.LEFT_MID, 5, 0)
|
||||
|
||||
font_han_sans_16_cjk = lv.font_load("S:../../assets/font/lv_font_source_han_sans_sc_16_cjk.fnt")
|
||||
|
||||
cz_label = lv.label(lv.screen_active())
|
||||
cz_label.set_style_text_font(font_han_sans_16_cjk, 0)
|
||||
cz_label.set_text("嵌入式系统(Embedded System),\n是一种嵌入机械或电气系统内部、具有专一功能和实时计算性能的计算机系统。")
|
||||
cz_label.set_width(310)
|
||||
cz_label.align(lv.ALIGN.BOTTOM_LEFT, 5, -5)
|
||||
|
||||
```
|
||||
</details>
|
||||
|
||||
## :arrow_forward: Começando
|
||||
Esta lista irá guiá-lo para começar com o LVGL passo a passo.
|
||||
|
||||
**Familiarize-se com o LVGL**
|
||||
|
||||
1. Confira as [demos on-line](https://lvgl.io/demos) para ver o LVGL em ação (~3 minutos)
|
||||
2. Leia a página de [introdução](https://docs.lvgl.io/master/intro/index.html) da documentação (~5 minutos)
|
||||
3. Familiarize-se com o básico na página de [visão geral rápida](https://docs.lvgl.io/master/intro/getting_started.html) (~15 minutos)
|
||||
|
||||
**Começando a usar o LVGL**
|
||||
|
||||
4. Configure um [simulador](https://docs.lvgl.io/master/details/integration/ide/pc-simulator.html) (~10 minutos)
|
||||
5. Experimente alguns [exemplos](https://github.com/lvgl/lvgl/tree/master/examples)
|
||||
6. Porte o LVGL para uma placa. Veja o guia [portando o LVGL](https://docs.lvgl.io/master/details/integration/adding-lvgl-to-your-project/index.html) ou veja um projeto pronto para usar em [projetos](https://github.com/lvgl?q=lv_port_)
|
||||
|
||||
**Torne-se um profissional**
|
||||
|
||||
7. Leia a página [visão geral](https://docs.lvgl.io/master/intro/getting_started.html) para entender melhor a biblioteca (~2-3 horas)
|
||||
8. Verifique a documentação dos [widgets](https://docs.lvgl.io/master/details/widgets/index.html) para ver seus recursos e usabilidade
|
||||
|
||||
**Obtenha ajuda e ajude outras pessoas**
|
||||
|
||||
9. Se você tiver dúvidas, acesse o [Fórum](http://forum.lvgl.io)
|
||||
10. Leia o guia de [contribuição](https://docs.lvgl.io/master/contributing/index.html) para ver como você pode ajudar a melhorar o LVGL (~15 minutos)
|
||||
|
||||
**E mais**
|
||||
|
||||
11. Baixe e experimente o editor [SquareLine Studio](https://squareline.io).
|
||||
12. Entre em contato conosco para [serviços e consultoria](https://lvgl.io/services).
|
||||
|
||||
## :handshake: Serviços
|
||||
A LVGL LLC foi criada para fornecer uma base sólida para a biblioteca LVGL e oferecer vários tipos de serviços para ajudá-lo no desenvolvimento da sua interface do usuário. Com mais de 15 anos de experiência na indústria gráfica e de interface do usuário, podemos ajudá-lo a levar sua interface do usuário para o próximo nível.
|
||||
|
||||
- **Design gráfico**: Nossos designers gráficos internos são especialistas em criar belos designs modernos que se adaptam ao seu produto e aos recursos do seu hardware.
|
||||
- **Implementação da interface do usuário**: Também podemos implementar sua interface do usuário com base no design que você ou nós criamos. Você pode ter certeza de que tiraremos o máximo proveito de seu hardware e do LVGL. Se um recurso ou widget estiver faltando no LVGL, não se preocupe, nós o implementaremos para você.
|
||||
- **Consultoria e Suporte**: Também podemos apoiá-lo com consultoria para evitar erros que podem te custar caros durante o desenvolvimento da sua interface do usuário.
|
||||
- **Certificação**: Para empresas que oferecem placas para desenvolvimento ou kits prontos para produção, fazemos certificação que mostram como uma placa pode executar o LVGL.
|
||||
|
||||
Confira nossas [demonstrações](https://lvgl.io/demos) como referência. Para obter mais informações, consulte a [página de serviços](https://lvgl.io/services).
|
||||
|
||||
[Fale conosco](https://lvgl.io/#contact) e conte como podemos ajudar.
|
||||
|
||||
## :star2: Contribuindo
|
||||
O LVGL é um projeto aberto e sua contribuição é muito bem-vinda. Há muitas maneiras de contribuir, desde simplesmente falando sobre seu projeto, escrevendo exemplos, melhorando a documentação, corrigindo bugs até hospedar seu próprio projeto sob a organização LVGL.
|
||||
|
||||
Para obter uma descrição detalhada das oportunidades de contribuição, visite a página de [contribuição](https://docs.lvgl.io/master/contributing/index.html) da documentação.
|
||||
|
||||
Mais de 300 pessoas já deixaram sua impressão digital no LVGL. Seja um deles! Veja o seu aqui! :slightly_smiling_face:
|
||||
|
||||
<a href="https://github.com/lvgl/lvgl/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=lvgl/lvgl&max=48" />
|
||||
</a>
|
||||
|
||||
... e muitos outros.
|
||||
464
managed_components/lvgl__lvgl/docs/README_zh.md
Normal file
@@ -0,0 +1,464 @@
|
||||
<a href="https://github.com/sponsors/lvgl" target="_blank"><img align="left" src="https://lvgl.io/github-assets/sponsor.png" height="32px"></a>
|
||||
|
||||
<p align="right">
|
||||
<a href="../README.md">English</a> | <b>中文</b> | <a href="./README_pt_BR.md">Português do Brasil</a> | <a href="./README_jp.md">日本語</a>
|
||||
</p>
|
||||
<br>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://lvgl.io/github-assets/logo-colored.png" width=300px>
|
||||
</p>
|
||||
|
||||
|
||||
<h1 align="center">
|
||||
Light and Versatile Graphics Library
|
||||
</h1>
|
||||
|
||||
<h1 align="center">
|
||||
轻量级通用型图形库
|
||||
</h1>
|
||||
|
||||
<div align="center">
|
||||
<img src="https://raw.githubusercontent.com/kisvegabor/test/master/smartwatch_demo.gif">
|
||||
|
||||
<img border="1px" src="https://lvgl.io/github-assets/widgets-demo.gif">
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://lvgl.io" title="Homepage of LVGL">官网 </a> |
|
||||
<a href="https://docs.lvgl.io/" title="Detailed documentation with 100+ examples">文档</a> |
|
||||
<a href="https://forum.lvgl.io" title="Get help and help others">论坛</a> |
|
||||
<a href="https://lvgl.io/demos" title="Demos running in your browser">示例</a> |
|
||||
<a href="https://lvgl.io/services" title="Graphics design, UI implementation and consulting">服务</a>
|
||||
</p>
|
||||
|
||||
|
||||
[中文宣传单](./flyers/LVGL-Chinese-Flyer.pdf)
|
||||
|
||||
## :ledger: 目录
|
||||
|
||||
- [:ledger: 目录](#ledger-目录)
|
||||
- [:rocket: 概况与总览](#rocket-概况与总览)
|
||||
- [硬件要求](#硬件要求)
|
||||
- [:package: 已经支持的平台](#package-已经支持的平台)
|
||||
- [如何入门](#如何入门)
|
||||
- [:robot: 例程](#robot-例程)
|
||||
- [Button with Click Event 按钮与点击事件](#button-with-click-event-按钮与点击事件)
|
||||
- [Micropython](#micropython)
|
||||
- [Checkboxes with Layout 带布局的复选框](#checkboxes-with-layout-带布局的复选框)
|
||||
- [Styling a Slider 设置滑块的样式](#styling-a-slider-设置滑块的样式)
|
||||
- [English, Hebrew (mixed LRT-RTL) and Chinese texts 英语、希伯来语( 双向文本排版 )和中文](#english-hebrew-mixed-lrt-rtl-and-chinese-texts-英语希伯来语-双向文本排版-和中文)
|
||||
- [:handshake: 服务](#handshake-服务)
|
||||
- [:star: 如何向社区贡献](#star-如何向社区贡献)
|
||||
|
||||
## :rocket: 概况与总览
|
||||
|
||||
**特性**
|
||||
|
||||
- 丰富且强大的模块化[图形组件](https://docs.lvgl.io/master/details/widgets/index.html):按钮
|
||||
(buttons)、图表 (charts)、列表 (lists)、滑动条 (sliders)、图片
|
||||
(images) 等
|
||||
- 高级的图形引擎:动画、抗锯齿、透明度、平滑滚动、图层混合等效果
|
||||
- 支持多种[输入设备](https://docs.lvgl.io/master/details/main-modules/indev.html):触摸屏、键盘、编码器、按键等
|
||||
- 支持[多显示设备](https://docs.lvgl.io/master/details/main-modules/display/index.html)
|
||||
- 不依赖特定的硬件平台,可以在任何显示屏上运行
|
||||
- 配置可裁剪(最低资源占用:64 kB Flash,16 kB RAM)
|
||||
- 基于UTF-8的多语种支持,例如中文、日文、韩文、阿拉伯文等
|
||||
- 可以通过[类CSS](https://docs.lvgl.io/master/details/common-widget-features/styles/style.html)的方式来设计、布局图形界面(例如:[Flexbox](https://docs.lvgl.io/master/details/common-widget-features/layouts/flex.html)、[Grid](https://docs.lvgl.io/master/details/common-widget-features/layouts/grid.html))
|
||||
- 支持操作系统、外置内存、以及硬件加速(LVGL已内建支持STM32 DMA2D、SWM341 DMA2D、NXP PXP和VGLite)
|
||||
- 即便仅有[单缓冲区(frame buffer)](https://docs.lvgl.io/master/details/main-modules/display/index.html)的情况下,也可保证渲染如丝般顺滑
|
||||
- 全部由C编写完成,并支持C++调用
|
||||
- 支持Micropython编程,参见:[LVGL API in Micropython](https://blog.lvgl.io/2019-02-20/micropython-bindings)
|
||||
- 支持[模拟器](https://docs.lvgl.io/master/details/integration/ide/pc-simulator.html) 仿真,可以无硬件依托进行开发
|
||||
- 丰富详实的[例程](https://github.com/lvgl/lvgl/tree/master/examples)
|
||||
- 详尽的[文档](http://docs.lvgl.io/) 以及API参考手册,可线上查阅或可下载为PDF格式
|
||||
|
||||
### 硬件要求
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><strong>要求</strong></td>
|
||||
<td><strong>最低要求</strong></td>
|
||||
<td><strong>建议要求</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>架构</strong></td>
|
||||
<td colspan="2">16、32、64位微控制器或微处理器</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>时钟</strong></td>
|
||||
<td> > 16 MHz</td>
|
||||
<td> > 48 MHz</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Flash/ROM</strong></td>
|
||||
<td> > 64 kB</td>
|
||||
<td> > 180 kB</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Static RAM</strong></td>
|
||||
<td> > 16 kB</td>
|
||||
<td> > 48 kB</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Draw buffer</strong></td>
|
||||
<td> > 1 × <em>hor. res.</em> pixels</td>
|
||||
<td> > 1/10屏幕大小 </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>编译器</strong></td>
|
||||
<td colspan="2">C99或更新 </td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
*注意:资源占用情况与具体硬件平台、编译器等因素有关,上表中仅给出参考值*
|
||||
|
||||
### :package: 已经支持的平台
|
||||
|
||||
LVGL本身并不依赖特定的硬件平台,任何满足LVGL硬件配置要求的微控制器均可运行LVGL。
|
||||
如下仅列举其中一部分:
|
||||
|
||||
- NXP: Kinetis, LPC, iMX, iMX RT
|
||||
- STM32F1, STM32F3, STM32F4, STM32F7, STM32L4, STM32L5, STM32H7
|
||||
- Microchip dsPIC33, PIC24, PIC32MX, PIC32MZ
|
||||
- [Linux frame buffer](https://blog.lvgl.io/2018-01-03/linux_fb) (/dev/fb)
|
||||
- [Espressif ESP32](https://github.com/lvgl/lv_port_esp32)
|
||||
- [Infineon Aurix](https://github.com/lvgl/lv_port_aurix)
|
||||
- Nordic NRF52 Bluetooth modules
|
||||
- Quectel modems
|
||||
- [SYNWIT SWM341](https://www.synwit.cn/)
|
||||
|
||||
LVGL也支持:
|
||||
- [Arduino library](https://docs.lvgl.io/master/details/integration/framework/arduino.html)
|
||||
- [PlatformIO package](https://registry.platformio.org/libraries/lvgl/lvgl)
|
||||
- [Zephyr library](https://docs.zephyrproject.org/latest/kconfig.html#CONFIG_LVGL)
|
||||
- [ESP32 component](https://docs.lvgl.io/master/details/integration/chip/espressif.html)
|
||||
- [NXP MCUXpresso component](https://www.nxp.com/design/software/embedded-software/lvgl-open-source-graphics-library:LITTLEVGL-OPEN-SOURCE-GRAPHICS-LIBRARY)
|
||||
- [NuttX library](https://docs.lvgl.io/master/details/integration/os/nuttx.html)
|
||||
- [RT-Thread RTOS](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/packages-manual/lvgl-docs/introduction)
|
||||
|
||||
|
||||
|
||||
## 如何入门
|
||||
|
||||
请按照如下顺序来学习LVGL:
|
||||
1. 使用[网页在线例程](https://lvgl.io/demos) 来体验LVGL(3分钟)
|
||||
2. 阅读文档[简介](https://docs.lvgl.io/master/intro/introduction.html)章节来初步了解LVGL(5分钟)
|
||||
3. 再来阅读一下文档快速[快速概览](https://docs.lvgl.io/master/intro/getting_started.html)章节来了解LVGL的基本知识(15分钟)
|
||||
4. 学习如何使用[模拟器](https://docs.lvgl.io/master/details/integration/ide/pc-simulator.html)来在电脑上仿真LVGL(10分钟)
|
||||
5. 试着动手实践一些[例程](https://github.com/lvgl/lvgl/tree/master/examples)
|
||||
6. 参考[移植指南](https://docs.lvgl.io/master/details/integration/adding-lvgl-to-your-project/index.html)尝试将LVGL移植到一块开发板上,LVGL也已经提供了一些移植好的[工程](https://github.com/lvgl?q=lv_port_)
|
||||
7. 仔细阅读文档[总览](https://docs.lvgl.io/master/details/main-modules/index.html)章节来更加深入的了解和熟悉LVGL(2-3小时)
|
||||
8. 浏览文档[组件(Widgets)](https://docs.lvgl.io/master/details/widgets/index.html)章节来了解如何使用它们
|
||||
9. 如果你有问题可以到LVGL[论坛](http://forum.lvgl.io/)提问
|
||||
10. 阅读文档[如何向社区贡献](https://docs.lvgl.io/master/contributing/index.html)章节来看看你能帮LVGL社区做些什么,以促进LVGL软件质量的不断提高(15分钟)
|
||||
|
||||
|
||||
## :robot: 例程
|
||||
|
||||
更多例程请参见 [examples](https://github.com/lvgl/lvgl/tree/master/examples) 文件夹。
|
||||
|
||||
### Button with Click Event 按钮与点击事件
|
||||
|
||||

|
||||
|
||||
<details>
|
||||
<summary>C code</summary>
|
||||
|
||||
```c
|
||||
lv_obj_t * btn = lv_button_create(lv_screen_active()); /*Add a button to the current screen*/
|
||||
lv_obj_center(btn); /*Set its position*/
|
||||
lv_obj_set_size(btn, 100, 50); /*Set its size*/
|
||||
lv_obj_add_event(btn, btn_event_cb, LV_EVENT_CLICKED, NULL); /*Assign a callback to the button*/
|
||||
|
||||
lv_obj_t * label = lv_label_create(btn); /*Add a label to the button*/
|
||||
lv_label_set_text(label, "Button"); /*Set the labels text*/
|
||||
lv_obj_center(label); /*Align the label to the center*/
|
||||
...
|
||||
|
||||
void btn_event_cb(lv_event_t * e)
|
||||
{
|
||||
printf("Clicked\n");
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
### Micropython
|
||||
|
||||
更多信息请到 [Micropython官网](https://docs.lvgl.io/master/get-started/bindings/micropython.html) 查询.
|
||||
|
||||
<details>
|
||||
<summary>MicroPython code | <a href="https://sim.lvgl.io/v8.3/micropython/ports/javascript/index.html?script_startup=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/header.py&script=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/widgets/slider/lv_example_slider_2.py&script_direct=926bde43ec7af0146c486de470c53f11f167491e" target="_blank">Online Simulator</a> :gb:</summary>
|
||||
|
||||
```python
|
||||
def btn_event_cb(e):
|
||||
print("Clicked")
|
||||
|
||||
# Create a Button and a Label
|
||||
btn = lv.btn(lv.screen_active())
|
||||
btn.center()
|
||||
btn.set_size(100, 50)
|
||||
btn.add_event(btn_event_cb, lv.EVENT.CLICKED, None)
|
||||
|
||||
label = lv.label(btn)
|
||||
label.set_text("Button")
|
||||
label.center()
|
||||
```
|
||||
|
||||
</details>
|
||||
<br>
|
||||
|
||||
|
||||
### Checkboxes with Layout 带布局的复选框
|
||||

|
||||
|
||||
<details>
|
||||
<summary>C code</summary>
|
||||
|
||||
```c
|
||||
|
||||
lv_obj_set_flex_flow(lv_screen_active(), LV_FLEX_FLOW_COLUMN);
|
||||
lv_obj_set_flex_align(lv_screen_active(), LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER);
|
||||
|
||||
lv_obj_t * cb;
|
||||
cb = lv_checkbox_create(lv_screen_active());
|
||||
lv_checkbox_set_text(cb, "Apple");
|
||||
lv_obj_add_event(cb, event_handler, LV_EVENT_ALL, NULL);
|
||||
|
||||
cb = lv_checkbox_create(lv_screen_active());
|
||||
lv_checkbox_set_text(cb, "Banana");
|
||||
lv_obj_add_state(cb, LV_STATE_CHECKED);
|
||||
lv_obj_add_event(cb, event_handler, LV_EVENT_ALL, NULL);
|
||||
|
||||
cb = lv_checkbox_create(lv_screen_active());
|
||||
lv_checkbox_set_text(cb, "Lemon");
|
||||
lv_obj_add_state(cb, LV_STATE_DISABLED);
|
||||
lv_obj_add_event(cb, event_handler, LV_EVENT_ALL, NULL);
|
||||
|
||||
cb = lv_checkbox_create(lv_screen_active());
|
||||
lv_obj_add_state(cb, LV_STATE_CHECKED | LV_STATE_DISABLED);
|
||||
lv_checkbox_set_text(cb, "Melon\nand a new line");
|
||||
lv_obj_add_event(cb, event_handler, LV_EVENT_ALL, NULL);
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>MicroPython code | <a href="https://sim.lvgl.io/v8.3/micropython/ports/javascript/index.html?script_startup=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/header.py&script=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/widgets/slider/lv_example_slider_2.py&script_direct=311d37e5f70daf1cb0d2cad24c7f72751b5f1792" target="_blank">Online Simulator</a> :gb:</summary>
|
||||
|
||||
```python
|
||||
def event_handler(e):
|
||||
code = e.get_code()
|
||||
obj = e.get_target_obj()
|
||||
if code == lv.EVENT.VALUE_CHANGED:
|
||||
txt = obj.get_text()
|
||||
if obj.get_state() & lv.STATE.CHECKED:
|
||||
state = "Checked"
|
||||
else:
|
||||
state = "Unchecked"
|
||||
print(txt + ":" + state)
|
||||
|
||||
|
||||
lv.scr_act().set_flex_flow(lv.FLEX_FLOW.COLUMN)
|
||||
lv.scr_act().set_flex_align(lv.FLEX_ALIGN.CENTER, lv.FLEX_ALIGN.START, lv.FLEX_ALIGN.CENTER)
|
||||
|
||||
cb = lv.checkbox(lv.screen_active())
|
||||
cb.set_text("Apple")
|
||||
cb.add_event(event_handler, lv.EVENT.ALL, None)
|
||||
|
||||
cb = lv.checkbox(lv.screen_active())
|
||||
cb.set_text("Banana")
|
||||
cb.add_state(lv.STATE.CHECKED)
|
||||
cb.add_event(event_handler, lv.EVENT.ALL, None)
|
||||
|
||||
cb = lv.checkbox(lv.screen_active())
|
||||
cb.set_text("Lemon")
|
||||
cb.add_state(lv.STATE.DISABLED)
|
||||
cb.add_event(event_handler, lv.EVENT.ALL, None)
|
||||
|
||||
cb = lv.checkbox(lv.screen_active())
|
||||
cb.add_state(lv.STATE.CHECKED | lv.STATE.DISABLED)
|
||||
cb.set_text("Melon")
|
||||
cb.add_event(event_handler, lv.EVENT.ALL, None)
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
### Styling a Slider 设置滑块的样式
|
||||

|
||||
|
||||
|
||||
<details>
|
||||
<summary>C code</summary>
|
||||
|
||||
```c
|
||||
lv_obj_t * slider = lv_slider_create(lv_screen_active());
|
||||
lv_slider_set_value(slider, 70, LV_ANIM_OFF);
|
||||
lv_obj_set_size(slider, 300, 20);
|
||||
lv_obj_center(slider);
|
||||
|
||||
/*Add local styles to MAIN part (background rectangle)*/
|
||||
lv_obj_set_style_bg_color(slider, lv_color_hex(0x0F1215), LV_PART_MAIN);
|
||||
lv_obj_set_style_bg_opa(slider, 255, LV_PART_MAIN);
|
||||
lv_obj_set_style_border_color(slider, lv_color_hex(0x333943), LV_PART_MAIN);
|
||||
lv_obj_set_style_border_width(slider, 5, LV_PART_MAIN);
|
||||
lv_obj_set_style_pad_all(slider, 5, LV_PART_MAIN);
|
||||
|
||||
/*Create a reusable style sheet for the INDICATOR part*/
|
||||
static lv_style_t style_indicator;
|
||||
lv_style_init(&style_indicator);
|
||||
lv_style_set_bg_color(&style_indicator, lv_color_hex(0x37B9F5));
|
||||
lv_style_set_bg_grad_color(&style_indicator, lv_color_hex(0x1464F0));
|
||||
lv_style_set_bg_grad_dir(&style_indicator, LV_GRAD_DIR_HOR);
|
||||
lv_style_set_shadow_color(&style_indicator, lv_color_hex(0x37B9F5));
|
||||
lv_style_set_shadow_width(&style_indicator, 15);
|
||||
lv_style_set_shadow_spread(&style_indicator, 5);
|
||||
|
||||
/*Add the style sheet to the slider's INDICATOR part*/
|
||||
lv_obj_add_style(slider, &style_indicator, LV_PART_INDICATOR);
|
||||
|
||||
/*Add the same style to the KNOB part too and locally overwrite some properties*/
|
||||
lv_obj_add_style(slider, &style_indicator, LV_PART_KNOB);
|
||||
|
||||
lv_obj_set_style_outline_color(slider, lv_color_hex(0x0096FF), LV_PART_KNOB);
|
||||
lv_obj_set_style_outline_width(slider, 3, LV_PART_KNOB);
|
||||
lv_obj_set_style_outline_pad(slider, -5, LV_PART_KNOB);
|
||||
lv_obj_set_style_shadow_spread(slider, 2, LV_PART_KNOB);
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>MicroPython code |
|
||||
<a href="https://sim.lvgl.io/v8.3/micropython/ports/javascript/index.html?script_startup=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/header.py&script=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/widgets/slider/lv_example_slider_2.py&script_direct=c431c7b4dfd2cc0dd9c392b74365d5af6ea986f0" target="_blank">Online Simulator</a> :gb:
|
||||
</summary>
|
||||
|
||||
|
||||
```python
|
||||
# Create a slider and add the style
|
||||
slider = lv.slider(lv.screen_active())
|
||||
slider.set_value(70, lv.ANIM.OFF)
|
||||
slider.set_size(300, 20)
|
||||
slider.center()
|
||||
|
||||
# Add local styles to MAIN part (background rectangle)
|
||||
slider.set_style_bg_color(lv.color_hex(0x0F1215), lv.PART.MAIN)
|
||||
slider.set_style_bg_opa(255, lv.PART.MAIN)
|
||||
slider.set_style_border_color(lv.color_hex(0x333943), lv.PART.MAIN)
|
||||
slider.set_style_border_width(5, lv.PART.MAIN)
|
||||
slider.set_style_pad_all(5, lv.PART.MAIN)
|
||||
|
||||
# Create a reusable style sheet for the INDICATOR part
|
||||
style_indicator = lv.style_t()
|
||||
style_indicator.init()
|
||||
style_indicator.set_bg_color(lv.color_hex(0x37B9F5))
|
||||
style_indicator.set_bg_grad_color(lv.color_hex(0x1464F0))
|
||||
style_indicator.set_bg_grad_dir(lv.GRAD_DIR.HOR)
|
||||
style_indicator.set_shadow_color(lv.color_hex(0x37B9F5))
|
||||
style_indicator.set_shadow_width(15)
|
||||
style_indicator.set_shadow_spread(5)
|
||||
|
||||
# Add the style sheet to the slider's INDICATOR part
|
||||
slider.add_style(style_indicator, lv.PART.INDICATOR)
|
||||
slider.add_style(style_indicator, lv.PART.KNOB)
|
||||
|
||||
# Add the same style to the KNOB part too and locally overwrite some properties
|
||||
slider.set_style_outline_color(lv.color_hex(0x0096FF), lv.PART.KNOB)
|
||||
slider.set_style_outline_width(3, lv.PART.KNOB)
|
||||
slider.set_style_outline_pad(-5, lv.PART.KNOB)
|
||||
slider.set_style_shadow_spread(2, lv.PART.KNOB)
|
||||
```
|
||||
</details>
|
||||
<br>
|
||||
|
||||
|
||||
### English, Hebrew (mixed LRT-RTL) and Chinese texts 英语、希伯来语( 双向文本排版 )和中文
|
||||
|
||||

|
||||
|
||||
<details>
|
||||
<summary>C code</summary>
|
||||
|
||||
```c
|
||||
lv_obj_t * ltr_label = lv_label_create(lv_screen_active());
|
||||
lv_label_set_text(ltr_label, "In modern terminology, a microcontroller is similar to a system on a chip (SoC).");
|
||||
lv_obj_set_style_text_font(ltr_label, &lv_font_montserrat_16, 0);
|
||||
lv_obj_set_width(ltr_label, 310);
|
||||
lv_obj_align(ltr_label, LV_ALIGN_TOP_LEFT, 5, 5);
|
||||
|
||||
lv_obj_t * rtl_label = lv_label_create(lv_screen_active());
|
||||
lv_label_set_text(rtl_label,"מעבד, או בשמו המלא יחידת עיבוד מרכזית (באנגלית: CPU - Central Processing Unit).");
|
||||
lv_obj_set_style_base_dir(rtl_label, LV_BASE_DIR_RTL, 0);
|
||||
lv_obj_set_style_text_font(rtl_label, &lv_font_dejavu_16_persian_hebrew, 0);
|
||||
lv_obj_set_width(rtl_label, 310);
|
||||
lv_obj_align(rtl_label, LV_ALIGN_LEFT_MID, 5, 0);
|
||||
|
||||
lv_obj_t * cz_label = lv_label_create(lv_screen_active());
|
||||
lv_label_set_text(cz_label,
|
||||
"嵌入式系统(Embedded System),\n是一种嵌入机械或电气系统内部、具有专一功能和实时计算性能的计算机系统。");
|
||||
lv_obj_set_style_text_font(cz_label, &lv_font_source_han_sans_sc_16_cjk, 0);
|
||||
lv_obj_set_width(cz_label, 310);
|
||||
lv_obj_align(cz_label, LV_ALIGN_BOTTOM_LEFT, 5, -5);
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>MicroPython code | <a href="https://sim.lvgl.io/v8.3/micropython/ports/javascript/index.html?script_startup=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/header.py&script=https://raw.githubusercontent.com/lvgl/lvgl/0d9ab4ee0e591aad1970e3c9164fd7c544ecce70/examples/widgets/slider/lv_example_slider_2.py&script_direct=18bb38200a64e10ead1aa17a65c977fc18131842" target="_blank">Online Simulator</a> :gb:</summary>
|
||||
|
||||
```python
|
||||
ltr_label = lv.label(lv.screen_active())
|
||||
ltr_label.set_text("In modern terminology, a microcontroller is similar to a system on a chip (SoC).")
|
||||
ltr_label.set_style_text_font(lv.font_montserrat_16, 0);
|
||||
|
||||
ltr_label.set_width(310)
|
||||
ltr_label.align(lv.ALIGN.TOP_LEFT, 5, 5)
|
||||
|
||||
rtl_label = lv.label(lv.screen_active())
|
||||
rtl_label.set_text("מעבד, או בשמו המלא יחידת עיבוד מרכזית (באנגלית: CPU - Central Processing Unit).")
|
||||
rtl_label.set_style_base_dir(lv.BASE_DIR.RTL, 0)
|
||||
rtl_label.set_style_text_font(lv.font_dejavu_16_persian_hebrew, 0)
|
||||
rtl_label.set_width(310)
|
||||
rtl_label.align(lv.ALIGN.LEFT_MID, 5, 0)
|
||||
|
||||
font_han_sans_16_cjk = lv.font_load("S:../../assets/font/lv_font_source_han_sans_sc_16_cjk.fnt")
|
||||
|
||||
cz_label = lv.label(lv.screen_active())
|
||||
cz_label.set_style_text_font(font_han_sans_16_cjk, 0)
|
||||
cz_label.set_text("嵌入式系统(Embedded System),\n是一种嵌入机械或电气系统内部、具有专一功能和实时计算性能的计算机系统。")
|
||||
cz_label.set_width(310)
|
||||
cz_label.align(lv.ALIGN.BOTTOM_LEFT, 5, -5)
|
||||
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
|
||||
## :handshake: 服务
|
||||
|
||||
LVGL
|
||||
责任有限公司成立的目的是为了给用户使用LVGL图形库提供额外的技术支持,我们致力于提供以下服务:
|
||||
|
||||
- 图形设计
|
||||
- UI设计
|
||||
- 技术咨询以及技术支持
|
||||
|
||||
更多信息请参见 https://lvgl.io/services ,如果有任何问题请随时联系我们。
|
||||
|
||||
|
||||
## :star: 如何向社区贡献
|
||||
|
||||
LVGL是一个开源项目,非常欢迎您参与到社区贡献当中。您有很多种方式来为提高LVGL贡献您的一份力量,包括但不限于:
|
||||
|
||||
- 介绍你基于LVGL设计的作品或项目
|
||||
- 写一些例程
|
||||
- 修改以及完善文档
|
||||
- 修复bug
|
||||
|
||||
请参见文档[如何向社区贡献](https://docs.lvgl.io/master/contributing/index.html)章节来获取更多信息。
|
||||
BIN
managed_components/lvgl__lvgl/docs/_obsolete/btn_example.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
BIN
managed_components/lvgl__lvgl/docs/_obsolete/codeblocks.jpg
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
managed_components/lvgl__lvgl/docs/_obsolete/eclipse.jpg
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
managed_components/lvgl__lvgl/docs/_obsolete/home_banner.jpg
Normal file
|
After Width: | Height: | Size: 186 KiB |
BIN
managed_components/lvgl__lvgl/docs/_obsolete/js_calculator.gif
Normal file
|
After Width: | Height: | Size: 519 KiB |
BIN
managed_components/lvgl__lvgl/docs/_obsolete/js_code.png
Normal file
|
After Width: | Height: | Size: 312 KiB |
BIN
managed_components/lvgl__lvgl/docs/_obsolete/js_on_device.jpg
Normal file
|
After Width: | Height: | Size: 189 KiB |
BIN
managed_components/lvgl__lvgl/docs/_obsolete/js_widgets_demo.gif
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
managed_components/lvgl__lvgl/docs/_obsolete/platformio.jpg
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
managed_components/lvgl__lvgl/docs/_obsolete/qtcreator.jpg
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
BIN
managed_components/lvgl__lvgl/docs/_obsolete/sys.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
managed_components/lvgl__lvgl/docs/_obsolete/visualstudio.jpg
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
50
managed_components/lvgl__lvgl/docs/add_translation.py
Normal file
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
|
||||
"""
|
||||
Please add the translation language you want to add here, while also modifying the variable URL_BASE in _ext/link_roles.py
|
||||
For example:
|
||||
|
||||
LANGUAGE = ':link_to_translation:`zh_CN:[中文]`\t' + \
|
||||
':link_to_translation:`en:[English]`\t' + \
|
||||
'\n\n'
|
||||
|
||||
|
||||
URL_BASE = {
|
||||
"zh_CN": "https://lvgl.100ask.net/",
|
||||
"en": "https://docs.lvgl.io/"
|
||||
}
|
||||
"""
|
||||
|
||||
LANGUAGE = ':link_to_translation:`zh_CN:[中文]`\t' + \
|
||||
'\n\n'
|
||||
|
||||
|
||||
|
||||
def find_files(dir_path, suffix):
|
||||
files = []
|
||||
|
||||
for root, _, filenames in os.walk(dir_path):
|
||||
for filename in filenames:
|
||||
if filename.endswith(suffix):
|
||||
files.append(os.path.join(root, filename))
|
||||
return files
|
||||
|
||||
|
||||
|
||||
def exec(temp_directory):
|
||||
"""
|
||||
files = find_files(temp_directory, '.rst')
|
||||
|
||||
for rst_file in files:
|
||||
with open(rst_file, 'r+', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
f.seek(0, 0)
|
||||
f.write(LANGUAGE + content)
|
||||
"""
|
||||
|
||||
rst_file = os.path.join(temp_directory, 'index.rst')
|
||||
with open(rst_file, 'r+', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
f.seek(0, 0)
|
||||
f.write(LANGUAGE + content)
|
||||
118
managed_components/lvgl__lvgl/docs/announce.py
Normal file
@@ -0,0 +1,118 @@
|
||||
"""announce.py
|
||||
Manage logging announcements to `stdout`
|
||||
|
||||
It is the designer's intention that:
|
||||
|
||||
1. The variable `__file__` be passed as the first argument in
|
||||
`announce()` and `announce_start()`.
|
||||
(Unfortunately, there is no way this author knows of yet to
|
||||
have this module know what Python module is importing it. So
|
||||
this is a hold-over requirement until that need is fulfilled.)
|
||||
|
||||
2. `announce_start()` and `announce_finish()` should be used
|
||||
in pairs like this:
|
||||
|
||||
announce_start(__file__, 'something is running...')
|
||||
# do something that takes a while here
|
||||
announce_finish()
|
||||
|
||||
3. If this is used in a module that sometimes has a need to
|
||||
not have anything output to STDOUT, when that is known,
|
||||
call `announce_set_silent_mode()`. To turn "silent mode"
|
||||
off, call `announce_set_silent_mode(False)`.
|
||||
|
||||
"""
|
||||
import os
|
||||
import datetime
|
||||
|
||||
__all__ = ('announce', 'announce_colored', 'announce_start', 'announce_finish', 'announce_set_silent_mode')
|
||||
_announce_start_time: datetime.datetime
|
||||
_announce_silent_mode: bool = False
|
||||
_console_color_commands = {
|
||||
'default' : '\x1b[0m',
|
||||
'black' : '\x1b[30m',
|
||||
'red' : '\x1b[31m',
|
||||
'green' : '\x1b[32m',
|
||||
'yellow' : '\x1b[33m',
|
||||
'blue' : '\x1b[34m',
|
||||
'majenta' : '\x1b[35m',
|
||||
'cyan' : '\x1b[36m',
|
||||
'white' : '\x1b[37m',
|
||||
'bright_black' : '\x1b[90m',
|
||||
'bright_red' : '\x1b[91m',
|
||||
'bright_green' : '\x1b[92m',
|
||||
'bright_yellow' : '\x1b[93m',
|
||||
'bright_blue' : '\x1b[94m',
|
||||
'bright_majenta': '\x1b[95m',
|
||||
'bright_cyan' : '\x1b[96m',
|
||||
'bright_white' : '\x1b[97m'
|
||||
}
|
||||
|
||||
|
||||
def _announce(file: str, args: tuple, start: bool, box: bool, box_char: str):
|
||||
if _announce_silent_mode:
|
||||
return
|
||||
|
||||
_args = []
|
||||
|
||||
for arg in args:
|
||||
# Avoid the single quotes `repr()` puts around strings.
|
||||
if type(arg) is str:
|
||||
_args.append(arg)
|
||||
else:
|
||||
_args.append(repr(arg))
|
||||
|
||||
msg = f'{os.path.basename(file)}: ' + ' '.join(_args)
|
||||
msg_len = len(msg)
|
||||
|
||||
# `start` takes precedence over `box` argument.
|
||||
if start:
|
||||
print(msg, end='', flush=True)
|
||||
else:
|
||||
if box:
|
||||
line = box_char * msg_len
|
||||
print(line)
|
||||
print(msg)
|
||||
print(line, flush=True)
|
||||
else:
|
||||
print(msg, flush=True)
|
||||
|
||||
|
||||
def announce(file: str, *args, box: bool = False, box_char: str = '*'):
|
||||
global _announce_start_time
|
||||
_announce_start_time = None
|
||||
_announce(file, args, False, box, box_char)
|
||||
|
||||
|
||||
def announce_colored(file: str, clr: str, *args, box: bool = False, box_char: str = '*'):
|
||||
global _announce_start_time
|
||||
_announce_start_time = None
|
||||
if len(args) > 0 and clr in _console_color_commands:
|
||||
# Tuples are non-mutable so we have to build a new one -- can't insert new elements.
|
||||
new_args_tuple = (_console_color_commands[clr],) + args + (_console_color_commands['default'],)
|
||||
_announce(file, new_args_tuple, False, box, box_char)
|
||||
else:
|
||||
_announce(file, args, False, box, box_char)
|
||||
|
||||
|
||||
def announce_start(file: str, *args, box: bool = False, box_char: str = '*'):
|
||||
global _announce_start_time
|
||||
_announce_start_time = datetime.datetime.now()
|
||||
_announce(file, args, True, box, box_char)
|
||||
|
||||
|
||||
def announce_finish():
|
||||
# Just output line ending to terminate output for `announce_start()`.
|
||||
global _announce_start_time
|
||||
if _announce_start_time is not None:
|
||||
if not _announce_silent_mode:
|
||||
print(' Elapsed: ', datetime.datetime.now() - _announce_start_time, flush=True)
|
||||
_announce_start_time = None
|
||||
else:
|
||||
if not _announce_silent_mode:
|
||||
print(flush=True)
|
||||
|
||||
|
||||
def announce_set_silent_mode(mode=True):
|
||||
global _announce_silent_mode
|
||||
_announce_silent_mode = mode
|
||||
770
managed_components/lvgl__lvgl/docs/api_doc_builder.py
Normal file
@@ -0,0 +1,770 @@
|
||||
"""api_doc_builder.py
|
||||
|
||||
Create and provide links to API pages in LVGL doc build.
|
||||
|
||||
Uses doxygen_xml.py module to:
|
||||
|
||||
- Prep and run Doxygen
|
||||
- make Doxygen XML output available, and
|
||||
- make Doxygen-documented symbols from the C code available.
|
||||
"""
|
||||
import os
|
||||
import re
|
||||
import doxygen_xml
|
||||
from announce import *
|
||||
|
||||
old_html_files = {}
|
||||
EMIT_WARNINGS = True
|
||||
rst_section_line_char = '='
|
||||
|
||||
# Multi-line match ``API + newline + \*\*\* + whitespace``.
|
||||
# NB: the ``\s*`` at the end forces the regex to match the whitespace
|
||||
# at the end including all \r\n's. This will match UP TO:
|
||||
# - the next non-blank character (could be many blank lines), or
|
||||
# - to the end of the file, whichever comes first.
|
||||
_re_api_section_sep = re.compile(r'(?mi)^API *\r?\n^\*\*\*\s*')
|
||||
|
||||
# Regex to identify '.. API equals: lv_obj_t, lv_array_t' directives.
|
||||
_re_api_equals = re.compile(r'(?mi)^\s*\.\.\s+API\s+equals:\s*([\w,\s]+)\r\n\s*')
|
||||
|
||||
# Regex to identify '.. API startswith: lv_obj, lv_array' directives.
|
||||
_re_api_startswith = re.compile(r'(?mi)^\s*\.\.\s+API\s+startswith:\s*([\w,\s]+)\r\n\s*')
|
||||
|
||||
# Regex to match comma and whitespace list-item separators on multiple lines.
|
||||
_re_multi_line_comma_sep = re.compile(r'(?m)[,\s]+')
|
||||
|
||||
# Regex to identify editor-added hyperlinks: :ref:`lv_obj_h`
|
||||
_re_editor_added_hyperlink = re.compile(r'^\s*:ref:`(\w+)`')
|
||||
|
||||
# Separator to mark place where this script added hyperlinks.
|
||||
_auto_gen_sep = '.. Autogenerated'
|
||||
|
||||
# List of symbol dictionaries
|
||||
_defines = {}
|
||||
_enums = {}
|
||||
_variables = {}
|
||||
_namespaces = {}
|
||||
_structs = {}
|
||||
_unions = {}
|
||||
_typedefs = {}
|
||||
_functions = {}
|
||||
|
||||
_symbol_dict_list = [
|
||||
_defines,
|
||||
_enums,
|
||||
_variables,
|
||||
_namespaces,
|
||||
_structs,
|
||||
_unions,
|
||||
_typedefs,
|
||||
_functions
|
||||
]
|
||||
|
||||
|
||||
def old_clean_name(nme):
|
||||
"""Strip beginning "_lv" and ending "_t"."""
|
||||
# Handle error:
|
||||
# AttributeError: 'NoneType' object has no attribute 'startswith'
|
||||
if nme is None:
|
||||
return nme
|
||||
|
||||
if nme.startswith('_lv_'):
|
||||
nme = nme[4:]
|
||||
elif nme.startswith('lv_'):
|
||||
nme = nme[3:]
|
||||
|
||||
if nme.endswith('_t'):
|
||||
nme = nme[:-2]
|
||||
|
||||
return nme
|
||||
|
||||
|
||||
# Definitions:
|
||||
# - "section" => The name "abc_def" has 2 sections.
|
||||
# - N = number of sections in `item_name`.
|
||||
# After removing leading '_lv_', 'lv_' and trailing '_t' from `obj_name`,
|
||||
# do the remaining first N "sections" of `obj_name` match `item_name`
|
||||
# (case sensitive)?
|
||||
def old_is_name_match(item_name, obj_name):
|
||||
# Handle error:
|
||||
# AttributeError: 'NoneType' object has no attribute 'split'
|
||||
if obj_name is None:
|
||||
return False
|
||||
|
||||
sect_count = item_name.count('_') + 1
|
||||
|
||||
obj_name = obj_name.split('_')
|
||||
|
||||
# Reject (False) if `obj_name` doesn't have as many sections as `item_name`.
|
||||
if len(obj_name) < sect_count:
|
||||
return False
|
||||
|
||||
obj_name = '_'.join(obj_name[:sect_count])
|
||||
|
||||
return item_name == obj_name
|
||||
|
||||
|
||||
def old_get_includes(name1, name2, obj, includes):
|
||||
name2 = old_clean_name(name2)
|
||||
|
||||
if not old_is_name_match(name1, name2):
|
||||
return
|
||||
|
||||
if obj.parent is not None and hasattr(obj.parent, 'header_file'):
|
||||
header_file = obj.parent.header_file
|
||||
elif hasattr(obj, 'header_file'):
|
||||
header_file = obj.header_file
|
||||
else:
|
||||
return
|
||||
|
||||
if not header_file:
|
||||
return
|
||||
|
||||
if header_file not in old_html_files:
|
||||
return
|
||||
|
||||
includes.add((header_file, old_html_files[header_file]))
|
||||
|
||||
|
||||
def _conditionally_add_hyperlink(obj, genned_link_set: set, exclude_set: set):
|
||||
"""
|
||||
Add hyperlink names to `link_set` if:
|
||||
- not in `exclude_set`, and
|
||||
- not already in `link_set`.
|
||||
:param obj: "thing" from dictionary with matching symbol.
|
||||
These are objects instantiated from classes
|
||||
in `doxygen_xml` module such as
|
||||
STRUCT, FUNCTION, DEFINE, ENUMVALUE, etc.
|
||||
:param genned_link_set: Set in which to accumulate link names
|
||||
:param exclude_set: Set with link names not to add to `link_set`
|
||||
:return:
|
||||
"""
|
||||
if obj.file_name is not None:
|
||||
link_name = os.path.basename(obj.file_name).replace('.', '_')
|
||||
if link_name not in genned_link_set:
|
||||
if link_name not in exclude_set:
|
||||
genned_link_set.add(link_name)
|
||||
|
||||
|
||||
def _add_startswith_matches(strings: [str], genned_link_set, editor_link_set):
|
||||
"""
|
||||
Add set of hyperlinks to `genned_link_set` that are not already in
|
||||
`editor_link_set`, for C symbols that start with strings in `strings`.
|
||||
|
||||
:param strings: List of strings to match against
|
||||
:param genned_link_set: Generated link set
|
||||
:param editor_link_set: Hyperlinks added by editor
|
||||
:return: n/a
|
||||
"""
|
||||
for partial_symbol in strings:
|
||||
for symbol_dict in _symbol_dict_list:
|
||||
for key in symbol_dict:
|
||||
if key is None:
|
||||
# Dictionary `enums` has a key `None` which contains
|
||||
# all enum values from all unnamed enums, and each
|
||||
# enum value has a `file_name` field.
|
||||
enum_values_list = symbol_dict[None].members
|
||||
for enum_val in enum_values_list:
|
||||
if enum_val.name.startswith(partial_symbol):
|
||||
_conditionally_add_hyperlink(enum_val, genned_link_set, editor_link_set)
|
||||
else:
|
||||
if key.startswith(partial_symbol):
|
||||
obj = symbol_dict[key]
|
||||
_conditionally_add_hyperlink(obj, genned_link_set, editor_link_set)
|
||||
|
||||
|
||||
def _add_exact_matches(symbols: [str], genned_link_set, editor_link_set):
|
||||
"""
|
||||
Add set of hyperlinks to `genned_link_set` that are not already in
|
||||
`editor_link_set`, for exact C symbol matches.
|
||||
|
||||
:param symbols: List of C symbols to match against
|
||||
:param genned_link_set: Generated link set
|
||||
:param editor_link_set: Hyperlinks added by editor
|
||||
:return: n/a
|
||||
"""
|
||||
for symbol in symbols:
|
||||
for symbol_dict in _symbol_dict_list:
|
||||
if symbol in symbol_dict:
|
||||
obj = symbol_dict[symbol]
|
||||
_conditionally_add_hyperlink(obj, genned_link_set, editor_link_set)
|
||||
|
||||
|
||||
def _hyperlink_sort_value(init_value: str):
|
||||
if init_value.endswith('_h'):
|
||||
result = init_value[:-2]
|
||||
else:
|
||||
result = init_value
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def _process_end_of_eligible_doc(b: str, rst_file: str) -> (str, str, int):
|
||||
"""
|
||||
Edit end section after API section heading.
|
||||
|
||||
3. Initialize:
|
||||
- links_added_count = 0
|
||||
- editor_link_set = set()
|
||||
- genned_link_set = set()
|
||||
- C = '' # string for generated hyperlinks
|
||||
4. Remove `_auto_gen_sep` and everything after it:
|
||||
- new_B = B.split(_auto_gen_sep, 1)[0]
|
||||
5. With `new_B, add any editor-added hyperlinks to set:
|
||||
`editor_link_set`.
|
||||
6. If `_re_api_equals` match present:
|
||||
- build list of symbols
|
||||
- compute list of hyperlinks from symbols
|
||||
- add hyperlinks to `genned_link_set` if not in `editor_link_set`.
|
||||
7. If `_re_api_startswith` match present:
|
||||
- build list of symbols
|
||||
- compute list of hyperlinks from symbols
|
||||
- add hyperlinks to `genned_link_set` if not in `editor_link_set`.
|
||||
8. Lacking either of the above custom directives, use the lower-case
|
||||
stem of the filename and prefix it with "lv_" and try an
|
||||
"API startswith" search with it.
|
||||
9. If len(genned_link_set) > 0:
|
||||
- `C` = _auto_gen_sep + '\n\n' + `genned_link_set`
|
||||
(with a blank line between each).
|
||||
10. Return tuple: (new_B, C, links_added_count).
|
||||
|
||||
:param b: End of document after API section heading + whitespace.
|
||||
:return: Tuple: (new_B, C, links_added_count)
|
||||
"""
|
||||
# 3. Initialize:
|
||||
editor_link_set = set()
|
||||
genned_link_set = set()
|
||||
c = ''
|
||||
api_directives_found_count = 0
|
||||
|
||||
# 4. Remove `_auto_gen_sep` and everything after it:
|
||||
new_b = b.split(_auto_gen_sep, 1)[0]
|
||||
|
||||
# 5. With `new_B, add any editor-added hyperlinks to set:
|
||||
# `editor_link_set`.
|
||||
for line in new_b.splitlines():
|
||||
match = _re_editor_added_hyperlink.match(line)
|
||||
if match is not None:
|
||||
editor_link_set.add(match[1])
|
||||
|
||||
# 6. If `_re_api_equals` present:
|
||||
# - build list of symbols
|
||||
# - compute list of hyperlinks from symbols
|
||||
# - add hyperlinks to `genned_link_set` if not in `editor_link_set`.
|
||||
match = _re_api_equals.search(new_b)
|
||||
if match is not None:
|
||||
api_directives_found_count += 1
|
||||
comma_sep_list = match[1].strip()
|
||||
symbols = _re_multi_line_comma_sep.split(comma_sep_list)
|
||||
_add_exact_matches(symbols, genned_link_set, editor_link_set)
|
||||
|
||||
# 7. If `_re_api_startswith` present:
|
||||
# - build list of symbols
|
||||
# - compute list of hyperlinks from symbols
|
||||
# - add hyperlinks to `genned_link_set` if not in `editor_link_set`.
|
||||
match = _re_api_startswith.search(new_b)
|
||||
if match is not None:
|
||||
api_directives_found_count += 1
|
||||
comma_sep_list = match[1].strip()
|
||||
symbols = _re_multi_line_comma_sep.split(comma_sep_list)
|
||||
_add_startswith_matches(symbols, genned_link_set, editor_link_set)
|
||||
|
||||
# 8. Lacking either of the above custom directives, use the lower-case
|
||||
# stem of the filename and prefix it with "lv_" and try an
|
||||
# "API startswith" with it.
|
||||
if api_directives_found_count == 0:
|
||||
base = os.path.basename(rst_file)
|
||||
stem = os.path.splitext(base)[0]
|
||||
_add_startswith_matches(['lv_' + stem], genned_link_set, editor_link_set)
|
||||
|
||||
# 9. If len(genned_link_set) > 0:
|
||||
# - `C` = _auto_gen_sep + '\n\n' + `genned_link_set`
|
||||
# (with a blank line between each).
|
||||
links_added_count = len(genned_link_set)
|
||||
|
||||
if links_added_count > 0:
|
||||
c = _auto_gen_sep + '\n\n'
|
||||
for link_name in sorted(genned_link_set, key=_hyperlink_sort_value):
|
||||
c += ':ref:`' + link_name + '`\n\n'
|
||||
|
||||
# 10. Return tuple: (new_B, C, links_added_count).
|
||||
return new_b, c, links_added_count
|
||||
|
||||
|
||||
def _process_one_file(rst_file: str):
|
||||
"""
|
||||
Add applicable API hyperlinks to one file.
|
||||
|
||||
Eligible
|
||||
An `.rst` file is eligible if it contains an API section at
|
||||
its end. This can happen also in `index.rst` files when
|
||||
they head a single subject for which an API section is
|
||||
appropriate there and not in the sub-docs. So `index.rst`
|
||||
files are included, whereas they were not included previously.
|
||||
|
||||
Algorithm:
|
||||
----------
|
||||
A. Doc editors may have already added a set of hyperlinks of their
|
||||
own. This routine takes note of and does not duplicate what is
|
||||
already there.
|
||||
|
||||
B. Doc editors may also have added specifications for this routine
|
||||
that look like this:
|
||||
|
||||
.. API equals: lv_obj_t, lv_arc_t, lv_barcode_t,
|
||||
lv_win_t, lv_list_t,
|
||||
lv_button_t
|
||||
|
||||
.. API startswith: lv_obj, lv_arc, lv_barcode,
|
||||
lv_win, lv_list,
|
||||
lv_button
|
||||
|
||||
as directives for this routine to build a set of applicable
|
||||
hyperlinks.
|
||||
|
||||
C. Lacking any of the above custom directives, use the lower-case
|
||||
stem of the filename and prefix it with "lv_" and try an
|
||||
"API startswith" search with it.
|
||||
|
||||
Any hyperlinks added by this routine are prefixed with the
|
||||
reStructuredText comment defined by the `_auto_gen_sep`
|
||||
variable, normally:
|
||||
|
||||
.. Autogenerated
|
||||
|
||||
If `rst_file` is eligible, edit after API section heading such that:
|
||||
- any editor-added hyperlinks are retained at the top of the list;
|
||||
- `_auto_gen_sep` (looked for in case a source file ends up having it;
|
||||
anything after it is replaced);
|
||||
- applicable hyperlinks added such that they do not repeat those
|
||||
added by editors of `.rst` file.
|
||||
|
||||
Steps to Implement:
|
||||
-------------------
|
||||
0. links_added_count = 0
|
||||
1. Determine if eligible.
|
||||
- If not, skip to step 12.
|
||||
- If so, continue.
|
||||
2. Split doc into 2 parts:
|
||||
A. beginning through API section heading and subsequent
|
||||
whitespace including subsequent blank lines;
|
||||
B. anything after that which may include editor-added hyperlinks.
|
||||
3-10. new_B, C, links_added_count = _process_end_of_eligible_doc()
|
||||
11. Write `A` + `new_B` + `C` back to `rst_file`.
|
||||
12. Return links_added_count.
|
||||
|
||||
:param rst_file: Full path to `.rst` file in question.
|
||||
It may or may not be eligible.
|
||||
:return: Number of links added.
|
||||
"""
|
||||
links_added_count = 0
|
||||
|
||||
with open(rst_file, 'rb') as f:
|
||||
try:
|
||||
rst_contents = f.read().decode('utf-8')
|
||||
except UnicodeDecodeError:
|
||||
announce(__file__, f'Error: UnicodeDecodeError in [{rst_file}].')
|
||||
raise
|
||||
|
||||
eligible_match = _re_api_section_sep.search(rst_contents)
|
||||
|
||||
if eligible_match is not None:
|
||||
# Eligible (API section found).
|
||||
i = eligible_match.end()
|
||||
# Split just after the API section heading + whitespace.
|
||||
a = rst_contents[:i]
|
||||
b = rst_contents[i:]
|
||||
new_b, c, links_added_count = _process_end_of_eligible_doc(b, rst_file)
|
||||
|
||||
if links_added_count > 0:
|
||||
rst_contents = a + new_b + c
|
||||
|
||||
with open(rst_file, 'wb') as f:
|
||||
f.write(rst_contents.encode('utf-8'))
|
||||
|
||||
return links_added_count
|
||||
|
||||
|
||||
def _build_one_local_dictionary(local_dict, remote_dict):
|
||||
"""
|
||||
Remove '_' prefix in symbols beginning with '_lv' to make
|
||||
symbols like `lv_obj_t` actually connect with the struct
|
||||
in `lv_obj_private.h`, and not the typedef in `lv_types.h`.
|
||||
|
||||
:param local_dict: Local (adjusted) symbol dictionary
|
||||
:param remote_dict: Dictionary from `doxygen_xml` module
|
||||
:return: n/a
|
||||
"""
|
||||
for symbol in remote_dict:
|
||||
# Note: symbol `None` is actually a valid symbol in the
|
||||
# `enums` dictionary, containing all enum-value symbols
|
||||
# for enums without names.
|
||||
if symbol is None or not symbol.startswith('_lv'):
|
||||
loc_symbol = symbol
|
||||
else:
|
||||
# Remove '_' prefix.
|
||||
loc_symbol = symbol[1:]
|
||||
|
||||
local_dict[loc_symbol] = remote_dict[symbol]
|
||||
|
||||
|
||||
def _build_local_symbol_dictionaries():
|
||||
"""
|
||||
Build "work-around" dictionaries so that a symbol like `lv_obj_t`
|
||||
actually connects with the struct in `lv_obj_private.h`, and not
|
||||
the typedef in `lv_types.h`.
|
||||
|
||||
:return: n/a
|
||||
"""
|
||||
_build_one_local_dictionary(_defines, doxygen_xml.defines)
|
||||
_build_one_local_dictionary(_enums, doxygen_xml.enums)
|
||||
_build_one_local_dictionary(_variables, doxygen_xml.variables)
|
||||
_build_one_local_dictionary(_namespaces, doxygen_xml.namespaces)
|
||||
_build_one_local_dictionary(_structs, doxygen_xml.structures)
|
||||
_build_one_local_dictionary(_unions, doxygen_xml.unions)
|
||||
_build_one_local_dictionary(_typedefs, doxygen_xml.typedefs)
|
||||
_build_one_local_dictionary(_functions, doxygen_xml.functions)
|
||||
|
||||
|
||||
def _add_hyperlinks_to_eligible_files(intermediate_dir: str,
|
||||
new_algorithm: bool,
|
||||
*doc_rel_paths: [str]):
|
||||
"""
|
||||
Add applicable hyperlinks to eligible docs found joining
|
||||
`intermediate_dir` with each relative path in `doc_rel_paths`.
|
||||
|
||||
See API-link algorithm documented under `_process_one_file()`.
|
||||
|
||||
:param intermediate_dir: Top directory where hyperlinks are to be added.
|
||||
:param doc_rel_paths: Tuple of relative paths from `intermediate_dir` to
|
||||
walk to find docs eligible for API hyperlinks.
|
||||
:return:
|
||||
"""
|
||||
if new_algorithm:
|
||||
# Populate local symbol dictionary set with
|
||||
# symbols WITHOUT any '_' prefixes.
|
||||
_build_local_symbol_dictionaries()
|
||||
|
||||
# Build `.rst` file list.
|
||||
file_list = []
|
||||
|
||||
for rel_path in doc_rel_paths:
|
||||
top_dir = os.path.join(intermediate_dir, rel_path)
|
||||
for dir_bep, sub_dirs, files in os.walk(top_dir, topdown=False):
|
||||
for file in files:
|
||||
if file.lower().endswith('.rst'):
|
||||
file_list.append(os.path.join(dir_bep, file))
|
||||
|
||||
total_eligible_doc_count = 0
|
||||
total_links_added_count = 0
|
||||
|
||||
# For each `.rst` file, add appropriate API hyperlinks.
|
||||
for rst_file in file_list:
|
||||
links_added_count = _process_one_file(rst_file)
|
||||
|
||||
if links_added_count > 0:
|
||||
total_links_added_count += links_added_count
|
||||
total_eligible_doc_count += 1
|
||||
# announce(__file__, f'Eligible doc: [{rst_file}].')
|
||||
|
||||
announce(__file__, f'Docs eligible for API hyperlinks: {total_eligible_doc_count:>4}')
|
||||
announce(__file__, f'API hyperlinks added : {total_links_added_count:>4}')
|
||||
else:
|
||||
for folder in doc_rel_paths:
|
||||
# Fetch a list of '.rst' files excluding 'index.rst'.
|
||||
rst_files = list(
|
||||
(os.path.splitext(item)[0], os.path.join(folder, item))
|
||||
for item in os.listdir(folder)
|
||||
if item.endswith('.rst') and 'index.rst' not in item
|
||||
)
|
||||
|
||||
# For each .RST file in that directory...
|
||||
for stem, path in rst_files:
|
||||
# Start with an empty set.
|
||||
html_includes = set()
|
||||
|
||||
# Build `html_includes` set as a list of tuples containing
|
||||
# (name, html_file). Example: "draw.rst" has `stem` == 'draw',
|
||||
# and generates a list of tuples from .H files where matching
|
||||
# C-code-element names were found. Example:
|
||||
# {('lv_draw_line', 'draw\\lv_draw_line.html'),
|
||||
# ('lv_draw_sdl', 'draw\\sdl\\lv_draw_sdl.html'),
|
||||
# ('lv_draw_sw_blend_to_i1', 'draw\\sw\\blend\\lv_draw_sw_blend_to_i1.html'),
|
||||
# etc.}
|
||||
for symbol_dict in (
|
||||
doxygen_xml.defines,
|
||||
doxygen_xml.enums,
|
||||
doxygen_xml.variables,
|
||||
doxygen_xml.namespaces,
|
||||
doxygen_xml.structures,
|
||||
doxygen_xml.unions,
|
||||
doxygen_xml.typedefs,
|
||||
doxygen_xml.functions
|
||||
):
|
||||
for key, obj in symbol_dict.items():
|
||||
old_get_includes(stem, key, obj, html_includes)
|
||||
|
||||
if html_includes:
|
||||
# Convert `html_includes` set to a list of strings containing the
|
||||
# Sphinx hyperlink syntax "link references". Example from above:
|
||||
# [':ref:`lv_draw_line_h`\n',
|
||||
# ':ref:`lv_draw_sdl_h`\n',
|
||||
# ':ref:`lv_draw_sw_blend_to_i1_h`\n',
|
||||
# etc.]
|
||||
html_includes = list(
|
||||
':ref:`{0}_h`\n'.format(inc)
|
||||
for inc, _ in html_includes
|
||||
)
|
||||
|
||||
# Convert that list to a single string of Sphinx hyperlink
|
||||
# references with blank lines between them.
|
||||
# :ref:`lv_draw_line_h`
|
||||
#
|
||||
# :ref:`lv_draw_sdl_h`
|
||||
#
|
||||
# :ref:`lv_draw_sw_blend_to_i1_h`
|
||||
#
|
||||
# etc.
|
||||
output = ('\n'.join(html_includes)) + '\n'
|
||||
|
||||
# Append that string to the source .RST file being processed.
|
||||
with open(path, 'rb') as f:
|
||||
try:
|
||||
data = f.read().decode('utf-8')
|
||||
except UnicodeDecodeError:
|
||||
print(path)
|
||||
raise
|
||||
|
||||
data = data.split(_auto_gen_sep, 1)[0]
|
||||
|
||||
data += f'{_auto_gen_sep}\n\n'
|
||||
data += output
|
||||
|
||||
with open(path, 'wb') as f:
|
||||
f.write(data.encode('utf-8'))
|
||||
|
||||
|
||||
def _create_rst_files_for_dir(src_root_dir_len: int,
|
||||
src_dir_bep: str,
|
||||
elig_h_files: [str],
|
||||
elig_sub_dirs: [str],
|
||||
out_root_dir: str):
|
||||
"""
|
||||
- Create `index.rst` file and add its top section.
|
||||
- For each file in `elig_h_files`:
|
||||
- Create one `.rst` file.
|
||||
- Add reference to it in `index.rst`.
|
||||
- For each subdir in `elig_sub_dirs`:
|
||||
- add reference "sub_dir_name/index" in `index.rst`.
|
||||
|
||||
:param src_root_dir_len: Length of source-root path string, used with `out_root_dir` to build paths
|
||||
:param src_dir_bep: Directory currently *being processed*
|
||||
:param elig_h_files: Eligible `.h` files directly contained in `src_dir_bep`
|
||||
:param elig_sub_dirs: List of sub-dirs that contained eligible `.h` files
|
||||
:param out_root_dir: Root of output directory, used with to build paths.
|
||||
:return: n/a
|
||||
"""
|
||||
indent = ' '
|
||||
sub_path = src_dir_bep[src_root_dir_len:]
|
||||
out_dir = str(os.path.join(out_root_dir, sub_path))
|
||||
|
||||
# Ensure dir exists. Multiple dirs MAY have to be created
|
||||
# since `.rst` files are created in bottom-up sequence.
|
||||
if not os.path.isdir(out_dir):
|
||||
os.makedirs(out_dir)
|
||||
|
||||
# For top-level directory only... (the last index.rst created,
|
||||
# since they are created in bottom-up sequence)
|
||||
if len(sub_path) == 0 and out_dir.endswith(os.sep):
|
||||
# Trim trailing slash from `out_dir`.
|
||||
out_dir = out_dir[:-1]
|
||||
|
||||
# index.rst
|
||||
with open(os.path.join(out_dir, 'index.rst'), 'w') as f:
|
||||
subdir_stem = os.path.split(out_dir)[-1]
|
||||
section_line = (rst_section_line_char * len(subdir_stem)) + '\n'
|
||||
f.write(section_line)
|
||||
f.write(subdir_stem + '\n')
|
||||
f.write(section_line)
|
||||
f.write('\n')
|
||||
f.write('.. toctree::\n :maxdepth: 1\n\n')
|
||||
|
||||
# One entry per `.rst` file
|
||||
for h_file in elig_h_files:
|
||||
filename = os.path.basename(h_file)
|
||||
stem = os.path.splitext(filename)[0]
|
||||
f.write(indent + stem + '\n')
|
||||
|
||||
# One entry per eligible subdirectory.
|
||||
for sub_dir in elig_sub_dirs:
|
||||
stem = os.path.split(sub_dir)[-1]
|
||||
f.write(indent + stem + '/index\n')
|
||||
|
||||
# One .rst file per h_file
|
||||
for h_file in elig_h_files:
|
||||
filename = os.path.basename(h_file)
|
||||
stem = os.path.splitext(filename)[0]
|
||||
rst_file = os.path.join(out_dir, stem + '.rst')
|
||||
html_file = os.path.join(sub_path, stem + '.html')
|
||||
old_html_files[stem] = html_file
|
||||
|
||||
with open(rst_file, 'w') as f:
|
||||
# Sphinx link target.
|
||||
f.write(f'.. _{stem}_h:\n\n')
|
||||
# Doc title.
|
||||
section_line = (rst_section_line_char * len(filename)) + '\n'
|
||||
f.write(section_line)
|
||||
f.write(filename + '\n')
|
||||
f.write(section_line)
|
||||
f.write('\n')
|
||||
# Content for `breathe`.
|
||||
f.write(f'.. doxygenfile:: {filename}\n')
|
||||
f.write(' :project: lvgl\n\n')
|
||||
|
||||
|
||||
def _recursively_create_api_rst_files(depth: int,
|
||||
src_root_len: int,
|
||||
src_dir_bep: str,
|
||||
out_root_dir: str) -> int:
|
||||
"""
|
||||
Create `.rst` files for the eligible `.h` found in `src_dir_bep` and
|
||||
recursively for subdirectories below it. ("bep" = being processed.)
|
||||
|
||||
Eligible
|
||||
An `.h` file is eligible if Doxygen generated documentation for it.
|
||||
The `EXCLUDE_PATTERNS` Doxygen configuration value can cause
|
||||
Doxygen to skip certain files and directories, in which case,
|
||||
the `.h` files skipped ARE NOT eligible.
|
||||
|
||||
Whether a subdirectory is eligible to be included in an `index.rst`
|
||||
file depends upon whether any eligible `.h` files were recursively
|
||||
found within it. And that isn't known until this function finishes
|
||||
(recursively) processing a directory and returns the number of
|
||||
eligible `.h` files found. Thus, the steps taken within are:
|
||||
|
||||
- Discover all eligible `.h` files directly contained in `src_dir_bep`.
|
||||
- Recursively do the same for each subdirectory, adding the returned
|
||||
count of eligible `.h` files to the sum (`elig_h_file_count`).
|
||||
- If `elig_h_file_count > 0`:
|
||||
- call _create_rst_files_for_dir() to generate appropriate
|
||||
`.rst` files for this directory.
|
||||
- Return `elig_h_file_count`.
|
||||
|
||||
Once we have accumulated this information, then we can generate
|
||||
all the `.rst` files for the current directory without any further
|
||||
directory-tree walking.
|
||||
|
||||
:param depth: Only used for testing/debugging
|
||||
:param src_root_len: Length of source-root path
|
||||
:param src_dir_bep: Source directory *being processed*
|
||||
:param out_root_dir: Output root directory (used to build output paths)
|
||||
:return: Number of `.h` files encountered (so caller knows
|
||||
whether that directory recursively held any
|
||||
eligible `.h` files, to know whether to include
|
||||
"subdir/index" in caller's local `index.rst` file).
|
||||
"""
|
||||
elig_h_files = []
|
||||
sub_dirs = []
|
||||
elig_sub_dirs = []
|
||||
elig_h_file_count = 0
|
||||
|
||||
# For each "thing" found in `src_dir_bep`, build lists:
|
||||
# `elig_sub_dirs` and `elig_h_files`.
|
||||
# By design change, we are including files with 'private'
|
||||
# in their names. Reason: advanced users who need to use
|
||||
# the structs defined within will need the documentation
|
||||
# in those API pages!
|
||||
for dir_item in os.listdir(src_dir_bep):
|
||||
path_bep = os.path.join(src_dir_bep, dir_item)
|
||||
if os.path.isdir(path_bep):
|
||||
sub_dirs.append(path_bep) # Add to sub-dir list.
|
||||
else:
|
||||
if dir_item.lower().endswith('.h'):
|
||||
eligible = (dir_item in doxygen_xml.files)
|
||||
if eligible:
|
||||
elig_h_files.append(path_bep) # Add to .H file list.
|
||||
elig_h_file_count += 1
|
||||
|
||||
# For each subdir...
|
||||
for sub_dir in sub_dirs:
|
||||
subdir_eligible_h_file_count = \
|
||||
_recursively_create_api_rst_files(depth + 1,
|
||||
src_root_len,
|
||||
sub_dir,
|
||||
out_root_dir)
|
||||
|
||||
if subdir_eligible_h_file_count > 0:
|
||||
elig_sub_dirs.append(sub_dir)
|
||||
elig_h_file_count += subdir_eligible_h_file_count
|
||||
|
||||
if elig_h_file_count > 0:
|
||||
# Create index.rst plus .RST files for any direct .H files in dir.
|
||||
_create_rst_files_for_dir(src_root_len,
|
||||
src_dir_bep,
|
||||
elig_h_files,
|
||||
elig_sub_dirs,
|
||||
out_root_dir)
|
||||
|
||||
return elig_h_file_count
|
||||
|
||||
|
||||
def create_api_rst_files(src_root_dir: str, out_root_dir: str):
|
||||
"""
|
||||
Create `.rst` files for API pages based on the `.h` files found
|
||||
in a tree-walk of `a_src_root` and the current contents of the
|
||||
`doxygen_xml.files` dictionary (used to filter out `.h` files that
|
||||
Doxygen generated no documentation for). Output the `.rst` files
|
||||
into `out_root_dir` mirroring the `a_src_root` directory structure.
|
||||
|
||||
:param src_root_dir: root source directory to walk
|
||||
:param out_root_dir: output directory
|
||||
:return: n/a
|
||||
"""
|
||||
src_root_len = len(src_root_dir) + 1
|
||||
_recursively_create_api_rst_files(0, src_root_len, src_root_dir, out_root_dir)
|
||||
|
||||
|
||||
def build_api_docs(lvgl_src_dir, intermediate_dir, doxyfile_src_file, *doc_rel_paths):
|
||||
"""
|
||||
- Prep and run Doxygen, outputting XML.
|
||||
- Load that XML in a form that can quickly tie C symbols to the
|
||||
source files they came from.
|
||||
- Generate API page `.rst` files for source files Doxygen generated
|
||||
documentation for.
|
||||
- Add hyperlinks to these API pages for `.rst` files in `*doc_rel_paths`
|
||||
that are eligible.
|
||||
|
||||
:param lvgl_src_dir: Path to LVGL src directory
|
||||
:param intermediate_dir: Path to intermediate dir being built
|
||||
:param doxyfile_src_file: Full path to src doxygen configuration file
|
||||
:param doc_rel_paths: List of relative paths from `intermediate_dir` to
|
||||
walk to find docs eligible for API hyperlinks.
|
||||
"""
|
||||
# ---------------------------------------------------------------------
|
||||
# - Generate Doxyfile replacing tokens,
|
||||
# - run Doxygen generating XML, and
|
||||
# - load the generated XML from Doxygen output.
|
||||
# ---------------------------------------------------------------------
|
||||
doxygen_xml.EMIT_WARNINGS = EMIT_WARNINGS
|
||||
|
||||
xml_parser = doxygen_xml.DoxygenXml(lvgl_src_dir,
|
||||
intermediate_dir,
|
||||
doxyfile_src_file,
|
||||
silent_mode=False
|
||||
)
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Generate .RST files for API pages.
|
||||
# ---------------------------------------------------------------------
|
||||
announce(__file__, "Generating API documentation .RST files...")
|
||||
api_out_root_dir = os.path.join(intermediate_dir, 'API')
|
||||
create_api_rst_files(lvgl_src_dir, api_out_root_dir)
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# For each directory entry in `doc_rel_paths` array...
|
||||
# - add API hyperlinks to .RST files in the directories in passed array.
|
||||
# ---------------------------------------------------------------------
|
||||
announce(__file__, "Adding API-page hyperlinks to source docs...")
|
||||
_add_hyperlinks_to_eligible_files(intermediate_dir,
|
||||
True,
|
||||
*doc_rel_paths)
|
||||
755
managed_components/lvgl__lvgl/docs/build.py
Normal file
@@ -0,0 +1,755 @@
|
||||
#!/usr/bin/env python3
|
||||
""" build.py -- Generate LVGL documentation using Doxygen and Sphinx + Breathe.
|
||||
|
||||
Synopsis
|
||||
--------
|
||||
- $ python build.py html [ skip_api ] [ fresh_env ]
|
||||
- $ python build.py latex [ skip_api ] [ fresh_env ]
|
||||
- $ python build.py intermediate [ skip_api ]
|
||||
- $ python build.py clean
|
||||
- $ python build.py clean_intermediate
|
||||
- $ python build.py clean_html
|
||||
- $ python build.py clean_latex
|
||||
|
||||
Build Arguments and Clean Arguments can be used one at a time
|
||||
or be freely mixed and combined.
|
||||
|
||||
Description
|
||||
-----------
|
||||
Copy source files to an intermediate directory and modify them there before
|
||||
doc generation occurs. If a full rebuild is being done (e.g. after a `clean`)
|
||||
run Doxygen LVGL's source files to generate intermediate API information
|
||||
in XML format. Generate API documents for Breathe's consumption. Add API
|
||||
links to end of some documents. Generate example documents. From there,
|
||||
Sphinx with Breathe extension uses the resulting set of intermediate files
|
||||
to generate the desired output.
|
||||
|
||||
It is only during this first build that the `skip_api` option has meaning.
|
||||
After the first build, no further actions is taken regarding API pages since
|
||||
they are not regenerated after the first build.
|
||||
|
||||
The intermediate directory has a fixed location (overridable by
|
||||
`LVGL_DOC_BUILD_INTERMEDIATE_DIR` environment variable) and by default this
|
||||
script attempts to rebuild only those documents whose path, name or
|
||||
modification date has changed since the last build.
|
||||
|
||||
The output directory also has a fixed location (overridable by
|
||||
`LVGL_DOC_BUILD_OUTPUT_DIR` environment variable).
|
||||
|
||||
Caution:
|
||||
|
||||
The document build meant for end-user consumption should ONLY be done after a
|
||||
`clean` unless you know that no API documentation and no code examples have changed.
|
||||
|
||||
A `sphinx-build` will do a full doc rebuild any time:
|
||||
|
||||
- the intermediate directory doesn't exist or is empty (since the new files in
|
||||
the intermediate directory will have modification times after the generated
|
||||
HTML or Latex files, even if nothing changed),
|
||||
- the targeted output directory doesn't exist or is empty, or
|
||||
- Sphinx determines that a full rebuild is necessary. This happens when:
|
||||
- intermediate directory (Sphinx's source-file path) has changed,
|
||||
- any options on the `sphinx-build` command line have changed,
|
||||
- `conf.py` modification date has changed, or
|
||||
- `fresh_env` argument is included (runs `sphinx-build` with -E option).
|
||||
|
||||
Typical run time:
|
||||
|
||||
Full build: 22.5 min
|
||||
skip_api : 1.9 min (applies to first build only)
|
||||
|
||||
Options
|
||||
-------
|
||||
help
|
||||
Print usage note and exit with status 0.
|
||||
|
||||
html [ skip_api ] [ fresh_env ]
|
||||
Build HTML output.
|
||||
`skip_api` only has effect on first build after a `clean` or `clean_intermediate`.
|
||||
|
||||
latex [ skip_api ] [ fresh_env ]
|
||||
Build Latex/PDF output (on hold pending removal of non-ASCII characters from input files).
|
||||
`skip_api` only has effect on first build after a `clean` or `clean_intermediate`.
|
||||
|
||||
intermediate [ skip_api ]
|
||||
Generate intermediate directory contents (ready to build output formats).
|
||||
If they already exist, they are removed and re-generated.
|
||||
Note: "intermediate" can be abbreviated down to "int".
|
||||
|
||||
skip_api (with `html` and/or `latex` and/or `intermediate` options)
|
||||
Skip API pages and links when intermediate directory contents are being generated
|
||||
(saving about 91% of build time). Note: they are not thereafter regenerated unless
|
||||
requested by `intermediate` argument or the intermediate directory does not
|
||||
exist. This is intended to be used only during doc development to speed up
|
||||
turn-around time between doc modifications and seeing final results.
|
||||
|
||||
fresh_env (with `html` and/or `latex` options)
|
||||
Run `sphinx-build` with -E command-line argument, which makes it regenerate its
|
||||
"environment" (memory of what was built previously, forcing a full rebuild).
|
||||
|
||||
clean
|
||||
Remove all generated files.
|
||||
|
||||
clean_intermediate
|
||||
Remove intermediate directory.
|
||||
Note: "clean_intermediate" can be abbreviated down to "clean_int".
|
||||
|
||||
clean_html
|
||||
Remove HTML output directory.
|
||||
|
||||
clean_latex
|
||||
Remove Latex output directory.
|
||||
|
||||
Unrecognized arguments print error message, usage note, and exit with status 1.
|
||||
|
||||
Python Package Requirements
|
||||
---------------------------
|
||||
The list of Python package requirements are in `requirements.txt`.
|
||||
|
||||
Install them by:
|
||||
|
||||
$ pip install -r requirements.txt
|
||||
|
||||
History
|
||||
-------
|
||||
The first version of this file (Apr 2021) discovered the name of
|
||||
the current branch (e.g. 'master', 'release/v8.4', etc.) to support
|
||||
different versions of the documentation by establishing the base URL
|
||||
(used in `conf.py` and in [Edit on GitHub] links), and then ran:
|
||||
|
||||
- Doxygen (to generate LVGL API XML), then
|
||||
- Sphinx
|
||||
|
||||
to generate the LVGL document tree. Internally, Sphinx uses `breathe`
|
||||
(a Sphinx extension) to provide a bridge between Doxygen XML output and
|
||||
Sphinx documentation. It also supported a command-line option `clean`
|
||||
to remove generated files before starting (eliminates orphan files,
|
||||
for docs that have moved or changed).
|
||||
|
||||
Since then its duties have grown to include:
|
||||
|
||||
- Using environment variables to convey branch names to several more
|
||||
places where they are used in the docs-generating process (instead
|
||||
of re-writing writing `conf.py` and a `header.rst` each time docs
|
||||
were generated). These are documented where they generated below.
|
||||
|
||||
- Supporting additional command-line options.
|
||||
|
||||
- Generating a `./docs/lv_conf.h` for Doxygen to use (config_builder.py).
|
||||
|
||||
- Supporting multiple execution platforms (which then required tokenizing
|
||||
Doxygen's INPUT path in `Doxyfile` and re-writing portions that used
|
||||
`sed` to generate input or modify files).
|
||||
|
||||
- Adding translation and API links (requiring generating docs in an
|
||||
intermediate directory so that the links could be programmatically
|
||||
added to each document before Sphinx was run). Note: translation link
|
||||
are now done manually since they are only on the main landing page.
|
||||
|
||||
- Generating EXAMPLES page + sub-examples where applicable to individual
|
||||
documents, e.g. to widget-, style-, layout-pages, etc.
|
||||
|
||||
- Building PDF via latex (when working).
|
||||
|
||||
- Shifting doc-generation paradigm to behave more like `make`.
|
||||
|
||||
"""
|
||||
|
||||
# ****************************************************************************
|
||||
# IMPORTANT: If you are getting a PDF-lexer error for an example, check
|
||||
# for extra lines at the end of the file. Only a single empty line
|
||||
# is allowed!!! Ask me how long it took me to figure this out.
|
||||
# -- @kdschlosser
|
||||
# ****************************************************************************
|
||||
|
||||
# Python Library
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
import shutil
|
||||
import dirsync
|
||||
from datetime import datetime
|
||||
|
||||
# LVGL Custom
|
||||
import example_list
|
||||
import api_doc_builder
|
||||
import config_builder
|
||||
from src.lvgl_version import lvgl_version
|
||||
from announce import *
|
||||
|
||||
# Not Currently Used
|
||||
# (Code is kept in case we want to re-implement it later.)
|
||||
# import add_translation
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Configuration
|
||||
# -------------------------------------------------------------------------
|
||||
# These are relative paths from the ./docs/ directory.
|
||||
cfg_project_dir = '..'
|
||||
cfg_lvgl_src_dir = 'src'
|
||||
cfg_doc_src_dir = 'src'
|
||||
cfg_examples_dir = 'examples'
|
||||
cfg_default_intermediate_dir = 'intermediate'
|
||||
cfg_default_output_dir = 'build'
|
||||
cfg_static_dir = '_static'
|
||||
cfg_downloads_dir = 'downloads'
|
||||
cfg_lv_conf_filename = 'lv_conf.h'
|
||||
cfg_lv_version_filename = 'lv_version.h'
|
||||
cfg_doxyfile_filename = 'Doxyfile'
|
||||
cfg_top_index_filename = 'index.rst'
|
||||
cfg_default_branch = 'master'
|
||||
|
||||
# Filename generated in `latex_output_dir` and copied to `pdf_output_dir`.
|
||||
cfg_pdf_filename = 'LVGL.pdf'
|
||||
|
||||
|
||||
def print_usage_note():
|
||||
"""Print usage note."""
|
||||
print('Usage:')
|
||||
print(' $ python build.py [optional_arg ...]')
|
||||
print()
|
||||
print(' where `optional_arg` can be any of these:')
|
||||
print(' html [ skip_api ] [ fresh_env ]')
|
||||
print(' latex [ skip_api ] [ fresh_env ]')
|
||||
print(' intermediate [ skip_api ]')
|
||||
print(' clean')
|
||||
print(' clean_intermediate')
|
||||
print(' clean_html')
|
||||
print(' clean_latex')
|
||||
print(' help')
|
||||
|
||||
|
||||
def remove_dir(tgt_dir):
|
||||
"""Remove directory `tgt_dir`."""
|
||||
if os.path.isdir(tgt_dir):
|
||||
announce(__file__, f'Removing {tgt_dir}...')
|
||||
shutil.rmtree(tgt_dir)
|
||||
else:
|
||||
announce(__file__, f'{tgt_dir} already removed...')
|
||||
|
||||
|
||||
def cmd(cmd_str, start_dir=None, exit_on_error=True):
|
||||
"""Run external command and abort build on error."""
|
||||
saved_dir = None
|
||||
|
||||
if start_dir is not None:
|
||||
saved_dir = os.getcwd()
|
||||
os.chdir(start_dir)
|
||||
|
||||
announce(__file__, f'Running [{cmd_str}] in [{os.getcwd()}]...')
|
||||
return_code = os.system(cmd_str)
|
||||
|
||||
if saved_dir is not None:
|
||||
os.chdir(saved_dir)
|
||||
|
||||
if return_code != 0 and exit_on_error:
|
||||
announce(__file__, "Exiting build due to previous error.")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def intermediate_dir_contents_exists(dir):
|
||||
"""Provide answer to question: Can we have reasonable confidence that
|
||||
the contents of `intermediate_directory` already exists?
|
||||
"""
|
||||
result = False
|
||||
c1 = os.path.isdir(dir)
|
||||
|
||||
if c1:
|
||||
temp_path = os.path.join(dir, 'CHANGELOG.rst')
|
||||
c2 = os.path.exists(temp_path)
|
||||
temp_path = os.path.join(dir, '_ext')
|
||||
c3 = os.path.isdir(temp_path)
|
||||
temp_path = os.path.join(dir, '_static')
|
||||
c4 = os.path.isdir(temp_path)
|
||||
temp_path = os.path.join(dir, 'details')
|
||||
c5 = os.path.isdir(temp_path)
|
||||
temp_path = os.path.join(dir, 'intro')
|
||||
c6 = os.path.isdir(temp_path)
|
||||
temp_path = os.path.join(dir, 'contributing')
|
||||
c7 = os.path.isdir(temp_path)
|
||||
temp_path = os.path.join(dir, cfg_examples_dir)
|
||||
c8 = os.path.isdir(temp_path)
|
||||
result = c2 and c3 and c4 and c5 and c6 and c7 and c8
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def run(args):
|
||||
"""Perform doc-build function(s) requested."""
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Set default settings.
|
||||
# ---------------------------------------------------------------------
|
||||
build_html = False
|
||||
build_latex = False
|
||||
build_intermediate = False
|
||||
skip_api = False
|
||||
fresh_sphinx_env = False
|
||||
clean_all = False
|
||||
clean_intermediate = False
|
||||
clean_html = False
|
||||
clean_latex = False
|
||||
|
||||
def print_setting(setting_name, val):
|
||||
"""Print one setting; used for debugging."""
|
||||
announce(__file__, f'{setting_name:18} = [{val}]')
|
||||
|
||||
def print_settings(and_exit):
|
||||
"""Print all settings and optionally exit; used for debugging."""
|
||||
print_setting("build_html", build_html)
|
||||
print_setting("build_latex", build_latex)
|
||||
print_setting("build_intermediate", build_intermediate)
|
||||
print_setting("skip_api", skip_api)
|
||||
print_setting("fresh_sphinx_env", fresh_sphinx_env)
|
||||
print_setting("clean_all", clean_all)
|
||||
print_setting("clean_intermediate", clean_intermediate)
|
||||
print_setting("clean_html", clean_html)
|
||||
print_setting("clean_latex", clean_latex)
|
||||
|
||||
if and_exit:
|
||||
exit(0)
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Process args.
|
||||
# ---------------------------------------------------------------------
|
||||
|
||||
# No args means print usage note and exit with status 0.
|
||||
if len(args) == 0:
|
||||
print_usage_note()
|
||||
exit(0)
|
||||
|
||||
# Some args are present. Interpret them in loop here.
|
||||
# Unrecognized arg means print error, print usage note, exit with status 1.
|
||||
for arg in args:
|
||||
# We use chained `if-elif-else` instead of `match` for those on Linux
|
||||
# systems that will not have the required version 3.10 of Python yet.
|
||||
if arg == 'help':
|
||||
print_usage_note()
|
||||
exit(0)
|
||||
elif arg == "html":
|
||||
build_html = True
|
||||
elif arg == "latex":
|
||||
build_latex = True
|
||||
elif "intermediate".startswith(arg) and len(arg) >= 3:
|
||||
# Accept abbreviations.
|
||||
build_intermediate = True
|
||||
elif arg == 'skip_api':
|
||||
skip_api = True
|
||||
elif arg == 'fresh_env':
|
||||
fresh_sphinx_env = True
|
||||
elif arg == "clean":
|
||||
clean_all = True
|
||||
clean_intermediate = True
|
||||
clean_html = True
|
||||
clean_latex = True
|
||||
elif arg == "clean_html":
|
||||
clean_html = True
|
||||
elif arg == "clean_latex":
|
||||
clean_latex = True
|
||||
elif "clean_intermediate".startswith(arg) and len(arg) >= 9:
|
||||
# Accept abbreviations.
|
||||
# Needs to be after others so "cl" will not
|
||||
clean_intermediate = True
|
||||
else:
|
||||
print(f'Argument [{arg}] not recognized.')
|
||||
print()
|
||||
print_usage_note()
|
||||
exit(2) # 2 = customary Unix command-line syntax error.
|
||||
|
||||
# '-E' option forces Sphinx to rebuild its environment so all docs are
|
||||
# fully regenerated, even if not changed.
|
||||
# Note: Sphinx runs in ./docs/, but uses `intermediate_dir` for input.
|
||||
if fresh_sphinx_env:
|
||||
announce(__file__, "Force-regenerating all files...")
|
||||
env_opt = '-E'
|
||||
else:
|
||||
env_opt = ''
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Start.
|
||||
# ---------------------------------------------------------------------
|
||||
t0 = datetime.now()
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Set up paths.
|
||||
#
|
||||
# Variable Suffixes:
|
||||
# _filename = filename without path
|
||||
# _path = path leading to a file or directory (absolute or relative)
|
||||
# _file = path leading to a file (absolute or relative)
|
||||
# _dir = path leading to a directory (absolute or relative)
|
||||
# ---------------------------------------------------------------------
|
||||
base_dir = os.path.abspath(os.path.dirname(__file__))
|
||||
project_dir = os.path.abspath(os.path.join(base_dir, cfg_project_dir))
|
||||
examples_dir = os.path.join(project_dir, cfg_examples_dir)
|
||||
lvgl_src_dir = os.path.join(project_dir, cfg_lvgl_src_dir)
|
||||
|
||||
# Establish intermediate directory. The presence of environment variable
|
||||
# `LVGL_DOC_BUILD_INTERMEDIATE_DIR` overrides default in `cfg_default_intermediate_dir`.
|
||||
if 'LVGL_DOC_BUILD_INTERMEDIATE_DIR' in os.environ:
|
||||
intermediate_dir = os.environ['LVGL_DOC_BUILD_INTERMEDIATE_DIR']
|
||||
else:
|
||||
intermediate_dir = os.path.join(base_dir, cfg_default_intermediate_dir)
|
||||
|
||||
lv_conf_file = os.path.join(intermediate_dir, cfg_lv_conf_filename)
|
||||
version_dst_file = os.path.join(intermediate_dir, cfg_lv_version_filename)
|
||||
top_index_file = os.path.join(intermediate_dir, cfg_top_index_filename)
|
||||
doxyfile_src_file = os.path.join(base_dir, cfg_doxyfile_filename)
|
||||
doxyfile_dst_file = os.path.join(intermediate_dir, cfg_doxyfile_filename)
|
||||
pdf_intermediate_dst_dir = os.path.join(intermediate_dir, cfg_static_dir, cfg_downloads_dir)
|
||||
pdf_intermediate_dst_file = os.path.join(pdf_intermediate_dst_dir, cfg_pdf_filename)
|
||||
sphinx_path_sep = '/'
|
||||
pdf_relative_file = cfg_static_dir + sphinx_path_sep + cfg_downloads_dir + sphinx_path_sep + cfg_pdf_filename
|
||||
pdf_link_ref_str = f'PDF Version: :download:`{cfg_pdf_filename} <{pdf_relative_file}>`'
|
||||
|
||||
# Establish build directory. The presence of environment variable
|
||||
# `LVGL_DOC_BUILD_OUTPUT_DIR` overrides default in `cfg_default_output_dir`.
|
||||
if 'LVGL_DOC_BUILD_OUTPUT_DIR' in os.environ:
|
||||
output_dir = os.environ['LVGL_DOC_BUILD_OUTPUT_DIR']
|
||||
else:
|
||||
output_dir = os.path.join(base_dir, cfg_default_output_dir)
|
||||
|
||||
html_output_dir = os.path.join(output_dir, 'html')
|
||||
latex_output_dir = os.path.join(output_dir, 'latex')
|
||||
pdf_output_dir = os.path.join(output_dir, 'pdf')
|
||||
pdf_src_file = os.path.join(latex_output_dir, cfg_pdf_filename)
|
||||
pdf_dst_file = os.path.join(pdf_output_dir, cfg_pdf_filename)
|
||||
version_src_file = os.path.join(project_dir, cfg_lv_version_filename)
|
||||
|
||||
# Special stuff for right-aligning PDF download link.
|
||||
# Note: this needs to be embedded in a <div> tag because the
|
||||
# Sphinx `:download:` role causes the link to appear in a <p> tag
|
||||
# and in HTML5, <p> tags cannot be nested!
|
||||
cfg_right_just_para_text = """.. raw:: html
|
||||
|
||||
<div style="text-align: right;">"""
|
||||
cfg_end_right_just_para_text = """.. raw:: html
|
||||
|
||||
</div>"""
|
||||
# Blank lines are required due to the directives.
|
||||
cfg_pdf_link_ref_block_str = \
|
||||
cfg_right_just_para_text + os.linesep \
|
||||
+ os.linesep \
|
||||
+ pdf_link_ref_str + os.linesep + \
|
||||
os.linesep \
|
||||
+ cfg_end_right_just_para_text + os.linesep \
|
||||
+ os.linesep
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Change to script directory for consistent run-time environment.
|
||||
# ---------------------------------------------------------------------
|
||||
os.chdir(base_dir)
|
||||
announce(__file__, f'Intermediate dir: [{intermediate_dir}]')
|
||||
announce(__file__, f'Output dir : [{output_dir}]')
|
||||
announce(__file__, f'Running from : [{base_dir}]')
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Clean? If so, clean (like `make clean`), but do not exit.
|
||||
# ---------------------------------------------------------------------
|
||||
some_cleaning_to_be_done = clean_intermediate or clean_html or clean_latex \
|
||||
or clean_all or (os.path.isdir(intermediate_dir) and build_intermediate)
|
||||
|
||||
if some_cleaning_to_be_done:
|
||||
announce(__file__, "Cleaning...", box=True)
|
||||
|
||||
if clean_intermediate:
|
||||
remove_dir(intermediate_dir)
|
||||
|
||||
if clean_html:
|
||||
remove_dir(html_output_dir)
|
||||
|
||||
if clean_latex:
|
||||
remove_dir(latex_output_dir)
|
||||
|
||||
if clean_all:
|
||||
remove_dir(output_dir)
|
||||
|
||||
if os.path.isdir(intermediate_dir) and build_intermediate:
|
||||
remove_dir(intermediate_dir)
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Populate LVGL_URLPATH and LVGL_GITCOMMIT environment variables:
|
||||
# - LVGL_URLPATH <= 'master' or '8.4' '9.2' etc.
|
||||
# - LVGL_GITCOMMIT <= same (see 03-Oct-2024 note below).
|
||||
#
|
||||
# These supply input later in the doc-generation process as follows:
|
||||
#
|
||||
# LVGL_URLPATH is used by:
|
||||
# - `conf.py` to build `html_baseurl` for Sphinx for
|
||||
# - generated index
|
||||
# - generated search window
|
||||
# - establishing canonical page for search engines
|
||||
# - `link_roles.py` to generate translation links
|
||||
# - `doxygen_xml.py` to generate links to API pages
|
||||
#
|
||||
# LVGL_GITCOMMIT is used by:
|
||||
# - `conf.py` => html_context['github_version'] for
|
||||
# Sphinx Read-the-Docs theme to add to [Edit on GitHub] links
|
||||
# - `conf.py` => repo_commit_hash for generated EXAMPLES pages for:
|
||||
# - [View on GitHub] buttons (view C code examples)
|
||||
# - [View on GitHub] buttons (view Python code examples)
|
||||
# ---------------------------------------------------------------------
|
||||
# 03-Oct-2024: Gabor requested LVGL_GITCOMMIT be changed to a branch
|
||||
# name since that will always be current, and it will fix a large
|
||||
# number of broken links on the docs website, since commits that
|
||||
# generated docs can sometimes go away. This gets used in:
|
||||
# - [Edit on GitHub] links in doc pages (via Sphinx theme), and
|
||||
# - [View on GitHub] links in example pages (via `example_list.py`
|
||||
# and `lv_example.py`).
|
||||
# Original code:
|
||||
# status, br = subprocess.getstatusoutput("git branch --show-current")
|
||||
# _, gitcommit = subprocess.getstatusoutput("git rev-parse HEAD")
|
||||
# br = re.sub(r'\* ', '', br)
|
||||
# 're' was previously used to remove leading '* ' from current branch
|
||||
# string when we were parsing output from bare `git branch` output.
|
||||
# This is no longer needed with `--show-current` option now used.
|
||||
# ---------------------------------------------------------------------
|
||||
status, branch = subprocess.getstatusoutput("git branch --show-current")
|
||||
|
||||
# If above failed (i.e. `branch` not valid), default to 'master'.
|
||||
if status != 0:
|
||||
branch = cfg_default_branch
|
||||
elif branch == cfg_default_branch:
|
||||
# Expected in most cases. Nothing to change.
|
||||
pass
|
||||
else:
|
||||
# `branch` is valid. Capture release version if in a 'release/' branch.
|
||||
if branch.startswith('release/'):
|
||||
branch = branch[8:]
|
||||
else:
|
||||
# Default to 'master'.
|
||||
branch = cfg_default_branch
|
||||
|
||||
os.environ['LVGL_URLPATH'] = branch
|
||||
os.environ['LVGL_GITCOMMIT'] = branch
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Prep `intermediate_dir` to become the `sphinx-build` source dir.
|
||||
# ---------------------------------------------------------------------
|
||||
# dirsync `exclude_list` = list of regex patterns to exclude.
|
||||
intermediate_re = r'^' + cfg_default_intermediate_dir + r'.*'
|
||||
output_re = r'^' + cfg_default_output_dir + r'.*'
|
||||
exclude_list = [r'lv_conf\.h', r'^__pycache__.*', intermediate_re, output_re]
|
||||
|
||||
if intermediate_dir_contents_exists(intermediate_dir):
|
||||
# We are just doing an update of the intermediate_dir contents.
|
||||
announce(__file__, "Updating intermediate directory...", box=True)
|
||||
|
||||
exclude_list.append(r'examples.*')
|
||||
options = {
|
||||
'verbose': True, # Report files copied.
|
||||
'create': True, # Create directories if they don't exist.
|
||||
'twoway': False, # False means data flow only src => tgt.
|
||||
'purge': False, # False means DO NOT remove orphan files/dirs in tgt dir (preserving examples/ dir).
|
||||
'exclude': exclude_list
|
||||
}
|
||||
# action == 'sync' means copy files even when they do not already exist in tgt dir.
|
||||
# action == 'update' means DO NOT copy files when they do not already exist in tgt dir.
|
||||
dirsync.sync(cfg_doc_src_dir, intermediate_dir, 'sync', **options)
|
||||
dirsync.sync(examples_dir, os.path.join(intermediate_dir, cfg_examples_dir), 'sync', **options)
|
||||
elif build_intermediate or build_html or build_latex:
|
||||
# We are having to create the intermediate_dir contents by copying.
|
||||
announce(__file__, "Building intermediate directory...", box=True)
|
||||
|
||||
t1 = datetime.now()
|
||||
copy_method = 1
|
||||
|
||||
# Both of these methods work.
|
||||
if copy_method == 0:
|
||||
# --------- Method 0:
|
||||
ignore_func = shutil.ignore_patterns('tmp*', 'output*')
|
||||
announce(__file__, 'Copying docs...')
|
||||
shutil.copytree(cfg_doc_src_dir, intermediate_dir, ignore=ignore_func, dirs_exist_ok=True)
|
||||
announce(__file__, 'Copying examples...')
|
||||
shutil.copytree(examples_dir, os.path.join(intermediate_dir, cfg_examples_dir), dirs_exist_ok=True)
|
||||
else:
|
||||
# --------- Method 1:
|
||||
options = {
|
||||
'create': True, # Create directories if they don't exist.
|
||||
'exclude': exclude_list
|
||||
}
|
||||
# action == 'sync' means copy files even when they do not already exist in tgt dir.
|
||||
# action == 'update' means DO NOT copy files when they do not already exist in tgt dir.
|
||||
announce(__file__, 'Copying docs...')
|
||||
dirsync.sync(cfg_doc_src_dir, intermediate_dir, 'sync', **options)
|
||||
announce(__file__, 'Copying examples...')
|
||||
dirsync.sync(examples_dir, os.path.join(intermediate_dir, cfg_examples_dir), 'sync', **options)
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# Build <intermediate_dir>/lv_conf.h from lv_conf_template.h.
|
||||
# -----------------------------------------------------------------
|
||||
config_builder.run(lv_conf_file)
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# Copy `lv_version.h` into intermediate directory.
|
||||
# -----------------------------------------------------------------
|
||||
shutil.copyfile(version_src_file, version_dst_file)
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# Generate examples pages. Include sub-pages pages that get included
|
||||
# in individual documents where applicable.
|
||||
# -----------------------------------------------------------------
|
||||
announce(__file__, "Generating examples...")
|
||||
example_list.exec(intermediate_dir)
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# Add translation links.
|
||||
# This is being skipped in favor of a manually-placed
|
||||
# translation link at the top of `./docs/index.rst`.
|
||||
# -----------------------------------------------------------------
|
||||
# Original code:
|
||||
# if True:
|
||||
# announce(__file__, "Skipping adding translation links.")
|
||||
# else:
|
||||
# announce(__file__, "Adding translation links...")
|
||||
# add_translation.exec(intermediate_dir)
|
||||
|
||||
if skip_api:
|
||||
announce(__file__, "Skipping API generation as requested.")
|
||||
else:
|
||||
# -------------------------------------------------------------
|
||||
# Generate API pages and links thereto.
|
||||
# -------------------------------------------------------------
|
||||
announce(__file__, "API page and link processing...")
|
||||
api_doc_builder.EMIT_WARNINGS = False
|
||||
|
||||
# api_doc_builder.run() => doxy_xml_parser.DoxygenXml() now:
|
||||
# - preps and runs Doxygen generating XML,
|
||||
# - loads generated XML.
|
||||
# Then api_doc_builder.run():
|
||||
# - creates .RST files for API pages, and
|
||||
# - adds API hyperlinks to .RST files in the directories in passed array.
|
||||
api_doc_builder.build_api_docs(lvgl_src_dir,
|
||||
intermediate_dir,
|
||||
doxyfile_src_file,
|
||||
'details',
|
||||
'intro'
|
||||
)
|
||||
|
||||
t2 = datetime.now()
|
||||
announce(__file__, 'Example/API run time: ' + str(t2 - t1))
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Build PDF
|
||||
# ---------------------------------------------------------------------
|
||||
if not build_latex:
|
||||
announce(__file__, "Skipping Latex build.")
|
||||
else:
|
||||
t1 = datetime.now()
|
||||
announce(__file__, "Building Latex output...", box=True)
|
||||
|
||||
# If PDF link is present in top index.rst, remove it so PDF
|
||||
# does not have a link to itself.
|
||||
with open(top_index_file, 'rb') as f:
|
||||
index_data = f.read().decode('utf-8')
|
||||
|
||||
if pdf_link_ref_str in index_data:
|
||||
index_data = index_data.replace(pdf_link_ref_str, '')
|
||||
|
||||
with open(top_index_file, 'wb') as f:
|
||||
f.write(index_data.encode('utf-8'))
|
||||
|
||||
src = intermediate_dir
|
||||
dst = output_dir
|
||||
cpu = os.cpu_count()
|
||||
# As of 22-Feb-2025, sadly the -D version=xxx is not working as documented.
|
||||
# So the version strings applicable to Latex/PDF/man pages/texinfo
|
||||
# formats are assembled by `conf.py`.
|
||||
cmd_line = f'sphinx-build -M latex "{src}" "{dst}" -j {cpu}'
|
||||
cmd(cmd_line)
|
||||
|
||||
# Generate PDF.
|
||||
announce(__file__, "Building PDF...", box=True)
|
||||
cmd_line = 'latexmk -pdf "LVGL.tex"'
|
||||
cmd(cmd_line, latex_output_dir, False)
|
||||
|
||||
# Move resulting PDF to its output directory.
|
||||
if not os.path.exists(pdf_output_dir):
|
||||
os.makedirs(pdf_output_dir)
|
||||
|
||||
shutil.move(pdf_src_file, pdf_dst_file)
|
||||
t2 = datetime.now()
|
||||
announce(__file__, 'PDF : ' + pdf_dst_file)
|
||||
announce(__file__, 'Latex gen time: ' + str(t2 - t1))
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Build HTML
|
||||
# ---------------------------------------------------------------------
|
||||
if not build_html:
|
||||
announce(__file__, "Skipping HTML build.")
|
||||
else:
|
||||
t1 = datetime.now()
|
||||
announce(__file__, "Building HTML output...", box=True)
|
||||
|
||||
# If PDF is present in build directory, copy it to
|
||||
# intermediate directory for use by HTML build.
|
||||
# (Sphinx copies it to its HTML output, so it ends
|
||||
# up on the webserver where it can be downloaded).
|
||||
if os.path.isfile(pdf_dst_file):
|
||||
# Create _static/download/ directory if needed.
|
||||
if not os.path.exists(pdf_intermediate_dst_dir):
|
||||
os.makedirs(pdf_intermediate_dst_dir)
|
||||
|
||||
shutil.copyfile(pdf_dst_file, pdf_intermediate_dst_file)
|
||||
|
||||
# If PDF is present, ensure there is a link to it in the top
|
||||
# index.rst so HTML build will have it.
|
||||
# Support both Windows and Linux platforms with `os.linesep`.
|
||||
if os.path.isfile(pdf_intermediate_dst_file):
|
||||
with open(top_index_file, 'rb') as f:
|
||||
index_data = f.read().decode('utf-8')
|
||||
|
||||
if pdf_link_ref_str not in index_data:
|
||||
index_data = cfg_pdf_link_ref_block_str + index_data
|
||||
|
||||
with open(top_index_file, 'wb') as f:
|
||||
f.write(index_data.encode('utf-8'))
|
||||
|
||||
# Note: While it can be done (e.g. if one needs to set a stop point
|
||||
# in Sphinx code for development purposes), it is NOT a good idea to
|
||||
# run Sphinx from script as
|
||||
# from sphinx.cmd.build import main as sphinx_build
|
||||
# sphinx_args = [...]
|
||||
# sphinx_build(sphinx_args)
|
||||
# because it takes ~10X longer to run than `sphinx_build` executable,
|
||||
# literally > 3 hours.
|
||||
|
||||
ver = lvgl_version(version_src_file)
|
||||
src = intermediate_dir
|
||||
dst = output_dir
|
||||
cpu = os.cpu_count()
|
||||
|
||||
debugging_breathe = 0
|
||||
if debugging_breathe:
|
||||
from sphinx.cmd.build import main as sphinx_build
|
||||
# Don't allow parallel processing while debugging (the '-j' arg is removed).
|
||||
sphinx_args = ['-M', 'html', f'{src}', f'{dst}', '-D', f'version={ver}']
|
||||
|
||||
if len(env_opt) > 0:
|
||||
sphinx_args.append(f'{env_opt}')
|
||||
|
||||
sphinx_build(sphinx_args)
|
||||
else:
|
||||
# The -D option correctly replaces (overrides) configuration attribute
|
||||
# values in the `conf.py` module. Since `conf.py` now correctly
|
||||
# computes its own `version` value, we don't have to override it here
|
||||
# with a -D options. If it should need to be used in the future,
|
||||
# the value after the '=' MUST NOT have quotation marks around it
|
||||
# or it won't work. Correct usage: f'-D version={ver}' .
|
||||
cmd_line = f'sphinx-build -M html "{src}" "{dst}" -j {cpu} {env_opt}'
|
||||
cmd(cmd_line)
|
||||
|
||||
t2 = datetime.now()
|
||||
announce(__file__, 'HTML gen time : ' + str(t2 - t1))
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Indicate results.
|
||||
# ---------------------------------------------------------------------
|
||||
t_end = datetime.now()
|
||||
announce(__file__, 'Total run time: ' + str(t_end - t0))
|
||||
announce(__file__, 'Done.')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
"""Make module importable as well as run-able."""
|
||||
run(sys.argv[1:])
|
||||
74
managed_components/lvgl__lvgl/docs/config_builder.py
Normal file
@@ -0,0 +1,74 @@
|
||||
"""
|
||||
Create lv_conf.h in same directory as this file
|
||||
from ../lv_conf_template.h that has:
|
||||
|
||||
1. all its #define LV_USE... 0-or-1 options set to 1
|
||||
(except for LV_USER_PROFILER),
|
||||
2. all its #define LV_FONT... 0-or-1 options set to 1,
|
||||
3. its #if 0 directive set to #if 1.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
|
||||
base_path = os.path.dirname(__file__)
|
||||
dst_config = os.path.join(base_path, 'lv_conf.h')
|
||||
src_config = os.path.abspath(os.path.join(
|
||||
base_path,
|
||||
'..',
|
||||
'lv_conf_template.h'
|
||||
))
|
||||
disabled_option_re = re.compile(r'^\s*#define\s+\w+\s+(\b0\b)')
|
||||
|
||||
|
||||
def run(c_path=None):
|
||||
global dst_config
|
||||
os.chdir(base_path)
|
||||
|
||||
if c_path is not None:
|
||||
dst_config = c_path
|
||||
|
||||
with open(src_config, 'r') as f:
|
||||
data = f.read()
|
||||
|
||||
lines = data.split('\n')
|
||||
|
||||
for i, line in enumerate(lines):
|
||||
if 'LV_USE_PROFILER' in line:
|
||||
continue
|
||||
|
||||
# These 2 fonts have been deprecated in favor of
|
||||
# LV_FONT_SOURCE_HAN_SANS_SC_14_CJK and
|
||||
# LV_FONT_SOURCE_HAN_SANS_SC_16_CJK.
|
||||
if 'LV_FONT_SIMSUN_14_CJK' in line:
|
||||
continue
|
||||
|
||||
if 'LV_FONT_SIMSUN_16_CJK' in line:
|
||||
continue
|
||||
|
||||
if 'LV_USE' in line or ('LV_FONT' in line and '#define' in line):
|
||||
match = disabled_option_re.search(line)
|
||||
if match:
|
||||
# Replace '0' with '1' without altering any other part of line.
|
||||
# Set `j` to index where '0' was found.
|
||||
j = match.regs[1][0]
|
||||
# Surgically insert '1' in place of '0'.
|
||||
lines[i] = line[:j] + '1' + line[j + 1:]
|
||||
elif line.startswith('#if 0'):
|
||||
line = line.replace('#if 0', '#if 1')
|
||||
lines[i] = line
|
||||
|
||||
data = '\n'.join(lines)
|
||||
|
||||
with open(dst_config, 'w') as f:
|
||||
f.write(data)
|
||||
|
||||
|
||||
def cleanup():
|
||||
if os.path.exists(dst_config):
|
||||
os.remove(dst_config)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
"""Make module importable as well as run-able."""
|
||||
run()
|
||||
429
managed_components/lvgl__lvgl/docs/doxygen_config.py
Normal file
@@ -0,0 +1,429 @@
|
||||
"""doxygen_config.py
|
||||
Python Interface to Doxygen Config Files (Doxyfiles)
|
||||
|
||||
Author : "Victor Wheeler"
|
||||
Copyright: "Copyright (C) 2025 WGA Crystal Research, Inc."
|
||||
License : "MIT"
|
||||
Version : "1.0"
|
||||
|
||||
This work was inspired by the `doxygen-python-interface` project at
|
||||
https://github.com/TraceSoftwareInternational/doxygen-python-interface.
|
||||
On 27-Feb-2025 I was engaged in a production project wherein I wanted
|
||||
to find a Python module that I could re-use to reliably work with
|
||||
Doxygen configuration files (Doxyfiles). The best one I found was
|
||||
`doxygen-python-interface`. Unfortunately, the ``configParser`` from
|
||||
that project could not be used because it both had important bugs and
|
||||
design flaws in it (conflicts with legal Doxygen config syntax), and
|
||||
it appears to have been abandoned after 26-Apr-2018, preventing these
|
||||
things from being remedied.
|
||||
|
||||
So a brand-new module has been created herewith based on sound O-O design
|
||||
principles and a design that actually works in alignment with Doxygen
|
||||
configuration syntax.
|
||||
|
||||
Usage:
|
||||
|
||||
import doxygen_config
|
||||
...
|
||||
# 1. Load configuration from Doxyfile.
|
||||
cfg = doxygen_config.DoxygenConfig()
|
||||
cfg.load(doxyfile_src_file)
|
||||
|
||||
# 2. Get a list of Doxygen option names.
|
||||
opt_list = cfg.options()
|
||||
ok_to_proceed = cfg.is_valid_option('PREDEFINED') \
|
||||
and cfg.is_valid_option('INPUT')
|
||||
|
||||
# 3. Update it.
|
||||
if ok_to_proceed:
|
||||
temp = cfg.value('PREDEFINED')
|
||||
temp = temp.replace('<<CONFIG_PATH>>', config_file)
|
||||
cfg.set('PREDEFINED', temp)
|
||||
|
||||
temp = cfg.value('INPUT')
|
||||
temp = temp.replace('<<SRC>>', f'"{pjt_src_dir}"')
|
||||
cfg.set('INPUT', temp)
|
||||
|
||||
# 4. Save it.
|
||||
# The original comments and order of config options are preserved.
|
||||
# The ``bare`` argument discards comments from the output.
|
||||
cfg.save(cfg_dict, doxyfile_dst_file, bare=True)
|
||||
|
||||
Design Differences from `doxygen-python-interface`:
|
||||
|
||||
- The DoxygenConfig class represents the actual Doxygen configuration,
|
||||
in alignment with O-O theory --- it is not just a place to store a
|
||||
set of functions that never needed to be a class.
|
||||
|
||||
- If the user does a default ``save()`` (not requesting a "bare"
|
||||
version of the Doxygen configuration), the saved Doxyfile
|
||||
should be a binary match to the original Doxyfile loaded.
|
||||
|
||||
Exceptions:
|
||||
|
||||
1. Any trailing whitespace in original Doxyfile after the ``=``
|
||||
on empty options is not preserved.
|
||||
|
||||
2. Multi-line lists that had unaligned backslashes after them like this:
|
||||
|
||||
EXCLUDE_PATTERNS = */libs/barcode/code* \
|
||||
*/libs/freetype/ft* \
|
||||
*/libs/gif/gif* \
|
||||
*/libs/lodepng/lode* \
|
||||
*/libs/qrcode/qr* \
|
||||
*/libs/thorvg/* \
|
||||
*/libs/tiny_ttf/stb* \
|
||||
*/libs/tjpgd/tjp* \
|
||||
*/others/vg_lite_tvg/vg*
|
||||
|
||||
will be saved like this:
|
||||
|
||||
EXCLUDE_PATTERNS = */libs/barcode/code* \
|
||||
*/libs/freetype/ft* \
|
||||
*/libs/gif/gif* \
|
||||
*/libs/lodepng/lode* \
|
||||
*/libs/qrcode/qr* \
|
||||
*/libs/thorvg/* \
|
||||
*/libs/tiny_ttf/stb* \
|
||||
*/libs/tjpgd/tjp* \
|
||||
*/others/vg_lite_tvg/vg*
|
||||
|
||||
``doxygen-python-interface`` did not save the comments so an
|
||||
"edit in place" of a Doxyfile could be catastrophic if the
|
||||
comments were needed in the source Doxyfile as they often are
|
||||
in production scenarios.
|
||||
|
||||
- The ``save()`` method has an optional ``bare`` argument (default False)
|
||||
that can be used to save a "bare" version of the Doxyfile options,
|
||||
discarding the comments from the currently-loaded Doxyfile.
|
||||
|
||||
- Input values are preserved exactly as they were found. The
|
||||
`doxygen-python-interface`'s ``configParser`` class removed
|
||||
quotation marks from incoming values and added quotation marks
|
||||
to values containing spaces before storing them again. While
|
||||
this "sounds nice", it was incompatible with Doxygen for every
|
||||
type of item that could have a "list" as a value, such as the
|
||||
PREDEFINED and ABBREVIATE_BRIEF options.
|
||||
|
||||
Examples:
|
||||
|
||||
PREDEFINED = USE_LIST USE_TABLE USE_CHART
|
||||
|
||||
PREDEFINED = DOXYGEN CONFIG_PATH="/path with spaces/to/config.h"
|
||||
|
||||
PREDEFINED = DOXYGEN \
|
||||
CONFIG_PATH="/path with spaces/to/config.h"
|
||||
|
||||
These are all valid values for the PREDEFINED option and
|
||||
MUST NOT have quotes around any of them! Can you imagine the havoc
|
||||
that would result if a Python module meant to handle Doxygen Doxyfiles
|
||||
altered Doxygen configuration items like this?
|
||||
|
||||
PREDEFINED = "USE_LIST USE_TABLE USE_CHART"
|
||||
|
||||
Thus, it is up to the user to know when values he is changing
|
||||
have space(s) AND ALSO need quotes and take appropriate measures
|
||||
by adding quotes when needed and not otherwise.
|
||||
|
||||
- The storage of the list of Doxygen options is encapsulated
|
||||
in the instance of the DoxygenConfig class instead of being
|
||||
returned as a dictionary from the ``load...()`` function.
|
||||
Its values are readable and writeable via methods. The
|
||||
end user is not able to add options that were not part
|
||||
of the original input Doxyfile, nor remove options that were
|
||||
part of the original input Doxyfile. This gives some level of
|
||||
control on retaining valid Doxygen options.
|
||||
|
||||
It is an error to attempt to set a value with an option name
|
||||
that does not exist in the configuration. A NameError exception
|
||||
is raised if it is attempted. Attempting to read the value of
|
||||
an option name that does not exist returns the value ``None``.
|
||||
|
||||
While Doxygen options change from time to time, it is up to the
|
||||
end user to use ``doxygen -u Doxyfile`` to keep his input
|
||||
Doxyfile(s) up to date.
|
||||
|
||||
Storage:
|
||||
|
||||
The actual configuration values are represented in an internal
|
||||
dictionary not intended to be accessed directly by the typical end
|
||||
user. The keys are the Doxygen option names and the values are:
|
||||
|
||||
- str : single values with possibly embedded spaces
|
||||
- list: multi-line values with possibly embedded spaces
|
||||
|
||||
Quotation marks are neither removed nor added, so it is up to the
|
||||
user to set values compatible with Doxygen configuration syntax.
|
||||
This also makes it okay for multi-line values to have more than one
|
||||
value per line: if it is okay by Doxygen, then it is okay by
|
||||
the DoxygenConfig class.
|
||||
|
||||
If the user sets an option value passing a list, those values
|
||||
will be represented as a multi-line value in the saved Doxyfile.
|
||||
|
||||
The Philosophy of Removing Quotation Marks Is Not Workable for Doxygen:
|
||||
|
||||
When one asks, "Is it appropriate to remove the quotation marks?"
|
||||
What if a value looked like this (2 quoted items in one line),
|
||||
removing quotation marks would be an error:
|
||||
|
||||
"abc def" "ghi jkl"
|
||||
|
||||
The ABBREVIATE_BRIEF list could indeed appear like this.
|
||||
|
||||
If it were argued that all multi-value items should be formatted as
|
||||
multi-line lists, then quotation marks theory works, as the
|
||||
ABBREVIATE_BRIEF option does not require quotation marks around
|
||||
every value.
|
||||
|
||||
However, since Doxygen does not require this, there is still a
|
||||
strong argument for not tampering with quotation marks at all
|
||||
when importing values. The strongest reasons are:
|
||||
|
||||
- Doxygen can and does accept values like this where the value of
|
||||
an option can be a list. Doxygen sees this as 2 separate values:
|
||||
|
||||
"abc def" "ghi jkl"
|
||||
|
||||
- If the end user is going to set values with spaces in them,
|
||||
it could be made the user's responsibility to know when
|
||||
there are spaces and thus include quotes when needed.
|
||||
|
||||
In the end, the "do not tamper with quotation marks" argument wins
|
||||
for sake of reliability. So the policy is: quotation marks are
|
||||
neither removed nor added. It is up to the user to know when they
|
||||
are needed and add them himself.
|
||||
"""
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
|
||||
|
||||
__author__ = "Victor Wheeler"
|
||||
__copyright__ = "Copyright (C) 2025 WGA Crystal Research, Inc."
|
||||
__license__ = "MIT"
|
||||
__version__ = "1.0"
|
||||
|
||||
|
||||
class ParseException(Exception):
|
||||
"""Exception thrown upon unexpected parsing errors."""
|
||||
pass
|
||||
|
||||
|
||||
class DoxygenConfig:
|
||||
"""Doxygen Configurations (from/to Doxyfiles)"""
|
||||
|
||||
def __init__(self):
|
||||
"""Prepare instantiated DoxygenConfig for use."""
|
||||
# Regexes used during Doxyfile parsing
|
||||
self._re_single_line_option = re.compile(r'^\s*(\w+)\s*=\s*([^\\]*)\s*$')
|
||||
self._re_top_of_multiline_option = re.compile(r'^\s*(\w+)\s*=\s*(|.*\S)\s*\\$')
|
||||
# Doxygen cfg items by option name
|
||||
self._cfg_items_dict = {}
|
||||
# Comments by name of option below it.
|
||||
# Comments at end of file have key 'self._end_key'.
|
||||
self._cfg_comments_dict = {}
|
||||
# Key used for comments found after last option in Doxyfile
|
||||
self._end_key = 'END'
|
||||
# Configuration to match Doxygen -g output (template Doxyfile)
|
||||
self._char_count_before_equals = 23
|
||||
|
||||
def load(self, doxyfile: str):
|
||||
"""Load options and comments from `doxyfile`
|
||||
|
||||
:param doxyfile: Path to doxyfile
|
||||
|
||||
:raise FileNotFoundError: When doxyfile not found
|
||||
:raise ParseException: When there is a parsing error
|
||||
"""
|
||||
|
||||
if not os.path.exists(doxyfile):
|
||||
logging.error(f'Doxyfile not found {doxyfile}.')
|
||||
raise FileNotFoundError(doxyfile)
|
||||
|
||||
self._cfg_items_dict.clear()
|
||||
self._cfg_comments_dict.clear()
|
||||
|
||||
# Default encoding: UTF-8.
|
||||
with open(doxyfile, 'r') as file:
|
||||
in_multiline_opt = False
|
||||
multiline_opt_name_bep = None # "bep" = "being processed"
|
||||
accumulated_other_lines = []
|
||||
|
||||
for line in file.readlines():
|
||||
line = line.strip()
|
||||
|
||||
if in_multiline_opt:
|
||||
# There are 2 ways this list can end:
|
||||
# 1. the normal way when last item has no trailing `\`, or
|
||||
# 2. the last item has a trailing `\` and there is a blank-
|
||||
# or comment-line after it, which should NOT be added
|
||||
# to the list, but instead signal end-of-list.
|
||||
if not line.endswith('\\'):
|
||||
in_multiline_opt = False
|
||||
|
||||
val = line.rstrip('\\').strip()
|
||||
|
||||
if self._bool_comment_or_blank_line(val):
|
||||
accumulated_other_lines.append(line)
|
||||
in_multiline_opt = False
|
||||
else:
|
||||
self._cfg_items_dict[multiline_opt_name_bep].append(val)
|
||||
|
||||
elif self._bool_comment_or_blank_line(line):
|
||||
accumulated_other_lines.append(line)
|
||||
|
||||
elif self._bool_top_of_multiline_option(line):
|
||||
multiline_opt_name_bep, val = self._parse_multiline_option(line)
|
||||
self._cfg_items_dict[multiline_opt_name_bep] = [val]
|
||||
self._cfg_comments_dict[multiline_opt_name_bep] = accumulated_other_lines
|
||||
accumulated_other_lines = []
|
||||
in_multiline_opt = True
|
||||
|
||||
elif self._bool_single_line_option(line):
|
||||
option_name, val = self._parse_single_line_option(line)
|
||||
self._cfg_items_dict[option_name] = val
|
||||
self._cfg_comments_dict[option_name] = accumulated_other_lines
|
||||
accumulated_other_lines = []
|
||||
|
||||
# Any comments or blank lines found after last Doxygen option
|
||||
# are represented in _cfg_comments_dict with key `self._end_key`.
|
||||
if accumulated_other_lines:
|
||||
self._cfg_comments_dict[self._end_key] = accumulated_other_lines
|
||||
accumulated_other_lines.clear()
|
||||
|
||||
def save(self, doxyfile: str, bare=False):
|
||||
"""Save configuration to `doxyfile`.
|
||||
|
||||
:param doxyfile: Output path where Doxygen configuration will be
|
||||
written. Overwrites file if it exists.
|
||||
:param bare: Do not preserve comments from loaded file.
|
||||
"""
|
||||
|
||||
lines = []
|
||||
|
||||
for option_name, val in self._cfg_items_dict.items():
|
||||
if not bare:
|
||||
lines.extend(self._cfg_comments_dict[option_name])
|
||||
|
||||
if type(val) is list:
|
||||
# We will be aligning the backslashes after the
|
||||
# items in the list, so we need to know the longest.
|
||||
# First value in list:
|
||||
multi_line_indent = ' ' * (self._char_count_before_equals + 2)
|
||||
longest_len = len(max(val, key=len))
|
||||
val_w_len = val[0].ljust(longest_len)
|
||||
lines.append(f'{option_name:<23}= {val_w_len} \\')
|
||||
|
||||
# Next n-2 values in list:
|
||||
if len(val) > 2:
|
||||
for temp in val[1:-1]:
|
||||
val_w_len = temp.ljust(longest_len)
|
||||
lines.append(f'{multi_line_indent}{val_w_len} \\')
|
||||
|
||||
# Last value in list:
|
||||
lines.append(f'{multi_line_indent}{val[-1]}')
|
||||
elif type(val) is str:
|
||||
val_w_len = option_name.ljust(self._char_count_before_equals)
|
||||
if len(val) == 0:
|
||||
lines.append(f'{val_w_len}=')
|
||||
else:
|
||||
lines.append(f'{val_w_len}= {val}')
|
||||
|
||||
if self._end_key in self._cfg_comments_dict:
|
||||
if not bare:
|
||||
lines.extend(self._cfg_comments_dict[self._end_key])
|
||||
|
||||
# Ensure there is exactly 1 newline at end of file.
|
||||
lines.append('')
|
||||
|
||||
with open(doxyfile, 'w') as file:
|
||||
file.write('\n'.join(lines))
|
||||
|
||||
logging.debug(f'Saved configuration to [{doxyfile}].')
|
||||
|
||||
def option_names(self):
|
||||
"""List of contained Doxygen option names"""
|
||||
return self._cfg_items_dict.keys()
|
||||
|
||||
def is_valid_option(self, option_name: str) -> bool:
|
||||
"""Is `option_name` a valid option name?"""
|
||||
return option_name in self._cfg_items_dict
|
||||
|
||||
def set(self, option_name: str, val: str or list):
|
||||
"""Set value of specified option
|
||||
|
||||
:param option_name: Name of Doxygen option whose value to fetch
|
||||
:param val: Value to set
|
||||
- str = single-line value;
|
||||
- list = multi-line value.
|
||||
|
||||
:raises NameError: When ``name`` is not found.
|
||||
"""
|
||||
if option_name in self._cfg_items_dict:
|
||||
self._cfg_items_dict[option_name] = val
|
||||
if type(val) is list:
|
||||
logging.debug(f'Item [{option_name}] set to list.')
|
||||
else:
|
||||
logging.debug(f'Item [{option_name}] set to [{val}].')
|
||||
else:
|
||||
logging.error(f'Doxyfile option {option_name} not found.')
|
||||
raise NameError(f'Doxygen option {option_name} not found.')
|
||||
|
||||
def value(self, option_name: str) -> str or list:
|
||||
"""Value of specified option
|
||||
|
||||
:param option_name: Name of Doxygen option whose value to fetch
|
||||
|
||||
:returns string: single-line value
|
||||
:returns list: multi-line value
|
||||
:returns None: When ``option_name`` is not found.
|
||||
"""
|
||||
if option_name in self._cfg_items_dict:
|
||||
result = self._cfg_items_dict[option_name]
|
||||
logging.debug(f'Item [{option_name}] fetched.')
|
||||
else:
|
||||
result = None
|
||||
logging.debug(f'Item [{option_name}] not found.')
|
||||
|
||||
return result
|
||||
|
||||
def _parse_multiline_option(self, line) -> (str, str):
|
||||
"""Extract option name and first line of value of multi-line option.
|
||||
|
||||
:param line: line to parse
|
||||
:return: name and first line of multi-line option
|
||||
:raise ParseException: When process fail to extract data
|
||||
"""
|
||||
|
||||
matches = self._re_top_of_multiline_option.search(line)
|
||||
if matches is None or len(matches.groups()) != 2:
|
||||
logging.error(f'Error extracting first value in multi-line option from [{line}].')
|
||||
raise ParseException(f'Error extracting first value in multi-line option from [{line}].')
|
||||
|
||||
return matches.group(1), matches.group(2)
|
||||
|
||||
def _parse_single_line_option(self, line) -> (str, str):
|
||||
"""Extract option name and value of single line option.
|
||||
|
||||
:param line: line to parse
|
||||
:return: option name and value
|
||||
:raise ParseException: When process fail to extract data
|
||||
"""
|
||||
|
||||
matches = self._re_single_line_option.search(line)
|
||||
|
||||
if matches is None or len(matches.groups()) != 2:
|
||||
logging.error(f'Error extracting option name and value from [{line}].')
|
||||
raise ParseException(f'Error extracting option name and value from [{line}].')
|
||||
|
||||
return matches.group(1), matches.group(2)
|
||||
|
||||
def _bool_single_line_option(self, line: str) -> bool:
|
||||
return self._re_single_line_option.match(line) is not None
|
||||
|
||||
def _bool_comment_or_blank_line(self, line: str) -> bool: # NoQA
|
||||
return line.startswith("#") or (len(line) == 0)
|
||||
|
||||
def _bool_top_of_multiline_option(self, line) -> bool:
|
||||
return self._re_top_of_multiline_option.match(line) is not None
|
||||
1714
managed_components/lvgl__lvgl/docs/doxygen_xml.py
Normal file
163
managed_components/lvgl__lvgl/docs/example_list.py
Normal file
@@ -0,0 +1,163 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
|
||||
|
||||
def process_index_rst(path):
|
||||
# print(path)
|
||||
with open(path, 'r') as fp:
|
||||
data = fp.read()
|
||||
|
||||
data = data.split('\n')
|
||||
|
||||
last_line = ""
|
||||
title_tmp = ""
|
||||
|
||||
for line in data:
|
||||
line = line.strip()
|
||||
|
||||
if not line:
|
||||
continue
|
||||
|
||||
if line.startswith('---'):
|
||||
title_tmp = last_line.strip()
|
||||
|
||||
elif line.startswith('.. lv_example::'):
|
||||
name = line.replace('.. lv_example::', '').strip()
|
||||
yield name, title_tmp
|
||||
|
||||
last_line = line
|
||||
|
||||
|
||||
h1 = {
|
||||
"get_started": "Get Started",
|
||||
"styles": "Styles",
|
||||
"anim": "Animations",
|
||||
"event": "Events",
|
||||
"layouts": "Layouts",
|
||||
"scroll": "Scrolling",
|
||||
"widgets": "Widgets"
|
||||
}
|
||||
|
||||
widgets = {
|
||||
"obj": "Base Widget",
|
||||
"animimg": "Animation Image",
|
||||
"arc": "Arc",
|
||||
"bar": "Bar",
|
||||
"button": "Button",
|
||||
"buttonmatrix": "Button Matrix",
|
||||
"calendar": "Calendar",
|
||||
"canvas": "Canvas",
|
||||
"chart": "Chart",
|
||||
"checkbox": "Checkbox",
|
||||
"dropdown": "Dropdown",
|
||||
"image": "Image",
|
||||
"imagebutton": "Image Button",
|
||||
"keyboard": "Keyboard",
|
||||
"label": "Label",
|
||||
"led": "LED",
|
||||
"line": "Line",
|
||||
"list": "List",
|
||||
"lottie": "Lottie",
|
||||
"menu": "Menu",
|
||||
"msgbox": "Message Box",
|
||||
"roller": "Roller",
|
||||
"scale":"Scale",
|
||||
"slider": "Slider",
|
||||
"span": "Span",
|
||||
"spinbox": "Spinbox",
|
||||
"spinner": "Spinner",
|
||||
"switch": "Switch",
|
||||
"table": "Table",
|
||||
"tabview": "Tabview",
|
||||
"textarea": "Textarea",
|
||||
"tileview": "Tileview",
|
||||
"win": "Window",
|
||||
}
|
||||
|
||||
HEADING = '='
|
||||
CHAPTER = '#'
|
||||
SECTION = '*'
|
||||
SUBSECTION = '='
|
||||
SUBSUBSECTION = '-'
|
||||
|
||||
|
||||
def write_header(h_num, text, f):
|
||||
text = text.strip()
|
||||
if h_num == 0:
|
||||
f.write(header_defs[h_num] * len(text))
|
||||
f.write('\n')
|
||||
|
||||
f.write(text + '\n')
|
||||
f.write(header_defs[h_num] * len(text))
|
||||
f.write('\n\n')
|
||||
|
||||
|
||||
# This is the order that Sphinx uses for the headings/titles. 0 is the
|
||||
# largest and 4 is the smallest. If this order is not kept in the reST files
|
||||
# Sphinx will complain
|
||||
header_defs = {
|
||||
0: HEADING,
|
||||
1: CHAPTER,
|
||||
2: SECTION,
|
||||
3: SUBSECTION,
|
||||
4: SUBSUBSECTION
|
||||
}
|
||||
|
||||
layouts = {
|
||||
"flex": "Flex",
|
||||
"grid": "Grid",
|
||||
}
|
||||
|
||||
|
||||
def print_item(path, lvl, d, fout):
|
||||
for k in d:
|
||||
v = d[k]
|
||||
if k.startswith(path + "/lv_example_"):
|
||||
write_header(lvl, v, fout)
|
||||
fout.write(f".. lv_example:: {k}\n")
|
||||
fout.write("\n")
|
||||
|
||||
|
||||
def exec(temp_directory):
|
||||
output_path = os.path.join(temp_directory, 'examples.rst')
|
||||
|
||||
paths = ["../examples/", "../demos/"]
|
||||
fout = open(output_path, "w")
|
||||
filelist = []
|
||||
|
||||
for path in paths:
|
||||
for root, dirs, files in os.walk(path):
|
||||
for f in files:
|
||||
# append the file name to the list
|
||||
filelist.append(os.path.join(root, f))
|
||||
|
||||
filelist = [fi for fi in filelist if fi.endswith("index.rst")]
|
||||
|
||||
d_all = {}
|
||||
# print all the file names
|
||||
for fn in filelist:
|
||||
d_all.update(dict(tuple(item for item in process_index_rst(fn))))
|
||||
|
||||
# fout.write("```eval_rst\n")
|
||||
# fout.write(":github_url: |github_link_base|/examples.md\n")
|
||||
# fout.write("```\n")
|
||||
# fout.write("\n")
|
||||
|
||||
fout.write('.. _examples:\n\n')
|
||||
write_header(0, 'Examples', fout)
|
||||
|
||||
for h in h1:
|
||||
write_header(1, h1[h], fout)
|
||||
|
||||
if h == "widgets":
|
||||
for w in widgets:
|
||||
write_header(2, widgets[w], fout)
|
||||
print_item(h + "/" + w, 3, d_all, fout)
|
||||
elif h == "layouts":
|
||||
for l in layouts:
|
||||
write_header(2, layouts[l], fout)
|
||||
print_item(h + "/" + l, 3, d_all, fout)
|
||||
else:
|
||||
print_item(h, 2, d_all, fout)
|
||||
|
||||
fout.write("")
|
||||
23082
managed_components/lvgl__lvgl/docs/flyers/LVGL-Chinese-Flyer.pdf
Normal file
77
managed_components/lvgl__lvgl/docs/make.bat
Normal file
@@ -0,0 +1,77 @@
|
||||
@ECHO OFF
|
||||
rem -----------------------------------------------------------------------
|
||||
rem This file is intended only to be used after the contents of the
|
||||
rem intermediate directory have been created. Do so by:
|
||||
rem
|
||||
rem $ python build.py intermediate [skip_api]
|
||||
rem
|
||||
rem This is a modified version of the standard Sphinx `make.bat` file.
|
||||
rem Changes:
|
||||
rem - uses these environment variables in the same way `build.py` does
|
||||
rem when they are set:
|
||||
rem - LVGL_DOC_BUILD_INTERMEDIATE_DIR
|
||||
rem - LVGL_DOC_BUILD_OUTPUT_DIR
|
||||
rem - Cleans up locally-created environment variables at end, so they
|
||||
rem do not clutter environment variables.
|
||||
rem -----------------------------------------------------------------------
|
||||
|
||||
pushd %~dp0
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
setlocal ENABLEDELAYEDEXPANSION
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
if "%LVGL_DOC_BUILD_INTERMEDIATE_DIR%" == "" (
|
||||
set SOURCEDIR=intermediate
|
||||
) else (
|
||||
set SOURCEDIR=%LVGL_DOC_BUILD_INTERMEDIATE_DIR%
|
||||
)
|
||||
if "%LVGL_DOC_BUILD_OUTPUT_DIR%" == "" (
|
||||
set BUILDDIR=build
|
||||
) else (
|
||||
set BUILDDIR=%LVGL_DOC_BUILD_OUTPUT_DIR%
|
||||
)
|
||||
if "%SPHINXOPTS%" == "" (
|
||||
rem python ./src/lvgl_version.py >_version_temp.txt
|
||||
rem set /p VER=<_version_temp.txt
|
||||
rem del _version_temp.txt
|
||||
for /F %%v in ('python lvgl_version.py') do set VER=%%v
|
||||
echo VERSION [!VER!]
|
||||
set SPHINXOPTS=-D version="!VER!" -j 4
|
||||
set VER=
|
||||
)
|
||||
|
||||
echo SOURCEDIR [%SOURCEDIR%]
|
||||
echo BUILDDIR [%BUILDDIR%]
|
||||
echo SPHINXOPTS [%SPHINXOPTS%]
|
||||
|
||||
%SPHINXBUILD% >NUL 2>NUL
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.https://www.sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
echo %SPHINXBUILD% -M %1 "%SOURCEDIR%" "%BUILDDIR%" %SPHINXOPTS% %2 %3 %4 %5 %6 %7 %8 %9
|
||||
%SPHINXBUILD% -M %1 "%SOURCEDIR%" "%BUILDDIR%" %SPHINXOPTS% %2 %3 %4 %5 %6 %7 %8 %9
|
||||
goto end
|
||||
|
||||
:help
|
||||
%SPHINXBUILD% -M help "%SOURCEDIR%" "%BUILDDIR%" %SPHINXOPTS% %2 %3 %4 %5 %6 %7 %8 %9
|
||||
|
||||
:end
|
||||
rem Clean up.
|
||||
popd
|
||||
set BUILDDIR=
|
||||
set SOURCEDIR=
|
||||
18
managed_components/lvgl__lvgl/docs/requirements.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
Sphinx
|
||||
breathe
|
||||
imagesize
|
||||
importlib-metadata
|
||||
sphinx-rtd-theme
|
||||
sphinx-sitemap
|
||||
sphinxcontrib-applehelp
|
||||
sphinxcontrib-devhelp
|
||||
sphinxcontrib-htmlhelp
|
||||
sphinxcontrib-jsmath
|
||||
sphinxcontrib-qthelp
|
||||
sphinxcontrib-serializinghtml
|
||||
sphinxcontrib-mermaid==0.9.2
|
||||
sphinx-design
|
||||
sphinx-rtd-dark-mode
|
||||
typing-extensions
|
||||
sphinx-reredirects
|
||||
dirsync
|
||||
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>
|
||||