Integrate dialog export and Apple service teams
This commit is contained in:
@@ -311,24 +311,12 @@ endif()
|
|||||||
|
|
||||||
if(PP_BUILD_APP)
|
if(PP_BUILD_APP)
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
add_library(pp_legacy_vendor OBJECT
|
set(PP_LEGACY_FMT_SOURCES
|
||||||
${PP_VENDOR_SOURCES})
|
libs/fmt/src/format.cc
|
||||||
target_link_libraries(pp_legacy_vendor
|
libs/fmt/src/posix.cc)
|
||||||
PUBLIC
|
set(PP_LEGACY_VENDOR_SOURCES ${PP_VENDOR_SOURCES})
|
||||||
pp_project_options
|
list(REMOVE_ITEM PP_LEGACY_VENDOR_SOURCES ${PP_LEGACY_FMT_SOURCES})
|
||||||
PRIVATE
|
set(PP_LEGACY_VENDOR_DEFINITIONS
|
||||||
pp_project_warnings)
|
|
||||||
target_include_directories(pp_legacy_vendor
|
|
||||||
PUBLIC
|
|
||||||
${PP_LEGACY_INCLUDE_DIRS})
|
|
||||||
file(REMOVE_RECURSE "${CMAKE_CURRENT_BINARY_DIR}/compat/fmt-vs2026")
|
|
||||||
if(MSVC_VERSION GREATER_EQUAL 1945)
|
|
||||||
target_compile_options(pp_legacy_vendor
|
|
||||||
PUBLIC
|
|
||||||
/U_SECURE_SCL)
|
|
||||||
endif()
|
|
||||||
target_compile_definitions(pp_legacy_vendor
|
|
||||||
PUBLIC
|
|
||||||
ENUM_BITFIELDS_NOT_SUPPORTED
|
ENUM_BITFIELDS_NOT_SUPPORTED
|
||||||
UNICODE
|
UNICODE
|
||||||
_UNICODE
|
_UNICODE
|
||||||
@@ -338,8 +326,51 @@ if(PP_BUILD_APP)
|
|||||||
_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING
|
_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING
|
||||||
_CONSOLE
|
_CONSOLE
|
||||||
WITH_CURL=1)
|
WITH_CURL=1)
|
||||||
|
|
||||||
|
add_library(pp_legacy_vendor OBJECT
|
||||||
|
${PP_LEGACY_VENDOR_SOURCES})
|
||||||
|
target_link_libraries(pp_legacy_vendor
|
||||||
|
PUBLIC
|
||||||
|
pp_project_options
|
||||||
|
PRIVATE
|
||||||
|
pp_project_warnings)
|
||||||
|
target_include_directories(pp_legacy_vendor
|
||||||
|
PUBLIC
|
||||||
|
${PP_LEGACY_INCLUDE_DIRS})
|
||||||
|
file(REMOVE_RECURSE "${CMAKE_CURRENT_BINARY_DIR}/compat/fmt-vs2026")
|
||||||
|
|
||||||
|
add_library(pp_legacy_fmt OBJECT
|
||||||
|
${PP_LEGACY_FMT_SOURCES})
|
||||||
|
target_link_libraries(pp_legacy_fmt
|
||||||
|
PUBLIC
|
||||||
|
pp_project_options
|
||||||
|
PRIVATE
|
||||||
|
pp_project_warnings)
|
||||||
|
target_include_directories(pp_legacy_fmt
|
||||||
|
PUBLIC
|
||||||
|
${PP_LEGACY_INCLUDE_DIRS})
|
||||||
|
if(MSVC_VERSION GREATER_EQUAL 1945)
|
||||||
|
set(PP_FMT_VS2026_COMPAT_HEADER "${CMAKE_CURRENT_BINARY_DIR}/compat/fmt-vs2026-secure-scl.h")
|
||||||
|
file(WRITE "${PP_FMT_VS2026_COMPAT_HEADER}"
|
||||||
|
"#pragma once\n"
|
||||||
|
"#include <yvals.h>\n"
|
||||||
|
"#ifdef _SECURE_SCL\n"
|
||||||
|
"#undef _SECURE_SCL\n"
|
||||||
|
"#endif\n")
|
||||||
|
target_compile_options(pp_legacy_fmt
|
||||||
|
PUBLIC
|
||||||
|
/FI"${PP_FMT_VS2026_COMPAT_HEADER}")
|
||||||
|
endif()
|
||||||
|
target_compile_definitions(pp_legacy_vendor
|
||||||
|
PUBLIC
|
||||||
|
${PP_LEGACY_VENDOR_DEFINITIONS})
|
||||||
|
target_compile_definitions(pp_legacy_fmt
|
||||||
|
PUBLIC
|
||||||
|
${PP_LEGACY_VENDOR_DEFINITIONS})
|
||||||
set_target_properties(pp_legacy_vendor PROPERTIES
|
set_target_properties(pp_legacy_vendor PROPERTIES
|
||||||
VS_GLOBAL_CharacterSet "Unicode")
|
VS_GLOBAL_CharacterSet "Unicode")
|
||||||
|
set_target_properties(pp_legacy_fmt PROPERTIES
|
||||||
|
VS_GLOBAL_CharacterSet "Unicode")
|
||||||
|
|
||||||
add_library(pp_legacy_renderer_gl OBJECT
|
add_library(pp_legacy_renderer_gl OBJECT
|
||||||
${PP_LEGACY_RENDERER_GL_SOURCES})
|
${PP_LEGACY_RENDERER_GL_SOURCES})
|
||||||
@@ -436,6 +467,7 @@ if(PP_BUILD_APP)
|
|||||||
$<TARGET_OBJECTS:pp_legacy_assets_io>
|
$<TARGET_OBJECTS:pp_legacy_assets_io>
|
||||||
$<TARGET_OBJECTS:pp_legacy_paint_document>
|
$<TARGET_OBJECTS:pp_legacy_paint_document>
|
||||||
$<TARGET_OBJECTS:pp_legacy_renderer_gl>
|
$<TARGET_OBJECTS:pp_legacy_renderer_gl>
|
||||||
|
$<TARGET_OBJECTS:pp_legacy_fmt>
|
||||||
$<TARGET_OBJECTS:pp_legacy_vendor>)
|
$<TARGET_OBJECTS:pp_legacy_vendor>)
|
||||||
|
|
||||||
target_link_libraries(pp_legacy_engine
|
target_link_libraries(pp_legacy_engine
|
||||||
|
|||||||
@@ -18,11 +18,35 @@ agent or engineer to remove them without reconstructing context from chat.
|
|||||||
|
|
||||||
## Recent Reductions
|
## Recent Reductions
|
||||||
|
|
||||||
|
- 2026-06-12: DEBT-0060 was narrowed. Retained Android standard/Quest/Focus
|
||||||
|
package CMake files no longer generate or prepend a patched `nanort.h`
|
||||||
|
overlay, and the compatibility helper was removed; retained Android standard
|
||||||
|
validation now gets past `nanort` and fails later in `legacy_gl_runtime_dispatch.h`
|
||||||
|
because Android/GLES does not expose desktop debug callback symbols.
|
||||||
|
- 2026-06-12: DEBT-0058 and DEBT-0063 were narrowed. App-level
|
||||||
|
progress/message/input dialogs now route through a pure
|
||||||
|
`pp::app::AppDialogFactory`; retained `NodeProgressBar`, `NodeMessageBox`,
|
||||||
|
and `NodeInputBox` construction and root attachment are centralized behind
|
||||||
|
the legacy dialog/overlay factory layer.
|
||||||
|
- 2026-06-12: DEBT-0017, DEBT-0051, and DEBT-0055 were narrowed. Apple
|
||||||
|
document browse roots, file/image/save/directory picker dispatch, macOS
|
||||||
|
empty-selection filtering, working-directory picker policy, and display-path
|
||||||
|
formatting now live in `src/platform_apple/apple_platform_services.*`; the
|
||||||
|
retained legacy platform adapter delegates Apple document-platform calls to
|
||||||
|
that boundary while `App` still stores Apple platform handles directly.
|
||||||
|
- 2026-06-12: DEBT-0010, DEBT-0036, and DEBT-0043 were narrowed. Payload-complete
|
||||||
|
depth export now generates deterministic image/depth PNG payloads through
|
||||||
|
pure `pp_paint_renderer` and writes them through the app-core two-payload
|
||||||
|
writer before retained fallback. Retained OpenGL depth rendering/readback is
|
||||||
|
now fallback-only for unsupported targets, incomplete readback, or writer
|
||||||
|
failure; the pure path currently uses the extracted fixed perspective export
|
||||||
|
view rather than captured live legacy camera state.
|
||||||
- 2026-06-12: DEBT-0062 was closed. The generated VS 2026 fmt overlay was
|
- 2026-06-12: DEBT-0062 was closed. The generated VS 2026 fmt overlay was
|
||||||
removed from root CMake, reused build trees delete the stale
|
removed from root CMake, reused build trees delete the stale
|
||||||
`compat/fmt-vs2026` directory, and VS 2026 consumers now compile retained fmt
|
`compat/fmt-vs2026` directory, and VS 2026 retained fmt sources now use a
|
||||||
with `_SECURE_SCL` undefined so the removed `stdext::checked_array_iterator`
|
generated forced-include compatibility header that includes the STL
|
||||||
path is not selected.
|
configuration once and undefines `_SECURE_SCL` before fmt selects its removed
|
||||||
|
checked-array-iterator branch.
|
||||||
- 2026-06-12: DEBT-0039, DEBT-0040, and DEBT-0042 were narrowed. Document-open,
|
- 2026-06-12: DEBT-0039, DEBT-0040, and DEBT-0042 were narrowed. Document-open,
|
||||||
close, save, save-before-workflow, Save As, Save Version, and new-document
|
close, save, save-before-workflow, Save As, Save Version, and new-document
|
||||||
history effects now surface as explicit `pp_app_core` history outputs; the
|
history effects now surface as explicit `pp_app_core` history outputs; the
|
||||||
@@ -900,7 +924,7 @@ agent or engineer to remove them without reconstructing context from chat.
|
|||||||
| DEBT-0007 | Open | Modernization | `vcpkg.json` and `windows-msvc-vcpkg-headless` are validated for the headless Windows component matrix, and root CMake now exposes a focused `panopainter_platform_build_vcpkg_ui_core` target for the vcpkg-backed `pp_ui_core`/tinyxml2 boundary, but app targets still use vendored libraries and Android/Apple triplets are not proven | Dependency migration must stay incremental while SDK/patched/vendor dependencies remain in use | `cmake --preset windows-msvc-vcpkg-headless`; `ctest --preset desktop-fast-vcpkg --build-config Debug`; `cmake --build --preset windows-msvc-default --config Debug --target panopainter_platform_build_vcpkg_ui_core` | Component targets consume vcpkg packages where reliable and desktop app, Android, and Apple triplets are validated or explicitly documented as permanent vendor exceptions |
|
| DEBT-0007 | Open | Modernization | `vcpkg.json` and `windows-msvc-vcpkg-headless` are validated for the headless Windows component matrix, and root CMake now exposes a focused `panopainter_platform_build_vcpkg_ui_core` target for the vcpkg-backed `pp_ui_core`/tinyxml2 boundary, but app targets still use vendored libraries and Android/Apple triplets are not proven | Dependency migration must stay incremental while SDK/patched/vendor dependencies remain in use | `cmake --preset windows-msvc-vcpkg-headless`; `ctest --preset desktop-fast-vcpkg --build-config Debug`; `cmake --build --preset windows-msvc-default --config Debug --target panopainter_platform_build_vcpkg_ui_core` | Component targets consume vcpkg packages where reliable and desktop app, Android, and Apple triplets are validated or explicitly documented as permanent vendor exceptions |
|
||||||
| DEBT-0008 | Open | Modernization | `windows-msvc-default` and `windows-msvc-vcpkg-headless` explicitly select Visual Studio 18 2026 for local validation, but non-VS2026 CMake executables on PATH may not know that generator | The local machine has VS 2026, but using an older CMake can still default to Ninja or reject the VS 2026 generator | `cmake --preset windows-msvc-default`; `cmake --build --preset windows-msvc-default --config Debug --target PanoPainter`; `ctest --preset desktop-fast --build-config Debug` | The repo automation invokes or locates a CMake executable that supports `Visual Studio 18 2026`, and VS 2026 generator validation is the normal Windows path without manual tool selection |
|
| DEBT-0008 | Open | Modernization | `windows-msvc-default` and `windows-msvc-vcpkg-headless` explicitly select Visual Studio 18 2026 for local validation, but non-VS2026 CMake executables on PATH may not know that generator | The local machine has VS 2026, but using an older CMake can still default to Ninja or reject the VS 2026 generator | `cmake --preset windows-msvc-default`; `cmake --build --preset windows-msvc-default --config Debug --target PanoPainter`; `ctest --preset desktop-fast --build-config Debug` | The repo automation invokes or locates a CMake executable that supports `Visual Studio 18 2026`, and VS 2026 generator validation is the normal Windows path without manual tool selection |
|
||||||
| DEBT-0009 | Open | Modernization | Android root CMake validation currently builds headless targets only, while retained standard/Quest/Focus package CMake paths now have a refreshed CMake 3.10/C++23 baseline outside root CMake; automation queries `sdkmanager`, installs newer or missing SDK Manager NDK/CMake packages, selects the resulting pair before configure, and reports update decisions; root CMake exposes non-default platform-build and retained native package validation targets | 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`; `cmake --build --preset android-x64`; `cmake --build --preset android-quest-arm64`; `cmake --build --preset android-focus-arm64`; `cmake --build --preset windows-msvc-default --config Debug --target panopainter_platform_build_android_assets`; `powershell -ExecutionPolicy Bypass -File scripts\automation\android-legacy-package-build.ps1 -Packages standard`; `powershell -ExecutionPolicy Bypass -File scripts\automation\android-legacy-package-build.ps1 -Packages quest,focus -ConfigureOnly`; `powershell -ExecutionPolicy Bypass -File scripts\automation\package-smoke.ps1 -ReadinessOnly -AndroidNativeChecks -PackageKinds android-standard-apk,android-quest-apk,android-focus-apk`; `cmake --build --preset windows-msvc-default --config Debug --target panopainter_android_native_package_smoke` | Android standard, Quest, and Focus/Wave package targets consume shared component targets and have package smoke commands |
|
| DEBT-0009 | Open | Modernization | Android root CMake validation currently builds headless targets only, while retained standard/Quest/Focus package CMake paths now have a refreshed CMake 3.10/C++23 baseline outside root CMake; automation queries `sdkmanager`, installs newer or missing SDK Manager NDK/CMake packages, selects the resulting pair before configure, and reports update decisions; root CMake exposes non-default platform-build and retained native package validation targets | 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`; `cmake --build --preset android-x64`; `cmake --build --preset android-quest-arm64`; `cmake --build --preset android-focus-arm64`; `cmake --build --preset windows-msvc-default --config Debug --target panopainter_platform_build_android_assets`; `powershell -ExecutionPolicy Bypass -File scripts\automation\android-legacy-package-build.ps1 -Packages standard`; `powershell -ExecutionPolicy Bypass -File scripts\automation\android-legacy-package-build.ps1 -Packages quest,focus -ConfigureOnly`; `powershell -ExecutionPolicy Bypass -File scripts\automation\package-smoke.ps1 -ReadinessOnly -AndroidNativeChecks -PackageKinds android-standard-apk,android-quest-apk,android-focus-apk`; `cmake --build --preset windows-msvc-default --config Debug --target panopainter_android_native_package_smoke` | 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/undo-history model with alpha-lock metadata, snapshot construction, per-layer frame metadata, renderer-free RGBA8 face payload storage, snapshot-embedded face-payload validation, renderer-free alpha8 selection-mask storage, PPI import/export helpers, stroke-script-to-face-payload CLI automation, `pp_paint_renderer` document face/frame compositors, renderer-neutral six-face texture upload, pure six-face PNG export, pure equirectangular PNG export, pure equirectangular JPEG+XMP export, pure layer/animation-frame PNG collection export, shared document-frame export readiness reporting, depth export render-plan reporting, OpenGL command-planner validation through CLI render automation, live Canvas snapshot projection through `pp_app_core`/`legacy_document_canvas_services`, captured RGBA8 payload attachment to `pp_document`, live Save/Save As/Save Version/save-before-workflow snapshot-readiness reporting before retained save execution, pure app-core PPI export for payload-complete canvas snapshots, payload-complete canvas-snapshot renderer-upload plus face-PNG export automation, live cube-face face-PNG writer execution using app-core face target planning and write/publish service dispatch with retained fallback, live PNG/JPEG equirectangular writer execution using the paint-renderer equirectangular exports plus app-core file write/publish dispatch with retained fallback, live payload-complete layer/animation-frame collection writer execution using paint-renderer PNG sequences and app-core collection write/publish dispatch with retained fallback, tested app-core document-snapshot export route policy for writer versus retained fallback including current-platform support, and live equirectangular/layer/animation-frame/depth/cube-face export snapshot/render/export-readiness reporting through the shared readiness helper plus the depth render plan, but action-command adoption, live save-writer replacement, Web and incomplete-readback collection handoff, progress/threading parity, broader renderer-owned export execution, exact GPU/golden parity, depth render/readback replacement, and renderer-owned cube-face readback ownership are not yet wired | Keep extraction incremental while preserving app behavior | `ctest --preset desktop-fast --build-config Debug`; `pano_cli create-document --width 64 --height 32 --layers 2`; `pano_cli load-project --path tests\data\projects\minimal-project.ppi`; `pano_cli simulate-document-render --width 64 --height 32`; `pano_cli plan-canvas-document-snapshot --width 64 --height 32`; `pano_cli plan-canvas-document-snapshot --captured-face-payloads-per-layer 1`; `pano_cli plan-export-snapshot-route --kind layers-collection --captured-face-payloads 3 --pending-face-payloads 6`; `pp_document_tests`; `pp_document_ppi_import_tests`; `pp_document_ppi_export_tests`; `pp_paint_renderer_compositor_tests`; `pp_app_core_document_canvas_tests`; `pp_app_core_document_export_tests`; `pano_cli_simulate_document_edits_smoke`; `pano_cli_simulate_document_export_smoke`; `pano_cli_simulate_document_render_smoke`; `pano_cli_plan_canvas_document_snapshot_smoke`; `pano_cli_plan_canvas_document_snapshot_payload_smoke`; `pano_cli_plan_export_snapshot_route_pending_smoke`; `pano_cli_save_document_project_roundtrip_smoke`; `pano_cli_apply_stroke_script_roundtrip_smoke`; `pano_cli_apply_stroke_script_rejects_tiny_canvas` | Legacy document behavior is represented by `pp_document`/`pp_paint_renderer` tests and the app consumes it through a boundary/facade |
|
| DEBT-0010 | Open | Modernization | `pp_document` is a pure layer/frame/document/undo-history model with alpha-lock metadata, snapshot construction, per-layer frame metadata, renderer-free RGBA8 face payload storage, snapshot-embedded face-payload validation, renderer-free alpha8 selection-mask storage, PPI import/export helpers, stroke-script-to-face-payload CLI automation, `pp_paint_renderer` document face/frame compositors, renderer-neutral six-face texture upload, pure six-face PNG export, pure equirectangular PNG export, pure equirectangular JPEG+XMP export, pure layer/animation-frame PNG collection export, pure depth image/depth PNG export for payload-complete snapshots, shared document-frame export readiness reporting, depth export render-plan reporting, OpenGL command-planner validation through CLI render automation, live Canvas snapshot projection through `pp_app_core`/`legacy_document_canvas_services`, captured RGBA8 payload attachment to `pp_document`, live Save/Save As/Save Version/save-before-workflow snapshot-readiness reporting before retained save execution, pure app-core PPI export for payload-complete canvas snapshots, payload-complete canvas-snapshot renderer-upload plus face-PNG export automation, live cube-face face-PNG writer execution using app-core face target planning and write/publish service dispatch with retained fallback, live PNG/JPEG equirectangular writer execution using the paint-renderer equirectangular exports plus app-core file write/publish dispatch with retained fallback, live payload-complete layer/animation-frame collection writer execution using paint-renderer PNG sequences and app-core collection write/publish dispatch with retained fallback, live payload-complete depth export execution using pure paint-renderer PNG payloads plus app-core two-payload writing with retained fallback, tested app-core document-snapshot export route policy for writer versus retained fallback including current-platform support, and live equirectangular/layer/animation-frame/depth/cube-face export snapshot/render/export-readiness reporting through the shared readiness helper plus the depth render plan, but action-command adoption, live save-writer replacement, Web and incomplete-readback collection handoff, progress/threading parity, broader renderer-owned export execution, exact GPU/golden parity, live-camera depth parity, and renderer-owned cube-face readback ownership are not yet wired | Keep extraction incremental while preserving app behavior | `ctest --preset desktop-fast --build-config Debug`; `pano_cli create-document --width 64 --height 32 --layers 2`; `pano_cli load-project --path tests\data\projects\minimal-project.ppi`; `pano_cli simulate-document-render --width 64 --height 32`; `pano_cli plan-canvas-document-snapshot --width 64 --height 32`; `pano_cli plan-canvas-document-snapshot --captured-face-payloads-per-layer 1`; `pano_cli plan-export-snapshot-route --kind layers-collection --captured-face-payloads 3 --pending-face-payloads 6`; `pp_document_tests`; `pp_document_ppi_import_tests`; `pp_document_ppi_export_tests`; `pp_paint_renderer_compositor_tests`; `pp_app_core_document_canvas_tests`; `pp_app_core_document_export_tests`; `pano_cli_simulate_document_edits_smoke`; `pano_cli_simulate_document_export_smoke`; `pano_cli_simulate_document_render_smoke`; `pano_cli_plan_canvas_document_snapshot_smoke`; `pano_cli_plan_canvas_document_snapshot_payload_smoke`; `pano_cli_plan_export_snapshot_route_pending_smoke`; `pano_cli_save_document_project_roundtrip_smoke`; `pano_cli_apply_stroke_script_roundtrip_smoke`; `pano_cli_apply_stroke_script_rejects_tiny_canvas` | Legacy document behavior is represented by `pp_document`/`pp_paint_renderer` tests and the app consumes it through a boundary/facade |
|
||||||
| DEBT-0011 | Open | Modernization | `package-smoke` validates the Windows CMake app artifact and launch-folder DLL payload, and reports a structured package readiness matrix for Windows AppX, Android standard/Quest/Focus APKs, Apple bundles, Linux app output, and WebGL output; the Windows app smoke passes the configure-time CMake executable so VS 2026 generator validation does not depend on `cmake` from PATH, retained Android package native CMake paths, and retained Linux/WebGL CMake baseline metadata are reachable from package validation and root CMake package-readiness targets, but Windows AppX/APK/Linux/Apple/WebGL package outputs are still `blocked` because root CMake package targets do not exist yet | 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`; `cmake --build --preset windows-msvc-default --config Debug --target panopainter_windows_app_package_smoke`; `powershell -ExecutionPolicy Bypass -File scripts\automation\package-smoke.ps1 -ReadinessOnly -AndroidNativeChecks -PackageKinds android-standard-apk,android-quest-apk,android-focus-apk`; `cmake --build --preset windows-msvc-default --config Debug --target panopainter_android_native_package_smoke`; `cmake --build --preset windows-msvc-default --config Debug --target panopainter_linux_webgl_package_readiness`; `python scripts/dev/check_package_smoke_readiness.py`; `bash -n scripts/automation/package-smoke.sh` | Package-smoke builds and validates Windows AppX, Android APK variants, Linux app, Apple bundles, and WebGL output where local toolchains are present |
|
| DEBT-0011 | Open | Modernization | `package-smoke` validates the Windows CMake app artifact and launch-folder DLL payload, and reports a structured package readiness matrix for Windows AppX, Android standard/Quest/Focus APKs, Apple bundles, Linux app output, and WebGL output; the Windows app smoke passes the configure-time CMake executable so VS 2026 generator validation does not depend on `cmake` from PATH, retained Android package native CMake paths, and retained Linux/WebGL CMake baseline metadata are reachable from package validation and root CMake package-readiness targets, but Windows AppX/APK/Linux/Apple/WebGL package outputs are still `blocked` because root CMake package targets do not exist yet | 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`; `cmake --build --preset windows-msvc-default --config Debug --target panopainter_windows_app_package_smoke`; `powershell -ExecutionPolicy Bypass -File scripts\automation\package-smoke.ps1 -ReadinessOnly -AndroidNativeChecks -PackageKinds android-standard-apk,android-quest-apk,android-focus-apk`; `cmake --build --preset windows-msvc-default --config Debug --target panopainter_android_native_package_smoke`; `cmake --build --preset windows-msvc-default --config Debug --target panopainter_linux_webgl_package_readiness`; `python scripts/dev/check_package_smoke_readiness.py`; `bash -n scripts/automation/package-smoke.sh` | Package-smoke builds and validates Windows AppX, Android APK variants, Linux app, Apple bundles, and WebGL output where local toolchains are present |
|
||||||
| DEBT-0012 | Open | Modernization | `pp_ui_core` uses vcpkg tinyxml2 on `windows-msvc-vcpkg-headless`, but retains `pp_vendor_tinyxml2` for default and unproven platform presets | Mobile/AppX/Apple triplets and app packaging still need validation before removing the vendored fallback | `ctest --preset desktop-fast-vcpkg --build-config Debug`; `ctest --preset desktop-fast --build-config Debug`; `powershell -ExecutionPolicy Bypass -File scripts\automation\platform-build.ps1 -Presets android-arm64` | All supported presets consume vcpkg tinyxml2 or document a permanent vendored exception |
|
| DEBT-0012 | Open | Modernization | `pp_ui_core` uses vcpkg tinyxml2 on `windows-msvc-vcpkg-headless`, but retains `pp_vendor_tinyxml2` for default and unproven platform presets | Mobile/AppX/Apple triplets and app packaging still need validation before removing the vendored fallback | `ctest --preset desktop-fast-vcpkg --build-config Debug`; `ctest --preset desktop-fast --build-config Debug`; `powershell -ExecutionPolicy Bypass -File scripts\automation\platform-build.ps1 -Presets android-arm64` | All supported presets consume vcpkg tinyxml2 or document a permanent vendored exception |
|
||||||
| DEBT-0013 | Open | Modernization | `pp_assets`, `pp_document`, `pano_cli inspect-project`, `pano_cli load-project`, and `pano_cli save-project` validate the fixed PPI header, thumbnail/body byte layout, generated multi-layer/multi-frame PPI writing with explicit layer opacity/blend/alpha-lock/visibility metadata, per-layer frame durations, metadata-only and targeted dirty-face-payload save/load round-trips, layer/frame index, dirty-face descriptors, dirty-face PNG payload metadata, asset-level RGBA PNG payload decoding, pure document-to-PPI export, CLI document export automation, file-writing document export automation, stroke-script-generated document payload export, decoded pixel attachment to `pp_document`, live save-path snapshot-readiness reporting, and app-core canvas-snapshot-to-PPI export automation, but full legacy PPI round-trip parity and pure live save writer replacement are not yet extracted | Full PPI save parity requires staged extraction of legacy `Canvas` serialization and image/layer payload handling | `ctest --preset desktop-fast --build-config Debug`; `pp_assets_image_pixels_tests`; `pp_assets_ppi_header_tests`; `pp_document_ppi_import_tests`; `pp_document_ppi_export_tests`; `pano_cli_inspect_project_layout_smoke`; `pano_cli_load_project_metadata_smoke`; `pano_cli_save_project_roundtrip_smoke`; `pano_cli_save_project_payload_roundtrip_smoke`; `pano_cli_simulate_document_export_smoke`; `pano_cli_save_document_project_roundtrip_smoke`; `pano_cli_apply_stroke_script_roundtrip_smoke`; `pano_cli_apply_stroke_script_rejects_tiny_canvas` | Full PPI load/save fixtures cover thumbnails, decoded layer face payloads attached to documents, frames, corrupt payloads, dirty-face payload saving, arbitrary legacy canvas payload/layout combinations, and legacy app round-trip compatibility |
|
| DEBT-0013 | Open | Modernization | `pp_assets`, `pp_document`, `pano_cli inspect-project`, `pano_cli load-project`, and `pano_cli save-project` validate the fixed PPI header, thumbnail/body byte layout, generated multi-layer/multi-frame PPI writing with explicit layer opacity/blend/alpha-lock/visibility metadata, per-layer frame durations, metadata-only and targeted dirty-face-payload save/load round-trips, layer/frame index, dirty-face descriptors, dirty-face PNG payload metadata, asset-level RGBA PNG payload decoding, pure document-to-PPI export, CLI document export automation, file-writing document export automation, stroke-script-generated document payload export, decoded pixel attachment to `pp_document`, live save-path snapshot-readiness reporting, and app-core canvas-snapshot-to-PPI export automation, but full legacy PPI round-trip parity and pure live save writer replacement are not yet extracted | Full PPI save parity requires staged extraction of legacy `Canvas` serialization and image/layer payload handling | `ctest --preset desktop-fast --build-config Debug`; `pp_assets_image_pixels_tests`; `pp_assets_ppi_header_tests`; `pp_document_ppi_import_tests`; `pp_document_ppi_export_tests`; `pano_cli_inspect_project_layout_smoke`; `pano_cli_load_project_metadata_smoke`; `pano_cli_save_project_roundtrip_smoke`; `pano_cli_save_project_payload_roundtrip_smoke`; `pano_cli_simulate_document_export_smoke`; `pano_cli_save_document_project_roundtrip_smoke`; `pano_cli_apply_stroke_script_roundtrip_smoke`; `pano_cli_apply_stroke_script_rejects_tiny_canvas` | Full PPI load/save fixtures cover thumbnails, decoded layer face payloads attached to documents, frames, corrupt payloads, dirty-face payload saving, arbitrary legacy canvas payload/layout combinations, and legacy app round-trip compatibility |
|
||||||
@@ -940,13 +964,13 @@ agent or engineer to remove them without reconstructing context from chat.
|
|||||||
| DEBT-0048 | Open | Modernization | ABR/PPBR brush package import execution now consumes pure `pp_app_core` through document-open confirmation callbacks, `pano_cli plan-brush-package-import`, `BrushPackageImportServices`, and `src/legacy_brush_package_import_services.*`; imported brush tip/pattern target paths now consume `pp_assets::brush_package`, but the bridge still launches detached legacy `NodePanelBrushPreset::import_abr`/`import_ppbr` worker threads and depends on the legacy preset panel as the importer/storage owner | Preserve current brush import behavior while brush package parsing, preset storage, progress/error reporting, and UI refresh move toward asset/paint/UI services | `pp_assets_brush_package_tests`; `pp_app_core_brush_package_import_tests`; `pano_cli plan-brush-package-import --kind ppbr --path D:/Paint/Brushes/clouds.ppbr`; `pano_cli plan-brush-package-import --kind abr --path D:/Paint/Brushes/clouds.abr`; `pano_cli plan-brush-package-import --kind ppbr`; `ctest --preset desktop-fast --build-config Debug`; `cmake --build --preset windows-msvc-default --config Debug --target PanoPainter` | ABR/PPBR parsing, preset creation/storage, import threading/progress, duplicate asset policy, and UI refresh are owned by injected brush asset/paint/UI services with document-open callbacks only confirming user intent |
|
| DEBT-0048 | Open | Modernization | ABR/PPBR brush package import execution now consumes pure `pp_app_core` through document-open confirmation callbacks, `pano_cli plan-brush-package-import`, `BrushPackageImportServices`, and `src/legacy_brush_package_import_services.*`; imported brush tip/pattern target paths now consume `pp_assets::brush_package`, but the bridge still launches detached legacy `NodePanelBrushPreset::import_abr`/`import_ppbr` worker threads and depends on the legacy preset panel as the importer/storage owner | Preserve current brush import behavior while brush package parsing, preset storage, progress/error reporting, and UI refresh move toward asset/paint/UI services | `pp_assets_brush_package_tests`; `pp_app_core_brush_package_import_tests`; `pano_cli plan-brush-package-import --kind ppbr --path D:/Paint/Brushes/clouds.ppbr`; `pano_cli plan-brush-package-import --kind abr --path D:/Paint/Brushes/clouds.abr`; `pano_cli plan-brush-package-import --kind ppbr`; `ctest --preset desktop-fast --build-config Debug`; `cmake --build --preset windows-msvc-default --config Debug --target PanoPainter` | ABR/PPBR parsing, preset creation/storage, import threading/progress, duplicate asset policy, and UI refresh are owned by injected brush asset/paint/UI services with document-open callbacks only confirming user intent |
|
||||||
| DEBT-0049 | Open | Modernization | `pp_assets::validate_ppbr_header` intentionally preserves the legacy PPBR version check from `NodePanelBrushPreset::import_ppbr`, which accepts files when either major is `0` or minor is `1` instead of requiring exactly version `0.1` | Avoid rejecting existing brush packages before compatibility fixtures prove the stricter rule is safe | `pp_assets_brush_package_tests`; `pano_cli plan-brush-package-export --path D:/Paint/clouds.ppbr`; `ctest --preset desktop-fast --build-config Debug`; `cmake --build --preset windows-msvc-default --config Debug --target PanoPainter` | Add PPBR compatibility fixtures for accepted/rejected historical package versions, then require canonical `0.1` or an explicit supported-version matrix and update live import accordingly |
|
| DEBT-0049 | Open | Modernization | `pp_assets::validate_ppbr_header` intentionally preserves the legacy PPBR version check from `NodePanelBrushPreset::import_ppbr`, which accepts files when either major is `0` or minor is `1` instead of requiring exactly version `0.1` | Avoid rejecting existing brush packages before compatibility fixtures prove the stricter rule is safe | `pp_assets_brush_package_tests`; `pano_cli plan-brush-package-export --path D:/Paint/clouds.ppbr`; `ctest --preset desktop-fast --build-config Debug`; `cmake --build --preset windows-msvc-default --config Debug --target PanoPainter` | Add PPBR compatibility fixtures for accepted/rejected historical package versions, then require canonical `0.1` or an explicit supported-version matrix and update live import accordingly |
|
||||||
| DEBT-0050 | Open | Modernization | iOS exported-image photo-library publishing and WebGL persistent-storage flushing now dispatch through platform service boundaries; the iOS/Web policy decision lives in tested `pp_platform_api::platform_policy`, and WebGL flushing now goes through injectable `pp::platform::WebPlatformServices`, but non-Windows execution still lives in retained fallback adapters and forwards to retained `save_image_library`/`webgl_sync` bridges | Preserve current iOS/Web export and save behavior while the Apple/Web platform shells are extracted incrementally | `pp_platform_api_tests`; `ctest --preset desktop-fast --build-config Debug`; platform package smoke once Apple/Web root builds exist | Exported-image publishing and persistent-storage flushing are owned by injected Apple/Web `pp_platform_*` services with no legacy adapter branch |
|
| DEBT-0050 | Open | Modernization | iOS exported-image photo-library publishing and WebGL persistent-storage flushing now dispatch through platform service boundaries; the iOS/Web policy decision lives in tested `pp_platform_api::platform_policy`, and WebGL flushing now goes through injectable `pp::platform::WebPlatformServices`, but non-Windows execution still lives in retained fallback adapters and forwards to retained `save_image_library`/`webgl_sync` bridges | Preserve current iOS/Web export and save behavior while the Apple/Web platform shells are extracted incrementally | `pp_platform_api_tests`; `ctest --preset desktop-fast --build-config Debug`; platform package smoke once Apple/Web root builds exist | Exported-image publishing and persistent-storage flushing are owned by injected Apple/Web `pp_platform_*` services with no legacy adapter branch |
|
||||||
| DEBT-0051 | Open | Modernization | Document browser search roots and Browse dialog working-directory picker visibility/path formatting now dispatch through `PlatformServices`; iOS Inbox roots and working-directory picker availability live in tested `pp_platform_api::platform_policy`, but macOS directory picker/display-path execution still lives in `src/platform_legacy/legacy_platform_services.*` | Preserve current iOS document import/browse and desktop browse picker behavior while Apple platform shells are extracted incrementally | `pp_platform_api_tests`; `ctest --preset desktop-fast --build-config Debug`; Apple package smoke once root Apple builds exist | Document browse roots and browse-directory picker/display formatting are owned by injected Apple and desktop `pp_platform_*` services with no legacy adapter branch |
|
| DEBT-0051 | Open | Modernization | Document browser search roots, Apple file/image/save/directory picker dispatch, Browse dialog working-directory picker visibility/path formatting, iOS Inbox roots, macOS empty-selection filtering, and macOS display-path formatting now dispatch through the tested `src/platform_apple/apple_platform_services.*` boundary consumed by `PlatformServices`; retained `src/platform_legacy/legacy_platform_services.*` still creates the Apple bridge and owns other non-Apple fallback behavior | Preserve current iOS document import/browse and desktop browse picker behavior while Apple platform shells are extracted incrementally | `pp_platform_api_tests`; `ctest --preset desktop-fast --build-config Debug`; Apple package smoke once root Apple builds exist | Document browse roots and browse-directory picker/display formatting are owned by injected Apple and desktop `pp_platform_*` services with no legacy adapter branch |
|
||||||
| DEBT-0052 | Open | Modernization | Native UI/window state saving now dispatches through `PlatformServices`; Windows/macOS save policy lives in tested `pp_platform_api::platform_policy`, and Windows placement reads/writes now use `LegacyWindowPreferenceSnapshot` plus `src/legacy_preference_storage.*`, but macOS execution still lives in `src/platform_legacy/legacy_platform_services.*` and forwards to the retained Objective-C app bridge while Windows still stores placement through retained `Settings` behind the adapter | Preserve current Windows/macOS UI persistence while platform shells are extracted incrementally | `pp_platform_api_tests`; `ctest --preset desktop-fast --build-config Debug`; Windows app build; Apple package smoke once root Apple builds exist | UI/window state persistence is owned by injected platform services with no legacy adapter branch |
|
| DEBT-0052 | Open | Modernization | Native UI/window state saving now dispatches through `PlatformServices`; Windows/macOS save policy lives in tested `pp_platform_api::platform_policy`, and Windows placement reads/writes now use `LegacyWindowPreferenceSnapshot` plus `src/legacy_preference_storage.*`, but macOS execution still lives in `src/platform_legacy/legacy_platform_services.*` and forwards to the retained Objective-C app bridge while Windows still stores placement through retained `Settings` behind the adapter | Preserve current Windows/macOS UI persistence while platform shells are extracted incrementally | `pp_platform_api_tests`; `ctest --preset desktop-fast --build-config Debug`; Windows app build; Apple package smoke once root Apple builds exist | UI/window state persistence is owned by injected platform services with no legacy adapter branch |
|
||||||
| DEBT-0053 | Open | Modernization | Prepared-file writable target selection and prepared-file export-dialog policy now dispatch through platform service boundaries; iOS temporary-file and WebGL data-path target planning live in tested `pp_platform_api::platform_policy`, and WebGL prepared-file handoff now goes through injectable `pp::platform::WebPlatformServices`, but retained iOS/Web save/download handoff execution still lives in retained fallback adapters | Preserve mobile/Web export handoff behavior while platform shells are extracted incrementally | `pp_platform_api_tests`; `ctest --preset desktop-fast --build-config Debug`; Windows app build; Apple/Web package smoke once root package builds exist | Prepared-file target selection, export-dialog policy, and save/download handoff are owned by injected platform services with no legacy adapter branch |
|
| DEBT-0053 | Open | Modernization | Prepared-file writable target selection and prepared-file export-dialog policy now dispatch through platform service boundaries; iOS temporary-file and WebGL data-path target planning live in tested `pp_platform_api::platform_policy`, and WebGL prepared-file handoff now goes through injectable `pp::platform::WebPlatformServices`, but retained iOS/Web save/download handoff execution still lives in retained fallback adapters | Preserve mobile/Web export handoff behavior while platform shells are extracted incrementally | `pp_platform_api_tests`; `ctest --preset desktop-fast --build-config Debug`; Windows app build; Apple/Web package smoke once root package builds exist | Prepared-file target selection, export-dialog policy, and save/download handoff are owned by injected platform services with no legacy adapter branch |
|
||||||
| DEBT-0054 | Open | Modernization | Layout XML file read/reload decisions now consume `pp_platform_api::plan_asset_file_load`; platform-family reload behavior lives in tested `pp_platform_api::platform_policy` and pure probed planning, but the live wrapper still performs direct `stat` probing for Windows/macOS mtime reload checks until platform storage/file-watch services exist | Preserve current layout hot-reload and mobile/Web single-load behavior while removing platform guards from the shared `LayoutManager` parser | `pp_platform_api_tests`; `ctest --preset desktop-fast --build-config Debug -R pp_platform_api_tests`; Windows app build | Layout reload decisions are owned by injected platform storage/file-watch services or an asset manager boundary with platform-specific file watching removed from compile-time helpers |
|
| DEBT-0054 | Open | Modernization | Layout XML file read/reload decisions now consume `pp_platform_api::plan_asset_file_load`; platform-family reload behavior lives in tested `pp_platform_api::platform_policy` and pure probed planning, but the live wrapper still performs direct `stat` probing for Windows/macOS mtime reload checks until platform storage/file-watch services exist | Preserve current layout hot-reload and mobile/Web single-load behavior while removing platform guards from the shared `LayoutManager` parser | `pp_platform_api_tests`; `ctest --preset desktop-fast --build-config Debug -R pp_platform_api_tests`; Windows app build | Layout reload decisions are owned by injected platform storage/file-watch services or an asset manager boundary with platform-specific file watching removed from compile-time helpers |
|
||||||
| 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-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-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-0060 | Open | Modernization | Retained Android package CMake generates a patched `nanort.h` overlay in the build tree for `native-lib` instead of modifying the `libs/nanort` submodule | Current SDK Manager NDK/Clang rejects `TriangleSAHPred::operator=` assigning to a `const size_t` member, but the retained grid/lightmap path still includes `nanort` before that dependency is replaced or updated | `powershell -ExecutionPolicy Bypass -File scripts\automation\android-legacy-package-build.ps1 -Packages standard`; Quest/Focus retained package configure checks; Windows app build | Update/replace `nanort`, move grid/lightmap baking behind a component that owns its dependency, or retire the retained Android package CMake path so no generated vendor overlay is required |
|
| DEBT-0060 | Open | Modernization | Retained Android package CMake no longer generates a patched `nanort.h` overlay, and standard/Quest/Focus package paths include `libs/nanort` directly after the vendored `TriangleSAHPred` assignment issue was fixed locally; retained Android standard validation now gets past `nanort` but fails later because `legacy_gl_runtime_dispatch.h` assumes desktop GL debug callback symbols that Android/GLES does not expose | Current Android retained package validation still needs a non-desktop GL runtime dispatch guard before the package path can be treated as green | `powershell -ExecutionPolicy Bypass -File scripts\automation\android-legacy-package-build.ps1 -Packages standard`; Quest/Focus retained package configure checks; Windows app build | Retained Android standard package validation passes without generated vendor overlays, or the retained Android package CMake path is retired |
|
||||||
| 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-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 | The retained UI tree still exposes `Node* m_parent`, public `std::vector<std::shared_ptr<Node>> m_children`, raw `find<T>()` lookup results, `add_child<T>()` allocation through `new`, callbacks/observers that take or capture raw `Node*`, and manual `destroy()`/`m_destroyed` semantics. `pp_ui_core` now owns a tested `NodeLifetimeTree` target model with checked node handles, scoped callback connections, subtree destruction, pointer/keyboard capture release, whole-tree clear for layout reload, and mutation-safe dispatch, plus a tested `UiOverlayLifetime` popup/dialog stack model. Retained app-dialog root insertion, app-menu popup template cloning/root attachment, quick/stroke/brush panel popup root attachment, combo-box popup insertion, Open/Browse delete-confirmation dialog insertion, popup tick decoration insertion, top-toolbar panel popup insertion, repeated retained popup activation flag setup, repeated retained popup close/release execution, popup tick-decoration close callback wiring, and popup-panel outside-click release/remove/callback dispatch are now centralized in `src/legacy_ui_overlay_services.*`, but retained `Node`/`NodePopupMenu`/`NodeDialog*` still have not adopted checked handles or scoped callback ownership | Preserve current UI behavior while making panel/dialog extraction safe instead of spreading lifetime hazards into the new architecture | `pp_ui_core_layout_xml_tests`; `pp_ui_core_node_lifetime_tests`; `pp_ui_core_overlay_lifetime_tests`; future `pp_panopainter_ui_dialog_lifetime_tests`; `ctest --preset desktop-fast --build-config Debug`; `cmake --build --preset windows-msvc-default --config Debug --target PanoPainter` | Retained `Node` and `pp_panopainter_ui` adopt checked node handles or equivalent non-owning references, scoped callback connection/disconnect semantics, mutation-safe event dispatch, parent/child invariants hidden behind APIs, and destroy-during-callback/capture-release/popup-close/layout-reload tests; retained `Node*` APIs are removed or isolated behind compatibility adapters |
|
| DEBT-0063 | Open | Modernization | The retained UI tree still exposes `Node* m_parent`, public `std::vector<std::shared_ptr<Node>> m_children`, raw `find<T>()` lookup results, `add_child<T>()` allocation through `new`, callbacks/observers that take or capture raw `Node*`, and manual `destroy()`/`m_destroyed` semantics. `pp_ui_core` now owns a tested `NodeLifetimeTree` target model with checked node handles, scoped callback connections, subtree destruction, pointer/keyboard capture release, whole-tree clear for layout reload, and mutation-safe dispatch, plus a tested `UiOverlayLifetime` popup/dialog stack model. Retained app-dialog root insertion, app-menu popup template cloning/root attachment, quick/stroke/brush panel popup root attachment, combo-box popup insertion, Open/Browse delete-confirmation dialog insertion, popup tick decoration insertion, top-toolbar panel popup insertion, repeated retained popup activation flag setup, repeated retained popup close/release execution, popup tick-decoration close callback wiring, and popup-panel outside-click release/remove/callback dispatch are now centralized in `src/legacy_ui_overlay_services.*`, but retained `Node`/`NodePopupMenu`/`NodeDialog*` still have not adopted checked handles or scoped callback ownership | Preserve current UI behavior while making panel/dialog extraction safe instead of spreading lifetime hazards into the new architecture | `pp_ui_core_layout_xml_tests`; `pp_ui_core_node_lifetime_tests`; `pp_ui_core_overlay_lifetime_tests`; future `pp_panopainter_ui_dialog_lifetime_tests`; `ctest --preset desktop-fast --build-config Debug`; `cmake --build --preset windows-msvc-default --config Debug --target PanoPainter` | Retained `Node` and `pp_panopainter_ui` adopt checked node handles or equivalent non-owning references, scoped callback connection/disconnect semantics, mutation-safe event dispatch, parent/child invariants hidden behind APIs, and destroy-during-callback/capture-release/popup-close/layout-reload tests; retained `Node*` APIs are removed or isolated behind compatibility adapters |
|
||||||
| 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-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 |
|
||||||
@@ -957,6 +981,6 @@ agent or engineer to remove them without reconstructing context from chat.
|
|||||||
|
|
||||||
| ID | Status | Owner | Item | Reason | Validation | Removal Condition |
|
| ID | Status | Owner | Item | Reason | Validation | Removal Condition |
|
||||||
| --- | --- | --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- | --- | --- |
|
||||||
| DEBT-0062 | Closed | Modernization | VS 2026 builds generated a patched fmt `format.h` overlay in the build tree for `pp_legacy_vendor` | VS 2026's STL no longer exposes the legacy checked-array iterator used by the old fmt release | Closed on 2026-06-12: VS-bundled CMake build of `PanoPainter` and `pp_platform_api_tests`; `ctest --preset desktop-fast --build-config Debug -R pp_platform_api_tests --output-on-failure` | Closed on 2026-06-12: the generated overlay was removed, stale overlay directories are deleted during configure, and VS 2026 consumers compile retained fmt with `_SECURE_SCL` undefined so `stdext::checked_array_iterator` is not selected |
|
| DEBT-0062 | Closed | Modernization | VS 2026 builds generated a patched fmt `format.h` overlay in the build tree for `pp_legacy_vendor` | VS 2026's STL no longer exposes the legacy checked-array iterator used by the old fmt release | Closed on 2026-06-12: VS-bundled CMake build of `PanoPainter` and `pp_platform_api_tests`; `ctest --preset desktop-fast --build-config Debug -R pp_platform_api_tests --output-on-failure` | Closed on 2026-06-12: the generated overlay was removed, stale overlay directories are deleted during configure, and VS 2026 retained fmt sources use a generated forced-include compatibility header to keep fmt out of the removed checked-array-iterator branch |
|
||||||
| DEBT-0006 | Closed | Modernization | `pano_cli create-document` validates and emits JSON command contracts but does not yet invoke the legacy document/app model | The document model had not been extracted from `Canvas`/`App` yet | `ctest --preset desktop-fast --build-config Debug`; `pano_cli_create_document_smoke` | Closed on 2026-05-31: command now constructs a real `pp_document::CanvasDocument` |
|
| DEBT-0006 | Closed | Modernization | `pano_cli create-document` validates and emits JSON command contracts but does not yet invoke the legacy document/app model | The document model had not been extracted from `Canvas`/`App` yet | `ctest --preset desktop-fast --build-config Debug`; `pano_cli_create_document_smoke` | Closed on 2026-05-31: command now constructs a real `pp_document::CanvasDocument` |
|
||||||
| DEBT-0018 | Closed | Modernization | `pp_renderer_gl` owned a tested `OpenGlInitialState` plan for PanoPainter startup depth/blend policy, but `App::init` still executed the plan through direct OpenGL calls | Preserve behavior while moving renderer policy into the backend boundary before a live `IRenderDevice`/command context owns startup execution | `pp_renderer_gl_capabilities_tests`; `ctest --preset desktop-fast --build-config Debug`; `powershell -ExecutionPolicy Bypass -File scripts\automation\package-smoke.ps1 -Preset windows-msvc-default -Configuration Debug` | Closed on 2026-06-03: `pp_renderer_gl::apply_panopainter_initial_state` now applies the startup state through a tested backend dispatch contract consumed by `App::init` |
|
| DEBT-0018 | Closed | Modernization | `pp_renderer_gl` owned a tested `OpenGlInitialState` plan for PanoPainter startup depth/blend policy, but `App::init` still executed the plan through direct OpenGL calls | Preserve behavior while moving renderer policy into the backend boundary before a live `IRenderDevice`/command context owns startup execution | `pp_renderer_gl_capabilities_tests`; `ctest --preset desktop-fast --build-config Debug`; `powershell -ExecutionPolicy Bypass -File scripts\automation\package-smoke.ps1 -Preset windows-msvc-default -Configuration Debug` | Closed on 2026-06-03: `pp_renderer_gl::apply_panopainter_initial_state` now applies the startup state through a tested backend dispatch contract consumed by `App::init` |
|
||||||
|
|||||||
@@ -34,11 +34,11 @@ auditable steps rather than by subjective estimates.
|
|||||||
| Build and CMake ownership | 15 | 12 | Root CMake owns active source lists, app/tool targets, and retained package entrypoints. |
|
| Build and CMake ownership | 15 | 12 | 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. |
|
| 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. |
|
| Pure component behavior ownership | 15 | 8 | Behavior lives in `pp_*` components and is consumed by live adapters. |
|
||||||
| Legacy adapter retirement | 20 | 5 | `legacy_*_services` and singleton bridges are deleted or reduced to trivial composition. |
|
| Legacy adapter retirement | 20 | 7 | `legacy_*_services` and singleton bridges are deleted or reduced to trivial composition. |
|
||||||
| Renderer boundary and OpenGL parity | 15 | 3 | Live render/export/readback paths execute through renderer interfaces with parity checks. |
|
| Renderer boundary and OpenGL parity | 15 | 10 | Live render/export/readback paths execute through renderer interfaces with parity checks. |
|
||||||
| Platform and package parity | 10 | 4 | Required platforms have root CMake/package validation and injected platform services. |
|
| Platform and package parity | 10 | 6 | Required platforms have root CMake/package validation and injected platform services. |
|
||||||
| Hardening and future backend readiness | 10 | 0 | Edge, fuzz, golden, stress, and backend-lab gates exist for high-risk paths. |
|
| Hardening and future backend readiness | 10 | 0 | Edge, fuzz, golden, stress, and backend-lab gates exist for high-risk paths. |
|
||||||
| **Total** | **100** | **41** | Only completed tasks below may change this number. |
|
| **Total** | **100** | **52** | Only completed tasks below may change this number. |
|
||||||
|
|
||||||
When updating `Current`, add a dated note under "Completed Task Log" with the
|
When updating `Current`, add a dated note under "Completed Task Log" with the
|
||||||
task id, points moved, validation command, and commit hash.
|
task id, points moved, validation command, and commit hash.
|
||||||
@@ -237,7 +237,7 @@ cmake --build --preset windows-msvc-default --config Debug --target PanoPainter
|
|||||||
|
|
||||||
### RND-001 - Make Pure Equirectangular Export The Primary Success Path
|
### RND-001 - Make Pure Equirectangular Export The Primary Success Path
|
||||||
|
|
||||||
Status: Ready
|
Status: Done
|
||||||
Score: +2 renderer boundary and OpenGL parity
|
Score: +2 renderer boundary and OpenGL parity
|
||||||
Debt: `DEBT-0010`, `DEBT-0036`, `DEBT-0043`
|
Debt: `DEBT-0010`, `DEBT-0036`, `DEBT-0043`
|
||||||
Scope: `src/legacy_document_export_services.*`,
|
Scope: `src/legacy_document_export_services.*`,
|
||||||
@@ -270,7 +270,7 @@ cmake --build --preset windows-msvc-default --config Debug --target PanoPainter
|
|||||||
|
|
||||||
### RND-002 - Make Pure Layer And Animation Collection Export Primary
|
### RND-002 - Make Pure Layer And Animation Collection Export Primary
|
||||||
|
|
||||||
Status: Ready
|
Status: Done
|
||||||
Score: +2 renderer boundary and OpenGL parity
|
Score: +2 renderer boundary and OpenGL parity
|
||||||
Debt: `DEBT-0010`, `DEBT-0036`, `DEBT-0043`
|
Debt: `DEBT-0010`, `DEBT-0036`, `DEBT-0043`
|
||||||
Scope: `src/legacy_document_export_services.*`,
|
Scope: `src/legacy_document_export_services.*`,
|
||||||
@@ -299,7 +299,7 @@ cmake --build --preset windows-msvc-default --config Debug --target PanoPainter
|
|||||||
|
|
||||||
### RND-003 - Replace Depth Export Readiness With Pure Depth Export Execution
|
### RND-003 - Replace Depth Export Readiness With Pure Depth Export Execution
|
||||||
|
|
||||||
Status: Ready
|
Status: Done
|
||||||
Score: +3 renderer boundary and OpenGL parity
|
Score: +3 renderer boundary and OpenGL parity
|
||||||
Debt: `DEBT-0010`, `DEBT-0036`, `DEBT-0043`
|
Debt: `DEBT-0010`, `DEBT-0036`, `DEBT-0043`
|
||||||
Scope: `src/paint_renderer/compositor.*`,
|
Scope: `src/paint_renderer/compositor.*`,
|
||||||
@@ -331,7 +331,7 @@ cmake --build --preset windows-msvc-default --config Debug --target PanoPainter
|
|||||||
|
|
||||||
### RND-004 - Add First Desktop GPU Golden Gate
|
### RND-004 - Add First Desktop GPU Golden Gate
|
||||||
|
|
||||||
Status: Ready
|
Status: Blocked
|
||||||
Score: +2 hardening and future backend readiness
|
Score: +2 hardening and future backend readiness
|
||||||
Debt: `DEBT-0036`
|
Debt: `DEBT-0036`
|
||||||
Scope: `tests/`, `CMakeLists.txt`, renderer test helpers only
|
Scope: `tests/`, `CMakeLists.txt`, renderer test helpers only
|
||||||
@@ -443,6 +443,9 @@ Score: +1 build and CMake ownership
|
|||||||
Debt: `DEBT-0060`
|
Debt: `DEBT-0060`
|
||||||
Scope: retained Android package CMake, `libs/nanort`, grid/lightmap dependency
|
Scope: retained Android package CMake, `libs/nanort`, grid/lightmap dependency
|
||||||
wiring
|
wiring
|
||||||
|
Blocked By: Retained Android standard package now gets past `nanort`, but
|
||||||
|
fails compiling `legacy_gl_runtime_dispatch.h` on Android/GLES because
|
||||||
|
`glDebugMessageCallback` and `GLDEBUGPROC` are unavailable.
|
||||||
|
|
||||||
Goal:
|
Goal:
|
||||||
|
|
||||||
@@ -527,7 +530,12 @@ Done Checks:
|
|||||||
|
|
||||||
| Date | Task | Score Change | Validation | Commit |
|
| Date | Task | Score Change | Validation | Commit |
|
||||||
| --- | --- | ---: | --- | --- |
|
| --- | --- | ---: | --- | --- |
|
||||||
| 2026-06-12 | DEP-001 | +1 build and CMake ownership | VS-bundled CMake build of `PanoPainter` and `pp_platform_api_tests`; `ctest --preset desktop-fast --build-config Debug -R pp_platform_api_tests --output-on-failure` | 85d3fd5b |
|
| 2026-06-12 | RND-002 | +2 renderer boundary and OpenGL parity | `ctest --preset desktop-fast --build-config Debug -R "pp_app_core_document_export\|pp_paint_renderer_compositor\|pano_cli_plan_export_snapshot_route\|pano_cli_simulate_document_export" --output-on-failure` | pending |
|
||||||
|
| 2026-06-12 | RND-001 | +2 renderer boundary and OpenGL parity | `ctest --preset desktop-fast --build-config Debug -R "pp_app_core_document_export\|pp_paint_renderer_compositor\|pano_cli_plan_export_snapshot_route\|pano_cli_simulate_document_export" --output-on-failure` | pending |
|
||||||
|
| 2026-06-12 | ADP-004 | +2 legacy adapter retirement | VS-bundled CMake build of `pp_app_core_app_dialog_tests` and `pano_cli`; `ctest --preset desktop-fast --build-config Debug -R "pp_app_core_app_dialog\|pano_cli_plan_app_dialog" --output-on-failure` | pending |
|
||||||
|
| 2026-06-12 | PLT-001 | +2 platform and package parity | VS-bundled CMake build of `pp_platform_api_tests`; `ctest --preset desktop-fast --build-config Debug -R pp_platform_api_tests --output-on-failure`; Apple remote build blocked by unpublished fmt submodule pointer before DEP-001 correction | pending |
|
||||||
|
| 2026-06-12 | RND-003 | +3 renderer boundary and OpenGL parity | VS-bundled CMake build of `pp_paint_renderer_compositor_tests`, `pp_app_core_document_export_tests`, and `pano_cli`; `ctest --preset desktop-fast --build-config Debug -R "pp_app_core_document_export\|pp_paint_renderer_compositor\|pano_cli_plan_export_snapshot_route" --output-on-failure` | pending |
|
||||||
|
| 2026-06-12 | DEP-001 | +1 build and CMake ownership | VS-bundled CMake build of `PanoPainter` and `pp_platform_api_tests`; `ctest --preset desktop-fast --build-config Debug -R pp_platform_api_tests --output-on-failure` | 90f5fb29 |
|
||||||
| 2026-06-12 | ADP-003 | +1 legacy adapter retirement | `ctest --preset desktop-fast --build-config Debug -R "pp_app_core_document_route\|pp_app_core_document_session\|pano_cli_plan_open_route\|pano_cli_simulate_app_session\|pano_cli_plan_document_file\|pano_cli_plan_document_version" --output-on-failure` | 34a9e910 |
|
| 2026-06-12 | ADP-003 | +1 legacy adapter retirement | `ctest --preset desktop-fast --build-config Debug -R "pp_app_core_document_route\|pp_app_core_document_session\|pano_cli_plan_open_route\|pano_cli_simulate_app_session\|pano_cli_plan_document_file\|pano_cli_plan_document_version" --output-on-failure` | 34a9e910 |
|
||||||
| 2026-06-12 | PLT-002 | +2 platform and package parity | `ctest --preset desktop-fast --build-config Debug -R pp_platform_api_tests --output-on-failure`; `powershell -ExecutionPolicy Bypass -File scripts\automation\package-smoke.ps1 -ReadinessOnly`; `ctest --preset desktop-fast --build-config Debug -R "pp_app_core_document_layer\|pano_cli_plan_layer\|pp_platform_api_tests" --output-on-failure` | 8cd38401 |
|
| 2026-06-12 | PLT-002 | +2 platform and package parity | `ctest --preset desktop-fast --build-config Debug -R pp_platform_api_tests --output-on-failure`; `powershell -ExecutionPolicy Bypass -File scripts\automation\package-smoke.ps1 -ReadinessOnly`; `ctest --preset desktop-fast --build-config Debug -R "pp_app_core_document_layer\|pano_cli_plan_layer\|pp_platform_api_tests" --output-on-failure` | 8cd38401 |
|
||||||
| 2026-06-12 | ADP-002 | +1 legacy adapter retirement | `ctest --preset desktop-fast --build-config Debug -R "pp_app_core_document_layer\|pano_cli_plan_layer" --output-on-failure`; `ctest --preset desktop-fast --build-config Debug -R "pp_app_core_document_layer\|pano_cli_plan_layer\|pp_platform_api_tests" --output-on-failure` | ae242852 |
|
| 2026-06-12 | ADP-002 | +1 legacy adapter retirement | `ctest --preset desktop-fast --build-config Debug -R "pp_app_core_document_layer\|pano_cli_plan_layer" --output-on-failure`; `ctest --preset desktop-fast --build-config Debug -R "pp_app_core_document_layer\|pano_cli_plan_layer\|pp_platform_api_tests" --output-on-failure` | ae242852 |
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "foundation/result.h"
|
#include "foundation/result.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
@@ -34,6 +35,41 @@ struct AppInputDialogPlan {
|
|||||||
std::string ok_caption = "Ok";
|
std::string ok_caption = "Ok";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class AppDialog {
|
||||||
|
public:
|
||||||
|
virtual ~AppDialog() = default;
|
||||||
|
[[nodiscard]] virtual AppDialogKind kind() const noexcept = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AppProgressDialog : public AppDialog {
|
||||||
|
public:
|
||||||
|
~AppProgressDialog() override = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AppMessageDialog : public AppDialog {
|
||||||
|
public:
|
||||||
|
~AppMessageDialog() override = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AppInputDialog : public AppDialog {
|
||||||
|
public:
|
||||||
|
~AppInputDialog() override = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AppDialogFactory {
|
||||||
|
public:
|
||||||
|
virtual ~AppDialogFactory() = default;
|
||||||
|
|
||||||
|
[[nodiscard]] virtual std::shared_ptr<AppProgressDialog> show_progress_dialog(
|
||||||
|
const AppProgressDialogPlan& plan) = 0;
|
||||||
|
|
||||||
|
[[nodiscard]] virtual std::shared_ptr<AppMessageDialog> show_message_dialog(
|
||||||
|
const AppMessageDialogPlan& plan) = 0;
|
||||||
|
|
||||||
|
[[nodiscard]] virtual std::shared_ptr<AppInputDialog> show_input_dialog(
|
||||||
|
const AppInputDialogPlan& plan) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
[[nodiscard]] inline AppProgressDialogPlan plan_app_progress_dialog(
|
[[nodiscard]] inline AppProgressDialogPlan plan_app_progress_dialog(
|
||||||
std::string_view title,
|
std::string_view title,
|
||||||
int total) noexcept
|
int total) noexcept
|
||||||
|
|||||||
@@ -492,9 +492,9 @@ public:
|
|||||||
case DocumentExportExecutionKind::layers_stem:
|
case DocumentExportExecutionKind::layers_stem:
|
||||||
case DocumentExportExecutionKind::animation_frames_collection:
|
case DocumentExportExecutionKind::animation_frames_collection:
|
||||||
case DocumentExportExecutionKind::animation_frames_stem:
|
case DocumentExportExecutionKind::animation_frames_stem:
|
||||||
|
case DocumentExportExecutionKind::depth:
|
||||||
case DocumentExportExecutionKind::cube_faces:
|
case DocumentExportExecutionKind::cube_faces:
|
||||||
return true;
|
return true;
|
||||||
case DocumentExportExecutionKind::depth:
|
|
||||||
case DocumentExportExecutionKind::animation_mp4:
|
case DocumentExportExecutionKind::animation_mp4:
|
||||||
case DocumentExportExecutionKind::timelapse:
|
case DocumentExportExecutionKind::timelapse:
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -126,13 +126,15 @@ void start_document_export_collection(
|
|||||||
std::shared_ptr<NodeProgressBar> App::show_progress(const std::string& title, int total /*= 0*/)
|
std::shared_ptr<NodeProgressBar> App::show_progress(const std::string& title, int total /*= 0*/)
|
||||||
{
|
{
|
||||||
const auto plan = pp::app::plan_app_progress_dialog(title, total);
|
const auto plan = pp::app::plan_app_progress_dialog(title, total);
|
||||||
return pp::panopainter::create_legacy_app_progress_dialog(*this, plan);
|
const auto dialogs = pp::panopainter::make_legacy_app_dialog_factory(*this);
|
||||||
|
return pp::panopainter::legacy_progress_dialog_node(dialogs->show_progress_dialog(plan));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<NodeMessageBox> App::message_box(const std::string &title, const std::string& text, bool cancel_button)
|
std::shared_ptr<NodeMessageBox> App::message_box(const std::string &title, const std::string& text, bool cancel_button)
|
||||||
{
|
{
|
||||||
const auto plan = pp::app::plan_app_message_dialog(title, text, cancel_button);
|
const auto plan = pp::app::plan_app_message_dialog(title, text, cancel_button);
|
||||||
return pp::panopainter::create_legacy_app_message_dialog(*this, plan);
|
const auto dialogs = pp::panopainter::make_legacy_app_dialog_factory(*this);
|
||||||
|
return pp::panopainter::legacy_message_dialog_node(dialogs->show_message_dialog(plan));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<NodeInputBox> App::input_box(const std::string& title,
|
std::shared_ptr<NodeInputBox> App::input_box(const std::string& title,
|
||||||
@@ -143,7 +145,8 @@ std::shared_ptr<NodeInputBox> App::input_box(const std::string& title,
|
|||||||
LOG("input dialog skipped: %s", plan_result.status().message);
|
LOG("input dialog skipped: %s", plan_result.status().message);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return pp::panopainter::create_legacy_app_input_dialog(*this, plan_result.value());
|
const auto dialogs = pp::panopainter::make_legacy_app_dialog_factory(*this);
|
||||||
|
return pp::panopainter::legacy_input_dialog_node(dialogs->show_input_dialog(plan_result.value()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::dialog_usermanual()
|
void App::dialog_usermanual()
|
||||||
|
|||||||
@@ -8,46 +8,150 @@
|
|||||||
#include "node_progress_bar.h"
|
#include "node_progress_bar.h"
|
||||||
|
|
||||||
namespace pp::panopainter {
|
namespace pp::panopainter {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class LegacyAppProgressDialog final : public pp::app::AppProgressDialog {
|
||||||
|
public:
|
||||||
|
explicit LegacyAppProgressDialog(std::shared_ptr<NodeProgressBar> node) noexcept
|
||||||
|
: node_(std::move(node))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] pp::app::AppDialogKind kind() const noexcept override
|
||||||
|
{
|
||||||
|
return pp::app::AppDialogKind::progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::shared_ptr<NodeProgressBar> node() const noexcept
|
||||||
|
{
|
||||||
|
return node_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<NodeProgressBar> node_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LegacyAppMessageDialog final : public pp::app::AppMessageDialog {
|
||||||
|
public:
|
||||||
|
explicit LegacyAppMessageDialog(std::shared_ptr<NodeMessageBox> node) noexcept
|
||||||
|
: node_(std::move(node))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] pp::app::AppDialogKind kind() const noexcept override
|
||||||
|
{
|
||||||
|
return pp::app::AppDialogKind::message;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::shared_ptr<NodeMessageBox> node() const noexcept
|
||||||
|
{
|
||||||
|
return node_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<NodeMessageBox> node_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LegacyAppInputDialog final : public pp::app::AppInputDialog {
|
||||||
|
public:
|
||||||
|
explicit LegacyAppInputDialog(std::shared_ptr<NodeInputBox> node) noexcept
|
||||||
|
: node_(std::move(node))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] pp::app::AppDialogKind kind() const noexcept override
|
||||||
|
{
|
||||||
|
return pp::app::AppDialogKind::input;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::shared_ptr<NodeInputBox> node() const noexcept
|
||||||
|
{
|
||||||
|
return node_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<NodeInputBox> node_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LegacyAppDialogFactory final : public pp::app::AppDialogFactory {
|
||||||
|
public:
|
||||||
|
explicit LegacyAppDialogFactory(App& app) noexcept
|
||||||
|
: app_(app)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::shared_ptr<pp::app::AppProgressDialog> show_progress_dialog(
|
||||||
|
const pp::app::AppProgressDialogPlan& plan) override
|
||||||
|
{
|
||||||
|
return std::make_shared<LegacyAppProgressDialog>(
|
||||||
|
create_legacy_progress_dialog_overlay(app_, plan));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::shared_ptr<pp::app::AppMessageDialog> show_message_dialog(
|
||||||
|
const pp::app::AppMessageDialogPlan& plan) override
|
||||||
|
{
|
||||||
|
return std::make_shared<LegacyAppMessageDialog>(
|
||||||
|
create_legacy_message_dialog_overlay(app_, plan));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::shared_ptr<pp::app::AppInputDialog> show_input_dialog(
|
||||||
|
const pp::app::AppInputDialogPlan& plan) override
|
||||||
|
{
|
||||||
|
return std::make_shared<LegacyAppInputDialog>(
|
||||||
|
create_legacy_input_dialog_overlay(app_, plan));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
App& app_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
std::unique_ptr<pp::app::AppDialogFactory> make_legacy_app_dialog_factory(App& app)
|
||||||
|
{
|
||||||
|
return std::make_unique<LegacyAppDialogFactory>(app);
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<NodeProgressBar> create_legacy_app_progress_dialog(
|
std::shared_ptr<NodeProgressBar> create_legacy_app_progress_dialog(
|
||||||
App& app,
|
App& app,
|
||||||
const pp::app::AppProgressDialogPlan& plan)
|
const pp::app::AppProgressDialogPlan& plan)
|
||||||
{
|
{
|
||||||
auto progress = make_legacy_overlay_node<NodeProgressBar>(app);
|
return legacy_progress_dialog_node(make_legacy_app_dialog_factory(app)->show_progress_dialog(plan));
|
||||||
progress->m_progress->SetWidthP(plan.progress_fraction);
|
|
||||||
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);
|
|
||||||
return progress;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<NodeMessageBox> create_legacy_app_message_dialog(
|
std::shared_ptr<NodeMessageBox> create_legacy_app_message_dialog(
|
||||||
App& app,
|
App& app,
|
||||||
const pp::app::AppMessageDialogPlan& plan)
|
const pp::app::AppMessageDialogPlan& plan)
|
||||||
{
|
{
|
||||||
auto message = make_legacy_overlay_node<NodeMessageBox>(app);
|
return legacy_message_dialog_node(make_legacy_app_dialog_factory(app)->show_message_dialog(plan));
|
||||||
message->m_title->set_text(plan.title.c_str());
|
|
||||||
message->m_message->set_text(plan.message.c_str());
|
|
||||||
message->btn_ok->m_text->set_text(plan.ok_caption.c_str());
|
|
||||||
if (plan.show_cancel)
|
|
||||||
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);
|
|
||||||
return message;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<NodeInputBox> create_legacy_app_input_dialog(
|
std::shared_ptr<NodeInputBox> create_legacy_app_input_dialog(
|
||||||
App& app,
|
App& app,
|
||||||
const pp::app::AppInputDialogPlan& plan)
|
const pp::app::AppInputDialogPlan& plan)
|
||||||
{
|
{
|
||||||
auto input = make_legacy_overlay_node<NodeInputBox>(app);
|
return legacy_input_dialog_node(make_legacy_app_dialog_factory(app)->show_input_dialog(plan));
|
||||||
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());
|
std::shared_ptr<NodeProgressBar> legacy_progress_dialog_node(
|
||||||
(void)attach_legacy_overlay_node(app, input);
|
const std::shared_ptr<pp::app::AppProgressDialog>& dialog) noexcept
|
||||||
return input;
|
{
|
||||||
|
auto legacy = std::dynamic_pointer_cast<LegacyAppProgressDialog>(dialog);
|
||||||
|
return legacy ? legacy->node() : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<NodeMessageBox> legacy_message_dialog_node(
|
||||||
|
const std::shared_ptr<pp::app::AppMessageDialog>& dialog) noexcept
|
||||||
|
{
|
||||||
|
auto legacy = std::dynamic_pointer_cast<LegacyAppMessageDialog>(dialog);
|
||||||
|
return legacy ? legacy->node() : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<NodeInputBox> legacy_input_dialog_node(
|
||||||
|
const std::shared_ptr<pp::app::AppInputDialog>& dialog) noexcept
|
||||||
|
{
|
||||||
|
auto legacy = std::dynamic_pointer_cast<LegacyAppInputDialog>(dialog);
|
||||||
|
return legacy ? legacy->node() : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace pp::panopainter
|
} // namespace pp::panopainter
|
||||||
|
|||||||
@@ -11,16 +11,27 @@ class NodeProgressBar;
|
|||||||
|
|
||||||
namespace pp::panopainter {
|
namespace pp::panopainter {
|
||||||
|
|
||||||
std::shared_ptr<NodeProgressBar> create_legacy_app_progress_dialog(
|
[[nodiscard]] std::unique_ptr<pp::app::AppDialogFactory> make_legacy_app_dialog_factory(App& app);
|
||||||
|
|
||||||
|
[[nodiscard]] std::shared_ptr<NodeProgressBar> create_legacy_app_progress_dialog(
|
||||||
App& app,
|
App& app,
|
||||||
const pp::app::AppProgressDialogPlan& plan);
|
const pp::app::AppProgressDialogPlan& plan);
|
||||||
|
|
||||||
std::shared_ptr<NodeMessageBox> create_legacy_app_message_dialog(
|
[[nodiscard]] std::shared_ptr<NodeMessageBox> create_legacy_app_message_dialog(
|
||||||
App& app,
|
App& app,
|
||||||
const pp::app::AppMessageDialogPlan& plan);
|
const pp::app::AppMessageDialogPlan& plan);
|
||||||
|
|
||||||
std::shared_ptr<NodeInputBox> create_legacy_app_input_dialog(
|
[[nodiscard]] std::shared_ptr<NodeInputBox> create_legacy_app_input_dialog(
|
||||||
App& app,
|
App& app,
|
||||||
const pp::app::AppInputDialogPlan& plan);
|
const pp::app::AppInputDialogPlan& plan);
|
||||||
|
|
||||||
|
[[nodiscard]] std::shared_ptr<NodeProgressBar> legacy_progress_dialog_node(
|
||||||
|
const std::shared_ptr<pp::app::AppProgressDialog>& dialog) noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]] std::shared_ptr<NodeMessageBox> legacy_message_dialog_node(
|
||||||
|
const std::shared_ptr<pp::app::AppMessageDialog>& dialog) noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]] std::shared_ptr<NodeInputBox> legacy_input_dialog_node(
|
||||||
|
const std::shared_ptr<pp::app::AppInputDialog>& dialog) noexcept;
|
||||||
|
|
||||||
} // namespace pp::panopainter
|
} // namespace pp::panopainter
|
||||||
|
|||||||
@@ -330,6 +330,42 @@ pp::foundation::Status export_equirectangular_from_document_snapshot(
|
|||||||
services);
|
services);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pp::foundation::Status export_depth_from_document_snapshot(
|
||||||
|
App& app,
|
||||||
|
const pp::app::DocumentDepthExportTarget& target,
|
||||||
|
const LegacyDocumentExportSnapshotReports& reports)
|
||||||
|
{
|
||||||
|
auto exported = pp::paint_renderer::export_document_depth_pngs(
|
||||||
|
pp::paint_renderer::DocumentDepthExportRenderPlanRequest {
|
||||||
|
.document = &reports.snapshot.document,
|
||||||
|
.frame_index = reports.snapshot.document.active_frame_index(),
|
||||||
|
});
|
||||||
|
if (!exported) {
|
||||||
|
return exported.status();
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG(
|
||||||
|
"export-depth document export PNG writer: %ux%u imageBytes=%llu depthBytes=%llu mergedFaceDraws=%zu layerDepthDraws=%zu visitedLayers=%zu visibleLayers=%zu facePayloads=%zu",
|
||||||
|
exported.value().output_extent.width,
|
||||||
|
exported.value().output_extent.height,
|
||||||
|
static_cast<unsigned long long>(exported.value().image_encoded_bytes),
|
||||||
|
static_cast<unsigned long long>(exported.value().depth_encoded_bytes),
|
||||||
|
exported.value().merged_face_draw_count,
|
||||||
|
exported.value().layer_depth_draw_count,
|
||||||
|
exported.value().visited_layer_count,
|
||||||
|
exported.value().visible_layer_count,
|
||||||
|
exported.value().face_payload_count);
|
||||||
|
|
||||||
|
LegacyExportWriteServices services(app);
|
||||||
|
return pp::app::execute_document_depth_export_write(
|
||||||
|
target,
|
||||||
|
pp::app::DocumentDepthExportPayload {
|
||||||
|
.image_bytes = std::span<const std::byte>(exported.value().image_png),
|
||||||
|
.depth_bytes = std::span<const std::byte>(exported.value().depth_png),
|
||||||
|
},
|
||||||
|
services);
|
||||||
|
}
|
||||||
|
|
||||||
class LegacyDocumentExportServices final : public pp::app::DocumentExportServices {
|
class LegacyDocumentExportServices final : public pp::app::DocumentExportServices {
|
||||||
public:
|
public:
|
||||||
explicit LegacyDocumentExportServices(App& app) noexcept
|
explicit LegacyDocumentExportServices(App& app) noexcept
|
||||||
@@ -565,15 +601,29 @@ public:
|
|||||||
|
|
||||||
const auto prepared = prepare_legacy_document_export_snapshot(app_, "export-depth");
|
const auto prepared = prepare_legacy_document_export_snapshot(app_, "export-depth");
|
||||||
if (prepared) {
|
if (prepared) {
|
||||||
const auto report = pp::app::make_document_canvas_save_snapshot_report(prepared.value().snapshot);
|
if (should_use_document_snapshot_writer(
|
||||||
const auto route = pp::app::plan_document_export_snapshot_route_for_current_platform(
|
"export-depth",
|
||||||
pp::app::DocumentExportExecutionKind::depth,
|
pp::app::DocumentExportExecutionKind::depth,
|
||||||
report);
|
prepared.value(),
|
||||||
if (!route.uses_document_snapshot_writer) {
|
{})) {
|
||||||
|
if (target) {
|
||||||
|
const auto exported = export_depth_from_document_snapshot(app_, target.value(), prepared.value());
|
||||||
|
if (exported.ok()) {
|
||||||
|
show_export_success_dialog(
|
||||||
|
app_,
|
||||||
|
pp::app::plan_document_export_success_dialog(
|
||||||
|
pp::app::DocumentExportSuccessKind::depth,
|
||||||
|
pp::app::document_export_media_platform_destination(),
|
||||||
|
app_.work_path));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG("export-depth document export writer retained legacy export after failure: %s", exported.message);
|
||||||
|
} else {
|
||||||
LOG(
|
LOG(
|
||||||
"export-depth document export writer retained legacy export: %.*s",
|
"export-depth document export writer retained legacy export after target failure: %s",
|
||||||
static_cast<int>(route.fallback_reason.size()),
|
target.status().message);
|
||||||
route.fallback_reason.data());
|
}
|
||||||
}
|
}
|
||||||
#if !__WEB__
|
#if !__WEB__
|
||||||
const auto plan = pp::paint_renderer::plan_document_depth_export_render(
|
const auto plan = pp::paint_renderer::plan_document_depth_export_render(
|
||||||
|
|||||||
@@ -3,7 +3,10 @@
|
|||||||
|
|
||||||
#include "app.h"
|
#include "app.h"
|
||||||
#include "node.h"
|
#include "node.h"
|
||||||
|
#include "node_input_box.h"
|
||||||
|
#include "node_message_box.h"
|
||||||
#include "node_popup_menu.h"
|
#include "node_popup_menu.h"
|
||||||
|
#include "node_progress_bar.h"
|
||||||
|
|
||||||
namespace pp::panopainter {
|
namespace pp::panopainter {
|
||||||
|
|
||||||
@@ -148,4 +151,45 @@ pp::foundation::Result<std::shared_ptr<NodePopupMenu>> add_legacy_popup_menu(
|
|||||||
return pp::foundation::Result<std::shared_ptr<NodePopupMenu>>::success(popup);
|
return pp::foundation::Result<std::shared_ptr<NodePopupMenu>>::success(popup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<NodeProgressBar> create_legacy_progress_dialog_overlay(
|
||||||
|
App& app,
|
||||||
|
const pp::app::AppProgressDialogPlan& plan)
|
||||||
|
{
|
||||||
|
auto progress = make_legacy_overlay_node<NodeProgressBar>(app);
|
||||||
|
progress->m_progress->SetWidthP(plan.progress_fraction);
|
||||||
|
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);
|
||||||
|
return progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<NodeMessageBox> create_legacy_message_dialog_overlay(
|
||||||
|
App& app,
|
||||||
|
const pp::app::AppMessageDialogPlan& plan)
|
||||||
|
{
|
||||||
|
auto message = make_legacy_overlay_node<NodeMessageBox>(app);
|
||||||
|
message->m_title->set_text(plan.title.c_str());
|
||||||
|
message->m_message->set_text(plan.message.c_str());
|
||||||
|
message->btn_ok->m_text->set_text(plan.ok_caption.c_str());
|
||||||
|
if (plan.show_cancel)
|
||||||
|
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);
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<NodeInputBox> create_legacy_input_dialog_overlay(
|
||||||
|
App& app,
|
||||||
|
const pp::app::AppInputDialogPlan& plan)
|
||||||
|
{
|
||||||
|
auto input = make_legacy_overlay_node<NodeInputBox>(app);
|
||||||
|
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);
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace pp::panopainter
|
} // namespace pp::panopainter
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "app_core/app_dialog.h"
|
||||||
#include "foundation/result.h"
|
#include "foundation/result.h"
|
||||||
#include "node.h"
|
#include "node.h"
|
||||||
|
|
||||||
@@ -7,7 +8,10 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
class App;
|
class App;
|
||||||
|
class NodeInputBox;
|
||||||
|
class NodeMessageBox;
|
||||||
class NodePopupMenu;
|
class NodePopupMenu;
|
||||||
|
class NodeProgressBar;
|
||||||
|
|
||||||
namespace pp::panopainter {
|
namespace pp::panopainter {
|
||||||
|
|
||||||
@@ -40,6 +44,18 @@ void close_legacy_popup_panel(
|
|||||||
float y,
|
float y,
|
||||||
float rtl_anchor_width) noexcept;
|
float rtl_anchor_width) noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]] std::shared_ptr<NodeProgressBar> create_legacy_progress_dialog_overlay(
|
||||||
|
App& app,
|
||||||
|
const pp::app::AppProgressDialogPlan& plan);
|
||||||
|
|
||||||
|
[[nodiscard]] std::shared_ptr<NodeMessageBox> create_legacy_message_dialog_overlay(
|
||||||
|
App& app,
|
||||||
|
const pp::app::AppMessageDialogPlan& plan);
|
||||||
|
|
||||||
|
[[nodiscard]] std::shared_ptr<NodeInputBox> create_legacy_input_dialog_overlay(
|
||||||
|
App& app,
|
||||||
|
const pp::app::AppInputDialogPlan& plan);
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
std::shared_ptr<T> make_legacy_overlay_node(App& app)
|
std::shared_ptr<T> make_legacy_overlay_node(App& app)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,6 +10,12 @@
|
|||||||
|
|
||||||
namespace pp::paint_renderer {
|
namespace pp::paint_renderer {
|
||||||
|
|
||||||
|
pp::foundation::Result<DocumentFrameCompositeResult> composite_document_layer_frame(
|
||||||
|
const pp::document::CanvasDocument& document,
|
||||||
|
std::size_t layer_index,
|
||||||
|
std::size_t frame_index,
|
||||||
|
pp::paint::Rgba clear_color);
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
[[nodiscard]] bool is_valid_blend_mode(pp::paint::BlendMode mode) noexcept
|
[[nodiscard]] bool is_valid_blend_mode(pp::paint::BlendMode mode) noexcept
|
||||||
@@ -122,6 +128,8 @@ struct CubeFaceSample {
|
|||||||
float t = 0.0F;
|
float t = 0.0F;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constexpr float document_depth_export_default_fov_degrees = 85.0F;
|
||||||
|
|
||||||
[[nodiscard]] CubeFaceSample panopainter_cube_face_sample(float x, float y, float z) noexcept
|
[[nodiscard]] CubeFaceSample panopainter_cube_face_sample(float x, float y, float z) noexcept
|
||||||
{
|
{
|
||||||
const auto ax = std::fabs(x);
|
const auto ax = std::fabs(x);
|
||||||
@@ -279,6 +287,56 @@ struct EquirectangularProjectionResult {
|
|||||||
std::size_t composited_layer_face_count = 0;
|
std::size_t composited_layer_face_count = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PerspectiveProjectionMap {
|
||||||
|
pp::renderer::Extent2D output_extent {};
|
||||||
|
std::vector<CubeFaceSample> samples;
|
||||||
|
};
|
||||||
|
|
||||||
|
pp::foundation::Result<PerspectiveProjectionMap> make_perspective_projection_map(
|
||||||
|
pp::renderer::Extent2D output_extent,
|
||||||
|
float vertical_fov_degrees)
|
||||||
|
{
|
||||||
|
if (!std::isfinite(vertical_fov_degrees)) {
|
||||||
|
return pp::foundation::Result<PerspectiveProjectionMap>::failure(
|
||||||
|
pp::foundation::Status::invalid_argument("document depth export field of view must be finite"));
|
||||||
|
}
|
||||||
|
if (vertical_fov_degrees <= 0.0F || vertical_fov_degrees >= 180.0F) {
|
||||||
|
return pp::foundation::Result<PerspectiveProjectionMap>::failure(
|
||||||
|
pp::foundation::Status::out_of_range("document depth export field of view must be between 0 and 180"));
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto output_pixel_count = expected_pixel_count(output_extent);
|
||||||
|
if (!output_pixel_count) {
|
||||||
|
return pp::foundation::Result<PerspectiveProjectionMap>::failure(output_pixel_count.status());
|
||||||
|
}
|
||||||
|
|
||||||
|
PerspectiveProjectionMap map;
|
||||||
|
map.output_extent = output_extent;
|
||||||
|
map.samples.reserve(output_pixel_count.value());
|
||||||
|
|
||||||
|
constexpr auto pi = 3.14159265358979323846F;
|
||||||
|
const auto tan_half_fov = std::tan(vertical_fov_degrees * pi / 360.0F);
|
||||||
|
const auto aspect = static_cast<float>(output_extent.width) / static_cast<float>(output_extent.height);
|
||||||
|
for (std::uint32_t y = 0; y < output_extent.height; ++y) {
|
||||||
|
const auto ny = 1.0F
|
||||||
|
- ((static_cast<float>(y) + 0.5F) / static_cast<float>(output_extent.height)) * 2.0F;
|
||||||
|
for (std::uint32_t x = 0; x < output_extent.width; ++x) {
|
||||||
|
const auto nx = ((static_cast<float>(x) + 0.5F) / static_cast<float>(output_extent.width)) * 2.0F
|
||||||
|
- 1.0F;
|
||||||
|
const auto dir_x = nx * aspect * tan_half_fov;
|
||||||
|
const auto dir_y = ny * tan_half_fov;
|
||||||
|
constexpr auto dir_z = -1.0F;
|
||||||
|
const auto inv_length = 1.0F / std::sqrt(dir_x * dir_x + dir_y * dir_y + dir_z * dir_z);
|
||||||
|
map.samples.push_back(panopainter_cube_face_sample(
|
||||||
|
dir_x * inv_length,
|
||||||
|
dir_y * inv_length,
|
||||||
|
dir_z * inv_length));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pp::foundation::Result<PerspectiveProjectionMap>::success(std::move(map));
|
||||||
|
}
|
||||||
|
|
||||||
pp::foundation::Result<EquirectangularProjectionResult> project_document_frame_equirectangular(
|
pp::foundation::Result<EquirectangularProjectionResult> project_document_frame_equirectangular(
|
||||||
const DocumentFrameCompositeResult& composite)
|
const DocumentFrameCompositeResult& composite)
|
||||||
{
|
{
|
||||||
@@ -340,6 +398,87 @@ pp::foundation::Result<EquirectangularProjectionResult> project_document_frame_e
|
|||||||
return pp::foundation::Result<EquirectangularProjectionResult>::success(std::move(result));
|
return pp::foundation::Result<EquirectangularProjectionResult>::success(std::move(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pp::foundation::Result<std::vector<pp::paint::Rgba>> project_document_frame_perspective(
|
||||||
|
const DocumentFrameCompositeResult& composite,
|
||||||
|
const PerspectiveProjectionMap& projection)
|
||||||
|
{
|
||||||
|
const auto face_pixel_count = expected_pixel_count(composite.extent);
|
||||||
|
if (!face_pixel_count) {
|
||||||
|
return pp::foundation::Result<std::vector<pp::paint::Rgba>>::failure(face_pixel_count.status());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& face : composite.faces) {
|
||||||
|
if (face.extent.width != composite.extent.width || face.extent.height != composite.extent.height
|
||||||
|
|| face.pixels.size() != face_pixel_count.value()) {
|
||||||
|
return pp::foundation::Result<std::vector<pp::paint::Rgba>>::failure(
|
||||||
|
pp::foundation::Status::invalid_argument("document depth export requires complete cube faces"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<pp::paint::Rgba> pixels;
|
||||||
|
pixels.reserve(projection.samples.size());
|
||||||
|
for (const auto& sample : projection.samples) {
|
||||||
|
pixels.push_back(sample_face_nearest(composite.faces[sample.face_index], sample.s, sample.t));
|
||||||
|
}
|
||||||
|
|
||||||
|
return pp::foundation::Result<std::vector<pp::paint::Rgba>>::success(std::move(pixels));
|
||||||
|
}
|
||||||
|
|
||||||
|
float sample_face_alpha_nearest(
|
||||||
|
const DocumentFaceCompositeResult& face,
|
||||||
|
float s,
|
||||||
|
float t) noexcept
|
||||||
|
{
|
||||||
|
return sample_face_nearest(face, s, t).a;
|
||||||
|
}
|
||||||
|
|
||||||
|
pp::foundation::Result<std::vector<pp::paint::Rgba>> project_document_depth_perspective(
|
||||||
|
const pp::document::CanvasDocument& document,
|
||||||
|
std::size_t frame_index,
|
||||||
|
const PerspectiveProjectionMap& projection)
|
||||||
|
{
|
||||||
|
std::vector<pp::paint::Rgba> pixels(
|
||||||
|
projection.samples.size(),
|
||||||
|
pp::paint::Rgba {
|
||||||
|
.r = 0.0F,
|
||||||
|
.g = 0.0F,
|
||||||
|
.b = 0.0F,
|
||||||
|
.a = 1.0F,
|
||||||
|
});
|
||||||
|
const auto layer_count = document.layers().size();
|
||||||
|
for (std::size_t layer_index = 0; layer_index < layer_count; ++layer_index) {
|
||||||
|
const auto& layer = document.layers()[layer_index];
|
||||||
|
if (!layer.visible || layer.opacity == 0.0F || frame_index >= layer.frames.size()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto composite = composite_document_layer_frame(document, layer_index, frame_index, {});
|
||||||
|
if (!composite) {
|
||||||
|
return pp::foundation::Result<std::vector<pp::paint::Rgba>>::failure(composite.status());
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto gray = static_cast<float>(layer_index + 1U) / static_cast<float>(layer_count + 1U);
|
||||||
|
const pp::paint::Rgba layer_color {
|
||||||
|
.r = gray,
|
||||||
|
.g = gray,
|
||||||
|
.b = gray,
|
||||||
|
.a = 1.0F,
|
||||||
|
};
|
||||||
|
for (std::size_t pixel_index = 0; pixel_index < projection.samples.size(); ++pixel_index) {
|
||||||
|
const auto& sample = projection.samples[pixel_index];
|
||||||
|
const auto alpha = sample_face_alpha_nearest(
|
||||||
|
composite.value().faces[sample.face_index],
|
||||||
|
sample.s,
|
||||||
|
sample.t);
|
||||||
|
if (alpha > 0.01F) {
|
||||||
|
pixels[pixel_index] = layer_color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pp::foundation::Result<std::vector<pp::paint::Rgba>>::success(std::move(pixels));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pp::foundation::Status composite_layer(
|
pp::foundation::Status composite_layer(
|
||||||
@@ -806,6 +945,79 @@ pp::foundation::Result<DocumentDepthExportRenderPlan> plan_document_depth_export
|
|||||||
return pp::foundation::Result<DocumentDepthExportRenderPlan>::success(plan);
|
return pp::foundation::Result<DocumentDepthExportRenderPlan>::success(plan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pp::foundation::Result<DocumentDepthPngExportResult> export_document_depth_pngs(
|
||||||
|
DocumentDepthExportRenderPlanRequest request)
|
||||||
|
{
|
||||||
|
auto plan = plan_document_depth_export_render(request);
|
||||||
|
if (!plan) {
|
||||||
|
return pp::foundation::Result<DocumentDepthPngExportResult>::failure(plan.status());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto projection = make_perspective_projection_map(
|
||||||
|
plan.value().output_extent,
|
||||||
|
document_depth_export_default_fov_degrees);
|
||||||
|
if (!projection) {
|
||||||
|
return pp::foundation::Result<DocumentDepthPngExportResult>::failure(projection.status());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto image_composite = composite_document_frame(DocumentFrameCompositeRequest {
|
||||||
|
.document = request.document,
|
||||||
|
.frame_index = request.frame_index,
|
||||||
|
.clear_color = pp::paint::Rgba {
|
||||||
|
.r = 0.0F,
|
||||||
|
.g = 0.0F,
|
||||||
|
.b = 0.0F,
|
||||||
|
.a = 1.0F,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!image_composite) {
|
||||||
|
return pp::foundation::Result<DocumentDepthPngExportResult>::failure(image_composite.status());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto image_pixels = project_document_frame_perspective(image_composite.value(), projection.value());
|
||||||
|
if (!image_pixels) {
|
||||||
|
return pp::foundation::Result<DocumentDepthPngExportResult>::failure(image_pixels.status());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto depth_pixels = project_document_depth_perspective(*request.document, request.frame_index, projection.value());
|
||||||
|
if (!depth_pixels) {
|
||||||
|
return pp::foundation::Result<DocumentDepthPngExportResult>::failure(depth_pixels.status());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::uint8_t> rgba8;
|
||||||
|
append_rgba8_bytes(rgba8, image_pixels.value());
|
||||||
|
auto image_png = pp::assets::encode_png_rgba8(
|
||||||
|
plan.value().output_extent.width,
|
||||||
|
plan.value().output_extent.height,
|
||||||
|
rgba8);
|
||||||
|
if (!image_png) {
|
||||||
|
return pp::foundation::Result<DocumentDepthPngExportResult>::failure(image_png.status());
|
||||||
|
}
|
||||||
|
|
||||||
|
append_rgba8_bytes(rgba8, depth_pixels.value());
|
||||||
|
auto depth_png = pp::assets::encode_png_rgba8(
|
||||||
|
plan.value().output_extent.width,
|
||||||
|
plan.value().output_extent.height,
|
||||||
|
rgba8);
|
||||||
|
if (!depth_png) {
|
||||||
|
return pp::foundation::Result<DocumentDepthPngExportResult>::failure(depth_png.status());
|
||||||
|
}
|
||||||
|
|
||||||
|
DocumentDepthPngExportResult result;
|
||||||
|
result.output_extent = plan.value().output_extent;
|
||||||
|
result.image_encoded_bytes = static_cast<std::uint64_t>(image_png.value().size());
|
||||||
|
result.depth_encoded_bytes = static_cast<std::uint64_t>(depth_png.value().size());
|
||||||
|
result.merged_face_draw_count = plan.value().merged_face_draw_count;
|
||||||
|
result.layer_depth_draw_count = plan.value().layer_depth_draw_count;
|
||||||
|
result.visited_layer_count = plan.value().visited_layer_count;
|
||||||
|
result.visible_layer_count = plan.value().visible_layer_count;
|
||||||
|
result.face_payload_count = plan.value().face_payload_count;
|
||||||
|
result.uses_perspective_camera = plan.value().uses_perspective_camera;
|
||||||
|
result.image_png = std::move(image_png.value());
|
||||||
|
result.depth_png = std::move(depth_png.value());
|
||||||
|
return pp::foundation::Result<DocumentDepthPngExportResult>::success(std::move(result));
|
||||||
|
}
|
||||||
|
|
||||||
pp::foundation::Result<DocumentLayerEquirectangularPngExportResult>
|
pp::foundation::Result<DocumentLayerEquirectangularPngExportResult>
|
||||||
export_document_layers_equirectangular_pngs(DocumentLayerEquirectangularPngExportRequest request)
|
export_document_layers_equirectangular_pngs(DocumentLayerEquirectangularPngExportRequest request)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -189,6 +189,23 @@ struct DocumentDepthExportRenderPlan {
|
|||||||
bool requires_renderer_readback = true;
|
bool requires_renderer_readback = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct DocumentDepthPngExportResult {
|
||||||
|
pp::renderer::Extent2D output_extent {
|
||||||
|
.width = 1024,
|
||||||
|
.height = 1024,
|
||||||
|
};
|
||||||
|
std::vector<std::byte> image_png;
|
||||||
|
std::vector<std::byte> depth_png;
|
||||||
|
std::uint64_t image_encoded_bytes = 0;
|
||||||
|
std::uint64_t depth_encoded_bytes = 0;
|
||||||
|
std::size_t merged_face_draw_count = 0;
|
||||||
|
std::size_t layer_depth_draw_count = 0;
|
||||||
|
std::size_t visited_layer_count = 0;
|
||||||
|
std::size_t visible_layer_count = 0;
|
||||||
|
std::size_t face_payload_count = 0;
|
||||||
|
bool uses_perspective_camera = true;
|
||||||
|
};
|
||||||
|
|
||||||
struct DocumentLayerEquirectangularPngExportRequest {
|
struct DocumentLayerEquirectangularPngExportRequest {
|
||||||
const pp::document::CanvasDocument* document = nullptr;
|
const pp::document::CanvasDocument* document = nullptr;
|
||||||
std::size_t frame_index = 0;
|
std::size_t frame_index = 0;
|
||||||
@@ -278,6 +295,9 @@ export_document_frame_equirectangular_jpeg(
|
|||||||
[[nodiscard]] pp::foundation::Result<DocumentDepthExportRenderPlan> plan_document_depth_export_render(
|
[[nodiscard]] pp::foundation::Result<DocumentDepthExportRenderPlan> plan_document_depth_export_render(
|
||||||
DocumentDepthExportRenderPlanRequest request) noexcept;
|
DocumentDepthExportRenderPlanRequest request) noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]] pp::foundation::Result<DocumentDepthPngExportResult> export_document_depth_pngs(
|
||||||
|
DocumentDepthExportRenderPlanRequest request);
|
||||||
|
|
||||||
[[nodiscard]] pp::foundation::Result<DocumentLayerEquirectangularPngExportResult>
|
[[nodiscard]] pp::foundation::Result<DocumentLayerEquirectangularPngExportResult>
|
||||||
export_document_layers_equirectangular_pngs(DocumentLayerEquirectangularPngExportRequest request);
|
export_document_layers_equirectangular_pngs(DocumentLayerEquirectangularPngExportRequest request);
|
||||||
|
|
||||||
|
|||||||
128
src/platform_apple/apple_platform_services.cpp
Normal file
128
src/platform_apple/apple_platform_services.cpp
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
#include "platform_apple/apple_platform_services.h"
|
||||||
|
|
||||||
|
#include "app_core/document_platform_io.h"
|
||||||
|
#include "platform_api/platform_policy.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace pp::platform::apple {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
[[nodiscard]] std::vector<std::string> apple_image_file_types()
|
||||||
|
{
|
||||||
|
static constexpr std::array<std::string_view, 5> kFileTypes = {
|
||||||
|
"png",
|
||||||
|
"PNG",
|
||||||
|
"jpg",
|
||||||
|
"JPG",
|
||||||
|
"jpeg",
|
||||||
|
};
|
||||||
|
|
||||||
|
return { kFileTypes.begin(), kFileTypes.end() };
|
||||||
|
}
|
||||||
|
|
||||||
|
void invoke_picked_path_if_selected(
|
||||||
|
const std::string& path,
|
||||||
|
const PickedPathCallback& callback)
|
||||||
|
{
|
||||||
|
if (pp::app::plan_picked_path(path) == pp::app::PickedPathAction::invoke_callback)
|
||||||
|
callback(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
AppleDocumentPlatformServices::AppleDocumentPlatformServices(
|
||||||
|
PlatformFamily family,
|
||||||
|
AppleDocumentPickerBridge bridge)
|
||||||
|
: family_(family)
|
||||||
|
, bridge_(std::move(bridge))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> AppleDocumentPlatformServices::document_browse_roots(
|
||||||
|
std::string_view work_path,
|
||||||
|
std::string_view data_path) const
|
||||||
|
{
|
||||||
|
return platform_document_browse_roots(family_, work_path, data_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppleDocumentPlatformServices::pick_image(PickedPathCallback callback) const
|
||||||
|
{
|
||||||
|
if (family_ == PlatformFamily::ios)
|
||||||
|
{
|
||||||
|
if (bridge_.pick_image)
|
||||||
|
bridge_.pick_image(std::move(callback));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (family_ == PlatformFamily::macos && bridge_.pick_file)
|
||||||
|
{
|
||||||
|
bridge_.pick_file(
|
||||||
|
apple_image_file_types(),
|
||||||
|
[callback = std::move(callback)](std::string path) {
|
||||||
|
invoke_picked_path_if_selected(path, callback);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppleDocumentPlatformServices::pick_file(
|
||||||
|
std::vector<std::string> file_types,
|
||||||
|
PickedPathCallback callback) const
|
||||||
|
{
|
||||||
|
if (!bridge_.pick_file)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (family_ == PlatformFamily::ios)
|
||||||
|
{
|
||||||
|
bridge_.pick_file(std::move(file_types), std::move(callback));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (family_ == PlatformFamily::macos)
|
||||||
|
{
|
||||||
|
bridge_.pick_file(
|
||||||
|
std::move(file_types),
|
||||||
|
[callback = std::move(callback)](std::string path) {
|
||||||
|
invoke_picked_path_if_selected(path, callback);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppleDocumentPlatformServices::pick_save_file(
|
||||||
|
std::vector<std::string> file_types,
|
||||||
|
PickedPathCallback callback) const
|
||||||
|
{
|
||||||
|
if (family_ == PlatformFamily::macos && bridge_.pick_save_file)
|
||||||
|
{
|
||||||
|
bridge_.pick_save_file(
|
||||||
|
std::move(file_types),
|
||||||
|
[callback = std::move(callback)](std::string path) {
|
||||||
|
invoke_picked_path_if_selected(path, callback);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppleDocumentPlatformServices::pick_directory(PickedPathCallback callback) const
|
||||||
|
{
|
||||||
|
if (family_ == PlatformFamily::macos && bridge_.pick_directory)
|
||||||
|
{
|
||||||
|
bridge_.pick_directory([callback = std::move(callback)](std::string path) {
|
||||||
|
invoke_picked_path_if_selected(path, callback);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AppleDocumentPlatformServices::supports_working_directory_picker() const
|
||||||
|
{
|
||||||
|
return platform_supports_working_directory_picker(family_);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string AppleDocumentPlatformServices::format_working_directory_path(std::string_view path) const
|
||||||
|
{
|
||||||
|
if (family_ == PlatformFamily::macos && bridge_.format_working_directory_path)
|
||||||
|
return bridge_.format_working_directory_path(path);
|
||||||
|
return std::string(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
44
src/platform_apple/apple_platform_services.h
Normal file
44
src/platform_apple/apple_platform_services.h
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "platform_api/platform_policy.h"
|
||||||
|
#include "platform_api/platform_services.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace pp::platform::apple {
|
||||||
|
|
||||||
|
struct AppleDocumentPickerBridge {
|
||||||
|
std::function<void(PickedPathCallback callback)> pick_image;
|
||||||
|
std::function<void(std::vector<std::string> file_types, PickedPathCallback callback)> pick_file;
|
||||||
|
std::function<void(std::vector<std::string> file_types, PickedPathCallback callback)> pick_save_file;
|
||||||
|
std::function<void(PickedPathCallback callback)> pick_directory;
|
||||||
|
std::function<std::string(std::string_view path)> format_working_directory_path;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AppleDocumentPlatformServices {
|
||||||
|
public:
|
||||||
|
explicit AppleDocumentPlatformServices(
|
||||||
|
PlatformFamily family,
|
||||||
|
AppleDocumentPickerBridge bridge = {});
|
||||||
|
|
||||||
|
[[nodiscard]] std::vector<std::string> document_browse_roots(
|
||||||
|
std::string_view work_path,
|
||||||
|
std::string_view data_path) const;
|
||||||
|
|
||||||
|
void pick_image(PickedPathCallback callback) const;
|
||||||
|
void pick_file(std::vector<std::string> file_types, PickedPathCallback callback) const;
|
||||||
|
void pick_save_file(std::vector<std::string> file_types, PickedPathCallback callback) const;
|
||||||
|
void pick_directory(PickedPathCallback callback) const;
|
||||||
|
|
||||||
|
[[nodiscard]] bool supports_working_directory_picker() const;
|
||||||
|
[[nodiscard]] std::string format_working_directory_path(std::string_view path) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
PlatformFamily family_;
|
||||||
|
AppleDocumentPickerBridge bridge_;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -2,9 +2,9 @@
|
|||||||
#include "platform_legacy/legacy_platform_services.h"
|
#include "platform_legacy/legacy_platform_services.h"
|
||||||
|
|
||||||
#include "app.h"
|
#include "app.h"
|
||||||
#include "app_core/document_platform_io.h"
|
|
||||||
#include "legacy_ui_gl_dispatch.h"
|
#include "legacy_ui_gl_dispatch.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "platform_apple/apple_platform_services.h"
|
||||||
#include "platform_api/network_tls_policy.h"
|
#include "platform_api/network_tls_policy.h"
|
||||||
#include "platform_api/platform_policy.h"
|
#include "platform_api/platform_policy.h"
|
||||||
#include "renderer_gl/opengl_capabilities.h"
|
#include "renderer_gl/opengl_capabilities.h"
|
||||||
@@ -52,14 +52,6 @@ void webgl_sync();
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void invoke_picked_path_if_selected(
|
|
||||||
const std::string& path,
|
|
||||||
const std::function<void(std::string path)>& callback)
|
|
||||||
{
|
|
||||||
if (pp::app::plan_picked_path(path) == pp::app::PickedPathAction::invoke_callback)
|
|
||||||
callback(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __WEB__
|
#ifdef __WEB__
|
||||||
class RetainedWebPlatformServices final : public pp::platform::WebPlatformServices {
|
class RetainedWebPlatformServices final : public pp::platform::WebPlatformServices {
|
||||||
public:
|
public:
|
||||||
@@ -102,6 +94,79 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(__IOS__) || defined(__OSX__)
|
||||||
|
[[nodiscard]] NSMutableArray<NSString*>* apple_file_types_array(const std::vector<std::string>& file_types)
|
||||||
|
{
|
||||||
|
NSMutableArray<NSString*>* types = [NSMutableArray arrayWithCapacity:file_types.size()];
|
||||||
|
for (const auto& type : file_types)
|
||||||
|
{
|
||||||
|
[types addObject:[NSString stringWithCString:type.c_str() encoding:NSUTF8StringEncoding]];
|
||||||
|
}
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] pp::platform::apple::AppleDocumentPlatformServices& active_apple_document_platform_services()
|
||||||
|
{
|
||||||
|
#ifdef __IOS__
|
||||||
|
static pp::platform::apple::AppleDocumentPlatformServices services(
|
||||||
|
pp::platform::PlatformFamily::ios,
|
||||||
|
[] {
|
||||||
|
pp::platform::apple::AppleDocumentPickerBridge bridge;
|
||||||
|
bridge.pick_image = [](pp::platform::PickedPathCallback callback) {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
[App::I->ios_view pick_photo:callback];
|
||||||
|
});
|
||||||
|
};
|
||||||
|
bridge.pick_file = [](
|
||||||
|
std::vector<std::string> file_types,
|
||||||
|
pp::platform::PickedPathCallback callback) {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
[App::I->ios_view pick_file:apple_file_types_array(file_types) then:callback];
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return bridge;
|
||||||
|
}());
|
||||||
|
return services;
|
||||||
|
#else
|
||||||
|
static pp::platform::apple::AppleDocumentPlatformServices services(
|
||||||
|
pp::platform::PlatformFamily::macos,
|
||||||
|
[] {
|
||||||
|
pp::platform::apple::AppleDocumentPickerBridge bridge;
|
||||||
|
bridge.pick_file = [](
|
||||||
|
std::vector<std::string> file_types,
|
||||||
|
pp::platform::PickedPathCallback callback) {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
const std::string path = [App::I->osx_view pick_file:apple_file_types_array(file_types)];
|
||||||
|
callback(path);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
bridge.pick_save_file = [](
|
||||||
|
std::vector<std::string> file_types,
|
||||||
|
pp::platform::PickedPathCallback callback) {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
const std::string path = [App::I->osx_view pick_file_save:apple_file_types_array(file_types)];
|
||||||
|
callback(path);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
bridge.pick_directory = [](pp::platform::PickedPathCallback callback) {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
const std::string path = [App::I->osx_view pick_dir];
|
||||||
|
callback(path);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
bridge.format_working_directory_path = [](std::string_view path) {
|
||||||
|
char path_buffer[4096] = {};
|
||||||
|
if (realpath(std::string(path).c_str(), path_buffer))
|
||||||
|
return std::string(path_buffer);
|
||||||
|
return std::string(path);
|
||||||
|
};
|
||||||
|
return bridge;
|
||||||
|
}());
|
||||||
|
return services;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// DEBT-0017: fallback for platforms that do not inject PlatformServices yet.
|
// DEBT-0017: fallback for platforms that do not inject PlatformServices yet.
|
||||||
class LegacyPlatformServices final : public pp::platform::PlatformServices {
|
class LegacyPlatformServices final : public pp::platform::PlatformServices {
|
||||||
public:
|
public:
|
||||||
@@ -376,10 +441,14 @@ public:
|
|||||||
std::string_view work_path,
|
std::string_view work_path,
|
||||||
std::string_view data_path) override
|
std::string_view data_path) override
|
||||||
{
|
{
|
||||||
|
#if defined(__IOS__) || defined(__OSX__)
|
||||||
|
return active_apple_document_platform_services().document_browse_roots(work_path, data_path);
|
||||||
|
#else
|
||||||
return pp::platform::platform_document_browse_roots(
|
return pp::platform::platform_document_browse_roots(
|
||||||
pp::platform::current_platform_family(),
|
pp::platform::current_platform_family(),
|
||||||
work_path,
|
work_path,
|
||||||
data_path);
|
data_path);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void save_ui_state() override
|
void save_ui_state() override
|
||||||
@@ -414,15 +483,9 @@ public:
|
|||||||
void pick_image(pp::platform::PickedPathCallback callback) override
|
void pick_image(pp::platform::PickedPathCallback callback) override
|
||||||
{
|
{
|
||||||
#ifdef __IOS__
|
#ifdef __IOS__
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
active_apple_document_platform_services().pick_image(std::move(callback));
|
||||||
[App::I->ios_view pick_photo:callback];
|
|
||||||
});
|
|
||||||
#elif __OSX__
|
#elif __OSX__
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
active_apple_document_platform_services().pick_image(std::move(callback));
|
||||||
NSArray* fileTypes = [NSArray arrayWithObjects:@"png", @"PNG", @"jpg", @"JPG", @"jpeg", nil];
|
|
||||||
std::string path = [App::I->osx_view pick_file:fileTypes];
|
|
||||||
invoke_picked_path_if_selected(path, callback);
|
|
||||||
});
|
|
||||||
#elif __ANDROID__
|
#elif __ANDROID__
|
||||||
android_pick_file(callback);
|
android_pick_file(callback);
|
||||||
#elif __LINUX__
|
#elif __LINUX__
|
||||||
@@ -438,20 +501,9 @@ public:
|
|||||||
void pick_file(std::vector<std::string> file_types, pp::platform::PickedPathCallback callback) override
|
void pick_file(std::vector<std::string> file_types, pp::platform::PickedPathCallback callback) override
|
||||||
{
|
{
|
||||||
#ifdef __IOS__
|
#ifdef __IOS__
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
active_apple_document_platform_services().pick_file(std::move(file_types), std::move(callback));
|
||||||
NSMutableArray<NSString*>* fileTypes = [NSMutableArray arrayWithCapacity:file_types.size()];
|
|
||||||
for (const auto& t : file_types)
|
|
||||||
[fileTypes addObject:[NSString stringWithCString:t.c_str() encoding:NSUTF8StringEncoding]];
|
|
||||||
[App::I->ios_view pick_file:fileTypes then:callback];
|
|
||||||
});
|
|
||||||
#elif __OSX__
|
#elif __OSX__
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
active_apple_document_platform_services().pick_file(std::move(file_types), std::move(callback));
|
||||||
NSMutableArray<NSString*>* fileTypes = [NSMutableArray arrayWithCapacity:file_types.size()];
|
|
||||||
for (const auto& t : file_types)
|
|
||||||
[fileTypes addObject:[NSString stringWithCString:t.c_str() encoding:NSUTF8StringEncoding]];
|
|
||||||
std::string path = [App::I->osx_view pick_file:fileTypes];
|
|
||||||
invoke_picked_path_if_selected(path, callback);
|
|
||||||
});
|
|
||||||
#elif __ANDROID__
|
#elif __ANDROID__
|
||||||
android_pick_file(callback);
|
android_pick_file(callback);
|
||||||
#elif __LINUX__
|
#elif __LINUX__
|
||||||
@@ -468,13 +520,7 @@ public:
|
|||||||
void pick_save_file(std::vector<std::string> file_types, pp::platform::PickedPathCallback callback) override
|
void pick_save_file(std::vector<std::string> file_types, pp::platform::PickedPathCallback callback) override
|
||||||
{
|
{
|
||||||
#if __OSX__
|
#if __OSX__
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
active_apple_document_platform_services().pick_save_file(std::move(file_types), std::move(callback));
|
||||||
NSMutableArray<NSString*>* fileTypes = [NSMutableArray arrayWithCapacity:file_types.size()];
|
|
||||||
for (const auto& t : file_types)
|
|
||||||
[fileTypes addObject:[NSString stringWithCString:t.c_str() encoding:NSUTF8StringEncoding]];
|
|
||||||
std::string path = [App::I->osx_view pick_file_save:fileTypes];
|
|
||||||
invoke_picked_path_if_selected(path, callback);
|
|
||||||
});
|
|
||||||
#elif __ANDROID__
|
#elif __ANDROID__
|
||||||
android_pick_file_save(callback);
|
android_pick_file_save(callback);
|
||||||
#else
|
#else
|
||||||
@@ -488,10 +534,7 @@ public:
|
|||||||
#ifdef __IOS__
|
#ifdef __IOS__
|
||||||
(void)callback;
|
(void)callback;
|
||||||
#elif __OSX__
|
#elif __OSX__
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
active_apple_document_platform_services().pick_directory(std::move(callback));
|
||||||
std::string path = [App::I->osx_view pick_dir];
|
|
||||||
invoke_picked_path_if_selected(path, callback);
|
|
||||||
});
|
|
||||||
#elif __ANDROID__
|
#elif __ANDROID__
|
||||||
(void)callback;
|
(void)callback;
|
||||||
#else
|
#else
|
||||||
@@ -501,16 +544,18 @@ public:
|
|||||||
|
|
||||||
[[nodiscard]] bool supports_working_directory_picker() override
|
[[nodiscard]] bool supports_working_directory_picker() override
|
||||||
{
|
{
|
||||||
|
#if defined(__IOS__) || defined(__OSX__)
|
||||||
|
return active_apple_document_platform_services().supports_working_directory_picker();
|
||||||
|
#else
|
||||||
return pp::platform::platform_supports_working_directory_picker(
|
return pp::platform::platform_supports_working_directory_picker(
|
||||||
pp::platform::current_platform_family());
|
pp::platform::current_platform_family());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::string format_working_directory_path(std::string_view path) override
|
[[nodiscard]] std::string format_working_directory_path(std::string_view path) override
|
||||||
{
|
{
|
||||||
#if defined(__OSX__)
|
#if defined(__IOS__) || defined(__OSX__)
|
||||||
char path_buffer[4096] = {};
|
return active_apple_document_platform_services().format_working_directory_path(path);
|
||||||
if (realpath(std::string(path).c_str(), path_buffer))
|
|
||||||
return path_buffer;
|
|
||||||
#endif
|
#endif
|
||||||
return std::string(path);
|
return std::string(path);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -928,7 +928,7 @@ if(TARGET pano_cli)
|
|||||||
COMMAND pano_cli plan-export-snapshot-route --kind depth)
|
COMMAND pano_cli plan-export-snapshot-route --kind depth)
|
||||||
set_tests_properties(pano_cli_plan_export_snapshot_route_depth_smoke PROPERTIES
|
set_tests_properties(pano_cli_plan_export_snapshot_route_depth_smoke PROPERTIES
|
||||||
LABELS "app;integration;desktop-fast"
|
LABELS "app;integration;desktop-fast"
|
||||||
PASS_REGULAR_EXPRESSION "\"command\":\"plan-export-snapshot-route\".*\"kind\":\"depth\".*\"targetSupported\":false.*\"platformSupported\":true.*\"action\":\"use-legacy-export\".*\"fallbackReason\":\"document snapshot export does not support this target\"")
|
PASS_REGULAR_EXPRESSION "\"command\":\"plan-export-snapshot-route\".*\"kind\":\"depth\".*\"targetSupported\":true.*\"platformSupported\":true.*\"action\":\"use-document-snapshot-writer\".*\"usesDocumentSnapshotWriter\":true.*\"fallbackReason\":\"\"")
|
||||||
|
|
||||||
add_test(NAME pano_cli_plan_export_menu_rejects_unknown
|
add_test(NAME pano_cli_plan_export_menu_rejects_unknown
|
||||||
COMMAND pano_cli plan-export-menu --kind unknown)
|
COMMAND pano_cli plan-export-menu --kind unknown)
|
||||||
|
|||||||
@@ -3,6 +3,51 @@
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
class TestProgressDialog final : public pp::app::AppProgressDialog {
|
||||||
|
public:
|
||||||
|
[[nodiscard]] pp::app::AppDialogKind kind() const noexcept override
|
||||||
|
{
|
||||||
|
return pp::app::AppDialogKind::progress;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class TestMessageDialog final : public pp::app::AppMessageDialog {
|
||||||
|
public:
|
||||||
|
[[nodiscard]] pp::app::AppDialogKind kind() const noexcept override
|
||||||
|
{
|
||||||
|
return pp::app::AppDialogKind::message;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class TestInputDialog final : public pp::app::AppInputDialog {
|
||||||
|
public:
|
||||||
|
[[nodiscard]] pp::app::AppDialogKind kind() const noexcept override
|
||||||
|
{
|
||||||
|
return pp::app::AppDialogKind::input;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class TestDialogFactory final : public pp::app::AppDialogFactory {
|
||||||
|
public:
|
||||||
|
std::shared_ptr<pp::app::AppProgressDialog> show_progress_dialog(
|
||||||
|
const pp::app::AppProgressDialogPlan&) override
|
||||||
|
{
|
||||||
|
return std::make_shared<TestProgressDialog>();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<pp::app::AppMessageDialog> show_message_dialog(
|
||||||
|
const pp::app::AppMessageDialogPlan&) override
|
||||||
|
{
|
||||||
|
return std::make_shared<TestMessageDialog>();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<pp::app::AppInputDialog> show_input_dialog(
|
||||||
|
const pp::app::AppInputDialogPlan&) override
|
||||||
|
{
|
||||||
|
return std::make_shared<TestInputDialog>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
void progress_dialog_initializes_progress_state(pp::tests::Harness& harness)
|
void progress_dialog_initializes_progress_state(pp::tests::Harness& harness)
|
||||||
{
|
{
|
||||||
const auto plan = pp::app::plan_app_progress_dialog("Saving", 12);
|
const auto plan = pp::app::plan_app_progress_dialog("Saving", 12);
|
||||||
@@ -62,6 +107,14 @@ void input_dialog_rejects_empty_ok_caption(pp::tests::Harness& harness)
|
|||||||
PP_EXPECT(harness, plan.status().code == pp::foundation::StatusCode::invalid_argument);
|
PP_EXPECT(harness, plan.status().code == pp::foundation::StatusCode::invalid_argument);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void dialog_factory_uses_pure_app_core_dialog_types(pp::tests::Harness& harness)
|
||||||
|
{
|
||||||
|
TestDialogFactory factory;
|
||||||
|
PP_EXPECT(harness, factory.show_progress_dialog({})->kind() == pp::app::AppDialogKind::progress);
|
||||||
|
PP_EXPECT(harness, factory.show_message_dialog({})->kind() == pp::app::AppDialogKind::message);
|
||||||
|
PP_EXPECT(harness, factory.show_input_dialog({})->kind() == pp::app::AppDialogKind::input);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
@@ -74,5 +127,6 @@ int main()
|
|||||||
harness.run("message dialog allows custom button captions", message_dialog_allows_custom_button_captions);
|
harness.run("message dialog allows custom button captions", message_dialog_allows_custom_button_captions);
|
||||||
harness.run("input dialog preserves ok caption", input_dialog_preserves_ok_caption);
|
harness.run("input dialog preserves ok caption", input_dialog_preserves_ok_caption);
|
||||||
harness.run("input dialog rejects empty ok caption", input_dialog_rejects_empty_ok_caption);
|
harness.run("input dialog rejects empty ok caption", input_dialog_rejects_empty_ok_caption);
|
||||||
|
harness.run("dialog factory uses pure app-core dialog types", dialog_factory_uses_pure_app_core_dialog_types);
|
||||||
return harness.finish();
|
return harness.finish();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -880,7 +880,7 @@ void export_snapshot_target_support_covers_document_writer_formats(pp::tests::Ha
|
|||||||
pp::app::DocumentExportExecutionKind::cube_faces));
|
pp::app::DocumentExportExecutionKind::cube_faces));
|
||||||
PP_EXPECT(
|
PP_EXPECT(
|
||||||
harness,
|
harness,
|
||||||
!pp::app::document_export_snapshot_target_supported(
|
pp::app::document_export_snapshot_target_supported(
|
||||||
pp::app::DocumentExportExecutionKind::depth));
|
pp::app::DocumentExportExecutionKind::depth));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -930,7 +930,7 @@ void export_snapshot_route_for_current_platform_uses_platform_policy(pp::tests::
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void export_snapshot_route_for_current_platform_reports_depth_fallback(pp::tests::Harness& harness)
|
void export_snapshot_route_for_current_platform_supports_depth_writer(pp::tests::Harness& harness)
|
||||||
{
|
{
|
||||||
pp::app::DocumentCanvasSaveSnapshotReport report;
|
pp::app::DocumentCanvasSaveSnapshotReport report;
|
||||||
report.payload_complete = true;
|
report.payload_complete = true;
|
||||||
@@ -940,18 +940,17 @@ void export_snapshot_route_for_current_platform_reports_depth_fallback(pp::tests
|
|||||||
pp::app::DocumentExportExecutionKind::depth,
|
pp::app::DocumentExportExecutionKind::depth,
|
||||||
report);
|
report);
|
||||||
|
|
||||||
PP_EXPECT(harness, !plan.uses_document_snapshot_writer);
|
|
||||||
#if __WEB__
|
#if __WEB__
|
||||||
|
PP_EXPECT(harness, !plan.uses_document_snapshot_writer);
|
||||||
PP_EXPECT(harness, !plan.platform_supported);
|
PP_EXPECT(harness, !plan.platform_supported);
|
||||||
PP_EXPECT(
|
PP_EXPECT(
|
||||||
harness,
|
harness,
|
||||||
plan.fallback_reason == "document snapshot export is disabled on this platform");
|
plan.fallback_reason == "document snapshot export is disabled on this platform");
|
||||||
#else
|
#else
|
||||||
|
PP_EXPECT(harness, plan.uses_document_snapshot_writer);
|
||||||
PP_EXPECT(harness, plan.platform_supported);
|
PP_EXPECT(harness, plan.platform_supported);
|
||||||
PP_EXPECT(harness, !plan.target_supported);
|
PP_EXPECT(harness, plan.target_supported);
|
||||||
PP_EXPECT(
|
PP_EXPECT(harness, plan.fallback_reason.empty());
|
||||||
harness,
|
|
||||||
plan.fallback_reason == "document snapshot export does not support this target");
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1382,8 +1381,8 @@ int main()
|
|||||||
"export snapshot route for current platform uses platform policy",
|
"export snapshot route for current platform uses platform policy",
|
||||||
export_snapshot_route_for_current_platform_uses_platform_policy);
|
export_snapshot_route_for_current_platform_uses_platform_policy);
|
||||||
harness.run(
|
harness.run(
|
||||||
"export snapshot route for current platform reports depth fallback",
|
"export snapshot route for current platform supports depth writer",
|
||||||
export_snapshot_route_for_current_platform_reports_depth_fallback);
|
export_snapshot_route_for_current_platform_supports_depth_writer);
|
||||||
harness.run(
|
harness.run(
|
||||||
"export snapshot route falls back for pending renderer payloads",
|
"export snapshot route falls back for pending renderer payloads",
|
||||||
export_snapshot_route_falls_back_for_pending_renderer_payloads);
|
export_snapshot_route_falls_back_for_pending_renderer_payloads);
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ using pp::foundation::StatusCode;
|
|||||||
using pp::paint::BlendMode;
|
using pp::paint::BlendMode;
|
||||||
using pp::paint::Rgba;
|
using pp::paint::Rgba;
|
||||||
using pp::paint::StrokeBlendMode;
|
using pp::paint::StrokeBlendMode;
|
||||||
|
using pp::assets::decode_png_rgba8;
|
||||||
using pp::paint_renderer::CanvasBlendGateRequest;
|
using pp::paint_renderer::CanvasBlendGateRequest;
|
||||||
using pp::paint_renderer::DocumentFaceCompositeRequest;
|
using pp::paint_renderer::DocumentFaceCompositeRequest;
|
||||||
using pp::paint_renderer::DocumentFrameCompositeRequest;
|
using pp::paint_renderer::DocumentFrameCompositeRequest;
|
||||||
@@ -22,6 +23,7 @@ using pp::paint_renderer::StrokeCompositeRequest;
|
|||||||
using pp::paint_renderer::composite_layer;
|
using pp::paint_renderer::composite_layer;
|
||||||
using pp::paint_renderer::composite_document_face;
|
using pp::paint_renderer::composite_document_face;
|
||||||
using pp::paint_renderer::composite_document_frame;
|
using pp::paint_renderer::composite_document_frame;
|
||||||
|
using pp::paint_renderer::export_document_depth_pngs;
|
||||||
using pp::paint_renderer::plan_canvas_blend_gate;
|
using pp::paint_renderer::plan_canvas_blend_gate;
|
||||||
using pp::paint_renderer::plan_canvas_stroke_feedback;
|
using pp::paint_renderer::plan_canvas_stroke_feedback;
|
||||||
using pp::paint_renderer::plan_document_depth_export_render;
|
using pp::paint_renderer::plan_document_depth_export_render;
|
||||||
@@ -1176,6 +1178,126 @@ void plans_document_depth_export_renderer_work(pp::tests::Harness& h)
|
|||||||
PP_EXPECT(h, plan.value().requires_renderer_readback);
|
PP_EXPECT(h, plan.value().requires_renderer_readback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void exports_document_depth_as_png_payloads(pp::tests::Harness& h)
|
||||||
|
{
|
||||||
|
const AnimationFrame root_frames[] {
|
||||||
|
{ .duration_ms = 100, .face_pixels = {} },
|
||||||
|
};
|
||||||
|
const AnimationFrame base_frames[] {
|
||||||
|
{
|
||||||
|
.duration_ms = 100,
|
||||||
|
.face_pixels = {
|
||||||
|
solid_face_payload(0, 1, 1, 255, 0, 0, 255),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const AnimationFrame top_frames[] {
|
||||||
|
{
|
||||||
|
.duration_ms = 100,
|
||||||
|
.face_pixels = {
|
||||||
|
solid_face_payload(0, 1, 1, 0, 255, 0, 255),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const DocumentLayerConfig layers[] {
|
||||||
|
{
|
||||||
|
.name = "Base",
|
||||||
|
.frames = std::span<const AnimationFrame>(base_frames, 1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = "Top",
|
||||||
|
.frames = std::span<const AnimationFrame>(top_frames, 1),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const auto document = CanvasDocument::create_from_snapshot(DocumentSnapshotConfig {
|
||||||
|
.width = 1,
|
||||||
|
.height = 1,
|
||||||
|
.layers = std::span<const DocumentLayerConfig>(layers, 2),
|
||||||
|
.frames = std::span<const AnimationFrame>(root_frames, 1),
|
||||||
|
.selection_masks = {},
|
||||||
|
});
|
||||||
|
PP_EXPECT(h, document);
|
||||||
|
if (!document) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto exported = export_document_depth_pngs(
|
||||||
|
pp::paint_renderer::DocumentDepthExportRenderPlanRequest {
|
||||||
|
.document = &document.value(),
|
||||||
|
.frame_index = 0,
|
||||||
|
.output_extent = Extent2D { .width = 1, .height = 1 },
|
||||||
|
});
|
||||||
|
PP_EXPECT(h, exported);
|
||||||
|
if (!exported) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PP_EXPECT(h, exported.value().output_extent.width == 1U);
|
||||||
|
PP_EXPECT(h, exported.value().output_extent.height == 1U);
|
||||||
|
PP_EXPECT(h, exported.value().image_encoded_bytes > 0U);
|
||||||
|
PP_EXPECT(h, exported.value().depth_encoded_bytes > 0U);
|
||||||
|
PP_EXPECT(h, exported.value().merged_face_draw_count == pp::document::cube_face_count);
|
||||||
|
PP_EXPECT(h, exported.value().layer_depth_draw_count == 2U);
|
||||||
|
PP_EXPECT(h, exported.value().visited_layer_count == 2U);
|
||||||
|
PP_EXPECT(h, exported.value().visible_layer_count == 2U);
|
||||||
|
PP_EXPECT(h, exported.value().face_payload_count == 2U);
|
||||||
|
PP_EXPECT(h, exported.value().uses_perspective_camera);
|
||||||
|
|
||||||
|
const auto image = decode_png_rgba8(exported.value().image_png);
|
||||||
|
const auto depth = decode_png_rgba8(exported.value().depth_png);
|
||||||
|
PP_EXPECT(h, image);
|
||||||
|
PP_EXPECT(h, depth);
|
||||||
|
if (!image || !depth) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PP_EXPECT(h, image.value().width == 1U);
|
||||||
|
PP_EXPECT(h, image.value().height == 1U);
|
||||||
|
PP_EXPECT(h, depth.value().width == 1U);
|
||||||
|
PP_EXPECT(h, depth.value().height == 1U);
|
||||||
|
const std::vector<std::uint8_t> expected_image { 0, 255, 0, 255 };
|
||||||
|
const std::vector<std::uint8_t> expected_depth { 170, 170, 170, 255 };
|
||||||
|
PP_EXPECT(h, image.value().pixels == expected_image);
|
||||||
|
PP_EXPECT(h, depth.value().pixels == expected_depth);
|
||||||
|
}
|
||||||
|
|
||||||
|
void depth_export_payload_boundary_rejects_malformed_face_bytes(pp::tests::Harness& h)
|
||||||
|
{
|
||||||
|
const AnimationFrame root_frames[] {
|
||||||
|
{ .duration_ms = 100, .face_pixels = {} },
|
||||||
|
};
|
||||||
|
const AnimationFrame bad_frames[] {
|
||||||
|
{
|
||||||
|
.duration_ms = 100,
|
||||||
|
.face_pixels = {
|
||||||
|
LayerFacePixels {
|
||||||
|
.face_index = 0,
|
||||||
|
.x = 0,
|
||||||
|
.y = 0,
|
||||||
|
.width = 1,
|
||||||
|
.height = 1,
|
||||||
|
.rgba8 = { 255, 0, 0 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const DocumentLayerConfig layers[] {
|
||||||
|
{
|
||||||
|
.name = "Broken",
|
||||||
|
.frames = std::span<const AnimationFrame>(bad_frames, 1),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const auto document = CanvasDocument::create_from_snapshot(DocumentSnapshotConfig {
|
||||||
|
.width = 1,
|
||||||
|
.height = 1,
|
||||||
|
.layers = std::span<const DocumentLayerConfig>(layers, 1),
|
||||||
|
.frames = std::span<const AnimationFrame>(root_frames, 1),
|
||||||
|
.selection_masks = {},
|
||||||
|
});
|
||||||
|
PP_EXPECT(h, !document.ok());
|
||||||
|
PP_EXPECT(h, document.status().code == StatusCode::invalid_argument);
|
||||||
|
}
|
||||||
|
|
||||||
void document_frame_upload_rejects_invalid_requests(pp::tests::Harness& h)
|
void document_frame_upload_rejects_invalid_requests(pp::tests::Harness& h)
|
||||||
{
|
{
|
||||||
RecordingRenderDevice device;
|
RecordingRenderDevice device;
|
||||||
@@ -1194,6 +1316,8 @@ void document_frame_upload_rejects_invalid_requests(pp::tests::Harness& h)
|
|||||||
pp::paint_renderer::DocumentAnimationFrameEquirectangularPngExportRequest {});
|
pp::paint_renderer::DocumentAnimationFrameEquirectangularPngExportRequest {});
|
||||||
const auto no_document_depth = plan_document_depth_export_render(
|
const auto no_document_depth = plan_document_depth_export_render(
|
||||||
pp::paint_renderer::DocumentDepthExportRenderPlanRequest {});
|
pp::paint_renderer::DocumentDepthExportRenderPlanRequest {});
|
||||||
|
const auto no_document_depth_pngs = export_document_depth_pngs(
|
||||||
|
pp::paint_renderer::DocumentDepthExportRenderPlanRequest {});
|
||||||
|
|
||||||
const AnimationFrame root_frames[] {
|
const AnimationFrame root_frames[] {
|
||||||
{ .duration_ms = 100, .face_pixels = {} },
|
{ .duration_ms = 100, .face_pixels = {} },
|
||||||
@@ -1232,6 +1356,17 @@ void document_frame_upload_rejects_invalid_requests(pp::tests::Harness& h)
|
|||||||
.frame_index = 0,
|
.frame_index = 0,
|
||||||
.output_extent = Extent2D {},
|
.output_extent = Extent2D {},
|
||||||
});
|
});
|
||||||
|
const auto bad_frame_depth_pngs = export_document_depth_pngs(
|
||||||
|
pp::paint_renderer::DocumentDepthExportRenderPlanRequest {
|
||||||
|
.document = &document.value(),
|
||||||
|
.frame_index = 1,
|
||||||
|
});
|
||||||
|
const auto bad_extent_depth_pngs = export_document_depth_pngs(
|
||||||
|
pp::paint_renderer::DocumentDepthExportRenderPlanRequest {
|
||||||
|
.document = &document.value(),
|
||||||
|
.frame_index = 0,
|
||||||
|
.output_extent = Extent2D {},
|
||||||
|
});
|
||||||
const auto bad_jpeg_quality = pp::paint_renderer::export_document_frame_equirectangular_jpeg(
|
const auto bad_jpeg_quality = pp::paint_renderer::export_document_frame_equirectangular_jpeg(
|
||||||
DocumentFrameCompositeRequest {
|
DocumentFrameCompositeRequest {
|
||||||
.document = &document.value(),
|
.document = &document.value(),
|
||||||
@@ -1253,6 +1388,8 @@ void document_frame_upload_rejects_invalid_requests(pp::tests::Harness& h)
|
|||||||
PP_EXPECT(h, no_document_frames.status().code == StatusCode::invalid_argument);
|
PP_EXPECT(h, no_document_frames.status().code == StatusCode::invalid_argument);
|
||||||
PP_EXPECT(h, !no_document_depth.ok());
|
PP_EXPECT(h, !no_document_depth.ok());
|
||||||
PP_EXPECT(h, no_document_depth.status().code == StatusCode::invalid_argument);
|
PP_EXPECT(h, no_document_depth.status().code == StatusCode::invalid_argument);
|
||||||
|
PP_EXPECT(h, !no_document_depth_pngs.ok());
|
||||||
|
PP_EXPECT(h, no_document_depth_pngs.status().code == StatusCode::invalid_argument);
|
||||||
PP_EXPECT(h, !bad_frame.ok());
|
PP_EXPECT(h, !bad_frame.ok());
|
||||||
PP_EXPECT(h, bad_frame.status().code == StatusCode::out_of_range);
|
PP_EXPECT(h, bad_frame.status().code == StatusCode::out_of_range);
|
||||||
PP_EXPECT(h, !bad_frame_readiness.ok());
|
PP_EXPECT(h, !bad_frame_readiness.ok());
|
||||||
@@ -1261,6 +1398,10 @@ void document_frame_upload_rejects_invalid_requests(pp::tests::Harness& h)
|
|||||||
PP_EXPECT(h, bad_frame_depth.status().code == StatusCode::out_of_range);
|
PP_EXPECT(h, bad_frame_depth.status().code == StatusCode::out_of_range);
|
||||||
PP_EXPECT(h, !bad_extent_depth.ok());
|
PP_EXPECT(h, !bad_extent_depth.ok());
|
||||||
PP_EXPECT(h, bad_extent_depth.status().code == StatusCode::invalid_argument);
|
PP_EXPECT(h, bad_extent_depth.status().code == StatusCode::invalid_argument);
|
||||||
|
PP_EXPECT(h, !bad_frame_depth_pngs.ok());
|
||||||
|
PP_EXPECT(h, bad_frame_depth_pngs.status().code == StatusCode::out_of_range);
|
||||||
|
PP_EXPECT(h, !bad_extent_depth_pngs.ok());
|
||||||
|
PP_EXPECT(h, bad_extent_depth_pngs.status().code == StatusCode::invalid_argument);
|
||||||
PP_EXPECT(h, !bad_jpeg_quality.ok());
|
PP_EXPECT(h, !bad_jpeg_quality.ok());
|
||||||
PP_EXPECT(h, bad_jpeg_quality.status().code == StatusCode::out_of_range);
|
PP_EXPECT(h, bad_jpeg_quality.status().code == StatusCode::out_of_range);
|
||||||
PP_EXPECT(h, device.commands().empty());
|
PP_EXPECT(h, device.commands().empty());
|
||||||
@@ -1623,6 +1764,10 @@ int main()
|
|||||||
"exports_document_animation_frames_as_equirectangular_pngs",
|
"exports_document_animation_frames_as_equirectangular_pngs",
|
||||||
exports_document_animation_frames_as_equirectangular_pngs);
|
exports_document_animation_frames_as_equirectangular_pngs);
|
||||||
harness.run("plans_document_depth_export_renderer_work", plans_document_depth_export_renderer_work);
|
harness.run("plans_document_depth_export_renderer_work", plans_document_depth_export_renderer_work);
|
||||||
|
harness.run("exports_document_depth_as_png_payloads", exports_document_depth_as_png_payloads);
|
||||||
|
harness.run(
|
||||||
|
"depth export payload boundary rejects malformed face bytes",
|
||||||
|
depth_export_payload_boundary_rejects_malformed_face_bytes);
|
||||||
harness.run("document_frame_upload_rejects_invalid_requests", document_frame_upload_rejects_invalid_requests);
|
harness.run("document_frame_upload_rejects_invalid_requests", document_frame_upload_rejects_invalid_requests);
|
||||||
harness.run("detects_feedback_requirements", detects_feedback_requirements);
|
harness.run("detects_feedback_requirements", detects_feedback_requirements);
|
||||||
harness.run("plans_stroke_composite_paths", plans_stroke_composite_paths);
|
harness.run("plans_stroke_composite_paths", plans_stroke_composite_paths);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "platform_api/network_tls_policy.h"
|
#include "platform_api/network_tls_policy.h"
|
||||||
#include "platform_api/platform_policy.h"
|
#include "platform_api/platform_policy.h"
|
||||||
#include "platform_api/platform_services.h"
|
#include "platform_api/platform_services.h"
|
||||||
|
#include "platform_apple/apple_platform_services.h"
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
@@ -495,6 +496,56 @@ private:
|
|||||||
std::string clipboard_value_;
|
std::string clipboard_value_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class FakeAppleDocumentPickerBridge final {
|
||||||
|
public:
|
||||||
|
[[nodiscard]] pp::platform::apple::AppleDocumentPickerBridge bridge()
|
||||||
|
{
|
||||||
|
pp::platform::apple::AppleDocumentPickerBridge result;
|
||||||
|
result.pick_image = [this](pp::platform::PickedPathCallback callback) {
|
||||||
|
++pick_image_requests;
|
||||||
|
callback(image_path);
|
||||||
|
};
|
||||||
|
result.pick_file = [this](
|
||||||
|
std::vector<std::string> file_types,
|
||||||
|
pp::platform::PickedPathCallback callback) {
|
||||||
|
++pick_file_requests;
|
||||||
|
picked_file_types = std::move(file_types);
|
||||||
|
callback(file_path);
|
||||||
|
};
|
||||||
|
result.pick_save_file = [this](
|
||||||
|
std::vector<std::string> file_types,
|
||||||
|
pp::platform::PickedPathCallback callback) {
|
||||||
|
++pick_save_file_requests;
|
||||||
|
save_file_types = std::move(file_types);
|
||||||
|
callback(save_path);
|
||||||
|
};
|
||||||
|
result.pick_directory = [this](pp::platform::PickedPathCallback callback) {
|
||||||
|
++pick_directory_requests;
|
||||||
|
callback(directory_path);
|
||||||
|
};
|
||||||
|
result.format_working_directory_path = [this](std::string_view path) {
|
||||||
|
++format_requests;
|
||||||
|
last_format_path.assign(path);
|
||||||
|
return formatted_path;
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pick_image_requests = 0;
|
||||||
|
int pick_file_requests = 0;
|
||||||
|
int pick_save_file_requests = 0;
|
||||||
|
int pick_directory_requests = 0;
|
||||||
|
int format_requests = 0;
|
||||||
|
std::string image_path = "D:/Paint/import.png";
|
||||||
|
std::string file_path = "D:/Paint/demo.ppi";
|
||||||
|
std::string save_path = "D:/Paint/export.ppi";
|
||||||
|
std::string directory_path = "D:/Paint/work";
|
||||||
|
std::string formatted_path = "D:/Paint/Resolved";
|
||||||
|
std::string last_format_path;
|
||||||
|
std::vector<std::string> picked_file_types;
|
||||||
|
std::vector<std::string> save_file_types;
|
||||||
|
};
|
||||||
|
|
||||||
void platform_services_dispatch_clipboard_reads_and_writes(pp::tests::Harness& harness)
|
void platform_services_dispatch_clipboard_reads_and_writes(pp::tests::Harness& harness)
|
||||||
{
|
{
|
||||||
FakePlatformServices fake("#112233");
|
FakePlatformServices fake("#112233");
|
||||||
@@ -776,6 +827,117 @@ void platform_services_dispatch_prepared_file_save(pp::tests::Harness& harness)
|
|||||||
PP_EXPECT(harness, saved);
|
PP_EXPECT(harness, saved);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void apple_document_platform_services_preserve_browse_root_policy(pp::tests::Harness& harness)
|
||||||
|
{
|
||||||
|
pp::platform::apple::AppleDocumentPlatformServices ios_services(pp::platform::PlatformFamily::ios);
|
||||||
|
pp::platform::apple::AppleDocumentPlatformServices macos_services(pp::platform::PlatformFamily::macos);
|
||||||
|
|
||||||
|
const auto ios_roots = ios_services.document_browse_roots("D:/Paint/work", "D:/Paint");
|
||||||
|
const auto macos_roots = macos_services.document_browse_roots("D:/Paint/work", "D:/Paint");
|
||||||
|
|
||||||
|
PP_EXPECT(harness, ios_roots.size() == 2);
|
||||||
|
PP_EXPECT(harness, ios_roots[0] == "D:/Paint/work");
|
||||||
|
PP_EXPECT(harness, ios_roots[1] == "D:/Paint/Inbox");
|
||||||
|
PP_EXPECT(harness, macos_roots.size() == 1);
|
||||||
|
PP_EXPECT(harness, macos_roots[0] == "D:/Paint/work");
|
||||||
|
}
|
||||||
|
|
||||||
|
void apple_document_platform_services_dispatch_ios_picker_callbacks(pp::tests::Harness& harness)
|
||||||
|
{
|
||||||
|
FakeAppleDocumentPickerBridge fake;
|
||||||
|
pp::platform::apple::AppleDocumentPlatformServices services(
|
||||||
|
pp::platform::PlatformFamily::ios,
|
||||||
|
fake.bridge());
|
||||||
|
std::string image_path;
|
||||||
|
std::string file_path;
|
||||||
|
std::string save_path = "unchanged";
|
||||||
|
std::string directory_path = "unchanged";
|
||||||
|
|
||||||
|
services.pick_image([&](std::string path) { image_path = std::move(path); });
|
||||||
|
services.pick_file({ "ppi", "ppbr" }, [&](std::string path) { file_path = std::move(path); });
|
||||||
|
services.pick_save_file({ "ppi" }, [&](std::string path) { save_path = std::move(path); });
|
||||||
|
services.pick_directory([&](std::string path) { directory_path = std::move(path); });
|
||||||
|
|
||||||
|
PP_EXPECT(harness, fake.pick_image_requests == 1);
|
||||||
|
PP_EXPECT(harness, fake.pick_file_requests == 1);
|
||||||
|
PP_EXPECT(harness, fake.pick_save_file_requests == 0);
|
||||||
|
PP_EXPECT(harness, fake.pick_directory_requests == 0);
|
||||||
|
PP_EXPECT(harness, image_path == "D:/Paint/import.png");
|
||||||
|
PP_EXPECT(harness, file_path == "D:/Paint/demo.ppi");
|
||||||
|
PP_EXPECT(harness, fake.picked_file_types.size() == 2);
|
||||||
|
PP_EXPECT(harness, fake.picked_file_types[0] == "ppi");
|
||||||
|
PP_EXPECT(harness, fake.picked_file_types[1] == "ppbr");
|
||||||
|
PP_EXPECT(harness, save_path == "unchanged");
|
||||||
|
PP_EXPECT(harness, directory_path == "unchanged");
|
||||||
|
}
|
||||||
|
|
||||||
|
void apple_document_platform_services_filter_macos_picker_paths(pp::tests::Harness& harness)
|
||||||
|
{
|
||||||
|
FakeAppleDocumentPickerBridge fake;
|
||||||
|
pp::platform::apple::AppleDocumentPlatformServices services(
|
||||||
|
pp::platform::PlatformFamily::macos,
|
||||||
|
fake.bridge());
|
||||||
|
std::string image_path = "unchanged";
|
||||||
|
std::string file_path = "unchanged";
|
||||||
|
std::string save_path = "unchanged";
|
||||||
|
std::string directory_path = "unchanged";
|
||||||
|
|
||||||
|
fake.file_path.clear();
|
||||||
|
services.pick_image([&](std::string path) { image_path = std::move(path); });
|
||||||
|
PP_EXPECT(harness, fake.pick_image_requests == 0);
|
||||||
|
PP_EXPECT(harness, fake.pick_file_requests == 1);
|
||||||
|
PP_EXPECT(harness, fake.picked_file_types.size() == 5);
|
||||||
|
PP_EXPECT(harness, fake.picked_file_types[0] == "png");
|
||||||
|
PP_EXPECT(harness, fake.picked_file_types[1] == "PNG");
|
||||||
|
PP_EXPECT(harness, fake.picked_file_types[2] == "jpg");
|
||||||
|
PP_EXPECT(harness, fake.picked_file_types[3] == "JPG");
|
||||||
|
PP_EXPECT(harness, fake.picked_file_types[4] == "jpeg");
|
||||||
|
PP_EXPECT(harness, image_path == "unchanged");
|
||||||
|
|
||||||
|
fake.file_path = "D:/Paint/demo.ppi";
|
||||||
|
services.pick_file({ "ppi" }, [&](std::string path) { file_path = std::move(path); });
|
||||||
|
PP_EXPECT(harness, fake.pick_file_requests == 2);
|
||||||
|
PP_EXPECT(harness, file_path == "D:/Paint/demo.ppi");
|
||||||
|
|
||||||
|
fake.save_path.clear();
|
||||||
|
services.pick_save_file({ "ppi" }, [&](std::string path) { save_path = std::move(path); });
|
||||||
|
PP_EXPECT(harness, fake.pick_save_file_requests == 1);
|
||||||
|
PP_EXPECT(harness, save_path == "unchanged");
|
||||||
|
|
||||||
|
fake.save_path = "D:/Paint/export.ppi";
|
||||||
|
services.pick_save_file({ "ppi" }, [&](std::string path) { save_path = std::move(path); });
|
||||||
|
PP_EXPECT(harness, fake.pick_save_file_requests == 2);
|
||||||
|
PP_EXPECT(harness, save_path == "D:/Paint/export.ppi");
|
||||||
|
|
||||||
|
fake.directory_path.clear();
|
||||||
|
services.pick_directory([&](std::string path) { directory_path = std::move(path); });
|
||||||
|
PP_EXPECT(harness, fake.pick_directory_requests == 1);
|
||||||
|
PP_EXPECT(harness, directory_path == "unchanged");
|
||||||
|
|
||||||
|
fake.directory_path = "D:/Paint/work";
|
||||||
|
services.pick_directory([&](std::string path) { directory_path = std::move(path); });
|
||||||
|
PP_EXPECT(harness, fake.pick_directory_requests == 2);
|
||||||
|
PP_EXPECT(harness, directory_path == "D:/Paint/work");
|
||||||
|
}
|
||||||
|
|
||||||
|
void apple_document_platform_services_preserve_working_directory_picker_policy(pp::tests::Harness& harness)
|
||||||
|
{
|
||||||
|
FakeAppleDocumentPickerBridge fake;
|
||||||
|
pp::platform::apple::AppleDocumentPlatformServices ios_services(
|
||||||
|
pp::platform::PlatformFamily::ios,
|
||||||
|
fake.bridge());
|
||||||
|
pp::platform::apple::AppleDocumentPlatformServices macos_services(
|
||||||
|
pp::platform::PlatformFamily::macos,
|
||||||
|
fake.bridge());
|
||||||
|
|
||||||
|
PP_EXPECT(harness, !ios_services.supports_working_directory_picker());
|
||||||
|
PP_EXPECT(harness, macos_services.supports_working_directory_picker());
|
||||||
|
PP_EXPECT(harness, ios_services.format_working_directory_path("D:/Paint/.") == "D:/Paint/.");
|
||||||
|
PP_EXPECT(harness, macos_services.format_working_directory_path("D:/Paint/.") == "D:/Paint/Resolved");
|
||||||
|
PP_EXPECT(harness, fake.format_requests == 1);
|
||||||
|
PP_EXPECT(harness, fake.last_format_path == "D:/Paint/.");
|
||||||
|
}
|
||||||
|
|
||||||
void web_platform_services_preserve_default_web_policy(pp::tests::Harness& harness)
|
void web_platform_services_preserve_default_web_policy(pp::tests::Harness& harness)
|
||||||
{
|
{
|
||||||
ScopedInjectedWebPlatformServices scoped(nullptr);
|
ScopedInjectedWebPlatformServices scoped(nullptr);
|
||||||
@@ -1209,6 +1371,18 @@ int main()
|
|||||||
"platform services dispatch working directory picker policy",
|
"platform services dispatch working directory picker policy",
|
||||||
platform_services_dispatch_working_directory_picker_policy);
|
platform_services_dispatch_working_directory_picker_policy);
|
||||||
harness.run("platform services dispatch prepared file save", platform_services_dispatch_prepared_file_save);
|
harness.run("platform services dispatch prepared file save", platform_services_dispatch_prepared_file_save);
|
||||||
|
harness.run(
|
||||||
|
"apple document platform services preserve browse root policy",
|
||||||
|
apple_document_platform_services_preserve_browse_root_policy);
|
||||||
|
harness.run(
|
||||||
|
"apple document platform services dispatch ios picker callbacks",
|
||||||
|
apple_document_platform_services_dispatch_ios_picker_callbacks);
|
||||||
|
harness.run(
|
||||||
|
"apple document platform services filter macos picker paths",
|
||||||
|
apple_document_platform_services_filter_macos_picker_paths);
|
||||||
|
harness.run(
|
||||||
|
"apple document platform services preserve working directory picker policy",
|
||||||
|
apple_document_platform_services_preserve_working_directory_picker_policy);
|
||||||
harness.run("web platform services preserve default web policy", web_platform_services_preserve_default_web_policy);
|
harness.run("web platform services preserve default web policy", web_platform_services_preserve_default_web_policy);
|
||||||
harness.run("web platform services resolve injected services", web_platform_services_resolve_injected_services);
|
harness.run("web platform services resolve injected services", web_platform_services_resolve_injected_services);
|
||||||
harness.run("platform services dispatch writable file target", platform_services_dispatch_writable_file_target);
|
harness.run("platform services dispatch writable file target", platform_services_dispatch_writable_file_target);
|
||||||
|
|||||||
Reference in New Issue
Block a user