From dc369c89b0a8b7411ab9a88a2b1cf46341368ae9 Mon Sep 17 00:00:00 2001 From: omigamedev Date: Thu, 4 Jun 2026 16:48:57 +0200 Subject: [PATCH] Route UI state save through platform services --- docs/modernization/build-inventory.md | 5 +++-- docs/modernization/debt.md | 1 + docs/modernization/roadmap.md | 6 +++++- src/app.h | 1 + src/app_dialogs.cpp | 2 -- src/app_events.cpp | 5 +++++ src/app_layout.cpp | 8 +------- src/platform_api/platform_services.h | 1 + .../legacy_platform_services.cpp | 7 +++++++ .../windows_platform_services.cpp | 6 ++++++ tests/platform_api/platform_services_tests.cpp | 17 +++++++++++++++++ 11 files changed, 47 insertions(+), 12 deletions(-) diff --git a/docs/modernization/build-inventory.md b/docs/modernization/build-inventory.md index 5e0756c..8daf776 100644 --- a/docs/modernization/build-inventory.md +++ b/docs/modernization/build-inventory.md @@ -543,8 +543,9 @@ Known local toolchain state: render-target binding hooks, render platform hint hooks, render-capture frame hooks, render debug callback hooks, per-frame platform hooks, picker callbacks, recording cleanup, exported-image publishing, persistent storage - flushing, document browse roots, live asset/layout reload policy, diagnostic - stacktrace/crash hooks, prepared-file save/download handoff; + flushing, document browse roots, native UI/window state saving, live + asset/layout reload policy, diagnostic stacktrace/crash hooks, prepared-file + save/download handoff; Windows live app execution now uses injected `WindowsPlatformServices` from diff --git a/docs/modernization/debt.md b/docs/modernization/debt.md index 467f5ea..a439a0c 100644 --- a/docs/modernization/debt.md +++ b/docs/modernization/debt.md @@ -69,6 +69,7 @@ agent or engineer to remove them without reconstructing context from chat. | 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 `PlatformServices`, but non-Windows execution still lives in `src/platform_legacy/legacy_platform_services.*` 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 now dispatch through `PlatformServices`, but iOS `Inbox` inclusion still lives in `src/platform_legacy/legacy_platform_services.*` | Preserve current iOS document import/browse 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 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`, but macOS execution still lives in `src/platform_legacy/legacy_platform_services.*` and forwards to the retained Objective-C app bridge | 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 | ## Closed Debt diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index 1ab028d..6a6eaca 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -646,6 +646,10 @@ removing those direct platform calls from `Canvas` and brush preset storage. Document-browser search root selection now dispatches through `PlatformServices`, preserving the iOS `Inbox` root in the legacy adapter while removing the iOS-specific branch from `App::dialog_browse`. +Native UI/window state saving now dispatches through `PlatformServices`, +preserving Windows window placement persistence in `WindowsPlatformServices` +and macOS UI state persistence in the legacy adapter while removing platform +guards from `App::ui_save`. `App::show_cursor`, `App::hide_cursor`, `App::showKeyboard`, and `App::hideKeyboard` now dispatch through the active service without local platform guards; unsupported platforms rely on their service no-op behavior. @@ -1688,7 +1692,7 @@ Results: dispatch, render debug callback dispatch, render-capture frame hook dispatch, recording cleanup dispatch, exported-image publish dispatch, persistent storage flush dispatch, document browse-root dispatch, - live asset/layout reload policy dispatch, + native UI/window state save dispatch, live asset/layout reload policy dispatch, diagnostic hook dispatch, per-frame platform hook dispatch, picker callback dispatch, and prepared-file save/download callback dispatch. The live Windows app now diff --git a/src/app.h b/src/app.h index 236692e..f4a68e4 100644 --- a/src/app.h +++ b/src/app.h @@ -207,6 +207,7 @@ public: void publish_exported_image(std::string path); void flush_platform_storage(); [[nodiscard]] std::vector document_browse_roots() const; + void save_platform_ui_state(); [[nodiscard]] bool platform_enables_live_asset_reloading(); void update_platform_frame(float delta_time_seconds); void report_rendered_frames(int frames); diff --git a/src/app_dialogs.cpp b/src/app_dialogs.cpp index 18c9f15..a12f67d 100644 --- a/src/app_dialogs.cpp +++ b/src/app_dialogs.cpp @@ -29,8 +29,6 @@ #ifdef __QUEST__ #include "oculus_vr.h" -#elif __WEB__ -void webgl_pick_file(std::function callback); #endif namespace { diff --git a/src/app_events.cpp b/src/app_events.cpp index e0f975c..720e8e4 100644 --- a/src/app_events.cpp +++ b/src/app_events.cpp @@ -292,6 +292,11 @@ std::vector App::document_browse_roots() const return active_platform_services().document_browse_roots(work_path, data_path); } +void App::save_platform_ui_state() +{ + active_platform_services().save_ui_state(); +} + bool App::platform_enables_live_asset_reloading() { return active_platform_services().enables_live_asset_reloading(); diff --git a/src/app_layout.cpp b/src/app_layout.cpp index 589bca7..18c3742 100644 --- a/src/app_layout.cpp +++ b/src/app_layout.cpp @@ -1679,13 +1679,7 @@ void App::ui_save() Settings::set("ui", d); Settings::set("ui-rtl", Serializer::Boolean(ui_rtl)); - -#if _WIN32 - extern void win32_save_window_state(); - win32_save_window_state(); -#elif __OSX__ - [osx_app save_ui_state]; -#endif + save_platform_ui_state(); Settings::save(); } diff --git a/src/platform_api/platform_services.h b/src/platform_api/platform_services.h index 079e6c5..fa9130f 100644 --- a/src/platform_api/platform_services.h +++ b/src/platform_api/platform_services.h @@ -46,6 +46,7 @@ public: [[nodiscard]] virtual std::vector document_browse_roots( std::string_view work_path, std::string_view data_path) = 0; + virtual void save_ui_state() = 0; [[nodiscard]] virtual bool enables_live_asset_reloading() = 0; virtual void update_platform_frame(float delta_time_seconds) = 0; virtual void report_rendered_frames(int frames) = 0; diff --git a/src/platform_legacy/legacy_platform_services.cpp b/src/platform_legacy/legacy_platform_services.cpp index 2dfef4e..2237137 100644 --- a/src/platform_legacy/legacy_platform_services.cpp +++ b/src/platform_legacy/legacy_platform_services.cpp @@ -313,6 +313,13 @@ public: #endif } + void save_ui_state() override + { +#ifdef __OSX__ + [App::I->osx_app save_ui_state]; +#endif + } + [[nodiscard]] bool enables_live_asset_reloading() override { #if defined(__OSX__) diff --git a/src/platform_windows/windows_platform_services.cpp b/src/platform_windows/windows_platform_services.cpp index 0859407..5fe320d 100644 --- a/src/platform_windows/windows_platform_services.cpp +++ b/src/platform_windows/windows_platform_services.cpp @@ -19,6 +19,7 @@ void win32_renderdoc_frame_start(); void win32_renderdoc_frame_end(); void win32_update_fps(int frames); void win32_update_stylus(float dt); +void win32_save_window_state(); namespace { @@ -386,6 +387,11 @@ public: return { std::string(work_path) }; } + void save_ui_state() override + { + win32_save_window_state(); + } + [[nodiscard]] bool enables_live_asset_reloading() override { return true; diff --git a/tests/platform_api/platform_services_tests.cpp b/tests/platform_api/platform_services_tests.cpp index ccea12b..e3cec2d 100644 --- a/tests/platform_api/platform_services_tests.cpp +++ b/tests/platform_api/platform_services_tests.cpp @@ -144,6 +144,11 @@ public: return browse_roots; } + void save_ui_state() override + { + ++ui_state_saves; + } + [[nodiscard]] bool enables_live_asset_reloading() override { ++live_asset_reload_policy_checks; @@ -239,6 +244,7 @@ public: int exported_image_publishes = 0; int persistent_storage_flushes = 0; int document_browse_root_requests = 0; + int ui_state_saves = 0; int live_asset_reload_policy_checks = 0; int platform_frame_updates = 0; int frame_reports = 0; @@ -436,6 +442,16 @@ void platform_services_dispatch_document_browse_roots(pp::tests::Harness& harnes PP_EXPECT(harness, roots[1] == "D:/Paint/Inbox"); } +void platform_services_dispatch_ui_state_save(pp::tests::Harness& harness) +{ + FakePlatformServices fake("unused"); + pp::platform::PlatformServices& services = fake; + + services.save_ui_state(); + + PP_EXPECT(harness, fake.ui_state_saves == 1); +} + void platform_services_dispatch_live_asset_reload_policy(pp::tests::Harness& harness) { FakePlatformServices fake("unused"); @@ -542,6 +558,7 @@ int main() harness.run("platform services dispatch recording cleanup", platform_services_dispatch_recording_cleanup); harness.run("platform services dispatch exported image and storage flush", platform_services_dispatch_exported_image_and_storage_flush); harness.run("platform services dispatch document browse roots", platform_services_dispatch_document_browse_roots); + harness.run("platform services dispatch UI state save", platform_services_dispatch_ui_state_save); harness.run("platform services dispatch live asset reload policy", platform_services_dispatch_live_asset_reload_policy); harness.run("platform services dispatch frame hooks", platform_services_dispatch_frame_hooks); harness.run("platform services dispatch file actions", platform_services_dispatch_file_actions);