diff --git a/docs/modernization/build-inventory.md b/docs/modernization/build-inventory.md index 90e8103..5e0756c 100644 --- a/docs/modernization/build-inventory.md +++ b/docs/modernization/build-inventory.md @@ -543,8 +543,8 @@ 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, live asset/layout reload policy, diagnostic stacktrace/crash hooks, - prepared-file save/download handoff; + flushing, document browse roots, 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 8def995..467f5ea 100644 --- a/docs/modernization/debt.md +++ b/docs/modernization/debt.md @@ -68,6 +68,7 @@ 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-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 | ## Closed Debt diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index e8df039..1ab028d 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -643,6 +643,9 @@ Canvas image export publishing and explicit persistent-storage flushes now dispatch through `PlatformServices` too, preserving iOS photo-library export publication and WebGL filesystem sync behavior in the legacy adapter while 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`. `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. @@ -1684,7 +1687,8 @@ Results: lifecycle dispatch, render-target binding dispatch, render platform hint dispatch, render debug callback dispatch, render-capture frame hook dispatch, recording cleanup dispatch, exported-image publish dispatch, persistent - storage flush dispatch, live asset/layout reload policy dispatch, + storage flush dispatch, document browse-root 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 bef9f39..236692e 100644 --- a/src/app.h +++ b/src/app.h @@ -206,6 +206,7 @@ public: void clear_platform_recorded_files(std::string path); void publish_exported_image(std::string path); void flush_platform_storage(); + [[nodiscard]] std::vector document_browse_roots() const; [[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 c3839ef..18c9f15 100644 --- a/src/app_dialogs.cpp +++ b/src/app_dialogs.cpp @@ -238,11 +238,7 @@ void App::dialog_browse() // load thumbnail test auto dialog = std::make_shared(); dialog->set_manager(&layout); -#ifdef __IOS__ - dialog->search_paths = {work_path, data_path + "/Inbox"}; -#else - dialog->search_paths = {work_path}; -#endif + dialog->search_paths = document_browse_roots(); dialog->init(); dialog->create(); dialog->loaded(); diff --git a/src/app_events.cpp b/src/app_events.cpp index 205c770..e0f975c 100644 --- a/src/app_events.cpp +++ b/src/app_events.cpp @@ -287,6 +287,11 @@ void App::flush_platform_storage() active_platform_services().flush_persistent_storage(); } +std::vector App::document_browse_roots() const +{ + return active_platform_services().document_browse_roots(work_path, data_path); +} + bool App::platform_enables_live_asset_reloading() { return active_platform_services().enables_live_asset_reloading(); diff --git a/src/platform_api/platform_services.h b/src/platform_api/platform_services.h index afb7fe5..079e6c5 100644 --- a/src/platform_api/platform_services.h +++ b/src/platform_api/platform_services.h @@ -43,6 +43,9 @@ public: virtual void clear_recorded_files(std::string_view recording_path) = 0; virtual void publish_exported_image(std::string_view path) = 0; virtual void flush_persistent_storage() = 0; + [[nodiscard]] virtual std::vector document_browse_roots( + std::string_view work_path, + std::string_view data_path) = 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 09afc9d..2dfef4e 100644 --- a/src/platform_legacy/legacy_platform_services.cpp +++ b/src/platform_legacy/legacy_platform_services.cpp @@ -298,6 +298,21 @@ public: #endif } + [[nodiscard]] std::vector document_browse_roots( + std::string_view work_path, + std::string_view data_path) override + { +#ifdef __IOS__ + return { + std::string(work_path), + std::string(data_path) + "/Inbox", + }; +#else + (void)data_path; + return { std::string(work_path) }; +#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 415ef64..0859407 100644 --- a/src/platform_windows/windows_platform_services.cpp +++ b/src/platform_windows/windows_platform_services.cpp @@ -378,6 +378,14 @@ public: { } + [[nodiscard]] std::vector document_browse_roots( + std::string_view work_path, + std::string_view data_path) override + { + (void)data_path; + return { std::string(work_path) }; + } + [[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 9d35ff1..ccea12b 100644 --- a/tests/platform_api/platform_services_tests.cpp +++ b/tests/platform_api/platform_services_tests.cpp @@ -134,6 +134,16 @@ public: ++persistent_storage_flushes; } + [[nodiscard]] std::vector document_browse_roots( + std::string_view work_path, + std::string_view data_path) override + { + ++document_browse_root_requests; + browse_work_path.assign(work_path); + browse_data_path.assign(data_path); + return browse_roots; + } + [[nodiscard]] bool enables_live_asset_reloading() override { ++live_asset_reload_policy_checks; @@ -228,6 +238,7 @@ public: int recording_clears = 0; int exported_image_publishes = 0; int persistent_storage_flushes = 0; + int document_browse_root_requests = 0; int live_asset_reload_policy_checks = 0; int platform_frame_updates = 0; int frame_reports = 0; @@ -252,6 +263,8 @@ public: std::string prepared_file_name; std::string cleared_recording_path; std::string exported_image_path; + std::string browse_work_path; + std::string browse_data_path; std::string picker_path = "D:/Paint/import.png"; std::string save_path = "D:/Paint/export.ppi"; std::string directory_path = "D:/Paint"; @@ -263,6 +276,10 @@ public: }; std::vector picked_file_types; std::vector save_file_types; + std::vector browse_roots = { + "D:/Paint/work", + "D:/Paint/Inbox", + }; private: std::string clipboard_value_; @@ -404,6 +421,21 @@ void platform_services_dispatch_exported_image_and_storage_flush(pp::tests::Harn PP_EXPECT(harness, fake.persistent_storage_flushes == 1); } +void platform_services_dispatch_document_browse_roots(pp::tests::Harness& harness) +{ + FakePlatformServices fake("unused"); + pp::platform::PlatformServices& services = fake; + + const auto roots = services.document_browse_roots("D:/Paint/work", "D:/Paint"); + + PP_EXPECT(harness, fake.document_browse_root_requests == 1); + PP_EXPECT(harness, fake.browse_work_path == "D:/Paint/work"); + PP_EXPECT(harness, fake.browse_data_path == "D:/Paint"); + PP_EXPECT(harness, roots.size() == 2); + PP_EXPECT(harness, roots[0] == "D:/Paint/work"); + PP_EXPECT(harness, roots[1] == "D:/Paint/Inbox"); +} + void platform_services_dispatch_live_asset_reload_policy(pp::tests::Harness& harness) { FakePlatformServices fake("unused"); @@ -509,6 +541,7 @@ int main() harness.run("platform services dispatch render capture hooks", platform_services_dispatch_render_capture_hooks); 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 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);