Skip to content

Latest commit

 

History

History
714 lines (589 loc) · 30.2 KB

File metadata and controls

714 lines (589 loc) · 30.2 KB

Qt Quick UI Engineering

Use this guide for any new or modified Qt graphical interface. Canonical rules: GUI-*, plus ARC-*, NAM-*, SYN-*, RES-*, and TST-*.

When the task includes an application icon, launcher icon, store icon, or in-application brand mark, also read APP_ICONS_AND_BRANDING.md. Native packaging artifacts remain platform resources; only the in-application mark belongs under the ui/ visual boundary.

Generated projects should begin with the combined ordering in PROJECT_CMAKE_BASELINE.md, which keeps project modules, Qt policy setup, and generated-type include paths aligned.

The default stack is Qt 6, Qt Quick, QML, and Qt Quick Controls. Qt Widgets is a compatibility technology, not the default for new interfaces.

Primary Qt references:

Interaction Surface Selection

Classify the product surface before designing screens or targets:

Request or inspected context Primary surface
Explicit graphical, desktop, Qt, or QML application Qt Quick
User-facing interactive application with no interface specified Qt Quick
Explicit CLI tool, service, library, daemon, or headless process Requested non-graphical surface
Existing compatible UI under a constrained change Preserve it and document any GUI-002 exception

Do not silently interpret an unspecified user-facing interactive application as CLI-only. Under GUI-015, Qt Quick is the primary interface. If automation, testing, or headless use has real value, a CLI may be added as a secondary adapter under GUI-016; it is not a substitute for the graphical interface.

Both adapters call the same C++ application and domain modules. QML and CLI argument parsing must not contain duplicated domain decisions. When the request is explicitly non-graphical, do not add Qt merely to follow a default that does not apply.

Required Design Pass

Do not begin with Main.qml. Define the interaction model first:

Audience and usage context:
Primary user goal:
Product-specific visual direction:
Information hierarchy and content density:
Screens and navigation:
User actions:
Authoritative state:
Loading, empty, success, and failure states:
Affordances, immediate feedback, and error prevention/recovery:
Keyboard path and focus order:
Reusable components:
Design tokens:
Application icon and in-application brand-mark ownership, when in scope:
Outer content bounds and maximum task width:
Columns, gutters, and shared alignment lines:
Repeated-control size and gap invariants:
Responsive breakpoints or layout behavior:
Grow, shrink, wrap, overflow, and scrolling rules by region:
Accessibility labels and announcements:
Localization requirements:

A visually attractive screen that omits failure, focus, resizing, or ownership states is incomplete.

Product-Specific UI/UX Direction

Consistency is not repetition. Tokens and reusable components should make the product coherent, but every screen composition must follow its task and information hierarchy. Before drawing components, explain:

  • who uses the product, in which environment, and what they need to notice first;
  • which action is primary, which actions are secondary, and which actions are destructive or reversible;
  • how the interface makes system status, selection, progress, validation, and recovery visible without requiring recall;
  • how progressive disclosure controls complexity and preserves an appropriate content density;
  • which visual qualities make the product recognizable without weakening usability or accessibility.

Do not default every product to the same centered hero, rounded-card grid, gratuitous gradient, glass panel, oversized empty spacing, or dashboard shell. Do not copy a reference screenshot as a component recipe. Extract useful interaction principles, then adapt hierarchy, density, typography, motion, and component composition to the actual product.

Use established UX principles deliberately: recognition over recall, visible system status, clear affordances, immediate feedback, error prevention, actionable recovery, consistent terminology, keyboard efficiency, and accessible contrast. A distinctive visual direction is successful only when the main task becomes easier to understand and complete.

Architecture

flowchart LR
    QML["Qt Quick presentation"] --> ViewModel["C++ presentation adapter"]
    ViewModel --> Application["Application module"]
    CLI["Optional CLI adapter"] --> Application
    Application --> Domain["Pure C++ domain modules"]
    Adapter["Platform and persistence adapters"] --> Application
Loading

Dependency rules:

  • Domain modules do not import Qt Quick, QML, or visual types.
  • Application modules own use cases and authoritative behavior.
  • A presentation adapter translates typed C++ state into a minimal QML-facing contract.
  • QML owns layout, transitions, visual state, and input forwarding.
  • The composition root creates concrete dependencies and registers UI types.

QML And C++ Responsibility Matrix

Responsibility Owner
Domain decisions, validation, business rules C++ domain module
Use-case orchestration and authoritative state C++ application module
QML properties, signals, commands, list models C++ presentation adapter
Layout, controls, animation, visual feedback QML
Filesystem, network, settings, OS APIs C++ adapter/platform module

Small QML expressions for visibility, formatting, and visual state are acceptable. Parsing, persistence, validation, and domain decisions are not.

Responsibility-Oriented Reference Layout

src/
  domain/
    app_domain.cppm
    app_domain.cpp
  application/
    app.cppm
    app.cpp
  presentation/
    app_view_model.hpp
    app_view_model.cpp
  adapters/
  bootstrap/
    main.cpp
  cli/
    main.cpp  # optional secondary adapter
ui/
  Main.qml
  pages/
  components/
    PrimaryActionButton.qml
    StatusPanel.qml
  theme/
    Theme.qml
  assets/
tests/
  domain/
  application/
  presentation/
  ui/
    tst_AppShell.qml

Create only directories that own real behavior or assets. The structure may be smaller for a small product, but a new Qt Quick project uses ui/ as its visual boundary rather than a top-level qml/ technology bucket. pages/, components/, theme/, and assets/ are responsibility-based subdivisions, not mandatory empty ceremony.

The .hpp presentation file is permitted only when Qt MOC requires a textual meta-object boundary. It is an external-tool adapter under MOD-007, not a reason to replace domain modules with headers.

Presentation Contract

Expose the smallest contract QML needs:

  • typed Q_PROPERTY state with NOTIFY or bindable semantics;
  • clearly named invokable user intents;
  • signals for observable events, not hidden command channels;
  • QAbstractItemModel derivatives for structured collections;
  • immutable value snapshots where practical;
  • explicit busy, error, empty, and disabled states.

Do not expose a large service object or raw domain graph to QML.

Creatable Types And final

QML_ELEMENT makes a class creatable from QML unless another registration policy says otherwise. Qt generates an internal wrapper derived from that C++ type. Therefore a type instantiated like this:

AppViewModel {
    id: appViewModel
}

must not be declared final:

class AppViewModel : public QObject {
    Q_OBJECT
    QML_ELEMENT
};

This is a framework extension point, not an invitation for project code to subclass the adapter. A presentation type may remain final only when QML does not instantiate it and the selected singleton, uncreatable, context-property, or factory ownership strategy has been verified not to require Qt-generated subclassing.

Generated projects copy cmake/AimcppProjectChecks.cmake from this repository and call aimcpp_reject_final_qml_creatable_types for every project-owned QML registration header before qt_add_qml_module. This turns the common QML_ELEMENT plus final contradiction into a configure-time GUI-021 diagnostic. It is a fast preflight, not proof of Qt integration; the generated registration sources and graphical executable still must complete a clean full build.

Optional CLI Adapter

When an additional CLI is justified, keep it as a thin composition and input/output adapter:

add_executable(MyAppCli src/cli/main.cpp)
target_link_libraries(MyAppCli PRIVATE app_core)
target_compile_features(MyAppCli PRIVATE cxx_std_26)

The CLI forwards user intent to app_core; it does not reimplement validation, state transitions, persistence policy, or other authoritative behavior.

QML Naming And Component Structure

  • Keep QML, visual tokens, and presentation assets under the top-level ui/ boundary for new repositories.
  • QML component files and exported QML types use PascalCase.
  • id, property, signal, handler, and function names use lowerCamelCase.
  • Reusable components describe a UI role: PrimaryActionButton, not BlueButton.
  • Keep pages responsible for composition; move reusable visuals into focused components.
  • Avoid giant Main.qml files that own the whole product.

Layout And Visual System

Using RowLayout, ColumnLayout, or GridLayout does not by itself create a good layout. Define the relationships those containers must preserve.

Layout Contract

Before QML implementation, record:

Decision Required evidence
Content bounds Outer insets and the maximum useful task width
Columns Column purpose, width policy, gutter, and collapse order
Alignment lines Shared left/right edges, centers, text baselines, and numeric edges
Spacing scale Named increments and where each tier is used
Repeated controls Equal width/height, row/column gaps, radius, icon box, and label baseline
Region sizing Which regions fill, stay intrinsic, cap, reflow, scroll, or hide
Breakpoints Compact, standard, and wide compositions with transition criteria
Safe bounds Bottom/top insets and containment for footers, overlays, focus rings, and shadows

Use one authoritative spacing scale and semantic tokens. A value should come from content, a token, or a documented constraint—not from nudging one element until a single screenshot looks acceptable.

Alignment And Rhythm Audit

  • Establish a small set of visible alignment lines per screen. Titles, header actions, dividers, panels, displays, grids, and footers should resolve to those lines instead of drifting independently.
  • Repeated peers must share geometry. Keypads, toolbars, list rows, and card groups require consistent sizes and gaps unless hierarchy deliberately marks an exception.
  • Align text by the appropriate metric. Use baselines for related labels, optical centering for icons and glyphs, and a stable right edge with tabular figures for changing numeric values when the product benefits from it.
  • Preserve nested padding relationships. A child surface should not appear arbitrarily closer to one parent edge than another.
  • Keep peripheral content inside its owning layout and safe inset. A status message, keyboard hint, or trailing icon must not float near the window edge because it was anchored outside the main content hierarchy.

Content Balance And Empty Space

Empty space is useful only when it supports hierarchy. For every large empty region, identify whether it provides focus, future content capacity, or a deliberate visual pause. Otherwise rebalance the composition.

  • Do not let one child remain at a small fixed width on the left while its parent expands indefinitely and leaves unused space on the right.
  • Related elements should normally share a bounded content width or a deliberate alignment relationship. For example, a display and keypad should not imply different grids without a product reason.
  • At wide sizes, cap and center the task area, redistribute columns, or reveal a justified secondary region. Do not scale every control indefinitely.
  • At compact sizes, reflow or collapse secondary panels before primary controls become clipped, crowded, or unreachable.

Visual Tokens And Detail

  • Define reusable spacing, radius, typography, color, icon-size, control-height, border, and motion tokens.
  • Let task hierarchy and content density determine composition; do not place every piece of content in an identical card merely for visual consistency.
  • Support light/dark appearance through tokens rather than scattered colors.
  • Preserve readable content under resizing, long values, and text expansion.
  • Ensure icons share a coherent visual weight, bounding box, and baseline with adjacent text; geometric centering may still require optical adjustment.
  • Make destructive, primary, and secondary actions visually distinct without breaking their placement and alignment contracts.
  • Use animation to explain state changes, not delay interaction.
  • Avoid magic pixels repeated across components.

Qt Quick Controls Style Contract

Choose the Controls strategy before creating reusable controls:

Strategy Required behavior
Product-owned visual system Select one customizable style such as Basic, Fusion, Imagine, Material, or Universal before loading QML; custom background, contentItem, indicator, delegates, and popups are then allowed and verified under that style
Native platform controls Keep the platform style and customize only its documented surface; do not replace visual delegates that the native style rejects

Do not let the workstation choose this architecture implicitly. macOS and Windows native styles may reject delegate replacement even when the same QML appears to work under another style. The application composition root, tests, lint environment, screenshots, and packaged application must agree on the effective style.

For a product-owned system, select the style before any QML importing Qt Quick Controls is loaded:

#include <QQuickStyle>

QQuickStyle::setStyle(QStringLiteral("Basic"));
QQmlApplicationEngine engine;

The full entry point still owns QGuiApplication, object creation failure, dependency composition, and smoke-test readiness. This excerpt shows ordering, not a complete main function.

Exact QML API Compatibility

Validate QML against the exact instantiated type and the declared minimum Qt version. Similar controls do not necessarily expose the same API. A property on Text is not automatically a property on TextEdit or TextArea, and a newer Qt documentation page does not expand the project's minimum-version contract.

Required checks:

  • inspect the exact type documentation and inherited-member list;
  • keep imports compatible with the declared minimum Qt version;
  • run the generated module lint target and strict qmllint with zero allowed project warnings;
  • create every component at runtime, including lazy dialogs, popups, delegates, and alternate responsive branches;
  • treat Type ... unavailable, Cannot assign to non-existent property, and unresolved import/type diagnostics as causal failures.

Deleting a failing property until the window opens is not verification. Confirm the intended typography or behavior still exists through a supported API.

Acyclic Geometry And Scrollable Content

Every size relationship needs one owner. A parent may constrain a child, or a child's intrinsic content may inform a parent, but both directions must not be coupled through implicit size.

Incorrect

ScrollView {
    id: viewport

    TextArea {
        width: viewport.availableWidth
        implicitHeight: Math.max(contentHeight, viewport.availableHeight)
    }
}

The viewport can derive content size from the editor while the editor derives its implicit size from the viewport, producing a binding loop.

Correct direction

ScrollView {
    id: viewport
    clip: true

    TextArea {
        width: viewport.availableWidth
        height: Math.max(contentHeight, viewport.height)
        background: null
    }
}

This shape is appropriate only when the surrounding layout gives the viewport an explicit height. Other valid designs may let content own height and place the editor inside a separately constrained flickable. In either case, document viewport ownership, content ownership, minimums, maximums, and overflow.

Content-Safe Controls, Popups, And Dialogs

  • Primary, destructive, and confirmation action labels remain fully readable in the reference locale. Size from content plus padding, set a justified minimum, or reflow actions; do not silently elide the action meaning.
  • Test translated expansion and realistic longest labels. Fixed control widths require evidence, not a convenient initial screenshot.
  • Popup width must account for both anchors and delegate content while remaining inside safe window bounds. Long language names, native names, model names, and RTL text must not be clipped at the trailing edge.
  • Delegate rows define how leading text, trailing text, icons, and indicators divide space. Do not compute both text widths from an unstable parent.width or rely on coincidental remaining space.
  • Dialog header, body, and footer share alignment anchors. Footer actions may wrap or widen before their labels truncate.
  • Instantiate open popups, open dialogs, long editor content, empty/error/loading states, and compact/wide branches during verification; closed lazy controls cannot be judged from startup alone.

Portable Typography

Prefer Qt's resolved application/system fonts and consistent typography tokens. Name a specific family only when it is bundled with a documented license or verified on every supported platform with a deliberate fallback. Do not assume that strings such as Monospace or monospace resolve to a portable family. For code-like content, use a verified fixed-pitch resolution strategy and test the resulting metrics on each supported platform.

Accessibility And Input

Every interactive flow must be usable without a mouse:

  • deliberate tab/focus order;
  • visible focus indicators;
  • keyboard activation and shortcuts where appropriate;
  • accessible names, descriptions, roles, and state;
  • adequate contrast and target size;
  • no color-only communication;
  • screen-reader announcement for important result or error changes.

Localization And Text

User-visible strings must be translation-ready. Layouts must tolerate longer translations, different number formats, and right-to-left presentation when the product scope requires it. Do not concatenate translated sentence fragments.

Responsiveness And Performance

  • Never block the GUI thread with I/O or expensive computation.
  • Model asynchronous progress, cancellation, failure, and object lifetime.
  • Avoid bindings that form loops or repeatedly perform expensive work.
  • Load large or optional UI regions deliberately.
  • Test representative minimum, normal, and expanded window sizes.

CMake Shape

find_package(Qt6 REQUIRED COMPONENTS Quick Qml QuickControls2 Test)

set(QT_QML_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/qml")

set(my_app_qml_files
    ui/Main.qml
    ui/pages/HomePage.qml
    ui/components/PrimaryActionButton.qml
    ui/components/StatusPanel.qml
    ui/theme/Theme.qml
)

foreach(qmlFile IN LISTS my_app_qml_files)
    string(REGEX REPLACE "^ui/" "" qmlResourceAlias "${qmlFile}")
    set_source_files_properties(
        "${qmlFile}"
        PROPERTIES QT_RESOURCE_ALIAS "${qmlResourceAlias}"
    )
endforeach()

if(QT_KNOWN_POLICY_QTP0004)
    qt_policy(SET QTP0004 NEW)
endif()

qt_add_executable(MyApp
    src/bootstrap/main.cpp
)

set_target_properties(MyApp PROPERTIES
    RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin"
)

target_compile_definitions(MyApp
    PRIVATE
        MY_APP_QT_QUICK_CONTROLS_STYLE="Basic"
)

qt_add_qml_module(MyApp
    URI MyApp
    VERSION 1.0
    QML_FILES
        ${my_app_qml_files}
    SOURCES
        src/presentation/app_view_model.cpp
        src/presentation/app_view_model.hpp
)

target_include_directories(MyApp
    PRIVATE
        "${CMAKE_CURRENT_SOURCE_DIR}/src/presentation"
)

target_link_libraries(MyApp
    PRIVATE
        app_core
        Qt6::Quick
        Qt6::Qml
        Qt6::QuickControls2
)

Keep QML sources project-relative and set their aliases before qt_add_qml_module. The source ui/Main.qml aliases to Main.qml, while ui/components/PrimaryActionButton.qml aliases to components/PrimaryActionButton.qml. This makes ui/ an architectural source boundary without inserting it into the module's runtime resource namespace. The alias, URI, and loadFromModule call are one contract: the root alias must remain Main.qml for loadFromModule("MyApp", "Main").

Keep the QML artifact root and runtime target root separate. It is normal for the executable target and URI to share the approved name MyApp; with explicit roots, the module is generated under qml/MyApp/ and the executable under bin/ instead of both competing for <binary-dir>/MyApp. The QT_QML_OUTPUT_DIRECTORY variable is the deliberate project-wide root for Qt QML modules. RUNTIME_OUTPUT_DIRECTORY remains target-local.

Set guarded Qt policies after find_package(Qt6 ...) and before qt_add_qml_module. QTP0004 requires qmldir metadata for extra QML directories; guarding it with QT_KNOWN_POLICY_QTP0004 preserves compatibility when the project's declared minimum Qt predates that policy. Do not claim a higher Qt minimum merely to silence the warning.

QML type metadata such as MyApp.qmltypes is generated during a successful CMake Generate/build workflow. If generation first fails for an unrelated toolchain property, a missing .qmltypes diagnostic from the IDE is a cascading symptom. Fix the first CMake failure, clear the stale CMake configuration, and regenerate before diagnosing QML registration.

Qt-generated QML registration code may include a QML_ELEMENT adapter header by basename. When that header lives under src/presentation/, add the directory to the QML target with target_include_directories. Register the adapter under the SOURCES section of qt_add_qml_module, keep the include path target-local, and never edit the generated *_qmltyperegistrations.cpp file.

Do not link Qt6::Widgets unless GUI-002 has a documented exception.

The composition root must call QQuickStyle::setStyle with MY_APP_QT_QUICK_CONTROLS_STYLE before loading QML. Keep the compile-time selection, test environment, and packaged application consistent. A custom Controls design must not fall back silently to the host's native default style.

Strict lint must import the configured QT_QML_OUTPUT_DIRECTORY, the current binary directory, and the active Qt installation's QML directory. Prefer module mode (qmllint -M MyApp) after the target has generated its qmldir and .qmltypes; it validates C++-registered types, singleton metadata, aliases, and nested components as one module. Run it from the source root. --bare and -M are available at the declared Qt 6.6 minimum. Add --max-warnings 0 only for Qt 6.8 or newer; Qt 6.6/6.7 already fail when warnings are emitted and do not recognize that option.

Verification

At minimum verify:

  1. Pure C++ domain behavior, invalid input, and boundary values.
  2. Presentation adapter state transitions and signals.
  3. QML component creation and primary interactions.
  4. Keyboard-only primary flow and focus visibility.
  5. Resizing, long translations, empty/error/loading states, and theme contrast.
  6. The generated QML lint target plus strict qmllint with zero project warnings. Verify the exact type/minimum-version API rather than assuming a property exists on a similar control.
  7. Configure, build, CTest, and relevant QML test runner results separately.
  8. When a CLI adapter exists, verify it calls the shared application/domain behavior and does not replace graphical interaction coverage.
  9. Inspect the main flow at representative window sizes and confirm that the product-specific hierarchy, affordances, feedback, and recovery behavior are clear rather than merely visually consistent.
  10. Configure a clean build with the GUI enabled, build the full default target, and confirm that MOC, QML type registration, resources, QML cache sources, and the graphical executable all compile and link.
  11. Run a deterministic QML creation/interaction smoke check under the selected Controls style with project-owned Qt/QML warnings treated as failures. The test must reach an explicit ready state and exercise the primary path, including lazy popups, dialogs, delegates, or editors that path uses. A fixed-delay launch does not provide this evidence. Core-only tests do not validate the graphical product.
  12. Capture and inspect rendered screenshots at minimum, standard, and wide viewport sizes. Include light/dark modes where supported and empty, populated, error, focus, and long-content states that materially change the composition.
  13. Perform a detail pass for shared edges, text baselines, control metrics, gaps, optical centering, contrast, clipping, truncation, overlap, safe insets, and unexplained dead space. Fix visible defects before completion.
  14. Add deterministic geometry assertions for critical containment, non-overlap, breakpoint, repeated-size, and alignment invariants where the QML test environment can measure them reliably.
  15. Confirm zero component-load errors, invalid-property diagnostics, unsupported-style customization warnings, binding loops, missing-font warnings, clipped popup rows, and truncated primary actions.
  16. Verify source-to-resource mappings: ui/Main.qml is the module-root Main.qml, logical subdirectories remain present, and loadFromModule(uri, "Main") reaches explicit readiness.
  17. Verify the produced runtime target is under bin/ and qmldir plus .qmltypes are under the configured qml/<URI path>/ tree. The full final link must pass when target name and URI root are identical.

If Qt or another required GUI dependency is unavailable, report the Qt surface as NOT VERIFIED. Do not describe the application or downloadable archive as ready until the Qt-enabled build and smoke evidence exist.

Visual Acceptance Matrix

Record evidence in a form such as:

Viewport Appearance State Inspected details Result
Minimum supported Light Empty + focus containment, reflow, focus, labels PASS/FAIL
Standard Light + dark Populated + error hierarchy, spacing, contrast PASS/FAIL
Wide Light + dark Long content/history max width, balance, alignment PASS/FAIL

The exact sizes belong to the product's layout contract. A screenshot at one convenient desktop size cannot prove responsiveness or detail quality.

Forbidden Shapes

  • Choosing QWidget because the request merely says "Qt".
  • Delivering only a CLI for an unspecified user-facing interactive application.
  • Duplicating application or domain behavior between QML and a CLI adapter.
  • Implementing domain decisions or validation in button-handler JavaScript.
  • Registering a global mutable service object for convenient QML access.
  • Hard-coding every position and size for one screenshot.
  • Reusing a generic card/gradient/dashboard recipe without a product-specific UX rationale.
  • Copying a reference design without adapting hierarchy and interaction to the product.
  • Creating a top-level qml/ dumping directory in a new repository instead of an explicit ui/ boundary.
  • Ignoring QTP0004 for QML files in extra directories or requiring a newer Qt release solely to avoid a guarded policy check.
  • Diagnosing a missing generated .qmltypes file before fixing an earlier failed CMake Generate step.
  • Omitting the target-local include directory for a nested QML_ELEMENT header or editing generated QML type registration source to compensate.
  • Declaring a QML-creatable QML_ELEMENT QObject final even though Qt's generated registration wrapper must derive from it.
  • Claiming GUI completion from a core-only, GUI-disabled, or headless test build.
  • Delivering a final archive when the requested Qt target or QML smoke flow was not verified in an environment with Qt installed.
  • Blocking the GUI thread during file, network, or expensive domain work.
  • Linking all Qt modules rather than the required target-local components.
  • Claiming a polished interface without keyboard and accessibility verification.
  • Treating layout-container usage as proof of alignment, balance, or responsive quality without defining and reviewing geometry invariants.
  • Stretching a wide panel while leaving primary controls pinned to one edge and accidental dead space on the other.
  • Letting header actions, dividers, content cards, grids, or footers drift from their intended shared alignment lines.
  • Leaving status text, hints, icons, focus rings, or overlays clipped, detached from their owning region, or too close to a viewport edge.
  • Calling a UI polished after inspecting only one viewport, appearance mode, or content state.
  • Replacing background, contentItem, indicator, delegates, or popups while inheriting an unspecified native Controls style.
  • Assigning a property because another text or control type exposes it without checking the exact QML type and minimum Qt version.
  • Coupling a child's implicit size to a viewport size that is itself derived from the child.
  • Naming an unbundled, unverified font family and accepting platform substitution warnings.
  • Using fixed action or popup widths that clip primary labels, translations, bilingual rows, RTL content, icons, or focus rings.
  • Passing a GUI smoke test that only waits briefly at startup, never opens lazy controls, and ignores project-owned Qt/QML warnings.
  • Passing absolute project QML paths to qt_add_qml_module and relying on Qt to infer stable resource names.
  • Letting the architectural ui/ directory become an accidental runtime module path segment or assigning aliases that move Main.qml away from the module root.
  • Leaving QML module output and runtime executable output in the same filesystem location when the target and URI share a name.
  • Renaming the approved product or QML URI, disabling cache generation, or deleting the colliding directory as a substitute for separating output roots.