diff --git a/docs/modernization/debt.md b/docs/modernization/debt.md index 4cbcc405..9e3be4ec 100644 --- a/docs/modernization/debt.md +++ b/docs/modernization/debt.md @@ -18,6 +18,13 @@ agent or engineer to remove them without reconstructing context from chat. ## Recent Reductions +- 2026-06-15: `DEBT-0058`/`DEBT-0063` were narrowed again. App-owned + progress/message/input dialogs created through + `src/legacy_ui_overlay_services.*` now open through checked overlay handles + when the main layout anchor is available instead of only attaching raw root + children; toolbar settings, cloud browser, and other remaining retained + dialog families still stay open under the same debt until they move to the + same lifetime model. - 2026-06-15: `DEBT-0049` was narrowed again. `pp_assets_brush_package_tests` now names the currently accepted legacy PPBR version matrix explicitly: canonical `0.1`, tolerated `0.2`, tolerated `1.1`, and rejected `1.2`. The @@ -1844,9 +1851,9 @@ agent or engineer to remove them without reconstructing context from chat. | DEBT-0055 | Open | Modernization | `src/app.h` now forward-declares retained iOS/macOS/Android/Linux/Web platform handles instead of including platform SDK headers, and full SDK includes are isolated in `src/platform_legacy/legacy_platform_services.cpp`, but the `App` singleton still stores those platform handles directly | Reduce central header platform coupling incrementally without rewriting non-Windows platform entrypoints before Phase 6 | Windows app build; Apple/Android/Linux/Web package smoke once platform root builds are active | Platform handles are owned by injected `pp_platform_*` shell state or services, and `App` has no platform SDK handle fields or platform conditional members | | DEBT-0056 | Open | Modernization | `src/asset.h` is now Android-SDK-free and uses opaque Android asset handles behind `Asset::set_android_asset_manager`, but retained `Asset` still owns a static Android asset-manager bridge and `src/asset.cpp` still performs Android `AAssetManager` reads directly; the current `android-arm64` root preset is headless and does not expose `pp_legacy_assets_io`, though the retained Android standard package `native-lib` now builds through its refreshed C++23 CMake path | Reduce legacy asset I/O header coupling without rewriting Android asset loading before the asset manager/storage boundary exists | Windows app build; `powershell -ExecutionPolicy Bypass -File scripts\automation\platform-build.ps1 -Presets android-arm64 -Targets pp_assets`; `powershell -ExecutionPolicy Bypass -File scripts\automation\android-legacy-package-build.ps1 -Packages standard` | Android asset loading is owned by injected asset storage/platform services or `pp_assets` file providers, with no static Android asset manager on `Asset` | | DEBT-0061 | Open | Modernization | Desktop XR runtime selection now lives in tested `pp_platform_api` policy and prefers OpenXR, but `WindowsPlatformServices` still reports OpenXR unavailable and reaches the retained OpenVR SDK bridge as a legacy fallback; Windows runtime deployment copies `openvr_api.dll` beside `PanoPainter.exe` until that fallback is removed | Preserve current desktop VR behavior while replacing OpenVR with OpenXR behind the platform/renderer boundary | `pp_platform_api_tests`; `ctest --preset desktop-fast --build-config Debug -R pp_platform_api_tests --output-on-failure`; `cmake --build --preset windows-msvc-default --config Debug --target PanoPainter` | Add an OpenXR SDK/package target, implement desktop OpenXR startup/shutdown/pose/controller submission behind `pp_platform_vr` or `PlatformServices`, validate parity with mocked/runtime smoke coverage, and remove `libs/openvr` plus the OpenVR link/include paths from root CMake | -| DEBT-0063 | Open | Modernization | `pp_ui_core` now owns tested `NodeLifetimeTree` and `UiOverlayLifetime` models for checked handles, scoped callback connections, subtree destruction, capture release, and mutation-safe dispatch. `src/legacy_ui_overlay_services.*` now keeps a root-scoped checked-overlay registry for retained nodes, and `src/node_panel_layer.h/.cpp`, `src/node_combobox.cpp`, `src/node_dialog_open.cpp`, `src/node_dialog_browse.cpp`, `src/node_panel_stroke.cpp`, `src/node_panel_brush.cpp`, `src/node_panel_color.cpp`, `src/node_panel_grid.cpp`, `src/node_dialog_picker.cpp`, `src/legacy_quick_ui_services.cpp`, and `src/node_popup_menu.h/.cpp` now route popup/dialog/menu lifetime through checked overlay handles or handle-based close instead of raw attach-and-destroy callbacks. Layer-panel selection/order state also stays on shared ownership rather than raw child pointers while mutation-safe close remains open in other legacy panel/dialog families | Preserve current UI behavior while completing safe panel/dialog lifetime migration incrementally | `pp_ui_core_layout_xml_tests`; `pp_ui_core_node_lifetime_tests`; `pp_ui_core_overlay_lifetime_tests`; `tests/ui_core/node_lifetime_tests.cpp:destroy_subtree_clears_child_connections`; `tests/ui_core/overlay_lifetime_tests.cpp:double_close_overlay_returns_invalid_argument`; `ctest --preset desktop-fast --build-config Debug`; `cmake --build --preset windows-msvc-default --config Debug --target panopainter_app pp_ui_core_node_lifetime_tests pp_ui_core_overlay_lifetime_tests` | Remaining legacy popup/dialog families still use non-handle ownership and open lifetimes; migration stays open until their surfaces are converted, including lifecycle safety parity checks | +| DEBT-0063 | Open | Modernization | `pp_ui_core` now owns tested `NodeLifetimeTree` and `UiOverlayLifetime` models for checked handles, scoped callback connections, subtree destruction, capture release, and mutation-safe dispatch. `src/legacy_ui_overlay_services.*` now keeps a root-scoped checked-overlay registry for retained nodes; app-owned progress/message/input dialogs created through that service now also open through checked overlay handles when the main layout anchor is available; and `src/node_panel_layer.h/.cpp`, `src/node_combobox.cpp`, `src/node_dialog_open.cpp`, `src/node_dialog_browse.cpp`, `src/node_panel_stroke.cpp`, `src/node_panel_brush.cpp`, `src/node_panel_color.cpp`, `src/node_panel_grid.cpp`, `src/node_dialog_picker.cpp`, `src/legacy_quick_ui_services.cpp`, and `src/node_popup_menu.h/.cpp` now route popup/dialog/menu lifetime through checked overlay handles or handle-based close instead of raw attach-and-destroy callbacks. Layer-panel selection/order state also stays on shared ownership rather than raw child pointers while mutation-safe close remains open in other legacy panel/dialog families | Preserve current UI behavior while completing safe panel/dialog lifetime migration incrementally | `pp_ui_core_layout_xml_tests`; `pp_ui_core_node_lifetime_tests`; `pp_ui_core_overlay_lifetime_tests`; `tests/ui_core/node_lifetime_tests.cpp:destroy_subtree_clears_child_connections`; `tests/ui_core/overlay_lifetime_tests.cpp:double_close_overlay_returns_invalid_argument`; `ctest --preset desktop-fast --build-config Debug`; `cmake --build --preset windows-msvc-default --config Debug --target panopainter_app pp_ui_core_node_lifetime_tests pp_ui_core_overlay_lifetime_tests` | Remaining legacy popup/dialog families still use non-handle ownership and open lifetimes; migration stays open until their surfaces are converted, including lifecycle safety parity checks | | DEBT-0057 | Open | Modernization | Default canvas allocation size now dispatches through `PlatformServices::default_canvas_resolution`, removing the `CANVAS_RES` platform macro from `src/canvas.h`; WebGL's retained 512 default now lives in tested `pp_platform_api` policy behind injectable `pp::platform::WebPlatformServices`, but the Web shell still reaches the default implementation through the retained fallback until a dedicated Web service is injected directly | Preserve WebGL memory behavior while moving canvas creation policy out of shared canvas headers and into the platform boundary | `pp_platform_api_tests`; `ctest --preset desktop-fast --build-config Debug -R pp_platform_api_tests`; Windows app build; WebGL package smoke once root Web build exists | Default canvas resolution is owned by injected `pp_platform_*` services for every supported platform, with no WebGL branch in the legacy fallback | -| DEBT-0058 | Open | Modernization | App-level progress/message/input dialog metadata, including message-dialog OK/cancel captions, now consumes pure `pp_app_core` through `App::show_progress`, `App::message_box`, `App::input_box`, `pano_cli plan-app-dialog`, and `pp_app_core_app_dialog_tests`; live execution is centralized in `src/legacy_app_dialog_services.*`, retained root insertion now routes through `src/legacy_ui_overlay_services.*`, and whats-new dialog state persistence routes through `src/legacy_preference_storage.*`, but the bridge still creates retained `NodeProgressBar`, `NodeMessageBox`, and `NodeInputBox` instances with raw callback/lifetime ownership | Preserve current app-shell dialog behavior while moving shared dialog policy toward UI/app services | `pp_app_core_app_dialog_tests`; `pano_cli plan-app-dialog --kind progress --total -4`; `pano_cli plan-app-dialog --kind message --cancel`; `pano_cli plan-app-dialog --kind input --ok-caption Save`; `ctest --preset desktop-fast --build-config Debug`; Windows app build | Progress/message/input dialog creation, callback wiring, layout insertion, lifetime ownership, and headless automation are owned by injected app/UI services with `App` methods acting only as adapters | +| DEBT-0058 | Open | Modernization | App-level progress/message/input dialog metadata, including message-dialog OK/cancel captions, now consumes pure `pp_app_core` through `App::show_progress`, `App::message_box`, `App::input_box`, `pano_cli plan-app-dialog`, and `pp_app_core_app_dialog_tests`; live execution is centralized in `src/legacy_app_dialog_services.*`, retained dialog opening now routes through `src/legacy_ui_overlay_services.*` with checked-handle registration when the main layout anchor exists, and whats-new dialog state persistence routes through `src/legacy_preference_storage.*`, but the bridge still creates retained `NodeProgressBar`, `NodeMessageBox`, and `NodeInputBox` instances and still relies on legacy callback/node implementations for final close behavior | Preserve current app-shell dialog behavior while moving shared dialog policy toward UI/app services | `pp_app_core_app_dialog_tests`; `pano_cli plan-app-dialog --kind progress --total -4`; `pano_cli plan-app-dialog --kind message --cancel`; `pano_cli plan-app-dialog --kind input --ok-caption Save`; `ctest --preset desktop-fast --build-config Debug`; Windows app build | Progress/message/input dialog creation, callback wiring, layout insertion, lifetime ownership, and headless automation are owned by injected app/UI services with `App` methods acting only as adapters | | DEBT-0059 | Open | Modernization | iOS root CMake headless builds assign generated bundle identifiers and disable code signing for executable test/tool targets | The current Apple gate is compile validation for shared component targets; signed iOS app/package validation is not migrated to root CMake yet | `powershell -ExecutionPolicy Bypass -File scripts\automation\apple-remote-build.ps1 -Presets macos,ios-simulator,ios-device`; `sh scripts/automation/platform-build.sh "ios-device"` on `panopainter-mac` | Root CMake owns the signed Apple app/package targets, package-smoke validates Apple bundles where signing material is available, and headless iOS test/tool targets are either excluded from signed package builds or use explicit test-runner signing policy | | DEBT-0064 | Open | Modernization | Stroke-preview result copy now routes through the renderer-facing `pp::paint_renderer::copy_stroke_preview_result_to_texture(...)` callback-only contract in `src/paint_renderer/compositor.h`; the node-level copy wrapper, final-composite wrapper, and pass-sequence wrapper are removed, and `pp_paint_renderer_compositor_tests` no longer needs either a test-local `Texture2D::bind()` definition. Live preview still owns concrete texture binding and OpenGL execution around the remaining preview flow | `tests` are configured before `pp_legacy_engine`, and linking the app legacy texture object into this planner test would still couple the renderer-neutral test target back to the app target graph | `cmake --build --preset windows-msvc-default --config Debug --target pp_paint_renderer_compositor_tests`; `ctest --preset desktop-fast --build-config Debug -R "pp_paint_renderer_compositor" --output-on-failure` | Keep preview-copy behavior on renderer-facing callback-only seams and move the remaining live preview copy execution behind renderer-owned services so the remaining preview flow can become fully app-texture-free | diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index 1a5e3886..0b17466c 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -68,6 +68,11 @@ contract freeze and conformance (`RND-007`, `RND-008`), opt-in Vulkan/Metal lab targets (`RND-009`, `RND-010`), UI thread-affinity and checked-handle migration (`UI-001`, `UI-002`), render/UI queue race coverage (`APP-001`), package gates for backend/UI work (`PLT-010`), and historical task cleanup (`AUD-001`). +Recent 2026-06-15 app-dialog lifetime cleanup now routes app-owned +progress/message/input overlays through checked handle registration in +`src/legacy_ui_overlay_services.*` before they fall back to retained dialog +node close helpers, narrowing another app-owned `DEBT-0058`/`DEBT-0063` +surface without changing dialog plans or captions. Recent 2026-06-13 retained preview reductions continue to narrow DEBT-0036: `NodeStrokePreview::draw_stroke_immediate()` now also routes diff --git a/docs/modernization/tasks.md b/docs/modernization/tasks.md index b6e9ea55..55194df0 100644 --- a/docs/modernization/tasks.md +++ b/docs/modernization/tasks.md @@ -34,11 +34,11 @@ auditable steps rather than by subjective estimates. | Build and CMake ownership | 15 | 13 | Root CMake owns active source lists, app/tool targets, and retained package entrypoints. | | Test and automation coverage | 15 | 9 | Headless, platform, package, and focused validation commands exist and are current. | | Pure component behavior ownership | 15 | 8 | Behavior lives in `pp_*` components and is consumed by live adapters. | -| Legacy adapter retirement | 20 | 9 | `legacy_*_services` and singleton bridges are deleted or reduced to trivial composition. | +| Legacy adapter retirement | 20 | 10 | `legacy_*_services` and singleton bridges are deleted or reduced to trivial composition. | | Renderer boundary and OpenGL parity | 15 | 11 | Live render/export/readback paths execute through renderer interfaces with parity checks. | | Platform and package parity | 10 | 8 | Required platforms have root CMake/package validation and injected platform services. | | Hardening and future backend readiness | 10 | 4 | Edge, fuzz, golden, stress, and backend-lab gates exist for high-risk paths. | -| **Total** | **100** | **63** | Only completed tasks below may change this number. | +| **Total** | **100** | **64** | Only completed tasks below may change this number. | When updating `Current`, add a dated note under "Completed Task Log" with the task id, points moved, validation command, and commit hash. @@ -259,6 +259,38 @@ ctest --preset desktop-fast --build-config Debug -R "pp_ui_core_overlay_lifetime cmake --build --preset windows-msvc-default --config Debug --target PanoPainter ``` +### ADP-006 - Move App-Owned Dialog Overlays To Checked Handles + +Status: Done +Score: +1 legacy adapter retirement +Debt: `DEBT-0058`, `DEBT-0063` +Scope: `src/legacy_ui_overlay_services.*`, `src/legacy_app_dialog_services.*`, +dialog tests only + +Goal: + +Move the retained app-owned progress/message/input dialog overlays off the +attach-only root insertion path so the factory-owned live dialogs participate in +the checked overlay lifetime registry before broader dialog cleanup. + +Done Checks: + +- `App::show_progress`, `App::message_box`, and `App::input_box` still preserve + existing captions, cancel behavior, and keyboard behavior. +- `create_legacy_progress_dialog_overlay`, `create_legacy_message_dialog_overlay`, + and `create_legacy_input_dialog_overlay` open through checked overlay handles + when the main layout anchor is available instead of only attaching raw root + children. +- `DEBT-0058` and `DEBT-0063` describe the reduced app-dialog lifetime surface + and the remaining retained dialog families. + +Validation: + +```powershell +ctest --preset desktop-fast --build-config Debug -R "pp_app_core_app_dialog|pp_ui_core_node_lifetime|pp_ui_core_overlay_lifetime" --output-on-failure +cmake --build --preset windows-msvc-default --config Debug --target PanoPainter +``` + ### RND-001 - Make Pure Equirectangular Export The Primary Success Path Status: Done diff --git a/src/legacy_ui_overlay_services.cpp b/src/legacy_ui_overlay_services.cpp index e5cf5c02..635c477d 100644 --- a/src/legacy_ui_overlay_services.cpp +++ b/src/legacy_ui_overlay_services.cpp @@ -15,6 +15,25 @@ namespace pp::panopainter { namespace { +template +void attach_legacy_app_overlay_with_handle_or_fallback( + App& app, + const std::shared_ptr& node) noexcept +{ + if (!node) { + return; + } + + if (auto* anchor = app.layout[app.main_id]) { + const auto overlay = open_legacy_overlay_node_with_handle(*anchor, node); + if (overlay) { + return; + } + } + + (void)attach_legacy_overlay_node(app, node); +} + struct LegacyOverlayContext { pp::ui::NodeHandle root_handle {}; pp::ui::NodeLifetimeTree tree {}; @@ -320,7 +339,7 @@ std::shared_ptr create_legacy_progress_dialog_overlay( progress->m_title->set_text(plan.title.c_str()); progress->m_total = plan.total; progress->m_count = plan.count; - (void)attach_legacy_overlay_node(app, progress); + attach_legacy_app_overlay_with_handle_or_fallback(app, progress); return progress; } @@ -336,7 +355,7 @@ std::shared_ptr create_legacy_message_dialog_overlay( message->btn_cancel->m_text->set_text(plan.cancel_caption.c_str()); else close_legacy_dialog_node(*message->btn_cancel); - (void)attach_legacy_overlay_node(app, message); + attach_legacy_app_overlay_with_handle_or_fallback(app, message); return message; } @@ -348,7 +367,7 @@ std::shared_ptr create_legacy_input_dialog_overlay( input->m_title->set_text(plan.title.c_str()); input->m_field_name->set_text(plan.field_name.c_str()); input->btn_ok->m_text->set_text(plan.ok_caption.c_str()); - (void)attach_legacy_overlay_node(app, input); + attach_legacy_app_overlay_with_handle_or_fallback(app, input); return input; }