935 lines
52 KiB
Markdown
935 lines
52 KiB
Markdown
# PanoPainter Modernization Roadmap
|
|
|
|
Status: live
|
|
Last updated: 2026-06-17
|
|
|
|
This roadmap is now architecture-first.
|
|
The active execution queue lives in `docs/modernization/tasks.md`.
|
|
Completed and superseded task history moved to
|
|
`docs/modernization/tasks-done.md`.
|
|
The debt log remains `docs/modernization/debt.md`.
|
|
|
|
## Objective
|
|
|
|
Turn PanoPainter into a thin composition-root application over separable C++23
|
|
components while preserving current behavior.
|
|
|
|
The target end state is not "more planners around the same legacy shell".
|
|
The next phase is measured by ownership transfer in the live app, not by
|
|
planner count, CLI breadth, or test count.
|
|
The target end state is:
|
|
|
|
- real component ownership
|
|
- real platform boundaries
|
|
- real renderer boundaries
|
|
- a thin `panopainter_app`
|
|
- legacy containment targets either deleted or reduced to trivial,
|
|
debt-tracked adapters
|
|
|
|
## What This Roadmap Covers
|
|
|
|
- app architecture
|
|
- component boundaries
|
|
- platform boundaries
|
|
- renderer/app ownership boundaries
|
|
- the order of work needed to finish the split
|
|
|
|
It does not try to be the full build, test, or automation reference.
|
|
Those details live in the other modernization docs when needed.
|
|
|
|
## What Does Not Count As Top-Priority Progress
|
|
|
|
These can still be useful, but they are not first-order modernization work
|
|
while the app shell still mostly looks like the old codebase:
|
|
|
|
- planner-only extraction that leaves the same live owner in place
|
|
- new CLI surface without corresponding live app ownership reduction
|
|
- test-only or automation-only expansion that does not unblock code movement
|
|
- backend lab scaffolds
|
|
- debt-log churn without a target or ownership change
|
|
|
|
## Reality Check
|
|
|
|
The codebase is meaningfully farther along than the old flat app, but it is not
|
|
close to the final architecture yet.
|
|
Historical percentage claims such as the earlier 67% score should not be used
|
|
as a proxy for architectural completion. The live app still mostly runs through
|
|
the same large shell and hotspot files.
|
|
|
|
What is already real:
|
|
|
|
- `pp_foundation`
|
|
- `pp_assets`
|
|
- `pp_paint`
|
|
- `pp_document`
|
|
- `pp_renderer_api`
|
|
- `pp_renderer_gl`
|
|
- `pp_paint_renderer`
|
|
- `pp_ui_core`
|
|
- `pp_platform_api`
|
|
- `pp_app_core`
|
|
|
|
Latest slice:
|
|
- `src/platform_legacy/legacy_platform_services.*` now owns Android storage
|
|
paths through explicit `create_platform_services(...)` configuration instead
|
|
of reading them from a shared legacy singleton.
|
|
- `android/src/cpp/main.cpp` now seeds Android storage paths into its owned
|
|
legacy `PlatformServices` instance.
|
|
- `src/platform_legacy/legacy_platform_state.*` no longer carries the Android
|
|
storage-path singleton API.
|
|
- `src/platform_legacy/legacy_platform_state.*` no longer keeps its own
|
|
retained Web platform-service binding; the legacy Web helper path now uses
|
|
the shared `platform_api` injection surface instead.
|
|
- `src/platform_legacy/legacy_platform_state.*` no longer carries any retained
|
|
GLFW window state; the leftover `set_legacy_glfw_window*` surface is gone.
|
|
- `linux/src/main.cpp` and `webgl/src/main.cpp` no longer seed legacy GLFW
|
|
retained state at startup.
|
|
- `src/platform_windows/windows_window_shell.cpp` no longer keeps any retained
|
|
mouse-position pocket for button events; it now reads client coordinates
|
|
directly from the Win32 messages that already carry them.
|
|
- `src/platform_legacy/legacy_platform_services.*` now accepts an injected
|
|
`LegacyGlfwPlatformShell` for Linux/WebGL `acquire_render_context`,
|
|
`present_render_context`, and app-close callbacks.
|
|
- `linux/src/main.cpp` and `webgl/src/main.cpp` now provide that GLFW shell
|
|
directly from the entrypoint-owned window state instead of relying on legacy
|
|
free helpers for those operations.
|
|
- `src/platform_legacy/legacy_platform_state.*` no longer exports the old GLFW
|
|
`acquire/present/request_close` helper trio.
|
|
- `src/platform_windows/windows_platform_services.cpp` now owns the Win32
|
|
virtual-key map and `initialize_retained_input_state()` path, so
|
|
`windows_window_shell.cpp` no longer carries that retained input setup.
|
|
- `src/platform_windows/windows_window_shell.*` no longer carries the dead
|
|
retained raw key-state array or its accessor; Win32 key synchronization now
|
|
relies only on the virtual-key map plus `App` key state.
|
|
- `linux/src/main.cpp` now binds and clears the FPS-title callback directly in
|
|
the Linux entrypoint-owned shell; `App::set_platform_services()` no longer
|
|
installs Linux-specific GLFW title behavior.
|
|
- `src/platform_windows/windows_window_shell.*` now keeps the Win32 virtual-key
|
|
map outside the retained input-state struct, shrinking that shared pocket.
|
|
- `src/platform_windows/windows_runtime_shell.cpp` no longer keeps a second
|
|
retained `App*` copy alongside `App::I`; the runtime shell now reads the
|
|
bound app directly from the singleton composition edge.
|
|
- `src/platform_windows/windows_window_shell.*` and
|
|
`src/platform_windows/windows_platform_services.cpp` now keep Win32 VR state
|
|
outside the generic retained input-state bundle.
|
|
- `src/platform_legacy/legacy_platform_state.*` no longer carries the dead
|
|
generic storage-path singleton for the current runtime matrix.
|
|
- `src/platform_legacy/legacy_platform_services.cpp` now drops the dead generic
|
|
storage-path fallback read; only the platform-specific storage-path branches
|
|
remain live.
|
|
- `src/legacy_app_startup_services.cpp` no longer seeds the dead generic
|
|
storage-path fallback state on non-Android startup.
|
|
- `src/platform_legacy/legacy_platform_services.cpp` now reads Android storage
|
|
paths from Android-owned retained state instead of the shared legacy
|
|
storage-path singleton.
|
|
- `src/legacy_app_startup_services.cpp` and `android/src/cpp/main.cpp` now
|
|
update Android-owned retained storage paths instead of the shared legacy
|
|
storage-path singleton.
|
|
- `src/app_events.cpp`, `linux/src/main.cpp`, and
|
|
`src/platform_legacy/legacy_platform_state.*` now route the Linux FPS-title
|
|
callback through a narrow legacy GLFW title helper instead of reaching into
|
|
retained GLFW window state from `App::set_platform_services(...)`.
|
|
- `src/platform_legacy/legacy_platform_state.h` no longer exports the raw
|
|
retained GLFW window-state accessor; callers now go through narrow GLFW
|
|
helpers only.
|
|
- `src/platform_apple/apple_platform_state.cpp` no longer reads Apple storage
|
|
paths from `pp::platform::legacy::active_legacy_storage_paths()`;
|
|
Apple-owned retained state now carries that path bundle.
|
|
- `PanoPainter-OSX/main.cpp` and `PanoPainter/GameViewController.m` now seed
|
|
Apple-owned storage paths directly instead of writing them through the shared
|
|
legacy storage-path singleton.
|
|
- `src/platform_legacy/legacy_platform_state.*` now exposes an ownable Web
|
|
platform-services entrypoint plus an explicit binding hook instead of only a
|
|
retained fallback object.
|
|
- `webgl/src/main.cpp` now owns the Web platform-services implementation and
|
|
seeds that owned instance into legacy platform state during startup.
|
|
- `src/platform_windows/windows_runtime_shell.*` no longer keeps `HINSTANCE`
|
|
in the retained main-window session state; the startup/shutdown path now
|
|
threads the module handle explicitly.
|
|
- `webgl/src/main.cpp` now owns a TU-local legacy `PlatformServices`
|
|
instance and binds that owned service into `App` during `StartApp()`.
|
|
- `android/src/cpp/main.cpp` now owns a function-lifetime legacy
|
|
`PlatformServices` instance in `android_main()` and binds that owned service
|
|
into `App` instead of binding the process-global fallback directly.
|
|
- `src/platform_legacy/legacy_platform_services.*` now exposes an ownable
|
|
`create_platform_services()` entrypoint alongside the legacy fallback
|
|
accessor.
|
|
- `linux/src/main.cpp` now owns a local legacy `PlatformServices` instance and
|
|
binds it into `App` explicitly instead of binding the process-global legacy
|
|
accessor directly.
|
|
- `src/app_events.cpp` no longer silently falls back to
|
|
`pp::platform::legacy::platform_services()` when `App` has no bound platform
|
|
services; the live app path now expects explicit platform-service ownership.
|
|
- `linux/src/main.cpp`, `webgl/src/main.cpp`, and `android/src/cpp/main.cpp`
|
|
now bind `pp::platform::legacy::platform_services()` explicitly at app
|
|
creation instead of relying on the hidden fallback in `App` event/platform
|
|
dispatch.
|
|
- `src/platform_windows/windows_runtime_shell.cpp` now owns the Windows tablet
|
|
object directly; the composition edge no longer binds `&WacomTablet::I` into
|
|
the Windows runtime path.
|
|
- `src/platform_legacy/legacy_platform_state.*` now exposes narrow Web helper
|
|
functions for the touched publish/flush/default-canvas/prepared-file paths,
|
|
so less of the Web fallback behavior lives inline in the legacy platform
|
|
singleton implementation.
|
|
- `src/platform_windows/windows_runtime_shell.cpp` no longer keeps a separate
|
|
retained `AppRuntime*` binding; the touched Windows shell and platform
|
|
helpers now derive runtime ownership directly from the owned `App`.
|
|
- `src/platform_windows/windows_runtime_shell.cpp` now explicitly owns the
|
|
Windows `App` lifetime through a retained `std::unique_ptr<App>` instead of
|
|
raw `new`/`delete` plus shutdown-side manual cleanup in the lifecycle shell.
|
|
- `src/platform_windows/windows_lifecycle_shell.cpp` now releases the bound
|
|
Windows app through `release_bound_app()` after runtime shutdown instead of
|
|
deleting it directly through the global shutdown path.
|
|
- `src/platform_windows/windows_window_shell.cpp` now routes the touched
|
|
key-map and VR-state reads through narrow helpers instead of keeping the
|
|
broader retained-state bundle live across the main window-proc body.
|
|
- `src/platform_legacy/legacy_platform_state.*` no longer exposes the mutable
|
|
retained GLFW hook bundle; Linux/Web fallback render-context/present/close
|
|
calls now go through narrow GLFW helper functions instead of an exported hook
|
|
struct.
|
|
- `scripts/automation/quiet-validate.ps1` is now the bundled checkpoint path
|
|
for Windows build/test plus optional platform and Apple remote validation,
|
|
with one compact JSON summary under `out/logs/quiet-validation`.
|
|
- `scripts/automation/platform-build.ps1` quiet mode now writes per-preset
|
|
configure/build logs and compact JSON output so Android/headless sweeps can
|
|
plug into the bundled quiet wrapper without flooding the console.
|
|
- `scripts/automation/apple-remote-build.ps1` quiet mode now writes the local
|
|
SSH session log, reports the remote `platform-build.sh` log path, and emits
|
|
compact JSON output so the bundled quiet wrapper can include the Apple gate
|
|
in the same checkpoint run.
|
|
|
|
What is still carrying too much live ownership:
|
|
|
|
- `pp_panopainter_ui`: 34 files, about 9102 lines
|
|
- `panopainter_app`: 29 files, about 8817 lines
|
|
- `pp_legacy_paint_document`: 7 files, about 5709 lines
|
|
- `pp_legacy_app`: 20 files, about 4368 lines
|
|
- `pp_legacy_ui_core`: 20 files, about 3770 lines
|
|
|
|
Current hotspot files:
|
|
|
|
- `src/canvas.cpp`: 17 lines
|
|
- `src/app_layout.cpp`: 109 lines
|
|
- `src/canvas_modes.cpp`: 1 line
|
|
- `src/node.cpp`: 12 lines
|
|
- `src/main.cpp`: 10 lines
|
|
- `src/node_panel_brush.cpp`: 2 lines
|
|
- `src/node_stroke_preview.cpp`: 76 lines
|
|
- `src/node_canvas.cpp`: 69 lines
|
|
- `src/app.cpp`: 94 lines
|
|
- `src/app_dialogs.cpp`: 95 lines
|
|
|
|
Latest slice:
|
|
|
|
- The remaining Windows entry/exit singleton write no longer lives at the
|
|
`run_main_application(...)` and `handle_window_close_message(...)` callsites;
|
|
`src/platform_windows/windows_runtime_shell.cpp` now centralizes that legacy
|
|
`App::I` side effect inside `bind_app(...)`, leaving the touched runtime and
|
|
lifecycle shell as explicit binder users instead of direct singleton writers.
|
|
- The touched `src/platform_windows/windows_platform_services.cpp` fan-out no
|
|
longer reaches the broader retained window bundle directly for main-window,
|
|
sandbox, and VR/session reads; the touched window/VR queries now route
|
|
through narrow runtime-shell accessors instead.
|
|
- `src/platform_windows/windows_bootstrap_helpers.cpp` no longer uses
|
|
`Canvas::I` for crash-recovery saves; the BugTrap pre-error handler now uses
|
|
the app-owned `NodeCanvas` document (`app.canvas->m_canvas`) and the new
|
|
runtime-shell window/sandbox accessors instead of direct singleton or
|
|
retained-state reads in the touched recovery path.
|
|
- The retained Apple document bridge/state pocket no longer lives in
|
|
`src/platform_legacy/legacy_platform_state.*`; it now lives in the Apple-owned
|
|
`src/platform_apple/apple_platform_state.cpp` plus
|
|
`src/platform_apple/apple_platform_services.*`, and the macOS/iOS entrypoints
|
|
now seed that state through `pp::platform::apple::set_legacy_apple_state(...)`
|
|
instead of the legacy namespace.
|
|
- `src/platform_legacy/legacy_platform_services.cpp` now consumes the
|
|
Apple-owned retained provider through `pp::platform::apple::...` call-throughs
|
|
instead of constructing or caching Apple retained bridge state inside the
|
|
legacy platform layer.
|
|
- The Win32 stylus and pointer-input path no longer reaches
|
|
`WacomTablet::I` directly inside
|
|
`src/platform_windows/windows_window_shell.cpp` or
|
|
`src/platform_windows/windows_stylus_input.cpp`; the live Windows runtime
|
|
shell now binds the active tablet explicitly through
|
|
`src/platform_windows/windows_runtime_shell.*`, leaving
|
|
`WacomTablet::I` only at the entry composition edge where the runtime shell
|
|
binds the active tablet instance.
|
|
- The remaining dense Windows bootstrap singleton pocket moved off
|
|
`App::I`: `setup_exception_handler(...)`, `initialize_main_window_startup_state(...)`,
|
|
and `_pre_call_callback(...)` now use explicit app/bound-runtime state
|
|
instead of reading the app singleton directly in
|
|
`src/platform_windows/windows_bootstrap_helpers.*`, and the app-side
|
|
platform dispatch helpers in `src/app_events.cpp` now also use the
|
|
instance they are invoked on instead of a global `App::I` fallback.
|
|
- The retained Web fallback service object and the Apple storage-path
|
|
preparation helper now also live in
|
|
`src/platform_legacy/legacy_platform_state.*` instead of being built inline
|
|
inside `src/platform_legacy/legacy_platform_services.cpp`, which trims
|
|
another process-global fallback/service pocket out of the legacy platform
|
|
shell.
|
|
- The Win32 window procedure, stylus state updates, lifecycle shutdown path,
|
|
and VR shell callback wiring no longer reach `App::I` directly; the live
|
|
Windows shell now binds the active `App*` explicitly through
|
|
`src/platform_windows/windows_runtime_shell.*`, leaving `App::I` only at
|
|
the entry/shutdown composition edge in the touched Win32 path.
|
|
- The full retained Apple document bridge construction no longer lives inline
|
|
in `src/platform_legacy/legacy_platform_services.cpp`; it now lives behind
|
|
`active_legacy_apple_document_platform_services()` in
|
|
`src/platform_legacy/legacy_platform_state.*`, and that retained service now
|
|
resets when seeded Apple handles change so first-use bridge capture stays in
|
|
sync with the active entrypoint state.
|
|
- Win32 main-thread task dispatch no longer reaches `AppRuntime` through
|
|
`App::I` inside `src/platform_windows/windows_platform_services.cpp`;
|
|
`src/platform_windows/windows_runtime_shell.*` now binds the active runtime
|
|
explicitly and clears that binding on shutdown, leaving the Windows queue
|
|
helper as a thinner runtime forwarder.
|
|
- `App` no longer owns `and_app` or `and_engine`; the retained Android
|
|
entrypoint now seeds only the explicit legacy platform storage snapshot
|
|
needed by touched platform services instead of storing Android-native
|
|
handles on the app singleton.
|
|
- `active_legacy_storage_paths()` no longer snapshots `App::I` lazily inside
|
|
`src/platform_legacy/legacy_platform_state.*`; storage roots are now seeded
|
|
explicitly from app startup plus the iOS, macOS, and Android entrypoints
|
|
through `set_legacy_storage_paths(...)`.
|
|
- `pp_platform_api` no longer compiles
|
|
`src/platform_linux/linux_platform_services.*`; Linux concrete platform code
|
|
now lives in `pp_platform_linux`, which `pp_legacy_app` and
|
|
`pp_platform_api_tests` link where needed.
|
|
- Win32 main-thread queued task ownership now lives in `AppRuntime` instead of
|
|
`src/platform_windows/windows_platform_services.cpp`, which removes another
|
|
runtime queue from retained platform-local static state and leaves the
|
|
Windows shell as a thin forwarder.
|
|
- The `platform_legacy`-mirrored Apple and GLFW handle cluster no longer lives
|
|
on `App`; retained Apple/GLFW platform state is now seeded explicitly from
|
|
the iOS, macOS, Linux, and WebGL entrypoints through
|
|
`src/platform_legacy/legacy_platform_state.*`.
|
|
- `pp_platform_api` no longer compiles
|
|
`src/platform_apple/apple_platform_services.*`; Apple concrete platform
|
|
code now lives in the new `pp_platform_apple` target, and
|
|
`panopainter_app` plus `pp_platform_api_tests` link that concrete target
|
|
where needed.
|
|
- Retained GLFW window hooks/state and retained Apple UI/app handle snapshots
|
|
now live in `src/platform_legacy/legacy_platform_state.*` instead of staying
|
|
inline in `src/platform_legacy/legacy_platform_services.cpp`, which trims
|
|
another process-global platform-state pocket out of the legacy platform
|
|
shell and removes more direct `App::I` reads from touched platform paths.
|
|
- Windows VR session snapshot ownership now lives in
|
|
`src/platform_windows/windows_vr_shell.h` and
|
|
`src/platform_windows/windows_platform_services.*` instead of on `App`,
|
|
with app-side reads now routed through `App::vr_session_snapshot()`.
|
|
- The live Windows entry shell now routes through
|
|
`run_main_application(...)` in
|
|
`src/platform_windows/windows_runtime_shell.*`, leaving `src/main.cpp` as a
|
|
minimal entry wrapper around `main(...)` and `WinMain(...)`.
|
|
- Retained legacy storage-path state now lives in
|
|
`src/platform_legacy/legacy_platform_state.*` instead of staying inline in
|
|
`src/platform_legacy/legacy_platform_services.cpp`, which trims another
|
|
process-global platform-state pocket out of the legacy platform shell.
|
|
- The remaining `NodeStrokePreview` clone-init, stroke-frame planning,
|
|
mix-pass adapter wiring, sample-pass adapter wiring, and immediate-draw
|
|
request construction now route through
|
|
`src/legacy_node_stroke_preview_runtime_services.*`,
|
|
`src/legacy_node_stroke_preview_draw_services.*`, and
|
|
`src/legacy_node_stroke_preview_sample_services.*`, leaving
|
|
`src/node_stroke_preview.cpp` as a thinner live adapter.
|
|
|
|
- `NodeCanvas::handle_event()` now routes through
|
|
`handle_legacy_node_canvas_event(...)` in
|
|
`src/legacy_canvas_tool_services.*`, leaving `src/node_canvas.cpp` as a much
|
|
thinner controller shell.
|
|
- `App::open_document()` now routes through
|
|
`src/legacy_document_open_services.cpp`, which moved the document route
|
|
classification and unsaved-project gating out of `src/app.cpp` and into the
|
|
retained document-open helper.
|
|
- `App::dialog_layer_rename()` now routes through
|
|
`open_legacy_document_layer_rename_dialog(...)` in
|
|
`src/legacy_document_layer_services.*`, which moved the remaining
|
|
overlay-open/wire/close workflow out of `src/app_dialogs.cpp`.
|
|
- The remaining low-level `NodeStrokePreview` viewport/query and texture-slot
|
|
plumbing now lives in
|
|
`src/legacy_node_stroke_preview_runtime_services.*` instead of staying
|
|
inline in `src/node_stroke_preview.cpp`.
|
|
- `NodePanelBrushPreset` global panel registration now lives in
|
|
`src/legacy_brush_preset_list_services.*` instead of staying on the live
|
|
node type as a static registry field.
|
|
- The remaining generic `Node` geometry/state pocket for `SetSize(...)`,
|
|
`SetMinSize(...)`, `SetMaxSize(...)`, and `SetPosition(const glm::vec2)` now
|
|
lives in `src/legacy_ui_node_style.*` instead of staying inline in
|
|
`src/node.cpp`.
|
|
- `Node::app_redraw()` and `Node::watch(...)` now live in
|
|
`src/legacy_ui_node_execution.cpp` instead of staying inline in `src/node.cpp`.
|
|
- `NodeStrokePreview::draw_stroke_immediate()` now routes through
|
|
`execute_legacy_node_stroke_preview_immediate_draw(...)` in
|
|
`src/legacy_node_stroke_preview_runtime_services.*`, which moves render-target
|
|
validation, viewport/clear-color save-restore, and immediate-runtime request
|
|
assembly out of the live node file.
|
|
- The remaining `NodePanelBrush` and `NodePanelBrushPreset` member bodies now
|
|
live in the existing retained helper layers
|
|
(`src/legacy_brush_panel_item_ui.*`,
|
|
`src/legacy_brush_panel_ui.*`,
|
|
`src/legacy_brush_panel_services.*`,
|
|
`src/legacy_brush_preset_panel_ui.*`,
|
|
`src/legacy_brush_preset_list_services.*`, and
|
|
`src/legacy_brush_preset_services.*`), leaving
|
|
`src/node_panel_brush.cpp` as a thin translation unit.
|
|
- `Node::load_internal(...)` now routes through
|
|
`load_legacy_ui_node(...)` in `src/legacy_ui_node_loader.*`, which moves the
|
|
init/attribute-parse/create/child-load/loaded shell out of `src/node.cpp`.
|
|
- The remaining Win32 shell wrappers for close, async lock/swap, stylus/FPS
|
|
updates, VR start/stop, window-state save, and the window-handle accessor now
|
|
live in `src/platform_windows/windows_platform_services.cpp` instead of
|
|
`src/main.cpp`, leaving `main.cpp` as a thinner entry/runtime dispatcher.
|
|
- The entire `CanvasModeGrid` implementation plus `ActionModeGrid` undo/redo
|
|
glue now live in `src/legacy_canvas_mode_helpers.cpp` instead of
|
|
`src/canvas_modes.cpp`, leaving `src/canvas_modes.cpp` as a minimal shell.
|
|
- `App::request_close()`, the RenderDoc frame wrappers, and the render/UI
|
|
thread entrypoint wrappers now route through
|
|
`src/legacy_app_runtime_shell_services.*` instead of staying inline in
|
|
`src/app.cpp`, leaving `app.cpp` as a thinner retained app shell.
|
|
- `App::show_progress()`, `App::message_box()`, and `App::input_box()` now
|
|
route through `src/legacy_app_dialog_services.*` instead of building dialog
|
|
plans and factories inline in `src/app_dialogs.cpp`.
|
|
- The remaining generic `Node` event/capture/resize shell plus the
|
|
width/height/padding/margin/flex/visibility/geometry wrappers now live in
|
|
`src/legacy_ui_node_execution.cpp` and `src/legacy_ui_node_style.*` instead
|
|
of staying inline in `src/node.cpp`, leaving `node.cpp` as a near-trivial
|
|
attribute/load shell.
|
|
|
|
Current architecture mismatches that must be treated as real blockers:
|
|
|
|
- `pp_platform_api` no longer compiles Apple implementation files, but it
|
|
still owns too much concrete platform implementation instead of only
|
|
platform-neutral policy and interface code.
|
|
- `src/platform_apple/apple_platform_services.cpp` no longer reaches `App::I`
|
|
directly, and Linux FPS title reporting now uses an injected callback, but
|
|
retained Apple bridging in `platform_legacy` and other platform/app coupling
|
|
remain, even though iOS keyboard visibility and prepared-file save handoff
|
|
now also route through explicit Apple bridge callbacks and Apple render-
|
|
context hooks plus iOS main-render-target binding now route through the same
|
|
bridge style, as do Apple crash-test, app-close, and iOS SonarPen hooks,
|
|
while Linux/Web GLFW render-context acquire/present and Linux app-close now
|
|
route through retained local GLFW callback hooks, and retained Apple ObjC
|
|
handles plus storage paths now sit behind one local `platform_legacy`
|
|
helper instead of being re-read through `App::I` in each touched path, with
|
|
the retained GLFW window hooks, Apple handle snapshots, and fallback
|
|
storage-path return now also using local retained-state helpers instead of
|
|
direct method-body reads, while Windows VR session snapshot state now also
|
|
lives behind platform-owned helpers instead of on `App`, the
|
|
`platform_legacy`-mirrored Apple/GLFW handle cluster is now seeded
|
|
explicitly from platform entrypoints instead of being copied out of `App`,
|
|
and retained storage roots are now also seeded explicitly instead of being
|
|
lazily copied from `App::I` inside `active_legacy_storage_paths()`, while the
|
|
retained Apple document bridge now also lives in `legacy_platform_state.*`
|
|
instead of being built inline in `legacy_platform_services.cpp`.
|
|
- `src/platform_legacy/legacy_platform_services.*` is still part of the live
|
|
app shell.
|
|
- `pp_panopainter_ui` still depends on `pp_legacy_app`.
|
|
- `Canvas`, `NodeCanvas`, and `NodeStrokePreview` still own too much live
|
|
OpenGL execution around the renderer boundary, even though `NodeCanvas`
|
|
display resolve, cache-to-screen composite, post-draw mask/grid/current-mode
|
|
sequencing, per-layer/per-plane retained draw execution, and shared
|
|
checkerboard background setup now route through retained draw-merge helpers,
|
|
with the cache-to-screen checkerboard-plane callback setup also reduced and
|
|
the merged-path checkerboard background-plane callback plus per-plane
|
|
merged-texture draw callback plus non-`draw_merged` per-frame layer draw
|
|
callback plus the smoothing-mask face shader/draw pass plus heightmap,
|
|
current-mode, and grid-mode callback setup now routed through the same
|
|
retained helper family, while post-draw callback assembly and the remaining
|
|
per-layer render-path orchestration now also route through retained
|
|
draw-merge helpers even though the broader node draw loop is still inline,
|
|
with the non-`draw_merged` outer layer/plane traversal now also routing
|
|
through `execute_legacy_canvas_draw_layer_traversal(...)`, while the heavier
|
|
per-layer GL setup now also routes through
|
|
`make_legacy_canvas_draw_merge_layer_path_gl_execution(...)` even though the
|
|
remaining draw lambdas and broader node draw loop still live in
|
|
`src/node_canvas.cpp`, where the post-draw/display-resolve tail now also
|
|
routes through `execute_node_canvas_draw_merge_tail(...)`, while the
|
|
unmerged-path onion-range planning, plane filtering, per-layer visit
|
|
handling, and per-visit layer-path execution now also route through
|
|
`execute_legacy_canvas_draw_unmerged_node_canvas_shell(...)`, while the
|
|
broader unmerged cache/viewport/background/composite pass setup now also
|
|
routes through
|
|
`execute_legacy_canvas_draw_unmerged_node_canvas_pass(...)`, while
|
|
`NodeCanvas::draw()` setup, merged-pass shell, and unmerged-pass shell now
|
|
also route through `prepare_legacy_node_canvas_draw_setup(...)`,
|
|
`execute_legacy_canvas_draw_node_canvas_shell(...)`,
|
|
`execute_legacy_canvas_draw_merged_pass(...)`, and
|
|
`execute_legacy_canvas_draw_node_canvas_unmerged_pass(...)`, which
|
|
materially shortens the live `NodeCanvas::draw()` body even though the file
|
|
itself is still large.
|
|
- `app_layout.cpp` and `app_dialogs.cpp` are still mixed shell/controller files
|
|
rather than thin composition/binding surfaces, even though tools-menu binding
|
|
plus nested panels/options submenu wiring now live in
|
|
`src/app_layout_tools_menu.cpp` and `App::init_menu_tools()` is now a thin
|
|
call-through, while file-menu binding plus the export submenu wiring now also
|
|
live in `src/app_layout_file_menu.cpp` and `App::init_menu_file()` is now a
|
|
thin call-through, while about-menu and layer-menu wiring now also live in
|
|
`src/app_layout_about_layer_menu.cpp` and `App::init_menu_about()` plus
|
|
`App::init_menu_layer()` are now thin call-throughs, while sidebar panel
|
|
binding and popup wiring now also live in `src/app_layout_sidebar.cpp` and
|
|
`App::init_sidebar()` is now a thin call-through, while main-toolbar binding
|
|
now also lives in `src/app_layout_main_toolbar.cpp` and
|
|
`App::init_toolbar_main()` is now a thin call-through, while edit-menu
|
|
binding now also lives in `src/app_layout_edit_menu.cpp` and
|
|
`App::init_menu_edit()` is now a thin call-through, while UI-direction and
|
|
persisted floating/docked panel-state ownership now also live in
|
|
`src/app_layout_ui_state.cpp`, while draw-toolbar binding now also lives in
|
|
`src/app_layout_draw_toolbar.cpp`, while brush-refresh now also lives in
|
|
`src/app_layout_brush.cpp`, while layout bootstrap plus reload/load
|
|
continuation wiring now also lives in `src/app_layout_bootstrap.cpp`, and
|
|
`src/app_layout.cpp` is now mostly thin call-through entrypoints, while the
|
|
informational overlay opener family now also lives in
|
|
`src/app_dialogs_info_openers.cpp` and the corresponding `App::dialog_*`
|
|
entrypoints are thinner, while the export/video/PPBR dialog family now also
|
|
lives in
|
|
`src/app_dialogs_export.cpp` and those `App::dialog_*` entrypoints are
|
|
thinner too, while new/open/save/browse/resize workflow entrypoints now also
|
|
live in `src/app_dialogs_workflow.cpp`, while the layer-rename dialog open /
|
|
wire / close pocket now lives in `src/legacy_document_layer_services.*`, and
|
|
`src/app_dialogs.cpp` is now a thinner dialog dispatch surface.
|
|
- `App`, `Canvas`, `Node`, retained workers, and platform entrypoints still use
|
|
global singleton reach, raw observer pointers, retained static worker
|
|
ownership in several app families, and ad hoc mutex/condition-variable
|
|
ownership, even though most previously detached or raw app-facing worker
|
|
launches now use owned `std::jthread` or service-owned worker queues and
|
|
`AppRuntime` now owns render/UI workers with explicit `std::jthread`
|
|
shutdown semantics while the Windows splash-dialog and HMD renderer workers
|
|
also use owned `std::jthread` lifecycle, `LogRemote` now uses the same
|
|
ownership model, the Windows VR device now has explicit `std::unique_ptr`
|
|
ownership instead of raw global lifetime, and the Windows main-loop/VR
|
|
coordination flags now use `std::atomic` instead of unsynchronized globals,
|
|
while the main Win32 entrypoint now groups window/GL/task/VR state behind a
|
|
retained local state object instead of separate process-wide globals, the
|
|
Win32 async GL/context lock state now lives under
|
|
`src/platform_windows/windows_platform_services.cpp` instead of `main.cpp`
|
|
retained state, the main-thread queued task state now lives under
|
|
`src/platform_windows/windows_platform_services.cpp` instead of staying in
|
|
`src/main.cpp`, the canvas async worker now sits behind a named retained
|
|
local worker-state helper instead of a bare static accessor, the
|
|
prepared-file worker and the canvas async import/export/save/open worker now
|
|
live under `AppRuntime` instead of retained static app-events/canvas
|
|
workers, and the splash-screen dialog loop, HWND ownership, and bitmap setup
|
|
now live in `src/platform_windows/windows_splash.cpp` instead of
|
|
`src/main.cpp`, while Win32 pointer API loading, stylus/ink timer decay,
|
|
Wintab packet reset, and `WM_POINTERUPDATE` pen/touch handling now also live
|
|
in `src/platform_windows/windows_stylus_input.cpp` instead of `src/main.cpp`,
|
|
while the retained Win32 VR/HMD shell now also routes through
|
|
`src/platform_windows/windows_vr_shell.h` instead of staying inline in
|
|
`src/main.cpp`, while RenderDoc startup/frame capture, SHCore DPI bootstrap,
|
|
Win32 error-string conversion, the GL debug pre/post callbacks, and the WMI
|
|
startup probe now also live in
|
|
`src/platform_windows/windows_bootstrap_helpers.cpp` instead of
|
|
`src/main.cpp`, while Win32 lifecycle running-state, close/shutdown
|
|
sequencing, FPS title update/wakeup posting, stylus frame update, window
|
|
preference save, and VR lifecycle wrappers now also live in
|
|
`src/platform_windows/windows_lifecycle_shell.cpp` instead of `src/main.cpp`,
|
|
while the Win32 startup/window/bootstrap path now also lives under
|
|
`src/platform_windows/windows_bootstrap_helpers.*` for runtime-data
|
|
discovery, startup-state initialization, window creation, pixel-format
|
|
setup, GL loader init, runtime-info logging, and core-context upgrade
|
|
sequencing, while BugTrap/SEH recovery setup now also lives in
|
|
`src/platform_windows/windows_bootstrap_helpers.cpp` instead of
|
|
`src/main.cpp`, while the Win32 window procedure and retained message-handling
|
|
shell now also live in `src/platform_windows/windows_window_shell.*`
|
|
instead of `src/main.cpp`, while the `WinMain` argv conversion bridge now
|
|
also lives in `src/platform_windows/windows_bootstrap_helpers.*` instead of
|
|
staying inline in `src/main.cpp`, while retained input-state zeroing and reverse
|
|
key-map initialization now also live in
|
|
`src/platform_windows/windows_window_shell.*` instead of `src/main.cpp`,
|
|
while the remaining interactive Win32 runtime
|
|
pocket for touch registration, render/UI thread startup, GL debug callback
|
|
hookup, Wintab initialization/skip, icon setup, placement restore, optional
|
|
VR start, splash dismissal, message loop, and shutdown cleanup now also
|
|
lives in `src/platform_windows/windows_runtime_shell.*` instead of
|
|
`src/main.cpp`, which materially thins `src/main.cpp` even though broader
|
|
entrypoint/runtime composition still remains there,
|
|
while `App::rec_loop()` now delegates worker-iteration orchestration into
|
|
the retained recording bridge, `App::update_rec_frames()` now delegates
|
|
recording label refresh through that same retained recording path, and the
|
|
UI observer math, repeated UI child traversal, and canvas toolbar refresh
|
|
now live in `src/legacy_app_frame_services.cpp` instead of staying inline in
|
|
`src/app.cpp`, while the larger document/export/save/open/thumbnail
|
|
document-IO cluster now lives in `src/legacy_canvas_document_io_services.cpp`
|
|
and `src/app.cpp` is materially thinner, while `App::clear()`,
|
|
`App::check_license()`, `App::async_start()`, `App::async_redraw()`,
|
|
`App::async_end()`, and `App::async_swap()` now also live in
|
|
`src/legacy_app_runtime_shell_services.cpp` instead of staying inline in
|
|
`src/app.cpp`,
|
|
while the canvas state-management cluster for picking, clear/clear-all, layer
|
|
add/remove/order/lookups, animation frame control, resize, and snapshot
|
|
save/restore now lives in `src/legacy_canvas_state_services.cpp` instead of
|
|
`src/canvas.cpp`, while the larger import/export/save/open/thumbnail
|
|
document-IO cluster now lives in `src/legacy_canvas_document_io_services.cpp`
|
|
and `NodeStrokePreview` render-target setup plus immediate-pass sequencing
|
|
now route through retained preview execution helpers, even though the bridge
|
|
still owns worker-side readback flow and encoder-state label reads, while the
|
|
main live-pass request assembly and framebuffer-copy setup now also route
|
|
through `src/legacy_node_stroke_preview_execution_services.h`, even though
|
|
broader preview-pass orchestration still lives in
|
|
`src/node_stroke_preview.cpp`, while the immediate preview pass-sequencing
|
|
family inside `draw_stroke_immediate()` now also routes through
|
|
`NodeStrokePreview::execute_stroke_draw_immediate_pass_sequence(...)`, while
|
|
the remaining immediate preview pass shell now also routes through
|
|
`execute_legacy_node_stroke_preview_draw_immediate_shell(...)`, which
|
|
materially reduces the live preview-pass body even though broader
|
|
worker/readback flow still remains inline, while the immediate preview
|
|
runtime/orchestration block for stroke setup, prepared-stroke construction,
|
|
pass planning, shader setup, and live render request assembly now also
|
|
routes through `src/legacy_node_stroke_preview_runtime_services.*` instead
|
|
of staying inline in `src/node_stroke_preview.cpp`, while
|
|
the low-level preview GL dispatch and texture-slot binding pocket now also
|
|
routes through `src/legacy_node_stroke_preview_runtime_services.*` instead
|
|
of staying inline in `src/node_stroke_preview.cpp`, while
|
|
`NodeStrokePreview` remaining mix-pass planning and execution now also route
|
|
through `src/legacy_node_stroke_preview_draw_services.*`, which trims the
|
|
last dedicated mix-orchestration pocket from `src/node_stroke_preview.cpp`,
|
|
while
|
|
`NodeCanvas::draw()` unmerged-pass blend-gate, layer-orientation, and
|
|
callback-assembly setup now also route through
|
|
`execute_node_canvas_draw_unmerged_pass(...)`, which trims another coherent
|
|
unmerged draw shell from the live node even though the file itself remains
|
|
large, while `NodeCanvas::draw()` merged-pass callback wiring and pass setup
|
|
now also route through `execute_node_canvas_draw_merged_pass(...)`, which
|
|
trims another coherent merged draw shell from the live node even though the
|
|
broader draw loop still remains inline,
|
|
while `Canvas::draw_objects_direct(...)` and `Canvas::draw_objects(...)` now
|
|
route through `src/legacy_canvas_object_draw_services.*` instead of staying
|
|
inline in `src/canvas.cpp`, which trims another coherent object-draw and
|
|
viewport-state execution family from the live canvas shell, while the
|
|
static canvas plane geometry/orientation data now also lives in
|
|
`src/legacy_canvas_plane_data.cpp` instead of staying inline in
|
|
`src/canvas.cpp`, which trims another retained data-ownership pocket from
|
|
the live canvas shell, while
|
|
`Canvas::stroke_draw_samples(...)`, `Canvas::stroke_commit()`, and the
|
|
larger stroke commit/sample execution family now also route through
|
|
`src/legacy_canvas_stroke_commit_services.*` instead of staying inline in
|
|
`src/canvas.cpp`, which trims another large retained stroke-render and
|
|
viewport-state execution family from the live canvas shell, while the live
|
|
`Canvas::stroke_draw()` orchestration now also routes through
|
|
`src/legacy_canvas_stroke_live_services.cpp` instead of staying inline in
|
|
`src/canvas.cpp`, which materially thins another large retained live
|
|
stroke-render pocket, while `Canvas::layer_merge(...)`,
|
|
`Canvas::flood_fill(...)`, and `Canvas::FloodData::apply()` now also route
|
|
through `src/legacy_canvas_layer_services.cpp` instead of staying inline in
|
|
`src/canvas.cpp`, which trims another coherent retained layer/fill workflow
|
|
pocket, while `Canvas::stroke_end(...)`, `Canvas::stroke_cancel(...)`,
|
|
`Canvas::stroke_draw_mix(...)`, `Canvas::stroke_draw_project(...)`,
|
|
`Canvas::stroke_update(...)`, and `Canvas::stroke_start(...)` now also route
|
|
through `src/legacy_canvas_stroke_runtime_services.*` instead of staying
|
|
inline in `src/canvas.cpp`, which trims another large retained stroke/runtime
|
|
pocket, while the final camera/timelapse member definitions now also live in
|
|
`src/legacy_canvas_camera_services.cpp` instead of staying inline in
|
|
`src/canvas.cpp`, which trims another retained canvas shell pocket, while the
|
|
`CanvasModeTransform` interaction family now also routes through
|
|
`src/legacy_canvas_mode_transform.cpp` instead of staying inline in
|
|
`src/canvas_modes.cpp`, which materially thins another retained canvas-view
|
|
and transform-mode execution pocket, while the `CanvasModePen` and
|
|
`CanvasModeLine` interaction families now also route through
|
|
`src/legacy_canvas_mode_pen_line.cpp` instead of staying inline in
|
|
`src/canvas_modes.cpp`,
|
|
while the `CanvasModeFill` and `CanvasModeFloodFill` interaction families now
|
|
also route through `src/legacy_canvas_mode_fill.cpp` instead of staying
|
|
inline in `src/canvas_modes.cpp`, which materially thins another retained
|
|
fill-mode execution pocket from the broader canvas/render hotspot family,
|
|
while `NodePanelBrush` save/restore/scan/reload/find/get-path ownership now
|
|
routes through `src/legacy_brush_panel_services.*` instead of staying inline
|
|
in `src/node_panel_brush.cpp`, which trims another retained brush-workflow
|
|
pocket from the live UI node even though the broader panel still remains
|
|
large, while `NodePanelBrushPreset` save/restore and package
|
|
import/export/import-ABR routing now also lives in
|
|
`src/legacy_brush_preset_services.*` instead of staying inline in
|
|
`src/node_panel_brush.cpp`, which trims another large preset-workflow pocket
|
|
from the live UI node, while `NodePanelBrushPreset` init/menu wiring, click
|
|
handling, item construction, and added-state update now also route through
|
|
`src/legacy_brush_preset_panel_ui.*` instead of staying inline in
|
|
`src/node_panel_brush.cpp`, which materially thins another retained preset
|
|
panel UI pocket, while the retained `LegacyBrushPresetListServices` block
|
|
now also lives in `src/legacy_brush_preset_list_services.*` instead of
|
|
staying inline in `src/node_panel_brush.cpp`, which trims another retained
|
|
preset-list pocket, while `NodeButtonBrush` clone/init/icon/read/write/draw
|
|
behavior and `NodeBrushPresetItem` clone/init/draw behavior now also live in
|
|
`src/legacy_brush_panel_item_ui.*` instead of staying inline in
|
|
`src/node_panel_brush.cpp`, which trims the remaining brush-item UI pocket
|
|
from the live brush panel file, while `NodePanelBrushPreset` popup-close
|
|
event handling now also lives in
|
|
`src/legacy_brush_preset_panel_ui.*`
|
|
instead of staying inline in `src/node_panel_brush.cpp`, which removes the
|
|
last inline brush-panel popup close handler from the live node. The
|
|
`NodePanelBrushPreset` registration/lifecycle pocket now also routes through
|
|
the preset-list helper registry instead of a node-local static vector,
|
|
which removes the remaining live preset-panel ownership glue from
|
|
`src/node_panel_brush.cpp`, and preset-restore notification visibility now
|
|
also stays with `src/legacy_brush_preset_services.*` instead of the node
|
|
wrapper. The broader preset workflow pocket still remains, while
|
|
`NodeCanvas::handle_event()`
|
|
now also routes
|
|
through `execute_node_canvas_handle_event(...)`, which trims another coherent
|
|
input-routing block from `src/node_canvas.cpp` even though the file is still
|
|
a live canvas/controller shell, while `NodeCanvas` restore/clear context,
|
|
resize handling, camera reset, buffer creation, cursor visibility/update,
|
|
tick, and destroy ownership now also route through
|
|
`src/legacy_node_canvas_state_services.*` instead of staying inline in
|
|
`src/node_canvas.cpp`, which materially thins another retained state/control
|
|
pocket, while shared canvas-mode GL wrappers plus the
|
|
`CanvasModeBasicCamera` and `CanvasModeCamera` input handlers now also route
|
|
through `src/legacy_canvas_mode_helpers.*` instead of staying inline in
|
|
`src/canvas_modes.cpp`, while
|
|
preview stroke preparation, dual-brush setup, and live pass-orchestration
|
|
request assembly now also route through retained preview execution helpers,
|
|
while `NodeStrokePreview` retained lifecycle, worker-thread shell,
|
|
render-to-image path, on-screen handling, and preview texture ownership now
|
|
also route through `src/legacy_node_stroke_preview_runtime_services.cpp`
|
|
instead of staying inline in `src/node_stroke_preview.cpp`, while
|
|
`NodeCanvas::init()` plus the remaining `NodeCanvas::draw()` outer shell now
|
|
also route through `src/legacy_node_canvas_draw_services.*` instead of
|
|
staying inline in `src/node_canvas.cpp`, which materially reduces the live
|
|
node to a thinner controller surface around event routing and state wrappers,
|
|
while `Node::on_event(...)` plus mouse/key capture and release ownership now
|
|
also route through `src/legacy_ui_node_event.*` instead of staying inline in
|
|
`src/node.cpp`, which materially thins the base scene-graph event shell
|
|
without changing its public surface, while `Node` child attach/detach/reorder
|
|
operations now route through named
|
|
local helpers in `src/node.cpp`, and `Node::load_internal(...)` child XML
|
|
loading now also routes through `src/legacy_ui_node_loader.*`, which makes
|
|
the scene-graph mutation and child-instantiation paths easier to reason
|
|
about without yet moving ownership into `pp_ui_core`, while the generic
|
|
per-frame node execution/traversal family for `restore_context`,
|
|
`clear_context`, `update`, `update_internal`, and `tick` now also lives in
|
|
`src/legacy_ui_node_execution.cpp`, while `Node::parse_attributes(...)` now
|
|
also routes through `src/legacy_ui_node_attributes.*` instead of staying
|
|
inline in `src/node.cpp`, while the remaining generic `Node` lifecycle/state
|
|
pocket for no-op lifecycle hooks, add/remove propagation, move construction,
|
|
destruction cleanup, and base clone plumbing now also routes through
|
|
`src/legacy_ui_node_lifecycle.*` instead of staying inline in
|
|
`src/node.cpp`,
|
|
while `Canvas` point-trace/unproject/project/camera push-pop-get-set and
|
|
face-to-shape helpers now also route through
|
|
`src/legacy_canvas_projection_services.*` instead of staying inline in
|
|
`src/canvas.cpp`, while `App::draw(...)`, `App::update(...)`,
|
|
`App::terminate(...)`, `App::update_memory_usage(...)`,
|
|
`App::update_rec_frames(...)`, `App::res_from_index(...)`,
|
|
`App::res_to_index(...)`, `App::res_to_string(...)`, `App::rec_clear(...)`,
|
|
`App::rec_start(...)`, `App::rec_stop(...)`, `App::rec_export(...)`,
|
|
`App::rec_loop(...)`, and `App::render_thread_tick(...)` now also route
|
|
through `src/legacy_app_runtime_shell_services.cpp` instead of staying
|
|
inline in `src/app.cpp`, while `Canvas::draw_merge(...)`, the temporary
|
|
paint/branch orchestration helpers, final-plane composite, timelapse commit,
|
|
create/destroy, clear-context, and camera accessors now also route through
|
|
`src/legacy_canvas_render_shell_services.*` instead of staying inline in
|
|
`src/canvas.cpp`, while `Node::destroy()`, `root()`, `set_manager(...)`,
|
|
`added_to_root()`, `handle_on_screen(...)`, template loading helpers, child
|
|
add/remove/move helpers, and child query helpers now also route through
|
|
`src/legacy_ui_node_tree_services.cpp` instead of staying inline in
|
|
`src/node.cpp`, while the `CanvasModeMaskFree` and `CanvasModeMaskLine`
|
|
interaction families now also route through
|
|
`src/legacy_canvas_mode_mask.cpp` instead of staying inline in
|
|
`src/canvas_modes.cpp`, while the remaining live render/pass orchestration in
|
|
`NodeStrokePreview::draw_stroke_immediate()` now also routes through
|
|
`src/legacy_node_stroke_preview_draw_services.*` instead of staying inline in
|
|
`src/node_stroke_preview.cpp`, and while the generic Yoga
|
|
style/visibility pocket from `Node::SetWidth(...)` through `Node::GetRTL()`
|
|
now also routes through `src/legacy_ui_node_style.*` instead of staying
|
|
inline in `src/node.cpp`, while the preview sample execution pocket for
|
|
sample-point conversion, brush vertex upload, request assembly, and the
|
|
`execute_legacy_canvas_stroke_sample(...)` call now also lives in
|
|
`src/legacy_node_stroke_preview_sample_services.*` instead of staying inline
|
|
in `src/node_stroke_preview.cpp`.
|
|
- Modern C++23 usage exists in extracted components, especially `std::span`,
|
|
explicit result/status objects, and a few concepts, but the live app still
|
|
does not consistently express ownership, thread affinity, or renderer
|
|
resources through safe component contracts.
|
|
|
|
Conclusion:
|
|
|
|
- the base component extraction is real
|
|
- the app shell thinning is not done
|
|
- the platform split is not done
|
|
- the UI split is not done
|
|
- the renderer/app ownership split is not done
|
|
- future backend lab work is still premature until those four statements change
|
|
|
|
## Final Target Architecture
|
|
|
|
The old roadmap showed a straight chain.
|
|
That was too simple.
|
|
The real target is a layered DAG with a thin composition root.
|
|
|
|
```text
|
|
pp_foundation
|
|
-> pp_assets
|
|
-> pp_paint
|
|
-> pp_document
|
|
|
|
pp_foundation
|
|
-> pp_renderer_api
|
|
-> pp_renderer_gl
|
|
|
|
pp_document + pp_paint + pp_renderer_api
|
|
-> pp_paint_renderer
|
|
|
|
pp_foundation + pp_document
|
|
-> pp_app_core
|
|
|
|
pp_foundation
|
|
-> pp_ui_core
|
|
|
|
pp_platform_api
|
|
-> pp_platform_windows
|
|
-> pp_platform_apple
|
|
-> pp_platform_linux
|
|
-> pp_platform_android
|
|
-> pp_platform_web
|
|
-> pp_platform_vr
|
|
|
|
pp_app_core + pp_ui_core + pp_paint_renderer + pp_platform_api
|
|
-> pp_panopainter_ui
|
|
|
|
pp_app_core + pp_panopainter_ui + pp_platform_*
|
|
-> panopainter_app
|
|
```
|
|
|
|
Key ownership rules:
|
|
|
|
- `pp_platform_api` is interface and policy only. No concrete platform service
|
|
implementation files belong there.
|
|
- `pp_platform_*` owns platform SDK, OS handles, platform event loops, and
|
|
concrete service bridges.
|
|
- `pp_ui_core` owns generic node/layout/overlay behavior and generic controls.
|
|
- `pp_panopainter_ui` owns app-specific panels, dialogs, canvas views, and
|
|
UI-to-app bindings.
|
|
- `pp_app_core` owns planner logic, workflow policy, and service contracts. It
|
|
does not own nodes, GL objects, or platform handles.
|
|
- `pp_paint_renderer` owns renderer-facing paint/export/preview contracts.
|
|
- `panopainter_app` owns composition only. It should stop being a second home
|
|
for document workflow, dialog orchestration, platform state, or renderer
|
|
execution.
|
|
- Threading and task dispatch are app runtime services, not incidental static
|
|
queues on `App` or detached workers launched from panels, dialogs, canvas, or
|
|
cloud helpers.
|
|
- UI ownership is handle/registry based at component boundaries. Raw `Node*`
|
|
can remain as non-owning implementation detail only when lifetime is proven
|
|
by checked handles or scoped connections.
|
|
- Renderer-facing app code uses `pp_renderer_api` resources and command/context
|
|
contracts. `Texture2D`, `RTT`, direct GL dispatch, and render-thread helpers
|
|
must not leak into future-backend-facing UI or document code.
|
|
|
|
## Workstreams
|
|
|
|
### 1. Break The Canvas And Preview Hotspots First
|
|
|
|
This is the highest-value work because it moves the largest concentration of
|
|
real app behavior out of the old shell.
|
|
|
|
Required outcomes:
|
|
|
|
- `canvas.cpp` loses major document-plus-render ownership
|
|
- `node_canvas.cpp` and `node_stroke_preview.cpp` lose major render-pass
|
|
orchestration
|
|
- concrete GL execution moves behind renderer-facing services instead of living
|
|
in app/node files
|
|
|
|
### 2. Thin The App Shell
|
|
|
|
`app.cpp`, `app_layout.cpp`, and `app_dialogs.cpp` must stop acting as mixed
|
|
workflow, UI, and composition files.
|
|
|
|
Required outcomes:
|
|
|
|
- `app_layout.cpp` becomes menu/toolbar binding composition
|
|
- `app_dialogs.cpp` becomes workflow dispatch plus retained dialog opening glue
|
|
- `app.cpp` becomes startup/frame/queue composition over named helpers
|
|
|
|
### 3. Finish The UI Core Split
|
|
|
|
`pp_ui_core` exists, but generic widget ownership is still incomplete.
|
|
|
|
Required outcomes:
|
|
|
|
- generic `Node` and control code moves out of `pp_legacy_ui_core`
|
|
- `pp_panopainter_ui` keeps only app-specific nodes
|
|
- shared overlay/lifetime behavior stays centered in `pp_ui_core`
|
|
- the scene graph has explicit ownership, non-owning references, scoped
|
|
callback connections, and documented UI-thread affinity
|
|
|
|
### 4. Make Runtime And Thread Ownership Explicit
|
|
|
|
This is crucial for a modern app architecture and must move with the app-shell
|
|
split, not after it.
|
|
|
|
Required outcomes:
|
|
|
|
- render/UI/worker queues are owned by explicit runtime services
|
|
- detached worker threads are replaced by joinable/cancellable ownership or a
|
|
task service with shutdown semantics
|
|
- render-thread and UI-thread access are expressed through small contracts that
|
|
can be implemented by future platform shells
|
|
- `App::I` and `Canvas::I` stop being the way cross-thread code reaches state
|
|
|
|
### 5. Finish The Platform Split
|
|
|
|
This is still a real blocker, but it should follow the bulk code-moving work
|
|
above instead of taking priority over the main app hotspots.
|
|
|
|
Required outcomes:
|
|
|
|
- remove concrete platform code from `pp_platform_api`
|
|
- remove `App::I` reach from platform service implementations
|
|
- remove app-owned cross-platform handle storage
|
|
- reduce `platform_legacy` to thin composition or delete it
|
|
|
|
### 6. Retire Thick Workflow Bridges
|
|
|
|
Open/save/session/export/cloud/brush package flows are still too distributed
|
|
across retained app, panel, and dialog files.
|
|
|
|
Required outcomes:
|
|
|
|
- document workflow bridges become thin adapters over `pp_app_core`
|
|
- cloud transfer and cloud browser ownership move out of retained UI nodes
|
|
- brush package import/export ownership moves out of retained panel code and
|
|
no longer depends on detached worker launch sites
|
|
|
|
### 7. Only Then Resume Future Backend Work
|
|
|
|
Vulkan, Metal, and package-only cleanup are explicitly downstream of the app
|
|
architecture work above.
|
|
|
|
Do not treat future backend scaffolds as proof that modernization is near done
|
|
while the current shell still depends on:
|
|
|
|
- `platform_legacy`
|
|
- `pp_legacy_app`
|
|
- `pp_legacy_ui_core`
|
|
- `pp_legacy_paint_document`
|
|
- large GL-heavy node and canvas files
|
|
|
|
## Exit Criteria
|
|
|
|
The modernization is not done until these are all true:
|
|
|
|
- `pp_platform_api` contains only platform-neutral code
|
|
- `src/platform_apple/*`, `src/platform_linux/*`, and other concrete platform
|
|
implementations do not reach `App::I`
|
|
- `platform_legacy` is gone or reduced to a trivial temporary shim
|
|
- `App` no longer stores cross-platform handle state that belongs to platform
|
|
shells
|
|
- `pp_panopainter_ui` no longer depends on `pp_legacy_app`
|
|
- `panopainter_app` is a composition root, not a second workflow layer
|
|
- `canvas.cpp`, `node_canvas.cpp`, and `node_stroke_preview.cpp` no longer own
|
|
large renderer-orchestration bodies
|
|
- live app, UI, canvas, cloud, and platform code no longer launch detached
|
|
worker threads without owned shutdown/cancellation
|
|
- render/UI task queues are owned behind explicit app runtime services
|
|
- raw app/UI pointers are non-owning implementation details only, backed by
|
|
checked handles, scoped connections, or documented owner objects
|
|
- future-backend-facing app and UI code depends on renderer API contracts, not
|
|
retained OpenGL resource classes or direct GL execution
|
|
- `pp_legacy_ui_core`, `pp_legacy_app`, and `pp_legacy_paint_document` are
|
|
removed or reduced to narrow, explicit adapter seams with debt ids and clear
|
|
removal conditions
|
|
|
|
## Active Queue
|
|
|
|
Use `docs/modernization/tasks.md` for the current architecture task bundles,
|
|
ordered by real code-moving priority.
|
|
Use `docs/modernization/tasks-done.md` only for history.
|