Centralize legacy app shell services

This commit is contained in:
2026-06-03 21:32:15 +02:00
parent 7460453b80
commit 22bbc93b43
7 changed files with 440 additions and 361 deletions

View File

@@ -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

View File

@@ -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`.

View File

@@ -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

View File

@@ -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

View File

@@ -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>& 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<CanvasModeTransform*>(
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<std::chrono::milliseconds>(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<CanvasModeGrid*>(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)

View File

@@ -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<CanvasModeTransform*>(
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<std::chrono::milliseconds>(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<CanvasModeGrid*>(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

View File

@@ -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