diff --git a/CMakeLists.txt b/CMakeLists.txt index d289503..3393218 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,6 +46,15 @@ target_compile_features(pp_project_options INTERFACE cxx_std_23) add_library(pp_project_warnings INTERFACE) pp_configure_project_warnings(pp_project_warnings) +add_library(pp_vendor_tinyxml2 STATIC + libs/tinyxml2/tinyxml2.cpp) +target_include_directories(pp_vendor_tinyxml2 + SYSTEM PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}/libs/tinyxml2") +target_link_libraries(pp_vendor_tinyxml2 + PUBLIC + pp_project_options) + add_custom_target(panopainter_modernization_status COMMAND "${CMAKE_COMMAND}" -E echo "PanoPainter modernization scaffold configured." COMMAND "${CMAKE_COMMAND}" -E echo "Roadmap: docs/modernization/roadmap.md" @@ -129,7 +138,8 @@ target_link_libraries(pp_paint_renderer pp_project_warnings) add_library(pp_ui_core STATIC - src/ui_core/layout_value.cpp) + src/ui_core/layout_value.cpp + src/ui_core/layout_xml.cpp) target_include_directories(pp_ui_core PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src") @@ -138,6 +148,7 @@ target_link_libraries(pp_ui_core pp_foundation pp_project_options PRIVATE + pp_vendor_tinyxml2 pp_project_warnings) if(PP_BUILD_TOOLS) diff --git a/docs/modernization/build-inventory.md b/docs/modernization/build-inventory.md index d1070fc..12e643b 100644 --- a/docs/modernization/build-inventory.md +++ b/docs/modernization/build-inventory.md @@ -77,7 +77,8 @@ Known local toolchain state: - Android arm64 headless configure/build passes through root CMake and the `platform-build` automation wrapper for `pp_foundation`, `pp_assets`, `pp_paint`, `pp_document`, `pp_renderer_api`, `pp_paint_renderer`, - `pp_ui_core`, `pano_cli`, and their current headless test binaries. + `pp_ui_core`, `pano_cli`, and their current headless test binaries, + including layout XML parse coverage. - `vcpkg` is not on PATH yet; see DEBT-0007. Known warnings after the current CMake app build: diff --git a/docs/modernization/debt.md b/docs/modernization/debt.md index 7ddd354..f641d54 100644 --- a/docs/modernization/debt.md +++ b/docs/modernization/debt.md @@ -30,6 +30,7 @@ agent or engineer to remove them without reconstructing context from chat. | DEBT-0009 | Open | Modernization | Android root CMake validation currently builds headless targets only, not APK/package variants | Platform app entrypoints still live in legacy Gradle/CMake projects and need Phase 6 alignment | `powershell -ExecutionPolicy Bypass -File scripts\automation\platform-build.ps1 -Presets android-arm64` | Android standard, Quest, and Focus/Wave package targets consume shared component targets and have package smoke commands | | DEBT-0010 | Open | Modernization | `pp_document` is a pure layer/frame/document model but is not yet wired to legacy `Canvas`, PPI load/save, selection masks, or undo/redo | Keep extraction incremental while preserving app behavior | `ctest --preset desktop-fast --build-config Debug`; `pano_cli create-document --width 64 --height 32 --layers 2` | Legacy document behavior is represented by `pp_document` tests and the app consumes it through a boundary/facade | | DEBT-0011 | Open | Modernization | `package-smoke` validates the Windows CMake app artifact only, not AppX/APK/Apple/WebGL package outputs | Platform package targets are not migrated to root CMake yet | `powershell -ExecutionPolicy Bypass -File scripts\automation\package-smoke.ps1 -Preset windows-msvc-default -Configuration Debug` | Package-smoke covers Windows AppX, Android APK variants, Apple bundles, and WebGL output where local toolchains are present | +| DEBT-0012 | Open | Modernization | `pp_vendor_tinyxml2` compiles the retained vendored tinyxml2 copy for `pp_ui_core` layout parsing | vcpkg is not validated yet, but layout parsing needs a structured XML parser now | `ctest --preset desktop-fast --build-config Debug`; `powershell -ExecutionPolicy Bypass -File scripts\automation\platform-build.ps1 -Presets android-arm64` | Replace with vcpkg tinyxml2 target once desktop and mobile triplets are validated | ## Closed Debt diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index 89506f6..fb4b1e3 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -311,9 +311,10 @@ and layer/frame invariant tests. `pp_renderer_api` has started with renderer-neu texture/readback descriptors and validation tests. `pp_paint_renderer` has started with deterministic CPU layer compositing over renderer extents using the paint blend reference. `pp_ui_core` has started with XML-layout-facing -length parsing and invalid input tests. Continue expanding document behavior -toward legacy Canvas parity and then port OpenGL classes behind the renderer -boundary. +length parsing, tinyxml-backed layout XML parsing, and invalid input tests. +`pano_cli parse-layout` now exercises that path. Continue expanding document +behavior toward legacy Canvas parity and then port OpenGL classes behind the +renderer boundary. Implementation tasks: @@ -517,7 +518,7 @@ Last verified on 2026-06-01: ```powershell cmake --preset windows-msvc-default -cmake --build --preset windows-msvc-default --config Debug --target pp_foundation_binary_stream_tests pp_foundation_parse_tests pp_foundation_trace_tests pp_assets_image_format_tests pp_paint_blend_tests pp_document_tests pp_renderer_api_tests pp_paint_renderer_compositor_tests pp_ui_core_layout_value_tests pano_cli PanoPainter +cmake --build --preset windows-msvc-default --config Debug --target pp_foundation_binary_stream_tests pp_foundation_parse_tests pp_foundation_trace_tests pp_assets_image_format_tests pp_paint_blend_tests pp_document_tests pp_renderer_api_tests pp_paint_renderer_compositor_tests pp_ui_core_layout_value_tests pp_ui_core_layout_xml_tests pano_cli PanoPainter ctest --preset desktop-fast --build-config Debug powershell -ExecutionPolicy Bypass -File scripts\automation\test.ps1 -Preset desktop-fast -Configuration Debug powershell -ExecutionPolicy Bypass -File scripts\automation\build.ps1 -Preset windows-msvc-default -Configuration Debug -Target pano_cli @@ -537,9 +538,11 @@ Results: - `pp_renderer_api_tests` passed. - `pp_paint_renderer_compositor_tests` passed. - `pp_ui_core_layout_value_tests` passed. +- `pp_ui_core_layout_xml_tests` passed. - `pano_cli_create_document_smoke` passed. - `pano_cli_inspect_image_rejects_unsupported` passed as an expected failure test. +- `pano_cli_parse_layout_smoke` passed. - `PanoPainter.exe` built through CMake at `out/build/windows-msvc-default/Debug/PanoPainter.exe`. - PowerShell build/test automation wrappers return JSON summaries and passed diff --git a/scripts/automation/platform-build.ps1 b/scripts/automation/platform-build.ps1 index 8a136d8..10d0169 100644 --- a/scripts/automation/platform-build.ps1 +++ b/scripts/automation/platform-build.ps1 @@ -1,7 +1,7 @@ [CmdletBinding()] param( [string[]]$Presets = @("android-arm64"), - [string[]]$Targets = @("pp_foundation", "pp_assets", "pp_paint", "pp_document", "pp_renderer_api", "pp_paint_renderer", "pp_ui_core", "pano_cli", "pp_foundation_binary_stream_tests", "pp_foundation_parse_tests", "pp_foundation_trace_tests", "pp_assets_image_format_tests", "pp_paint_blend_tests", "pp_document_tests", "pp_renderer_api_tests", "pp_paint_renderer_compositor_tests", "pp_ui_core_layout_value_tests") + [string[]]$Targets = @("pp_foundation", "pp_assets", "pp_paint", "pp_document", "pp_renderer_api", "pp_paint_renderer", "pp_ui_core", "pano_cli", "pp_foundation_binary_stream_tests", "pp_foundation_parse_tests", "pp_foundation_trace_tests", "pp_assets_image_format_tests", "pp_paint_blend_tests", "pp_document_tests", "pp_renderer_api_tests", "pp_paint_renderer_compositor_tests", "pp_ui_core_layout_value_tests", "pp_ui_core_layout_xml_tests") ) $ErrorActionPreference = "Stop" diff --git a/scripts/automation/platform-build.sh b/scripts/automation/platform-build.sh index 0d7694d..733c235 100644 --- a/scripts/automation/platform-build.sh +++ b/scripts/automation/platform-build.sh @@ -3,7 +3,7 @@ set -u preset="${1:-android-arm64}" shift || true -targets="${*:-pp_foundation pp_assets pp_paint pp_document pp_renderer_api pp_paint_renderer pp_ui_core pano_cli pp_foundation_binary_stream_tests pp_foundation_parse_tests pp_foundation_trace_tests pp_assets_image_format_tests pp_paint_blend_tests pp_document_tests pp_renderer_api_tests pp_paint_renderer_compositor_tests pp_ui_core_layout_value_tests}" +targets="${*:-pp_foundation pp_assets pp_paint pp_document pp_renderer_api pp_paint_renderer pp_ui_core pano_cli pp_foundation_binary_stream_tests pp_foundation_parse_tests pp_foundation_trace_tests pp_assets_image_format_tests pp_paint_blend_tests pp_document_tests pp_renderer_api_tests pp_paint_renderer_compositor_tests pp_ui_core_layout_value_tests pp_ui_core_layout_xml_tests}" start="$(date +%s)" cmake --preset "$preset" diff --git a/src/ui_core/layout_xml.cpp b/src/ui_core/layout_xml.cpp new file mode 100644 index 0000000..98a9eee --- /dev/null +++ b/src/ui_core/layout_xml.cpp @@ -0,0 +1,71 @@ +#include "ui_core/layout_xml.h" + +#include "ui_core/layout_value.h" + +#include + +namespace pp::ui { + +namespace { + +[[nodiscard]] pp::foundation::Status visit_element(const tinyxml2::XMLElement& element, LayoutParseSummary& summary) +{ + ++summary.node_count; + + for (const char* name : { "width", "height" }) { + const char* value = element.Attribute(name); + if (value == nullptr) { + continue; + } + + const auto length = parse_layout_length(value); + if (!length) { + return length.status(); + } + ++summary.length_attribute_count; + } + + for (const tinyxml2::XMLElement* child = element.FirstChildElement(); + child != nullptr; + child = child->NextSiblingElement()) { + const auto status = visit_element(*child, summary); + if (!status.ok()) { + return status; + } + } + + return pp::foundation::Status::success(); +} + +} + +pp::foundation::Result parse_layout_xml(std::string_view xml) +{ + if (xml.empty()) { + return pp::foundation::Result::failure( + pp::foundation::Status::invalid_argument("layout XML must not be empty")); + } + + tinyxml2::XMLDocument document; + const auto error = document.Parse(xml.data(), xml.size()); + if (error != tinyxml2::XML_SUCCESS) { + return pp::foundation::Result::failure( + pp::foundation::Status::invalid_argument("layout XML could not be parsed")); + } + + const tinyxml2::XMLElement* root = document.RootElement(); + if (root == nullptr) { + return pp::foundation::Result::failure( + pp::foundation::Status::invalid_argument("layout XML has no root element")); + } + + LayoutParseSummary summary; + const auto status = visit_element(*root, summary); + if (!status.ok()) { + return pp::foundation::Result::failure(status); + } + + return pp::foundation::Result::success(summary); +} + +} diff --git a/src/ui_core/layout_xml.h b/src/ui_core/layout_xml.h new file mode 100644 index 0000000..eb98e40 --- /dev/null +++ b/src/ui_core/layout_xml.h @@ -0,0 +1,17 @@ +#pragma once + +#include "foundation/result.h" + +#include +#include + +namespace pp::ui { + +struct LayoutParseSummary { + std::size_t node_count = 0; + std::size_t length_attribute_count = 0; +}; + +[[nodiscard]] pp::foundation::Result parse_layout_xml(std::string_view xml); + +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 75593f9..cf7eaf8 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -96,6 +96,16 @@ add_test(NAME pp_ui_core_layout_value_tests COMMAND pp_ui_core_layout_value_test set_tests_properties(pp_ui_core_layout_value_tests PROPERTIES LABELS "ui;desktop-fast") +add_executable(pp_ui_core_layout_xml_tests + ui_core/layout_xml_tests.cpp) +target_link_libraries(pp_ui_core_layout_xml_tests PRIVATE + pp_ui_core + pp_test_harness) + +add_test(NAME pp_ui_core_layout_xml_tests COMMAND pp_ui_core_layout_xml_tests) +set_tests_properties(pp_ui_core_layout_xml_tests PROPERTIES + LABELS "ui;desktop-fast") + if(TARGET pano_cli) add_test(NAME pano_cli_create_document_smoke COMMAND pano_cli create-document --width 64 --height 32 --layers 2) @@ -107,4 +117,9 @@ if(TARGET pano_cli) set_tests_properties(pano_cli_inspect_image_rejects_unsupported PROPERTIES LABELS "assets;integration;desktop-fast" WILL_FAIL TRUE) + + add_test(NAME pano_cli_parse_layout_smoke + COMMAND pano_cli parse-layout --path "${CMAKE_CURRENT_SOURCE_DIR}/data/layouts/simple-layout.xml") + set_tests_properties(pano_cli_parse_layout_smoke PROPERTIES + LABELS "ui;integration;desktop-fast") endif() diff --git a/tests/data/layouts/simple-layout.xml b/tests/data/layouts/simple-layout.xml new file mode 100644 index 0000000..c70c1d5 --- /dev/null +++ b/tests/data/layouts/simple-layout.xml @@ -0,0 +1,5 @@ + + +