Centralize legacy brush UI bridge

This commit is contained in:
2026-06-04 12:18:26 +02:00
parent c3d85074ac
commit a2e795a356
10 changed files with 461 additions and 350 deletions

View File

@@ -86,6 +86,8 @@ set(PP_PANOPAINTER_APP_SOURCES
) )
set(PP_PANOPAINTER_UI_SOURCES set(PP_PANOPAINTER_UI_SOURCES
src/legacy_brush_ui_services.cpp
src/legacy_brush_ui_services.h
src/node_about.cpp src/node_about.cpp
src/node_canvas.cpp src/node_canvas.cpp
src/node_changelog.cpp src/node_changelog.cpp

View File

@@ -173,6 +173,12 @@ Known local toolchain state:
contracts while legacy `Canvas` mode state, transform actions, picking, contracts while legacy `Canvas` mode state, transform actions, picking,
touch-lock, save/UI/cursor calls, brush-size controls, and history execution touch-lock, save/UI/cursor calls, brush-size controls, and history execution
remain tracked by `DEBT-0027`. remain tracked by `DEBT-0027`.
- `src/legacy_brush_ui_services.*` is the current UI-shell bridge for brush
color, texture, preset, stroke-refresh, brush texture-list, and stroke-control
execution. It keeps those live paths on the `pp_app_core` contracts while
legacy `Brush`, `Canvas::I`, image load/save, `NodePanelBrush`,
`NodePanelStroke`, quick/color refreshes, and the temporary
`NodePanelBrush` friend adapter remain tracked by `DEBT-0023`.
- `pano_cli simulate-image-import` decodes an embedded tiny PNG through - `pano_cli simulate-image-import` decodes an embedded tiny PNG through
`pp_assets`, attaches it to `pp_document`, and is covered by `pp_assets`, attaches it to `pp_document`, and is covered by
`pano_cli_simulate_image_import_smoke`. `pano_cli_simulate_image_import_smoke`.

View File

@@ -40,7 +40,7 @@ agent or engineer to remove them without reconstructing context from chat.
| 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-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`, 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-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-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-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`, and live execution is centralized in `src/legacy_brush_ui_services.*`, but the bridge still mutates legacy `Brush`/`Canvas::I`, loads/saves legacy brush texture images, refreshes legacy quick/stroke/color widgets, and uses a temporary `NodePanelBrush` friend adapter to reach private list state | 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 or `NodePanelBrush` friend access |
| 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-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-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 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-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 |

View File

@@ -520,21 +520,22 @@ legacy `Canvas`/`Layer`/canvas-mode adapter continues.
changes, tip/pattern/dual texture changes, preset brush replacement, and stroke changes, tip/pattern/dual texture changes, preset brush replacement, and stroke
settings refreshes used by the live brush, quick, color, and floating panel settings refreshes used by the live brush, quick, color, and floating panel
callbacks. Brush UI execution now dispatches through `BrushUiServices` before callbacks. Brush UI execution now dispatches through `BrushUiServices` before
the legacy `Brush`/panel adapter mutates brush state or loads brush resources. the shared `src/legacy_brush_ui_services.*` bridge mutates legacy `Brush` and
panel state or loads brush resources.
`pano_cli plan-brush-texture-list` exposes app-core planning for brush/pattern `pano_cli plan-brush-texture-list` exposes app-core planning for brush/pattern
texture add, remove, and reorder actions, and `NodePanelBrush` now dispatches texture add, remove, and reorder actions, and `NodePanelBrush` now dispatches
those actions through `BrushTextureListServices` before the legacy image those actions through `BrushTextureListServices` in the shared brush bridge
load/save and UI-list adapter continues. before the legacy image load/save and UI-list adapter continues.
`pano_cli plan-brush-stroke-control` exposes app-core planning for the live `pano_cli plan-brush-stroke-control` exposes app-core planning for the live
stroke panel's slider, checkbox, blend-mode, tip-aspect reset, and default stroke panel's slider, checkbox, blend-mode, tip-aspect reset, and default
brush reset commands. `NodePanelStroke` now dispatches those controls through brush reset commands. `NodePanelStroke` now dispatches those controls through
`BrushStrokeControlServices` before the legacy `Canvas::I`/`Brush`/stroke-panel `BrushStrokeControlServices` in the shared brush bridge before the legacy
adapter continues. `Canvas::I`/`Brush`/stroke-panel adapter continues.
`pano_cli plan-canvas-tool` exposes app-core planning for draw/erase/line, `pano_cli plan-canvas-tool` exposes app-core planning for draw/erase/line,
camera, grid, copy, cut, fill, mask, flood-fill, pick, and touch-lock toolbar camera, grid, copy, cut, fill, mask, flood-fill, pick, and touch-lock toolbar
commands. Canvas tool execution now dispatches through `CanvasToolServices` commands. Canvas tool execution now dispatches through `CanvasToolServices`
through `src/legacy_canvas_tool_services.*` before legacy toolbar selection, in `src/legacy_canvas_tool_services.*` before legacy toolbar selection, `Canvas`
`Canvas` mode, pen picking, touch-lock, and transform state adapters continue. mode, pen picking, touch-lock, and transform state adapters continue.
`pano_cli plan-canvas-tool-state` exposes the matching toolbar active-state `pano_cli plan-canvas-tool-state` exposes the matching toolbar active-state
refresh used by `App::update` before legacy `Canvas` mode state remains the refresh used by `App::update` before legacy `Canvas` mode state remains the
source of truth. `NodeCanvas` stylus eraser mode switching consumes the same source of truth. `NodeCanvas` stylus eraser mode switching consumes the same

View File

@@ -18,6 +18,7 @@
#include "app_core/main_toolbar.h" #include "app_core/main_toolbar.h"
#include "app_core/tools_menu.h" #include "app_core/tools_menu.h"
#include "legacy_app_shell_services.h" #include "legacy_app_shell_services.h"
#include "legacy_brush_ui_services.h"
#include "legacy_canvas_tool_services.h" #include "legacy_canvas_tool_services.h"
#include "legacy_document_layer_services.h" #include "legacy_document_layer_services.h"
#include "legacy_history_services.h" #include "legacy_history_services.h"
@@ -31,115 +32,19 @@
namespace { namespace {
class LegacyBrushUiServices final : public pp::app::BrushUiServices {
public:
LegacyBrushUiServices(
App& app,
bool update_quick = false,
bool update_color_panel = false,
const std::shared_ptr<Brush>& preset_brush = nullptr) noexcept
: app_(app)
, update_quick_(update_quick)
, update_color_panel_(update_color_panel)
, preset_brush_(preset_brush)
{
}
void set_tip_color(float r, float g, float b, float a) override
{
if (!Canvas::I || !Canvas::I->m_current_brush)
return;
Canvas::I->m_current_brush->m_tip_color = glm::vec4(r, g, b, a);
if (update_quick_ && app_.quick)
app_.quick->set_color(Canvas::I->m_current_brush->m_tip_color);
if (update_color_panel_ && app_.color)
app_.color->set_color(Canvas::I->m_current_brush->m_tip_color);
}
void set_texture(
pp::app::BrushUiTextureSlot slot,
std::string_view path,
std::string_view thumbnail_path) override
{
if (!Canvas::I || !Canvas::I->m_current_brush)
return;
const std::string texture_path(path);
const std::string thumbnail(thumbnail_path);
switch (slot)
{
case pp::app::BrushUiTextureSlot::tip:
Canvas::I->m_current_brush->load_tip(texture_path, thumbnail);
break;
case pp::app::BrushUiTextureSlot::pattern:
Canvas::I->m_current_brush->load_pattern(texture_path, thumbnail);
break;
case pp::app::BrushUiTextureSlot::dual:
Canvas::I->m_current_brush->load_dual(texture_path, thumbnail);
break;
}
}
void replace_brush_from_preset(bool preserve_existing_color, bool load_resources) override
{
if (!Canvas::I || !Canvas::I->m_current_brush || !preset_brush_)
return;
const auto color = Canvas::I->m_current_brush->m_tip_color;
*Canvas::I->m_current_brush = *preset_brush_;
if (preserve_existing_color)
Canvas::I->m_current_brush->m_tip_color = color;
if (load_resources)
Canvas::I->m_current_brush->load();
}
void refresh_brush_ui(bool update_color_ui, bool update_brush_ui) override
{
app_.brush_update(update_color_ui, update_brush_ui);
}
private:
App& app_;
bool update_quick_ = false;
bool update_color_panel_ = false;
std::shared_ptr<Brush> preset_brush_;
};
bool apply_brush_color_plan(App& app, glm::vec4 color, bool update_quick, bool update_color_panel) bool apply_brush_color_plan(App& app, glm::vec4 color, bool update_quick, bool update_color_panel)
{ {
const auto plan = pp::app::plan_brush_ui_color(color.r, color.g, color.b, color.a); return pp::panopainter::apply_legacy_brush_color_plan(app, color, update_quick, update_color_panel);
if (!plan)
return false;
LegacyBrushUiServices services(app, update_quick, update_color_panel);
const auto status = pp::app::execute_brush_ui_plan(plan.value(), services);
if (!status.ok())
LOG("Brush color action failed: %s", status.message);
return status.ok();
} }
bool apply_brush_texture_plan(pp::app::BrushUiTextureSlot slot, const std::string& path, const std::string& thumb) bool apply_brush_texture_plan(App& app, pp::app::BrushUiTextureSlot slot, const std::string& path, const std::string& thumb)
{ {
const auto plan = pp::app::plan_brush_ui_texture(slot, path, thumb); return pp::panopainter::apply_legacy_brush_texture_plan(app, slot, path, thumb);
if (!plan)
return false;
LegacyBrushUiServices services(*App::I);
const auto status = pp::app::execute_brush_ui_plan(plan.value(), services);
if (!status.ok())
LOG("Brush texture action failed: %s", status.message);
return status.ok();
} }
bool apply_brush_preset_plan(App& app, const std::shared_ptr<Brush>& brush) bool apply_brush_preset_plan(App& app, const std::shared_ptr<Brush>& brush)
{ {
const auto plan = pp::app::plan_brush_ui_preset_replace(static_cast<bool>(brush)); return pp::panopainter::apply_legacy_brush_preset_plan(app, brush);
if (!plan)
return false;
LegacyBrushUiServices services(app, false, false, brush);
const auto status = pp::app::execute_brush_ui_plan(plan.value(), services);
if (!status.ok())
LOG("Brush preset action failed: %s", status.message);
return status.ok();
} }
bool apply_document_export_menu_plan(App& app, pp::app::DocumentExportMenuKind kind) bool apply_document_export_menu_plan(App& app, pp::app::DocumentExportMenuKind kind)
@@ -410,18 +315,16 @@ void App::init_sidebar()
}; };
stroke->on_brush_changed = [this](Node* target, const std::string& path, const std::string& thumb) { stroke->on_brush_changed = [this](Node* target, const std::string& path, const std::string& thumb) {
apply_brush_texture_plan(pp::app::BrushUiTextureSlot::tip, path, thumb); apply_brush_texture_plan(*this, pp::app::BrushUiTextureSlot::tip, path, thumb);
}; };
stroke->on_pattern_changed = [this](Node*target, const std::string& path, const std::string& thumb) { stroke->on_pattern_changed = [this](Node*target, const std::string& path, const std::string& thumb) {
apply_brush_texture_plan(pp::app::BrushUiTextureSlot::pattern, path, thumb); apply_brush_texture_plan(*this, pp::app::BrushUiTextureSlot::pattern, path, thumb);
}; };
stroke->on_dual_changed = [this](Node*target, const std::string& path, const std::string& thumb) { stroke->on_dual_changed = [this](Node*target, const std::string& path, const std::string& thumb) {
apply_brush_texture_plan(pp::app::BrushUiTextureSlot::dual, path, thumb); apply_brush_texture_plan(*this, pp::app::BrushUiTextureSlot::dual, path, thumb);
}; };
stroke->on_stroke_change = [this](Node*) { stroke->on_stroke_change = [this](Node*) {
const auto plan = pp::app::plan_brush_ui_stroke_settings_changed(); const auto status = pp::panopainter::execute_legacy_brush_stroke_changed_plan(*this);
LegacyBrushUiServices services(*this);
const auto status = pp::app::execute_brush_ui_plan(plan, services);
if (!status.ok()) if (!status.ok())
LOG("Brush stroke settings action failed: %s", status.message); LOG("Brush stroke settings action failed: %s", status.message);
}; };

View File

@@ -0,0 +1,388 @@
#include "pch.h"
#include "legacy_brush_ui_services.h"
#include "app.h"
#include "asset.h"
#include "canvas.h"
#include "image.h"
#include "log.h"
#include "node_panel_brush.h"
#include "node_panel_stroke.h"
namespace pp::panopainter {
namespace {
class LegacyBrushUiServices final : public pp::app::BrushUiServices {
public:
LegacyBrushUiServices(
App& app,
bool update_quick = false,
bool update_color_panel = false,
const std::shared_ptr<Brush>& preset_brush = nullptr) noexcept
: app_(app)
, update_quick_(update_quick)
, update_color_panel_(update_color_panel)
, preset_brush_(preset_brush)
{
}
void set_tip_color(float r, float g, float b, float a) override
{
if (!Canvas::I || !Canvas::I->m_current_brush)
return;
Canvas::I->m_current_brush->m_tip_color = glm::vec4(r, g, b, a);
if (update_quick_ && app_.quick)
app_.quick->set_color(Canvas::I->m_current_brush->m_tip_color);
if (update_color_panel_ && app_.color)
app_.color->set_color(Canvas::I->m_current_brush->m_tip_color);
}
void set_texture(
pp::app::BrushUiTextureSlot slot,
std::string_view path,
std::string_view thumbnail_path) override
{
if (!Canvas::I || !Canvas::I->m_current_brush)
return;
const std::string texture_path(path);
const std::string thumbnail(thumbnail_path);
switch (slot)
{
case pp::app::BrushUiTextureSlot::tip:
Canvas::I->m_current_brush->load_tip(texture_path, thumbnail);
break;
case pp::app::BrushUiTextureSlot::pattern:
Canvas::I->m_current_brush->load_pattern(texture_path, thumbnail);
break;
case pp::app::BrushUiTextureSlot::dual:
Canvas::I->m_current_brush->load_dual(texture_path, thumbnail);
break;
}
}
void replace_brush_from_preset(bool preserve_existing_color, bool load_resources) override
{
if (!Canvas::I || !Canvas::I->m_current_brush || !preset_brush_)
return;
const auto color = Canvas::I->m_current_brush->m_tip_color;
*Canvas::I->m_current_brush = *preset_brush_;
if (preserve_existing_color)
Canvas::I->m_current_brush->m_tip_color = color;
if (load_resources)
Canvas::I->m_current_brush->load();
}
void refresh_brush_ui(bool update_color_ui, bool update_brush_ui) override
{
app_.brush_update(update_color_ui, update_brush_ui);
}
private:
App& app_;
bool update_quick_ = false;
bool update_color_panel_ = false;
std::shared_ptr<Brush> preset_brush_;
};
class LegacyBrushStrokeControlServices final : public pp::app::BrushStrokeControlServices {
public:
explicit LegacyBrushStrokeControlServices(NodePanelStroke& panel) : panel_(panel) {}
void set_float_setting(pp::app::BrushStrokeFloatSetting setting, float value) override
{
auto& brush = *Canvas::I->m_current_brush;
switch (setting) {
case pp::app::BrushStrokeFloatSetting::tip_size: brush.m_tip_size = value; break;
case pp::app::BrushStrokeFloatSetting::tip_spacing: brush.m_tip_spacing = value; break;
case pp::app::BrushStrokeFloatSetting::tip_flow: brush.m_tip_flow = value; break;
case pp::app::BrushStrokeFloatSetting::tip_opacity: brush.m_tip_opacity = value; break;
case pp::app::BrushStrokeFloatSetting::tip_angle: brush.m_tip_angle = value; break;
case pp::app::BrushStrokeFloatSetting::tip_angle_smooth: brush.m_tip_angle_smooth = value; break;
case pp::app::BrushStrokeFloatSetting::tip_mix: brush.m_tip_mix = value; break;
case pp::app::BrushStrokeFloatSetting::tip_wet: brush.m_tip_wet = value; break;
case pp::app::BrushStrokeFloatSetting::tip_noise: brush.m_tip_noise = value; break;
case pp::app::BrushStrokeFloatSetting::tip_hue: brush.m_tip_hue = value; break;
case pp::app::BrushStrokeFloatSetting::tip_saturation: brush.m_tip_sat = value; break;
case pp::app::BrushStrokeFloatSetting::tip_value: brush.m_tip_val = value; break;
case pp::app::BrushStrokeFloatSetting::jitter_scale: brush.m_jitter_scale = value; break;
case pp::app::BrushStrokeFloatSetting::jitter_angle: brush.m_jitter_angle = value; break;
case pp::app::BrushStrokeFloatSetting::jitter_scatter: brush.m_jitter_scatter = value; break;
case pp::app::BrushStrokeFloatSetting::jitter_flow: brush.m_jitter_flow = value; break;
case pp::app::BrushStrokeFloatSetting::jitter_opacity: brush.m_jitter_opacity = value; break;
case pp::app::BrushStrokeFloatSetting::jitter_hue: brush.m_jitter_hue = value; break;
case pp::app::BrushStrokeFloatSetting::jitter_saturation: brush.m_jitter_sat = value; break;
case pp::app::BrushStrokeFloatSetting::jitter_value: brush.m_jitter_val = value; break;
case pp::app::BrushStrokeFloatSetting::jitter_aspect: brush.m_jitter_aspect = value; break;
case pp::app::BrushStrokeFloatSetting::dual_size: brush.m_dual_size = value; break;
case pp::app::BrushStrokeFloatSetting::dual_spacing: brush.m_dual_spacing = value; break;
case pp::app::BrushStrokeFloatSetting::dual_scatter: brush.m_dual_scatter = value; break;
case pp::app::BrushStrokeFloatSetting::tip_aspect: brush.m_tip_aspect = value; break;
case pp::app::BrushStrokeFloatSetting::dual_opacity: brush.m_dual_opacity = value; break;
case pp::app::BrushStrokeFloatSetting::dual_flow: brush.m_dual_flow = value; break;
case pp::app::BrushStrokeFloatSetting::dual_rotate: brush.m_dual_rotate = value; break;
case pp::app::BrushStrokeFloatSetting::pattern_scale: brush.m_pattern_scale = value; break;
case pp::app::BrushStrokeFloatSetting::pattern_brightness: brush.m_pattern_brightness = value; break;
case pp::app::BrushStrokeFloatSetting::pattern_contrast: brush.m_pattern_contrast = value; break;
case pp::app::BrushStrokeFloatSetting::pattern_depth: brush.m_pattern_depth = value; break;
}
}
void set_bool_setting(pp::app::BrushStrokeBoolSetting setting, bool value) override
{
auto& brush = *Canvas::I->m_current_brush;
switch (setting) {
case pp::app::BrushStrokeBoolSetting::tip_angle_init: brush.m_tip_angle_init = value; break;
case pp::app::BrushStrokeBoolSetting::tip_angle_follow: brush.m_tip_angle_follow = value; break;
case pp::app::BrushStrokeBoolSetting::tip_flow_pressure: brush.m_tip_flow_pressure = value; break;
case pp::app::BrushStrokeBoolSetting::tip_opacity_pressure: brush.m_tip_opacity_pressure = value; break;
case pp::app::BrushStrokeBoolSetting::tip_size_pressure: brush.m_tip_size_pressure = value; break;
case pp::app::BrushStrokeBoolSetting::jitter_scatter_both_axis: brush.m_jitter_scatter_bothaxis = value; break;
case pp::app::BrushStrokeBoolSetting::jitter_aspect_both_axis: brush.m_jitter_aspect_bothaxis = value; break;
case pp::app::BrushStrokeBoolSetting::jitter_hsv_each_sample: brush.m_jitter_hsv_eachsample = value; break;
case pp::app::BrushStrokeBoolSetting::tip_invert: brush.m_tip_invert = value; break;
case pp::app::BrushStrokeBoolSetting::tip_flip_x: brush.m_tip_flipx = value; break;
case pp::app::BrushStrokeBoolSetting::tip_flip_y: brush.m_tip_flipy = value; break;
case pp::app::BrushStrokeBoolSetting::pattern_enabled: brush.m_pattern_enabled = value; break;
case pp::app::BrushStrokeBoolSetting::dual_enabled: brush.m_dual_enabled = value; break;
case pp::app::BrushStrokeBoolSetting::dual_scatter_both_axis: brush.m_dual_scatter_bothaxis = value; break;
case pp::app::BrushStrokeBoolSetting::dual_invert: brush.m_dual_invert = value; break;
case pp::app::BrushStrokeBoolSetting::dual_flip_x: brush.m_dual_flipx = value; break;
case pp::app::BrushStrokeBoolSetting::dual_flip_y: brush.m_dual_flipy = value; break;
case pp::app::BrushStrokeBoolSetting::dual_random_flip: brush.m_dual_randflip = value; break;
case pp::app::BrushStrokeBoolSetting::tip_random_flip_x: brush.m_tip_randflipx = value; break;
case pp::app::BrushStrokeBoolSetting::tip_random_flip_y: brush.m_tip_randflipy = value; break;
case pp::app::BrushStrokeBoolSetting::pattern_each_sample: brush.m_pattern_eachsample = value; break;
case pp::app::BrushStrokeBoolSetting::pattern_invert: brush.m_pattern_invert = value; break;
case pp::app::BrushStrokeBoolSetting::pattern_flip_x: brush.m_pattern_flipx = value; break;
case pp::app::BrushStrokeBoolSetting::pattern_flip_y: brush.m_pattern_flipy = value; break;
case pp::app::BrushStrokeBoolSetting::pattern_random_offset: brush.m_pattern_rand_offset = value; break;
}
}
void set_blend_mode(pp::app::BrushStrokeBlendSetting setting, int blend_mode) override
{
auto& brush = *Canvas::I->m_current_brush;
switch (setting) {
case pp::app::BrushStrokeBlendSetting::tip: brush.m_blend_mode = blend_mode; break;
case pp::app::BrushStrokeBlendSetting::dual: brush.m_dual_blend_mode = blend_mode; break;
case pp::app::BrushStrokeBlendSetting::pattern: brush.m_pattern_blend_mode = blend_mode; break;
}
}
void reset_tip_aspect(float value) override
{
panel_.m_tip_aspect->set_value(value);
Canvas::I->m_current_brush->m_tip_aspect = value;
}
void reset_default_brush() override
{
auto brush = std::make_shared<Brush>();
brush->load_tip(
panel_.m_brush_popup->get_texture_path(panel_.m_default_brush_index),
panel_.m_brush_popup->get_thumb_path(panel_.m_default_brush_index));
brush->m_tip_size = 30;
brush->m_tip_flow = .9f;
brush->m_tip_spacing = .1f;
brush->m_tip_opacity = 1.f;
Canvas::I->m_current_brush = brush;
}
void update_stroke_controls() override
{
panel_.update_controls();
}
void refresh_stroke_preview() override
{
if (panel_.m_preview) {
panel_.m_preview->m_brush = Canvas::I->m_current_brush;
}
}
void notify_stroke_changed() override
{
if (panel_.on_stroke_change) {
panel_.on_stroke_change(&panel_);
}
}
private:
NodePanelStroke& panel_;
};
} // namespace
class LegacyBrushTextureListServices final : public pp::app::BrushTextureListServices {
public:
explicit LegacyBrushTextureListServices(NodePanelBrush& panel) noexcept
: panel_(panel)
{
}
pp::foundation::Status add_texture_from_source(
std::string_view source_path,
std::string_view high_path,
std::string_view thumbnail_path,
std::string_view brush_name,
bool converts_brush_alpha) override
{
Image img;
if (!img.load_file(std::string(source_path))) {
return pp::foundation::Status::invalid_argument("brush texture source could not be loaded");
}
if (converts_brush_alpha) {
img.gayscale_alpha();
}
auto thumbnail_image = img.resize(64, 64).resize_squared(glm::u8vec4(255));
thumbnail_image.save_png(std::string(thumbnail_path));
img.save_png(std::string(high_path));
NodeButtonBrush* brush = new NodeButtonBrush;
panel_.m_container->add_child(brush);
brush->init();
brush->create();
brush->loaded();
const auto thumbnail_path_string = std::string(thumbnail_path);
brush->set_icon(thumbnail_path_string.c_str());
brush->thumb_path = std::string(thumbnail_path);
brush->high_path = std::string(high_path);
brush->brush_name = std::string(brush_name);
brush->m_user_brush = true;
brush->on_click = std::bind(&NodePanelBrush::handle_click, &panel_, std::placeholders::_1);
return pp::foundation::Status::success();
}
void remove_texture(int index, bool delete_texture_files) override
{
auto* brush = brush_at(index);
if (!brush) {
return;
}
if (delete_texture_files) {
Asset::delete_file(brush->thumb_path);
Asset::delete_file(brush->high_path);
}
if (panel_.m_current == brush) {
panel_.m_current = nullptr;
}
panel_.m_container->remove_child(brush);
}
void move_texture(int from_index, int to_index) override
{
if (auto* brush = brush_at(from_index)) {
panel_.m_container->move_child(brush, to_index);
}
}
void select_texture(int index) override
{
if (panel_.m_current) {
panel_.m_current->m_selected = false;
}
panel_.m_current = brush_at(index);
if (!panel_.m_current) {
return;
}
panel_.m_current->m_selected = true;
if (panel_.on_brush_changed) {
panel_.on_brush_changed(&panel_, index);
}
}
void save_texture_list() override
{
panel_.save();
}
private:
NodeButtonBrush* brush_at(int index) const
{
if (index < 0 || index >= static_cast<int>(panel_.m_container->m_children.size())) {
return nullptr;
}
return static_cast<NodeButtonBrush*>(panel_.m_container->m_children[index].get());
}
NodePanelBrush& panel_;
};
bool apply_legacy_brush_color_plan(
App& app,
glm::vec4 color,
bool update_quick,
bool update_color_panel)
{
const auto plan = pp::app::plan_brush_ui_color(color.r, color.g, color.b, color.a);
if (!plan)
return false;
LegacyBrushUiServices services(app, update_quick, update_color_panel);
const auto status = pp::app::execute_brush_ui_plan(plan.value(), services);
if (!status.ok())
LOG("Brush color action failed: %s", status.message);
return status.ok();
}
bool apply_legacy_brush_texture_plan(
App& app,
pp::app::BrushUiTextureSlot slot,
const std::string& path,
const std::string& thumb)
{
const auto plan = pp::app::plan_brush_ui_texture(slot, path, thumb);
if (!plan)
return false;
LegacyBrushUiServices services(app);
const auto status = pp::app::execute_brush_ui_plan(plan.value(), services);
if (!status.ok())
LOG("Brush texture action failed: %s", status.message);
return status.ok();
}
bool apply_legacy_brush_preset_plan(App& app, const std::shared_ptr<Brush>& brush)
{
const auto plan = pp::app::plan_brush_ui_preset_replace(static_cast<bool>(brush));
if (!plan)
return false;
LegacyBrushUiServices services(app, false, false, brush);
const auto status = pp::app::execute_brush_ui_plan(plan.value(), services);
if (!status.ok())
LOG("Brush preset action failed: %s", status.message);
return status.ok();
}
pp::foundation::Status execute_legacy_brush_stroke_changed_plan(App& app)
{
const auto plan = pp::app::plan_brush_ui_stroke_settings_changed();
LegacyBrushUiServices services(app);
return pp::app::execute_brush_ui_plan(plan, services);
}
pp::foundation::Status execute_legacy_brush_texture_list_plan(
NodePanelBrush& panel,
const pp::app::BrushTextureListPlan& plan)
{
LegacyBrushTextureListServices services(panel);
return pp::app::execute_brush_texture_list_plan(plan, services);
}
pp::foundation::Status execute_legacy_brush_stroke_control_plan(
NodePanelStroke& panel,
const pp::app::BrushStrokeControlPlan& plan)
{
LegacyBrushStrokeControlServices services(panel);
return pp::app::execute_brush_stroke_control_plan(plan, services);
}
} // namespace pp::panopainter

View File

@@ -0,0 +1,38 @@
#pragma once
#include "app_core/brush_ui.h"
#include "foundation/result.h"
#include "glm/glm.hpp"
#include <memory>
#include <string>
class App;
class Brush;
class NodePanelBrush;
class NodePanelStroke;
namespace pp::panopainter {
[[nodiscard]] bool apply_legacy_brush_color_plan(
App& app,
glm::vec4 color,
bool update_quick,
bool update_color_panel);
[[nodiscard]] bool apply_legacy_brush_texture_plan(
App& app,
pp::app::BrushUiTextureSlot slot,
const std::string& path,
const std::string& thumb);
[[nodiscard]] bool apply_legacy_brush_preset_plan(
App& app,
const std::shared_ptr<Brush>& brush);
[[nodiscard]] pp::foundation::Status execute_legacy_brush_stroke_changed_plan(App& app);
[[nodiscard]] pp::foundation::Status execute_legacy_brush_texture_list_plan(
NodePanelBrush& panel,
const pp::app::BrushTextureListPlan& plan);
[[nodiscard]] pp::foundation::Status execute_legacy_brush_stroke_control_plan(
NodePanelStroke& panel,
const pp::app::BrushStrokeControlPlan& plan);
} // namespace pp::panopainter

View File

@@ -2,6 +2,7 @@
#include "log.h" #include "log.h"
#include "node_panel_brush.h" #include "node_panel_brush.h"
#include "app_core/brush_ui.h" #include "app_core/brush_ui.h"
#include "legacy_brush_ui_services.h"
#include "asset.h" #include "asset.h"
#include "texture.h" #include "texture.h"
@@ -78,109 +79,7 @@ Node* NodePanelBrush::clone_instantiate() const
void NodePanelBrush::execute_texture_list_plan(const pp::app::BrushTextureListPlan& plan) void NodePanelBrush::execute_texture_list_plan(const pp::app::BrushTextureListPlan& plan)
{ {
class LegacyBrushTextureListServices final : public pp::app::BrushTextureListServices { const auto status = pp::panopainter::execute_legacy_brush_texture_list_plan(*this, plan);
public:
explicit LegacyBrushTextureListServices(NodePanelBrush& panel) noexcept
: panel_(panel)
{
}
pp::foundation::Status add_texture_from_source(
std::string_view source_path,
std::string_view high_path,
std::string_view thumbnail_path,
std::string_view brush_name,
bool converts_brush_alpha) override
{
Image img;
if (!img.load_file(std::string(source_path))) {
return pp::foundation::Status::invalid_argument("brush texture source could not be loaded");
}
if (converts_brush_alpha) {
img.gayscale_alpha();
}
auto thumbnail_image = img.resize(64, 64).resize_squared(glm::u8vec4(255));
thumbnail_image.save_png(std::string(thumbnail_path));
img.save_png(std::string(high_path));
NodeButtonBrush* brush = new NodeButtonBrush;
panel_.m_container->add_child(brush);
brush->init();
brush->create();
brush->loaded();
const auto thumbnail_path_string = std::string(thumbnail_path);
brush->set_icon(thumbnail_path_string.c_str());
brush->thumb_path = std::string(thumbnail_path);
brush->high_path = std::string(high_path);
brush->brush_name = std::string(brush_name);
brush->m_user_brush = true;
brush->on_click = std::bind(&NodePanelBrush::handle_click, &panel_, std::placeholders::_1);
return pp::foundation::Status::success();
}
void remove_texture(int index, bool delete_texture_files) override
{
auto* brush = brush_at(index);
if (!brush) {
return;
}
if (delete_texture_files) {
Asset::delete_file(brush->thumb_path);
Asset::delete_file(brush->high_path);
}
if (panel_.m_current == brush) {
panel_.m_current = nullptr;
}
panel_.m_container->remove_child(brush);
}
void move_texture(int from_index, int to_index) override
{
if (auto* brush = brush_at(from_index)) {
panel_.m_container->move_child(brush, to_index);
}
}
void select_texture(int index) override
{
if (panel_.m_current) {
panel_.m_current->m_selected = false;
}
panel_.m_current = brush_at(index);
if (!panel_.m_current) {
return;
}
panel_.m_current->m_selected = true;
if (panel_.on_brush_changed) {
panel_.on_brush_changed(&panel_, index);
}
}
void save_texture_list() override
{
panel_.save();
}
private:
NodeButtonBrush* brush_at(int index) const
{
if (index < 0 || index >= static_cast<int>(panel_.m_container->m_children.size())) {
return nullptr;
}
return static_cast<NodeButtonBrush*>(panel_.m_container->m_children[index].get());
}
NodePanelBrush& panel_;
};
LegacyBrushTextureListServices services(*this);
const auto status = pp::app::execute_brush_texture_list_plan(plan, services);
if (!status.ok()) { if (!status.ok()) {
LOG("Brush texture list action failed: %s", status.message); LOG("Brush texture list action failed: %s", status.message);
} }

View File

@@ -12,6 +12,9 @@
namespace pp::app { namespace pp::app {
struct BrushTextureListPlan; struct BrushTextureListPlan;
} }
namespace pp::panopainter {
class LegacyBrushTextureListServices;
}
class NodeButtonBrush : public NodeButtonCustom, public Serializer::Type class NodeButtonBrush : public NodeButtonCustom, public Serializer::Type
{ {
@@ -34,6 +37,8 @@ public:
class NodePanelBrush : public Node class NodePanelBrush : public Node
{ {
friend class pp::panopainter::LegacyBrushTextureListServices;
// brushes that are marked as deleted but file still exists // brushes that are marked as deleted but file still exists
std::vector<std::string> m_deleted; std::vector<std::string> m_deleted;
NodeButtonBrush* m_current = nullptr; NodeButtonBrush* m_current = nullptr;

View File

@@ -1,5 +1,6 @@
#include "pch.h" #include "pch.h"
#include "app_core/brush_ui.h" #include "app_core/brush_ui.h"
#include "legacy_brush_ui_services.h"
#include "log.h" #include "log.h"
#include "node_panel_stroke.h" #include "node_panel_stroke.h"
#include "canvas.h" #include "canvas.h"
@@ -7,137 +8,6 @@
#include "app.h" #include "app.h"
#include "abr.h" #include "abr.h"
namespace {
class LegacyBrushStrokeControlServices final : public pp::app::BrushStrokeControlServices {
public:
explicit LegacyBrushStrokeControlServices(NodePanelStroke& panel) : panel_(panel) {}
void set_float_setting(pp::app::BrushStrokeFloatSetting setting, float value) override
{
auto& brush = *Canvas::I->m_current_brush;
switch (setting) {
case pp::app::BrushStrokeFloatSetting::tip_size: brush.m_tip_size = value; break;
case pp::app::BrushStrokeFloatSetting::tip_spacing: brush.m_tip_spacing = value; break;
case pp::app::BrushStrokeFloatSetting::tip_flow: brush.m_tip_flow = value; break;
case pp::app::BrushStrokeFloatSetting::tip_opacity: brush.m_tip_opacity = value; break;
case pp::app::BrushStrokeFloatSetting::tip_angle: brush.m_tip_angle = value; break;
case pp::app::BrushStrokeFloatSetting::tip_angle_smooth: brush.m_tip_angle_smooth = value; break;
case pp::app::BrushStrokeFloatSetting::tip_mix: brush.m_tip_mix = value; break;
case pp::app::BrushStrokeFloatSetting::tip_wet: brush.m_tip_wet = value; break;
case pp::app::BrushStrokeFloatSetting::tip_noise: brush.m_tip_noise = value; break;
case pp::app::BrushStrokeFloatSetting::tip_hue: brush.m_tip_hue = value; break;
case pp::app::BrushStrokeFloatSetting::tip_saturation: brush.m_tip_sat = value; break;
case pp::app::BrushStrokeFloatSetting::tip_value: brush.m_tip_val = value; break;
case pp::app::BrushStrokeFloatSetting::jitter_scale: brush.m_jitter_scale = value; break;
case pp::app::BrushStrokeFloatSetting::jitter_angle: brush.m_jitter_angle = value; break;
case pp::app::BrushStrokeFloatSetting::jitter_scatter: brush.m_jitter_scatter = value; break;
case pp::app::BrushStrokeFloatSetting::jitter_flow: brush.m_jitter_flow = value; break;
case pp::app::BrushStrokeFloatSetting::jitter_opacity: brush.m_jitter_opacity = value; break;
case pp::app::BrushStrokeFloatSetting::jitter_hue: brush.m_jitter_hue = value; break;
case pp::app::BrushStrokeFloatSetting::jitter_saturation: brush.m_jitter_sat = value; break;
case pp::app::BrushStrokeFloatSetting::jitter_value: brush.m_jitter_val = value; break;
case pp::app::BrushStrokeFloatSetting::jitter_aspect: brush.m_jitter_aspect = value; break;
case pp::app::BrushStrokeFloatSetting::dual_size: brush.m_dual_size = value; break;
case pp::app::BrushStrokeFloatSetting::dual_spacing: brush.m_dual_spacing = value; break;
case pp::app::BrushStrokeFloatSetting::dual_scatter: brush.m_dual_scatter = value; break;
case pp::app::BrushStrokeFloatSetting::tip_aspect: brush.m_tip_aspect = value; break;
case pp::app::BrushStrokeFloatSetting::dual_opacity: brush.m_dual_opacity = value; break;
case pp::app::BrushStrokeFloatSetting::dual_flow: brush.m_dual_flow = value; break;
case pp::app::BrushStrokeFloatSetting::dual_rotate: brush.m_dual_rotate = value; break;
case pp::app::BrushStrokeFloatSetting::pattern_scale: brush.m_pattern_scale = value; break;
case pp::app::BrushStrokeFloatSetting::pattern_brightness: brush.m_pattern_brightness = value; break;
case pp::app::BrushStrokeFloatSetting::pattern_contrast: brush.m_pattern_contrast = value; break;
case pp::app::BrushStrokeFloatSetting::pattern_depth: brush.m_pattern_depth = value; break;
}
}
void set_bool_setting(pp::app::BrushStrokeBoolSetting setting, bool value) override
{
auto& brush = *Canvas::I->m_current_brush;
switch (setting) {
case pp::app::BrushStrokeBoolSetting::tip_angle_init: brush.m_tip_angle_init = value; break;
case pp::app::BrushStrokeBoolSetting::tip_angle_follow: brush.m_tip_angle_follow = value; break;
case pp::app::BrushStrokeBoolSetting::tip_flow_pressure: brush.m_tip_flow_pressure = value; break;
case pp::app::BrushStrokeBoolSetting::tip_opacity_pressure: brush.m_tip_opacity_pressure = value; break;
case pp::app::BrushStrokeBoolSetting::tip_size_pressure: brush.m_tip_size_pressure = value; break;
case pp::app::BrushStrokeBoolSetting::jitter_scatter_both_axis: brush.m_jitter_scatter_bothaxis = value; break;
case pp::app::BrushStrokeBoolSetting::jitter_aspect_both_axis: brush.m_jitter_aspect_bothaxis = value; break;
case pp::app::BrushStrokeBoolSetting::jitter_hsv_each_sample: brush.m_jitter_hsv_eachsample = value; break;
case pp::app::BrushStrokeBoolSetting::tip_invert: brush.m_tip_invert = value; break;
case pp::app::BrushStrokeBoolSetting::tip_flip_x: brush.m_tip_flipx = value; break;
case pp::app::BrushStrokeBoolSetting::tip_flip_y: brush.m_tip_flipy = value; break;
case pp::app::BrushStrokeBoolSetting::pattern_enabled: brush.m_pattern_enabled = value; break;
case pp::app::BrushStrokeBoolSetting::dual_enabled: brush.m_dual_enabled = value; break;
case pp::app::BrushStrokeBoolSetting::dual_scatter_both_axis: brush.m_dual_scatter_bothaxis = value; break;
case pp::app::BrushStrokeBoolSetting::dual_invert: brush.m_dual_invert = value; break;
case pp::app::BrushStrokeBoolSetting::dual_flip_x: brush.m_dual_flipx = value; break;
case pp::app::BrushStrokeBoolSetting::dual_flip_y: brush.m_dual_flipy = value; break;
case pp::app::BrushStrokeBoolSetting::dual_random_flip: brush.m_dual_randflip = value; break;
case pp::app::BrushStrokeBoolSetting::tip_random_flip_x: brush.m_tip_randflipx = value; break;
case pp::app::BrushStrokeBoolSetting::tip_random_flip_y: brush.m_tip_randflipy = value; break;
case pp::app::BrushStrokeBoolSetting::pattern_each_sample: brush.m_pattern_eachsample = value; break;
case pp::app::BrushStrokeBoolSetting::pattern_invert: brush.m_pattern_invert = value; break;
case pp::app::BrushStrokeBoolSetting::pattern_flip_x: brush.m_pattern_flipx = value; break;
case pp::app::BrushStrokeBoolSetting::pattern_flip_y: brush.m_pattern_flipy = value; break;
case pp::app::BrushStrokeBoolSetting::pattern_random_offset: brush.m_pattern_rand_offset = value; break;
}
}
void set_blend_mode(pp::app::BrushStrokeBlendSetting setting, int blend_mode) override
{
auto& brush = *Canvas::I->m_current_brush;
switch (setting) {
case pp::app::BrushStrokeBlendSetting::tip: brush.m_blend_mode = blend_mode; break;
case pp::app::BrushStrokeBlendSetting::dual: brush.m_dual_blend_mode = blend_mode; break;
case pp::app::BrushStrokeBlendSetting::pattern: brush.m_pattern_blend_mode = blend_mode; break;
}
}
void reset_tip_aspect(float value) override
{
panel_.m_tip_aspect->set_value(value);
Canvas::I->m_current_brush->m_tip_aspect = value;
}
void reset_default_brush() override
{
auto brush = std::make_shared<Brush>();
brush->load_tip(
panel_.m_brush_popup->get_texture_path(panel_.m_default_brush_index),
panel_.m_brush_popup->get_thumb_path(panel_.m_default_brush_index));
brush->m_tip_size = 30;
brush->m_tip_flow = .9f;
brush->m_tip_spacing = .1f;
brush->m_tip_opacity = 1.f;
Canvas::I->m_current_brush = brush;
}
void update_stroke_controls() override
{
panel_.update_controls();
}
void refresh_stroke_preview() override
{
if (panel_.m_preview) {
panel_.m_preview->m_brush = Canvas::I->m_current_brush;
}
}
void notify_stroke_changed() override
{
if (panel_.on_stroke_change) {
panel_.on_stroke_change(&panel_);
}
}
private:
NodePanelStroke& panel_;
};
} // namespace
Node* NodePanelStroke::clone_instantiate() const Node* NodePanelStroke::clone_instantiate() const
{ {
return new NodePanelStroke(); return new NodePanelStroke();
@@ -704,8 +574,7 @@ void NodePanelStroke::handle_checkbox(
void NodePanelStroke::execute_stroke_control_plan(const pp::app::BrushStrokeControlPlan& plan) void NodePanelStroke::execute_stroke_control_plan(const pp::app::BrushStrokeControlPlan& plan)
{ {
LegacyBrushStrokeControlServices services(*this); const auto status = pp::panopainter::execute_legacy_brush_stroke_control_plan(*this, plan);
const auto status = pp::app::execute_brush_stroke_control_plan(plan, services);
if (!status.ok()) { if (!status.ok()) {
LOG("Brush stroke control action failed: %s", status.message); LOG("Brush stroke control action failed: %s", status.message);
} }