diff --git a/docs/modernization/build-inventory.md b/docs/modernization/build-inventory.md index 24a2881..f81df2c 100644 --- a/docs/modernization/build-inventory.md +++ b/docs/modernization/build-inventory.md @@ -777,13 +777,15 @@ Known local toolchain state: renderer API to OpenGL token mapping and command-planning contracts used by the OpenGL parity work. - `pano_cli plan-cloud-upload` exposes `pp_app_core` cloud upload availability, - new-document warning, publish prompt, and save-before-upload planning as JSON; + new-document warning, publish prompt, prompt title/message/captions, and + save-before-upload planning as JSON; the live cloud upload command consumes the same start contract before reaching legacy UI, canvas save, and network upload execution. - `pano_cli plan-cloud-upload-all` exposes bulk cloud upload file-count, - progress UI availability, and progress-total clamping as JSON; the live - upload-all command consumes the same contract before reaching legacy asset - file listing, OpenGL context guard, progress UI, and network upload + progress UI availability, progress-total clamping, and progress dialog + metadata as JSON; the live upload-all command consumes the same contract + before reaching legacy asset file listing, OpenGL context guard, progress UI, + and network upload execution. - `pano_cli plan-cloud-browse` exposes `pp_app_core` cloud browse availability and selected-file download planning as JSON; the live cloud browse command @@ -794,7 +796,10 @@ Known local toolchain state: live paths on the `pp_app_core` `CloudServices` contract while the app-owned curl upload/download/license helpers now ask `PlatformServices` for TLS verification policy and retained dialog/network curl sites use the shared - default platform TLS helper. Legacy save-before-upload, progress/message UI, + default platform TLS helper. Cloud upload warning/publish/success copy, bulk + upload progress title, and download-progress copy now come from + `pp_app_core` dialog/progress plans before retained `NodeMessageBox` and + progress-bar execution. Legacy save-before-upload, progress/message UI, network upload/download helper ownership, OpenGL context guarding, `NodeDialogCloud`, project open, layer refresh, and action-history reset remain tracked by `DEBT-0038`. diff --git a/docs/modernization/debt.md b/docs/modernization/debt.md index 616995e..6f25478 100644 --- a/docs/modernization/debt.md +++ b/docs/modernization/debt.md @@ -161,6 +161,15 @@ agent or engineer to remove them without reconstructing context from chat. progress-callback, and zero/overrun progress cases for automation. CURL ownership, response/error handling, progress UI, cloud dialog/document execution, and injected network service work remain open under DEBT-0038. +- 2026-06-05: DEBT-0038 was narrowed again. Cloud upload warning/publish/ + success prompts, upload/bulk progress dialog titles, download-progress prompt + metadata, and formatted download progress text now live in tested + `pp_app_core` plans consumed by `src/legacy_cloud_services.*`; existing + `pano_cli plan-cloud-upload`, `pano_cli plan-cloud-upload-all`, and + `pano_cli plan-cloud-transfer` expose that metadata for automation. Retained + `NodeMessageBox`/progress-bar creation, save-before-upload threading, CURL + ownership, response/error handling, and downloaded-project execution remain + open under DEBT-0038. - 2026-06-05: DEBT-0058 was opened. App-level progress, message, and input dialog metadata now lives in tested `pp_app_core` planning consumed by `App::show_progress`, `App::message_box`, `App::input_box`, and @@ -217,7 +226,7 @@ agent or engineer to remove them without reconstructing context from chat. | DEBT-0035 | Open | Modernization | Main toolbar/status command planning and execution dispatch now consume pure `pp_app_core` through `App::init_toolbar_main`, `pano_cli plan-main-toolbar`, and the `MainToolbarServices` boundary, history/canvas commands now hand off through `HistoryUiServices` and `DocumentCanvasClearServices`, and live execution is centralized in `src/legacy_app_shell_services.*`, but the bridge still opens legacy open/save/settings/message-box dialogs and delegates to legacy history/canvas adapters | Preserve reachable toolbar/status behavior while app shell commands move toward app/document/UI services | `pp_app_core_main_toolbar_tests`; `pano_cli plan-main-toolbar --command undo --undo-count 2`; `pano_cli plan-main-toolbar --command clear-canvas --no-canvas`; `ctest --preset desktop-fast --build-config Debug` | Open/save/settings/message-box routing, undo/redo/clear-history execution, and canvas-clear execution are owned by injected app/document/UI services with `App::init_toolbar_main` acting only as a UI adapter and no legacy toolbar adapter | | DEBT-0036 | Open | Modernization | `pp_renderer_api`, `pp_paint_renderer`, `pano_cli plan-paint-feedback`, and `pano_cli plan-stroke-composite` can choose backend-neutral complex paint feedback strategies for fixed-function blending, framebuffer-fetch-capable renderers, or ping-pong render targets. OpenGL extension detection now stores `pp::renderer::RenderDeviceFeatures` through `ShaderManager`, using `pp_renderer_gl::query_opengl_capability_detection`, `detect_opengl_feature_state`, and `render_device_features` as the backend conversion point; that feature snapshot now includes float32-linear filtering, so canvas stroke texture format selection, renderer diagnostics, grid lightmap render planning, and grid bake target selection no longer read `ShaderManager::ext_*` flags directly. `pp_paint_renderer::plan_canvas_blend_gate` owns the compatibility mapping from persisted layer/brush blend indices to the extracted stroke-composite planner, and live `Canvas::draw_merge` plus `NodeCanvas` panorama rendering both call it with the stored renderer-neutral feature set for their existing shader-blend gates and destination-copy versus framebuffer-fetch decisions. `pp_paint_renderer::plan_canvas_stroke_feedback` also owns the current destination-feedback decision, and live `Canvas::stroke_draw`, thumbnail layer blending, and `NodeStrokePreview` brush-preview rendering use it for framebuffer-fetch versus destination-copy decisions. The retained `copy_framebuffer_to_texture_2d` utility bridge now routes 2D framebuffer-to-texture copies through tested `pp_renderer_gl` dispatch, retained `RTT::create`/`RTT::destroy` render-target texture parameter setup, optional depth renderbuffer allocation, framebuffer allocation/attachment/status checks, binding restore, and resource deletion now route through tested `pp_renderer_gl` dispatch, retained RTT clear, masked clear with color-write-mask restore, texture bind/unbind, and RGBA8 dirty-region texture writes now route through tested `pp_renderer_gl` dispatch, retained Canvas, NodeCanvas, and NodeStrokePreview texture-unit switches now route through tested active-texture dispatch, retained Canvas, NodeCanvas, NodeStrokePreview, and desktop HMD viewport/scissor/capability execution now route through tested `pp_renderer_gl` dispatch adapters, retained NodeCanvas, CanvasMode, and NodePanelGrid capability-state snapshots now route through tested `pp_renderer_gl` query dispatch, CanvasLayer cube/equirect generation plus frame clears now route blend state, active texture units, viewport execution, color clears, and cube-face framebuffer-to-texture copies through tested `pp_renderer_gl` dispatch adapters, `NodePanelGrid` live heightmap draw and bake setup now route depth/blend state, depth clears, color-write-mask toggles, active texture selection, bake viewport execution, sun-overlay viewport query, and desktop texture-resize readback through tested `pp_renderer_gl` dispatch adapters, retained CanvasMode overlay/mask/transform paths now route active texture, depth/blend state, transform/cut viewport execution, paint-mode blend/depth state snapshots, and canvas-tip pick framebuffer readback through tested `pp_renderer_gl` dispatch adapters, retained simple UI draw paths now share `legacy_ui_gl_dispatch` for blend-state execution, fallback 2D texture unbinds, `NodeViewport` viewport query/restore, color-buffer clears, and clear-color restore, retained `NodeCanvas` plus `NodeStrokePreview` draw-state paths now route viewport query, clear-color query, color-buffer clear, and clear-color restore through tested `pp_renderer_gl` dispatch helpers, and retained `Canvas` plus `CanvasLayer` stroke/object/thumbnail/frame-clear draw-state paths now route saved viewport or clear-color query and restore through the same tested helpers, but actual live stroke rasterization, dual-brush compositing, pattern feedback math, thumbnail layer compositing, brush-preview compositing, and the retained `ShaderManager::ext_*` compatibility fields still use legacy OpenGL canvas/UI execution | Preserve current painting behavior while the renderer boundary matures for OpenGL parity and later Vulkan/Metal experiments | `pp_renderer_api_tests`; `pp_renderer_gl_capabilities_tests`; `pp_paint_renderer_compositor_tests`; `pano_cli plan-paint-feedback --framebuffer-fetch --explicit-transitions --render-only`; `pano_cli plan-paint-feedback --texture-copy`; `pano_cli plan-stroke-composite --stroke-blend 10 --framebuffer-fetch --explicit-transitions --render-only`; `pano_cli plan-stroke-composite --layer-blend 4 --dual-blend --texture-copy`; `ctest --preset desktop-fast --build-config Debug`; `cmake --build --preset windows-msvc-default --config Debug --target PanoPainter` | Live stroke/layer compositing chooses its feedback path through `pp_paint_renderer` and renderer services, with OpenGL golden parity and Vulkan/Metal lab tests covering framebuffer-fetch and ping-pong behavior | | 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.*`, and retained `PBO` allocation/readback/map/unmap/delete operations now route through tested `pp_renderer_gl` dispatch, but the bridge still owns legacy recording thread startup/shutdown, platform recorded-file cleanup, progress UI, retained `App::rec_loop` readback call sites, 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`; `pp_renderer_gl_capabilities_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 scheduling, 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.*`, the app-owned `upload`/`download` CURL helpers now consume `pp_app_core` cloud transfer request/progress planning and the platform TLS-verification bypass policy before retained CURL setup, `pano_cli plan-cloud-transfer` exposes the same missing endpoint, TLS policy, progress-callback, and progress fraction guards, and retained `Asset::open_url`, `LogRemote::net_init`, and `NodeDialogCloud::load_thumbs_thread` curl sites consume the `pp_platform_api` default TLS policy helper instead of spelling Android branches locally, but the bridge still uses legacy save-before-upload, app-owned curl helpers instead of an injected network service, upload form construction, response/error handling, 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`; `pp_platform_api_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`; `pano_cli plan-cloud-transfer --direction download --progress --disable-tls-verification`; `ctest --preset desktop-fast --build-config Debug` | Cloud upload/download, TLS policy, 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-0038 | Open | Modernization | Cloud upload/browse/bulk planning, cloud prompt/progress metadata, 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`, `pano_cli plan-cloud-transfer`, and the `CloudServices` boundary; live execution is centralized in `src/legacy_cloud_services.*`, the app-owned `upload`/`download` CURL helpers now consume `pp_app_core` cloud transfer request/progress planning and the platform TLS-verification bypass policy before retained CURL setup, upload warning/publish/success prompts, upload/bulk progress dialog titles, download-progress prompt metadata, and formatted download progress text come from tested app-core plans, and retained `Asset::open_url`, `LogRemote::net_init`, and `NodeDialogCloud::load_thumbs_thread` curl sites consume the `pp_platform_api` default TLS policy helper instead of spelling Android branches locally, but the bridge still uses legacy save-before-upload, app-owned curl helpers instead of an injected network service, upload form construction, response/error handling, retained `NodeMessageBox`/progress-bar execution, 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`; `pp_platform_api_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`; `pano_cli plan-cloud-transfer --direction download --progress --disable-tls-verification`; `ctest --preset desktop-fast --build-config Debug` | Cloud upload/download, TLS policy, 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 before delegating import execution to `src/legacy_brush_package_import_services.*`, 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, 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, save-before-workflow planning/execution dispatch, and close/save-before/save-error prompt metadata 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`, `pano_cli plan-document-session-prompt`, `DocumentSaveServices`, `CloseRequestServices`, `DocumentWorkflowServices`, and `src/legacy_document_session_services.*`; Save dialog working-directory picker visibility/path formatting now dispatches through `PlatformServices`, but the bridge still opens retained `NodeMessageBox`/save dialogs, wires callbacks directly, 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`; `pp_platform_api_tests`; `pano_cli plan-document-session-prompt --kind close-unsaved`; `pano_cli plan-document-session-prompt --kind save-before-workflow`; `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 and new-document overwrite prompt metadata now consume pure `pp_app_core` through `App::dialog_newdoc`, `pano_cli plan-new-document`, `pano_cli plan-document-session-prompt`, `NewDocumentServices`, and `src/legacy_document_session_services.*`; New Document dialog working-directory picker visibility/path formatting now dispatches through `PlatformServices`, 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, creates retained `NodeMessageBox` overwrite prompts, 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`; `pp_platform_api_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 plan-document-session-prompt --kind new-document-overwrite`; `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 | diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index e484ac7..1b4ad4b 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -1644,6 +1644,15 @@ Results: before retained CURL setup. - Android arm64 headless `pp_app_core`, `pano_cli`, and `pp_app_core_document_cloud_tests` built after the cloud transfer slice. +- `PanoPainter`, `pp_app_core_document_cloud_tests`, and `pano_cli` built after + cloud upload warning/publish/success prompts, bulk upload progress dialogs, + and download-progress messages moved to tested `pp_app_core` metadata plans. +- Focused cloud metadata CTest coverage passed for + `pp_app_core_document_cloud_tests` and representative + `pano_cli_plan_cloud_*` smoke tests, including prompt titles/captions, + progress-dialog titles, and formatted download progress messages. +- Android arm64 headless `pp_app_core`, `pano_cli`, and + `pp_app_core_document_cloud_tests` built after the cloud metadata slice. - `PanoPainter`, `pp_app_core_document_cloud_tests`, and `pano_cli` built after live cloud upload, bulk upload, and browse/download execution moved behind the `CloudServices` boundary and `src/legacy_cloud_services.*`. diff --git a/src/app_core/document_cloud.h b/src/app_core/document_cloud.h index 0de21bf..540c26e 100644 --- a/src/app_core/document_cloud.h +++ b/src/app_core/document_cloud.h @@ -1,7 +1,9 @@ #pragma once +#include "app_core/app_dialog.h" #include "foundation/result.h" +#include #include #include #include @@ -66,6 +68,61 @@ struct CloudTransferProgressPlan { float fraction = 0.0F; }; +[[nodiscard]] inline AppMessageDialogPlan plan_cloud_save_required_prompt() +{ + return plan_app_message_dialog( + "Warning", + "This document needs to be saved before upload.", + false); +} + +[[nodiscard]] inline AppMessageDialogPlan plan_cloud_publish_prompt() +{ + return plan_app_message_dialog( + "Publish document", + "Would you like to upload to the public domain?", + true, + "Yes", + "No"); +} + +[[nodiscard]] inline AppMessageDialogPlan plan_cloud_upload_success_prompt() +{ + return plan_app_message_dialog( + "Success", + "This document has been succesfully uploaded.", + false); +} + +[[nodiscard]] inline AppProgressDialogPlan plan_cloud_upload_progress_dialog() +{ + return plan_app_progress_dialog("Uploading", 0); +} + +[[nodiscard]] inline AppProgressDialogPlan plan_cloud_bulk_upload_progress_dialog(int progress_total) +{ + return plan_app_progress_dialog("Export Pano Image", progress_total); +} + +[[nodiscard]] inline AppMessageDialogPlan plan_cloud_download_progress_prompt() +{ + return plan_app_message_dialog( + "Downloading", + "Download in progress", + true); +} + +[[nodiscard]] inline std::string format_cloud_download_progress_message(float progress_fraction) +{ + char buffer[64] {}; + std::snprintf( + buffer, + sizeof(buffer), + "Download in progress %.2f%%", + progress_fraction * 100.0F); + return buffer; +} + class CloudServices { public: virtual ~CloudServices() = default; diff --git a/src/legacy_cloud_services.cpp b/src/legacy_cloud_services.cpp index 98c1b5f..be336a7 100644 --- a/src/legacy_cloud_services.cpp +++ b/src/legacy_cloud_services.cpp @@ -12,6 +12,20 @@ namespace pp::panopainter { namespace { +void apply_cloud_message_box_plan( + NodeMessageBox& msgbox, + const pp::app::AppMessageDialogPlan& plan) +{ + msgbox.m_title->set_text(plan.title.c_str()); + msgbox.m_message->set_text(plan.message.c_str()); + msgbox.btn_ok->m_text->set_text(plan.ok_caption.c_str()); + if (plan.show_cancel) { + msgbox.btn_cancel->m_text->set_text(plan.cancel_caption.c_str()); + } else { + msgbox.btn_cancel->destroy(); + } +} + class LegacyCloudServices final : public pp::app::CloudServices { public: explicit LegacyCloudServices(App& app) noexcept @@ -21,7 +35,8 @@ public: void show_save_required_warning() override { - app_.message_box("Warning", "This document needs to be saved before upload."); + const auto plan = pp::app::plan_cloud_save_required_prompt(); + app_.message_box(plan.title, plan.message, plan.show_cancel); } void prompt_publish(bool save_before_upload) override @@ -35,19 +50,20 @@ public: Canvas::I->project_save_thread(app->doc_path, true); } - auto pb = app->show_progress("Uploading"); + const auto progress_plan = pp::app::plan_cloud_upload_progress_dialog(); + auto pb = app->show_progress(progress_plan.title, progress_plan.total); app->upload(app->doc_path, app->doc_filename, [pb](float p) { pb->set_progress(p); }); pb->destroy(); - app->message_box("Success", "This document has been succesfully uploaded."); + const auto success_plan = pp::app::plan_cloud_upload_success_prompt(); + app->message_box(success_plan.title, success_plan.message, success_plan.show_cancel); }; - auto m = app_.message_box("Publish document", "Would you like to upload to the public domain?"); - m->btn_ok->m_text->set_text("Yes"); - m->btn_cancel->m_text->set_text("No"); + const auto prompt_plan = pp::app::plan_cloud_publish_prompt(); + auto m = app_.message_box(prompt_plan.title, prompt_plan.message, prompt_plan.show_cancel); m->btn_ok->on_click = [m, upload_thread](Node*) { std::thread(upload_thread).detach(); m->destroy(); @@ -61,7 +77,8 @@ public: { bulk_progress_.reset(); if (show_progress) { - bulk_progress_ = app_.show_progress("Export Pano Image", progress_total); + const auto progress_plan = pp::app::plan_cloud_bulk_upload_progress_dialog(progress_total); + bulk_progress_ = app_.show_progress(progress_plan.title, progress_plan.total); } } @@ -114,13 +131,11 @@ public: BT_SetTerminate(); auto* m = app->layout[app->main_id]->add_child(); - m->m_title->set_text("Downloading"); - m->m_message->set_text("Download in progress"); + apply_cloud_message_box_plan(*m, pp::app::plan_cloud_download_progress_prompt()); std::string url = "https://panopainter.com/cloud/cloud-dwl.php?file=" + request.selected_file; app->download(url, request.selected_path, [m](float p) { - static char progress[256]; - sprintf(progress, "Download in progress %.2f%%", p * 100.f); - m->m_message->set_text(progress); + const auto progress = pp::app::format_cloud_download_progress_message(p); + m->m_message->set_text(progress.c_str()); }); const auto reset_status = execute_legacy_canvas_camera_reset(*app); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 725c513..400b71b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -903,7 +903,7 @@ if(TARGET pano_cli) COMMAND pano_cli plan-cloud-upload) set_tests_properties(pano_cli_plan_cloud_upload_clean_smoke PROPERTIES LABELS "app;integration;desktop-fast" - PASS_REGULAR_EXPRESSION "\"command\":\"plan-cloud-upload\".*\"hasCanvas\":true.*\"newDocument\":false.*\"unsaved\":false.*\"decision\":\"prompt-publish\".*\"saveBeforeUpload\":false") + PASS_REGULAR_EXPRESSION "\"command\":\"plan-cloud-upload\".*\"hasCanvas\":true.*\"newDocument\":false.*\"unsaved\":false.*\"decision\":\"prompt-publish\".*\"saveBeforeUpload\":false.*\"title\":\"Publish document\".*\"okCaption\":\"Yes\".*\"cancelCaption\":\"No\"") add_test(NAME pano_cli_plan_cloud_upload_unsaved_smoke COMMAND pano_cli plan-cloud-upload --unsaved) @@ -915,7 +915,7 @@ if(TARGET pano_cli) COMMAND pano_cli plan-cloud-upload --new-document --unsaved) set_tests_properties(pano_cli_plan_cloud_upload_new_document_smoke PROPERTIES LABELS "app;integration;desktop-fast;fuzz" - PASS_REGULAR_EXPRESSION "\"command\":\"plan-cloud-upload\".*\"newDocument\":true.*\"decision\":\"show-save-required-warning\".*\"saveBeforeUpload\":false") + PASS_REGULAR_EXPRESSION "\"command\":\"plan-cloud-upload\".*\"newDocument\":true.*\"decision\":\"show-save-required-warning\".*\"saveBeforeUpload\":false.*\"title\":\"Warning\".*\"message\":\"This document needs to be saved before upload\\.\".*\"showCancel\":false") add_test(NAME pano_cli_plan_cloud_upload_no_canvas_smoke COMMAND pano_cli plan-cloud-upload --no-canvas --new-document --unsaved) @@ -945,7 +945,7 @@ if(TARGET pano_cli) COMMAND pano_cli plan-cloud-upload-all --file-count 3) set_tests_properties(pano_cli_plan_cloud_upload_all_progress_smoke PROPERTIES LABELS "app;integration;desktop-fast" - PASS_REGULAR_EXPRESSION "\"command\":\"plan-cloud-upload-all\".*\"fileCount\":3.*\"progressUiAvailable\":true.*\"progressTotal\":3.*\"showProgress\":true") + PASS_REGULAR_EXPRESSION "\"command\":\"plan-cloud-upload-all\".*\"fileCount\":3.*\"progressUiAvailable\":true.*\"progressTotal\":3.*\"showProgress\":true.*\"progressDialog\":\\{\"title\":\"Export Pano Image\".*\"total\":3") add_test(NAME pano_cli_plan_cloud_upload_all_headless_smoke COMMAND pano_cli plan-cloud-upload-all --file-count 3 --no-progress-ui) @@ -957,7 +957,7 @@ if(TARGET pano_cli) COMMAND pano_cli plan-cloud-transfer --direction download --progress --disable-tls-verification --progress-total 100 --progress-current 25) set_tests_properties(pano_cli_plan_cloud_transfer_download_smoke PROPERTIES LABELS "app;integration;desktop-fast" - PASS_REGULAR_EXPRESSION "\"command\":\"plan-cloud-transfer\".*\"direction\":\"download\".*\"action\":\"start-transfer\".*\"enableProgress\":true.*\"disableTlsVerification\":true.*\"notify\":true.*\"fraction\":0.25") + PASS_REGULAR_EXPRESSION "\"command\":\"plan-cloud-transfer\".*\"direction\":\"download\".*\"action\":\"start-transfer\".*\"enableProgress\":true.*\"disableTlsVerification\":true.*\"notify\":true.*\"fraction\":0.25.*\"progressPrompt\":\\{\"title\":\"Downloading\".*\"formattedMessage\":\"Download in progress 25.00%\"") add_test(NAME pano_cli_plan_cloud_transfer_upload_smoke COMMAND pano_cli plan-cloud-transfer --direction upload --source D:/Paint/demo.ppi --progress-total 10 --progress-current 20) diff --git a/tests/app_core/document_cloud_tests.cpp b/tests/app_core/document_cloud_tests.cpp index 4279c2f..a790317 100644 --- a/tests/app_core/document_cloud_tests.cpp +++ b/tests/app_core/document_cloud_tests.cpp @@ -157,6 +157,41 @@ void cloud_bulk_upload_clamps_progress_total(pp::tests::Harness& harness) PP_EXPECT(harness, plan.show_progress); } +void cloud_dialog_plans_preserve_upload_prompt_metadata(pp::tests::Harness& harness) +{ + const auto save_required = pp::app::plan_cloud_save_required_prompt(); + const auto publish = pp::app::plan_cloud_publish_prompt(); + const auto success = pp::app::plan_cloud_upload_success_prompt(); + + PP_EXPECT(harness, save_required.title == "Warning"); + PP_EXPECT(harness, save_required.message == "This document needs to be saved before upload."); + PP_EXPECT(harness, !save_required.show_cancel); + PP_EXPECT(harness, publish.title == "Publish document"); + PP_EXPECT(harness, publish.message == "Would you like to upload to the public domain?"); + PP_EXPECT(harness, publish.ok_caption == "Yes"); + PP_EXPECT(harness, publish.cancel_caption == "No"); + PP_EXPECT(harness, publish.show_cancel); + PP_EXPECT(harness, success.title == "Success"); + PP_EXPECT(harness, success.message == "This document has been succesfully uploaded."); + PP_EXPECT(harness, !success.show_cancel); +} + +void cloud_progress_dialog_plans_preserve_legacy_titles(pp::tests::Harness& harness) +{ + const auto upload = pp::app::plan_cloud_upload_progress_dialog(); + const auto bulk = pp::app::plan_cloud_bulk_upload_progress_dialog(4); + const auto download = pp::app::plan_cloud_download_progress_prompt(); + + PP_EXPECT(harness, upload.title == "Uploading"); + PP_EXPECT(harness, upload.total == 0); + PP_EXPECT(harness, bulk.title == "Export Pano Image"); + PP_EXPECT(harness, bulk.total == 4); + PP_EXPECT(harness, download.title == "Downloading"); + PP_EXPECT(harness, download.message == "Download in progress"); + PP_EXPECT(harness, download.show_cancel); + PP_EXPECT(harness, pp::app::format_cloud_download_progress_message(0.25F) == "Download in progress 25.00%"); +} + void cloud_download_transfer_rejects_missing_url(pp::tests::Harness& harness) { const auto plan = pp::app::plan_cloud_download_transfer("", "D:/Paint/demo.ppi", true, true); @@ -319,6 +354,8 @@ int main() harness.run("cloud bulk upload runs without progress when ui unavailable", cloud_bulk_upload_runs_without_progress_when_ui_unavailable); harness.run("cloud bulk upload keeps zero file progress explicit", cloud_bulk_upload_keeps_zero_file_progress_explicit); harness.run("cloud bulk upload clamps progress total", cloud_bulk_upload_clamps_progress_total); + harness.run("cloud dialog plans preserve upload prompt metadata", cloud_dialog_plans_preserve_upload_prompt_metadata); + harness.run("cloud progress dialog plans preserve legacy titles", cloud_progress_dialog_plans_preserve_legacy_titles); harness.run("cloud download transfer rejects missing url", cloud_download_transfer_rejects_missing_url); harness.run("cloud download transfer rejects missing destination", cloud_download_transfer_rejects_missing_destination); harness.run("cloud download transfer starts with progress and tls policy", cloud_download_transfer_starts_with_progress_and_tls_policy); diff --git a/tools/pano_cli/main.cpp b/tools/pano_cli/main.cpp index 0a5f3f0..0ba33af 100644 --- a/tools/pano_cli/main.cpp +++ b/tools/pano_cli/main.cpp @@ -3627,8 +3627,20 @@ int plan_cloud_upload(int argc, char** argv) << ",\"newDocument\":" << json_bool(args.new_document) << ",\"unsaved\":" << json_bool(args.unsaved) << "},\"decision\":\"" << cloud_upload_action_name(plan.action) - << "\",\"saveBeforeUpload\":" << json_bool(plan.save_before_upload) - << "}\n"; + << "\",\"saveBeforeUpload\":" << json_bool(plan.save_before_upload); + if (plan.action == pp::app::CloudUploadAction::show_save_required_warning + || plan.action == pp::app::CloudUploadAction::prompt_publish) { + const auto prompt = plan.action == pp::app::CloudUploadAction::show_save_required_warning + ? pp::app::plan_cloud_save_required_prompt() + : pp::app::plan_cloud_publish_prompt(); + std::cout << ",\"prompt\":{\"title\":\"" << json_escape(prompt.title) + << "\",\"message\":\"" << json_escape(prompt.message) + << "\",\"okCaption\":\"" << json_escape(prompt.ok_caption) + << "\",\"cancelCaption\":\"" << json_escape(prompt.cancel_caption) + << "\",\"showCancel\":" << json_bool(prompt.show_cancel) + << "}"; + } + std::cout << "}\n"; return 0; } @@ -3716,7 +3728,16 @@ int plan_cloud_upload_all(int argc, char** argv) << "},\"plan\":{\"fileCount\":" << plan.file_count << ",\"progressTotal\":" << plan.progress_total << ",\"showProgress\":" << json_bool(plan.show_progress) - << "}}\n"; + << "}"; + if (plan.show_progress) { + const auto progress = pp::app::plan_cloud_bulk_upload_progress_dialog(plan.progress_total); + std::cout << ",\"progressDialog\":{\"title\":\"" << json_escape(progress.title) + << "\",\"total\":" << progress.total + << ",\"count\":" << progress.count + << ",\"progressFraction\":" << progress.progress_fraction + << "}"; + } + std::cout << "}\n"; return 0; } @@ -3815,7 +3836,18 @@ int plan_cloud_transfer(int argc, char** argv) << ",\"disableTlsVerification\":" << json_bool(transfer.disable_tls_verification) << "},\"progress\":{\"notify\":" << json_bool(progress.notify) << ",\"fraction\":" << progress.fraction - << "}}\n"; + << "}"; + if (transfer.direction == pp::app::CloudTransferDirection::download + && transfer.action == pp::app::CloudTransferAction::start_transfer) { + const auto prompt = pp::app::plan_cloud_download_progress_prompt(); + std::cout << ",\"progressPrompt\":{\"title\":\"" << json_escape(prompt.title) + << "\",\"message\":\"" << json_escape(prompt.message) + << "\",\"showCancel\":" << json_bool(prompt.show_cancel) + << ",\"formattedMessage\":\"" + << json_escape(pp::app::format_cloud_download_progress_message(progress.fraction)) + << "\"}"; + } + std::cout << "}\n"; return 0; }