Centralize legacy document session bridge
This commit is contained in:
@@ -86,6 +86,8 @@ set(PP_PANOPAINTER_APP_SOURCES
|
||||
src/legacy_cloud_services.h
|
||||
src/legacy_document_open_services.cpp
|
||||
src/legacy_document_open_services.h
|
||||
src/legacy_document_session_services.cpp
|
||||
src/legacy_document_session_services.h
|
||||
src/platform_legacy/legacy_platform_services.cpp
|
||||
src/platform_legacy/legacy_platform_services.h
|
||||
src/version.cpp
|
||||
|
||||
@@ -584,14 +584,21 @@ Known local toolchain state:
|
||||
progress-total decisions.
|
||||
- `pp_app_core_document_session_tests` covers clean and dirty app session,
|
||||
document-open action planning and executor dispatch/rejection, save-request,
|
||||
save-before-workflow, new-document target/resolution/overwrite planning,
|
||||
document file target, combined save-file overwrite planning, and save-version
|
||||
target decisions without requiring a window, canvas, or message box.
|
||||
close-request executor dispatch/no-op preservation, document-save executor
|
||||
dispatch/no-op preservation, save-before-workflow executor dispatch,
|
||||
new-document target/resolution/overwrite planning, document file target,
|
||||
combined save-file overwrite planning, and save-version target decisions
|
||||
without requiring a window, canvas, or message box.
|
||||
- `src/legacy_document_open_services.*` is the current app-shell bridge between
|
||||
`pp_app_core` document-open plans and live ABR/PPBR import prompts,
|
||||
unsaved-project discard prompts, project opening, layer UI refresh, title
|
||||
updates, and action-history clearing; remaining legacy execution ownership is
|
||||
tracked by `DEBT-0039`.
|
||||
- `src/legacy_document_session_services.*` is the current app-shell bridge
|
||||
between `pp_app_core` document-session decisions and live close prompts,
|
||||
save dialogs, save-version routing, existing-project saves, and
|
||||
save-before-workflow prompts; retained legacy UI/canvas execution remains
|
||||
tracked by `DEBT-0040`.
|
||||
- `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
|
||||
|
||||
@@ -57,6 +57,7 @@ agent or engineer to remove them without reconstructing context from chat.
|
||||
| DEBT-0037 | Open | Modernization | Recording lifecycle/export planning and execution dispatch now consume pure `pp_app_core` through `App::rec_start`, `App::rec_stop`, `App::rec_clear`, `App::rec_export`, `pano_cli plan-recording-session`, and the `RecordingServices` boundary; live execution is centralized in `src/legacy_recording_services.*`, but the bridge still owns legacy recording thread startup/shutdown, platform recorded-file cleanup, progress UI, PBO readback through `App::rec_loop`, and `MP4Encoder::write_mp4` execution | Preserve current timelapse/MP4 behavior while recording moves toward app/document/renderer/video services | `pp_app_core_document_recording_tests`; `pano_cli plan-recording-session --running --frame-count 12`; `pano_cli plan-recording-session --platform-clears-files`; `ctest --preset desktop-fast --build-config Debug` | Recording thread lifecycle, frame readback, platform cleanup, progress reporting, and MP4 writing are owned by injected app/renderer/video services with `App` methods acting only as adapters |
|
||||
| DEBT-0038 | Open | Modernization | Cloud upload/browse/bulk planning and execution dispatch now consume pure `pp_app_core` through `App::cloud_upload`, `App::cloud_upload_all`, `App::cloud_browse`, `pano_cli plan-cloud-upload`, `pano_cli plan-cloud-upload-all`, `pano_cli plan-cloud-browse`, and the `CloudServices` boundary; live execution is centralized in `src/legacy_cloud_services.*`, but the bridge still uses legacy save-before-upload, `upload`/`download` network helpers, progress/message UI, OpenGL context guarding, `NodeDialogCloud`, `Canvas` project open, layer refresh, and `ActionManager` reset | Preserve current cloud behavior while cloud/network/document import flows move toward app/document/platform services | `pp_app_core_document_cloud_tests`; `pano_cli plan-cloud-upload --new-document --unsaved`; `pano_cli plan-cloud-browse --selected-file demo.ppi`; `pano_cli plan-cloud-upload-all --file-count 3`; `ctest --preset desktop-fast --build-config Debug` | Cloud upload/download, save-before-upload, progress reporting, cloud browse dialog, downloaded project opening, layer refresh, OpenGL context ownership, and action-history reset are owned by injected app/document/network/platform/renderer services with `App` methods acting only as adapters |
|
||||
| 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 |
|
||||
|
||||
## Closed Debt
|
||||
|
||||
|
||||
@@ -733,6 +733,12 @@ behind the renderer boundary.
|
||||
unsaved-project discard prompts, project open, layer refresh, title updates,
|
||||
and history clearing while those live effects remain tracked under
|
||||
`DEBT-0039`.
|
||||
`App::request_close`, `App::save_document`, and
|
||||
`App::continue_document_workflow_after_optional_save` now route through
|
||||
app-core document-session executors and `src/legacy_document_session_services.*`,
|
||||
preserving close prompts, save dialogs, save-version routing, existing-project
|
||||
save execution, and dirty-workflow save-before-continue prompts while retained
|
||||
legacy UI/canvas behavior remains tracked under `DEBT-0040`.
|
||||
|
||||
Implementation tasks:
|
||||
|
||||
@@ -1272,6 +1278,15 @@ Results:
|
||||
- Focused document-open CTest coverage passed for
|
||||
`pp_app_core_document_route_tests`, `pp_app_core_document_session_tests`, and
|
||||
the `pano_cli_plan_open_route_*` smoke tests after the live bridge split.
|
||||
- `PanoPainter`, `pp_app_core_document_session_tests`, and `pano_cli` built
|
||||
after close request, document save, and dirty-workflow continuation execution
|
||||
moved behind document-session services. A clean rebuild was required once
|
||||
because MSVC reported the known Debug PDB `LNK1103` corruption, after which
|
||||
the build passed.
|
||||
- Focused document-session CTest coverage passed for
|
||||
`pp_app_core_document_session_tests`, `pano_cli_simulate_app_session_*`, and
|
||||
`pano_cli_plan_document_file/version_*` smoke tests 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.
|
||||
|
||||
29
src/app.cpp
29
src/app.cpp
@@ -11,6 +11,7 @@
|
||||
#include "app_core/document_route.h"
|
||||
#include "app_core/document_session.h"
|
||||
#include "legacy_document_open_services.h"
|
||||
#include "legacy_document_session_services.h"
|
||||
#include "legacy_recording_services.h"
|
||||
#include "platform_api/platform_services.h"
|
||||
#include "renderer_gl/opengl_capabilities.h"
|
||||
@@ -208,26 +209,14 @@ bool App::request_close()
|
||||
const auto close_decision = pp::app::plan_close_request(
|
||||
Canvas::I->m_unsaved,
|
||||
dialog_already_opened);
|
||||
if (close_decision == pp::app::CloseRequestDecision::close_now)
|
||||
return true;
|
||||
if (close_decision == pp::app::CloseRequestDecision::show_unsaved_prompt)
|
||||
{
|
||||
auto* m = layout[main_id]->add_child<NodeMessageBox>();
|
||||
m->m_title->set_text("Unsaved document");
|
||||
m->m_message->set_text("Do you want to close without saving?");
|
||||
m->btn_ok->m_text->set_text("Yes");
|
||||
m->btn_ok->on_click = [this](Node*) {
|
||||
request_app_close();
|
||||
Canvas::I->m_unsaved = false;
|
||||
};
|
||||
m->btn_cancel->m_text->set_text("No");
|
||||
m->btn_cancel->on_click = [this,m](Node*) {
|
||||
m->destroy();
|
||||
dialog_already_opened = false;
|
||||
};
|
||||
dialog_already_opened = true;
|
||||
}
|
||||
return false;
|
||||
const auto status = pp::panopainter::execute_legacy_close_request_decision(
|
||||
*this,
|
||||
close_decision,
|
||||
dialog_already_opened);
|
||||
if (!status.ok())
|
||||
LOG("Close request action failed: %s", status.message);
|
||||
|
||||
return close_decision == pp::app::CloseRequestDecision::close_now;
|
||||
}
|
||||
|
||||
void App::clear()
|
||||
|
||||
@@ -65,6 +65,31 @@ public:
|
||||
virtual void prompt_discard_unsaved_project(const DocumentOpenRoute& route) = 0;
|
||||
};
|
||||
|
||||
class CloseRequestServices {
|
||||
public:
|
||||
virtual ~CloseRequestServices() = default;
|
||||
|
||||
virtual void request_close_now() = 0;
|
||||
virtual void show_unsaved_close_prompt() = 0;
|
||||
};
|
||||
|
||||
class DocumentSaveServices {
|
||||
public:
|
||||
virtual ~DocumentSaveServices() = default;
|
||||
|
||||
virtual void show_save_dialog() = 0;
|
||||
virtual void save_existing_document() = 0;
|
||||
virtual void save_document_version() = 0;
|
||||
};
|
||||
|
||||
class DocumentWorkflowServices {
|
||||
public:
|
||||
virtual ~DocumentWorkflowServices() = default;
|
||||
|
||||
virtual void continue_workflow_now() = 0;
|
||||
virtual void prompt_save_before_continue() = 0;
|
||||
};
|
||||
|
||||
struct DocumentFileTarget {
|
||||
std::string name;
|
||||
std::string directory;
|
||||
@@ -160,6 +185,24 @@ struct NewDocumentPlan {
|
||||
: CloseRequestDecision::show_unsaved_prompt;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline pp::foundation::Status execute_close_request_decision(
|
||||
CloseRequestDecision decision,
|
||||
CloseRequestServices& services)
|
||||
{
|
||||
switch (decision) {
|
||||
case CloseRequestDecision::close_now:
|
||||
services.request_close_now();
|
||||
return pp::foundation::Status::success();
|
||||
case CloseRequestDecision::show_unsaved_prompt:
|
||||
services.show_unsaved_close_prompt();
|
||||
return pp::foundation::Status::success();
|
||||
case CloseRequestDecision::wait_for_existing_prompt:
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
return pp::foundation::Status::invalid_argument("unknown close request decision");
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr DocumentSaveDecision plan_document_save(
|
||||
bool is_new_document,
|
||||
bool has_unsaved_changes,
|
||||
@@ -191,6 +234,27 @@ struct NewDocumentPlan {
|
||||
return DocumentSaveDecision::no_op;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline pp::foundation::Status execute_document_save_decision(
|
||||
DocumentSaveDecision decision,
|
||||
DocumentSaveServices& services)
|
||||
{
|
||||
switch (decision) {
|
||||
case DocumentSaveDecision::no_op:
|
||||
return pp::foundation::Status::success();
|
||||
case DocumentSaveDecision::show_save_dialog:
|
||||
services.show_save_dialog();
|
||||
return pp::foundation::Status::success();
|
||||
case DocumentSaveDecision::save_existing:
|
||||
services.save_existing_document();
|
||||
return pp::foundation::Status::success();
|
||||
case DocumentSaveDecision::save_version:
|
||||
services.save_document_version();
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
return pp::foundation::Status::invalid_argument("unknown document save decision");
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr DocumentWorkflowDecision plan_document_workflow(
|
||||
bool has_canvas,
|
||||
bool has_unsaved_changes) noexcept
|
||||
@@ -204,6 +268,24 @@ struct NewDocumentPlan {
|
||||
: DocumentWorkflowDecision::continue_now;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline pp::foundation::Status execute_document_workflow_decision(
|
||||
DocumentWorkflowDecision decision,
|
||||
DocumentWorkflowServices& services)
|
||||
{
|
||||
switch (decision) {
|
||||
case DocumentWorkflowDecision::unavailable:
|
||||
return pp::foundation::Status::success();
|
||||
case DocumentWorkflowDecision::continue_now:
|
||||
services.continue_workflow_now();
|
||||
return pp::foundation::Status::success();
|
||||
case DocumentWorkflowDecision::prompt_save_before_continue:
|
||||
services.prompt_save_before_continue();
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
return pp::foundation::Status::invalid_argument("unknown document workflow decision");
|
||||
}
|
||||
|
||||
[[nodiscard]] inline pp::foundation::Result<DocumentFileTarget> make_document_file_target(
|
||||
std::string_view work_directory,
|
||||
std::string_view document_name)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "app_core/document_session.h"
|
||||
#include "legacy_document_canvas_services.h"
|
||||
#include "legacy_document_layer_services.h"
|
||||
#include "legacy_document_session_services.h"
|
||||
#include "legacy_history_services.h"
|
||||
#include "settings.h"
|
||||
#include "node_dialog_open.h"
|
||||
@@ -23,6 +24,8 @@
|
||||
#define MP4V2_NO_STDINT_DEFS
|
||||
#include <mp4v2/mp4v2.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
#ifdef __QUEST__
|
||||
#include "oculus_vr.h"
|
||||
#elif __WEB__
|
||||
@@ -138,34 +141,12 @@ void App::continue_document_workflow_after_optional_save(std::function<void()> a
|
||||
const bool has_canvas = canvas != nullptr;
|
||||
const bool has_unsaved_changes = has_canvas && Canvas::I->m_unsaved;
|
||||
const auto decision = pp::app::plan_document_workflow(has_canvas, has_unsaved_changes);
|
||||
switch (decision) {
|
||||
case pp::app::DocumentWorkflowDecision::unavailable:
|
||||
return;
|
||||
case pp::app::DocumentWorkflowDecision::continue_now:
|
||||
action();
|
||||
return;
|
||||
case pp::app::DocumentWorkflowDecision::prompt_save_before_continue:
|
||||
break;
|
||||
}
|
||||
|
||||
auto m = layout[main_id]->add_child<NodeMessageBox>();
|
||||
m->m_title->set_text("Unsaved document");
|
||||
m->m_message->set_text("Would you like to save this document before closing?");
|
||||
m->btn_ok->m_text->set_text("Yes");
|
||||
m->btn_cancel->m_text->set_text("No");
|
||||
m->btn_ok->on_click = [this, m, action](Node*) {
|
||||
Canvas::I->project_save([this, m, action](bool success) {
|
||||
if (success)
|
||||
action();
|
||||
else
|
||||
message_box("Saving Error", "There was a problem saving the document");
|
||||
});
|
||||
m->destroy();
|
||||
};
|
||||
m->btn_cancel->on_click = [m, action](Node*) {
|
||||
action();
|
||||
m->destroy();
|
||||
};
|
||||
const auto status = pp::panopainter::execute_legacy_document_workflow_decision(
|
||||
*this,
|
||||
decision,
|
||||
std::move(action));
|
||||
if (!status.ok())
|
||||
LOG("Document workflow action failed: %s", status.message);
|
||||
}
|
||||
|
||||
void App::dialog_newdoc()
|
||||
@@ -350,19 +331,9 @@ void App::save_document(pp::app::DocumentSaveIntent intent)
|
||||
Canvas::I->m_newdoc,
|
||||
Canvas::I->m_unsaved,
|
||||
intent);
|
||||
switch (decision) {
|
||||
case pp::app::DocumentSaveDecision::show_save_dialog:
|
||||
dialog_save();
|
||||
break;
|
||||
case pp::app::DocumentSaveDecision::save_existing:
|
||||
Canvas::I->project_save();
|
||||
break;
|
||||
case pp::app::DocumentSaveDecision::save_version:
|
||||
dialog_save_ver();
|
||||
break;
|
||||
case pp::app::DocumentSaveDecision::no_op:
|
||||
break;
|
||||
}
|
||||
const auto status = pp::panopainter::execute_legacy_document_save_decision(*this, decision);
|
||||
if (!status.ok())
|
||||
LOG("Document save action failed: %s", status.message);
|
||||
}
|
||||
|
||||
void App::dialog_save()
|
||||
|
||||
143
src/legacy_document_session_services.cpp
Normal file
143
src/legacy_document_session_services.cpp
Normal file
@@ -0,0 +1,143 @@
|
||||
#include "pch.h"
|
||||
|
||||
#include "legacy_document_session_services.h"
|
||||
|
||||
#include "app.h"
|
||||
|
||||
namespace pp::panopainter {
|
||||
namespace {
|
||||
|
||||
class LegacyCloseRequestServices final : public pp::app::CloseRequestServices {
|
||||
public:
|
||||
LegacyCloseRequestServices(App& app, bool& dialog_already_opened) noexcept
|
||||
: app_(app)
|
||||
, dialog_already_opened_(dialog_already_opened)
|
||||
{
|
||||
}
|
||||
|
||||
void request_close_now() override
|
||||
{
|
||||
}
|
||||
|
||||
void show_unsaved_close_prompt() override
|
||||
{
|
||||
auto* app = &app_;
|
||||
auto* dialog_already_opened = &dialog_already_opened_;
|
||||
auto* m = app_.layout[app_.main_id]->add_child<NodeMessageBox>();
|
||||
m->m_title->set_text("Unsaved document");
|
||||
m->m_message->set_text("Do you want to close without saving?");
|
||||
m->btn_ok->m_text->set_text("Yes");
|
||||
m->btn_ok->on_click = [app](Node*) {
|
||||
app->request_app_close();
|
||||
Canvas::I->m_unsaved = false;
|
||||
};
|
||||
m->btn_cancel->m_text->set_text("No");
|
||||
m->btn_cancel->on_click = [dialog_already_opened, m](Node*) {
|
||||
m->destroy();
|
||||
*dialog_already_opened = false;
|
||||
};
|
||||
dialog_already_opened_ = true;
|
||||
}
|
||||
|
||||
private:
|
||||
App& app_;
|
||||
bool& dialog_already_opened_;
|
||||
};
|
||||
|
||||
class LegacyDocumentSaveServices final : public pp::app::DocumentSaveServices {
|
||||
public:
|
||||
explicit LegacyDocumentSaveServices(App& app) noexcept
|
||||
: app_(app)
|
||||
{
|
||||
}
|
||||
|
||||
void show_save_dialog() override
|
||||
{
|
||||
app_.dialog_save();
|
||||
}
|
||||
|
||||
void save_existing_document() override
|
||||
{
|
||||
Canvas::I->project_save();
|
||||
}
|
||||
|
||||
void save_document_version() override
|
||||
{
|
||||
app_.dialog_save_ver();
|
||||
}
|
||||
|
||||
private:
|
||||
App& app_;
|
||||
};
|
||||
|
||||
class LegacyDocumentWorkflowServices final : public pp::app::DocumentWorkflowServices {
|
||||
public:
|
||||
LegacyDocumentWorkflowServices(App& app, std::function<void()> action) noexcept
|
||||
: app_(app)
|
||||
, action_(std::move(action))
|
||||
{
|
||||
}
|
||||
|
||||
void continue_workflow_now() override
|
||||
{
|
||||
action_();
|
||||
}
|
||||
|
||||
void prompt_save_before_continue() override
|
||||
{
|
||||
auto m = app_.layout[app_.main_id]->add_child<NodeMessageBox>();
|
||||
m->m_title->set_text("Unsaved document");
|
||||
m->m_message->set_text("Would you like to save this document before closing?");
|
||||
m->btn_ok->m_text->set_text("Yes");
|
||||
m->btn_cancel->m_text->set_text("No");
|
||||
auto* app = &app_;
|
||||
auto action = action_;
|
||||
m->btn_ok->on_click = [app, m, action](Node*) {
|
||||
Canvas::I->project_save([app, m, action](bool success) {
|
||||
if (success)
|
||||
action();
|
||||
else
|
||||
app->message_box("Saving Error", "There was a problem saving the document");
|
||||
});
|
||||
m->destroy();
|
||||
};
|
||||
m->btn_cancel->on_click = [m, action](Node*) {
|
||||
action();
|
||||
m->destroy();
|
||||
};
|
||||
}
|
||||
|
||||
private:
|
||||
App& app_;
|
||||
std::function<void()> action_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
pp::foundation::Status execute_legacy_close_request_decision(
|
||||
App& app,
|
||||
pp::app::CloseRequestDecision decision,
|
||||
bool& dialog_already_opened)
|
||||
{
|
||||
LegacyCloseRequestServices services(app, dialog_already_opened);
|
||||
return pp::app::execute_close_request_decision(decision, services);
|
||||
}
|
||||
|
||||
pp::foundation::Status execute_legacy_document_save_decision(
|
||||
App& app,
|
||||
pp::app::DocumentSaveDecision decision)
|
||||
{
|
||||
LegacyDocumentSaveServices services(app);
|
||||
return pp::app::execute_document_save_decision(decision, services);
|
||||
}
|
||||
|
||||
pp::foundation::Status execute_legacy_document_workflow_decision(
|
||||
App& app,
|
||||
pp::app::DocumentWorkflowDecision decision,
|
||||
std::function<void()> action)
|
||||
{
|
||||
LegacyDocumentWorkflowServices services(app, std::move(action));
|
||||
return pp::app::execute_document_workflow_decision(decision, services);
|
||||
}
|
||||
|
||||
} // namespace pp::panopainter
|
||||
26
src/legacy_document_session_services.h
Normal file
26
src/legacy_document_session_services.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "app_core/document_session.h"
|
||||
#include "foundation/result.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
class App;
|
||||
|
||||
namespace pp::panopainter {
|
||||
|
||||
[[nodiscard]] pp::foundation::Status execute_legacy_close_request_decision(
|
||||
App& app,
|
||||
pp::app::CloseRequestDecision decision,
|
||||
bool& dialog_already_opened);
|
||||
|
||||
[[nodiscard]] pp::foundation::Status execute_legacy_document_save_decision(
|
||||
App& app,
|
||||
pp::app::DocumentSaveDecision decision);
|
||||
|
||||
[[nodiscard]] pp::foundation::Status execute_legacy_document_workflow_decision(
|
||||
App& app,
|
||||
pp::app::DocumentWorkflowDecision decision,
|
||||
std::function<void()> action);
|
||||
|
||||
} // namespace pp::panopainter
|
||||
@@ -43,6 +43,70 @@ public:
|
||||
std::string call_order;
|
||||
};
|
||||
|
||||
class FakeCloseRequestServices final : public pp::app::CloseRequestServices {
|
||||
public:
|
||||
void request_close_now() override
|
||||
{
|
||||
close_now += 1;
|
||||
call_order += "close;";
|
||||
}
|
||||
|
||||
void show_unsaved_close_prompt() override
|
||||
{
|
||||
close_prompts += 1;
|
||||
call_order += "prompt;";
|
||||
}
|
||||
|
||||
int close_now = 0;
|
||||
int close_prompts = 0;
|
||||
std::string call_order;
|
||||
};
|
||||
|
||||
class FakeDocumentSaveServices final : public pp::app::DocumentSaveServices {
|
||||
public:
|
||||
void show_save_dialog() override
|
||||
{
|
||||
dialogs += 1;
|
||||
call_order += "dialog;";
|
||||
}
|
||||
|
||||
void save_existing_document() override
|
||||
{
|
||||
saves += 1;
|
||||
call_order += "save;";
|
||||
}
|
||||
|
||||
void save_document_version() override
|
||||
{
|
||||
versions += 1;
|
||||
call_order += "version;";
|
||||
}
|
||||
|
||||
int dialogs = 0;
|
||||
int saves = 0;
|
||||
int versions = 0;
|
||||
std::string call_order;
|
||||
};
|
||||
|
||||
class FakeDocumentWorkflowServices final : public pp::app::DocumentWorkflowServices {
|
||||
public:
|
||||
void continue_workflow_now() override
|
||||
{
|
||||
continues += 1;
|
||||
call_order += "continue;";
|
||||
}
|
||||
|
||||
void prompt_save_before_continue() override
|
||||
{
|
||||
prompts += 1;
|
||||
call_order += "prompt-save;";
|
||||
}
|
||||
|
||||
int continues = 0;
|
||||
int prompts = 0;
|
||||
std::string call_order;
|
||||
};
|
||||
|
||||
[[nodiscard]] pp::app::DocumentOpenRoute project_route()
|
||||
{
|
||||
return {
|
||||
@@ -212,6 +276,25 @@ void close_dirty_document_opens_one_prompt(pp::tests::Harness& harness)
|
||||
pp::app::plan_close_request(true, true) == pp::app::CloseRequestDecision::wait_for_existing_prompt);
|
||||
}
|
||||
|
||||
void close_request_executor_dispatches_and_preserves_wait(pp::tests::Harness& harness)
|
||||
{
|
||||
FakeCloseRequestServices services;
|
||||
|
||||
PP_EXPECT(
|
||||
harness,
|
||||
pp::app::execute_close_request_decision(pp::app::CloseRequestDecision::close_now, services).ok());
|
||||
PP_EXPECT(
|
||||
harness,
|
||||
pp::app::execute_close_request_decision(pp::app::CloseRequestDecision::show_unsaved_prompt, services).ok());
|
||||
PP_EXPECT(
|
||||
harness,
|
||||
pp::app::execute_close_request_decision(pp::app::CloseRequestDecision::wait_for_existing_prompt, services).ok());
|
||||
|
||||
PP_EXPECT(harness, services.close_now == 1);
|
||||
PP_EXPECT(harness, services.close_prompts == 1);
|
||||
PP_EXPECT(harness, services.call_order == "close;prompt;");
|
||||
}
|
||||
|
||||
void save_clean_existing_document_is_no_op(pp::tests::Harness& harness)
|
||||
{
|
||||
PP_EXPECT(
|
||||
@@ -220,6 +303,29 @@ void save_clean_existing_document_is_no_op(pp::tests::Harness& harness)
|
||||
== pp::app::DocumentSaveDecision::no_op);
|
||||
}
|
||||
|
||||
void save_executor_dispatches_visible_work_and_no_ops_cleanly(pp::tests::Harness& harness)
|
||||
{
|
||||
FakeDocumentSaveServices services;
|
||||
|
||||
PP_EXPECT(
|
||||
harness,
|
||||
pp::app::execute_document_save_decision(pp::app::DocumentSaveDecision::show_save_dialog, services).ok());
|
||||
PP_EXPECT(
|
||||
harness,
|
||||
pp::app::execute_document_save_decision(pp::app::DocumentSaveDecision::save_existing, services).ok());
|
||||
PP_EXPECT(
|
||||
harness,
|
||||
pp::app::execute_document_save_decision(pp::app::DocumentSaveDecision::save_version, services).ok());
|
||||
PP_EXPECT(
|
||||
harness,
|
||||
pp::app::execute_document_save_decision(pp::app::DocumentSaveDecision::no_op, services).ok());
|
||||
|
||||
PP_EXPECT(harness, services.dialogs == 1);
|
||||
PP_EXPECT(harness, services.saves == 1);
|
||||
PP_EXPECT(harness, services.versions == 1);
|
||||
PP_EXPECT(harness, services.call_order == "dialog;save;version;");
|
||||
}
|
||||
|
||||
void save_new_or_dirty_document_has_user_visible_work(pp::tests::Harness& harness)
|
||||
{
|
||||
PP_EXPECT(
|
||||
@@ -292,6 +398,34 @@ void workflow_with_dirty_canvas_prompts_for_save(pp::tests::Harness& harness)
|
||||
== pp::app::DocumentWorkflowDecision::prompt_save_before_continue);
|
||||
}
|
||||
|
||||
void workflow_executor_dispatches_continue_prompt_and_unavailable(pp::tests::Harness& harness)
|
||||
{
|
||||
FakeDocumentWorkflowServices services;
|
||||
|
||||
PP_EXPECT(
|
||||
harness,
|
||||
pp::app::execute_document_workflow_decision(
|
||||
pp::app::DocumentWorkflowDecision::continue_now,
|
||||
services)
|
||||
.ok());
|
||||
PP_EXPECT(
|
||||
harness,
|
||||
pp::app::execute_document_workflow_decision(
|
||||
pp::app::DocumentWorkflowDecision::prompt_save_before_continue,
|
||||
services)
|
||||
.ok());
|
||||
PP_EXPECT(
|
||||
harness,
|
||||
pp::app::execute_document_workflow_decision(
|
||||
pp::app::DocumentWorkflowDecision::unavailable,
|
||||
services)
|
||||
.ok());
|
||||
|
||||
PP_EXPECT(harness, services.continues == 1);
|
||||
PP_EXPECT(harness, services.prompts == 1);
|
||||
PP_EXPECT(harness, services.call_order == "continue;prompt-save;");
|
||||
}
|
||||
|
||||
void document_file_target_rejects_empty_name(pp::tests::Harness& harness)
|
||||
{
|
||||
const auto target = pp::app::make_document_file_target("D:/Paint", "");
|
||||
@@ -484,13 +618,16 @@ int main()
|
||||
harness.run("document open executor rejects mismatched routes", document_open_executor_rejects_mismatched_routes);
|
||||
harness.run("close clean document executes immediately", close_clean_document_executes_immediately);
|
||||
harness.run("close dirty document opens one prompt", close_dirty_document_opens_one_prompt);
|
||||
harness.run("close request executor dispatches and preserves wait", close_request_executor_dispatches_and_preserves_wait);
|
||||
harness.run("save clean existing document is no op", save_clean_existing_document_is_no_op);
|
||||
harness.run("save executor dispatches visible work and no ops cleanly", save_executor_dispatches_visible_work_and_no_ops_cleanly);
|
||||
harness.run("save new or dirty document has user visible work", save_new_or_dirty_document_has_user_visible_work);
|
||||
harness.run("save as always shows save dialog", save_as_always_shows_save_dialog);
|
||||
harness.run("save version respects menu and hotkey behaviors", save_version_respects_menu_and_hotkey_behaviors);
|
||||
harness.run("workflow without canvas is unavailable", workflow_without_canvas_is_unavailable);
|
||||
harness.run("workflow with clean canvas continues now", workflow_with_clean_canvas_continues_now);
|
||||
harness.run("workflow with dirty canvas prompts for save", workflow_with_dirty_canvas_prompts_for_save);
|
||||
harness.run("workflow executor dispatches continue prompt and unavailable", workflow_executor_dispatches_continue_prompt_and_unavailable);
|
||||
harness.run("document file target rejects empty name", document_file_target_rejects_empty_name);
|
||||
harness.run("document file target builds legacy ppi path", document_file_target_builds_legacy_ppi_path);
|
||||
harness.run("document file write prompts only for existing targets", document_file_write_prompts_only_for_existing_targets);
|
||||
|
||||
Reference in New Issue
Block a user