Files
php-qml/framework/qml/CMakeLists.txt

55 lines
1.4 KiB
CMake
Raw Normal View History

# php-qml framework — Qt module (PhpQml.Bridge).
#
# Designed to be add_subdirectory()'d from the consuming application's
# top-level CMakeLists.txt (see framework/skeleton/qml/CMakeLists.txt
# arriving in Phase 1 sub-commit 6). Standalone configuration also
# works for module-only sanity builds.
cmake_minimum_required(VERSION 3.21)
if(NOT DEFINED PROJECT_NAME)
project(php_qml_bridge LANGUAGES CXX)
endif()
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTOMOC ON)
if(NOT TARGET Qt6::Core)
find_package(Qt6 6.5 REQUIRED COMPONENTS Core Gui Quick Qml Network)
qt_standard_project_setup(REQUIRES 6.5)
endif()
qt_add_qml_module(php_qml_bridge
URI PhpQml.Bridge
VERSION 1.0
STATIC
qml: pin OUTPUT_DIRECTORY of PhpQml.Bridge to match its URI path qmllint resolves QML modules by walking the import path looking for a directory layout that mirrors the URI (PhpQml.Bridge → PhpQml/Bridge/). qt_add_qml_module's OUTPUT_DIRECTORY defaults to CMAKE_CURRENT_BINARY_DIR — which, when consumers add_subdirectory() this with their own binary_dir (skeleton: build/qml/php_qml_bridge, todo: build/qml/php_qml_bridge), ends in `php_qml_bridge` instead of `PhpQml/Bridge`. cmake configure warns about the mismatch: The php_qml_bridge target is a QML module with target path PhpQml/Bridge. It uses an OUTPUT_DIRECTORY of .../php_qml_bridge, which should end in the same target path, but doesn't. Tooling such as qmllint may not work correctly. …and at lint time, qmllint can't find the module, so every file that `import PhpQml.Bridge` (AppShell.qml, DevConsole.qml) fails with "Failed to import PhpQml.Bridge", which cascades into bogus "Unqualified access" warnings for every BackendConnection reference. The cascade exits 255 in Qt 6.5.3's qmllint (CI), even when an older local qmllint would only warn. Fix: pin OUTPUT_DIRECTORY in the framework's own qt_add_qml_module so the layout is correct regardless of how consumers wire up the add_subdirectory binary_dir. Single source of truth in the framework, no consumer-side change needed. Verified locally: rebuild from scratch + `make quality` green (qmllint clean of the cascade — only the pre-existing DevConsole/Main.qml warnings remain, all non-fatal). PHPStan + cs-fixer + 16 tests + maker snapshots also still green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 10:48:47 +02:00
# OUTPUT_DIRECTORY must mirror the URI for qmllint to resolve the module.
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/PhpQml/Bridge
SOURCES
src/BackendConnection.h
src/BackendConnection.cpp
src/SingleInstance.h
src/SingleInstance.cpp
src/MercureClient.h
src/MercureClient.cpp
Phase 2 sub-commit 3: full Update Semantics + ReactiveListModel + AppShell BackendConnection's ConnectionState enum is now Connecting / Online / Reconnecting / Offline (PLAN.md §5). The probe loop tracks the first failure since the last Online and transitions to Reconnecting on any failed probe, then to Offline once the configurable threshold (30 s default) is exceeded. The Error state is gone; Reconnecting + the exposed `error` string subsume its UI role. ReactiveListModel is the headline QML type: - QAbstractListModel that GETs `baseUrl + source` for an initial JSON array and then keeps in sync via an internal MercureClient subscribed to `topic`. - Role names are derived dynamically from the first row's keys plus an internal `pending` boolean role used by optimistic mutations. - Diff application: upsert (insert-or-update), delete, replace; gap detection via the envelope `version` field with auto re-fetch. - `invoke(method, path, body, optimistic)` is the optimistic command primitive. Generates an Idempotency-Key, applies the local diff, POST/PATCH/DELETEs with that key, and resolves on the matching Mercure echo (correlation-key matched in ModelPublisher's envelope). Rolls back and emits commandFailed on 4xx/5xx, commandTimedOut after 10 s without an echo. Phase 4 packaging will surface configuration for the timeout. AppShell.qml is the optional convenience root: - Reads BackendConnection.connectionState. - Reconnecting → top banner. - Offline → modal overlay with the error string and a Retry button (calls BackendConnection.restart()). - Wraps user content via `default property alias content`. Apps that want full chrome control can skip AppShell entirely; the skeleton's Main.qml keeps its own status display for demonstration and is unaffected. ReactiveObject (single-entity twin of ReactiveListModel) is intentionally deferred — same envelope handling, smaller surface; will land in Phase 2 follow-up or Phase 3 alongside the todo example. Cursor pagination is similarly deferred (the Phase 2 done criterion uses small lists). Smoke tested: /healthz + /api/ping round-trip cleanly, zero Mercure 401s, clean shutdown. composer quality stays green (16 tests, 45 assertions). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 02:40:12 +02:00
src/ReactiveListModel.h
src/ReactiveListModel.cpp
src/ReactiveObject.h
src/ReactiveObject.cpp
QML_FILES
qml/RestClient.qml
Phase 2 sub-commit 3: full Update Semantics + ReactiveListModel + AppShell BackendConnection's ConnectionState enum is now Connecting / Online / Reconnecting / Offline (PLAN.md §5). The probe loop tracks the first failure since the last Online and transitions to Reconnecting on any failed probe, then to Offline once the configurable threshold (30 s default) is exceeded. The Error state is gone; Reconnecting + the exposed `error` string subsume its UI role. ReactiveListModel is the headline QML type: - QAbstractListModel that GETs `baseUrl + source` for an initial JSON array and then keeps in sync via an internal MercureClient subscribed to `topic`. - Role names are derived dynamically from the first row's keys plus an internal `pending` boolean role used by optimistic mutations. - Diff application: upsert (insert-or-update), delete, replace; gap detection via the envelope `version` field with auto re-fetch. - `invoke(method, path, body, optimistic)` is the optimistic command primitive. Generates an Idempotency-Key, applies the local diff, POST/PATCH/DELETEs with that key, and resolves on the matching Mercure echo (correlation-key matched in ModelPublisher's envelope). Rolls back and emits commandFailed on 4xx/5xx, commandTimedOut after 10 s without an echo. Phase 4 packaging will surface configuration for the timeout. AppShell.qml is the optional convenience root: - Reads BackendConnection.connectionState. - Reconnecting → top banner. - Offline → modal overlay with the error string and a Retry button (calls BackendConnection.restart()). - Wraps user content via `default property alias content`. Apps that want full chrome control can skip AppShell entirely; the skeleton's Main.qml keeps its own status display for demonstration and is unaffected. ReactiveObject (single-entity twin of ReactiveListModel) is intentionally deferred — same envelope handling, smaller surface; will land in Phase 2 follow-up or Phase 3 alongside the todo example. Cursor pagination is similarly deferred (the Phase 2 done criterion uses small lists). Smoke tested: /healthz + /api/ping round-trip cleanly, zero Mercure 401s, clean shutdown. composer quality stays green (16 tests, 45 assertions). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 02:40:12 +02:00
qml/AppShell.qml
qml/DevConsole.qml
)
target_include_directories(php_qml_bridge PUBLIC src/)
target_link_libraries(php_qml_bridge PUBLIC
Qt6::Core
Qt6::Gui
Qt6::Network
Qt6::Qml
Qt6::Quick
)