docs: modernize to .md/.mdx for new Fumadocs-based docs engine (#9993)

Co-authored-by: André Costa <andre_miguel_costa@hotmail.com>
This commit is contained in:
Mutahhar Mustafa Khan
2026-04-22 14:59:08 +01:00
committed by GitHub
parent fc1452573c
commit 4750fe054b
699 changed files with 34355 additions and 69004 deletions
+137
View File
@@ -1,3 +1,140 @@
name: Build Docs
on:
push:
branches:
- master
- 'release/*'
paths:
- 'docs/**'
- 'src/**'
- 'examples/**'
- 'demos/**'
- 'configs/ci/docs/**'
- 'scripts/build_html_examples.sh'
- 'scripts/find_version.sh'
- 'lv_conf_template.h'
- 'lv_version.h'
- 'lvgl.h'
# https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#concurrency
# Ensure that only one commit will be running tests at a time on each PR
concurrency:
group: ${{ github.ref }}-${{ github.workflow }}
cancel-in-progress: true
env:
EM_VERSION: 2.0.4
EM_CACHE_FOLDER: 'emsdk-cache'
jobs:
build-and-deploy:
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v6
with:
persist-credentials: false
fetch-depth: 0
- name: Setup Emscripten cache
id: cache-system-libraries
uses: actions/cache@v5
with:
path: ${{env.EM_CACHE_FOLDER}}
key: ${{env.EM_VERSION}}-${{ runner.os }}
- uses: mymindstorm/setup-emsdk@v14
with:
version: ${{env.EM_VERSION}}
actions-cache-folder: ${{env.EM_CACHE_FOLDER}}
- name: ccache
uses: hendrikmuhs/ccache-action@v1
- name: Build examples (with cache)
run: |
# Grab the current name in case the LVGL folder is not called lvgl
# as it could be the case if this is running from a fork
LVGL_FOLDER=$(pwd)
# Move to the parent folder to fetch emscripten port.
cd ..
./lvgl/scripts/build_html_examples.sh --symlink $LVGL_FOLDER
- name: Install Doxygen
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends doxygen
# Parses the source code and generates XML files from the documentation
- name: Generate Doxygen XML
run: |
cd docs
doxygen
# Tool used to convert Doxygen XML to .mdx API pages
- name: Install doxymark
if: ${{ github.event_name == 'push' (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release/')) }}
run: |
npm install github:mutahharmkhan/doxymark
# Generate Documentation API pages
- name: Generate API pages
if: ${{ github.event_name == 'push' (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release/')) }}
run: |
npx doxymark \
--input docs/doxygen/xml
--output docs/api
--index docs/api-index.json
--preset fumadocs
--auto-group
--source-url https://github.com/lvgl/lvgl/tree/${{ github.sha }}/src
rm -rf docs/doxygen
- name: Install Example Documentation
run: npm i @lvgl/examples-generator
- name: Check Example Documentation
run: |
npx -p @lvgl/examples-generator lvgl-examples-check-about --src examples
npx -p @lvgl/examples-generator lvgl-examples-check-examples --src examples
- name: Generate Example Documentation
if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release/')) }}
run: |
npx -p @lvgl/examples-generator lvgl-examples-gen \
--src examples \
--out docs/examples
- name: Retrieve version
id: version
run: |
echo "VERSION_NAME=$(scripts/find_version.sh)" >> "$GITHUB_OUTPUT"
- name: Deploy to subfolder
if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/release/')) }}
uses: JamesIves/github-pages-deploy-action@v4.8.0
with:
token: ${{ secrets.LVGL_BOT_TOKEN }}
repository-name: lvgl/open-docs
branch: gh-pages # The branch the action should deploy to.
folder: docs # The folder the action should deploy.
target-folder: ${{ steps.version.outputs.VERSION_NAME }} # needs to match what is inside `docs/version_list.json`
git-config-name: lvgl-bot
git-config-email: lvgl-bot@users.noreply.github.com
commit: true
- name: Deploy to master
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
uses: JamesIves/github-pages-deploy-action@v4.8.0
with:
token: ${{ secrets.LVGL_BOT_TOKEN }}
repository-name: lvgl/open-docs
branch: gh-pages # The branch the action should deploy to.
folder: docs # The folder the action should deploy.
target-folder: latest # needs to match what is inside `docs/version_list.json`
git-config-name: lvgl-bot
git-config-email: lvgl-bot@users.noreply.github.com
commit: true
name: Build docs
on:
pull_request:
+191 -3
View File
@@ -435,7 +435,7 @@ LOOKUP_CACHE_SIZE = 0
# normally produced when WARNINGS is set to YES.
# The default value is: NO.
EXTRACT_ALL = NO
EXTRACT_ALL = YES
# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
# be included in the documentation.
@@ -1907,7 +1907,7 @@ MAN_LINKS = NO
# captures the structure of the code including all documentation.
# The default value is: NO.
GENERATE_XML = NO
GENERATE_XML = YES
# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
@@ -2070,7 +2070,195 @@ PREDEFINED = DOXYGEN \
LV_ATTRIBUTE_LARGE_RAM_ARRAY= \
LV_ATTRIBUTE_FAST_MEM= \
LV_ATTRIBUTE_EXTERN_DATA= \
LV_FORMAT_ATTRIBUTE(fmt,va)=
LV_FORMAT_ATTRIBUTE(fmt,va)= \
LV_USE_3DTEXTURE=1 \
LV_USE_ANIMIMG=1 \
LV_USE_ARABIC_PERSIAN_CHARS=1 \
LV_USE_ARC=1 \
LV_USE_ARCLABEL=1 \
LV_USE_ASSERT_MALLOC=1 \
LV_USE_ASSERT_MEM_INTEGRITY=1 \
LV_USE_ASSERT_NULL=1 \
LV_USE_ASSERT_OBJ=1 \
LV_USE_ASSERT_STYLE=1 \
LV_USE_BAR=1 \
LV_USE_BARCODE=1 \
LV_USE_BIDI=1 \
LV_USE_BMP=1 \
LV_USE_BUILTIN_MALLOC=1 \
LV_USE_BUILTIN_SPRINTF=1 \
LV_USE_BUILTIN_STRING=1 \
LV_USE_BUTTON=1 \
LV_USE_BUTTONMATRIX=1 \
LV_USE_CALENDAR=1 \
LV_USE_CALENDAR_CHINESE=1 \
LV_USE_CALENDAR_HEADER_ARROW=1 \
LV_USE_CALENDAR_HEADER_DROPDOWN=1 \
LV_USE_CANVAS=1 \
LV_USE_CHART=1 \
LV_USE_CHECKBOX=1 \
LV_USE_COLOR_FILTER=1 \
LV_USE_DRAW_ARM2D_SYNC=1 \
LV_USE_DRAW_DAVE2D=1 \
LV_USE_DRAW_DMA2D=1 \
LV_USE_DRAW_DMA2D_INTERRUPT=1 \
LV_USE_DRAW_EVE=1 \
LV_USE_DRAW_NANOVG=1 \
LV_USE_DRAW_OPENGLES=1 \
LV_USE_DRAW_PXP=1 \
LV_USE_DRAW_SDL=1 \
LV_USE_DRAW_SW=1 \
LV_USE_DRAW_SW_ASM=1 \
LV_USE_DRAW_SW_COMPLEX_GRADIENTS=1 \
LV_USE_DRAW_VG_LITE=1 \
LV_USE_DROPDOWN=1 \
LV_USE_EGL=1 \
LV_USE_EVDEV=1 \
LV_USE_EXT_DATA=1 \
LV_USE_FFMPEG=1 \
LV_USE_FILE_EXPLORER=1 \
LV_USE_FLEX=1 \
LV_USE_FLOAT=1 \
LV_USE_FONT_COMPRESSED=1 \
LV_USE_FONT_MANAGER=1 \
LV_USE_FONT_PLACEHOLDER=1 \
LV_USE_FRAGMENT=1 \
LV_USE_FREERTOS_TASK_NOTIFY=1 \
LV_USE_FREETYPE=1 \
LV_USE_FS_ARDUINO_ESP_LITTLEFS=1 \
LV_USE_FS_ARDUINO_SD=1 \
LV_USE_FS_FATFS=1 \
LV_USE_FS_FROGFS=1 \
LV_USE_FS_LITTLEFS=1 \
LV_USE_FS_MEMFS=1 \
LV_USE_FS_POSIX=1 \
LV_USE_FS_STDIO=1 \
LV_USE_FS_UEFI=1 \
LV_USE_FS_WIN32=1 \
LV_USE_FT81X=1 \
LV_USE_G2D=1 \
LV_USE_G2D_ASSERT=1 \
LV_USE_GENERIC_MIPI=1 \
LV_USE_GESTURE_RECOGNITION=1 \
LV_USE_GIF=1 \
LV_USE_GLFW=1 \
LV_USE_GLTF=1 \
LV_USE_GRID=1 \
LV_USE_GRIDNAV=1 \
LV_USE_GSTREAMER=1 \
LV_USE_ILI9341=1 \
LV_USE_IMAGE=1 \
LV_USE_IMAGEBUTTON=1 \
LV_USE_IME_PINYIN=1 \
LV_USE_IMGFONT=1 \
LV_USE_KEYBOARD=1 \
LV_USE_LABEL=1 \
LV_USE_LAYER_DEBUG=1 \
LV_USE_LED=1 \
LV_USE_LIBINPUT=1 \
LV_USE_LIBJPEG_TURBO=1 \
LV_USE_LIBPNG=1 \
LV_USE_LIBWEBP=1 \
LV_USE_LINE=1 \
LV_USE_LINUX_DRM=1 \
LV_USE_LINUX_FBDEV=1 \
LV_USE_LIST=1 \
LV_USE_LODEPNG=1 \
LV_USE_LOG=1 \
LV_USE_LOTTIE=1 \
LV_USE_LOVYAN_GFX=1 \
LV_USE_LZ4=1 \
LV_USE_LZ4_EXTERNAL=1 \
LV_USE_LZ4_INTERNAL=1 \
LV_USE_MATRIX=1 \
LV_USE_MEM_MONITOR=1 \
LV_USE_MENU=1 \
LV_USE_MONKEY=1 \
LV_USE_MSGBOX=1 \
LV_USE_NANOVG=1 \
LV_USE_NATIVE_HELIUM_ASM=1 \
LV_USE_NEMA_GFX=1 \
LV_USE_NEMA_LIB=1 \
LV_USE_NEMA_VG=1 \
LV_USE_NUTTX=1 \
LV_USE_NUTTX_CUSTOM_INIT=1 \
LV_USE_NUTTX_LCD=1 \
LV_USE_NUTTX_LIBUV=1 \
LV_USE_NUTTX_MOUSE=1 \
LV_USE_NUTTX_TOUCHSCREEN=1 \
LV_USE_NV3007=1 \
LV_USE_NXP_ELCDIF=1 \
LV_USE_OBJ_ID=1 \
LV_USE_OBJ_ID_BUILTIN=1 \
LV_USE_OBJ_NAME=1 \
LV_USE_OBJ_PROPERTY=1 \
LV_USE_OBJ_PROPERTY_NAME=1 \
LV_USE_OBSERVER=1 \
LV_USE_OPENGLES=1 \
LV_USE_OPENGLES_DEBUG=1 \
LV_USE_OS=1 \
LV_USE_PARALLEL_DRAW_DEBUG=1 \
LV_USE_PERF_MONITOR=1 \
LV_USE_PPA=1 \
LV_USE_PRIVATE_API=1 \
LV_USE_PROFILER=1 \
LV_USE_PROFILER_BUILTIN=1 \
LV_USE_PROFILER_BUILTIN_POSIX=1 \
LV_USE_PXP=1 \
LV_USE_PXP_ASSERT=1 \
LV_USE_QNX=1 \
LV_USE_QRCODE=1 \
LV_USE_REFR_DEBUG=1 \
LV_USE_RENESAS_GLCDC=1 \
LV_USE_RLE=1 \
LV_USE_RLOTTIE=1 \
LV_USE_ROLLER=1 \
LV_USE_ROTATE_G2D=1 \
LV_USE_ROTATE_PXP=1 \
LV_USE_SCALE=1 \
LV_USE_SDL=1 \
LV_USE_SLIDER=1 \
LV_USE_SNAPSHOT=1 \
LV_USE_SPAN=1 \
LV_USE_SPINBOX=1 \
LV_USE_SPINNER=1 \
LV_USE_ST_LTDC=1 \
LV_USE_ST7735=1 \
LV_USE_ST7789=1 \
LV_USE_ST7796=1 \
LV_USE_STDLIB_MALLOC=1 \
LV_USE_STDLIB_SPRINTF=1 \
LV_USE_STDLIB_STRING=1 \
LV_USE_SVG=1 \
LV_USE_SVG_ANIMATION=1 \
LV_USE_SVG_DEBUG=1 \
LV_USE_SWITCH=1 \
LV_USE_SYSMON=1 \
LV_USE_TABLE=1 \
LV_USE_TABVIEW=1 \
LV_USE_TEMPL=1 \
LV_USE_TEST=1 \
LV_USE_TEST_SCREENSHOT_COMPARE=1 \
LV_USE_TEXTAREA=1 \
LV_USE_TFT_ESPI=1 \
LV_USE_THEME_DEFAULT=1 \
LV_USE_THEME_MONO=1 \
LV_USE_THEME_SIMPLE=1 \
LV_USE_THORVG=1 \
LV_USE_THORVG_EXTERNAL=1 \
LV_USE_THORVG_INTERNAL=1 \
LV_USE_TILEVIEW=1 \
LV_USE_TINY_TTF=1 \
LV_USE_TJPGD=1 \
LV_USE_TRANSLATION=1 \
LV_USE_UEFI=1 \
LV_USE_VECTOR_GRAPHIC=1 \
LV_USE_VG_LITE_DRIVER=1 \
LV_USE_VG_LITE_THORVG=1 \
LV_USE_WAYLAND=1 \
LV_USE_WIN=1 \
LV_USE_WINDOWS=1 \
LV_USE_X11=1
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The
-30
View File
@@ -1,30 +0,0 @@
# 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
SOURCEDIR = intermediate
BUILDDIR = build
# 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)
+26 -389
View File
@@ -1,412 +1,49 @@
# Documentation
LVGL documentation is authored in **MDX**. The rendering site that serves these pages is maintained separately by the LVGL team — contributors here author and review `.mdx` content under `./src/`.
## 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.
To build the documentation on Windows:
python build.py html
On Linux:
python3 build.py html
Intermediate files are normally prepared in `./docs/intermediate/` and the final documentation will normally appear in `./docs/build/html/`. Both of these directories can be overridden using environment variables. See documentation in `build.py` header comment for details.
If the list of document source files (including the `.h` files in the `./src/` directory) has changed (names or paths):
python build.py clean
Will remove the old intermediate and build files, eliminating the orphan files that would otherwise result.
To see a list of options available:
python build.py
Read the header comment in `build.py` for detailed documentation on each option.
> **Local preview Docker image — *coming soon*.** We will publish an image on GHCR (`ghcr.io/lvgl/lvgl-docs` — exact tag TBD) that bundles the docs site and serves this `./src/` content at `http://localhost:3000`.
## Everything Must Be Documented
## 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.
Our firm policy: ***EVERYTHING MUST BE DOCUMENTED***. Public APIs, widgets, features, and configuration options all need an MDX page or section. When in doubt, add it.
## Where Content Lives
## reStructuredText Content
All MDX pages live under `./src/`. The directory structure drives the URL, and sidebar ordering is controlled by `meta.json` files inside each directory.
LVGL documentation uses **reStructuredText** (reST), rendered into HTML by Sphinx. You will find the following is a fairly-complete list of references about how to do things using reStructuredText:
A page is a `.mdx` file with YAML frontmatter:
| Docutils References (Fundamentals) | Sphinx References (What Sphinx Adds to Docutils) |
| --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| [Introduction](https://docutils.sourceforge.io/docs/ref/rst/introduction.html) | [Configuration](https://www.sphinx-doc.org/en/master/usage/configuration.html) |
| [Markup Specification](https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html) | [Directives](https://www.sphinx-doc.org/en/master/usage/restructuredtext/directives.html) |
| [Markup Specification ∙ Tables](https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#tables) | [Directives ∙ Admonitions](https://www.sphinx-doc.org/en/master/usage/restructuredtext/directives.html#admonitions-messages-and-warnings) |
| [Markup Specification ∙ Substitution References](https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#substitution-references) | [Referencing](https://www.sphinx-doc.org/en/master/usage/referencing.html) |
| [Directives](https://docutils.sourceforge.io/docs/ref/rst/directives.html) | [Interpreted Text Roles](https://www.sphinx-doc.org/en/master/usage/restructuredtext/roles.html) |
| [Interpreted Text Roles](https://docutils.sourceforge.io/docs/ref/rst/roles.html) | [Glossary](https://www.sphinx-doc.org/en/master/glossary.html) |
| [Quickstart](https://docutils.sourceforge.io/docs/user/rst/quickstart.html) | [Furo-theme Examples](https://sphinx-themes.org/sample-sites/furo/) |
| [Examples](https://docutils.sourceforge.io/docs/user/rst/demo.html) | [Kitchen Sink Page](https://sphinx-themes.org/sample-sites/furo/kitchen-sink/) |
| [Quick Reference](https://docutils.sourceforge.io/docs/user/rst/quickref.html) | [Sphinx Themes Gallery](https://sphinx-themes.org/) |
```mdx
---
title: Animations
description: Animate widget properties over time with the LVGL animation engine.
---
If you prefer to learn by examples, the [Furo-theme Examples](https://sphinx-themes.org/sample-sites/furo/) and especially the [Kitchen Sink Page](https://sphinx-themes.org/sample-sites/furo/kitchen-sink/) are excellent resources. View the `.rst` source file that generated that page by clicking the "eye" icon at the top of the page.
# Animations
Note: the section headings in these pages use a different convention than the one presented below. For LVGL documentation, use the [section-heading convention presented below](https://github.com/lvgl/lvgl/tree/master/docs#section-headings).
Animations change a property's value over a period of time...
### Text Format
Please wrap the text around column 86 or narrower. Wrapping at *exactly* column 86 is not important, but readability and ease of editing is.
Indent using 4 spaces (not tab characters). This applies to code blocks as well.
### index.rst Files
If you create a new directory you will need 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/src/common-widget-features/layouts` directory.
```rst
.. _layouts:
=======
Layouts
=======
.. toctree::
:maxdepth: 2
overview
flex
grid
<LvglExample name="lv_example_anim_1" path="anim/lv_example_anim_1" />
```
Explanation:
## Common Components
```rst
.. _layouts: <=== Creates an explicit link target
<=== Empty line -- important!
=======
Layouts <=== Document title, seen in documentation
=======
A small set of components covers most authoring needs:
<=== any text introducing this topic and the TOC below
- `<Callout type="info|warn|error" title="...">` — notes, warnings, tips.
- `<LvglExample name="..." path="..." />` — embed a runnable code example.
- `<ApiLink name="lv_label_create" />` — inline link to an API symbol.
- `<ApiLinkList items={["lv_label"]} />` — link out to related API pages at the end of a page.
- `<Figure src="/_static/images/..." alt="..." caption="..." />` — images with captions.
- `<DirectoryIndex />` — auto-generated list of child pages on index pages.
.. toctree:: <=== Table of contents directive
:maxdepth: 2 <=== Internal use and needs to always be set this way
The **full component catalog**, formatting rules, `meta.json` examples, and icon list live in [Writing Docs](./src/contributing/writing_docs.mdx). Start there for anything beyond the components above.
overview <=== relative path to .rst files located in the same directory
flex
grid
```
## More on Contributing
### Section Headings
Section headings are created by underlining the section title with a punctuation character, at least as long as the text. Example:
```
This Is a Heading
*****************
```
Use these conventions for section headings in LVGL documentation:
```
==============
Document Title
==============
Section
*******
Sub Section
-----------
Sub Sub Section
~~~~~~~~~~~~~~~
```
1. ``====``, ``****``, ``----`` are encouraged.
2. ``~~~~`` only if really needed.
Being consistent about this helps the reST parser to format the tables of contents correctly.
For improved readability in text editors:
- place 3 blank lines above the 2nd and subsequent "Section" titles (see above),
- 2 blank lines above "Sub Section" headings, and
- 1 at least blank line above all lower-level section headings.
### Italics, Boldface and Underlining
Emphasis using `*italics*`. Emphasis using `**boldface**`.
Normally underlining and combining these text styles is not possible in reStructuredText. However, LVGL documentation provides a work-around for this using reST <u>Interpreted Text Roles</u>. Just remember that the Interpreted Text Role names combine the letters `i`, `b` and `u` to provide the desired combination. All possible permutations of these letters are supported so you do not have to remember what sequence works. Examples: ``:u:`underline` ``, ``:ub:`underline and bold` ``, `` :bi:`bold italic` ``.
### Code Blocks
* Indent using 4 spaces (not tab characters).
* 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. Do not use lone `::`, `:code:` or `.. code::`.
* Specify the language after the directive for appropriate syntax highlighting. Examples:
- `.. code-block:: c`,
- `.. code-block:: cpp`,
- `.. code-block:: python`,
- `.. code-block:: shell`,
- `.. code-block:: bash`,
- `.. code-block:: kconfig`,
- `.. code-block:: json`,
- `.. code-block:: yaml`,
- `.. code-block:: dot` (graphviz),
- `.. code-block:: html`,
- `.. code-block:: css`,
- `.. code-block:: xml`,
- `.. code-block:: make`.
See [the full set of supported code lexers](https://pygments.org/docs/lexers/) for more details.
### Bulleted Lists
```rst
- 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 multiple paragraphs and/or code blocks under a
list item, it must be intended to align with the list item like this:
Second paragraph.
.. code-block: python
<=== blank line here is important
# Python code here
<=== 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 for the reST parser to process it correctly.
### External Links
URLs are converted to links automatically. E.g. `Visit https://lvgl.io`.
To add links with custom link text use
```rst
Visit `My Website <https://pro.lvgl.io>`__.
```
If an external link will be used on many pages:
- Add it to `./docs/src/include/external_links.txt` if not already there. Example:
```rst
.. _LVGL Pro: https://pro.lvgl.io
```
- `.. include: /include/external_links.txt` once at the top of each `.rst` file that uses it.
- Use it by name in text:
```rst
For further details see `LVGL Pro`_.
```
Note: back-quotes are not needed if there are no spaces in the name.
### Internal Links
Add a link target (anchor) before heading or paragraph that will be linked to:
```rst
.. _unique_anchor_name:
My Heading
**********
```
`unique_anchor_name` must be unique throughout all `.rst` files under `./docs/src/`.
Reference the link (anchor) by:
```rst
Click :ref:`here <unique_anchor_name>` for more details.
```
Result: "Click **_here_** for more details."
Or use the heading's text as the link's text:
```rst
Click :ref:`unique_anchor_name` for more details.
```
Result: "Click **_My Heading_** for more details."
`unique_anchor_name` may appear in places other than before a heading, but if so, custom link text (like "here" the first example above) must be provided.
### Tightening Tables
reStructuredText syntax for creating tables can be found in the [reST examples](https://sphinx-themes.org/sample-sites/furo/kitchen-sink/tables/) referred to above. (Click on the "eye" icon to see the source file.)
Very long or very wide tables can be difficult to read and use. To squeeze them down to make them more readable and usable, move your existing table under a `.. container:: tighter-table-N` directive (`N` = digit 1-7 with 7 being the tightest), and indent your table to make it the "content" of the directive. Example:
```rst
.. container:: tighter-table-3
+-----------+--------------+--------------+--------+
| Heading 1 | Heading 2 | Heading 3 | Hdg 4 |
+===========+==============+==============+========+
| row 1 c 1 | row 1 col 3 | row 1 col 3 | r1 c4 |
+-----------+--------------+--------------+--------+
| row 2 c 1 | row 2 col 3 | row 2 col 3 | r2 c4 |
+-----------+--------------+--------------+--------+
| row 3 c 1 | row 3 col 3 | row 3 col 3 | r3 c4 |
+-----------+--------------+--------------+--------+
```
This works for all types of tables.
### Special Symbols
Because not everyone has editors that deal with Unicode characters well, please reST substitutions to insert special characters into documentation. A list of the most commonly-used special symbols can be found in `./docs/src/include/substitutions.txt`. To use one of these, add this line at the top of the `.rst` file if it is not already there:
```rst
.. include:: /include/substitutions.txt
```
Then, any of those substitutions can be used in that `.rst` file. Example:
```rst
The temperature outside is 20\ |deg|\ C.
```
Result: "The temperature outside is 20°C."
The spaces surrounding substitutions *are required for parsing*, but when you need to remove them in the output (as in the example above), do so by escaping them with the `\` character. Exception: the `substitutions.txt` file contains 3 substitution definitions which are marked with the `:trim:` option since their use *always* removes these spaces in the output. These do not need this escaping:
- `|nbsp|` (non-breaking space),
- `|shy|` (soft hyphen), and
- `|nbhyph|` (non-breaking hyphen used in titles and official names)
If you need a substitution that is not already in `substitutions.txt`, please add it.
### Referencing API Documentation
Using the following generates links to API documentation that the reader can click directly in the text.
#### In-Line Code Expressions
Use the following Interpreted Text Roles in text to include in-line C code that links to documentation on that symbol when available:
:cpp:func:`lv_init` (note there are no parentheses after the function name)
:c:macro:`LV_USE_FLEX`
:cpp:type:`lv_event_t`
:cpp:enum:`lv_state_t`
:cpp:enumerator:`LV_STATE_CHECKED`
:cpp:struct:`lv_image_dsc_t`
:cpp:union:`lv_style_value_t`
**Note:** Doxygen documentation for macros with parameters works perfectly, but Sphinx does not yet know how to parse the parameter(s) on a macro that might look like this:
```rst
:c:macro:`LV_FREETYPE_FONT_STYLE_WEIGHT(x)`
```
Here is how to make that work, look right in the document, and also produce a link to Doxygen documentation if there is any:
```rst
:c:macro:`LV_FREETYPE_FONT_STYLE_WEIGHT`\ (x)
```
#### More Complex Expressions
Use the `:cpp:expr:` Interpreted Text Role for more complex expressions, 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 contain more than one word or non-alphanumeric characters will cause the `:cpp:expr:` interpreted-text to fail. Examples:
| Expression | Cause of Failure |
| ------------------------------------------------------------ | ---------------------- |
| :cpp:expr:\`lv_obj_set_layout(widget, LV_LAYOUT_FLEX/GRID)\` | argument 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);\` | semicolon |
| :cpp:expr:\`lv_obj_set_layout(widget, ...)\` | lone ellipsis |
For such examples, simply use reStructuredText literal markup like this:
```rst
``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, ...)``
```
#### Providing Links to API Pages
To create a link to 1 or more API pages, set up a section at the end of your `.rst` file that looks like this, and use one or both types of the ``.. API `` pseudo-directives below:
```rst
API
***
.. API equals: lv_scale_t, lv_scale_create
.. API startswith:
lv_scale
lv_obj_set_style
```
The list of symbols (or prefixes) can be separated by commas or spaces, and they can wrap onto subsequent lines of text so long as they are indented. Each list is terminated by the next ``.. API `` pseudo-directive or end-of-file, whichever comes first.
The API-page generation logic will add at most 1 link to each API documentation page containing matched symbols. The links are to the whole API page, not to the symbols. The purpose is to provide the reader with links to applicable API pages. Links directly to code (e.g. function documentation) are accomplished by using the In-Line Code Expressions documented above.
For coding style, DCO, pull requests, and other contribution guidelines, see the pages under [`./src/contributing/`](./src/contributing).
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 186 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 519 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 312 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

-50
View File
@@ -1,50 +0,0 @@
#!/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)
-123
View File
@@ -1,123 +0,0 @@
"""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', 'is_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
def is_silent_mode(mode=True):
global _announce_silent_mode
return _announce_silent_mode
File diff suppressed because it is too large Load Diff
-788
View File
File diff suppressed because it is too large Load Diff
-76
View File
@@ -1,76 +0,0 @@
"""
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__)
dest_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+(LV_(?:USE|FONT)_\w+)\s+(\b0\b)')
leave_disabled_list = [
'LV_USE_PROFILER',
'LV_USE_DRAW_ARM2D_SYNC',
'LV_USE_NATIVE_HELIUM_ASM',
]
def run(output_cfg_path=None):
global dest_config
enable_content_macro_processed = False
os.chdir(base_path)
if output_cfg_path is not None:
dest_config = output_cfg_path
with open(src_config, 'r') as f:
data = f.read()
lines = data.split('\n')
for i, line in enumerate(lines):
if not enable_content_macro_processed:
if line.startswith('#if 0'):
line = line.replace('#if 0', '#if 1')
lines[i] = line
enable_content_macro_processed = True
else:
match = disabled_option_re.search(line)
if match:
# Except for these...
if match[1] in leave_disabled_list:
continue
else:
# ...replace '0' with '1' without altering any other part of line.
# Set `j` to index where '0' was found.
j = match.regs[2][0]
# Surgically insert '1' in place of '0'. Strings are immutable.
line = line[:j] + '1' + line[j + 1:]
lines[i] = line
data = '\n'.join(lines)
with open(dest_config, 'w') as f:
f.write(data)
def cleanup():
if os.path.exists(dest_config):
os.remove(dest_config)
if __name__ == '__main__':
"""Make module importable as well as run-able."""
run()
-429
View File
@@ -1,429 +0,0 @@
"""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
-1756
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
-80
View File
@@ -1,80 +0,0 @@
@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 src\lvgl_version.py') do set VER=%%v
echo VERSION [!VER!]
rem set SPHINXOPTS=-D version="!VER!" -j 4
set SPHINXOPTS=-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=
-19
View File
@@ -1,19 +0,0 @@
Sphinx<=8.2.3
breathe
imagesize
importlib-metadata
sphinx-sitemap
sphinxcontrib-applehelp
sphinxcontrib-devhelp
sphinxcontrib-htmlhelp
sphinxcontrib-jsmath
sphinxcontrib-qthelp
sphinxcontrib-serializinghtml
sphinxcontrib-mermaid==0.9.2
sphinx-copybutton
sphinx-design
typing-extensions
sphinx-reredirects
dirsync
furo
accessible-pygments
-17
View File
@@ -1,17 +0,0 @@
.. _api placeholder:
***************
API Placeholder
***************
This document is here merely as a placeholder so it's LACK of presence when
building the docs like this:
.. code-block:: bash
$ python build.py html skip_api
(which is done in a doc-development/testing environment) does not generate a warning
because this file is not there. When the API portion of the documents DOES get
built, this file is replaced with one programmatically generated by ``build.py``
making calls on ``api_doc_builder.py`` and ``doxygen_xml.py`` modules.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-60
View File
@@ -1,60 +0,0 @@
# 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
View File
@@ -1,133 +0,0 @@
import os
from docutils import nodes
from docutils.parsers.rst import Directive, directives
# from docutils.parsers.rst.directives.images import Image
# from sphinx.directives.code import LiteralInclude
def excluded_list(argument):
return argument.split(',')
class LvExample(Directive):
required_arguments = 1
option_spec = {
'excluded_languages': excluded_list,
'language': directives.unchanged,
'description': directives.unchanged
}
def get_example_code_path(self, example_path, language):
base_path = os.path.dirname(__file__)
examples_path = os.path.abspath(os.path.join(base_path, '..', 'examples'))
example_path = os.path.join(examples_path, example_path + '.' + language)
return example_path
def human_language_name(self, language):
if language == 'py':
return 'MicroPython'
elif language == 'c':
return 'C'
else:
return language
def github_path(self, example_path, language):
env = self.state.document.settings.env
return f"https://github.com/lvgl/lvgl/blob/{env.config.repo_commit_hash}/examples/{example_path}.{language}"
def embed_code(self, example_file, example_path, language, buttons={}):
toggle = nodes.container('', literal_block=False, classes=['toggle'])
header = nodes.container('', literal_block=False, classes=['header'])
toggle.append(header)
try:
with open(example_file, 'rb') as f:
contents = f.read().decode('utf-8')
except FileNotFoundError:
print('File Not Found', example_file)
contents = 'Error encountered while trying to open ' + example_file
literal_list = nodes.literal_block(contents, contents)
literal_list['language'] = language
toggle.append(literal_list)
paragraph_node = nodes.raw(text=f"<p>{self.human_language_name(language)} code &nbsp;</p>", format='html')
for text, url in buttons.items():
paragraph_node.append(nodes.raw(text=f"<a class='lv-example-link-button' onclick=\"event.stopPropagation();\" href='{url}'>{text}</a>", format='html'))
header.append(paragraph_node)
return toggle
def run(self):
example_path = self.arguments[0]
example_name = os.path.split(example_path)[1]
excluded_languages = self.options.get('excluded_languages', [])
node_list = []
env = self.state.document.settings.env
iframe_html = ""
c_path = self.get_example_code_path(example_path, 'c')
py_path = self.get_example_code_path(example_path, 'py')
if os.path.exists(c_path):
c_code = self.embed_code(c_path, example_path, 'c', buttons={
'<i class="fa fa-github"></i>&nbsp;View on GitHub': self.github_path(example_path, 'c')
})
else:
c_code = None
if os.path.exists(py_path):
py_code = self.embed_code(py_path, example_path, 'py', buttons={
'<i class="fa fa-github"></i>&nbsp;View on GitHub': self.github_path(example_path, 'py'),
'<i class="fa fa-play"></i>&nbsp;MicroPython Simulator': f"https://sim.lvgl.io/v{env.config.version}/micropython/ports/javascript/index.html?script_startup=https://raw.githubusercontent.com/lvgl/lvgl/{env.config.repo_commit_hash}/examples/header.py&script=https://raw.githubusercontent.com/lvgl/lvgl/{env.config.repo_commit_hash}/examples/{example_path}.py"
})
else:
py_code = None
if 'c' not in excluded_languages:
if env.app.tags.has('html'):
iframe_html = f"<div class='lv-example' data-real-src='/{env.config.version}/_static/built_lv_examples/index.html?example={example_name}&w=320&h=240'></div>"
description_html = f"<div class='lv-example-description'>{self.options.get('description', '')}</div>"
layout_node = nodes.raw(text=f"<div class='lv-example-container'>{iframe_html}{description_html}</div>", format='html')
node_list.append(layout_node)
if 'c' not in excluded_languages and c_code is not None:
node_list.append(c_code)
if 'py' not in excluded_languages and py_code is not None:
node_list.append(py_code)
trailing_node = nodes.raw(text=f"<hr/>", format='html')
node_list.append(trailing_node)
return node_list
def setup(app):
app.add_directive("lv_example", LvExample)
# Direct [View on GitHub] links in examples to use current
# branch (stored in LVGL_GITCOMMIT environment variable) instead
# of the current commit hash as was being done previously.
# Default to 'master' if Sphinx is being run outside of `build.py`.
# Resulting example link:
# [https://github.com/lvgl/lvgl/blob/master/examples/anim/lv_example_anim_1.c].
# [https://github.com/lvgl/lvgl/blob/v8.4.0/examples/anim/lv_example_anim_1.c].
# [https://github.com/lvgl/lvgl/blob/v9.2.0/examples/anim/lv_example_anim_1.c].
if 'LVGL_GITCOMMIT' in os.environ:
git_commit = os.environ['LVGL_GITCOMMIT']
else:
git_commit = 'master'
app.add_config_value("repo_commit_hash", git_commit, "env")
# if 'repo_commit_hash' in app.config._options:
# print(f"repo_commit_hash from lv_example.py: [{app.config._options['repo_commit_hash']}]")
# else:
# print("repo_commit_hash not found in [app.config._options] at this time.")
return {
'version': '0.1',
'parallel_read_safe': True,
'parallel_write_safe': True,
}
@@ -1,23 +0,0 @@
{% extends "furo/components/view-this-page.html" %}
{% from "basic-ng/components/view-this-page.html" import determine_page_view_link with context %}
{#- Only this block is redefined (overridden) from furo/components/view-this-page.html.
All else remains intact. -#}
{% block link_available -%}
{#- Ensure `page_source_suffix` exists, else `determine_page_view_link()` fails
during generation of the `genindex` page at the end. -#}
{%- if page_source_suffix -%}
{%- set page_link = determine_page_view_link() -%}
{%- if "docs/src/API/" in page_link -%}
{#- Adjust page_link from fictional API pages to point to real .h files. -#}
{%- set page_link = page_link.replace("docs/src/API/", "src/") -%}
{%- set page_link = page_link.replace("_h.rst", ".h") -%}
{%- set page_link = page_link.replace("?plain=true", "") -%}
{#- Redirect "src/lv_conf.h" => "lv_conf_template.h" -#}
{%- if page_link.endswith("/src/lv_conf.h") -%}
{%- set page_link = page_link.replace("/src/lv_conf.h", "/lv_conf_template.h") -%}
{%- endif -%}
{%- endif -%}
{{ furo_view_button(page_link) }}
{%- endif -%}
{%- endblock %}
-202
View File
@@ -1,202 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" style="display: none">
<symbol id="svg-toc" width="20" height="20" viewBox="0 0 16 16">
<title>Contents</title>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M9.5 14H4C2.89543 14 2 13.1046 2 12V4C2 2.89543 2.89543 2 4 2H9.5V14ZM12 2C13.1046 2 14 2.89543 14 4V12C14 13.1046 13.1046 14 12 14H10.5V2H12ZM7.30664 5.90137C7.01379 5.60882 6.53889 5.60884 6.24609 5.90137L4.40039 7.74805L4.34863 7.80566C4.10869 8.10016 4.12612 8.53408 4.40039 8.80859L6.24316 10.6533L6.30078 10.7051C6.59511 10.9451 7.02907 10.9283 7.30371 10.6543C7.57829 10.3799 7.59634 9.94504 7.35645 9.65039L7.30469 9.59277L5.99121 8.27832L7.30664 6.96289C7.59937 6.66997 7.59942 6.19418 7.30664 5.90137Z"
fill="currentColor" />
</svg>
</symbol>
<symbol id="svg-menu" width="20" height="20" viewBox="0 0 16 16">
<title>Menu</title>
<svg
width="16"
height="16"
viewBox="0 0 16 16"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M2.5 4H13.5" />
<path d="M2.5 8.5H13.5" />
<path d="M2.5 13H13.5" />
</svg>
</symbol>
<symbol id="svg-arrow-right" width="16" height="16" viewBox="0 0 16 16">
<title>Expand</title>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M7.00001 11L9.99999 8L6.99999 5.00001"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round" />
</svg>
</symbol>
<symbol id="svg-sun" width="20" height="20" viewBox="0 0 16 16">
<title>Light mode</title>
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<circle cx="8" cy="8" r="3" />
<path
d="M7.25 1.75C7.25 1.33579 7.58579 1 8 1C8.41421 1 8.75 1.33579 8.75 1.75V3.25C8.75 3.66421 8.41421 4 8 4C7.58579 4 7.25 3.66421 7.25 3.25V1.75Z" />
<path
d="M7.25 12.75C7.25 12.3358 7.58579 12 8 12C8.41421 12 8.75 12.3358 8.75 12.75V14.25C8.75 14.6642 8.41421 15 8 15C7.58579 15 7.25 14.6642 7.25 14.25V12.75Z" />
<path
d="M1.75 8.75C1.33579 8.75 1 8.41421 1 8C1 7.58579 1.33579 7.25 1.75 7.25L3.25 7.25C3.66421 7.25 4 7.58579 4 8C4 8.41421 3.66421 8.75 3.25 8.75L1.75 8.75Z" />
<path
d="M12.75 8.75C12.3358 8.75 12 8.41421 12 8C12 7.58579 12.3358 7.25 12.75 7.25L14.25 7.25C14.6642 7.25 15 7.58579 15 8C15 8.41421 14.6642 8.75 14.25 8.75L12.75 8.75Z" />
<path
d="M3.05026 4.11091C2.75736 3.81802 2.75736 3.34314 3.05026 3.05025C3.34315 2.75736 3.81803 2.75736 4.11092 3.05025L5.17158 4.11091C5.46447 4.40381 5.46447 4.87868 5.17158 5.17157C4.87869 5.46447 4.40381 5.46447 4.11092 5.17157L3.05026 4.11091Z" />
<path
d="M10.8284 11.8891C10.5355 11.5962 10.5355 11.1213 10.8284 10.8284C11.1213 10.5355 11.5962 10.5355 11.8891 10.8284L12.9498 11.8891C13.2426 12.182 13.2426 12.6569 12.9498 12.9497C12.6569 13.2426 12.182 13.2426 11.8891 12.9497L10.8284 11.8891Z" />
<path
d="M11.8891 3.05024C12.182 2.75734 12.6568 2.75734 12.9497 3.05024C13.2426 3.34313 13.2426 3.818 12.9497 4.1109L11.8891 5.17156C11.5962 5.46445 11.1213 5.46445 10.8284 5.17156C10.5355 4.87866 10.5355 4.40379 10.8284 4.1109L11.8891 3.05024Z" />
<path
d="M4.11089 10.8284C4.40378 10.5355 4.87866 10.5355 5.17155 10.8284C5.46444 11.1213 5.46444 11.5962 5.17155 11.8891L4.11089 12.9497C3.818 13.2426 3.34312 13.2426 3.05023 12.9497C2.75734 12.6568 2.75734 12.182 3.05023 11.8891L4.11089 10.8284Z" />
</svg>
</symbol>
<symbol id="svg-moon" width="20" height="20" viewBox="0 0 16 16">
<title>Dark mode</title>
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path
d="M12.5446 11.9176C12.8733 11.5367 12.5031 11 12 11C8.68629 11 6 8.3137 6 4.99999C6 4.72807 6.01809 4.46037 6.05313 4.19803C6.16641 3.34993 5.44091 2.50445 4.72444 2.97218C3.08409 4.04303 2 5.89491 2 8C2 11.3137 4.68629 14 8 14C9.81644 14 11.4443 13.1928 12.5446 11.9176Z"
fill="currentColor" />
</svg>
</symbol>
<symbol id="svg-moon-with-sun" width="20" height="20" viewBox="0 0 16 16">
<title>System, in dark mode</title>
<svg
width="16"
height="16"
viewBox="0 0 16 16"
stroke="currentColor"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg">
<path
d="M4.5 3.5H11.5C12.3283 3.5 13 4.17146 13 5V8C13 8.82843 12.3284 9.5 11.5 9.5H4.5C3.67157 9.5 3 8.82843 3 8V5C3 4.17145 3.6717 3.5 4.5 3.5Z" />
<path
d="M6.75 11.5H9.25C9.66409 11.5 10 11.8357 10 12.25C10 12.6642 9.66421 13 9.25 13H6.75C6.33579 13 6 12.6642 6 12.25C6 11.8357 6.33591 11.5 6.75 11.5Z" />
</svg>
</symbol>
<symbol id="svg-sun-with-moon" width="20" height="20" viewBox="0 0 16 16">
<title>System, in light mode</title>
<svg
width="16"
height="16"
viewBox="0 0 16 16"
stroke="currentColor"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg">
<path
d="M4.5 3.5H11.5C12.3283 3.5 13 4.17146 13 5V8C13 8.82843 12.3284 9.5 11.5 9.5H4.5C3.67157 9.5 3 8.82843 3 8V5C3 4.17145 3.6717 3.5 4.5 3.5Z" />
<path
d="M6.75 11.5H9.25C9.66409 11.5 10 11.8357 10 12.25C10 12.6642 9.66421 13 9.25 13H6.75C6.33579 13 6 12.6642 6 12.25C6 11.8357 6.33591 11.5 6.75 11.5Z" />
</svg>
</symbol>
<symbol id="svg-pencil" width="20" height="20" viewBox="0 0 24 24">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1"
stroke-linecap="round"
stroke-linejoin="round"
class="icon-tabler-pencil-code">
<path d="M4 20h4l10.5 -10.5a2.828 2.828 0 1 0 -4 -4l-10.5 10.5v4" />
<path d="M13.5 6.5l4 4" />
<path d="M20 21l2 -2l-2 -2" />
<path d="M17 17l-2 2l2 2" />
</svg>
</symbol>
<symbol id="svg-eye" width="20" height="20">
<title>View documentation source on GitHub</title>
<svg width="20" height="20" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M8 1C4.1325 1 1 4.21174 1 8.17704C1 11.3529 3.00375 14.0354 5.78625 14.9863C6.13625 15.0491 6.2675 14.8338 6.2675 14.6454C6.2675 14.4749 6.25875 13.9098 6.25875 13.3087C4.5 13.6406 4.045 12.8691 3.905 12.4654C3.82625 12.259 3.485 11.6221 3.1875 11.4516C2.9425 11.317 2.5925 10.9851 3.17875 10.9761C3.73 10.9672 4.12375 11.4965 4.255 11.7118C4.885 12.7973 5.89125 12.4923 6.29375 12.3039C6.355 11.8374 6.53875 11.5234 6.74 11.3439C5.1825 11.1645 3.555 10.5455 3.555 7.80027C3.555 7.01976 3.82625 6.37383 4.2725 5.87143C4.2025 5.692 3.9575 4.95636 4.3425 3.96951C4.3425 3.96951 4.92875 3.78111 6.2675 4.70516C6.8275 4.54368 7.4225 4.46293 8.0175 4.46293C8.6125 4.46293 9.2075 4.54368 9.7675 4.70516C11.1062 3.77214 11.6925 3.96951 11.6925 3.96951C12.0775 4.95636 11.8325 5.692 11.7625 5.87143C12.2087 6.37383 12.48 7.01079 12.48 7.80027C12.48 10.5545 10.8438 11.1645 9.28625 11.3439C9.54 11.5682 9.75875 11.9988 9.75875 12.6717C9.75875 13.6316 9.75 14.4032 9.75 14.6454C9.75 14.8338 9.88125 15.0581 10.2312 14.9863C11.6209 14.5053 12.8284 13.5896 13.6839 12.3681C14.5393 11.1466 14.9996 9.68085 15 8.17704C15 4.21174 11.8675 1 8 1Z"
fill="currentColor" />
</svg>
</symbol>
<symbol id="svg-home-intro" width="32" height="32" viewBox="0 0 32 32">
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M26 2C28.2091 2 30 3.79086 30 6V26C30 28.2091 28.2091 30 26 30H6C3.79086 30 2 28.2091 2 26V6C2 3.79086 3.79086 2 6 2H26ZM8.00781 18.5C6.91797 18.5 6 19.1719 6 19.9336C6 20.2812 6.26562 20.5312 6.64062 20.5312C6.90234 20.5312 7.07422 20.4297 7.3125 20.1328C7.51953 19.875 7.6875 19.7812 7.92188 19.7812C8.22656 19.7812 8.4375 19.9766 8.4375 20.2656C8.4375 20.5234 8.25781 20.7734 7.85156 21.1562L6.65625 22.2812C6.17578 22.7305 6.00781 22.9883 6.00781 23.2773C6.00781 23.6602 6.30078 23.9219 6.73047 23.9219H9.45312C9.91406 23.9219 10.1875 23.6875 10.1875 23.293C10.1875 22.8984 9.90625 22.6562 9.45312 22.6562H8.14453V22.5938L9.06641 21.7734C9.6875 21.2227 9.99219 20.6758 9.99219 20.1016C9.99219 19.1836 9.14453 18.5 8.00781 18.5ZM13 20C12.4477 20 12 20.4477 12 21C12 21.5523 12.4477 22 13 22H24C24.5523 22 25 21.5523 25 21C25 20.4477 24.5523 20 24 20H13ZM13 15C12.4477 15 12 15.4477 12 16C12 16.5523 12.4477 17 13 17H24C24.5523 17 25 16.5523 25 16C25 15.4477 24.5523 15 24 15H13ZM8.23438 8.5C7.91016 8.5 7.55078 8.60938 7.21484 8.8125L6.66406 9.14453C6.39062 9.30859 6.25 9.51562 6.25 9.75391C6.25 10.0742 6.47656 10.3008 6.79688 10.3008C6.95312 10.3008 7.07812 10.2539 7.33594 10.1016L7.55469 9.97266H7.5625V13.0625C7.5625 13.6172 7.85547 13.9297 8.37891 13.9297C8.89844 13.9297 9.1875 13.6211 9.1875 13.0625V9.47656C9.1875 8.88281 8.8125 8.5 8.23438 8.5ZM13 10C12.4477 10 12 10.4477 12 11C12 11.5523 12.4477 12 13 12H24C24.5523 12 25 11.5523 25 11C25 10.4477 24.5523 10 24 10H13Z"
fill="currentColor" />
</svg>
</symbol>
<symbol id="svg-home-getting-started" width="32" height="32" viewBox="0 0 32 32">
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M24 2C26.2091 2 28 3.79086 28 6V20.4941C27.9999 21.4443 27.5615 22.3414 26.8115 22.9248C25.1702 24.2014 25.2396 26.7041 26.9492 27.8877L27.5947 28.335C28.3308 28.8451 27.97 29.9999 27.0742 30H8C5.79086 30 4 28.2091 4 26V6C4 3.79086 5.79086 2 8 2H24ZM8 23C6.89543 23 6 23.8954 6 25V26C6 27.1046 6.89543 28 8 28H23C23.5523 28 24 27.5523 24 27V24C24 23.4477 23.5523 23 23 23H8ZM8 5C7.44772 5 7 5.44772 7 6V19C7 19.5523 7.44772 20 8 20C8.55228 20 9 19.5523 9 19V6C9 5.44772 8.55228 5 8 5ZM17.5205 8.60059C16.5361 8.60059 16.0029 9.04492 15.6338 10.1523L13.5693 16.2021C13.4121 16.6602 13.3506 16.9199 13.3506 17.1455C13.3506 17.6924 13.7744 18.0752 14.3896 18.0752C14.9912 18.0752 15.3125 17.7676 15.5381 16.9746L15.9004 15.8604H19.0381L19.3936 16.9746C19.6191 17.7471 19.9678 18.0752 20.5693 18.0752C21.2188 18.0752 21.6426 17.6924 21.6426 17.0977C21.6426 16.8584 21.5811 16.585 21.458 16.2021L19.4004 10.1523C19.0244 9.03809 18.5049 8.60059 17.5205 8.60059ZM17.5342 10.6924L18.5801 14.2266H16.3652L17.4248 10.6924H17.5342Z"
fill="currentColor" />
<path
d="M6 25C6 23.8954 6.89543 23 8 23L23 23C23.5523 23 24 23.4477 24 24V27C24 27.5523 23.5523 28 23 28L8 28C6.89543 28 6 27.1046 6 26L6 25Z"
fill="currentColor"
fill-opacity="0.1" />
<path
d="M6 25C6 23.8954 6.89543 23 8 23L23 23C23.5523 23 24 23.4477 24 24V27C24 27.5523 23.5523 28 23 28L8 28C6.89543 28 6 27.1046 6 26L6 25Z"
fill="currentColor"
fill-opacity="0.1" />
</svg>
</symbol>
<symbol id="svg-home-play" width="32" height="32" viewBox="0 0 32 32">
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M16 31C24.2843 31 31 24.2843 31 16C31 7.71573 24.2843 1 16 1C7.71573 1 1 7.71573 1 16C1 24.2843 7.71573 31 16 31ZM14.5932 10.1739C13.9329 9.68743 13 10.1588 13 10.979V20.8889C13 21.7327 13.9806 22.1972 14.6335 21.6626L21.0041 16.4469C21.509 16.0335 21.4892 15.2552 20.9638 14.8681L14.5932 10.1739Z"
fill="currentColor" />
</svg>
</symbol>
<symbol id="svg-home-integration" width="32" height="32" viewBox="0 0 32 32">
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M10 5C10 5.55228 10.4477 6 11 6C11.5523 6 12 5.55228 12 5V4H15V5C15 5.55228 15.4477 6 16 6C16.5523 6 17 5.55228 17 5V4H20V5C20 5.55228 20.4477 6 21 6C21.5523 6 22 5.55228 22 5V4H23C25.7614 4 28 6.23858 28 9V10H27C26.4477 10 26 10.4477 26 11C26 11.5523 26.4477 12 27 12H28V15H27C26.4477 15 26 15.4477 26 16C26 16.5523 26.4477 17 27 17H28V20H27C26.4477 20 26 20.4477 26 21C26 21.5523 26.4477 22 27 22H28V23C28 25.7614 25.7614 28 23 28H22V27C22 26.4477 21.5523 26 21 26C20.4477 26 20 26.4477 20 27V28H17V27C17 26.4477 16.5523 26 16 26C15.4477 26 15 26.4477 15 27V28H12V27C12 26.4477 11.5523 26 11 26C10.4477 26 10 26.4477 10 27V28H9C6.23858 28 4 25.7614 4 23V22H5C5.55228 22 6 21.5523 6 21C6 20.4477 5.55228 20 5 20H4V17H5C5.55228 17 6 16.5523 6 16C6 15.4477 5.55228 15 5 15H4V12H5C5.55228 12 6 11.5523 6 11C6 10.4477 5.55228 10 5 10H4V9C4 6.23858 6.23858 4 9 4H10V5ZM13 10C11.3431 10 10 11.3431 10 13V19C10 20.6569 11.3431 22 13 22H19C20.6569 22 22 20.6569 22 19V13C22 11.3431 20.6569 10 19 10H13Z"
fill="currentColor" />
<rect opacity="0.2" x="10" y="10" width="12" height="12" rx="3" fill="currentColor" />
<g opacity="0.4">
<rect x="12" y="2" width="4" height="2" rx="1" transform="rotate(90 12 2)" fill="currentColor" />
<rect x="17" y="2" width="4" height="2" rx="1" transform="rotate(90 17 2)" fill="currentColor" />
<rect x="22" y="2" width="4" height="2" rx="1" transform="rotate(90 22 2)" fill="currentColor" />
<rect x="30" y="12" width="4" height="2" rx="1" transform="rotate(-180 30 12)" fill="currentColor" />
<rect x="30" y="17" width="4" height="2" rx="1" transform="rotate(-180 30 17)" fill="currentColor" />
<rect x="30" y="22" width="4" height="2" rx="1" transform="rotate(-180 30 22)" fill="currentColor" />
<rect x="20" y="30" width="4" height="2" rx="1" transform="rotate(-90 20 30)" fill="currentColor" />
<rect x="15" y="30" width="4" height="2" rx="1" transform="rotate(-90 15 30)" fill="currentColor" />
<rect x="10" y="30" width="4" height="2" rx="1" transform="rotate(-90 10 30)" fill="currentColor" />
<rect x="2" y="20" width="4" height="2" rx="1" fill="currentColor" />
<rect x="2" y="15" width="4" height="2" rx="1" fill="currentColor" />
<rect x="2" y="10" width="4" height="2" rx="1" fill="currentColor" />
</g>
</svg>
</symbol>
<symbol id="svg-home-widgets" width="32" height="32" viewBox="0 0 32 32">
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect opacity="0.2" x="2" y="17" width="13" height="13" rx="2" fill="currentColor" />
<path
d="M11.7266 21.2664C11.3622 20.9112 10.7713 20.9112 10.4067 21.2664L7.80008 23.8052L6.59332 22.63C6.22884 22.275 5.63783 22.275 5.27336 22.63C4.90888 22.985 4.90888 23.5606 5.27336 23.9156L7.14001 25.7338C7.5045 26.0888 8.09551 26.0888 8.45998 25.7338L11.7266 22.552C12.0911 22.1968 12.0911 21.6214 11.7266 21.2664Z"
fill="currentColor" />
<rect opacity="0.2" x="17" y="17" width="13" height="13" rx="6.5" fill="currentColor" />
<rect x="19.9375" y="19.9375" width="7" height="7" rx="3.5" fill="currentColor" />
<path
d="M28 2C29.1046 2 30 2.89543 30 4V13C30 14.1046 29.1046 15 28 15H4C2.89543 15 2 14.1046 2 13V4C2 2.89543 2.89543 2 4 2H28ZM8.5 5C6.567 5 5 6.567 5 8.5C5 10.433 6.567 12 8.5 12H14.5C16.433 12 18 10.433 18 8.5C18 6.567 16.433 5 14.5 5H8.5Z"
fill="currentColor" />
</svg>
</symbol>
<symbol id="svg-home-contributing" width="32" height="32" viewBox="0 0 32 32">
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<ellipse opacity="0.6" cx="9.889" cy="11.9469" rx="4.44442" ry="4.63131" fill="currentColor" />
<ellipse cx="21" cy="10.2102" rx="4.99997" ry="5.21023" fill="currentColor" />
<path
d="M30.9999 24.0309C30.9999 27.8304 28.647 26.8974 20.9999 26.8974C13.3529 26.8974 11 27.8304 11 24.0309C11 20.2315 15.4771 16.5781 20.9999 16.5781C26.5228 16.5781 30.9999 20.2315 30.9999 24.0309Z"
fill="currentColor" />
<path
opacity="0.6"
d="M9.88867 17.7363C10.7113 17.7363 11.5076 17.8294 12.2637 17.9971C10.118 19.6601 8.77832 21.9142 8.77832 24.1045C8.77837 25.4207 8.99363 26.3104 9.56055 26.9111C3.02414 26.9364 1.00012 27.6833 1 24.3613C1 20.9841 4.97958 17.7364 9.88867 17.7363Z"
fill="currentColor" />
</svg>
</symbol>
</svg>

Before

Width:  |  Height:  |  Size: 16 KiB

-6
View File
@@ -1,6 +0,0 @@
<form class="sidebar-search-container" method="get" action="{{ pathto('search') }}" role="search">
<input class="sidebar-search" placeholder="{{ _("Search Documentation") }}" name="q" aria-label="{{ _("Search" ) }}">
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
<div id="searchbox"></div>
@@ -1,4 +0,0 @@
<div class="version-selector">
<label for="version_dropdown">Select LVGL version</label>
<select name="version" id="version_dropdown"></select>
</div>
@@ -0,0 +1,201 @@
---
title: File Explorer
description: "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-v..."
---
`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 [Table (lv_table)](/widgets/table) Widget for the "Browsing Area", and the
[List (lv_list)](/widgets/list) Widget for the "Quick-Access" panel when it is enabled. Thus,
<ApiLink name="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 <ApiLink name="LV_USE_LIST" /> must be set to a
non-zero value to use the "Quick-Access" panel.
<Callout type="info">
In order to use File Explorer, [File System (lv_fs_drv)](/main-modules/fs) has to be set up and
know about all the drive letters you use when passing paths to File System
(described below).
</Callout>
## Prerequisites
If you haven't already done so, you will need to learn about the LVGL [File
System abstraction](/main-modules/fs), since it must be set up and be functional
for File Explorer to work.
## Usage
Set <ApiLink name="LV_USE_FILE_EXPLORER" /> to a non-zero value in `lv_conf.h`.
First use <ApiLink name="lv_file_explorer_create" display="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 <ApiLink name="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 <ApiLink name="LV_EXPLORER_SORT_NONE" />)
### Accessing the Parts
This list of functions provides access to the parts shown in diagram above:
- <ApiLink name="lv_file_explorer_get_selected_file_name" display="lv_file_explorer_get_selected_file_name(explorer)" /> (pointer
to NUL-terminated string containing file-path user selected; typically used inside
an <ApiLink name="LV_EVENT_CLICKED" /> event)
- <ApiLink name="lv_file_explorer_get_current_path" display="lv_file_explorer_get_current_path(explorer)" /> (pointer to `current_path` `char` buffer)
- <ApiLink name="lv_file_explorer_get_file_table" display="lv_file_explorer_get_file_table(explorer)" /> (pointer to `File Table` [Table (lv_table)](/widgets/table) Widget)
- <ApiLink name="lv_file_explorer_get_header" display="lv_file_explorer_get_header(explorer)" /> (pointer to `Header` [base widget](/widgets/base_widget) Widget)
- <ApiLink name="lv_file_explorer_get_path_label" display="lv_file_explorer_get_path_label(explorer)" /> (pointer to `Current Path Label` [Label (lv_label)](/widgets/label) Widget)
- <ApiLink name="lv_file_explorer_get_quick_access_area" display="lv_file_explorer_get_quick_access_area(explorer)" /> (pointer to `Quick-Access Panel` [base widget](/widgets/base_widget))
- <ApiLink name="lv_file_explorer_get_places_list" display="lv_file_explorer_get_places_list(explorer)" /> (pointer to `Places List` [List (lv_list)](/widgets/list) Widget)
- <ApiLink name="lv_file_explorer_get_device_list" display="lv_file_explorer_get_device_list(explorer)" /> (pointer to `Device List` [List (lv_list)](/widgets/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 <ApiLink name="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
<ApiLink name="lv_file_explorer_set_quick_access_path" display="lv_file_explorer_set_quick_access_path(explorer, LV_EXPLORER_XXX_DIR, &quot;path&quot;)" />
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:
- <ApiLink name="LV_EXPLORER_HOME_DIR" />
- <ApiLink name="LV_EXPLORER_MUSIC_DIR" />
- <ApiLink name="LV_EXPLORER_PICTURES_DIR" />
- <ApiLink name="LV_EXPLORER_VIDEO_DIR" />
- <ApiLink name="LV_EXPLORER_DOCS_DIR" />
- <ApiLink name="LV_EXPLORER_FS_DIR" />
### Sort
You can use
<ApiLink name="lv_file_explorer_set_sort" display="lv_file_explorer_set_sort(explorer, LV_EXPLORER_SORT_XX)" /> to set
the sorting method.
These are the possible sorting methods:
- <ApiLink name="LV_EXPLORER_SORT_NONE" /> (default)
- <ApiLink name="LV_EXPLORER_SORT_KIND" />
<ApiLink name="lv_file_explorer_get_sort" display="lv_file_explorer_get_sort(explorer)" /> returns the current sorting method.
## Events
- <ApiLink name="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.
- <ApiLink name="LV_EVENT_VALUE_CHANGED" /> Sent once when any item (file) in the
`Browser Panel`'s file list is clicked.
- <ApiLink name="LV_EVENT_CLICKED" /> Sent twice when an item in the `Browser Panel`
is clicked: once as a result of the input-device <ApiLink name="LV_EVENT_RELEASED" />
event and a second as a result of the input device <ApiLink name="LV_EVENT_CLICKED" />
event. This applies to files, directories, and the "&lt; Back" item in the `Browser Panel`.
In these events you can use <ApiLink name="lv_file_explorer_get_current_path" /> to get the
current path and <ApiLink name="lv_file_explorer_get_selected_file_name" /> to get the name
of the currently selected file in the event processing function. For example:
```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 <ApiLink name="strcpy" /> and <ApiLink name="strcat" /> for later use.
## Examples
### Simple File Explorer
<LvglExample name="lv_example_file_explorer_1" path="others/file_explorer/lv_example_file_explorer_1" />
### Control File Explorer
<LvglExample name="lv_example_file_explorer_2" path="others/file_explorer/lv_example_file_explorer_2" />
### Custom sort
<LvglExample name="lv_example_file_explorer_3" path="others/file_explorer/lv_example_file_explorer_3" />
## API
@@ -1,226 +0,0 @@
.. _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
``Browser 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:
Examples
********
.. include:: /examples/others/file_explorer/index.rst
.. _file_explorer_api:
API
***
+83
View File
@@ -0,0 +1,83 @@
---
title: Fragment
description: Fragment is a concept copied from Android.
---
<Callout type="warn">
This module is now deprecated and will be removed in an upcoming release.
</Callout>
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.
## Usage
Enable <ApiLink name="LV_USE_FRAGMENT" /> in `lv_conf.h`.
### Create Fragment Class
```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`
```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
```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);
```
## Examples
### Basic fragment usage
<LvglExample name="lv_example_fragment_1" path="others/fragment/lv_example_fragment_1" />
### Stack navigation example
<LvglExample name="lv_example_fragment_2" path="others/fragment/lv_example_fragment_2" />
## API
-96
View File
@@ -1,96 +0,0 @@
.. _fragment:
========
Fragment
========
.. warning::
This module is now deprecated and will be removed in an upcoming release.
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:
Examples
********
.. include:: /examples/others/fragment/index.rst
.. _fragment_api:
API
***
+5
View File
@@ -0,0 +1,5 @@
---
title: Auxiliary Modules
---
-11
View File
@@ -1,11 +0,0 @@
.. _auxiliary_modules:
=================
Auxiliary Modules
=================
.. toctree::
:maxdepth: 2
file_explorer
fragment
+7
View File
@@ -0,0 +1,7 @@
{
"title": "Auxiliary Modules",
"pages": [
"file_explorer",
"fragment"
]
}
+123
View File
@@ -0,0 +1,123 @@
---
title: API Conventions
description: "In most cases, the API functions of LVGL widgets are structured like:"
---
In most cases, the API functions of LVGL widgets are structured like:
- `lv_ + <widget_name> + create(parent)`
- `lv_ + <widget_name> + set + <property>(widget, value)`
- `lv_ + <widget_name> + get + <property>(widget)`
- `lv_ + <widget_name> + add + <property>(widget)`
## Basic Attributes
All widget types share some basic attributes:
- Position
- Size
- Parent
- Styles
- Event callbacks
- Flags like *Clickable*, *Scrollable*, etc.
- Etc.
You can set/get these attributes with `lv_obj_set_...` and
`lv_obj_get_...` functions. For example:
```c
/* Set basic widget attributes */
lv_obj_set_size(btn1, 100, 50); /* Set a button's size */
lv_obj_set_pos(btn1, 20, 30); /* Set a button's position */
```
For complete details on position, size, coordinates, and layouts, see [Position and Size](/common-widget-features/coordinates).
## Widget-Specific Attributes
The widget types have special attributes as well. For example, a slider has:
- Minimum and maximum values
- Current value
For these special attributes, every widget type may have unique API functions. For
example, for a [Slider](/widgets/slider):
```c
/* Set slider-specific attributes */
lv_slider_set_range(slider1, 0, 100); /* Set the min and max values */
lv_slider_set_value(slider1, 40, LV_ANIM_ON); /* Set the current value (position) */
```
The API of the Widgets is described in their [Documentation](/widgets), but you
can also consult each Widget's respective header file (e.g., *widgets/lv_slider.h*) to
find a quick reference to function prototypes with brief documentation about each.
## Widget Creation
Widgets can be created and deleted dynamically at runtime. Only currently created
(existing) Widgets consume RAM.
This allows you to create a Screen only when a button is clicked to open it, and to
delete Screens when a new screen is loaded.
UIs can be created based on the current environment of the device. For example, you
can create meters, charts, bars, and sliders based on the currently attached sensors.
Every widget has its own **create** function with a prototype like this:
```c
lv_obj_t * lv_<widget>_create(lv_obj_t * parent)
```
The create functions only have a `parent` parameter specifying on which widget to
create the new widget.
The return value is a pointer to the created widget of type `lv_obj_t *`.
## Widget Deletion
There is a common **delete** function for all widget types. It deletes the widget and
all of its children.
```c
void lv_obj_delete(lv_obj_t * widget);
```
<ApiLink name="lv_obj_delete" /> deletes the widget immediately. If for any reason you can't
delete the widget immediately, you can use <ApiLink name="lv_obj_delete_async" display="lv_obj_delete_async(widget)" />
which will perform the deletion on the next call of <ApiLink name="lv_timer_handler" />. This
is useful, for example, if you want to delete the parent of a widget in the child's
<ApiLink name="LV_EVENT_DELETE" /> handler. Once deleted, the RAM a Widget occupies is
freed.
You can remove all the children of a widget (but not the widget itself) using
<ApiLink name="lv_obj_clean" display="lv_obj_clean(widget)" />.
You can use <ApiLink name="lv_obj_delete_delayed" display="lv_obj_delete_delayed(widget, 1000)" /> to delete a widget after
some time. The delay is expressed in milliseconds.
By calling <ApiLink name="lv_obj_null_on_delete" display="lv_obj_null_on_delete(&widget)" />, the `lv_obj_t *` variable of
the widget will be set to NULL when the widget is deleted. This makes it easy to check
whether the widget exists or not.
Here is an example that uses some of the functions above:
```c
static lv_obj_t * my_label; /* Static in the file so it stays valid */
/* Call it every 2000 ms */
void some_timer_callback(lv_timer_t * t)
{
/* If the label is not created yet, create it and also delete it after 1000 ms */
if(my_label == NULL) {
my_label = lv_label_create(lv_screen_active());
lv_obj_delete_delayed(my_label, 1000);
lv_obj_null_on_delete(&my_label);
}
/* Move the label if it exists */
else {
lv_obj_set_x(my_label, lv_obj_get_x(my_label) + 1);
}
}
```
-134
View File
@@ -1,134 +0,0 @@
================
API Conventions
================
In most cases, the API functions of LVGL widgets are structured like:
- ``lv_ + <widget_name> + create(parent)``
- ``lv_ + <widget_name> + set + <property>(widget, value)``
- ``lv_ + <widget_name> + get + <property>(widget)``
- ``lv_ + <widget_name> + add + <property>(widget)``
Basic Attributes
****************
All widget types share some basic attributes:
- Position
- Size
- Parent
- Styles
- Event callbacks
- Flags like *Clickable*, *Scrollable*, etc.
- Etc.
You can set/get these attributes with ``lv_obj_set_...`` and
``lv_obj_get_...`` functions. For example:
.. code-block:: c
/* Set basic widget attributes */
lv_obj_set_size(btn1, 100, 50); /* Set a button's size */
lv_obj_set_pos(btn1, 20, 30); /* Set a button's position */
For complete details on position, size, coordinates, and layouts, see :ref:`coord`.
Widget-Specific Attributes
**************************
The widget types have special attributes as well. For example, a slider has:
- Minimum and maximum values
- Current value
For these special attributes, every widget type may have unique API functions. For
example, for a :ref:`Slider <lv_slider>`:
.. code-block:: c
/* Set slider-specific attributes */
lv_slider_set_range(slider1, 0, 100); /* Set the min and max values */
lv_slider_set_value(slider1, 40, LV_ANIM_ON); /* Set the current value (position) */
The API of the Widgets is described in their :ref:`Documentation <widgets>`, but you
can also consult each Widget's respective header file (e.g., *widgets/lv_slider.h*) to
find a quick reference to function prototypes with brief documentation about each.
Widget Creation
***************
Widgets can be created and deleted dynamically at runtime. Only currently created
(existing) Widgets consume RAM.
This allows you to create a Screen only when a button is clicked to open it, and to
delete Screens when a new screen is loaded.
UIs can be created based on the current environment of the device. For example, you
can create meters, charts, bars, and sliders based on the currently attached sensors.
Every widget has its own **create** function with a prototype like this:
.. code-block:: c
lv_obj_t * lv_<widget>_create(lv_obj_t * parent)
The create functions only have a ``parent`` parameter specifying on which widget to
create the new widget.
The return value is a pointer to the created widget of type ``lv_obj_t *``.
Widget Deletion
***************
There is a common **delete** function for all widget types. It deletes the widget and
all of its children.
.. code-block:: c
void lv_obj_delete(lv_obj_t * widget);
:cpp:func:`lv_obj_delete` deletes the widget immediately. If for any reason you can't
delete the widget immediately, you can use :cpp:expr:`lv_obj_delete_async(widget)`
which will perform the deletion on the next call of :cpp:func:`lv_timer_handler`. This
is useful, for example, if you want to delete the parent of a widget in the child's
:cpp:enumerator:`LV_EVENT_DELETE` handler. Once deleted, the RAM a Widget occupies is
freed.
You can remove all the children of a widget (but not the widget itself) using
:cpp:expr:`lv_obj_clean(widget)`.
You can use :cpp:expr:`lv_obj_delete_delayed(widget, 1000)` to delete a widget after
some time. The delay is expressed in milliseconds.
By calling :cpp:expr:`lv_obj_null_on_delete(&widget)`, the ``lv_obj_t *`` variable of
the widget will be set to NULL when the widget is deleted. This makes it easy to check
whether the widget exists or not.
Here is an example that uses some of the functions above:
.. code:: c
static lv_obj_t * my_label; /* Static in the file so it stays valid */
/* Call it every 2000 ms */
void some_timer_callback(lv_timer_t * t)
{
/* If the label is not created yet, create it and also delete it after 1000 ms */
if(my_label == NULL) {
my_label = lv_label_create(lv_screen_active());
lv_obj_delete_delayed(my_label, 1000);
lv_obj_null_on_delete(&my_label);
}
/* Move the label if it exists */
else {
lv_obj_set_x(my_label, lv_obj_get_x(my_label) + 1);
}
}
@@ -0,0 +1,17 @@
---
title: Overview
description: A Widget is the basic building block of the LVGL user interface.
---
A Widget is the **basic building block** of the LVGL user interface.
Examples of Widgets: [Base Widget (and Screen)](/widgets/base_widget),
[Button](/widgets/button), [Label](/widgets/label),
[Image](/widgets/image), [List](/widgets/list),
[Chart](/widgets/chart), and [Text Area](/widgets/textarea).
See [All Widgets](/widgets) to view all widget types.
The following pages will introduce features that are common to all widgets.
For example, any widget's size and position can be changed. They can have
styles, events, layouts, and many other features.

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