diff --git a/docs/modernization/build-inventory.md b/docs/modernization/build-inventory.md index 790902f..de0368d 100644 --- a/docs/modernization/build-inventory.md +++ b/docs/modernization/build-inventory.md @@ -10,7 +10,7 @@ Keep it updated as platform paths move to shared CMake targets. | Platform/Target | Current Entrypoint | Notes | | --- | --- | --- | -| Windows desktop | Root `CMakeLists.txt`, preset `windows-msvc-default`; target preset `windows-vs2026-x64` retained for VS 2026 | Raw `.sln/.vcxproj` files removed on 2026-05-31; local machine currently uses Visual Studio 17 2022; `PanoPainter` now links through `pp_platform_windows` and `panopainter_app`, with Windows/vendor link dependencies owned by the platform shell, runtime payload deployment in `cmake/PanoPainterRuntime.cmake`, tested app-level document-open routing plus open/close/save session decisions owned by `pp_app_core`, SDK-free clipboard/cursor/virtual-keyboard service contracts owned by `pp_platform_api`, retained third-party source dependencies contained by `pp_legacy_vendor`, retained asset/file/serialization sources contained by `pp_legacy_assets_io`, retained paint/document/canvas sources contained by `pp_legacy_paint_document`, retained OpenGL runtime sources contained by `pp_legacy_renderer_gl` and folded into `pp_legacy_engine`, retained runtime shell sources contained by `pp_legacy_engine`, retained base UI controls contained by `pp_legacy_ui_core` and folded into `pp_legacy_app`, app orchestration/version metadata owned by `panopainter_app`, and app-specific modal/dialog/panel/canvas workflow nodes owned by `pp_panopainter_ui` | +| Windows desktop | Root `CMakeLists.txt`, preset `windows-msvc-default`; target preset `windows-vs2026-x64` retained for VS 2026 | Raw `.sln/.vcxproj` files removed on 2026-05-31; local machine currently uses Visual Studio 17 2022; `PanoPainter` now links through `pp_platform_windows` and `panopainter_app`, with Windows/vendor link dependencies owned by the platform shell, runtime payload deployment in `cmake/PanoPainterRuntime.cmake`, tested app-level document-open routing plus open/close/save session decisions owned by `pp_app_core`, SDK-free clipboard/cursor/virtual-keyboard/display/share service contracts owned by `pp_platform_api`, retained third-party source dependencies contained by `pp_legacy_vendor`, retained asset/file/serialization sources contained by `pp_legacy_assets_io`, retained paint/document/canvas sources contained by `pp_legacy_paint_document`, retained OpenGL runtime sources contained by `pp_legacy_renderer_gl` and folded into `pp_legacy_engine`, retained runtime shell sources contained by `pp_legacy_engine`, retained base UI controls contained by `pp_legacy_ui_core` and folded into `pp_legacy_app`, app orchestration/version metadata owned by `panopainter_app`, and app-specific modal/dialog/panel/canvas workflow nodes owned by `pp_panopainter_ui` | | Windows AppX | `PanoPainterPackage/Package.appxmanifest`, `.wapproj` referenced by solution | Distribution packaging | | macOS | `PanoPainter-OSX/` project files and `Info.plist` | Uses `NSOpenGLView` today | | iOS | `PanoPainter/Info.plist`, related Apple sources | Uses OpenGL ES today | @@ -445,9 +445,10 @@ Known local toolchain state: live clipboard get/set requests consume the same contracts before retained platform clipboard bridges. - `pp_platform_api` exposes the SDK-free `PlatformServices` interface for - clipboard text, cursor visibility, and virtual-keyboard visibility; live - app execution now reaches retained platform bridges through the - debt-tracked legacy adapter in `app_events.cpp`. + clipboard text, cursor visibility, virtual-keyboard visibility, external + file display, and file sharing; live app execution now reaches retained + platform bridges through the debt-tracked legacy adapter in + `app_events.cpp`. - `pano_cli plan-cloud-upload` exposes `pp_app_core` cloud upload availability, new-document warning, publish prompt, and save-before-upload planning as JSON; the live cloud upload command consumes the same start contract before @@ -485,8 +486,9 @@ Known local toolchain state: planning before platform cursor callbacks, plus clipboard read/write planning before platform clipboard callbacks. - `pp_platform_api_tests` covers service dispatch for clipboard read/write, - empty clipboard writes, cursor visibility, and virtual-keyboard visibility - without platform SDK headers or a window. + empty clipboard writes, cursor visibility, virtual-keyboard visibility, + external file display, and file sharing without platform SDK headers or a + window. - `pp_app_core_document_cloud_tests` covers cloud upload no-canvas, new-document warning, clean publish prompt, and dirty save-before-upload decisions, plus cloud browse no-canvas/show-browser and selected-download diff --git a/docs/modernization/capability-map.md b/docs/modernization/capability-map.md index 36be5a7..4a1cfa8 100644 --- a/docs/modernization/capability-map.md +++ b/docs/modernization/capability-map.md @@ -67,7 +67,7 @@ and validation command. | --- | --- | --- | --- | | Mouse/keyboard/touch/gestures/cursor | `App`, platform entrypoints | `pp_app_core`, `pp_platform_api`, `pp_platform_*`, app | Cursor visibility decision tests, platform service dispatch tests, synthetic event playback | | Wacom pressure | `WacomTablet` | `pp_platform_windows` | Adapter smoke with fallback | -| Clipboard/file picker/share/display | `App` platform methods | `pp_app_core`, `pp_platform_api`, `pp_platform_*` | Clipboard read/write, share saved-path, picked-path, and display-file decision tests, platform service dispatch tests, platform smoke or mocked service | +| Clipboard/file picker/share/display | `App` platform methods | `pp_app_core`, `pp_platform_api`, `pp_platform_*` | Clipboard read/write, share saved-path, picked-path, and display-file decision tests, platform service display/share dispatch tests, picker callback tests, platform smoke or mocked service | | Virtual keyboard | `App`, platform entrypoints | `pp_app_core`, `pp_platform_api`, `pp_platform_*` | Keyboard visibility decision tests, platform service dispatch tests, platform smoke | | OpenVR desktop | `HMD`, `Vive`, `app_vr` | `pp_platform_vr`, app | Compile gate and mocked pose tests | | Quest/OVR | Android Quest files | `pp_platform_android_quest` | Compile/package gate | diff --git a/docs/modernization/debt.md b/docs/modernization/debt.md index b71b433..3d3c970 100644 --- a/docs/modernization/debt.md +++ b/docs/modernization/debt.md @@ -35,7 +35,7 @@ agent or engineer to remove them without reconstructing context from chat. | DEBT-0014 | Open | Modernization | `windows-clangcl-asan` now configures as a headless Ninja/clang-cl preset and uses the release MSVC runtime required by ASan, but local builds still fail because installed clang-cl 18.1.8 is paired with VS 2026-preview STL headers that require Clang 20 or newer | Sanitizer validation should be local and repeatable, but this machine's compiler/header pairing is incompatible | `cmake --fresh --preset windows-clangcl-asan`; `cmake --build --preset windows-clangcl-asan --target pp_foundation` | Install/use Clang 20+ with the VS 2026 STL, or point the preset at a compatible VS 2022 toolchain, then make `platform-build.ps1 -Presets windows-clangcl-asan` pass for the headless matrix | | DEBT-0015 | Open | Modernization | Cursor visibility requests now consume pure `pp_app_core` planning through `pano_cli plan-cursor-visibility`, but live cursor execution still reaches retained Win32/macOS platform bridges from `App::show_cursor` and `App::hide_cursor` | Keep canvas cursor behavior stable while platform shells are extracted incrementally | `pp_app_core_document_platform_io_tests`; `pano_cli plan-cursor-visibility --visible`; `ctest --preset desktop-fast --build-config Debug` | Cursor visibility execution is owned by `pp_platform_*` services and live app code depends on an injected platform interface instead of direct singleton/platform calls | | DEBT-0016 | Open | Modernization | Clipboard get/set requests now consume pure `pp_app_core` planning through `pano_cli plan-clipboard-read` and `pano_cli plan-clipboard-write`, but live clipboard execution still reaches retained Win32/Apple/Android platform bridges from `App::clipboard_get_text` and `App::clipboard_set_text` | Keep picker/color text clipboard behavior stable while platform shells are extracted incrementally | `pp_app_core_document_platform_io_tests`; `pano_cli plan-clipboard-write --text #ff00aa`; `ctest --preset desktop-fast --build-config Debug` | Clipboard execution is owned by `pp_platform_*` services and live app code depends on an injected platform interface instead of direct singleton/platform calls | -| DEBT-0017 | Open | Modernization | `App::clipboard_get_text`, `App::clipboard_set_text`, `App::show_cursor`, `App::hide_cursor`, `App::showKeyboard`, and `App::hideKeyboard` now call the SDK-free `pp::platform::PlatformServices` interface, but the live implementation is still a legacy adapter in `app_events.cpp` that forwards to retained Win32/Apple/Android bridge functions | Preserve behavior while moving platform execution behind a testable service boundary before platform shell implementations are injected | `pp_platform_api_tests`; `pp_app_core_document_platform_io_tests`; `ctest --preset desktop-fast --build-config Debug`; `powershell -ExecutionPolicy Bypass -File scripts\automation\package-smoke.ps1 -Preset windows-msvc-default -Configuration Debug` | Replace the `app_events.cpp` legacy adapter with injected `pp_platform_*` service implementations owned by each platform shell | +| DEBT-0017 | Open | Modernization | `App::clipboard_get_text`, `App::clipboard_set_text`, `App::show_cursor`, `App::hide_cursor`, `App::showKeyboard`, `App::hideKeyboard`, `App::display_file`, and `App::share_file` now call the SDK-free `pp::platform::PlatformServices` interface, but the live implementation is still a legacy adapter in `app_events.cpp` that forwards to retained Win32/Apple/Android bridge functions and retained no-op branches | Preserve behavior while moving platform execution behind a testable service boundary before platform shell implementations are injected | `pp_platform_api_tests`; `pp_app_core_document_platform_io_tests`; `ctest --preset desktop-fast --build-config Debug`; `powershell -ExecutionPolicy Bypass -File scripts\automation\package-smoke.ps1 -Preset windows-msvc-default -Configuration Debug` | Replace the `app_events.cpp` legacy adapter with injected `pp_platform_*` service implementations owned by each platform shell | ## Closed Debt diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index cf3ea73..95d0b62 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -469,10 +469,11 @@ cursor bridges continue. app-core clipboard text decisions used by live clipboard get/set requests before retained platform clipboard bridges continue. `pp_platform_api` now owns a headless `PlatformServices` interface for -clipboard text, cursor visibility, and virtual-keyboard visibility. Live app -clipboard/cursor/keyboard execution routes through a debt-tracked legacy -adapter in `app_events.cpp`, so behavior is preserved while later platform -shell implementations can replace direct bridge calls. +clipboard text, cursor visibility, virtual-keyboard visibility, external file +display, and file sharing. Live app clipboard/cursor/keyboard/display/share +execution routes through a debt-tracked legacy adapter in `app_events.cpp`, so +behavior is preserved while later platform shell implementations can replace +direct bridge calls. `pano_cli plan-cloud-upload` exposes the app-core cloud upload decision used by the live cloud upload command for missing-canvas, new-document warning, publish prompt, and dirty-document save-before-upload states before legacy UI, canvas, @@ -985,9 +986,10 @@ Results: clipboard decisions as JSON, including empty write text. - `pp_platform_api_tests` passed, covering the SDK-free `PlatformServices` interface for clipboard read/write, empty clipboard writes, cursor - visibility dispatch, and virtual-keyboard visibility dispatch. The live app - now consumes this interface through the legacy platform adapter for - clipboard/cursor/keyboard execution. + visibility dispatch, virtual-keyboard visibility dispatch, external file + display dispatch, and file sharing dispatch. The live app now consumes this + interface through the legacy platform adapter for + clipboard/cursor/keyboard/display/share execution. - `panopainter_validate_shaders` passed, validating 25 shader programs and 7 shader includes for stage markers and include graph integrity. - `pp_renderer_gl_capabilities_tests` passed on default MSVC, vcpkg-headless, diff --git a/src/app_events.cpp b/src/app_events.cpp index 6e026ff..b9d887d 100644 --- a/src/app_events.cpp +++ b/src/app_events.cpp @@ -120,6 +120,36 @@ public: displayKeyboard(visible); #else (void)visible; +#endif + } + + void display_file(std::string_view path) override + { + const std::string value(path); +#ifdef __IOS__ + dispatch_async(dispatch_get_main_queue(), ^{ + [App::I->ios_view display_file:value]; + }); +#elif __OSX__ + [[NSWorkspace sharedWorkspace] openFile:[NSString stringWithUTF8String:value.c_str()]]; +#else + (void)value; +#endif + } + + void share_file(std::string_view path) override + { + const std::string value(path); +#ifdef __IOS__ + dispatch_async(dispatch_get_main_queue(), ^{ + [App::I->ios_view share_file:[NSString stringWithUTF8String:value.c_str()]]; + }); +#elif __OSX__ + dispatch_async(dispatch_get_main_queue(), ^{ + [App::I->osx_view share_file:[NSString stringWithUTF8String:value.c_str()]]; + }); +#else + (void)value; #endif } }; @@ -398,24 +428,7 @@ void App::display_file(std::string path) if (pp::app::plan_display_file(path) == pp::app::DisplayFileAction::ignore_empty_path) return; -#ifdef __IOS__ - dispatch_async(dispatch_get_main_queue(), ^{ - [ios_view display_file:path]; - }); -#elif __OSX__ - [[NSWorkspace sharedWorkspace] openFile:[NSString stringWithUTF8String:path.c_str()]]; -// dispatch_async(dispatch_get_main_queue(), ^{ -// std::string path = [osx_view pick_file]; -// if (!path.empty()) -// callback(path); -// }); -#elif __ANDROID__ - //displayKeyboard(and_app, false); -#elif _WIN32 -// std::string path = win32_open_file(); -// if (!path.empty()) -// callback(path); -#endif + legacy_platform_services().display_file(path); } void App::share_file(std::string path) @@ -426,18 +439,7 @@ void App::share_file(std::string path) message_box("Sharing failed", "Please save the document before sharing it."); return; } -#ifdef __IOS__ - dispatch_async(dispatch_get_main_queue(), ^{ - [ios_view share_file:[NSString stringWithUTF8String:path.c_str()]]; - }); -#elif __OSX__ - dispatch_async(dispatch_get_main_queue(), ^{ - [osx_view share_file:[NSString stringWithUTF8String:path.c_str()]]; - }); -#elif __ANDROID__ -#elif _WIN32 - // not implemented -#endif + legacy_platform_services().share_file(path); } bool App::mouse_down(int button, float x, float y, float pressure, kEventSource source, bool eraser) diff --git a/src/platform_api/platform_services.h b/src/platform_api/platform_services.h index 2ff1346..3a642dc 100644 --- a/src/platform_api/platform_services.h +++ b/src/platform_api/platform_services.h @@ -13,6 +13,8 @@ public: [[nodiscard]] virtual bool set_clipboard_text(std::string_view text) = 0; virtual void set_cursor_visible(bool visible) = 0; virtual void set_virtual_keyboard_visible(bool visible) = 0; + virtual void display_file(std::string_view path) = 0; + virtual void share_file(std::string_view path) = 0; }; } diff --git a/tests/platform_api/platform_services_tests.cpp b/tests/platform_api/platform_services_tests.cpp index 00b2352..f4fdb78 100644 --- a/tests/platform_api/platform_services_tests.cpp +++ b/tests/platform_api/platform_services_tests.cpp @@ -39,12 +39,28 @@ public: keyboard_visible = visible; } + void display_file(std::string_view path) override + { + ++display_file_requests; + displayed_path.assign(path); + } + + void share_file(std::string_view path) override + { + ++share_file_requests; + shared_path.assign(path); + } + int clipboard_reads = 0; int clipboard_writes = 0; int cursor_updates = 0; int keyboard_updates = 0; + int display_file_requests = 0; + int share_file_requests = 0; bool cursor_visible = false; bool keyboard_visible = false; + std::string displayed_path; + std::string shared_path; private: std::string clipboard_value_; @@ -88,6 +104,20 @@ void platform_services_dispatch_visibility_updates(pp::tests::Harness& harness) PP_EXPECT(harness, !fake.keyboard_visible); } +void platform_services_dispatch_file_actions(pp::tests::Harness& harness) +{ + FakePlatformServices fake("unused"); + pp::platform::PlatformServices& services = fake; + + services.display_file("D:/Paint/export.png"); + services.share_file("D:/Paint/demo.ppi"); + + PP_EXPECT(harness, fake.display_file_requests == 1); + PP_EXPECT(harness, fake.share_file_requests == 1); + PP_EXPECT(harness, fake.displayed_path == "D:/Paint/export.png"); + PP_EXPECT(harness, fake.shared_path == "D:/Paint/demo.ppi"); +} + } int main() @@ -96,5 +126,6 @@ int main() harness.run("platform services dispatch clipboard reads and writes", platform_services_dispatch_clipboard_reads_and_writes); harness.run("platform services preserve empty clipboard writes", platform_services_preserve_empty_clipboard_writes); harness.run("platform services dispatch visibility updates", platform_services_dispatch_visibility_updates); + harness.run("platform services dispatch file actions", platform_services_dispatch_file_actions); return harness.finish(); }