Centralize legacy document image exports
This commit is contained in:
@@ -84,6 +84,8 @@ set(PP_PANOPAINTER_APP_SOURCES
|
||||
src/app_vr.cpp
|
||||
src/legacy_cloud_services.cpp
|
||||
src/legacy_cloud_services.h
|
||||
src/legacy_document_export_services.cpp
|
||||
src/legacy_document_export_services.h
|
||||
src/legacy_document_open_services.cpp
|
||||
src/legacy_document_open_services.h
|
||||
src/legacy_document_session_services.cpp
|
||||
|
||||
@@ -561,7 +561,9 @@ Known local toolchain state:
|
||||
- `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.
|
||||
decisions, export menu executor dispatch, file/stem/collection export
|
||||
execution dispatch, failed directory creation preservation, named depth/cube
|
||||
export dispatch, and malformed export target rejection.
|
||||
- `pp_app_core_document_recording_tests` covers recording start/stop, clear,
|
||||
platform recorded-file cleanup, frame-count reset, export progress totals,
|
||||
and oversized progress-total clamping.
|
||||
@@ -605,6 +607,12 @@ Known local toolchain state:
|
||||
overwrite prompts, document field updates, title updates, and keyboard/dialog
|
||||
cleanup. Retained legacy UI/canvas execution remains tracked by `DEBT-0040`,
|
||||
`DEBT-0041`, and `DEBT-0042`.
|
||||
- `src/legacy_document_export_services.*` is the current app-shell bridge
|
||||
between `pp_app_core` document export execution plans and live equirectangular,
|
||||
layers, animation-frame, depth, and cube-face export calls. It preserves
|
||||
platform-specific export messages, directory creation, picker-selected stem
|
||||
exports, Web prepared-file handoff, and legacy `Canvas` export execution while
|
||||
retained renderer/document/platform ownership is tracked by `DEBT-0043`.
|
||||
- `src/legacy_history_services.*` is the current app-shell bridge between
|
||||
`pp_app_core` history plans and legacy `ActionManager`; toolbar and
|
||||
`NodeCanvas` hotkeys share it while document-history extraction remains
|
||||
|
||||
@@ -60,6 +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 |
|
||||
|
||||
## Closed Debt
|
||||
|
||||
|
||||
@@ -750,6 +750,14 @@ Save Version plans through app-core document file/version save executors and
|
||||
legacy `Canvas::project_save`, app document field updates, title updates, and
|
||||
keyboard/dialog cleanup while retained execution remains tracked under
|
||||
`DEBT-0042`.
|
||||
`App::dialog_export`, `App::dialog_export_layers`,
|
||||
`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`.
|
||||
|
||||
Implementation tasks:
|
||||
|
||||
@@ -1314,6 +1322,14 @@ Results:
|
||||
`pp_app_core_document_session_tests`, `pano_cli_plan_document_file_*`,
|
||||
`pano_cli_plan_document_version_*`, and `pano_cli_simulate_app_session_*`
|
||||
smoke tests after the live bridge split.
|
||||
- `PanoPainter`, `pp_app_core_document_export_tests`, and `pano_cli` built
|
||||
after equirectangular, layers, animation-frame, depth, and cube-face export
|
||||
execution moved behind document export services. A clean rebuild was required
|
||||
once because MSVC reported the known Debug PDB `LNK1103` corruption, after
|
||||
which the build passed.
|
||||
- Focused export CTest coverage passed for `pp_app_core_document_export_tests`,
|
||||
`pano_cli_plan_export_start/menu/target_*`, and
|
||||
`pano_cli_simulate_document_export_smoke` after the live bridge split.
|
||||
- `pp_app_core_document_recording_tests` passed, covering recording start/stop,
|
||||
clear, platform recorded-file cleanup, frame-count reset, export progress
|
||||
totals, and oversized progress-total clamping.
|
||||
|
||||
@@ -56,6 +56,11 @@ enum class DocumentExportMenuAction {
|
||||
unavailable_no_canvas,
|
||||
};
|
||||
|
||||
enum class DocumentExportCollectionKind {
|
||||
layers,
|
||||
animation_frames,
|
||||
};
|
||||
|
||||
struct DocumentExportMenuPlan {
|
||||
DocumentExportMenuKind kind = DocumentExportMenuKind::jpeg;
|
||||
DocumentExportMenuAction action = DocumentExportMenuAction::show_jpeg_dialog;
|
||||
@@ -77,6 +82,20 @@ public:
|
||||
virtual void show_license_disabled() = 0;
|
||||
};
|
||||
|
||||
class DocumentExportServices {
|
||||
public:
|
||||
virtual ~DocumentExportServices() = default;
|
||||
|
||||
virtual bool create_directory(std::string_view directory) = 0;
|
||||
virtual void export_equirectangular(const DocumentExportFileTarget& target) = 0;
|
||||
virtual void export_layers_to_stem(const DocumentExportStemTarget& target) = 0;
|
||||
virtual void export_layers_to_collection(const DocumentExportCollectionTarget& target) = 0;
|
||||
virtual void export_animation_frames_to_stem(const DocumentExportStemTarget& target) = 0;
|
||||
virtual void export_animation_frames_to_collection(const DocumentExportCollectionTarget& target) = 0;
|
||||
virtual void export_depth(std::string_view document_name) = 0;
|
||||
virtual void export_cube_faces(std::string_view document_name) = 0;
|
||||
};
|
||||
|
||||
[[nodiscard]] constexpr DocumentExportStartDecision plan_document_export_start(
|
||||
bool requires_license,
|
||||
bool license_valid,
|
||||
@@ -241,6 +260,88 @@ public:
|
||||
return pp::foundation::Result<DocumentExportSuggestedName>::success(std::move(target));
|
||||
}
|
||||
|
||||
[[nodiscard]] inline pp::foundation::Status execute_document_export_file(
|
||||
const DocumentExportFileTarget& target,
|
||||
DocumentExportServices& services)
|
||||
{
|
||||
if (target.path.empty() || target.suggested_name.empty()) {
|
||||
return pp::foundation::Status::invalid_argument("export file target requires a path and suggested name");
|
||||
}
|
||||
|
||||
services.export_equirectangular(target);
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
[[nodiscard]] inline pp::foundation::Status execute_document_export_stem(
|
||||
DocumentExportCollectionKind kind,
|
||||
const DocumentExportStemTarget& target,
|
||||
DocumentExportServices& services)
|
||||
{
|
||||
if (target.stem_path.empty()) {
|
||||
return pp::foundation::Status::invalid_argument("export stem target requires a stem path");
|
||||
}
|
||||
|
||||
switch (kind) {
|
||||
case DocumentExportCollectionKind::layers:
|
||||
services.export_layers_to_stem(target);
|
||||
return pp::foundation::Status::success();
|
||||
case DocumentExportCollectionKind::animation_frames:
|
||||
services.export_animation_frames_to_stem(target);
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
return pp::foundation::Status::invalid_argument("unknown document export collection kind");
|
||||
}
|
||||
|
||||
[[nodiscard]] inline pp::foundation::Status execute_document_export_collection(
|
||||
DocumentExportCollectionKind kind,
|
||||
const DocumentExportCollectionTarget& target,
|
||||
DocumentExportServices& services)
|
||||
{
|
||||
if (target.directory.empty() || target.stem_path.empty()) {
|
||||
return pp::foundation::Status::invalid_argument("export collection target requires a directory and stem path");
|
||||
}
|
||||
|
||||
if (!services.create_directory(target.directory)) {
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
switch (kind) {
|
||||
case DocumentExportCollectionKind::layers:
|
||||
services.export_layers_to_collection(target);
|
||||
return pp::foundation::Status::success();
|
||||
case DocumentExportCollectionKind::animation_frames:
|
||||
services.export_animation_frames_to_collection(target);
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
return pp::foundation::Status::invalid_argument("unknown document export collection kind");
|
||||
}
|
||||
|
||||
[[nodiscard]] inline pp::foundation::Status execute_document_export_depth(
|
||||
std::string_view document_name,
|
||||
DocumentExportServices& services)
|
||||
{
|
||||
if (document_name.empty()) {
|
||||
return pp::foundation::Status::invalid_argument("document name must not be empty");
|
||||
}
|
||||
|
||||
services.export_depth(document_name);
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
[[nodiscard]] inline pp::foundation::Status execute_document_export_cube_faces(
|
||||
std::string_view document_name,
|
||||
DocumentExportServices& services)
|
||||
{
|
||||
if (document_name.empty()) {
|
||||
return pp::foundation::Status::invalid_argument("document name must not be empty");
|
||||
}
|
||||
|
||||
services.export_cube_faces(document_name);
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
[[nodiscard]] inline pp::foundation::Status execute_document_export_menu_plan(
|
||||
const DocumentExportMenuPlan& plan,
|
||||
DocumentExportMenuServices& services)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "app_core/document_export.h"
|
||||
#include "app_core/document_session.h"
|
||||
#include "legacy_document_canvas_services.h"
|
||||
#include "legacy_document_export_services.h"
|
||||
#include "legacy_document_layer_services.h"
|
||||
#include "legacy_document_session_services.h"
|
||||
#include "settings.h"
|
||||
@@ -356,21 +357,9 @@ void App::dialog_export(std::string ext)
|
||||
return;
|
||||
}
|
||||
|
||||
canvas->m_canvas->export_equirectangular(target.value().path, [this, target = target.value()]{
|
||||
#if defined(__IOS__)
|
||||
message_box("Export Equirectangular", "Image exported to Photos");
|
||||
#elif defined(__OSX__)
|
||||
message_box("Export Equirectangular", "Image exported to Pictures/PanoPainter folder");
|
||||
#elif defined(_WIN32)
|
||||
message_box("Export Equirectangular", "Image exported to " + work_path);
|
||||
#elif defined(__QUEST__)
|
||||
//auto result = ovr_Media_ShareToFacebook("Sharing from PanoPainter on Oculus Quest", path.c_str(), ovrMediaContentType_Photo);
|
||||
#elif __WEB__
|
||||
ui_task([=]{
|
||||
save_prepared_file(target.path, target.suggested_name, [](const std::string&, bool) { });
|
||||
});
|
||||
#endif
|
||||
});
|
||||
const auto status = pp::panopainter::execute_legacy_document_export_file(*this, target.value());
|
||||
if (!status.ok())
|
||||
LOG("Document export file action failed: %s", status.message);
|
||||
}
|
||||
|
||||
void App::dialog_export_layers()
|
||||
@@ -385,12 +374,12 @@ void App::dialog_export_layers()
|
||||
return;
|
||||
}
|
||||
|
||||
if (Asset::create_dir(target.value().directory))
|
||||
{
|
||||
canvas->m_canvas->export_layers(target.value().stem_path, [this] {
|
||||
message_box("Export Layers", "Image layers exported to Files/PanoPainter");
|
||||
});
|
||||
}
|
||||
const auto status = pp::panopainter::execute_legacy_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);
|
||||
@@ -399,9 +388,12 @@ void App::dialog_export_layers()
|
||||
return;
|
||||
}
|
||||
|
||||
canvas->m_canvas->export_layers(target.value().stem_path, [this, target = target.value()] {
|
||||
message_box("Export Layers", "Layers exported to: " + target.stem_path);
|
||||
});
|
||||
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
|
||||
}
|
||||
@@ -418,12 +410,12 @@ void App::dialog_export_anim_frames()
|
||||
return;
|
||||
}
|
||||
|
||||
if (Asset::create_dir(target.value().directory))
|
||||
{
|
||||
canvas->m_canvas->export_anim_frames(target.value().stem_path, [this] {
|
||||
message_box("Export Layers", "Image layers exported to Files/PanoPainter");
|
||||
});
|
||||
}
|
||||
const auto status = pp::panopainter::execute_legacy_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);
|
||||
@@ -432,9 +424,12 @@ void App::dialog_export_anim_frames()
|
||||
return;
|
||||
}
|
||||
|
||||
canvas->m_canvas->export_anim_frames(target.value().stem_path, [this, target = target.value()] {
|
||||
message_box("Export Layers", "Layers exported to: " + target.stem_path);
|
||||
});
|
||||
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
|
||||
}
|
||||
@@ -444,16 +439,9 @@ void App::dialog_export_depth()
|
||||
if (!can_start_document_export(*this, true))
|
||||
return;
|
||||
|
||||
// TODO: use picker
|
||||
canvas->m_canvas->export_depth(doc_name, [this] {
|
||||
#if defined(__IOS__)
|
||||
message_box("Export 3D View + Depth", "Image and depth exported to Files/PanoPainter");
|
||||
#elif defined(__OSX__)
|
||||
message_box("Export 3D View + Depth", "Image and depth exported to Pictures/PanoPainter folder");
|
||||
#elif defined(_WIN32)
|
||||
message_box("Export 3D View + Depth", "Image and depth exported to " + work_path);
|
||||
#endif
|
||||
});
|
||||
const auto status = pp::panopainter::execute_legacy_document_export_depth(*this, doc_name);
|
||||
if (!status.ok())
|
||||
LOG("Document depth export failed: %s", status.message);
|
||||
}
|
||||
|
||||
void App::dialog_resize()
|
||||
@@ -487,15 +475,9 @@ void App::dialog_export_cube_faces()
|
||||
if (!can_start_document_export(*this, false))
|
||||
return;
|
||||
|
||||
canvas->m_canvas->export_cube_faces(doc_name, [this] {
|
||||
#if defined(__IOS__)
|
||||
message_box("Export Cube Faces", "Image and depth exported to Files/PanoPainter");
|
||||
#elif defined(__OSX__)
|
||||
message_box("Export Cube Faces", "Image and depth exported to Pictures/PanoPainter folder");
|
||||
#elif defined(_WIN32)
|
||||
message_box("Export Cube Faces", "Image and depth exported to " + work_path);
|
||||
#endif
|
||||
});
|
||||
const auto status = pp::panopainter::execute_legacy_document_export_cube_faces(*this, doc_name);
|
||||
if (!status.ok())
|
||||
LOG("Document cube-face export failed: %s", status.message);
|
||||
}
|
||||
|
||||
void App::dialog_layer_rename()
|
||||
|
||||
152
src/legacy_document_export_services.cpp
Normal file
152
src/legacy_document_export_services.cpp
Normal file
@@ -0,0 +1,152 @@
|
||||
#include "pch.h"
|
||||
|
||||
#include "legacy_document_export_services.h"
|
||||
|
||||
#include "app.h"
|
||||
|
||||
namespace pp::panopainter {
|
||||
namespace {
|
||||
|
||||
class LegacyDocumentExportServices final : public pp::app::DocumentExportServices {
|
||||
public:
|
||||
explicit LegacyDocumentExportServices(App& app) noexcept
|
||||
: app_(app)
|
||||
{
|
||||
}
|
||||
|
||||
bool create_directory(std::string_view directory) override
|
||||
{
|
||||
return Asset::create_dir(std::string(directory));
|
||||
}
|
||||
|
||||
void export_equirectangular(const pp::app::DocumentExportFileTarget& target) override
|
||||
{
|
||||
auto* app = &app_;
|
||||
app_.canvas->m_canvas->export_equirectangular(target.path, [app, target] {
|
||||
#if defined(__IOS__)
|
||||
app->message_box("Export Equirectangular", "Image exported to Photos");
|
||||
#elif defined(__OSX__)
|
||||
app->message_box("Export Equirectangular", "Image exported to Pictures/PanoPainter folder");
|
||||
#elif defined(_WIN32)
|
||||
app->message_box("Export Equirectangular", "Image exported to " + app->work_path);
|
||||
#elif defined(__QUEST__)
|
||||
(void)target;
|
||||
#elif __WEB__
|
||||
app->ui_task([app, target] {
|
||||
app->save_prepared_file(target.path, target.suggested_name, [](const std::string&, bool) { });
|
||||
});
|
||||
#else
|
||||
(void)target;
|
||||
#endif
|
||||
});
|
||||
}
|
||||
|
||||
void export_layers_to_stem(const pp::app::DocumentExportStemTarget& target) override
|
||||
{
|
||||
auto* app = &app_;
|
||||
app_.canvas->m_canvas->export_layers(target.stem_path, [app, target] {
|
||||
app->message_box("Export Layers", "Layers exported to: " + target.stem_path);
|
||||
});
|
||||
}
|
||||
|
||||
void export_layers_to_collection(const pp::app::DocumentExportCollectionTarget& target) override
|
||||
{
|
||||
auto* app = &app_;
|
||||
app_.canvas->m_canvas->export_layers(target.stem_path, [app] {
|
||||
app->message_box("Export Layers", "Image layers exported to Files/PanoPainter");
|
||||
});
|
||||
}
|
||||
|
||||
void export_animation_frames_to_stem(const pp::app::DocumentExportStemTarget& target) override
|
||||
{
|
||||
auto* app = &app_;
|
||||
app_.canvas->m_canvas->export_anim_frames(target.stem_path, [app, target] {
|
||||
app->message_box("Export Layers", "Layers exported to: " + target.stem_path);
|
||||
});
|
||||
}
|
||||
|
||||
void export_animation_frames_to_collection(const pp::app::DocumentExportCollectionTarget& target) override
|
||||
{
|
||||
auto* app = &app_;
|
||||
app_.canvas->m_canvas->export_anim_frames(target.stem_path, [app] {
|
||||
app->message_box("Export Layers", "Image layers exported to Files/PanoPainter");
|
||||
});
|
||||
}
|
||||
|
||||
void export_depth(std::string_view document_name) override
|
||||
{
|
||||
auto* app = &app_;
|
||||
app_.canvas->m_canvas->export_depth(std::string(document_name), [app] {
|
||||
#if defined(__IOS__)
|
||||
app->message_box("Export 3D View + Depth", "Image and depth exported to Files/PanoPainter");
|
||||
#elif defined(__OSX__)
|
||||
app->message_box("Export 3D View + Depth", "Image and depth exported to Pictures/PanoPainter folder");
|
||||
#elif defined(_WIN32)
|
||||
app->message_box("Export 3D View + Depth", "Image and depth exported to " + app->work_path);
|
||||
#endif
|
||||
});
|
||||
}
|
||||
|
||||
void export_cube_faces(std::string_view document_name) override
|
||||
{
|
||||
auto* app = &app_;
|
||||
app_.canvas->m_canvas->export_cube_faces(std::string(document_name), [app] {
|
||||
#if defined(__IOS__)
|
||||
app->message_box("Export Cube Faces", "Image and depth exported to Files/PanoPainter");
|
||||
#elif defined(__OSX__)
|
||||
app->message_box("Export Cube Faces", "Image and depth exported to Pictures/PanoPainter folder");
|
||||
#elif defined(_WIN32)
|
||||
app->message_box("Export Cube Faces", "Image and depth exported to " + app->work_path);
|
||||
#endif
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
App& app_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
pp::foundation::Status execute_legacy_document_export_file(
|
||||
App& app,
|
||||
const pp::app::DocumentExportFileTarget& target)
|
||||
{
|
||||
LegacyDocumentExportServices services(app);
|
||||
return pp::app::execute_document_export_file(target, services);
|
||||
}
|
||||
|
||||
pp::foundation::Status execute_legacy_document_export_stem(
|
||||
App& app,
|
||||
pp::app::DocumentExportCollectionKind kind,
|
||||
const pp::app::DocumentExportStemTarget& target)
|
||||
{
|
||||
LegacyDocumentExportServices services(app);
|
||||
return pp::app::execute_document_export_stem(kind, target, services);
|
||||
}
|
||||
|
||||
pp::foundation::Status execute_legacy_document_export_collection(
|
||||
App& app,
|
||||
pp::app::DocumentExportCollectionKind kind,
|
||||
const pp::app::DocumentExportCollectionTarget& target)
|
||||
{
|
||||
LegacyDocumentExportServices services(app);
|
||||
return pp::app::execute_document_export_collection(kind, target, services);
|
||||
}
|
||||
|
||||
pp::foundation::Status execute_legacy_document_export_depth(
|
||||
App& app,
|
||||
std::string_view document_name)
|
||||
{
|
||||
LegacyDocumentExportServices services(app);
|
||||
return pp::app::execute_document_export_depth(document_name, services);
|
||||
}
|
||||
|
||||
pp::foundation::Status execute_legacy_document_export_cube_faces(
|
||||
App& app,
|
||||
std::string_view document_name)
|
||||
{
|
||||
LegacyDocumentExportServices services(app);
|
||||
return pp::app::execute_document_export_cube_faces(document_name, services);
|
||||
}
|
||||
|
||||
} // namespace pp::panopainter
|
||||
32
src/legacy_document_export_services.h
Normal file
32
src/legacy_document_export_services.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "app_core/document_export.h"
|
||||
#include "foundation/result.h"
|
||||
|
||||
class App;
|
||||
|
||||
namespace pp::panopainter {
|
||||
|
||||
[[nodiscard]] pp::foundation::Status execute_legacy_document_export_file(
|
||||
App& app,
|
||||
const pp::app::DocumentExportFileTarget& target);
|
||||
|
||||
[[nodiscard]] pp::foundation::Status execute_legacy_document_export_stem(
|
||||
App& app,
|
||||
pp::app::DocumentExportCollectionKind kind,
|
||||
const pp::app::DocumentExportStemTarget& target);
|
||||
|
||||
[[nodiscard]] pp::foundation::Status execute_legacy_document_export_collection(
|
||||
App& app,
|
||||
pp::app::DocumentExportCollectionKind kind,
|
||||
const pp::app::DocumentExportCollectionTarget& target);
|
||||
|
||||
[[nodiscard]] pp::foundation::Status execute_legacy_document_export_depth(
|
||||
App& app,
|
||||
std::string_view document_name);
|
||||
|
||||
[[nodiscard]] pp::foundation::Status execute_legacy_document_export_cube_faces(
|
||||
App& app,
|
||||
std::string_view document_name);
|
||||
|
||||
} // namespace pp::panopainter
|
||||
@@ -1,6 +1,9 @@
|
||||
#include "app_core/document_export.h"
|
||||
#include "test_harness.h"
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace {
|
||||
|
||||
class FakeDocumentExportMenuServices final : public pp::app::DocumentExportMenuServices {
|
||||
@@ -33,6 +36,80 @@ public:
|
||||
int license_messages = 0;
|
||||
};
|
||||
|
||||
class FakeDocumentExportServices final : public pp::app::DocumentExportServices {
|
||||
public:
|
||||
bool create_directory(std::string_view directory) override
|
||||
{
|
||||
create_dir_calls += 1;
|
||||
last_directory = std::string(directory);
|
||||
return create_directory_result;
|
||||
}
|
||||
|
||||
void export_equirectangular(const pp::app::DocumentExportFileTarget& target) override
|
||||
{
|
||||
equirectangular_exports += 1;
|
||||
last_path = target.path;
|
||||
call_order += "equirect;";
|
||||
}
|
||||
|
||||
void export_layers_to_stem(const pp::app::DocumentExportStemTarget& target) override
|
||||
{
|
||||
layer_stem_exports += 1;
|
||||
last_stem = target.stem_path;
|
||||
call_order += "layers-stem;";
|
||||
}
|
||||
|
||||
void export_layers_to_collection(const pp::app::DocumentExportCollectionTarget& target) override
|
||||
{
|
||||
layer_collection_exports += 1;
|
||||
last_stem = target.stem_path;
|
||||
call_order += "layers-collection;";
|
||||
}
|
||||
|
||||
void export_animation_frames_to_stem(const pp::app::DocumentExportStemTarget& target) override
|
||||
{
|
||||
frame_stem_exports += 1;
|
||||
last_stem = target.stem_path;
|
||||
call_order += "frames-stem;";
|
||||
}
|
||||
|
||||
void export_animation_frames_to_collection(const pp::app::DocumentExportCollectionTarget& target) override
|
||||
{
|
||||
frame_collection_exports += 1;
|
||||
last_stem = target.stem_path;
|
||||
call_order += "frames-collection;";
|
||||
}
|
||||
|
||||
void export_depth(std::string_view document_name) override
|
||||
{
|
||||
depth_exports += 1;
|
||||
last_name = std::string(document_name);
|
||||
call_order += "depth;";
|
||||
}
|
||||
|
||||
void export_cube_faces(std::string_view document_name) override
|
||||
{
|
||||
cube_exports += 1;
|
||||
last_name = std::string(document_name);
|
||||
call_order += "cube;";
|
||||
}
|
||||
|
||||
bool create_directory_result = true;
|
||||
int create_dir_calls = 0;
|
||||
int equirectangular_exports = 0;
|
||||
int layer_stem_exports = 0;
|
||||
int layer_collection_exports = 0;
|
||||
int frame_stem_exports = 0;
|
||||
int frame_collection_exports = 0;
|
||||
int depth_exports = 0;
|
||||
int cube_exports = 0;
|
||||
std::string last_directory;
|
||||
std::string last_path;
|
||||
std::string last_stem;
|
||||
std::string last_name;
|
||||
std::string call_order;
|
||||
};
|
||||
|
||||
void equirectangular_export_builds_file_target(pp::tests::Harness& harness)
|
||||
{
|
||||
const auto target = pp::app::make_document_export_file_target("D:/Paint", "demo", ".png");
|
||||
@@ -253,6 +330,107 @@ void export_menu_executor_preserves_blocked_and_unavailable_actions(pp::tests::H
|
||||
PP_EXPECT(harness, services.total_calls() == 1);
|
||||
}
|
||||
|
||||
void export_executor_dispatches_file_stem_collection_and_named_exports(pp::tests::Harness& harness)
|
||||
{
|
||||
FakeDocumentExportServices services;
|
||||
const auto file = pp::app::make_document_export_file_target("D:/Paint", "demo", ".png");
|
||||
const auto collection = pp::app::make_document_export_collection_target("D:/Paint", "demo", "_layers");
|
||||
const auto stem = pp::app::make_document_export_stem_target("D:/Exports", "demo");
|
||||
|
||||
PP_EXPECT(harness, file);
|
||||
PP_EXPECT(harness, collection);
|
||||
PP_EXPECT(harness, stem);
|
||||
PP_EXPECT(harness, pp::app::execute_document_export_file(file.value(), services).ok());
|
||||
PP_EXPECT(
|
||||
harness,
|
||||
pp::app::execute_document_export_stem(
|
||||
pp::app::DocumentExportCollectionKind::layers,
|
||||
stem.value(),
|
||||
services)
|
||||
.ok());
|
||||
PP_EXPECT(
|
||||
harness,
|
||||
pp::app::execute_document_export_stem(
|
||||
pp::app::DocumentExportCollectionKind::animation_frames,
|
||||
stem.value(),
|
||||
services)
|
||||
.ok());
|
||||
PP_EXPECT(
|
||||
harness,
|
||||
pp::app::execute_document_export_collection(
|
||||
pp::app::DocumentExportCollectionKind::layers,
|
||||
collection.value(),
|
||||
services)
|
||||
.ok());
|
||||
PP_EXPECT(
|
||||
harness,
|
||||
pp::app::execute_document_export_collection(
|
||||
pp::app::DocumentExportCollectionKind::animation_frames,
|
||||
collection.value(),
|
||||
services)
|
||||
.ok());
|
||||
PP_EXPECT(harness, pp::app::execute_document_export_depth("demo", services).ok());
|
||||
PP_EXPECT(harness, pp::app::execute_document_export_cube_faces("demo", services).ok());
|
||||
|
||||
PP_EXPECT(harness, services.create_dir_calls == 2);
|
||||
PP_EXPECT(harness, services.equirectangular_exports == 1);
|
||||
PP_EXPECT(harness, services.layer_stem_exports == 1);
|
||||
PP_EXPECT(harness, services.layer_collection_exports == 1);
|
||||
PP_EXPECT(harness, services.frame_stem_exports == 1);
|
||||
PP_EXPECT(harness, services.frame_collection_exports == 1);
|
||||
PP_EXPECT(harness, services.depth_exports == 1);
|
||||
PP_EXPECT(harness, services.cube_exports == 1);
|
||||
PP_EXPECT(
|
||||
harness,
|
||||
services.call_order
|
||||
== "equirect;layers-stem;frames-stem;layers-collection;frames-collection;depth;cube;");
|
||||
}
|
||||
|
||||
void export_executor_preserves_failed_directory_creation(pp::tests::Harness& harness)
|
||||
{
|
||||
FakeDocumentExportServices services;
|
||||
services.create_directory_result = false;
|
||||
const auto collection = pp::app::make_document_export_collection_target("D:/Paint", "demo", "_layers");
|
||||
|
||||
PP_EXPECT(harness, collection);
|
||||
PP_EXPECT(
|
||||
harness,
|
||||
pp::app::execute_document_export_collection(
|
||||
pp::app::DocumentExportCollectionKind::layers,
|
||||
collection.value(),
|
||||
services)
|
||||
.ok());
|
||||
PP_EXPECT(harness, services.create_dir_calls == 1);
|
||||
PP_EXPECT(harness, services.layer_collection_exports == 0);
|
||||
PP_EXPECT(harness, services.call_order.empty());
|
||||
}
|
||||
|
||||
void export_executor_rejects_malformed_targets(pp::tests::Harness& harness)
|
||||
{
|
||||
FakeDocumentExportServices services;
|
||||
|
||||
PP_EXPECT(
|
||||
harness,
|
||||
!pp::app::execute_document_export_file(pp::app::DocumentExportFileTarget {}, services).ok());
|
||||
PP_EXPECT(
|
||||
harness,
|
||||
!pp::app::execute_document_export_stem(
|
||||
pp::app::DocumentExportCollectionKind::layers,
|
||||
pp::app::DocumentExportStemTarget {},
|
||||
services)
|
||||
.ok());
|
||||
PP_EXPECT(
|
||||
harness,
|
||||
!pp::app::execute_document_export_collection(
|
||||
pp::app::DocumentExportCollectionKind::animation_frames,
|
||||
pp::app::DocumentExportCollectionTarget {},
|
||||
services)
|
||||
.ok());
|
||||
PP_EXPECT(harness, !pp::app::execute_document_export_depth("", services).ok());
|
||||
PP_EXPECT(harness, !pp::app::execute_document_export_cube_faces("", services).ok());
|
||||
PP_EXPECT(harness, services.call_order.empty());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main()
|
||||
@@ -271,5 +449,10 @@ int main()
|
||||
harness.run("export menu reports missing canvas for unlicensed image exports", export_menu_reports_missing_canvas_for_unlicensed_image_exports);
|
||||
harness.run("export menu executor dispatches all dialog actions", export_menu_executor_dispatches_all_dialog_actions);
|
||||
harness.run("export menu executor preserves blocked and unavailable actions", export_menu_executor_preserves_blocked_and_unavailable_actions);
|
||||
harness.run(
|
||||
"export executor dispatches file stem collection and named exports",
|
||||
export_executor_dispatches_file_stem_collection_and_named_exports);
|
||||
harness.run("export executor preserves failed directory creation", export_executor_preserves_failed_directory_creation);
|
||||
harness.run("export executor rejects malformed targets", export_executor_rejects_malformed_targets);
|
||||
return harness.finish();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user