Route document workflow prompts through app core
This commit is contained in:
@@ -395,15 +395,16 @@ Known local toolchain state:
|
|||||||
contract as JSON and is covered for project files, ABR imports, PPBR
|
contract as JSON and is covered for project files, ABR imports, PPBR
|
||||||
imports, and malformed path rejection.
|
imports, and malformed path rejection.
|
||||||
- `pano_cli simulate-app-session` exposes `pp_app_core` project-open,
|
- `pano_cli simulate-app-session` exposes `pp_app_core` project-open,
|
||||||
app-close, save, save-as, and save-version decisions as JSON and is covered
|
app-close, save, save-as, save-version, and save-before-workflow decisions
|
||||||
for clean, dirty, already-prompting, new-document, save-as, save-version, and
|
as JSON and is covered for clean, dirty, already-prompting, missing-canvas,
|
||||||
dirty-save-version states.
|
new-document, save-as, save-version, and dirty-save-version states.
|
||||||
- `pp_app_core_document_route_tests` covers the app document-open route
|
- `pp_app_core_document_route_tests` covers the app document-open route
|
||||||
contract for PPI/project files, ABR imports, PPBR imports, inner-dot names,
|
contract for PPI/project files, ABR imports, PPBR imports, inner-dot names,
|
||||||
and malformed paths before the live `App::open_document` performs UI or
|
and malformed paths before the live `App::open_document` performs UI or
|
||||||
legacy canvas work.
|
legacy canvas work.
|
||||||
- `pp_app_core_document_session_tests` covers clean and dirty app session plus
|
- `pp_app_core_document_session_tests` covers clean and dirty app session plus
|
||||||
save-request decisions without requiring a window, canvas, or message box.
|
save-request and save-before-workflow decisions without requiring a window,
|
||||||
|
canvas, or message box.
|
||||||
- `pp_ui_core` consumes vcpkg tinyxml2 only when `PP_USE_VCPKG_TINYXML2=ON`
|
- `pp_ui_core` consumes vcpkg tinyxml2 only when `PP_USE_VCPKG_TINYXML2=ON`
|
||||||
through the vcpkg preset; default and Android validation still use the
|
through the vcpkg preset; default and Android validation still use the
|
||||||
retained vendored fallback tracked by DEBT-0012.
|
retained vendored fallback tracked by DEBT-0012.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# PanoPainter Capability Map
|
# PanoPainter Capability Map
|
||||||
|
|
||||||
Status: live
|
Status: live
|
||||||
Last updated: 2026-05-31
|
Last updated: 2026-06-02
|
||||||
|
|
||||||
This map is the preservation checklist for the modernization. When a component
|
This map is the preservation checklist for the modernization. When a component
|
||||||
is extracted, update the relevant rows with the owning component, test label,
|
is extracted, update the relevant rows with the owning component, test label,
|
||||||
@@ -13,7 +13,7 @@ and validation command.
|
|||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| PPI open/save | `Canvas`, serializer, dialogs | `pp_document`, `pp_assets`, `pano_cli` | Round-trip tiny project, old-version fixture, corrupt/truncated fixture |
|
| PPI open/save | `Canvas`, serializer, dialogs | `pp_document`, `pp_assets`, `pano_cli` | Round-trip tiny project, old-version fixture, corrupt/truncated fixture |
|
||||||
| Open-document routing | `App::open_document` | `pp_app_core`, `pano_cli`, `pp_panopainter_ui`, `pp_document`, `pp_assets` | Project/ABR/PPBR route tests, malformed path tests, CLI route smoke, app open smoke |
|
| Open-document routing | `App::open_document` | `pp_app_core`, `pano_cli`, `pp_panopainter_ui`, `pp_document`, `pp_assets` | Project/ABR/PPBR route tests, malformed path tests, CLI route smoke, app open smoke |
|
||||||
| Document session decisions | `App::open_document`, `App::request_close`, save hotkeys, file menu, dialogs | `pp_app_core`, `pano_cli`, `pp_panopainter_ui` | Clean/dirty/prompt-open/save/save-as/save-version decision tests, CLI session smoke, app close/open/save smoke |
|
| Document session decisions | `App::open_document`, `App::request_close`, save hotkeys, file menu, dialogs | `pp_app_core`, `pano_cli`, `pp_panopainter_ui` | Clean/dirty/prompt-open/save/save-as/save-version/save-before-workflow decision tests, CLI session smoke, app close/open/save/new/browse smoke |
|
||||||
| Version metadata | `scripts/pre-build.py`, `version.*` | build system, `pp_foundation` | Generated header smoke test, missing-tag behavior |
|
| Version metadata | `scripts/pre-build.py`, `version.*` | build system, `pp_foundation` | Generated header smoke test, missing-tag behavior |
|
||||||
| Thumbnail generation/read | `Canvas`, `Image` | `pp_assets`, `pp_paint_renderer` | Golden thumbnail, corrupt input |
|
| Thumbnail generation/read | `Canvas`, `Image` | `pp_assets`, `pp_paint_renderer` | Golden thumbnail, corrupt input |
|
||||||
| Save-as, overwrite prompts | App/dialogs | `pp_app_core`, `pp_panopainter_ui`, `pp_platform_*` | Decision tests, UI automation, and platform smoke |
|
| Save-as, overwrite prompts | App/dialogs | `pp_app_core`, `pp_panopainter_ui`, `pp_platform_*` | Decision tests, UI automation, and platform smoke |
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ agent or engineer to remove them without reconstructing context from chat.
|
|||||||
| --- | --- | --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- | --- | --- |
|
||||||
| DEBT-0001 | Open | Modernization | Existing platform build files remain alongside new CMake | Required for incremental migration without losing platform coverage | Existing platform builds plus new CMake configure | Remove after all platform builds consume shared CMake targets |
|
| DEBT-0001 | Open | Modernization | Existing platform build files remain alongside new CMake | Required for incremental migration without losing platform coverage | Existing platform builds plus new CMake configure | Remove after all platform builds consume shared CMake targets |
|
||||||
| DEBT-0002 | Open | Modernization | Vendored SDK and patched libraries retained initially | Some dependencies are SDK-only, patched, or have platform-specific binaries | Dependency inventory and platform build smoke tests | Replace with vcpkg packages or document permanent vendored status after triplet evaluation |
|
| DEBT-0002 | Open | Modernization | Vendored SDK and patched libraries retained initially | Some dependencies are SDK-only, patched, or have platform-specific binaries | Dependency inventory and platform build smoke tests | Replace with vcpkg packages or document permanent vendored status after triplet evaluation |
|
||||||
| DEBT-0003 | Open | Modernization | Existing singletons remain during initial split; `App::open_document`, `App::request_close`, file-menu save actions, `NodeCanvas` save hotkeys, `pano_cli classify-open`, and `pano_cli simulate-app-session` now consume pure `pp_app_core` route/session contracts, but document loading and saving still reach legacy `Canvas::I` and UI singletons | Avoid behavior changes while introducing component boundaries | App launch and component tests; `pp_app_core_document_route_tests`; `pp_app_core_document_session_tests`; `pano_cli classify-open --path D:/Paint/demo.ppi`; `pano_cli simulate-app-session --unsaved --save-intent save-dirty-version`; `ctest --preset desktop-fast --build-config Debug` | Replace singleton reaches with context/service injection at component boundaries |
|
| DEBT-0003 | Open | Modernization | Existing singletons remain during initial split; `App::open_document`, `App::request_close`, file-menu save actions, `NodeCanvas` save hotkeys, new/open/browse dirty-document workflow prompts, `pano_cli classify-open`, and `pano_cli simulate-app-session` now consume pure `pp_app_core` route/session contracts, but document loading and saving still reach legacy `Canvas::I` and UI singletons | Avoid behavior changes while introducing component boundaries | App launch and component tests; `pp_app_core_document_route_tests`; `pp_app_core_document_session_tests`; `pano_cli classify-open --path D:/Paint/demo.ppi`; `pano_cli simulate-app-session --unsaved --save-intent save-dirty-version`; `pano_cli simulate-app-session --no-canvas`; `ctest --preset desktop-fast --build-config Debug` | Replace singleton reaches with context/service injection at component boundaries |
|
||||||
| DEBT-0004 | Open | Modernization | Android, Linux, WebGL, Apple, and AppX build files remain platform-specific until root CMake alignment reaches them | Prevent platform regressions during incremental migration; raw Windows `.sln/.vcxproj` files were removed on 2026-05-31 by user decision | `cmake --preset windows-msvc-default`; platform-specific configure/build smoke checks as each platform is migrated | Root CMake owns every platform source list and package path |
|
| DEBT-0004 | Open | Modernization | Android, Linux, WebGL, Apple, and AppX build files remain platform-specific until root CMake alignment reaches them | Prevent platform regressions during incremental migration; raw Windows `.sln/.vcxproj` files were removed on 2026-05-31 by user decision | `cmake --preset windows-msvc-default`; platform-specific configure/build smoke checks as each platform is migrated | Root CMake owns every platform source list and package path |
|
||||||
| DEBT-0005 | Open | Modernization | Temporary local CTest harness is used before Catch2 is wired through vcpkg | `vcpkg` is not currently on PATH, but headless tests need to run now | `ctest --preset desktop-fast --build-config Debug` | Replace `tests/test_harness.h` tests with Catch2 tests once vcpkg toolchain/presets are validated |
|
| DEBT-0005 | Open | Modernization | Temporary local CTest harness is used before Catch2 is wired through vcpkg | `vcpkg` is not currently on PATH, but headless tests need to run now | `ctest --preset desktop-fast --build-config Debug` | Replace `tests/test_harness.h` tests with Catch2 tests once vcpkg toolchain/presets are validated |
|
||||||
| DEBT-0007 | Open | Modernization | `vcpkg.json` and `windows-msvc-vcpkg-headless` are validated for the headless Windows component matrix, but app targets still use vendored libraries and Android/Apple triplets are not proven | Dependency migration must stay incremental while SDK/patched/vendor dependencies remain in use | `$env:VCPKG_ROOT="C:\Program Files\Microsoft Visual Studio\2022\Community\VC\vcpkg"; cmake --preset windows-msvc-vcpkg-headless`; `ctest --preset desktop-fast-vcpkg --build-config Debug` | Component targets consume vcpkg packages where reliable and desktop app, Android, and Apple triplets are validated or explicitly documented as permanent vendor exceptions |
|
| DEBT-0007 | Open | Modernization | `vcpkg.json` and `windows-msvc-vcpkg-headless` are validated for the headless Windows component matrix, but app targets still use vendored libraries and Android/Apple triplets are not proven | Dependency migration must stay incremental while SDK/patched/vendor dependencies remain in use | `$env:VCPKG_ROOT="C:\Program Files\Microsoft Visual Studio\2022\Community\VC\vcpkg"; cmake --preset windows-msvc-vcpkg-headless`; `ctest --preset desktop-fast-vcpkg --build-config Debug` | Component targets consume vcpkg packages where reliable and desktop app, Android, and Apple triplets are validated or explicitly documented as permanent vendor exceptions |
|
||||||
|
|||||||
@@ -425,7 +425,8 @@ rejection smoke test for unsafe tiny canvas dimensions.
|
|||||||
contract for project files, ABR imports, PPBR imports, and malformed path
|
contract for project files, ABR imports, PPBR imports, and malformed path
|
||||||
rejection. `pano_cli simulate-app-session` exposes the pure `pp_app_core`
|
rejection. `pano_cli simulate-app-session` exposes the pure `pp_app_core`
|
||||||
session decisions used by project-open, app-close, save, save-as, and
|
session decisions used by project-open, app-close, save, save-as, and
|
||||||
save-version flows.
|
save-version flows, plus the save-before-continue workflow gate used by
|
||||||
|
new-document/open/browse dialogs.
|
||||||
`pano_cli parse-layout` exercises the XML layout path. Continue expanding
|
`pano_cli parse-layout` exercises the XML layout path. Continue expanding
|
||||||
document behavior toward legacy Canvas parity and then port OpenGL classes
|
document behavior toward legacy Canvas parity and then port OpenGL classes
|
||||||
behind the renderer boundary.
|
behind the renderer boundary.
|
||||||
|
|||||||
@@ -250,6 +250,7 @@ public:
|
|||||||
void dialog_changelog();
|
void dialog_changelog();
|
||||||
void dialog_about();
|
void dialog_about();
|
||||||
void save_document(pp::app::DocumentSaveIntent intent);
|
void save_document(pp::app::DocumentSaveIntent intent);
|
||||||
|
void continue_document_workflow_after_optional_save(std::function<void()> action);
|
||||||
void dialog_newdoc();
|
void dialog_newdoc();
|
||||||
void dialog_save();
|
void dialog_save();
|
||||||
void dialog_save_ver();
|
void dialog_save_ver();
|
||||||
|
|||||||
@@ -27,6 +27,12 @@ enum class DocumentSaveDecision {
|
|||||||
save_version,
|
save_version,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class DocumentWorkflowDecision {
|
||||||
|
unavailable,
|
||||||
|
continue_now,
|
||||||
|
prompt_save_before_continue,
|
||||||
|
};
|
||||||
|
|
||||||
[[nodiscard]] constexpr ProjectOpenDecision plan_project_open(bool has_unsaved_changes) noexcept
|
[[nodiscard]] constexpr ProjectOpenDecision plan_project_open(bool has_unsaved_changes) noexcept
|
||||||
{
|
{
|
||||||
return has_unsaved_changes
|
return has_unsaved_changes
|
||||||
@@ -78,4 +84,17 @@ enum class DocumentSaveDecision {
|
|||||||
return DocumentSaveDecision::no_op;
|
return DocumentSaveDecision::no_op;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr DocumentWorkflowDecision plan_document_workflow(
|
||||||
|
bool has_canvas,
|
||||||
|
bool has_unsaved_changes) noexcept
|
||||||
|
{
|
||||||
|
if (!has_canvas) {
|
||||||
|
return DocumentWorkflowDecision::unavailable;
|
||||||
|
}
|
||||||
|
|
||||||
|
return has_unsaved_changes
|
||||||
|
? DocumentWorkflowDecision::prompt_save_before_continue
|
||||||
|
: DocumentWorkflowDecision::continue_now;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,6 +105,41 @@ void App::dialog_about()
|
|||||||
layout[main_id]->add_child(dialog);
|
layout[main_id]->add_child(dialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void App::continue_document_workflow_after_optional_save(std::function<void()> action)
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
void App::dialog_newdoc()
|
void App::dialog_newdoc()
|
||||||
{
|
{
|
||||||
auto show_dialog = [this] {
|
auto show_dialog = [this] {
|
||||||
@@ -181,34 +216,7 @@ void App::dialog_newdoc()
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
if (canvas)
|
continue_document_workflow_after_optional_save(show_dialog);
|
||||||
{
|
|
||||||
if (Canvas::I->m_unsaved)
|
|
||||||
{
|
|
||||||
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, show_dialog](Node*) {
|
|
||||||
Canvas::I->project_save([this, m, show_dialog](bool success){
|
|
||||||
if (success)
|
|
||||||
show_dialog();
|
|
||||||
else
|
|
||||||
message_box("Saving Error", "There was a problem saving the document");
|
|
||||||
});
|
|
||||||
m->destroy();
|
|
||||||
};
|
|
||||||
m->btn_cancel->on_click = [this, m, show_dialog](Node*) {
|
|
||||||
show_dialog();
|
|
||||||
m->destroy();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
show_dialog();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DEPRECATED
|
// DEPRECATED
|
||||||
@@ -242,34 +250,7 @@ void App::dialog_open()
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
if (canvas)
|
continue_document_workflow_after_optional_save(show_dialog);
|
||||||
{
|
|
||||||
if (Canvas::I->m_unsaved)
|
|
||||||
{
|
|
||||||
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,show_dialog](Node*){
|
|
||||||
Canvas::I->project_save([this,m,show_dialog](bool success){
|
|
||||||
if (success)
|
|
||||||
show_dialog();
|
|
||||||
else
|
|
||||||
message_box("Saving Error", "There was a problem saving the document");
|
|
||||||
});
|
|
||||||
m->destroy();
|
|
||||||
};
|
|
||||||
m->btn_cancel->on_click = [this,m,show_dialog](Node*) {
|
|
||||||
show_dialog();
|
|
||||||
m->destroy();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
show_dialog();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::dialog_browse()
|
void App::dialog_browse()
|
||||||
@@ -299,34 +280,7 @@ void App::dialog_browse()
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
if (canvas)
|
continue_document_workflow_after_optional_save(show_dialog);
|
||||||
{
|
|
||||||
if (Canvas::I->m_unsaved)
|
|
||||||
{
|
|
||||||
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, show_dialog](Node*) {
|
|
||||||
Canvas::I->project_save([this, m, show_dialog](bool success){
|
|
||||||
if (success)
|
|
||||||
show_dialog();
|
|
||||||
else
|
|
||||||
message_box("Saving Error", "There was a problem saving the document");
|
|
||||||
});
|
|
||||||
m->destroy();
|
|
||||||
};
|
|
||||||
m->btn_cancel->on_click = [this, m, show_dialog](Node*) {
|
|
||||||
show_dialog();
|
|
||||||
m->destroy();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
show_dialog();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::dialog_save_ver()
|
void App::dialog_save_ver()
|
||||||
|
|||||||
@@ -375,13 +375,13 @@ if(TARGET pano_cli)
|
|||||||
COMMAND pano_cli simulate-app-session)
|
COMMAND pano_cli simulate-app-session)
|
||||||
set_tests_properties(pano_cli_simulate_app_session_clean_smoke PROPERTIES
|
set_tests_properties(pano_cli_simulate_app_session_clean_smoke PROPERTIES
|
||||||
LABELS "app;integration;desktop-fast"
|
LABELS "app;integration;desktop-fast"
|
||||||
PASS_REGULAR_EXPRESSION "\"command\":\"simulate-app-session\".*\"newDocument\":false.*\"unsaved\":false.*\"closePromptOpen\":false.*\"projectOpen\":\"open-now\".*\"closeRequest\":\"close-now\".*\"saveIntent\":\"save\".*\"saveRequest\":\"no-op\"")
|
PASS_REGULAR_EXPRESSION "\"command\":\"simulate-app-session\".*\"hasCanvas\":true.*\"newDocument\":false.*\"unsaved\":false.*\"closePromptOpen\":false.*\"projectOpen\":\"open-now\".*\"closeRequest\":\"close-now\".*\"saveIntent\":\"save\".*\"saveRequest\":\"no-op\".*\"workflowStart\":\"continue-now\"")
|
||||||
|
|
||||||
add_test(NAME pano_cli_simulate_app_session_unsaved_smoke
|
add_test(NAME pano_cli_simulate_app_session_unsaved_smoke
|
||||||
COMMAND pano_cli simulate-app-session --unsaved)
|
COMMAND pano_cli simulate-app-session --unsaved)
|
||||||
set_tests_properties(pano_cli_simulate_app_session_unsaved_smoke PROPERTIES
|
set_tests_properties(pano_cli_simulate_app_session_unsaved_smoke PROPERTIES
|
||||||
LABELS "app;integration;desktop-fast"
|
LABELS "app;integration;desktop-fast"
|
||||||
PASS_REGULAR_EXPRESSION "\"command\":\"simulate-app-session\".*\"newDocument\":false.*\"unsaved\":true.*\"closePromptOpen\":false.*\"projectOpen\":\"prompt-discard-unsaved\".*\"closeRequest\":\"show-unsaved-prompt\".*\"saveRequest\":\"save-existing\"")
|
PASS_REGULAR_EXPRESSION "\"command\":\"simulate-app-session\".*\"hasCanvas\":true.*\"newDocument\":false.*\"unsaved\":true.*\"closePromptOpen\":false.*\"projectOpen\":\"prompt-discard-unsaved\".*\"closeRequest\":\"show-unsaved-prompt\".*\"saveRequest\":\"save-existing\".*\"workflowStart\":\"prompt-save-before-continue\"")
|
||||||
|
|
||||||
add_test(NAME pano_cli_simulate_app_session_existing_prompt_smoke
|
add_test(NAME pano_cli_simulate_app_session_existing_prompt_smoke
|
||||||
COMMAND pano_cli simulate-app-session --unsaved --close-prompt-open)
|
COMMAND pano_cli simulate-app-session --unsaved --close-prompt-open)
|
||||||
@@ -413,6 +413,12 @@ if(TARGET pano_cli)
|
|||||||
LABELS "app;integration;desktop-fast"
|
LABELS "app;integration;desktop-fast"
|
||||||
PASS_REGULAR_EXPRESSION "\"command\":\"simulate-app-session\".*\"saveIntent\":\"save-dirty-version\".*\"saveRequest\":\"no-op\"")
|
PASS_REGULAR_EXPRESSION "\"command\":\"simulate-app-session\".*\"saveIntent\":\"save-dirty-version\".*\"saveRequest\":\"no-op\"")
|
||||||
|
|
||||||
|
add_test(NAME pano_cli_simulate_app_session_no_canvas_smoke
|
||||||
|
COMMAND pano_cli simulate-app-session --no-canvas)
|
||||||
|
set_tests_properties(pano_cli_simulate_app_session_no_canvas_smoke PROPERTIES
|
||||||
|
LABELS "app;integration;desktop-fast"
|
||||||
|
PASS_REGULAR_EXPRESSION "\"command\":\"simulate-app-session\".*\"hasCanvas\":false.*\"workflowStart\":\"unavailable\"")
|
||||||
|
|
||||||
add_test(NAME pano_cli_save_project_roundtrip_smoke
|
add_test(NAME pano_cli_save_project_roundtrip_smoke
|
||||||
COMMAND "${CMAKE_COMMAND}"
|
COMMAND "${CMAKE_COMMAND}"
|
||||||
-DPANO_CLI=$<TARGET_FILE:pano_cli>
|
-DPANO_CLI=$<TARGET_FILE:pano_cli>
|
||||||
|
|||||||
@@ -87,6 +87,34 @@ void save_version_respects_menu_and_hotkey_behaviors(pp::tests::Harness& harness
|
|||||||
== pp::app::DocumentSaveDecision::show_save_dialog);
|
== pp::app::DocumentSaveDecision::show_save_dialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void workflow_without_canvas_is_unavailable(pp::tests::Harness& harness)
|
||||||
|
{
|
||||||
|
PP_EXPECT(
|
||||||
|
harness,
|
||||||
|
pp::app::plan_document_workflow(false, false)
|
||||||
|
== pp::app::DocumentWorkflowDecision::unavailable);
|
||||||
|
PP_EXPECT(
|
||||||
|
harness,
|
||||||
|
pp::app::plan_document_workflow(false, true)
|
||||||
|
== pp::app::DocumentWorkflowDecision::unavailable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void workflow_with_clean_canvas_continues_now(pp::tests::Harness& harness)
|
||||||
|
{
|
||||||
|
PP_EXPECT(
|
||||||
|
harness,
|
||||||
|
pp::app::plan_document_workflow(true, false)
|
||||||
|
== pp::app::DocumentWorkflowDecision::continue_now);
|
||||||
|
}
|
||||||
|
|
||||||
|
void workflow_with_dirty_canvas_prompts_for_save(pp::tests::Harness& harness)
|
||||||
|
{
|
||||||
|
PP_EXPECT(
|
||||||
|
harness,
|
||||||
|
pp::app::plan_document_workflow(true, true)
|
||||||
|
== pp::app::DocumentWorkflowDecision::prompt_save_before_continue);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
@@ -100,5 +128,8 @@ int main()
|
|||||||
harness.run("save new or dirty document has user visible work", save_new_or_dirty_document_has_user_visible_work);
|
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 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("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);
|
||||||
return harness.finish();
|
return harness.finish();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ struct ClassifyOpenArgs {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct SimulateAppSessionArgs {
|
struct SimulateAppSessionArgs {
|
||||||
|
bool has_canvas = true;
|
||||||
bool new_document = false;
|
bool new_document = false;
|
||||||
bool unsaved = false;
|
bool unsaved = false;
|
||||||
bool close_prompt_open = false;
|
bool close_prompt_open = false;
|
||||||
@@ -298,6 +299,20 @@ const char* document_save_decision_name(pp::app::DocumentSaveDecision decision)
|
|||||||
return "no-op";
|
return "no-op";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* document_workflow_decision_name(pp::app::DocumentWorkflowDecision decision) noexcept
|
||||||
|
{
|
||||||
|
switch (decision) {
|
||||||
|
case pp::app::DocumentWorkflowDecision::unavailable:
|
||||||
|
return "unavailable";
|
||||||
|
case pp::app::DocumentWorkflowDecision::continue_now:
|
||||||
|
return "continue-now";
|
||||||
|
case pp::app::DocumentWorkflowDecision::prompt_save_before_continue:
|
||||||
|
return "prompt-save-before-continue";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "unavailable";
|
||||||
|
}
|
||||||
|
|
||||||
pp::foundation::Result<float> parse_float_arg(std::string_view text)
|
pp::foundation::Result<float> parse_float_arg(std::string_view text)
|
||||||
{
|
{
|
||||||
float value = 0.0F;
|
float value = 0.0F;
|
||||||
@@ -336,7 +351,7 @@ void print_help()
|
|||||||
<< " simulate-document-edits [--width N] [--height N]\n"
|
<< " simulate-document-edits [--width N] [--height N]\n"
|
||||||
<< " simulate-document-export [--width N] [--height N]\n"
|
<< " simulate-document-export [--width N] [--height N]\n"
|
||||||
<< " simulate-document-history [--width N] [--height N] [--history N]\n"
|
<< " simulate-document-history [--width N] [--height N] [--history N]\n"
|
||||||
<< " simulate-app-session [--new-document] [--unsaved] [--close-prompt-open] [--save-intent save|save-as|save-version|save-dirty-version]\n"
|
<< " simulate-app-session [--no-canvas] [--new-document] [--unsaved] [--close-prompt-open] [--save-intent save|save-as|save-version|save-dirty-version]\n"
|
||||||
<< " simulate-blend\n"
|
<< " simulate-blend\n"
|
||||||
<< " simulate-image-import [--width N] [--height N]\n"
|
<< " simulate-image-import [--width N] [--height N]\n"
|
||||||
<< " simulate-stroke --x1 N --y1 N --x2 N --y2 N [--spacing N]\n"
|
<< " simulate-stroke --x1 N --y1 N --x2 N --y2 N [--spacing N]\n"
|
||||||
@@ -1178,6 +1193,8 @@ pp::foundation::Status parse_simulate_app_session_args(
|
|||||||
const std::string_view key(argv[i]);
|
const std::string_view key(argv[i]);
|
||||||
if (key == "--new-document") {
|
if (key == "--new-document") {
|
||||||
args.new_document = true;
|
args.new_document = true;
|
||||||
|
} else if (key == "--no-canvas") {
|
||||||
|
args.has_canvas = false;
|
||||||
} else if (key == "--unsaved") {
|
} else if (key == "--unsaved") {
|
||||||
args.unsaved = true;
|
args.unsaved = true;
|
||||||
} else if (key == "--close-prompt-open") {
|
} else if (key == "--close-prompt-open") {
|
||||||
@@ -1221,8 +1238,10 @@ int simulate_app_session(int argc, char** argv)
|
|||||||
args.new_document,
|
args.new_document,
|
||||||
args.unsaved,
|
args.unsaved,
|
||||||
args.save_intent);
|
args.save_intent);
|
||||||
|
const auto workflow_decision = pp::app::plan_document_workflow(args.has_canvas, args.unsaved);
|
||||||
std::cout << "{\"ok\":true,\"command\":\"simulate-app-session\""
|
std::cout << "{\"ok\":true,\"command\":\"simulate-app-session\""
|
||||||
<< ",\"state\":{\"newDocument\":" << json_bool(args.new_document)
|
<< ",\"state\":{\"hasCanvas\":" << json_bool(args.has_canvas)
|
||||||
|
<< ",\"newDocument\":" << json_bool(args.new_document)
|
||||||
<< ",\"unsaved\":" << json_bool(args.unsaved)
|
<< ",\"unsaved\":" << json_bool(args.unsaved)
|
||||||
<< ",\"closePromptOpen\":" << json_bool(args.close_prompt_open)
|
<< ",\"closePromptOpen\":" << json_bool(args.close_prompt_open)
|
||||||
<< "},\"decisions\":{\"projectOpen\":\""
|
<< "},\"decisions\":{\"projectOpen\":\""
|
||||||
@@ -1233,6 +1252,8 @@ int simulate_app_session(int argc, char** argv)
|
|||||||
<< document_save_intent_name(args.save_intent)
|
<< document_save_intent_name(args.save_intent)
|
||||||
<< "\",\"saveRequest\":\""
|
<< "\",\"saveRequest\":\""
|
||||||
<< document_save_decision_name(save_decision)
|
<< document_save_decision_name(save_decision)
|
||||||
|
<< "\",\"workflowStart\":\""
|
||||||
|
<< document_workflow_decision_name(workflow_decision)
|
||||||
<< "\"}}\n";
|
<< "\"}}\n";
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user