--- name: qml description: "Use when writing or editing QML and Qt Quick code, including bindings, signals, components, layouts, models, Controls, focus, keyboard input, and accessibility." --- # QML QML declares an object tree whose property bindings react to dependency changes. Keep presentation declarative and keep state ownership explicit. ## Workflow 1. Inspect the surrounding component, its callers, imports, and the project's Qt version and tooling. Verify the executable's Qt major version; on Arch the bare `qmllint` may be Qt5, while `/usr/lib/qt6/bin/qmllint` is Qt6. 2. Name the owner of each piece of state and each item's geometry before editing. 3. Reuse an existing Qt Quick Control or project component before building interaction from primitives. 4. Bind owned state down into children and send user intent up through signals or narrowly scoped handlers. 5. Run `qmllint` with the project's import paths. 6. Load changed entry points using the project's isolated runtime and inspect complete logs for creation, import, binding-loop, and layout failures. Plain Qt Quick windows can load offscreen; toolkit windows such as Quickshell panels need their supported backend. 7. Exercise changed interaction with an existing test or UI driver when the task requires behavioral verification. For toolkit integrations, follow their official editor/LSP setup before diagnosing incomplete metadata as broken QML. Consult the installed Qt documentation or for type-specific APIs. Do not duplicate volatile property lists from memory. ## Invariants - Give mutable state one owner. Derived values stay bindings, and user events update the owner rather than sibling copies. - Give geometry one owner per axis. A layout, anchors, or explicit coordinates may own an axis, not several at once. - Treat imperative assignment to a bound property as binding replacement. Assign the binding's dependency or restore it explicitly with `Qt.binding(...)` when replacement is intentional. - Use ids within a component, and required properties or signals across component boundaries. Do not navigate with `parent.parent`. - Prefer `QtQuick.Controls` for buttons, fields, toggles, menus, and other standard interaction because Controls provide platform behavior, keyboard handling, focus, and accessibility semantics. - Give controls and custom interactive items meaningful accessible names when visible text does not already supply one. - Verify Tab, Shift+Tab, arrow-key, Enter/Space, Escape, and shortcut flows that apply. A `Keys` handler only receives events while its item has active focus. - Keep focus visible. Do not remove a Control's focus indicator without an accessible replacement. - Load the `gestalt` skill for actual visual organization, hierarchy, grouping, or layout design. This skill governs QML mechanics, not visual judgment. ## Imports Use versionless Qt imports in a Qt 6 application unless the project deliberately targets an older import version. Versioned imports restrict the API visible to QML, so copy the repository's established policy rather than pinning every import. ```qml import QtQuick import QtQuick.Controls // ApplicationWindow, Button, TextField, Slider import QtQuick.Layouts // RowLayout, ColumnLayout, GridLayout, StackLayout import QtQuick.Window // Window ``` ## State and events ```qml import QtQuick import QtQuick.Controls ApplicationWindow { id: root width: 400 height: 240 visible: true property real volume: 0.5 Slider { anchors.centerIn: parent value: root.volume Accessible.name: "Volume" onMoved: root.volume = value } } ``` The binding keeps the Slider synchronized from its owner. `onMoved` writes only user movement back to the owner, unlike `onValueChanged`, which also runs for programmatic changes. Writing `root.volume` does not break the binding on `value`; writing `value` imperatively does. ## Deferred and asynchronous work `Qt.callLater(callback, ...args)` schedules a callback for a later event-loop turn and coalesces repeated calls to the same function, using the last supplied arguments. It does not create or return a Promise and does not make blocking work asynchronous. Use the project's C++ or service boundary for I/O and expensive work. ## Validation levels ```sh qmllint path/to/File.qml QT_QPA_PLATFORM=offscreen timeout 3s qml6 path/to/File.qml ``` - `qmllint` is static analysis, not a load test. - A clean offscreen startup confirms that the entry point imports and instantiates. Timeout exit status `124` is expected for a window that remains in its event loop. - Offscreen loading does not verify clicks, keys, focus traversal, accessibility output, animations, or rendered appearance. - Claim interaction only after a Qt Quick Test or an existing UI automation route actually exercises it. ## References - [Syntax and bindings](references/syntax.md) - [Components](references/components.md) - [Layouts and geometry](references/layouts.md) - [Models and views](references/models.md) - [Signals and input](references/signals.md) - [JavaScript](references/javascript.md) - [Common pitfalls](references/common-pitfalls.md) - [Runnable examples](examples/)