From d1bd4e9b463a1542764b4d1b6bed007277b9a859 Mon Sep 17 00:00:00 2001 From: omigamedev Date: Wed, 3 Jun 2026 20:45:33 +0200 Subject: [PATCH] Centralize legacy history bridge --- cmake/PanoPainterSources.cmake | 2 + docs/modernization/build-inventory.md | 4 ++ docs/modernization/debt.md | 2 +- docs/modernization/roadmap.md | 6 ++- src/app.cpp | 3 +- src/app_layout.cpp | 36 +++++------------ src/legacy_history_services.cpp | 58 +++++++++++++++++++++++++++ src/legacy_history_services.h | 18 +++++++++ src/node_canvas.cpp | 28 +++---------- 9 files changed, 104 insertions(+), 53 deletions(-) create mode 100644 src/legacy_history_services.cpp create mode 100644 src/legacy_history_services.h diff --git a/cmake/PanoPainterSources.cmake b/cmake/PanoPainterSources.cmake index bc3dd56..6ed683f 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_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 a1c78fb..b17efcd 100644 --- a/docs/modernization/build-inventory.md +++ b/docs/modernization/build-inventory.md @@ -536,6 +536,10 @@ Known local toolchain state: new-document target/resolution/overwrite planning, document file target, combined save-file overwrite planning, and save-version target decisions without requiring a window, canvas, or message box. +- `src/legacy_history_services.*` is the current app-shell bridge between + `pp_app_core` history plans and legacy `ActionManager`; toolbar and + `NodeCanvas` hotkeys share it while document-history extraction remains + tracked by DEBT-0026. - `pp_ui_core` consumes vcpkg tinyxml2 only when `PP_USE_VCPKG_TINYXML2=ON` through the vcpkg preset; default and Android validation still use the retained vendored fallback tracked by DEBT-0012. diff --git a/docs/modernization/debt.md b/docs/modernization/debt.md index 85892b0..d697725 100644 --- a/docs/modernization/debt.md +++ b/docs/modernization/debt.md @@ -43,7 +43,7 @@ agent or engineer to remove them without reconstructing context from chat. | 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 | | DEBT-0025 | Open | Modernization | Quick brush/color slot and mini-state planning and execution dispatch now consume pure `pp_app_core` through `NodePanelQuick`, `pano_cli plan-quick-operation`, and the `QuickUiServices` boundary, but the live adapter still mutates legacy quick UI widgets, `Brush` previews, color picker popup state, and preset popup state | Preserve quick-panel behavior while quick brush/color commands move toward a brush/app command boundary with safer automation coverage | `pp_app_core_quick_ui_tests`; `pano_cli plan-quick-operation --kind brush --current-index 0 --slot-index 2`; `pano_cli plan-quick-operation --kind restore --brush-index 2 --color-index 1 --fire-event`; `ctest --preset desktop-fast --build-config Debug` | Quick-panel selection, popup, restore, reset, brush preview, and color execution are owned by injected app/brush/UI services with no legacy quick-panel adapter | -| DEBT-0026 | Open | Modernization | Toolbar history command planning and execution dispatch now consume pure `pp_app_core` through `App::init_toolbar_main`, `NodeCanvas`, `pano_cli plan-history-operation`, and the `HistoryUiServices` boundary, but the live adapter 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-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, but the live adapter 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 | diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index 08eb943..7eac171 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -568,8 +568,10 @@ commit used by the live grid panel before legacy image loading, OpenGL texture updates, nanort lightmap baking, and `Canvas::draw_objects` execution continue. `pano_cli plan-history-operation` exposes app-core planning for undo, redo, and clear-history availability used by toolbar buttons and canvas shortcuts; live -toolbar execution now dispatches through `HistoryUiServices` before the legacy -`ActionManager` stack adapter continues. +toolbar and canvas-hotkey execution now dispatch through a shared app-shell +legacy history bridge before the legacy `ActionManager` stack adapter +continues. The bridge also centralizes saturated history metrics so app-core +plans never receive wrapped negative counts from oversized legacy stacks. `pano_cli plan-main-toolbar` exposes app-core planning for the live main toolbar/status-bar shell, including open/save dialogs, undo/redo availability, clear-history availability, clear-canvas no-canvas blocking, message-box diff --git a/src/app.cpp b/src/app.cpp index 868fdd2..77c452c 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -10,6 +10,7 @@ #include "app_core/document_recording.h" #include "app_core/document_route.h" #include "app_core/document_session.h" +#include "legacy_history_services.h" #include "platform_api/platform_services.h" #include "renderer_gl/opengl_capabilities.h" @@ -239,7 +240,7 @@ void App::open_document(std::string path) "It may be inaccessible or corrupted."); } }); - ActionManager::clear(); + pp::panopainter::clear_legacy_history(); }; if (open_plan == pp::app::DocumentOpenPlanAction::open_project_now) { diff --git a/src/app_layout.cpp b/src/app_layout.cpp index 9e09d14..cd69fc5 100644 --- a/src/app_layout.cpp +++ b/src/app_layout.cpp @@ -16,9 +16,9 @@ #include "app_core/document_import.h" #include "app_core/file_menu.h" #include "app_core/app_status.h" -#include "app_core/history_ui.h" #include "app_core/main_toolbar.h" #include "app_core/tools_menu.h" +#include "legacy_history_services.h" #include "settings.h" #include "serializer.h" #include "font.h" @@ -440,28 +440,9 @@ public: } private: - class LegacyHistoryUiServices final : public pp::app::HistoryUiServices { - public: - void invoke_undo() override - { - ActionManager::undo(); - } - - void invoke_redo() override - { - ActionManager::redo(); - } - - void clear_history() override - { - ActionManager::clear(); - } - }; - void execute_history_plan(const pp::app::HistoryUiPlan& plan) { - LegacyHistoryUiServices services; - const auto status = pp::app::execute_history_ui_plan(plan, services); + const auto status = pp::panopainter::execute_legacy_history_plan(plan); if (!status.ok()) LOG("History action failed: %s", status.message); } @@ -888,9 +869,10 @@ void App::init_toolbar_main() if (auto* button = layout[main_id]->find("btn-undo")) { button->on_click = [this, button](Node*) { + const auto history = pp::panopainter::legacy_history_snapshot(); const auto plan = pp::app::plan_main_toolbar_command( pp::app::MainToolbarCommand::undo, - static_cast(ActionManager::I.m_actions.size())); + history.undo_count); if (plan) execute_main_toolbar_plan(*this, plan.value()); }; @@ -898,10 +880,11 @@ void App::init_toolbar_main() if (auto* button = layout[main_id]->find("btn-redo")) { button->on_click = [this, button](Node*) { + const auto history = pp::panopainter::legacy_history_snapshot(); const auto plan = pp::app::plan_main_toolbar_command( pp::app::MainToolbarCommand::redo, 0, - static_cast(ActionManager::I.m_redos.size())); + history.redo_count); if (plan) execute_main_toolbar_plan(*this, plan.value()); }; @@ -909,11 +892,12 @@ void App::init_toolbar_main() if (auto* button = layout[main_id]->find("btn-clean-memory")) { button->on_click = [this](Node*) { + const auto history = pp::panopainter::legacy_history_snapshot(); const auto plan = pp::app::plan_main_toolbar_command( pp::app::MainToolbarCommand::clear_history, - static_cast(ActionManager::I.m_actions.size()), - static_cast(ActionManager::I.m_redos.size()), - static_cast(ActionManager::I.m_memory)); + history.undo_count, + history.redo_count, + history.memory_bytes); if (plan) execute_main_toolbar_plan(*this, plan.value()); }; diff --git a/src/legacy_history_services.cpp b/src/legacy_history_services.cpp new file mode 100644 index 0000000..04349e6 --- /dev/null +++ b/src/legacy_history_services.cpp @@ -0,0 +1,58 @@ +#include "pch.h" + +#include "legacy_history_services.h" + +#include "action.h" + +#include + +namespace pp::panopainter { +namespace { + +[[nodiscard]] int saturated_history_metric(size_t value) noexcept +{ + constexpr auto max_int = static_cast(std::numeric_limits::max()); + return value > max_int ? std::numeric_limits::max() : static_cast(value); +} + +class LegacyHistoryUiServices final : public pp::app::HistoryUiServices { +public: + void invoke_undo() override + { + ActionManager::undo(); + } + + void invoke_redo() override + { + ActionManager::redo(); + } + + void clear_history() override + { + ActionManager::clear(); + } +}; + +} // namespace + +LegacyHistorySnapshot legacy_history_snapshot() noexcept +{ + return LegacyHistorySnapshot { + .undo_count = saturated_history_metric(ActionManager::I.m_actions.size()), + .redo_count = saturated_history_metric(ActionManager::I.m_redos.size()), + .memory_bytes = saturated_history_metric(ActionManager::I.m_memory), + }; +} + +pp::foundation::Status execute_legacy_history_plan(const pp::app::HistoryUiPlan& plan) +{ + LegacyHistoryUiServices services; + return pp::app::execute_history_ui_plan(plan, services); +} + +void clear_legacy_history() noexcept +{ + ActionManager::clear(); +} + +} // namespace pp::panopainter diff --git a/src/legacy_history_services.h b/src/legacy_history_services.h new file mode 100644 index 0000000..45bbe10 --- /dev/null +++ b/src/legacy_history_services.h @@ -0,0 +1,18 @@ +#pragma once + +#include "app_core/history_ui.h" +#include "foundation/result.h" + +namespace pp::panopainter { + +struct LegacyHistorySnapshot { + int undo_count = 0; + int redo_count = 0; + int memory_bytes = 0; +}; + +[[nodiscard]] LegacyHistorySnapshot legacy_history_snapshot() noexcept; +[[nodiscard]] pp::foundation::Status execute_legacy_history_plan(const pp::app::HistoryUiPlan& plan); +void clear_legacy_history() noexcept; + +} // namespace pp::panopainter diff --git a/src/node_canvas.cpp b/src/node_canvas.cpp index a0e6a21..a1263c9 100644 --- a/src/node_canvas.cpp +++ b/src/node_canvas.cpp @@ -7,8 +7,8 @@ #include "app_core/canvas_hotkey.h" #include "app_core/canvas_tool_ui.h" -#include "app_core/history_ui.h" #include "app.h" +#include "legacy_history_services.h" #include "log.h" #include "node_canvas.h" #include "node_image_texture.h" @@ -103,24 +103,6 @@ public: } }; -class LegacyNodeCanvasHistoryServices final : public pp::app::HistoryUiServices { -public: - void invoke_undo() override - { - ActionManager::undo(); - } - - void invoke_redo() override - { - ActionManager::redo(); - } - - void clear_history() override - { - ActionManager::clear(); - } -}; - class LegacyNodeCanvasHotkeyServices final : public pp::app::CanvasHotkeyServices { public: pp::foundation::Status execute_tool(const pp::app::CanvasToolPlan& plan) override @@ -131,8 +113,7 @@ public: pp::foundation::Status execute_history(const pp::app::HistoryUiPlan& plan) override { - LegacyNodeCanvasHistoryServices services; - return pp::app::execute_history_ui_plan(plan, services); + return pp::panopainter::execute_legacy_history_plan(plan); } void save_document(pp::app::DocumentSaveIntent intent) override @@ -191,8 +172,9 @@ pp::app::CanvasHotkeyState canvas_hotkey_state(bool mouse_focused, int touch_fin state.ctrl_down = App::I && App::I->keys[(int)kKey::KeyCtrl]; state.shift_down = App::I && App::I->keys[(int)kKey::KeyShift]; state.mouse_focused = mouse_focused; - state.undo_count = static_cast(ActionManager::I.m_actions.size()); - state.redo_count = static_cast(ActionManager::I.m_redos.size()); + const auto history = pp::panopainter::legacy_history_snapshot(); + state.undo_count = history.undo_count; + state.redo_count = history.redo_count; state.touch_finger_count = touch_finger_count; return state; }