diff --git a/cmake/PanoPainterSources.cmake b/cmake/PanoPainterSources.cmake index ad242e6..5d1cbdb 100644 --- a/cmake/PanoPainterSources.cmake +++ b/cmake/PanoPainterSources.cmake @@ -58,6 +58,8 @@ set(PP_LEGACY_UI_CORE_SOURCES set(PP_LEGACY_APP_SOURCES src/canvas_modes.cpp + src/legacy_app_shell_services.cpp + src/legacy_app_shell_services.h src/legacy_document_canvas_services.cpp src/legacy_document_canvas_services.h src/legacy_document_layer_services.cpp diff --git a/docs/modernization/build-inventory.md b/docs/modernization/build-inventory.md index 38c9dc4..936714c 100644 --- a/docs/modernization/build-inventory.md +++ b/docs/modernization/build-inventory.md @@ -160,6 +160,13 @@ Known local toolchain state: operations. It keeps those live paths on the `pp_app_core` contracts while legacy `Canvas`, `NodeLayer`, `NodePanelLayer`, and `ActionManager` execution remain tracked by `DEBT-0021` and `DEBT-0032`. +- `src/legacy_app_shell_services.*` is the current app-shell bridge for File + menu routing, export-menu routing, main-toolbar commands, About menu + commands, and direct Tools menu commands. It keeps those live paths on the + `pp_app_core` contracts while legacy dialogs, pickers, cloud/share/export, + Tools, About, history, canvas-clear, and settings execution remain tracked by + `DEBT-0029`, `DEBT-0030`, `DEBT-0031`, `DEBT-0033`, `DEBT-0034`, and + `DEBT-0035`. - `pano_cli simulate-image-import` decodes an embedded tiny PNG through `pp_assets`, attaches it to `pp_document`, and is covered by `pano_cli_simulate_image_import_smoke`. diff --git a/docs/modernization/debt.md b/docs/modernization/debt.md index 1ddfe24..93a30a7 100644 --- a/docs/modernization/debt.md +++ b/docs/modernization/debt.md @@ -46,13 +46,13 @@ agent or engineer to remove them without reconstructing context from chat. | DEBT-0026 | Open | Modernization | Toolbar history command planning and canvas hotkey history dispatch now consume pure `pp_app_core` through `App::init_toolbar_main`, `NodeCanvas`, `pano_cli plan-history-operation`, and the `HistoryUiServices` boundary, and both live callers share `src/legacy_history_services.*` for saturated legacy history metrics and execution, but the shared live bridge still mutates legacy `ActionManager` stacks directly | Preserve undo/redo/clear behavior while moving action history toward document/app command services | `pp_app_core_history_ui_tests`; `pano_cli plan-history-operation --kind undo --undo-count 2`; `pano_cli plan-history-operation --kind clear --undo-count 2 --redo-count 1 --memory-bytes 4096`; `ctest --preset desktop-fast --build-config Debug` | Undo/redo/clear execution is owned by injected document/app history services with no legacy `ActionManager` adapter | | DEBT-0027 | Open | Modernization | Canvas draw-tool toolbar command, canvas input mode switching, active-state planning/execution dispatch, and canvas keyboard/touch command planning now consume pure `pp_app_core` through `App::init_toolbar_draw`, `App::update`, `NodeCanvas`, `pano_cli plan-canvas-tool`, `pano_cli plan-canvas-tool-state`, `pano_cli plan-canvas-hotkey`, `CanvasToolServices`, and `CanvasHotkeyServices`, but live adapters still mutate or read legacy `Canvas` mode state, pen picking state, touch-lock state, transform copy/cut action objects, `ActionManager`, legacy save UI, legacy stroke size controls, and cursor/UI singletons | Preserve current toolbar, stylus eraser, keyboard, and touch command behavior while canvas input/tools move toward an app/document command boundary | `pp_app_core_canvas_tool_ui_tests`; `pp_app_core_canvas_hotkey_tests`; `pano_cli plan-canvas-tool --kind copy`; `pano_cli plan-canvas-tool-state --mode draw --picking --touch-lock`; `pano_cli plan-canvas-hotkey --event key-up --key z --ctrl --undo-count 2`; `pano_cli plan-canvas-hotkey --event key-up --key s --ctrl --shift`; `ctest --preset desktop-fast --build-config Debug` | Canvas tool selection, toolbar state refresh, picking, touch lock, stylus eraser/key mode switching, hotkey/touch command dispatch, save hotkeys, history hotkeys, brush-size hotkeys, and transform action execution are owned by injected app/document/canvas services with no legacy toolbar/canvas adapter | | DEBT-0028 | Open | Modernization | Canvas clear command planning and execution dispatch now consume pure `pp_app_core` through `App::init_toolbar_main`, Layer menu clear, `pano_cli plan-canvas-clear`, and the `DocumentCanvasClearServices` boundary, and toolbar/Layer-menu clear share `src/legacy_document_canvas_services.*`, but the shared live bridge still calls legacy `Canvas::clear`, which records `ActionLayerClear`, clears the current layer/frame, and marks legacy `Canvas::I` unsaved | Preserve clear-current-layer behavior while canvas/document commands move toward document/app command services | `pp_app_core_document_canvas_tests`; `pano_cli plan-canvas-clear --r 0 --g 0.1 --b 0.2 --a 0.3`; `pano_cli plan-canvas-clear --no-canvas`; `pano_cli plan-layer-menu --command clear --current-index 1 --current-name Paint`; `ctest --preset desktop-fast --build-config Debug` | Canvas clear execution, undo recording, dirty-state updates, and clear color handling are owned by injected document/app services with no legacy canvas-clear adapter | -| DEBT-0029 | Open | Modernization | Image import route planning and execution dispatch now consume pure `pp_app_core` through the File menu, `pano_cli plan-image-import`, and the `DocumentImageImportServices` boundary, but the live adapter still loads images with legacy `Image`, calls legacy `Canvas::import_equirectangular`, or configures legacy import transform mode directly | Preserve current File > Import behavior while image import moves toward document/app/asset command services | `pp_app_core_document_import_tests`; `pano_cli plan-image-import --width 4096 --height 2048`; `pano_cli plan-image-import --width 1024 --height 1024`; `ctest --preset desktop-fast --build-config Debug` | Image loading, equirectangular import, transform-placement import, and failure reporting are owned by injected document/app/asset services with File-menu callbacks acting only as adapters and no legacy image-import adapter | -| DEBT-0030 | Open | Modernization | File export menu action planning and execution dispatch now consume pure `pp_app_core` through the File menu, `pano_cli plan-export-menu`, and the `DocumentExportMenuServices` boundary, but the live adapter still opens legacy export dialogs and then reaches legacy canvas/render/video export code | Preserve current export menu behavior while export command execution moves toward document/app/renderer/video services | `pp_app_core_document_export_tests`; `pano_cli plan-export-menu --kind png`; `pano_cli plan-export-menu --kind animation-mp4 --demo`; `pano_cli plan-export-menu --kind layers --no-canvas`; `ctest --preset desktop-fast --build-config Debug` | Export menu routing, license gating, target creation, image/layer/cube/depth/animation/timelapse execution, and error reporting are owned by injected document/app/renderer/video services with File-menu callbacks acting only as UI adapters and no legacy export adapter | -| DEBT-0031 | Open | Modernization | Top-level File menu command planning and execution dispatch now consume pure `pp_app_core` through `App::init_menu_file`, `pano_cli plan-file-menu`, and the `FileMenuServices` boundary, but the live adapter still invokes legacy dialogs, platform pickers, cloud code, share code, and canvas import/export paths directly | Preserve File menu behavior while app workflows move toward app/document/platform command services | `pp_app_core_file_menu_tests`; `pano_cli plan-file-menu --command save-as`; `pano_cli plan-file-menu --command import`; `pano_cli plan-file-menu --command cloud-upload`; `ctest --preset desktop-fast --build-config Debug` | File menu routing, picker dispatch, save/share/cloud/resize/export execution, and image/project import execution are owned by injected app/document/platform services with `App::init_menu_file` acting only as a UI adapter and no legacy File menu adapter | +| DEBT-0029 | Open | Modernization | Image import route planning and execution dispatch now consume pure `pp_app_core` through the File menu, `pano_cli plan-image-import`, and the `DocumentImageImportServices` boundary, and live File-menu import execution is centralized in `src/legacy_app_shell_services.*`, but the bridge still loads images with legacy `Image`, calls legacy `Canvas::import_equirectangular`, or configures legacy import transform mode directly | Preserve current File > Import behavior while image import moves toward document/app/asset command services | `pp_app_core_document_import_tests`; `pano_cli plan-image-import --width 4096 --height 2048`; `pano_cli plan-image-import --width 1024 --height 1024`; `ctest --preset desktop-fast --build-config Debug` | Image loading, equirectangular import, transform-placement import, and failure reporting are owned by injected document/app/asset services with File-menu callbacks acting only as adapters and no legacy image-import adapter | +| DEBT-0030 | Open | Modernization | File export menu action planning and execution dispatch now consume pure `pp_app_core` through the File menu, `pano_cli plan-export-menu`, and the `DocumentExportMenuServices` boundary, and live execution is centralized in `src/legacy_app_shell_services.*`, but the bridge still opens legacy export dialogs and then reaches legacy canvas/render/video export code | Preserve current export menu behavior while export command execution moves toward document/app/renderer/video services | `pp_app_core_document_export_tests`; `pano_cli plan-export-menu --kind png`; `pano_cli plan-export-menu --kind animation-mp4 --demo`; `pano_cli plan-export-menu --kind layers --no-canvas`; `ctest --preset desktop-fast --build-config Debug` | Export menu routing, license gating, target creation, image/layer/cube/depth/animation/timelapse execution, and error reporting are owned by injected document/app/renderer/video services with File-menu callbacks acting only as UI adapters and no legacy export adapter | +| DEBT-0031 | Open | Modernization | Top-level File menu command planning and execution dispatch now consume pure `pp_app_core` through `App::init_menu_file`, `pano_cli plan-file-menu`, and the `FileMenuServices` boundary, and live execution is centralized in `src/legacy_app_shell_services.*`, but the bridge still invokes legacy dialogs, platform pickers, cloud code, share code, and canvas import/export paths directly | Preserve File menu behavior while app workflows move toward app/document/platform command services | `pp_app_core_file_menu_tests`; `pano_cli plan-file-menu --command save-as`; `pano_cli plan-file-menu --command import`; `pano_cli plan-file-menu --command cloud-upload`; `ctest --preset desktop-fast --build-config Debug` | File menu routing, picker dispatch, save/share/cloud/resize/export execution, and image/project import execution are owned by injected app/document/platform services with `App::init_menu_file` acting only as a UI adapter and no legacy File menu adapter | | DEBT-0032 | Open | Modernization | Layer menu command planning and execution dispatch now consume pure `pp_app_core` through `App::init_menu_layer`, `pano_cli plan-layer-menu`, and the `DocumentLayerMenuServices` boundary; Layer menu clear reuses the `DocumentCanvasClearServices` executor; and Layer menu rename/clear/merge now share `src/legacy_document_layer_services.*`, but the bridge still calls the legacy rename dialog path, `NodePanelLayer::merge`, and reads `Canvas::I` animation/layer state directly | Preserve existing Layer menu behavior while layer commands move toward document/app services | `pp_app_core_document_layer_tests`; `pano_cli plan-layer-menu --command clear --current-index 1 --current-name Paint`; `pano_cli plan-layer-menu --command merge --current-index 2 --lower-name Paint`; `pano_cli plan-layer-merge --layer-count 3 --from-index 2 --to-index 1`; `pano_cli plan-layer-merge --layer-count 3 --from-index 2 --to-index 1 --animation-duration 3`; `pano_cli plan-layer-menu --command rename --no-current-layer`; `ctest --preset desktop-fast --build-config Debug` | Layer rename, merge-down execution, animation gating, and selected-layer state are owned by injected document/app services with Layer-menu callbacks acting only as UI adapters and no legacy Layer menu adapter | -| DEBT-0033 | Open | Modernization | Tools menu planning and direct command execution dispatch now consume pure `pp_app_core` through `App::init_menu_tools`, `pano_cli plan-tools-menu`, `pano_cli plan-tools-panel`, and the `ToolsMenuServices` boundary, but live adapters still construct legacy `NodePanelFloating` panels, mutate legacy panel nodes, clear `CanvasModeGrid`, reset `NodeCanvas` camera state, open legacy shortcuts UI, and call the iOS SonarPen bridge directly | Preserve current Tools menu behavior while UI shell actions move toward app/UI/platform services | `pp_app_core_tools_menu_tests`; `pano_cli plan-tools-menu --command shortcuts`; `pano_cli plan-tools-panel --panel layers`; `pano_cli plan-tools-panel --panel animation --already-visible`; `ctest --preset desktop-fast --build-config Debug` | Tools panel creation, submenu routing, grid clear, camera reset, shortcuts dialog, and SonarPen dispatch are owned by injected app/UI/platform services with `App::init_menu_tools` acting only as a UI adapter and no legacy Tools adapter | -| DEBT-0034 | Open | Modernization | About menu command planning and execution dispatch now consume pure `pp_app_core` through `App::init_menu_about`, `pano_cli plan-about-menu`, and the `AboutMenuServices` boundary, but the live adapter still opens legacy About/manual/what's-new dialogs, invokes the injected crash hook, and runs the legacy Canvas stroke performance test directly | Preserve About menu behavior while dialogs and diagnostics move toward app/UI/platform services | `pp_app_core_about_menu_tests`; `pano_cli plan-about-menu --command news --version-major 2 --version-minor 5 --version-fix 7`; `pano_cli plan-about-menu --command performance --no-canvas`; `ctest --preset desktop-fast --build-config Debug` | About/manual/what's-new dialog dispatch, crash-test dispatch, and performance-test execution are owned by injected app/UI/platform services with `App::init_menu_about` acting only as a UI adapter and no legacy About adapter | -| 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, and history/canvas commands now hand off through `HistoryUiServices` and `DocumentCanvasClearServices`, but the live adapter 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-0033 | Open | Modernization | Tools menu planning and direct command execution dispatch now consume pure `pp_app_core` through `App::init_menu_tools`, `pano_cli plan-tools-menu`, `pano_cli plan-tools-panel`, and the `ToolsMenuServices` boundary, and direct command execution is centralized in `src/legacy_app_shell_services.*`, but live adapters still construct legacy `NodePanelFloating` panels, mutate legacy panel nodes, clear `CanvasModeGrid`, reset `NodeCanvas` camera state, open legacy shortcuts UI, and call the iOS SonarPen bridge directly | Preserve current Tools menu behavior while UI shell actions move toward app/UI/platform services | `pp_app_core_tools_menu_tests`; `pano_cli plan-tools-menu --command shortcuts`; `pano_cli plan-tools-panel --panel layers`; `pano_cli plan-tools-panel --panel animation --already-visible`; `ctest --preset desktop-fast --build-config Debug` | Tools panel creation, submenu routing, grid clear, camera reset, shortcuts dialog, and SonarPen dispatch are owned by injected app/UI/platform services with `App::init_menu_tools` acting only as a UI adapter and no legacy Tools adapter | +| DEBT-0034 | Open | Modernization | About menu command planning and execution dispatch now consume pure `pp_app_core` through `App::init_menu_about`, `pano_cli plan-about-menu`, and the `AboutMenuServices` boundary, and live execution is centralized in `src/legacy_app_shell_services.*`, but the bridge still opens legacy About/manual/what's-new dialogs, invokes the injected crash hook, and runs the legacy Canvas stroke performance test directly | Preserve About menu behavior while dialogs and diagnostics move toward app/UI/platform services | `pp_app_core_about_menu_tests`; `pano_cli plan-about-menu --command news --version-major 2 --version-minor 5 --version-fix 7`; `pano_cli plan-about-menu --command performance --no-canvas`; `ctest --preset desktop-fast --build-config Debug` | About/manual/what's-new dialog dispatch, crash-test dispatch, and performance-test execution are owned by injected app/UI/platform services with `App::init_menu_about` acting only as a UI adapter and no legacy About adapter | +| 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::render_device_features` as the backend conversion point. `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. Actual live stroke rasterization, dual-brush compositing, pattern feedback math, thumbnail layer compositing, and brush-preview compositing 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 | ## Closed Debt diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index dba5e7c..87a39ee 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -557,13 +557,14 @@ transform-mode setup continues. `pano_cli plan-file-menu` exposes app-core planning for the top-level File menu commands, including new/open/import, save/save-as/save-version, share, resize, cloud upload/browse, JPEG export, and export-submenu routing. Direct File menu -commands now dispatch through `FileMenuServices` before legacy dialogs, pickers, -platform services, cloud code, and canvas workflows continue. +commands now dispatch through `FileMenuServices` in the shared app-shell bridge +`src/legacy_app_shell_services.*` before legacy dialogs, pickers, platform +services, cloud code, and canvas workflows continue. `pano_cli plan-export-menu` exposes app-core planning for File menu export choices, including image, layer, cube-face, depth, animation-frame, MP4, and timelapse dialog routing plus license/canvas gating. Export menu commands now -dispatch through `DocumentExportMenuServices` before legacy export dialogs and -renderer/video execution continue. +dispatch through `DocumentExportMenuServices` in the shared app-shell bridge +before legacy export dialogs and renderer/video execution continue. `pano_cli plan-grid-operation` exposes app-core planning for grid heightmap pick/load/reload/clear, lightmap render capability/limit checks, and heightmap commit used by the live grid panel before legacy image loading, OpenGL texture @@ -579,8 +580,8 @@ toolbar/status-bar shell, including open/save dialogs, undo/redo availability, clear-history availability, clear-canvas no-canvas blocking, message-box creation, and settings dialog routing. `pp_app_core` now also owns a `MainToolbarServices` executor boundary, so `App::init_toolbar_main` dispatches -through a legacy adapter before legacy dialogs, history/canvas adapters, and -settings UI execution continue. +through `src/legacy_app_shell_services.*` before legacy dialogs, +history/canvas adapters, and settings UI execution continue. `pano_cli plan-quick-operation` exposes app-core planning for quick brush/color slot selection versus popup opening, plus quick mini-state restore/reset validation used by the live quick panel. Quick-panel execution now dispatches @@ -590,16 +591,17 @@ preview, and preset popup adapter continues. planning for top-level Tools commands and floating-panel requests, including already-visible no-ops, panel chrome metadata, shortcuts, camera reset, grid-clear, and platform-only SonarPen gating. Direct Tools commands now -dispatch through `ToolsMenuServices` before the legacy UI/panel/canvas/platform -adapters continue execution. The live animation panel route now also checks -animation panel visibility and applies animation panel layout state instead of -using the grid panel by mistake. +dispatch through `ToolsMenuServices` in the shared app-shell bridge before the +legacy UI/panel/canvas/platform adapters continue execution. The live animation +panel route now also checks animation panel visibility and applies animation +panel layout state instead of using the grid panel by mistake. `pano_cli plan-about-menu` exposes app-core planning for About menu help, about, what's-new, crash-test, and performance-test commands, including versioned what's-new labels, diagnostic gating, and no-canvas performance-test blocking. `pp_app_core` now also owns an `AboutMenuServices` executor boundary, -so `App::init_menu_about` dispatches through a legacy adapter before legacy -dialogs, platform crash hooks, and Canvas performance strokes continue. +so `App::init_menu_about` dispatches through `src/legacy_app_shell_services.*` +before legacy dialogs, platform crash hooks, and Canvas performance strokes +continue. `pp_platform_api` now owns a headless `PlatformServices` interface for startup storage path preparation, clipboard text, cursor visibility, virtual-keyboard visibility, UI-thread lifecycle hooks, render-context diff --git a/src/app_layout.cpp b/src/app_layout.cpp index da8722e..30f0a98 100644 --- a/src/app_layout.cpp +++ b/src/app_layout.cpp @@ -13,12 +13,11 @@ #include "app_core/document_layer.h" #include "app_core/document_canvas.h" #include "app_core/document_export.h" -#include "app_core/document_import.h" #include "app_core/file_menu.h" #include "app_core/app_status.h" #include "app_core/main_toolbar.h" #include "app_core/tools_menu.h" -#include "legacy_document_canvas_services.h" +#include "legacy_app_shell_services.h" #include "legacy_document_layer_services.h" #include "legacy_history_services.h" #include "settings.h" @@ -144,163 +143,12 @@ bool apply_brush_preset_plan(App& app, const std::shared_ptr& brush) bool apply_document_export_menu_plan(App& app, pp::app::DocumentExportMenuKind kind) { - class LegacyDocumentExportMenuServices final : public pp::app::DocumentExportMenuServices { - public: - explicit LegacyDocumentExportMenuServices(App& app) noexcept - : app_(app) - { - } - - void show_jpeg_dialog() override { app_.dialog_export(".jpg"); } - void show_png_dialog() override { app_.dialog_export(".png"); } - void show_layers_dialog() override { app_.dialog_export_layers(); } - void show_cube_faces_dialog() override { app_.dialog_export_cube_faces(); } - void show_depth_dialog() override { app_.dialog_export_depth(); } - void show_animation_frames_dialog() override { app_.dialog_export_anim_frames(); } - void show_animation_mp4_dialog() override { app_.dialog_export_mp4(); } - void show_timelapse_dialog() override { app_.dialog_timelapse_export(); } - void show_license_disabled() override - { - app_.message_box("License", "This function is disabled in demo mode."); - } - - private: - App& app_; - }; - - const auto requires_license = pp::app::document_export_menu_requires_license(kind); - const auto plan = pp::app::plan_document_export_menu_action( - kind, - app.canvas != nullptr, - !requires_license || app.check_license()); - LegacyDocumentExportMenuServices services(app); - const auto status = pp::app::execute_document_export_menu_plan(plan, services); - if (!status.ok()) - LOG("Document export menu action failed: %s", status.message); - return status.ok() && plan.opens_dialog; + return pp::panopainter::apply_legacy_document_export_menu_plan(app, kind); } -class LegacyFileMenuServices final : public pp::app::FileMenuServices { -public: - explicit LegacyFileMenuServices(App& app) noexcept - : app_(app) - { - } - - void show_new_document_dialog() override - { - app_.dialog_newdoc(); - } - - void pick_image_for_import() override - { - auto* app_ptr = &app_; - app_.pick_image([app_ptr](std::string path) { - Image img; - img.load_file(path); - const auto import_plan = pp::app::plan_document_image_import(img.width, img.height); - if (!import_plan) - return; - - class LegacyDocumentImageImportServices final : public pp::app::DocumentImageImportServices { - public: - LegacyDocumentImageImportServices(App& app, Image& image) noexcept - : app_(app) - , image_(image) - { - } - - void import_equirectangular(std::string_view import_path) override - { - if (Canvas::I) - Canvas::I->import_equirectangular(std::string(import_path)); - } - - void enter_transform_import(std::string_view) override - { - if (!app_.canvas || !app_.canvas->m_canvas) - return; - - auto* mode = static_cast( - app_.canvas->m_canvas->modes[(int)kCanvasMode::Import][0]); - mode->m_action = CanvasModeTransform::ActionType::Import; - mode->m_source_image = std::move(image_); - Canvas::set_mode(kCanvasMode::Import); - } - - private: - App& app_; - Image& image_; - }; - - LegacyDocumentImageImportServices services(*app_ptr, img); - const auto status = pp::app::execute_document_image_import_plan( - import_plan.value(), - path, - services); - if (!status.ok()) - LOG("Image import failed: %s", status.message); - }); - } - - void pick_project_file() override - { - auto* app_ptr = &app_; - app_.pick_file({ "ppi" }, [app_ptr](std::string path) { - app_ptr->open_document(path); - }); - } - - void show_cloud_browser_dialog() override - { - app_.dialog_browse(); - } - - void save_document(pp::app::DocumentSaveIntent intent) override - { - app_.save_document(intent); - } - - void show_export_jpeg_dialog(pp::app::DocumentExportMenuKind kind) override - { - apply_document_export_menu_plan(app_, kind); - } - - void show_export_submenu() override - { - } - - void share_document() override - { - app_.share_file(app_.doc_path); - } - - void show_resize_dialog() override - { - app_.dialog_resize(); - } - - void upload_to_cloud() override - { - app_.cloud_upload(); - } - - void browse_cloud_documents() override - { - app_.cloud_browse(); - } - -private: - App& app_; -}; - void apply_file_menu_plan(App& app, pp::app::FileMenuCommand command) { - const auto plan = pp::app::plan_file_menu_command(command); - LegacyFileMenuServices services(app); - const auto status = pp::app::execute_file_menu_plan(plan, services); - if (!status.ok()) - LOG("File menu action failed: %s", status.message); + pp::panopainter::apply_legacy_file_menu_command(app, command); } pp::app::DocumentLayerMenuPlan make_layer_menu_plan( @@ -359,204 +207,19 @@ void apply_tools_panel_chrome(NodePanelFloating& panel, const pp::app::ToolsPane panel.m_droppable = plan.droppable; } -class LegacyMainToolbarServices final : public pp::app::MainToolbarServices { -public: - explicit LegacyMainToolbarServices(App& app) noexcept - : app_(app) - { - } - - void show_open_dialog() override - { - app_.dialog_open(); - } - - void show_save_dialog() override - { - app_.dialog_save(); - } - - void invoke_undo(const pp::app::HistoryUiPlan& plan) override - { - execute_history_plan(plan); - } - - void invoke_redo(const pp::app::HistoryUiPlan& plan) override - { - execute_history_plan(plan); - } - - void clear_history(const pp::app::HistoryUiPlan& plan) override - { - execute_history_plan(plan); - } - - void clear_canvas(const pp::app::DocumentCanvasClearPlan& plan) override - { - const auto status = pp::panopainter::execute_legacy_document_canvas_clear_plan(app_, plan); - if (!status.ok()) - LOG("Canvas clear failed: %s", status.message); - } - - void show_message_box() override - { - app_.msgbox = new NodeMessageBox(); - app_.msgbox->set_manager(&app_.layout); - app_.msgbox->init(); - app_.layout[app_.main_id]->add_child(app_.msgbox); - } - - void show_settings_dialog() override - { - app_.settings = new NodeSettings(); - app_.settings->set_manager(&app_.layout); - app_.settings->init(); - app_.layout[app_.main_id]->add_child(app_.settings); - } - -private: - void execute_history_plan(const pp::app::HistoryUiPlan& plan) - { - const auto status = pp::panopainter::execute_legacy_history_plan(plan); - if (!status.ok()) - LOG("History action failed: %s", status.message); - } - - App& app_; -}; - -class LegacyAboutMenuServices final : public pp::app::AboutMenuServices { -public: - explicit LegacyAboutMenuServices(App& app) noexcept - : app_(app) - { - } - - void show_user_manual() override - { - app_.dialog_usermanual(); - } - - void show_about_dialog() override - { - app_.dialog_about(); - } - - void show_whats_new_dialog() override - { - app_.dialog_whatsnew(true); - } - - void trigger_crash_test() override - { - LOG("crashing"); - app_.crash_test(); - } - - void run_performance_test(const pp::app::AboutMenuPlan& plan) override - { - if (!Canvas::I) - return; - - LOG("perf"); - std::string message; - const int performance_iterations = plan.performance_iterations; - app_.render_task([&] - { - auto start = std::chrono::high_resolution_clock::now(); - Canvas::I->stroke_start({ 0, 0, 0 }, 0.9f); - for (int i = 0; i < performance_iterations; i++) - { - Canvas::I->stroke_update({ 100, 100, 0 }, 0.9f); - Canvas::I->stroke_update({ 200, 200, 0 }, 0.9f); - Canvas::I->stroke_update({ 200, 100, 0 }, 0.9f); - Canvas::I->stroke_update({ 100, 200, 0 }, 0.9f); - Canvas::I->stroke_update({ 300, 300, 0 }, 0.9f); - Canvas::I->stroke_update({ 200, 500, 0 }, 0.9f); - Canvas::I->stroke_update({ 500, 500, 0 }, 0.9f); - Canvas::I->stroke_update({ 400, 400, 0 }, 0.9f); - Canvas::I->stroke_update({ 0, 200, 0 }, 0.9f); - Canvas::I->stroke_update({ 200, 0, 0 }, 0.9f); - Canvas::I->stroke_draw(); - } - Canvas::I->stroke_end(); - auto diff = std::chrono::high_resolution_clock::now() - start; - auto ms = std::chrono::duration_cast(diff).count(); - LOG("%lld ms", ms); - message = "Time " + std::to_string(ms) + " ms"; - }); - app_.message_box("Performance test", message); - } - -private: - App& app_; -}; - -class LegacyToolsMenuServices final : public pp::app::ToolsMenuServices { -public: - explicit LegacyToolsMenuServices(App& app) noexcept - : app_(app) - { - } - - void show_panels_submenu() override - { - } - - void show_options_submenu() override - { - } - - void clear_grid_overlays() override - { - auto* mode = static_cast(Canvas::modes[(int)kCanvasMode::Grid][0]); - mode->clear(); - } - - void reset_camera() override - { - if (app_.canvas) - app_.canvas->reset_camera(); - } - - void show_shortcuts_dialog() override - { - app_.dialog_shortcuts(); - } - - void start_sonarpen() override - { -#if __IOS__ - [app_.ios_app sonarpen_start]; -#endif - } - -private: - App& app_; -}; - void execute_main_toolbar_plan(App& app, const pp::app::MainToolbarPlan& plan) { - LegacyMainToolbarServices services(app); - const auto status = pp::app::execute_main_toolbar_plan(plan, services); - if (!status.ok()) - LOG("Main toolbar action failed: %s", status.message); + pp::panopainter::execute_legacy_main_toolbar_plan(app, plan); } void execute_about_menu_plan(App& app, const pp::app::AboutMenuPlan& plan) { - LegacyAboutMenuServices services(app); - const auto status = pp::app::execute_about_menu_plan(plan, services); - if (!status.ok()) - LOG("About menu action failed: %s", status.message); + pp::panopainter::execute_legacy_about_menu_plan(app, plan); } void execute_tools_menu_plan(App& app, const pp::app::ToolsMenuPlan& plan) { - LegacyToolsMenuServices services(app); - const auto status = pp::app::execute_tools_menu_plan(plan, services); - if (!status.ok()) - LOG("Tools menu action failed: %s", status.message); + pp::panopainter::execute_legacy_tools_menu_plan(app, plan); } void execute_document_layer_menu_plan(App& app, const pp::app::DocumentLayerMenuPlan& plan) diff --git a/src/legacy_app_shell_services.cpp b/src/legacy_app_shell_services.cpp new file mode 100644 index 0000000..bb9d60c --- /dev/null +++ b/src/legacy_app_shell_services.cpp @@ -0,0 +1,376 @@ +#include "pch.h" + +#include "legacy_app_shell_services.h" + +#include "app.h" +#include "app_core/document_import.h" +#include "legacy_document_canvas_services.h" +#include "legacy_history_services.h" + +namespace pp::panopainter { +namespace { + +class LegacyDocumentExportMenuServices final : public pp::app::DocumentExportMenuServices { +public: + explicit LegacyDocumentExportMenuServices(App& app) noexcept + : app_(app) + { + } + + void show_jpeg_dialog() override { app_.dialog_export(".jpg"); } + void show_png_dialog() override { app_.dialog_export(".png"); } + void show_layers_dialog() override { app_.dialog_export_layers(); } + void show_cube_faces_dialog() override { app_.dialog_export_cube_faces(); } + void show_depth_dialog() override { app_.dialog_export_depth(); } + void show_animation_frames_dialog() override { app_.dialog_export_anim_frames(); } + void show_animation_mp4_dialog() override { app_.dialog_export_mp4(); } + void show_timelapse_dialog() override { app_.dialog_timelapse_export(); } + void show_license_disabled() override + { + app_.message_box("License", "This function is disabled in demo mode."); + } + +private: + App& app_; +}; + +class LegacyFileMenuServices final : public pp::app::FileMenuServices { +public: + explicit LegacyFileMenuServices(App& app) noexcept + : app_(app) + { + } + + void show_new_document_dialog() override + { + app_.dialog_newdoc(); + } + + void pick_image_for_import() override + { + auto* app_ptr = &app_; + app_.pick_image([app_ptr](std::string path) { + Image img; + img.load_file(path); + const auto import_plan = pp::app::plan_document_image_import(img.width, img.height); + if (!import_plan) + return; + + class LegacyDocumentImageImportServices final : public pp::app::DocumentImageImportServices { + public: + LegacyDocumentImageImportServices(App& app, Image& image) noexcept + : app_(app) + , image_(image) + { + } + + void import_equirectangular(std::string_view import_path) override + { + if (Canvas::I) + Canvas::I->import_equirectangular(std::string(import_path)); + } + + void enter_transform_import(std::string_view) override + { + if (!app_.canvas || !app_.canvas->m_canvas) + return; + + auto* mode = static_cast( + app_.canvas->m_canvas->modes[(int)kCanvasMode::Import][0]); + mode->m_action = CanvasModeTransform::ActionType::Import; + mode->m_source_image = std::move(image_); + Canvas::set_mode(kCanvasMode::Import); + } + + private: + App& app_; + Image& image_; + }; + + LegacyDocumentImageImportServices services(*app_ptr, img); + const auto status = pp::app::execute_document_image_import_plan( + import_plan.value(), + path, + services); + if (!status.ok()) + LOG("Image import failed: %s", status.message); + }); + } + + void pick_project_file() override + { + auto* app_ptr = &app_; + app_.pick_file({ "ppi" }, [app_ptr](std::string path) { + app_ptr->open_document(path); + }); + } + + void show_cloud_browser_dialog() override + { + app_.dialog_browse(); + } + + void save_document(pp::app::DocumentSaveIntent intent) override + { + app_.save_document(intent); + } + + void show_export_jpeg_dialog(pp::app::DocumentExportMenuKind kind) override + { + (void)apply_legacy_document_export_menu_plan(app_, kind); + } + + void show_export_submenu() override + { + } + + void share_document() override + { + app_.share_file(app_.doc_path); + } + + void show_resize_dialog() override + { + app_.dialog_resize(); + } + + void upload_to_cloud() override + { + app_.cloud_upload(); + } + + void browse_cloud_documents() override + { + app_.cloud_browse(); + } + +private: + App& app_; +}; + +class LegacyMainToolbarServices final : public pp::app::MainToolbarServices { +public: + explicit LegacyMainToolbarServices(App& app) noexcept + : app_(app) + { + } + + void show_open_dialog() override + { + app_.dialog_open(); + } + + void show_save_dialog() override + { + app_.dialog_save(); + } + + void invoke_undo(const pp::app::HistoryUiPlan& plan) override + { + execute_history_plan(plan); + } + + void invoke_redo(const pp::app::HistoryUiPlan& plan) override + { + execute_history_plan(plan); + } + + void clear_history(const pp::app::HistoryUiPlan& plan) override + { + execute_history_plan(plan); + } + + void clear_canvas(const pp::app::DocumentCanvasClearPlan& plan) override + { + const auto status = execute_legacy_document_canvas_clear_plan(app_, plan); + if (!status.ok()) + LOG("Canvas clear failed: %s", status.message); + } + + void show_message_box() override + { + app_.msgbox = new NodeMessageBox(); + app_.msgbox->set_manager(&app_.layout); + app_.msgbox->init(); + app_.layout[app_.main_id]->add_child(app_.msgbox); + } + + void show_settings_dialog() override + { + app_.settings = new NodeSettings(); + app_.settings->set_manager(&app_.layout); + app_.settings->init(); + app_.layout[app_.main_id]->add_child(app_.settings); + } + +private: + void execute_history_plan(const pp::app::HistoryUiPlan& plan) + { + const auto status = execute_legacy_history_plan(plan); + if (!status.ok()) + LOG("History action failed: %s", status.message); + } + + App& app_; +}; + +class LegacyAboutMenuServices final : public pp::app::AboutMenuServices { +public: + explicit LegacyAboutMenuServices(App& app) noexcept + : app_(app) + { + } + + void show_user_manual() override + { + app_.dialog_usermanual(); + } + + void show_about_dialog() override + { + app_.dialog_about(); + } + + void show_whats_new_dialog() override + { + app_.dialog_whatsnew(true); + } + + void trigger_crash_test() override + { + LOG("crashing"); + app_.crash_test(); + } + + void run_performance_test(const pp::app::AboutMenuPlan& plan) override + { + if (!Canvas::I) + return; + + LOG("perf"); + std::string message; + const int performance_iterations = plan.performance_iterations; + app_.render_task([&] + { + auto start = std::chrono::high_resolution_clock::now(); + Canvas::I->stroke_start({ 0, 0, 0 }, 0.9f); + for (int i = 0; i < performance_iterations; i++) + { + Canvas::I->stroke_update({ 100, 100, 0 }, 0.9f); + Canvas::I->stroke_update({ 200, 200, 0 }, 0.9f); + Canvas::I->stroke_update({ 200, 100, 0 }, 0.9f); + Canvas::I->stroke_update({ 100, 200, 0 }, 0.9f); + Canvas::I->stroke_update({ 300, 300, 0 }, 0.9f); + Canvas::I->stroke_update({ 200, 500, 0 }, 0.9f); + Canvas::I->stroke_update({ 500, 500, 0 }, 0.9f); + Canvas::I->stroke_update({ 400, 400, 0 }, 0.9f); + Canvas::I->stroke_update({ 0, 200, 0 }, 0.9f); + Canvas::I->stroke_update({ 200, 0, 0 }, 0.9f); + Canvas::I->stroke_draw(); + } + Canvas::I->stroke_end(); + auto diff = std::chrono::high_resolution_clock::now() - start; + auto ms = std::chrono::duration_cast(diff).count(); + LOG("%lld ms", ms); + message = "Time " + std::to_string(ms) + " ms"; + }); + app_.message_box("Performance test", message); + } + +private: + App& app_; +}; + +class LegacyToolsMenuServices final : public pp::app::ToolsMenuServices { +public: + explicit LegacyToolsMenuServices(App& app) noexcept + : app_(app) + { + } + + void show_panels_submenu() override + { + } + + void show_options_submenu() override + { + } + + void clear_grid_overlays() override + { + auto* mode = static_cast(Canvas::modes[(int)kCanvasMode::Grid][0]); + mode->clear(); + } + + void reset_camera() override + { + if (app_.canvas) + app_.canvas->reset_camera(); + } + + void show_shortcuts_dialog() override + { + app_.dialog_shortcuts(); + } + + void start_sonarpen() override + { +#if __IOS__ + [app_.ios_app sonarpen_start]; +#endif + } + +private: + App& app_; +}; + +} // namespace + +bool apply_legacy_document_export_menu_plan(App& app, pp::app::DocumentExportMenuKind kind) +{ + const auto requires_license = pp::app::document_export_menu_requires_license(kind); + const auto plan = pp::app::plan_document_export_menu_action( + kind, + app.canvas != nullptr, + !requires_license || app.check_license()); + LegacyDocumentExportMenuServices services(app); + const auto status = pp::app::execute_document_export_menu_plan(plan, services); + if (!status.ok()) + LOG("Document export menu action failed: %s", status.message); + return status.ok() && plan.opens_dialog; +} + +void apply_legacy_file_menu_command(App& app, pp::app::FileMenuCommand command) +{ + const auto plan = pp::app::plan_file_menu_command(command); + LegacyFileMenuServices services(app); + const auto status = pp::app::execute_file_menu_plan(plan, services); + if (!status.ok()) + LOG("File menu action failed: %s", status.message); +} + +void execute_legacy_main_toolbar_plan(App& app, const pp::app::MainToolbarPlan& plan) +{ + LegacyMainToolbarServices services(app); + const auto status = pp::app::execute_main_toolbar_plan(plan, services); + if (!status.ok()) + LOG("Main toolbar action failed: %s", status.message); +} + +void execute_legacy_about_menu_plan(App& app, const pp::app::AboutMenuPlan& plan) +{ + LegacyAboutMenuServices services(app); + const auto status = pp::app::execute_about_menu_plan(plan, services); + if (!status.ok()) + LOG("About menu action failed: %s", status.message); +} + +void execute_legacy_tools_menu_plan(App& app, const pp::app::ToolsMenuPlan& plan) +{ + LegacyToolsMenuServices services(app); + const auto status = pp::app::execute_tools_menu_plan(plan, services); + if (!status.ok()) + LOG("Tools menu action failed: %s", status.message); +} + +} // namespace pp::panopainter diff --git a/src/legacy_app_shell_services.h b/src/legacy_app_shell_services.h new file mode 100644 index 0000000..e2f0ae1 --- /dev/null +++ b/src/legacy_app_shell_services.h @@ -0,0 +1,29 @@ +#pragma once + +#include "app_core/about_menu.h" +#include "app_core/document_export.h" +#include "app_core/file_menu.h" +#include "app_core/main_toolbar.h" +#include "app_core/tools_menu.h" + +class App; + +namespace pp::panopainter { + +[[nodiscard]] bool apply_legacy_document_export_menu_plan( + App& app, + pp::app::DocumentExportMenuKind kind); +void apply_legacy_file_menu_command( + App& app, + pp::app::FileMenuCommand command); +void execute_legacy_main_toolbar_plan( + App& app, + const pp::app::MainToolbarPlan& plan); +void execute_legacy_about_menu_plan( + App& app, + const pp::app::AboutMenuPlan& plan); +void execute_legacy_tools_menu_plan( + App& app, + const pp::app::ToolsMenuPlan& plan); + +} // namespace pp::panopainter