From 7460453b809d5b21324e5b521b8b5700a8dbf321 Mon Sep 17 00:00:00 2001 From: omigamedev Date: Wed, 3 Jun 2026 21:16:07 +0200 Subject: [PATCH] Centralize legacy document layer bridge --- cmake/PanoPainterSources.cmake | 2 + docs/modernization/build-inventory.md | 5 + docs/modernization/debt.md | 4 +- docs/modernization/roadmap.md | 15 +- src/app_dialogs.cpp | 77 +----- src/app_layout.cpp | 221 +---------------- src/legacy_document_layer_services.cpp | 330 +++++++++++++++++++++++++ src/legacy_document_layer_services.h | 29 +++ 8 files changed, 382 insertions(+), 301 deletions(-) create mode 100644 src/legacy_document_layer_services.cpp create mode 100644 src/legacy_document_layer_services.h diff --git a/cmake/PanoPainterSources.cmake b/cmake/PanoPainterSources.cmake index 21198b0..ad242e6 100644 --- a/cmake/PanoPainterSources.cmake +++ b/cmake/PanoPainterSources.cmake @@ -60,6 +60,8 @@ set(PP_LEGACY_APP_SOURCES src/canvas_modes.cpp src/legacy_document_canvas_services.cpp src/legacy_document_canvas_services.h + src/legacy_document_layer_services.cpp + src/legacy_document_layer_services.h src/legacy_history_services.cpp src/legacy_history_services.h src/pch.cpp diff --git a/docs/modernization/build-inventory.md b/docs/modernization/build-inventory.md index f40d6d1..38c9dc4 100644 --- a/docs/modernization/build-inventory.md +++ b/docs/modernization/build-inventory.md @@ -155,6 +155,11 @@ Known local toolchain state: - `pano_cli simulate-document-history` exercises pure document history apply/undo/redo behavior and is covered by `pano_cli_simulate_document_history_smoke`. +- `src/legacy_document_layer_services.*` is the current app-shell bridge for + layer rename, layer menu clear/rename/merge, layer merge, and layer-panel + 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`. - `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 d518d83..1ddfe24 100644 --- a/docs/modernization/debt.md +++ b/docs/modernization/debt.md @@ -38,7 +38,7 @@ agent or engineer to remove them without reconstructing context from chat. | DEBT-0017 | Open | Modernization | Startup storage path preparation, `App::clipboard_get_text`, `App::clipboard_set_text`, `App::show_cursor`, `App::hide_cursor`, `App::showKeyboard`, `App::hideKeyboard`, `App::display_file`, `App::share_file`, native app/window close, UI-thread lifecycle hooks, render-context acquire/release/present hooks, render-target binding hooks, render platform hint hooks, render debug callback hooks, render-capture frame hooks, recording cleanup, live asset/layout reload policy, diagnostic stacktrace/crash hooks, per-frame platform hooks, `App::pick_image`, `App::pick_file`, the non-writer `App::pick_file_save`, `App::pick_dir`, and prepared-file save/download handoff now call the SDK-free `pp::platform::PlatformServices` interface, and Windows injects `WindowsPlatformServices` from `src/platform_windows/windows_platform_services.*`; non-Windows live implementations still use `src/platform_legacy/legacy_platform_services.*`, a named fallback adapter that forwards to retained Apple/Android/Linux/Web bridge functions and retained no-op branches | Preserve behavior while moving platform execution behind a testable service boundary before platform shell implementations are injected | `pp_platform_api_tests`; `pp_app_core_document_platform_io_tests`; `ctest --preset desktop-fast --build-config Debug`; `powershell -ExecutionPolicy Bypass -File scripts\automation\package-smoke.ps1 -Preset windows-msvc-default -Configuration Debug` | Replace `src/platform_legacy/legacy_platform_services.*` with injected `pp_platform_*` service implementations owned by each non-Windows platform shell | | DEBT-0019 | Open | Modernization | Unreferenced-parameter warnings are muted globally through `pp_project_warnings` with MSVC `/wd4100` and Clang/GCC `-Wno-unused-parameter` | Legacy callbacks, virtual hooks, serializer methods, and platform/API compatibility functions carry many intentionally unused parameters during the component split; muting this keeps stricter warning builds focused on higher-signal migration issues | `cmake --build --preset windows-msvc-default --config Debug --target PanoPainter`; `ctest --preset desktop-fast --build-config Debug`; `cmake --build --preset linux-clang --target pp_foundation` | Remove `/wd4100` and `-Wno-unused-parameter`, mark intentionally unused parameters with names/comments or `[[maybe_unused]]`, and make the Windows app plus headless Clang/GCC tests pass without unreferenced-parameter warnings | | DEBT-0020 | Open | Modernization | Document resize dialog state, selected-resolution planning, and execution dispatch now consume pure `pp_app_core` through `NodeDialogResize`, `App::dialog_resize`, `pano_cli plan-document-resize`, and the `DocumentResizeServices` boundary, and live resize shares `src/legacy_document_canvas_services.*` with canvas clear commands, but the shared live bridge still calls legacy `Canvas::resize`, updates the legacy app title, and clears legacy `ActionManager` history through the history bridge | Preserve existing layer/frame GPU resize behavior while the document model and canvas execution boundary are extracted incrementally | `pp_app_core_document_resize_tests`; `pano_cli plan-document-resize --current-resolution 2048 --selected-resolution-index 4`; `ctest --preset desktop-fast --build-config Debug` | Document resize execution is owned by injected document/app services with no legacy resize adapter, title shim, or direct `ActionManager` history clearing | -| DEBT-0021 | Open | Modernization | Layer rename planning/execution dispatch and layer panel operation planning/execution dispatch now consume pure `pp_app_core` through `App::dialog_layer_rename`, `App::init_sidebar` layer callbacks, `pano_cli plan-layer-rename`, `pano_cli plan-layer-operation`, `DocumentLayerRenameServices`, and `DocumentLayerOperationServices`, but the live adapters still mutate legacy `Canvas` layer state, `NodeLayer`/`NodePanelLayer`, and `ActionManager` undo entries | Preserve existing UI/canvas behavior while document layer commands and undo history are extracted incrementally | `pp_app_core_document_layer_tests`; `pano_cli plan-layer-rename --old-name Base --new-name Paint`; `pano_cli plan-layer-operation --kind add --layer-count 2 --index 1 --name Paint`; `ctest --preset desktop-fast --build-config Debug` | Layer command execution is owned by the document/app command boundary with legacy `Canvas`/UI nodes acting only as adapters or removed entirely | +| DEBT-0021 | Open | Modernization | Layer rename planning/execution dispatch and layer panel operation planning/execution dispatch now consume pure `pp_app_core` through `App::dialog_layer_rename`, `App::init_sidebar` layer callbacks, `pano_cli plan-layer-rename`, `pano_cli plan-layer-operation`, `DocumentLayerRenameServices`, and `DocumentLayerOperationServices`, and the live execution adapters are centralized in `src/legacy_document_layer_services.*`, but that shared bridge still mutates legacy `Canvas` layer state, `NodeLayer`/`NodePanelLayer`, and `ActionManager` undo entries | Preserve existing UI/canvas behavior while document layer commands and undo history are extracted incrementally | `pp_app_core_document_layer_tests`; `pano_cli plan-layer-rename --old-name Base --new-name Paint`; `pano_cli plan-layer-operation --kind add --layer-count 2 --index 1 --name Paint`; `ctest --preset desktop-fast --build-config Debug` | Layer command execution is owned by the document/app command boundary with legacy `Canvas`/UI nodes acting only as adapters or removed entirely | | DEBT-0022 | Open | Modernization | Animation panel frame command planning, panel action planning, panel-control/timeline execution dispatch, selected-frame click dispatch, playback tick stepping, and play-mode toggles now consume pure `pp_app_core` through `NodePanelAnimation`, `pano_cli plan-animation-operation`, `pano_cli plan-animation-panel-action`, and `DocumentAnimationServices`, and `pp_legacy_ui_core` temporarily links `pp_app_core`, but the live adapter still mutates or reads legacy `Canvas`/`Layer` frame state and canvas mode directly | Preserve existing animation panel behavior while timeline/frame commands move toward the document/app command boundary | `pp_app_core_document_animation_tests`; `pano_cli plan-animation-operation --kind add --frame-count 2 --current-frame 0`; `pano_cli plan-animation-operation --kind select --frame-count 3 --selected-frame 1 --layer-index 2 --layer-id 42`; `pano_cli plan-animation-operation --kind playback --total-duration 5 --current-frame 4 --offset 1`; `pano_cli plan-animation-operation --kind toggle-playback --playing`; `pano_cli plan-animation-panel-action --action next --total-duration 5 --current-frame 4`; `ctest --preset desktop-fast --build-config Debug` | Animation frame/timeline/playback execution is owned by injected document/app timeline services with no legacy `Canvas`/`Layer`/canvas-mode adapter and UI nodes acting only as adapters or removed entirely | | DEBT-0023 | Open | Modernization | Brush/color/preset/stroke-settings UI planning, texture-list add/remove/reorder planning, stroke-panel slider/toggle/blend/reset planning, and execution dispatch now consume pure `pp_app_core` through `App::init_sidebar`, `NodePanelBrush`, `NodePanelStroke`, restored/docked floating-panel callbacks, `pano_cli plan-brush-operation`, `pano_cli plan-brush-texture-list`, `pano_cli plan-brush-stroke-control`, `BrushUiServices`, `BrushTextureListServices`, and `BrushStrokeControlServices`, but the live adapter still mutates legacy `Brush`/`Canvas::I`, loads/saves legacy brush texture images, and refreshes legacy quick/stroke/color widgets | Preserve existing brush UI behavior while brush commands move toward a brush/app/asset command boundary and asset-managed texture selection | `pp_app_core_brush_ui_tests`; `pano_cli plan-brush-operation --kind color --r 0.25 --g 0.5 --b 0.75 --a 1`; `pano_cli plan-brush-operation --kind pattern --path data/patterns/noise.png --thumb data/patterns/thumbs/noise.png`; `pano_cli plan-brush-texture-list --kind add --dir brushes --data-path data --source C:/Temp/soft.png`; `pano_cli plan-brush-stroke-control --kind float --setting tip-size --value 42.5`; `pano_cli plan-brush-stroke-control --kind blend --setting pattern --blend-mode 3`; `ctest --preset desktop-fast --build-config Debug` | Brush color/texture/preset/stroke-settings, texture-list, and stroke-control execution are owned by injected brush/app/asset/UI services with no legacy brush/canvas adapter | | DEBT-0024 | Open | Modernization | Grid/heightmap/lightmap UI planning now consumes pure `pp_app_core` through `NodePanelGrid` and `pano_cli plan-grid-operation`, but live execution still performs legacy image loading, OpenGL texture updates, nanort lightmap baking, progress UI, and `Canvas::draw_objects` commit directly | Preserve grid/lightmap behavior while moving renderable grid commands toward app/renderer/document boundaries | `pp_app_core_grid_ui_tests`; `pano_cli plan-grid-operation --kind render --float32 --texture-resolution 1024 --samples 32`; `ctest --preset desktop-fast --build-config Debug` | Grid heightmap/lightmap execution is owned by app/renderer/document services with `NodePanelGrid` acting only as UI adapter | @@ -49,7 +49,7 @@ agent or engineer to remove them without reconstructing context from chat. | 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-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 merge validates/dispatches through `DocumentLayerMergeServices`, but the live adapter still calls `App::dialog_layer_rename`, `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-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 | diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index 421dd63..dba5e7c 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -490,21 +490,22 @@ shared app-shell document-canvas bridge runs the legacy `Canvas` resize adapter and history clearing. `pano_cli plan-layer-rename` exposes the app-core layer rename decision used by the live layer rename dialog, and rename execution now dispatches through -`DocumentLayerRenameServices` before legacy `Canvas`, `NodeLayer`, and +`DocumentLayerRenameServices` in the shared app-shell layer bridge +`src/legacy_document_layer_services.*` before legacy `Canvas`, `NodeLayer`, and `ActionManager` undo adapters continue. `pano_cli plan-layer-operation` exposes app-core planning for layer add, duplicate, select, reorder, remove, opacity, visibility, alpha-lock, blend-mode, and highlight actions used by the live layer panel. Direct layer-panel operations now dispatch through `DocumentLayerOperationServices` before the -legacy `Canvas` and UI layer adapter continues execution. +shared app-shell layer bridge continues legacy `Canvas` and UI layer execution. `pano_cli plan-layer-menu` exposes app-core planning for Layer menu clear, rename, and merge-down labels/actions, and direct Layer menu commands now dispatch through `DocumentLayerMenuServices` before the legacy canvas/layer UI -adapter continues execution. Layer menu clear now routes through the shared -`DocumentCanvasClearServices` executor before the legacy canvas-clear adapter -continues, and Layer menu merge now validates and dispatches through -`DocumentLayerMergeServices` before the legacy layer-panel merge adapter -continues. +adapter in `src/legacy_document_layer_services.*` continues execution. Layer +menu clear now routes through the shared `DocumentCanvasClearServices` executor +from that bridge before the legacy canvas-clear adapter continues, and Layer +menu merge now validates and dispatches through `DocumentLayerMergeServices` +before the legacy layer-panel merge adapter continues. `pano_cli plan-animation-operation` exposes app-core planning for animation frame add, duplicate, remove, duration adjustment, timeline moves, timeline goto/next/previous, onion-size updates, frame selection, no-reload playback diff --git a/src/app_dialogs.cpp b/src/app_dialogs.cpp index b84a288..386267d 100644 --- a/src/app_dialogs.cpp +++ b/src/app_dialogs.cpp @@ -1,11 +1,11 @@ #include "pch.h" #include "app.h" -#include "action.h" #include "app_core/document_layer.h" #include "app_core/document_resize.h" #include "app_core/document_export.h" #include "app_core/document_session.h" #include "legacy_document_canvas_services.h" +#include "legacy_document_layer_services.h" #include "legacy_history_services.h" #include "settings.h" #include "node_dialog_open.h" @@ -52,78 +52,6 @@ namespace { return false; } -struct LegacyActionLayerRename : public Action -{ - std::string m_old_name; - std::string m_new_name; - Layer* m_layer = nullptr; - std::shared_ptr m_layer_node; - - LegacyActionLayerRename( - std::string old_name, - std::string new_name, - std::shared_ptr layer_node, - Layer* layer) - : m_old_name(std::move(old_name)) - , m_new_name(std::move(new_name)) - , m_layer(layer) - , m_layer_node(std::move(layer_node)) - { - } - - void run() override { } - size_t memory() override { return 0; } - void undo() override - { - if (m_layer_node) - m_layer_node->set_name(m_old_name.c_str()); - if (m_layer) - m_layer->m_name = m_old_name; - } - Action* get_redo() override - { - return new LegacyActionLayerRename(m_new_name, m_old_name, m_layer_node, m_layer); - } -}; - -class LegacyDocumentLayerRenameServices final : public pp::app::DocumentLayerRenameServices { -public: - LegacyDocumentLayerRenameServices(App& app, const std::shared_ptr& dialog) noexcept - : app_(app) - , dialog_(dialog) - { - } - - void rename_layer(std::string_view old_name, std::string_view new_name) override - { - if (!app_.layers || !app_.layers->m_current_layer || !app_.canvas || !app_.canvas->m_canvas) - return; - - auto layer_node = std::static_pointer_cast(app_.layers->m_current_layer->shared_from_this()); - auto* layer = app_.canvas->m_canvas->m_layers[app_.canvas->m_canvas->m_current_layer_idx].get(); - const std::string old_name_copy(old_name); - const std::string new_name_copy(new_name); - ActionManager::add(new LegacyActionLayerRename( - old_name_copy, - new_name_copy, - layer_node, - layer)); - layer_node->set_name(new_name_copy.c_str()); - layer->m_name = new_name_copy; - } - - void finish_layer_rename() override - { - if (dialog_) - dialog_->destroy(); - app_.hideKeyboard(); - } - -private: - App& app_; - std::shared_ptr dialog_; -}; - } std::shared_ptr App::show_progress(const std::string& title, int total /*= 0*/) @@ -683,8 +611,7 @@ void App::dialog_layer_rename() const auto plan = pp::app::plan_document_layer_rename(old_name, dialog->get_name()); if (!plan) return; - LegacyDocumentLayerRenameServices services(*this, dialog); - const auto status = pp::app::execute_document_layer_rename_plan(plan.value(), services); + const auto status = pp::panopainter::execute_legacy_document_layer_rename_plan(*this, plan.value(), dialog); if (!status.ok()) LOG("Layer rename failed: %s", status.message); }; diff --git a/src/app_layout.cpp b/src/app_layout.cpp index bab67fb..da8722e 100644 --- a/src/app_layout.cpp +++ b/src/app_layout.cpp @@ -19,6 +19,7 @@ #include "app_core/main_toolbar.h" #include "app_core/tools_menu.h" #include "legacy_document_canvas_services.h" +#include "legacy_document_layer_services.h" #include "legacy_history_services.h" #include "settings.h" #include "serializer.h" @@ -141,8 +142,6 @@ bool apply_brush_preset_plan(App& app, const std::shared_ptr& brush) return status.ok(); } -void execute_document_layer_merge_plan(App& app, const pp::app::DocumentLayerMergePlan& plan); - bool apply_document_export_menu_plan(App& app, pp::app::DocumentExportMenuKind kind) { class LegacyDocumentExportMenuServices final : public pp::app::DocumentExportMenuServices { @@ -536,215 +535,6 @@ private: App& app_; }; -class LegacyDocumentLayerMenuServices final : public pp::app::DocumentLayerMenuServices { -public: - explicit LegacyDocumentLayerMenuServices(App& app) noexcept - : app_(app) - { - } - - void clear_current_layer() override - { - const auto plan = pp::app::plan_document_canvas_clear( - app_.canvas && app_.canvas->m_canvas, - 1.0F, - 1.0F, - 1.0F, - 0.0F); - if (!plan) - return; - - const auto status = pp::panopainter::execute_legacy_document_canvas_clear_plan(app_, plan.value()); - if (!status.ok()) - LOG("Canvas clear failed: %s", status.message); - } - - void show_rename_dialog() override - { - app_.dialog_layer_rename(); - } - - void merge_with_lower_layer(int from_index, int to_index) override - { - const int layer_count = app_.canvas && app_.canvas->m_canvas - ? static_cast(app_.canvas->m_canvas->m_layers.size()) - : 0; - const int animation_duration = Canvas::I - ? Canvas::I->anim_duration() - : 0; - const auto plan = pp::app::plan_document_layer_merge( - layer_count, - from_index, - to_index, - animation_duration); - if (!plan) - return; - - execute_document_layer_merge_plan(app_, plan.value()); - } - - void show_merge_animated_not_supported() override - { - app_.message_box("Not supported", "Merging animated layers is not supported yet."); - } - -private: - App& app_; -}; - -class LegacyDocumentLayerMergeServices final : public pp::app::DocumentLayerMergeServices { -public: - explicit LegacyDocumentLayerMergeServices(App& app) noexcept - : app_(app) - { - } - - void merge_layers(int from_index, int to_index, bool create_history) override - { - if (app_.layers) - app_.layers->merge(from_index, to_index, create_history); - } - -private: - App& app_; -}; - -class LegacyDocumentLayerOperationServices final : public pp::app::DocumentLayerOperationServices { -public: - LegacyDocumentLayerOperationServices( - App& app, - const std::shared_ptr& pending_layer = nullptr) noexcept - : app_(app) - , pending_layer_(pending_layer) - { - } - - void add_layer(std::string_view name, int insert_index) override - { - auto* canvas = legacy_canvas(); - if (!canvas) - return; - - canvas->layer_add(std::string(name), pending_layer_, insert_index); - canvas->anim_update(); - } - - void duplicate_layer(int source_index, int insert_index) override - { - auto* canvas = legacy_canvas(); - if (!canvas) - return; - - std::string duplicated_name = "Layer"; - if (app_.layers && !app_.layers->m_layers.empty()) - duplicated_name = app_.layers->m_layers.back()->m_label_text; - - canvas->layer_add(duplicated_name, nullptr, insert_index); - auto& dst = canvas->m_layers[insert_index]; - auto& src = canvas->m_layers[source_index]; - for (int i = 1; i < src->frames_count(); i++) - dst->add_frame(); - canvas->anim_update(); - for (int frame = 0; frame < src->frames_count(); frame++) - { - for (int i = 0; i < 6; i++) - { - if (!src->face(i)) - continue; - bool loaded = src->frame(frame).gpu_load(); - dst->frame(frame).gpu_load(); - dst->rtt(i, frame).copy(src->rtt(i)); - dst->face(i, frame) = src->face(i); - dst->box(i, frame) = src->box(i); - if (!loaded) - { - dst->frame(frame).gpu_unload(); - src->frame(frame).gpu_unload(); - } - } - } - dst->m_opacity = src->m_opacity; - dst->m_blend_mode = src->m_blend_mode; - dst->m_alpha_locked = src->m_alpha_locked; - } - - void select_layer(int index) override - { - if (auto* canvas = legacy_canvas()) - canvas->m_current_layer_idx = index; - } - - void reorder_layer(int from_index, int to_index) override - { - if (auto* canvas = legacy_canvas()) - canvas->layer_order(from_index, to_index); - } - - void remove_layer(int index) override - { - if (auto* canvas = legacy_canvas()) - canvas->layer_remove(index); - } - - void set_layer_opacity(int index, float opacity) override - { - if (auto* canvas = legacy_canvas()) - canvas->m_layers[index]->m_opacity = opacity; - } - - void set_layer_visibility(int index, bool visible) override - { - if (auto* canvas = legacy_canvas()) - canvas->m_layers[index]->m_visible = visible; - } - - void set_layer_alpha_lock(int index, bool locked) override - { - if (auto* canvas = legacy_canvas()) - canvas->m_layers[index]->m_alpha_locked = locked; - } - - void set_layer_blend_mode(int index, int blend_mode) override - { - if (auto* canvas = legacy_canvas()) - canvas->m_layers[index]->m_blend_mode = blend_mode; - } - - void set_layer_highlight(int index, bool highlighted) override - { - if (auto* canvas = legacy_canvas()) - canvas->m_layers[index]->m_hightlight = highlighted; - } - - void mark_unsaved() override - { - if (auto* canvas = legacy_canvas()) - canvas->m_unsaved = true; - } - - void reload_animation_layers() override - { - if (app_.animation) - app_.animation->load_layers(); - } - - void update_title() override - { - app_.title_update(); - } - -private: - [[nodiscard]] Canvas* legacy_canvas() const noexcept - { - if (app_.canvas && app_.canvas->m_canvas) - return app_.canvas->m_canvas.get(); - return Canvas::I; - } - - App& app_; - std::shared_ptr pending_layer_; -}; - void execute_main_toolbar_plan(App& app, const pp::app::MainToolbarPlan& plan) { LegacyMainToolbarServices services(app); @@ -771,16 +561,14 @@ void execute_tools_menu_plan(App& app, const pp::app::ToolsMenuPlan& plan) void execute_document_layer_menu_plan(App& app, const pp::app::DocumentLayerMenuPlan& plan) { - LegacyDocumentLayerMenuServices services(app); - const auto status = pp::app::execute_document_layer_menu_plan(plan, services); + const auto status = pp::panopainter::execute_legacy_document_layer_menu_plan(app, plan); if (!status.ok()) LOG("Layer menu action failed: %s", status.message); } void execute_document_layer_merge_plan(App& app, const pp::app::DocumentLayerMergePlan& plan) { - LegacyDocumentLayerMergeServices services(app); - const auto status = pp::app::execute_document_layer_merge_plan(plan, services); + const auto status = pp::panopainter::execute_legacy_document_layer_merge_plan(app, plan); if (!status.ok()) LOG("Layer merge failed: %s", status.message); } @@ -790,8 +578,7 @@ void execute_document_layer_operation_plan( const pp::app::DocumentLayerOperationPlan& plan, const std::shared_ptr& pending_layer = nullptr) { - LegacyDocumentLayerOperationServices services(app, pending_layer); - const auto status = pp::app::execute_document_layer_operation_plan(plan, services); + const auto status = pp::panopainter::execute_legacy_document_layer_operation_plan(app, plan, pending_layer); if (!status.ok()) LOG("Layer operation failed: %s", status.message); } diff --git a/src/legacy_document_layer_services.cpp b/src/legacy_document_layer_services.cpp new file mode 100644 index 0000000..6059a48 --- /dev/null +++ b/src/legacy_document_layer_services.cpp @@ -0,0 +1,330 @@ +#include "pch.h" + +#include "legacy_document_layer_services.h" + +#include "action.h" +#include "app.h" +#include "legacy_document_canvas_services.h" + +namespace pp::panopainter { +namespace { + +struct LegacyActionLayerRename : public Action { + std::string m_old_name; + std::string m_new_name; + Layer* m_layer = nullptr; + std::shared_ptr m_layer_node; + + LegacyActionLayerRename( + std::string old_name, + std::string new_name, + std::shared_ptr layer_node, + Layer* layer) + : m_old_name(std::move(old_name)) + , m_new_name(std::move(new_name)) + , m_layer(layer) + , m_layer_node(std::move(layer_node)) + { + } + + void run() override { } + size_t memory() override { return 0; } + void undo() override + { + if (m_layer_node) + m_layer_node->set_name(m_old_name.c_str()); + if (m_layer) + m_layer->m_name = m_old_name; + } + Action* get_redo() override + { + return new LegacyActionLayerRename(m_new_name, m_old_name, m_layer_node, m_layer); + } +}; + +class LegacyDocumentLayerRenameServices final : public pp::app::DocumentLayerRenameServices { +public: + LegacyDocumentLayerRenameServices(App& app, const std::shared_ptr& dialog) noexcept + : app_(app) + , dialog_(dialog) + { + } + + void rename_layer(std::string_view old_name, std::string_view new_name) override + { + if (!app_.layers || !app_.layers->m_current_layer || !app_.canvas || !app_.canvas->m_canvas) + return; + + auto layer_node = std::static_pointer_cast(app_.layers->m_current_layer->shared_from_this()); + auto* layer = app_.canvas->m_canvas->m_layers[app_.canvas->m_canvas->m_current_layer_idx].get(); + const std::string old_name_copy(old_name); + const std::string new_name_copy(new_name); + ActionManager::add(new LegacyActionLayerRename( + old_name_copy, + new_name_copy, + layer_node, + layer)); + layer_node->set_name(new_name_copy.c_str()); + layer->m_name = new_name_copy; + } + + void finish_layer_rename() override + { + if (dialog_) + dialog_->destroy(); + app_.hideKeyboard(); + } + +private: + App& app_; + std::shared_ptr dialog_; +}; + +class LegacyDocumentLayerMergeServices final : public pp::app::DocumentLayerMergeServices { +public: + explicit LegacyDocumentLayerMergeServices(App& app) noexcept + : app_(app) + { + } + + void merge_layers(int from_index, int to_index, bool create_history) override + { + if (app_.layers) + app_.layers->merge(from_index, to_index, create_history); + } + +private: + App& app_; +}; + +class LegacyDocumentLayerMenuServices final : public pp::app::DocumentLayerMenuServices { +public: + explicit LegacyDocumentLayerMenuServices(App& app) noexcept + : app_(app) + { + } + + void clear_current_layer() override + { + const auto plan = pp::app::plan_document_canvas_clear( + app_.canvas && app_.canvas->m_canvas, + 1.0F, + 1.0F, + 1.0F, + 0.0F); + if (!plan) + return; + + const auto status = execute_legacy_document_canvas_clear_plan(app_, plan.value()); + if (!status.ok()) + LOG("Canvas clear failed: %s", status.message); + } + + void show_rename_dialog() override + { + app_.dialog_layer_rename(); + } + + void merge_with_lower_layer(int from_index, int to_index) override + { + const int layer_count = app_.canvas && app_.canvas->m_canvas + ? static_cast(app_.canvas->m_canvas->m_layers.size()) + : 0; + const int animation_duration = Canvas::I + ? Canvas::I->anim_duration() + : 0; + const auto plan = pp::app::plan_document_layer_merge( + layer_count, + from_index, + to_index, + animation_duration); + if (!plan) + return; + + const auto status = execute_legacy_document_layer_merge_plan(app_, plan.value()); + if (!status.ok()) + LOG("Layer merge failed: %s", status.message); + } + + void show_merge_animated_not_supported() override + { + app_.message_box("Not supported", "Merging animated layers is not supported yet."); + } + +private: + App& app_; +}; + +class LegacyDocumentLayerOperationServices final : public pp::app::DocumentLayerOperationServices { +public: + LegacyDocumentLayerOperationServices( + App& app, + const std::shared_ptr& pending_layer = nullptr) noexcept + : app_(app) + , pending_layer_(pending_layer) + { + } + + void add_layer(std::string_view name, int insert_index) override + { + auto* canvas = legacy_canvas(); + if (!canvas) + return; + + canvas->layer_add(std::string(name), pending_layer_, insert_index); + canvas->anim_update(); + } + + void duplicate_layer(int source_index, int insert_index) override + { + auto* canvas = legacy_canvas(); + if (!canvas) + return; + + std::string duplicated_name = "Layer"; + if (app_.layers && !app_.layers->m_layers.empty()) + duplicated_name = app_.layers->m_layers.back()->m_label_text; + + canvas->layer_add(duplicated_name, nullptr, insert_index); + auto& dst = canvas->m_layers[insert_index]; + auto& src = canvas->m_layers[source_index]; + for (int i = 1; i < src->frames_count(); i++) + dst->add_frame(); + canvas->anim_update(); + for (int frame = 0; frame < src->frames_count(); frame++) + { + for (int i = 0; i < 6; i++) + { + if (!src->face(i)) + continue; + bool loaded = src->frame(frame).gpu_load(); + dst->frame(frame).gpu_load(); + dst->rtt(i, frame).copy(src->rtt(i)); + dst->face(i, frame) = src->face(i); + dst->box(i, frame) = src->box(i); + if (!loaded) + { + dst->frame(frame).gpu_unload(); + src->frame(frame).gpu_unload(); + } + } + } + dst->m_opacity = src->m_opacity; + dst->m_blend_mode = src->m_blend_mode; + dst->m_alpha_locked = src->m_alpha_locked; + } + + void select_layer(int index) override + { + if (auto* canvas = legacy_canvas()) + canvas->m_current_layer_idx = index; + } + + void reorder_layer(int from_index, int to_index) override + { + if (auto* canvas = legacy_canvas()) + canvas->layer_order(from_index, to_index); + } + + void remove_layer(int index) override + { + if (auto* canvas = legacy_canvas()) + canvas->layer_remove(index); + } + + void set_layer_opacity(int index, float opacity) override + { + if (auto* canvas = legacy_canvas()) + canvas->m_layers[index]->m_opacity = opacity; + } + + void set_layer_visibility(int index, bool visible) override + { + if (auto* canvas = legacy_canvas()) + canvas->m_layers[index]->m_visible = visible; + } + + void set_layer_alpha_lock(int index, bool locked) override + { + if (auto* canvas = legacy_canvas()) + canvas->m_layers[index]->m_alpha_locked = locked; + } + + void set_layer_blend_mode(int index, int blend_mode) override + { + if (auto* canvas = legacy_canvas()) + canvas->m_layers[index]->m_blend_mode = blend_mode; + } + + void set_layer_highlight(int index, bool highlighted) override + { + if (auto* canvas = legacy_canvas()) + canvas->m_layers[index]->m_hightlight = highlighted; + } + + void mark_unsaved() override + { + if (auto* canvas = legacy_canvas()) + canvas->m_unsaved = true; + } + + void reload_animation_layers() override + { + if (app_.animation) + app_.animation->load_layers(); + } + + void update_title() override + { + app_.title_update(); + } + +private: + [[nodiscard]] Canvas* legacy_canvas() const noexcept + { + if (app_.canvas && app_.canvas->m_canvas) + return app_.canvas->m_canvas.get(); + return Canvas::I; + } + + App& app_; + std::shared_ptr pending_layer_; +}; + +} // namespace + +pp::foundation::Status execute_legacy_document_layer_rename_plan( + App& app, + const pp::app::DocumentLayerRenamePlan& plan, + const std::shared_ptr& dialog) +{ + LegacyDocumentLayerRenameServices services(app, dialog); + return pp::app::execute_document_layer_rename_plan(plan, services); +} + +pp::foundation::Status execute_legacy_document_layer_menu_plan( + App& app, + const pp::app::DocumentLayerMenuPlan& plan) +{ + LegacyDocumentLayerMenuServices services(app); + return pp::app::execute_document_layer_menu_plan(plan, services); +} + +pp::foundation::Status execute_legacy_document_layer_merge_plan( + App& app, + const pp::app::DocumentLayerMergePlan& plan) +{ + LegacyDocumentLayerMergeServices services(app); + return pp::app::execute_document_layer_merge_plan(plan, services); +} + +pp::foundation::Status execute_legacy_document_layer_operation_plan( + App& app, + const pp::app::DocumentLayerOperationPlan& plan, + const std::shared_ptr& pending_layer) +{ + LegacyDocumentLayerOperationServices services(app, pending_layer); + return pp::app::execute_document_layer_operation_plan(plan, services); +} + +} // namespace pp::panopainter diff --git a/src/legacy_document_layer_services.h b/src/legacy_document_layer_services.h new file mode 100644 index 0000000..6be820a --- /dev/null +++ b/src/legacy_document_layer_services.h @@ -0,0 +1,29 @@ +#pragma once + +#include "app_core/document_layer.h" +#include "foundation/result.h" + +#include + +class App; +class Layer; +class NodeDialogLayerRename; + +namespace pp::panopainter { + +[[nodiscard]] pp::foundation::Status execute_legacy_document_layer_rename_plan( + App& app, + const pp::app::DocumentLayerRenamePlan& plan, + const std::shared_ptr& dialog); +[[nodiscard]] pp::foundation::Status execute_legacy_document_layer_menu_plan( + App& app, + const pp::app::DocumentLayerMenuPlan& plan); +[[nodiscard]] pp::foundation::Status execute_legacy_document_layer_merge_plan( + App& app, + const pp::app::DocumentLayerMergePlan& plan); +[[nodiscard]] pp::foundation::Status execute_legacy_document_layer_operation_plan( + App& app, + const pp::app::DocumentLayerOperationPlan& plan, + const std::shared_ptr& pending_layer = nullptr); + +} // namespace pp::panopainter