Document iteration 1: settings file, HA Dashboard tab, new hooks

README: settings file, --config, the new tab. ARCHITECTURE.md and
CLAUDE.md: prepare() hook, app command-line options, app code layout,
WebEngine notes, the moc raw-string pitfall. THIRD_PARTY_NOTICES: Qt
WebEngine. Ship config.example.json; the package README explains the
set-up. PROGRESS.md and DESIGN.md: phase 3 results and deviations.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-05 22:21:34 +02:00
co-authored by Claude Opus 5.5
parent 0676998033
commit 179316db52
9 changed files with 175 additions and 37 deletions
+23 -10
View File
@@ -37,13 +37,19 @@ the order in which all of that is created and destroyed.
Your app derives from it:
- **Constructor:** configuration (`addSurface()`, optionally `setAppKey()`,
`setDisplayName()`, `setQuickStyle()`, `setQmlModule()`).
`setDisplayName()`, `setQuickStyle()`, `setQmlModule()`). The app's own command-line
options go to the base constructor as a list of `QCommandLineOption`s; they are parsed and
listed in `--help` with the built-in ones, and read with `isOptionSet()`/`optionValue()`.
- **Hooks:**
- `prepare(QString* error)`: first, once mode and app key are known, before the QML
engine, the surfaces and SteamVR exist (not for manifest commands). Returning `false`
ends `exec()` with the error and exit code 1, e.g. for invalid settings.
- `loading()`: the shared QML engine, the surfaces and their render backends exist; QML
isn't loaded yet. Set up what the QML needs here.
- `started()`: everything runs. Connect to surfaces and input here.
- **Accessors:** `mode()`, `appKey()`, `appName()`, `engine()`, `surfaces()`, `surface()`
(the first), `surface(id)`, `vrRuntime()`, `navigation()`.
- **Accessors:** `mode()`, `appKey()`, `appName()`, `isOptionSet()`, `optionValue()`,
`engine()`, `surfaces()`, `surface()` (the first), `surface(id)`, `vrRuntime()`,
`navigation()`.
- **Lifetime:** the base constructor creates the `QGuiApplication`, so your members can be
QObjects with timers. All QML and surfaces are torn down at the end of `exec()`, while your
object and its members still exist.
@@ -121,15 +127,16 @@ sequenceDiagram
## Start and shutdown (overlay mode)
1. **Start:**
1. Connect to SteamVR (`VR_Init`).
2. Create one render backend per surface. The first surface decides the API, including a
1. Call `prepare()`; stop with exit code 1 if it fails.
2. Connect to SteamVR (`VR_Init`).
3. Create one render backend per surface. The first surface decides the API, including a
fallback to OpenGL; the others use the same API, because Qt's graphics API is
process-wide.
3. Call `loading()`.
4. Load each surface's QML.
5. Create the overlays.
6. Set up controller navigation.
7. Call `started()`, then run the event loop.
4. Call `loading()`.
5. Load each surface's QML.
6. Create the overlays.
7. Set up controller navigation.
8. Call `started()`, then run the event loop.
2. **Shutdown runs in reverse**, with one rule found on the Frame: **overlays first, then
SteamVR, then the graphics devices.** OpenVR requires shutting down before the devices it
got textures from are destroyed. Otherwise its client library frees objects on a dead
@@ -160,3 +167,9 @@ For e.g. a notification app: a `WorldOverlay` (another `Overlay` subclass, creat
(`VROverlayFlags_MakeOverlaysInteractiveIfVisible`).
The surface structure is ready for it: `SurfaceConfig` would gain a kind and a placement.
## FrameAssistant on top
The app itself (`src/Dashboard.*`, `src/config/`, `src/ha/`, `src/qml/`) builds on this
platform; its design (Home Assistant connection, entities, overlays, threads) is in
[DESIGN.md](DESIGN.md).