Centralize legacy document file saves
This commit is contained in:
@@ -587,8 +587,9 @@ Known local toolchain state:
|
|||||||
close-request executor dispatch/no-op preservation, document-save executor
|
close-request executor dispatch/no-op preservation, document-save executor
|
||||||
dispatch/no-op preservation, save-before-workflow executor dispatch,
|
dispatch/no-op preservation, save-before-workflow executor dispatch,
|
||||||
new-document target/resolution/overwrite planning and executor dispatch,
|
new-document target/resolution/overwrite planning and executor dispatch,
|
||||||
document file target, combined save-file overwrite planning, and save-version
|
document file target, combined save-file overwrite planning and executor
|
||||||
target decisions without requiring a window, canvas, or message box.
|
dispatch, plus save-version target decisions and executor validation without
|
||||||
|
requiring a window, canvas, or message box.
|
||||||
- `src/legacy_document_open_services.*` is the current app-shell bridge between
|
- `src/legacy_document_open_services.*` is the current app-shell bridge between
|
||||||
`pp_app_core` document-open plans and live ABR/PPBR import prompts,
|
`pp_app_core` document-open plans and live ABR/PPBR import prompts,
|
||||||
unsaved-project discard prompts, project opening, layer UI refresh, title
|
unsaved-project discard prompts, project opening, layer UI refresh, title
|
||||||
@@ -599,8 +600,11 @@ Known local toolchain state:
|
|||||||
save dialogs, save-version routing, existing-project saves, and
|
save dialogs, save-version routing, existing-project saves, and
|
||||||
save-before-workflow prompts. It also bridges accepted new-document plans to
|
save-before-workflow prompts. It also bridges accepted new-document plans to
|
||||||
legacy canvas resize/layer setup, overwrite prompts, title updates, and
|
legacy canvas resize/layer setup, overwrite prompts, title updates, and
|
||||||
keyboard/dialog cleanup. Retained legacy UI/canvas execution remains tracked
|
keyboard/dialog cleanup. Accepted Save As and Save Version plans now also
|
||||||
by `DEBT-0040` and `DEBT-0041`.
|
route through this bridge before reaching legacy project-save execution,
|
||||||
|
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_history_services.*` is the current app-shell bridge between
|
- `src/legacy_history_services.*` is the current app-shell bridge between
|
||||||
`pp_app_core` history plans and legacy `ActionManager`; toolbar and
|
`pp_app_core` history plans and legacy `ActionManager`; toolbar and
|
||||||
`NodeCanvas` hotkeys share it while document-history extraction remains
|
`NodeCanvas` hotkeys share it while document-history extraction remains
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ agent or engineer to remove them without reconstructing context from chat.
|
|||||||
| DEBT-0039 | Open | Modernization | Document-open planning and execution dispatch now consume pure `pp_app_core` through `App::open_document`, `pano_cli plan-open-route`, `DocumentOpenServices`, and `src/legacy_document_open_services.*`, but the bridge still opens ABR/PPBR import prompts, launches legacy brush preset import threads, applies unsaved-project discard prompts, calls legacy project-open execution, refreshes layer UI, updates the app title, and clears legacy history directly | Preserve current file-open/import behavior while document loading and brush import move toward app/document/asset/UI services | `pp_app_core_document_route_tests`; `pp_app_core_document_session_tests`; `pano_cli plan-open-route --path D:/Paint/Scenes/demo.ppi --unsaved`; `pano_cli plan-open-route --path D:/Paint/Brushes/clouds.ABR --unsaved`; `ctest --preset desktop-fast --build-config Debug` | Brush import prompting/execution, project-open execution, unsaved-project discard prompting, layer refresh, title updates, and history clearing are owned by injected app/document/asset/UI services with `App::open_document` acting only as an adapter |
|
| DEBT-0039 | Open | Modernization | Document-open planning and execution dispatch now consume pure `pp_app_core` through `App::open_document`, `pano_cli plan-open-route`, `DocumentOpenServices`, and `src/legacy_document_open_services.*`, but the bridge still opens ABR/PPBR import prompts, launches legacy brush preset import threads, applies unsaved-project discard prompts, calls legacy project-open execution, refreshes layer UI, updates the app title, and clears legacy history directly | Preserve current file-open/import behavior while document loading and brush import move toward app/document/asset/UI services | `pp_app_core_document_route_tests`; `pp_app_core_document_session_tests`; `pano_cli plan-open-route --path D:/Paint/Scenes/demo.ppi --unsaved`; `pano_cli plan-open-route --path D:/Paint/Brushes/clouds.ABR --unsaved`; `ctest --preset desktop-fast --build-config Debug` | Brush import prompting/execution, project-open execution, unsaved-project discard prompting, layer refresh, title updates, and history clearing are owned by injected app/document/asset/UI services with `App::open_document` acting only as an adapter |
|
||||||
| 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-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-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 |
|
||||||
|
|
||||||
## Closed Debt
|
## Closed Debt
|
||||||
|
|
||||||
|
|||||||
@@ -744,6 +744,12 @@ app-core new-document executor and `src/legacy_document_session_services.*`,
|
|||||||
preserving target overwrite prompts, legacy canvas resize/layer setup, history
|
preserving target overwrite prompts, legacy canvas resize/layer setup, history
|
||||||
clearing, title updates, dirty/new-document flag mutation, and keyboard/dialog
|
clearing, title updates, dirty/new-document flag mutation, and keyboard/dialog
|
||||||
cleanup while retained execution remains tracked under `DEBT-0041`.
|
cleanup while retained execution remains tracked under `DEBT-0041`.
|
||||||
|
`App::dialog_save` and `App::dialog_save_ver` now route accepted Save As and
|
||||||
|
Save Version plans through app-core document file/version save executors and
|
||||||
|
`src/legacy_document_session_services.*`, preserving overwrite prompts,
|
||||||
|
legacy `Canvas::project_save`, app document field updates, title updates, and
|
||||||
|
keyboard/dialog cleanup while retained execution remains tracked under
|
||||||
|
`DEBT-0042`.
|
||||||
|
|
||||||
Implementation tasks:
|
Implementation tasks:
|
||||||
|
|
||||||
@@ -1299,6 +1305,15 @@ Results:
|
|||||||
- Focused new-document/session CTest coverage passed for
|
- Focused new-document/session CTest coverage passed for
|
||||||
`pp_app_core_document_session_tests`, `pano_cli_plan_new_document_*`, and
|
`pp_app_core_document_session_tests`, `pano_cli_plan_new_document_*`, and
|
||||||
`pano_cli_simulate_app_session_*` smoke tests after the live bridge split.
|
`pano_cli_simulate_app_session_*` smoke tests after the live bridge split.
|
||||||
|
- `PanoPainter`, `pp_app_core_document_session_tests`, and `pano_cli` built
|
||||||
|
after accepted Save As and Save Version execution moved behind document
|
||||||
|
file/version save services. A clean rebuild was required once because MSVC
|
||||||
|
reported the known Debug PDB `LNK1103` corruption, after which the build
|
||||||
|
passed.
|
||||||
|
- Focused Save As/Version/session CTest coverage passed for
|
||||||
|
`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.
|
||||||
- `pp_app_core_document_recording_tests` passed, covering recording start/stop,
|
- `pp_app_core_document_recording_tests` passed, covering recording start/stop,
|
||||||
clear, platform recorded-file cleanup, frame-count reset, export progress
|
clear, platform recorded-file cleanup, frame-count reset, export progress
|
||||||
totals, and oversized progress-total clamping.
|
totals, and oversized progress-total clamping.
|
||||||
|
|||||||
@@ -106,6 +106,21 @@ struct DocumentFileSavePlan {
|
|||||||
DocumentFileWriteDecision write_decision = DocumentFileWriteDecision::save_now;
|
DocumentFileWriteDecision write_decision = DocumentFileWriteDecision::save_now;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class DocumentFileSaveServices {
|
||||||
|
public:
|
||||||
|
virtual ~DocumentFileSaveServices() = default;
|
||||||
|
|
||||||
|
virtual void save_document_file(const DocumentFileSavePlan& plan) = 0;
|
||||||
|
virtual void prompt_overwrite_document_file(const DocumentFileSavePlan& plan) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DocumentVersionSaveServices {
|
||||||
|
public:
|
||||||
|
virtual ~DocumentVersionSaveServices() = default;
|
||||||
|
|
||||||
|
virtual void save_document_version(const DocumentVersionTarget& target) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
struct NewDocumentPlan {
|
struct NewDocumentPlan {
|
||||||
DocumentFileTarget target;
|
DocumentFileTarget target;
|
||||||
int resolution = 0;
|
int resolution = 0;
|
||||||
@@ -339,6 +354,22 @@ template <typename ExistsPredicate>
|
|||||||
return pp::foundation::Result<DocumentFileSavePlan>::success(std::move(plan));
|
return pp::foundation::Result<DocumentFileSavePlan>::success(std::move(plan));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] inline pp::foundation::Status execute_document_file_save_plan(
|
||||||
|
const DocumentFileSavePlan& plan,
|
||||||
|
DocumentFileSaveServices& services)
|
||||||
|
{
|
||||||
|
switch (plan.write_decision) {
|
||||||
|
case DocumentFileWriteDecision::save_now:
|
||||||
|
services.save_document_file(plan);
|
||||||
|
return pp::foundation::Status::success();
|
||||||
|
case DocumentFileWriteDecision::prompt_overwrite:
|
||||||
|
services.prompt_overwrite_document_file(plan);
|
||||||
|
return pp::foundation::Status::success();
|
||||||
|
}
|
||||||
|
|
||||||
|
return pp::foundation::Status::invalid_argument("unknown document file save write decision");
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] constexpr pp::foundation::Result<int> document_resolution_from_index(int index) noexcept
|
[[nodiscard]] constexpr pp::foundation::Result<int> document_resolution_from_index(int index) noexcept
|
||||||
{
|
{
|
||||||
constexpr std::array<int, 6> resolutions{ 512, 1024, 1536, 2048, 4096, 8192 };
|
constexpr std::array<int, 6> resolutions{ 512, 1024, 1536, 2048, 4096, 8192 };
|
||||||
@@ -471,4 +502,16 @@ template <typename ExistsPredicate>
|
|||||||
pp::foundation::Status::out_of_range("no available document version target"));
|
pp::foundation::Status::out_of_range("no available document version target"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] inline pp::foundation::Status execute_document_version_save(
|
||||||
|
const DocumentVersionTarget& target,
|
||||||
|
DocumentVersionSaveServices& services)
|
||||||
|
{
|
||||||
|
if (target.name.empty() || target.path.empty()) {
|
||||||
|
return pp::foundation::Status::invalid_argument("document version target requires a name and path");
|
||||||
|
}
|
||||||
|
|
||||||
|
services.save_document_version(target);
|
||||||
|
return pp::foundation::Status::success();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -280,11 +280,9 @@ void App::dialog_save_ver()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
doc_name = target.value().name;
|
const auto status = pp::panopainter::execute_legacy_document_version_save(*this, target.value());
|
||||||
doc_path = target.value().path;
|
if (!status.ok())
|
||||||
canvas->m_canvas->m_unsaved = true;
|
LOG("Document version save action failed: %s", status.message);
|
||||||
title_update();
|
|
||||||
canvas->m_canvas->project_save(doc_path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::save_document(pp::app::DocumentSaveIntent intent)
|
void App::save_document(pp::app::DocumentSaveIntent intent)
|
||||||
@@ -332,34 +330,9 @@ void App::dialog_save()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto action = [this, dialog, plan = plan.value()] {
|
const auto status = pp::panopainter::execute_legacy_document_file_save_plan(*this, plan.value(), dialog);
|
||||||
canvas->m_canvas->project_save(plan.target.path);
|
if (!status.ok())
|
||||||
doc_name = plan.target.name;
|
LOG("Document file save action failed: %s", status.message);
|
||||||
doc_path = plan.target.path;
|
|
||||||
doc_dir = plan.target.directory;
|
|
||||||
title_update();
|
|
||||||
dialog->destroy();
|
|
||||||
App::I->hideKeyboard();
|
|
||||||
};
|
|
||||||
|
|
||||||
if (plan.value().write_decision == pp::app::DocumentFileWriteDecision::prompt_overwrite)
|
|
||||||
{
|
|
||||||
// ask confirm is file already exist
|
|
||||||
auto msgbox = new NodeMessageBox();
|
|
||||||
msgbox->set_manager(&layout);
|
|
||||||
msgbox->init();
|
|
||||||
msgbox->m_title->set_text("Warning");
|
|
||||||
msgbox->m_message->set_text(("Are you sure you want to overwrite " + plan.value().target.name + "?").c_str());
|
|
||||||
msgbox->btn_ok->on_click = [this, msgbox, action](Node*) {
|
|
||||||
action();
|
|
||||||
msgbox->destroy();
|
|
||||||
};
|
|
||||||
layout[main_id]->add_child(msgbox);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
action();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
dialog->btn_cancel->on_click = [this, dialog](Node*)
|
dialog->btn_cancel->on_click = [this, dialog](Node*)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -71,6 +71,74 @@ private:
|
|||||||
std::shared_ptr<NodeDialogNewDoc> dialog_;
|
std::shared_ptr<NodeDialogNewDoc> dialog_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void save_legacy_document_file(
|
||||||
|
App& app,
|
||||||
|
const pp::app::DocumentFileSavePlan& plan,
|
||||||
|
const std::shared_ptr<NodeDialogSave>& dialog)
|
||||||
|
{
|
||||||
|
app.canvas->m_canvas->project_save(plan.target.path);
|
||||||
|
app.doc_name = plan.target.name;
|
||||||
|
app.doc_path = plan.target.path;
|
||||||
|
app.doc_dir = plan.target.directory;
|
||||||
|
app.title_update();
|
||||||
|
dialog->destroy();
|
||||||
|
App::I->hideKeyboard();
|
||||||
|
}
|
||||||
|
|
||||||
|
class LegacyDocumentFileSaveServices final : public pp::app::DocumentFileSaveServices {
|
||||||
|
public:
|
||||||
|
LegacyDocumentFileSaveServices(App& app, std::shared_ptr<NodeDialogSave> dialog) noexcept
|
||||||
|
: app_(app)
|
||||||
|
, dialog_(std::move(dialog))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void save_document_file(const pp::app::DocumentFileSavePlan& plan) override
|
||||||
|
{
|
||||||
|
save_legacy_document_file(app_, plan, dialog_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void prompt_overwrite_document_file(const pp::app::DocumentFileSavePlan& plan) override
|
||||||
|
{
|
||||||
|
auto msgbox = new NodeMessageBox();
|
||||||
|
msgbox->set_manager(&app_.layout);
|
||||||
|
msgbox->init();
|
||||||
|
msgbox->m_title->set_text("Warning");
|
||||||
|
msgbox->m_message->set_text(("Are you sure you want to overwrite " + plan.target.name + "?").c_str());
|
||||||
|
auto* app = &app_;
|
||||||
|
auto dialog = dialog_;
|
||||||
|
msgbox->btn_ok->on_click = [app, msgbox, dialog, plan](Node*) {
|
||||||
|
save_legacy_document_file(*app, plan, dialog);
|
||||||
|
msgbox->destroy();
|
||||||
|
};
|
||||||
|
app_.layout[app_.main_id]->add_child(msgbox);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
App& app_;
|
||||||
|
std::shared_ptr<NodeDialogSave> dialog_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LegacyDocumentVersionSaveServices final : public pp::app::DocumentVersionSaveServices {
|
||||||
|
public:
|
||||||
|
explicit LegacyDocumentVersionSaveServices(App& app) noexcept
|
||||||
|
: app_(app)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void save_document_version(const pp::app::DocumentVersionTarget& target) override
|
||||||
|
{
|
||||||
|
app_.doc_name = target.name;
|
||||||
|
app_.doc_path = target.path;
|
||||||
|
app_.canvas->m_canvas->m_unsaved = true;
|
||||||
|
app_.title_update();
|
||||||
|
app_.canvas->m_canvas->project_save(app_.doc_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
App& app_;
|
||||||
|
};
|
||||||
|
|
||||||
class LegacyCloseRequestServices final : public pp::app::CloseRequestServices {
|
class LegacyCloseRequestServices final : public pp::app::CloseRequestServices {
|
||||||
public:
|
public:
|
||||||
LegacyCloseRequestServices(App& app, bool& dialog_already_opened) noexcept
|
LegacyCloseRequestServices(App& app, bool& dialog_already_opened) noexcept
|
||||||
@@ -213,4 +281,21 @@ pp::foundation::Status execute_legacy_new_document_plan(
|
|||||||
return pp::app::execute_new_document_plan(plan, services);
|
return pp::app::execute_new_document_plan(plan, services);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pp::foundation::Status execute_legacy_document_file_save_plan(
|
||||||
|
App& app,
|
||||||
|
const pp::app::DocumentFileSavePlan& plan,
|
||||||
|
std::shared_ptr<NodeDialogSave> dialog)
|
||||||
|
{
|
||||||
|
LegacyDocumentFileSaveServices services(app, std::move(dialog));
|
||||||
|
return pp::app::execute_document_file_save_plan(plan, services);
|
||||||
|
}
|
||||||
|
|
||||||
|
pp::foundation::Status execute_legacy_document_version_save(
|
||||||
|
App& app,
|
||||||
|
const pp::app::DocumentVersionTarget& target)
|
||||||
|
{
|
||||||
|
LegacyDocumentVersionSaveServices services(app);
|
||||||
|
return pp::app::execute_document_version_save(target, services);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace pp::panopainter
|
} // namespace pp::panopainter
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
class App;
|
class App;
|
||||||
class NodeDialogNewDoc;
|
class NodeDialogNewDoc;
|
||||||
|
class NodeDialogSave;
|
||||||
|
|
||||||
namespace pp::panopainter {
|
namespace pp::panopainter {
|
||||||
|
|
||||||
@@ -30,4 +31,13 @@ namespace pp::panopainter {
|
|||||||
const pp::app::NewDocumentPlan& plan,
|
const pp::app::NewDocumentPlan& plan,
|
||||||
std::shared_ptr<NodeDialogNewDoc> dialog);
|
std::shared_ptr<NodeDialogNewDoc> dialog);
|
||||||
|
|
||||||
|
[[nodiscard]] pp::foundation::Status execute_legacy_document_file_save_plan(
|
||||||
|
App& app,
|
||||||
|
const pp::app::DocumentFileSavePlan& plan,
|
||||||
|
std::shared_ptr<NodeDialogSave> dialog);
|
||||||
|
|
||||||
|
[[nodiscard]] pp::foundation::Status execute_legacy_document_version_save(
|
||||||
|
App& app,
|
||||||
|
const pp::app::DocumentVersionTarget& target);
|
||||||
|
|
||||||
} // namespace pp::panopainter
|
} // namespace pp::panopainter
|
||||||
|
|||||||
@@ -132,6 +132,44 @@ public:
|
|||||||
std::string call_order;
|
std::string call_order;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class FakeDocumentFileSaveServices final : public pp::app::DocumentFileSaveServices {
|
||||||
|
public:
|
||||||
|
void save_document_file(const pp::app::DocumentFileSavePlan& plan) override
|
||||||
|
{
|
||||||
|
saves += 1;
|
||||||
|
last_path = plan.target.path;
|
||||||
|
call_order += "save-file;";
|
||||||
|
}
|
||||||
|
|
||||||
|
void prompt_overwrite_document_file(const pp::app::DocumentFileSavePlan& plan) override
|
||||||
|
{
|
||||||
|
overwrite_prompts += 1;
|
||||||
|
last_path = plan.target.path;
|
||||||
|
call_order += "overwrite-file;";
|
||||||
|
}
|
||||||
|
|
||||||
|
int saves = 0;
|
||||||
|
int overwrite_prompts = 0;
|
||||||
|
std::string last_path;
|
||||||
|
std::string call_order;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FakeDocumentVersionSaveServices final : public pp::app::DocumentVersionSaveServices {
|
||||||
|
public:
|
||||||
|
void save_document_version(const pp::app::DocumentVersionTarget& target) override
|
||||||
|
{
|
||||||
|
saves += 1;
|
||||||
|
last_name = target.name;
|
||||||
|
last_path = target.path;
|
||||||
|
call_order += "save-version;";
|
||||||
|
}
|
||||||
|
|
||||||
|
int saves = 0;
|
||||||
|
std::string last_name;
|
||||||
|
std::string last_path;
|
||||||
|
std::string call_order;
|
||||||
|
};
|
||||||
|
|
||||||
[[nodiscard]] pp::app::DocumentOpenRoute project_route()
|
[[nodiscard]] pp::app::DocumentOpenRoute project_route()
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
@@ -506,6 +544,29 @@ void document_file_save_plan_rejects_empty_name(pp::tests::Harness& harness)
|
|||||||
PP_EXPECT(harness, plan.status().code == pp::foundation::StatusCode::invalid_argument);
|
PP_EXPECT(harness, plan.status().code == pp::foundation::StatusCode::invalid_argument);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void document_file_save_executor_dispatches_save_and_overwrite_paths(pp::tests::Harness& harness)
|
||||||
|
{
|
||||||
|
FakeDocumentFileSaveServices services;
|
||||||
|
auto save = pp::app::plan_document_file_save(
|
||||||
|
"D:/Paint",
|
||||||
|
"demo",
|
||||||
|
[](const std::string&) { return false; });
|
||||||
|
auto overwrite = pp::app::plan_document_file_save(
|
||||||
|
"D:/Paint",
|
||||||
|
"demo",
|
||||||
|
[](const std::string& path) { return path == "D:/Paint/demo.ppi"; });
|
||||||
|
|
||||||
|
PP_EXPECT(harness, save);
|
||||||
|
PP_EXPECT(harness, overwrite);
|
||||||
|
PP_EXPECT(harness, pp::app::execute_document_file_save_plan(save.value(), services).ok());
|
||||||
|
PP_EXPECT(harness, pp::app::execute_document_file_save_plan(overwrite.value(), services).ok());
|
||||||
|
|
||||||
|
PP_EXPECT(harness, services.saves == 1);
|
||||||
|
PP_EXPECT(harness, services.overwrite_prompts == 1);
|
||||||
|
PP_EXPECT(harness, services.last_path == "D:/Paint/demo.ppi");
|
||||||
|
PP_EXPECT(harness, services.call_order == "save-file;overwrite-file;");
|
||||||
|
}
|
||||||
|
|
||||||
void document_resolution_index_maps_legacy_choices(pp::tests::Harness& harness)
|
void document_resolution_index_maps_legacy_choices(pp::tests::Harness& harness)
|
||||||
{
|
{
|
||||||
const auto first = pp::app::document_resolution_from_index(0);
|
const auto first = pp::app::document_resolution_from_index(0);
|
||||||
@@ -654,6 +715,32 @@ void document_version_target_reports_when_no_slots_remain(pp::tests::Harness& ha
|
|||||||
PP_EXPECT(harness, target.status().code == pp::foundation::StatusCode::out_of_range);
|
PP_EXPECT(harness, target.status().code == pp::foundation::StatusCode::out_of_range);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void document_version_executor_dispatches_and_rejects_empty_targets(pp::tests::Harness& harness)
|
||||||
|
{
|
||||||
|
FakeDocumentVersionSaveServices services;
|
||||||
|
const pp::app::DocumentVersionTarget valid {
|
||||||
|
.name = "demo.02",
|
||||||
|
.path = "D:/Paint/demo.02.ppi",
|
||||||
|
};
|
||||||
|
const pp::app::DocumentVersionTarget missing_name {
|
||||||
|
.name = "",
|
||||||
|
.path = "D:/Paint/demo.02.ppi",
|
||||||
|
};
|
||||||
|
const pp::app::DocumentVersionTarget missing_path {
|
||||||
|
.name = "demo.02",
|
||||||
|
.path = "",
|
||||||
|
};
|
||||||
|
|
||||||
|
PP_EXPECT(harness, pp::app::execute_document_version_save(valid, services).ok());
|
||||||
|
PP_EXPECT(harness, !pp::app::execute_document_version_save(missing_name, services).ok());
|
||||||
|
PP_EXPECT(harness, !pp::app::execute_document_version_save(missing_path, services).ok());
|
||||||
|
|
||||||
|
PP_EXPECT(harness, services.saves == 1);
|
||||||
|
PP_EXPECT(harness, services.last_name == "demo.02");
|
||||||
|
PP_EXPECT(harness, services.last_path == "D:/Paint/demo.02.ppi");
|
||||||
|
PP_EXPECT(harness, services.call_order == "save-version;");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
@@ -686,6 +773,7 @@ int main()
|
|||||||
"document file save plan combines target and write decision",
|
"document file save plan combines target and write decision",
|
||||||
document_file_save_plan_combines_target_and_write_decision);
|
document_file_save_plan_combines_target_and_write_decision);
|
||||||
harness.run("document file save plan rejects empty name", document_file_save_plan_rejects_empty_name);
|
harness.run("document file save plan rejects empty name", document_file_save_plan_rejects_empty_name);
|
||||||
|
harness.run("document file save executor dispatches save and overwrite paths", document_file_save_executor_dispatches_save_and_overwrite_paths);
|
||||||
harness.run("document resolution index maps legacy choices", document_resolution_index_maps_legacy_choices);
|
harness.run("document resolution index maps legacy choices", document_resolution_index_maps_legacy_choices);
|
||||||
harness.run("document resolution index rejects out of range", document_resolution_index_rejects_out_of_range);
|
harness.run("document resolution index rejects out of range", document_resolution_index_rejects_out_of_range);
|
||||||
harness.run(
|
harness.run(
|
||||||
@@ -701,5 +789,6 @@ int main()
|
|||||||
"document version target preserves legacy nonnumeric suffix handling",
|
"document version target preserves legacy nonnumeric suffix handling",
|
||||||
document_version_target_preserves_legacy_nonnumeric_suffix_handling);
|
document_version_target_preserves_legacy_nonnumeric_suffix_handling);
|
||||||
harness.run("document version target reports when no slots remain", document_version_target_reports_when_no_slots_remain);
|
harness.run("document version target reports when no slots remain", document_version_target_reports_when_no_slots_remain);
|
||||||
|
harness.run("document version executor dispatches and rejects empty targets", document_version_executor_dispatches_and_rejects_empty_targets);
|
||||||
return harness.finish();
|
return harness.finish();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user