Route collection export targets through platform policy
This commit is contained in:
@@ -556,7 +556,10 @@ Known local toolchain state:
|
||||
asset/layout reload policy, diagnostic stacktrace/crash hooks,
|
||||
prepared-file writable target selection, and prepared-file save/download
|
||||
handoff; PPBR and MP4 export dialogs consume the same prepared-file policy
|
||||
at runtime instead of spelling mobile/Web branches locally;
|
||||
at runtime instead of spelling mobile/Web branches locally, and layer/frame
|
||||
collection export dialogs consume the work-directory collection policy before
|
||||
`pp_app_core` plans immediate collection export versus directory-picker stem
|
||||
export;
|
||||
Windows
|
||||
live app execution now uses injected
|
||||
`WindowsPlatformServices` from
|
||||
@@ -605,11 +608,12 @@ Known local toolchain state:
|
||||
and malformed paths before the live `App::open_document` performs UI or
|
||||
legacy canvas work.
|
||||
- `pp_app_core_document_export_tests` covers export file targets, collection
|
||||
directory/stem targets, picked-directory stems, MP4 suggested names, and
|
||||
invalid export naming inputs, plus export-start license/canvas availability
|
||||
decisions, export menu executor dispatch, file/stem/collection export
|
||||
execution dispatch, failed directory creation preservation, named depth/cube
|
||||
export dispatch, malformed export target rejection, video export dispatch for
|
||||
directory/stem targets, picked-directory stems, work-directory versus
|
||||
picker-stem collection target planning, MP4 suggested names, and invalid
|
||||
export naming inputs, plus export-start license/canvas availability decisions,
|
||||
export menu executor dispatch, file/stem/collection export execution
|
||||
dispatch, failed directory creation preservation, named depth/cube export
|
||||
dispatch, malformed export target rejection, video export dispatch for
|
||||
animation MP4/timelapse paths, and empty video-path rejection.
|
||||
- `pp_app_core_document_recording_tests` covers recording start/stop, clear,
|
||||
platform recorded-file cleanup, frame-count reset, export progress totals,
|
||||
|
||||
@@ -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`, `App::show_cursor`/`App::hide_cursor` dispatch through `PlatformServices` without platform guards, and Windows live execution uses injected `WindowsPlatformServices`, but macOS cursor execution still reaches the retained fallback adapter | 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 injected `pp_platform_*` services for every supported platform |
|
||||
| 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`, and Windows live execution uses injected `WindowsPlatformServices`, but Apple/Android clipboard execution still reaches retained fallback adapter branches 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 injected `pp_platform_*` services for every supported platform |
|
||||
| DEBT-0017 | Open | Modernization | Startup storage path preparation, `App::clipboard_get_text`, `App::clipboard_set_text`, `App::show_cursor`, `App::hide_cursor`, `App::showKeyboard`, `App::hideKeyboard`, `App::display_file`, `App::share_file`, native app/window close, UI-thread lifecycle hooks, render-context acquire/release/present hooks, render-target binding hooks, render platform hint hooks, render debug callback hooks, render-capture frame hooks, recording cleanup, live asset/layout reload policy, diagnostic stacktrace/crash hooks, per-frame platform hooks, `App::pick_image`, `App::pick_file`, the non-writer `App::pick_file_save`, `App::pick_dir`, and prepared-file save/download handoff now call the SDK-free `pp::platform::PlatformServices` interface, and Windows injects `WindowsPlatformServices` from `src/platform_windows/windows_platform_services.*`; non-Windows live implementations still use `src/platform_legacy/legacy_platform_services.*`, a named fallback adapter that forwards to retained Apple/Android/Linux/Web 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 `src/platform_legacy/legacy_platform_services.*` with injected `pp_platform_*` service implementations owned by each non-Windows platform shell |
|
||||
| DEBT-0017 | Open | Modernization | Startup storage path preparation, `App::clipboard_get_text`, `App::clipboard_set_text`, `App::show_cursor`, `App::hide_cursor`, `App::showKeyboard`, `App::hideKeyboard`, `App::display_file`, `App::share_file`, native app/window close, UI-thread lifecycle hooks, render-context acquire/release/present hooks, render-target binding hooks, render platform hint hooks, render debug callback hooks, render-capture frame hooks, recording cleanup, live asset/layout reload policy, diagnostic stacktrace/crash hooks, per-frame platform hooks, `App::pick_image`, `App::pick_file`, the non-writer `App::pick_file_save`, `App::pick_dir`, prepared-file save/download handoff, and work-directory document export collection policy now call the SDK-free `pp::platform::PlatformServices` interface, and Windows injects `WindowsPlatformServices` from `src/platform_windows/windows_platform_services.*`; non-Windows live implementations still use `src/platform_legacy/legacy_platform_services.*`, a named fallback adapter that forwards to retained Apple/Android/Linux/Web 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_export_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 `src/platform_legacy/legacy_platform_services.*` with injected `pp_platform_*` service implementations owned by each non-Windows platform shell |
|
||||
| DEBT-0019 | Open | Modernization | Unreferenced-parameter warnings are muted globally through `pp_project_warnings` with MSVC `/wd4100` and Clang/GCC `-Wno-unused-parameter` | Legacy callbacks, virtual hooks, serializer methods, and platform/API compatibility functions carry many intentionally unused parameters during the component split; muting this keeps stricter warning builds focused on higher-signal migration issues | `cmake --build --preset windows-msvc-default --config Debug --target PanoPainter`; `ctest --preset desktop-fast --build-config Debug`; `cmake --build --preset linux-clang --target pp_foundation` | Remove `/wd4100` and `-Wno-unused-parameter`, mark intentionally unused parameters with names/comments or `[[maybe_unused]]`, and make the Windows app plus headless Clang/GCC tests pass without unreferenced-parameter warnings |
|
||||
| DEBT-0020 | Open | Modernization | Document resize dialog state, selected-resolution planning, and execution dispatch now consume pure `pp_app_core` through `NodeDialogResize`, `App::dialog_resize`, `pano_cli plan-document-resize`, and the `DocumentResizeServices` boundary, and live resize shares `src/legacy_document_canvas_services.*` with canvas clear commands, but the shared live bridge still calls legacy `Canvas::resize`, updates the legacy app title, and clears legacy `ActionManager` history through the history bridge | Preserve existing layer/frame GPU resize behavior while the document model and canvas execution boundary are extracted incrementally | `pp_app_core_document_resize_tests`; `pano_cli plan-document-resize --current-resolution 2048 --selected-resolution-index 4`; `ctest --preset desktop-fast --build-config Debug` | Document resize execution is owned by injected document/app services with no legacy resize adapter, title shim, or direct `ActionManager` history clearing |
|
||||
| DEBT-0021 | Open | Modernization | Layer rename planning/execution dispatch and layer panel operation planning/execution dispatch now consume pure `pp_app_core` through `App::dialog_layer_rename`, `App::init_sidebar` layer callbacks, `pano_cli plan-layer-rename`, `pano_cli plan-layer-operation`, `DocumentLayerRenameServices`, and `DocumentLayerOperationServices`, and the live execution adapters are centralized in `src/legacy_document_layer_services.*`, but that shared bridge still mutates legacy `Canvas` layer state, `NodeLayer`/`NodePanelLayer`, and `ActionManager` undo entries | Preserve existing UI/canvas behavior while document layer commands and undo history are extracted incrementally | `pp_app_core_document_layer_tests`; `pano_cli plan-layer-rename --old-name Base --new-name Paint`; `pano_cli plan-layer-operation --kind add --layer-count 2 --index 1 --name Paint`; `ctest --preset desktop-fast --build-config Debug` | Layer command execution is owned by the document/app command boundary with legacy `Canvas`/UI nodes acting only as adapters or removed entirely |
|
||||
@@ -60,7 +60,7 @@ agent or engineer to remove them without reconstructing context from chat.
|
||||
| DEBT-0040 | Open | Modernization | Close request, document save, and save-before-workflow planning/execution dispatch now consume pure `pp_app_core` through `App::request_close`, `App::save_document`, `App::continue_document_workflow_after_optional_save`, `pano_cli simulate-app-session`, `DocumentSaveServices`, `CloseRequestServices`, `DocumentWorkflowServices`, and `src/legacy_document_session_services.*`, but the bridge still opens legacy message boxes/save dialogs, calls `Canvas::I->project_save`, mutates the unsaved flag on close confirmation, invokes native app close, and routes save-version through the retained legacy dialog | Preserve current close/save/dirty-workflow behavior while document session execution moves toward app/document/UI/platform services | `pp_app_core_document_session_tests`; `pano_cli simulate-app-session --unsaved --save-intent save-dirty-version`; `pano_cli simulate-app-session --no-canvas`; `pano_cli plan-document-file --work-dir D:/Paint --name demo --target-exists`; `pano_cli plan-document-version --directory D:/Paint --doc-name demo.01 --existing-path D:/Paint/demo.02.ppi`; `ctest --preset desktop-fast --build-config Debug` | Close prompt execution, native close requests, dirty-workflow save prompts, existing-project saves, save dialogs, save-version execution, and unsaved-flag mutation are owned by injected app/document/UI/platform services with `App` methods acting only as adapters |
|
||||
| DEBT-0041 | Open | Modernization | Accepted new-document planning/execution dispatch now consumes pure `pp_app_core` through `App::dialog_newdoc`, `pano_cli plan-new-document`, `NewDocumentServices`, and `src/legacy_document_session_services.*`, but the bridge still mutates legacy app document fields, clears legacy layer UI, resizes legacy `Canvas`, clears legacy history, creates the default layer through legacy UI, mutates unsaved/new-document flags, updates the title, and handles keyboard/dialog cleanup directly | Preserve current New Document dialog behavior while document creation moves toward app/document/UI services | `pp_app_core_document_session_tests`; `pano_cli plan-new-document --work-dir D:/Paint --name demo --resolution-index 3`; `pano_cli plan-new-document --work-dir D:/Paint --name demo --resolution-index 3 --target-exists`; `pano_cli simulate-app-session --save-intent save`; `ctest --preset desktop-fast --build-config Debug` | New document creation, overwrite confirmation, canvas/document allocation, default layer creation, history clearing, title updates, dirty/new-document state, and keyboard/dialog cleanup are owned by injected app/document/UI services with `App::dialog_newdoc` acting only as a UI adapter |
|
||||
| DEBT-0042 | Open | Modernization | Accepted Save As and Save Version planning/execution dispatch now consumes pure `pp_app_core` through `App::dialog_save`, `App::dialog_save_ver`, `pano_cli plan-document-file`, `pano_cli plan-document-version`, `DocumentFileSaveServices`, `DocumentVersionSaveServices`, and `src/legacy_document_session_services.*`, but the bridge still opens legacy overwrite prompts, calls legacy `Canvas::project_save`, mutates app document name/path/directory fields, marks version saves dirty before saving, updates the title, and handles keyboard/dialog cleanup directly | Preserve current Save As and Save Version behavior while document persistence moves toward app/document/storage/UI services | `pp_app_core_document_session_tests`; `pano_cli plan-document-file --work-dir D:/Paint --name demo --target-exists`; `pano_cli plan-document-version --directory D:/Paint --doc-name demo.01 --existing-path D:/Paint/demo.02.ppi`; `pano_cli simulate-app-session --save-intent save-as`; `pano_cli simulate-app-session --save-intent save-version`; `ctest --preset desktop-fast --build-config Debug` | Save As overwrite prompting, project-save execution, app document metadata updates, title updates, version-save dirty-state handling, and keyboard/dialog cleanup are owned by injected app/document/storage/UI services with `App::dialog_save` and `App::dialog_save_ver` acting only as UI adapters |
|
||||
| DEBT-0043 | Open | Modernization | Equirectangular, layer, animation-frame, depth, and cube-face export planning/execution dispatch now consumes pure `pp_app_core` through `App::dialog_export`, `App::dialog_export_layers`, `App::dialog_export_anim_frames`, `App::dialog_export_depth`, `App::dialog_export_cube_faces`, `pano_cli plan-export-*`, `DocumentExportServices`, and `src/legacy_document_export_services.*`, but the bridge still calls legacy `Canvas` export methods, owns platform-specific export success messages, creates export directories, handles picker-selected stems, and performs Web prepared-file handoff directly | Preserve current image/collection/depth/cube export behavior while export execution moves toward document/renderer/platform/storage services | `pp_app_core_document_export_tests`; `pano_cli plan-export-start --requires-license --demo`; `pano_cli plan-export-menu --kind layers`; `pano_cli plan-export-target --kind collection --work-dir D:/Paint --doc-name demo --suffix _layers`; `pano_cli simulate-document-export`; `ctest --preset desktop-fast --build-config Debug` | File, collection, stem, depth, and cube export execution, export-directory creation, platform success reporting, Web file handoff, and legacy canvas export calls are owned by injected document/renderer/platform/storage services with export dialogs acting only as UI adapters |
|
||||
| DEBT-0043 | Open | Modernization | Equirectangular, layer, animation-frame, depth, and cube-face export planning/execution dispatch now consumes pure `pp_app_core` through `App::dialog_export`, `App::dialog_export_layers`, `App::dialog_export_anim_frames`, `App::dialog_export_depth`, `App::dialog_export_cube_faces`, `pano_cli plan-export-*`, `DocumentExportServices`, and `src/legacy_document_export_services.*`; layer/frame dialogs also consume `plan_document_export_collection_target` plus `PlatformServices::uses_work_directory_document_export_collections()` instead of spelling local iOS branches, but the bridge still calls legacy `Canvas` export methods, owns platform-specific export success messages, creates export directories, handles picker-selected stems, and performs Web prepared-file handoff directly | Preserve current image/collection/depth/cube export behavior while export execution moves toward document/renderer/platform/storage services | `pp_app_core_document_export_tests`; `pp_platform_api_tests`; `pano_cli plan-export-start --requires-license --demo`; `pano_cli plan-export-menu --kind layers`; `pano_cli plan-export-target --kind collection --work-dir D:/Paint --doc-name demo --suffix _layers`; `pano_cli simulate-document-export`; `ctest --preset desktop-fast --build-config Debug` | File, collection, stem, depth, and cube export execution, export-directory creation, platform success reporting, Web file handoff, picker-selected stem handling, and legacy canvas export calls are owned by injected document/renderer/platform/storage services with export dialogs acting only as UI adapters |
|
||||
| DEBT-0044 | Open | Modernization | Timelapse and animation MP4 export execution dispatch now consumes pure `pp_app_core` through `App::dialog_timelapse_export`, `App::dialog_export_mp4`, `pano_cli plan-export-menu`, `pano_cli plan-export-target --kind name`, `DocumentVideoExportServices`, and `src/legacy_document_export_services.*`, but the bridge still launches legacy desktop timelapse worker threads, calls `App::rec_export`, calls `Canvas::export_anim_mp4`, owns mobile/Web save callbacks, and emits success messages directly | Preserve current MP4/timelapse export behavior while video export moves toward app/document/renderer/video/platform/storage services | `pp_app_core_document_export_tests`; `pano_cli plan-export-menu --kind animation-mp4`; `pano_cli plan-export-menu --kind timelapse`; `pano_cli plan-export-target --kind name --doc-name demo --suffix -animation`; `pano_cli plan-export-target --kind name --doc-name demo --suffix -timelapse`; `ctest --preset desktop-fast --build-config Debug` | Timelapse and animation MP4 execution, desktop worker threading, frame readback/video encoding handoff, mobile/Web save callbacks, and success reporting are owned by injected app/document/renderer/video/platform/storage services with export dialogs acting only as UI adapters |
|
||||
| DEBT-0045 | Open | Modernization | Options-menu preference execution now consumes pure `pp_app_core` through UI scale, viewport scale, RTL direction, VR mode, VR-controller, auto-timelapse, and canvas cursor-mode callbacks plus `AppPreferenceServices` and `src/legacy_app_preference_services.*`, but the bridge still calls legacy `App::set_ui_scale`, `App::set_ui_rtl`, `App::vr_start`, `App::vr_stop`, `NodeCanvas::set_density`, `NodeCanvas::set_cursor_visibility`, `App::rec_start`, `App::rec_stop`, and `Settings::save` directly | Preserve current options-menu behavior while preferences move toward app/UI/platform/storage services | `pp_app_core_app_preferences_tests`; `pano_cli plan-app-preferences --ui-scale 1.5 --display-density 2 --current-scale 1.6 --scale-option 1 --scale-option 1.5 --rtl`; `ctest --preset desktop-fast --build-config Debug`; `cmake --build --preset windows-msvc-default --config Debug --target PanoPainter` | Preference persistence, UI/layout direction, viewport density, cursor mode, VR mode start/stop/failure handling, VR-controller state, and auto-timelapse recording side effects are owned by injected app/UI/platform/storage services with options-menu callbacks acting only as UI adapters |
|
||||
| DEBT-0046 | Open | Modernization | Startup preference/runtime execution now consumes pure `pp_app_core` through `App::init`, `pano_cli plan-app-startup`, `AppStartupServices`, and `src/legacy_app_startup_services.*`, but the bridge still calls legacy `Settings::set`, `Settings::save`, `App::rec_start`, app VR-controller state mutation, and message-box license warning execution directly | Preserve current startup behavior while app startup moves toward app/preferences/storage/recording/UI services | `pp_app_core_app_startup_tests`; `pano_cli plan-app-startup --run-counter 7 --vr-controllers-disabled --license-invalid`; `pano_cli plan-app-startup --run-counter -1`; `ctest --preset desktop-fast --build-config Debug`; `cmake --build --preset windows-msvc-default --config Debug --target PanoPainter` | Startup preference persistence, auto-timelapse startup, stored VR-controller state, license validation/warning, and startup UI/runtime side effects are owned by injected app/preferences/storage/recording/UI services with `App::init` acting only as orchestration |
|
||||
|
||||
@@ -649,6 +649,12 @@ from `App::pick_file_save`.
|
||||
PPBR and MP4 export dialogs now ask `PlatformServices` whether prepared-file
|
||||
writes are used, so those dialog flows no longer spell local `__IOS__ ||
|
||||
__WEB__` branches for mobile/Web export handoff.
|
||||
Layer and animation-frame export dialogs now also ask `PlatformServices`
|
||||
whether work-directory collection exports are used, then feed that into the
|
||||
pure `pp_app_core` `plan_document_export_collection_target` policy. This
|
||||
removes the local iOS branches from those dialogs while preserving iOS
|
||||
`work_path/doc_layers` and `work_path/doc_frames` targets in the legacy
|
||||
adapter until Apple platform services are injected.
|
||||
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
|
||||
@@ -801,10 +807,11 @@ keyboard/dialog cleanup while retained execution remains tracked under
|
||||
`App::dialog_export_anim_frames`, `App::dialog_export_depth`, and
|
||||
`App::dialog_export_cube_faces` now route accepted file/stem/collection and
|
||||
named export work through app-core document export executors and
|
||||
`src/legacy_document_export_services.*`, preserving existing platform messages,
|
||||
directory creation, picker-selected stems, Web prepared-file handoff, and legacy
|
||||
`Canvas` export calls while retained execution remains tracked under
|
||||
`DEBT-0043`.
|
||||
`src/legacy_document_export_services.*`; layer/frame collection export target
|
||||
destination is now planned in `pp_app_core` and selected by `PlatformServices`,
|
||||
preserving existing platform messages, directory creation, picker-selected
|
||||
stems, Web prepared-file handoff, and legacy `Canvas` export calls while
|
||||
retained execution remains tracked under `DEBT-0043`.
|
||||
`App::dialog_timelapse_export` and `App::dialog_export_mp4` now route
|
||||
picker-selected MP4 export paths through the app-core document video export
|
||||
executor and `src/legacy_document_export_services.*`, preserving mobile/Web
|
||||
@@ -1703,7 +1710,8 @@ Results:
|
||||
recording cleanup dispatch, exported-image publish dispatch, persistent
|
||||
storage flush dispatch, document browse-root dispatch,
|
||||
native UI/window state save dispatch, prepared-file writable target dispatch,
|
||||
prepared-file export-dialog policy dispatch, live asset/layout reload policy dispatch,
|
||||
prepared-file export-dialog policy dispatch, work-directory document export
|
||||
collection policy 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
|
||||
|
||||
@@ -185,6 +185,7 @@ public:
|
||||
std::function<void(std::string path)> writer, std::function<void(const std::string& path, bool saved)> callback);
|
||||
void pick_file_save(std::vector<std::string> types, std::function<void(std::string path)> callback);
|
||||
[[nodiscard]] bool uses_prepared_file_writes() const;
|
||||
[[nodiscard]] bool uses_work_directory_document_export_collections() const;
|
||||
void pick_dir(std::function<void(std::string path)> callback);
|
||||
void display_file(std::string path);
|
||||
void share_file(std::string path);
|
||||
|
||||
@@ -26,6 +26,11 @@ struct DocumentExportSuggestedName {
|
||||
std::string name;
|
||||
};
|
||||
|
||||
enum class DocumentExportCollectionDestination {
|
||||
work_directory_collection,
|
||||
picked_directory_stem,
|
||||
};
|
||||
|
||||
enum class DocumentExportStartDecision {
|
||||
start_now,
|
||||
show_license_disabled,
|
||||
@@ -72,6 +77,12 @@ struct DocumentExportMenuPlan {
|
||||
bool opens_dialog = true;
|
||||
};
|
||||
|
||||
struct DocumentExportCollectionTargetPlan {
|
||||
DocumentExportCollectionKind kind = DocumentExportCollectionKind::layers;
|
||||
DocumentExportCollectionDestination destination = DocumentExportCollectionDestination::picked_directory_stem;
|
||||
std::string_view suffix;
|
||||
};
|
||||
|
||||
class DocumentExportMenuServices {
|
||||
public:
|
||||
virtual ~DocumentExportMenuServices() = default;
|
||||
@@ -193,6 +204,32 @@ public:
|
||||
return plan;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr std::string_view document_export_collection_suffix(
|
||||
DocumentExportCollectionKind kind) noexcept
|
||||
{
|
||||
switch (kind) {
|
||||
case DocumentExportCollectionKind::layers:
|
||||
return "_layers";
|
||||
case DocumentExportCollectionKind::animation_frames:
|
||||
return "_frames";
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr DocumentExportCollectionTargetPlan plan_document_export_collection_target(
|
||||
DocumentExportCollectionKind kind,
|
||||
bool use_work_directory_collection) noexcept
|
||||
{
|
||||
return {
|
||||
kind,
|
||||
use_work_directory_collection
|
||||
? DocumentExportCollectionDestination::work_directory_collection
|
||||
: DocumentExportCollectionDestination::picked_directory_stem,
|
||||
document_export_collection_suffix(kind),
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] inline pp::foundation::Result<DocumentExportFileTarget> make_document_export_file_target(
|
||||
std::string_view work_directory,
|
||||
std::string_view document_name,
|
||||
|
||||
@@ -53,6 +53,57 @@ namespace {
|
||||
return false;
|
||||
}
|
||||
|
||||
void start_document_export_collection(
|
||||
App& app,
|
||||
pp::app::DocumentExportCollectionKind kind,
|
||||
const char* message_title,
|
||||
const char* collection_log_message,
|
||||
const char* stem_log_message)
|
||||
{
|
||||
const auto plan = pp::app::plan_document_export_collection_target(
|
||||
kind,
|
||||
app.uses_work_directory_document_export_collections());
|
||||
|
||||
if (plan.destination == pp::app::DocumentExportCollectionDestination::work_directory_collection) {
|
||||
const auto target = pp::app::make_document_export_collection_target(
|
||||
app.work_path,
|
||||
app.doc_name,
|
||||
plan.suffix);
|
||||
if (!target) {
|
||||
app.message_box(message_title, target.status().message);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto status = pp::panopainter::execute_legacy_document_export_collection(
|
||||
app,
|
||||
plan.kind,
|
||||
target.value());
|
||||
if (!status.ok())
|
||||
LOG("%s: %s", collection_log_message, status.message);
|
||||
return;
|
||||
}
|
||||
|
||||
app.pick_dir([
|
||||
&app,
|
||||
kind = plan.kind,
|
||||
title = std::string(message_title),
|
||||
log_message = std::string(stem_log_message)
|
||||
](std::string path) {
|
||||
const auto target = pp::app::make_document_export_stem_target(path, app.doc_name);
|
||||
if (!target) {
|
||||
app.message_box(title, target.status().message);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto status = pp::panopainter::execute_legacy_document_export_stem(
|
||||
app,
|
||||
kind,
|
||||
target.value());
|
||||
if (!status.ok())
|
||||
LOG("%s: %s", log_message.c_str(), status.message);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::shared_ptr<NodeProgressBar> App::show_progress(const std::string& title, int total /*= 0*/)
|
||||
@@ -361,35 +412,12 @@ void App::dialog_export_layers()
|
||||
if (!can_start_document_export(*this, true))
|
||||
return;
|
||||
|
||||
#if defined(__IOS__)
|
||||
const auto target = pp::app::make_document_export_collection_target(work_path, doc_name, "_layers");
|
||||
if (!target) {
|
||||
message_box("Export Layers", target.status().message);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto status = pp::panopainter::execute_legacy_document_export_collection(
|
||||
start_document_export_collection(
|
||||
*this,
|
||||
pp::app::DocumentExportCollectionKind::layers,
|
||||
target.value());
|
||||
if (!status.ok())
|
||||
LOG("Document layer collection export failed: %s", status.message);
|
||||
#else
|
||||
pick_dir([this](std::string path) {
|
||||
const auto target = pp::app::make_document_export_stem_target(path, doc_name);
|
||||
if (!target) {
|
||||
message_box("Export Layers", target.status().message);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto status = pp::panopainter::execute_legacy_document_export_stem(
|
||||
*this,
|
||||
pp::app::DocumentExportCollectionKind::layers,
|
||||
target.value());
|
||||
if (!status.ok())
|
||||
LOG("Document layer stem export failed: %s", status.message);
|
||||
});
|
||||
#endif
|
||||
"Export Layers",
|
||||
"Document layer collection export failed",
|
||||
"Document layer stem export failed");
|
||||
}
|
||||
|
||||
void App::dialog_export_anim_frames()
|
||||
@@ -397,35 +425,12 @@ void App::dialog_export_anim_frames()
|
||||
if (!can_start_document_export(*this, true))
|
||||
return;
|
||||
|
||||
#if defined(__IOS__)
|
||||
const auto target = pp::app::make_document_export_collection_target(work_path, doc_name, "_frames");
|
||||
if (!target) {
|
||||
message_box("Export Layers", target.status().message);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto status = pp::panopainter::execute_legacy_document_export_collection(
|
||||
start_document_export_collection(
|
||||
*this,
|
||||
pp::app::DocumentExportCollectionKind::animation_frames,
|
||||
target.value());
|
||||
if (!status.ok())
|
||||
LOG("Document animation frame collection export failed: %s", status.message);
|
||||
#else
|
||||
pick_dir([this](std::string path) {
|
||||
const auto target = pp::app::make_document_export_stem_target(path, doc_name);
|
||||
if (!target) {
|
||||
message_box("Export Layers", target.status().message);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto status = pp::panopainter::execute_legacy_document_export_stem(
|
||||
*this,
|
||||
pp::app::DocumentExportCollectionKind::animation_frames,
|
||||
target.value());
|
||||
if (!status.ok())
|
||||
LOG("Document animation frame stem export failed: %s", status.message);
|
||||
});
|
||||
#endif
|
||||
"Export Layers",
|
||||
"Document animation frame collection export failed",
|
||||
"Document animation frame stem export failed");
|
||||
}
|
||||
|
||||
void App::dialog_export_depth()
|
||||
|
||||
@@ -186,6 +186,11 @@ bool App::uses_prepared_file_writes() const
|
||||
return active_platform_services().uses_prepared_file_writes();
|
||||
}
|
||||
|
||||
bool App::uses_work_directory_document_export_collections() const
|
||||
{
|
||||
return active_platform_services().uses_work_directory_document_export_collections();
|
||||
}
|
||||
|
||||
void App::pick_dir(std::function<void(std::string path)> callback)
|
||||
{
|
||||
redraw = true;
|
||||
|
||||
@@ -64,6 +64,7 @@ public:
|
||||
virtual void pick_save_file(std::vector<std::string> file_types, PickedPathCallback callback) = 0;
|
||||
virtual void pick_directory(PickedPathCallback callback) = 0;
|
||||
[[nodiscard]] virtual bool uses_prepared_file_writes() = 0;
|
||||
[[nodiscard]] virtual bool uses_work_directory_document_export_collections() = 0;
|
||||
[[nodiscard]] virtual PreparedFileTarget prepare_writable_file(
|
||||
std::string_view type,
|
||||
std::string_view default_name,
|
||||
|
||||
@@ -440,6 +440,15 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
[[nodiscard]] bool uses_work_directory_document_export_collections() override
|
||||
{
|
||||
#if __IOS__
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
[[nodiscard]] pp::platform::PreparedFileTarget prepare_writable_file(
|
||||
std::string_view type,
|
||||
std::string_view default_name,
|
||||
|
||||
@@ -453,6 +453,11 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool uses_work_directory_document_export_collections() override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] pp::platform::PreparedFileTarget prepare_writable_file(
|
||||
std::string_view type,
|
||||
std::string_view default_name,
|
||||
|
||||
@@ -192,6 +192,32 @@ void video_export_builds_suggested_name(pp::tests::Harness& harness)
|
||||
PP_EXPECT(harness, animation.value().name == "demo-animation");
|
||||
}
|
||||
|
||||
void collection_export_target_plan_selects_platform_destination(pp::tests::Harness& harness)
|
||||
{
|
||||
const auto layer_collection = pp::app::plan_document_export_collection_target(
|
||||
pp::app::DocumentExportCollectionKind::layers,
|
||||
true);
|
||||
const auto frame_collection = pp::app::plan_document_export_collection_target(
|
||||
pp::app::DocumentExportCollectionKind::animation_frames,
|
||||
true);
|
||||
const auto picked_layers = pp::app::plan_document_export_collection_target(
|
||||
pp::app::DocumentExportCollectionKind::layers,
|
||||
false);
|
||||
|
||||
PP_EXPECT(
|
||||
harness,
|
||||
layer_collection.destination == pp::app::DocumentExportCollectionDestination::work_directory_collection);
|
||||
PP_EXPECT(
|
||||
harness,
|
||||
frame_collection.destination == pp::app::DocumentExportCollectionDestination::work_directory_collection);
|
||||
PP_EXPECT(
|
||||
harness,
|
||||
picked_layers.destination == pp::app::DocumentExportCollectionDestination::picked_directory_stem);
|
||||
PP_EXPECT(harness, layer_collection.suffix == "_layers");
|
||||
PP_EXPECT(harness, frame_collection.suffix == "_frames");
|
||||
PP_EXPECT(harness, picked_layers.suffix == "_layers");
|
||||
}
|
||||
|
||||
void export_start_allows_valid_canvas_state(pp::tests::Harness& harness)
|
||||
{
|
||||
PP_EXPECT(
|
||||
@@ -525,6 +551,7 @@ int main()
|
||||
harness.run("collection export builds directory and stem", collection_export_builds_directory_and_stem);
|
||||
harness.run("picked directory export builds stem", picked_directory_export_builds_stem);
|
||||
harness.run("video export builds suggested name", video_export_builds_suggested_name);
|
||||
harness.run("collection export target plan selects platform destination", collection_export_target_plan_selects_platform_destination);
|
||||
harness.run("export start allows valid canvas state", export_start_allows_valid_canvas_state);
|
||||
harness.run("export start blocks demo only when license required", export_start_blocks_demo_only_when_license_required);
|
||||
harness.run("export start reports missing canvas after license gate", export_start_reports_missing_canvas_after_license_gate);
|
||||
|
||||
@@ -216,6 +216,12 @@ public:
|
||||
return prepared_file_writes;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool uses_work_directory_document_export_collections() override
|
||||
{
|
||||
++document_export_collection_policy_checks;
|
||||
return work_directory_document_export_collections;
|
||||
}
|
||||
|
||||
[[nodiscard]] pp::platform::PreparedFileTarget prepare_writable_file(
|
||||
std::string_view type,
|
||||
std::string_view default_name,
|
||||
@@ -276,12 +282,14 @@ public:
|
||||
int pick_save_file_requests = 0;
|
||||
int pick_directory_requests = 0;
|
||||
int prepared_file_write_policy_checks = 0;
|
||||
int document_export_collection_policy_checks = 0;
|
||||
int prepare_writable_file_requests = 0;
|
||||
int save_prepared_file_requests = 0;
|
||||
bool cursor_visible = false;
|
||||
bool keyboard_visible = false;
|
||||
bool prepared_file_saved = true;
|
||||
bool prepared_file_writes = true;
|
||||
bool work_directory_document_export_collections = false;
|
||||
bool deletes_recorded_files = true;
|
||||
bool live_asset_reloading = true;
|
||||
float last_platform_delta = 0.0f;
|
||||
@@ -597,6 +605,17 @@ void platform_services_dispatch_writable_file_target(pp::tests::Harness& harness
|
||||
PP_EXPECT(harness, target.write_on_background_thread);
|
||||
}
|
||||
|
||||
void platform_services_dispatch_document_export_collection_policy(pp::tests::Harness& harness)
|
||||
{
|
||||
FakePlatformServices fake("unused");
|
||||
pp::platform::PlatformServices& services = fake;
|
||||
|
||||
PP_EXPECT(harness, !services.uses_work_directory_document_export_collections());
|
||||
fake.work_directory_document_export_collections = true;
|
||||
PP_EXPECT(harness, services.uses_work_directory_document_export_collections());
|
||||
PP_EXPECT(harness, fake.document_export_collection_policy_checks == 2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main()
|
||||
@@ -620,5 +639,8 @@ int main()
|
||||
harness.run("platform services dispatch picker callbacks", platform_services_dispatch_picker_callbacks);
|
||||
harness.run("platform services dispatch prepared file save", platform_services_dispatch_prepared_file_save);
|
||||
harness.run("platform services dispatch writable file target", platform_services_dispatch_writable_file_target);
|
||||
harness.run(
|
||||
"platform services dispatch document export collection policy",
|
||||
platform_services_dispatch_document_export_collection_policy);
|
||||
return harness.finish();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user