Route brush refresh view through app core
This commit is contained in:
@@ -269,8 +269,10 @@ Known local toolchain state:
|
||||
`NodePanelStroke::update_controls()` now consumes the tested `pp_app_core`
|
||||
stroke-panel view model for brush float settings, toggles, blend modes, and
|
||||
thumbnail paths, and `pano_cli plan-brush-stroke-panel-view` exposes that
|
||||
projection for automation. These paths stay on the `pp_app_core` contracts
|
||||
while
|
||||
projection for automation. `App::brush_update()` now consumes the tested
|
||||
`pp_app_core` brush refresh view before updating retained stroke, quick, and
|
||||
floating color widgets, and `pano_cli plan-brush-refresh` exposes that
|
||||
fan-out for automation. These paths stay on the `pp_app_core` contracts while
|
||||
legacy `Brush`, `Canvas::I`, image load/save, `NodePanelBrush`,
|
||||
`NodePanelStroke`, quick/color refreshes, direct preset child-node mutation,
|
||||
brush thumbnail/popup ownership, and the temporary `NodePanelBrush` friend
|
||||
|
||||
@@ -57,6 +57,11 @@ agent or engineer to remove them without reconstructing context from chat.
|
||||
`pano_cli plan-brush-stroke-panel-view` exposes the same state path for
|
||||
automation. Legacy brush mutation, brush thumbnail ownership, popup behavior,
|
||||
and preset child-node mutation remain open under DEBT-0023.
|
||||
- 2026-06-05: DEBT-0023 was narrowed again. App-level brush refresh projection
|
||||
now goes through tested `pp_app_core` planning, `App::brush_update()` consumes
|
||||
that view model in the live app, and `pano_cli plan-brush-refresh` exposes the
|
||||
same fan-out path for automation. Retained legacy quick/stroke/color widget
|
||||
writes plus `Brush`/`Canvas::I` ownership remain open under DEBT-0023.
|
||||
- 2026-06-04: DEBT-0036 was narrowed again. Canvas stroke commit,
|
||||
thumbnail, and object-draw history paths now query saved blend state through
|
||||
tested `pp_renderer_gl` capability-state dispatch; CanvasLayer equirect
|
||||
@@ -93,7 +98,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-0021 | Open | Modernization | Layer rename planning/execution dispatch, layer panel operation planning/execution dispatch, and layer panel selected-control/visibility view projection now consume pure `pp_app_core` through `App::dialog_layer_rename`, `App::init_sidebar` layer callbacks, `NodePanelLayer::update_attributes()`, `pano_cli plan-layer-rename`, `pano_cli plan-layer-operation`, `pano_cli plan-layer-panel-view`, `DocumentLayerRenameServices`, and `DocumentLayerOperationServices`, and the live execution adapters are centralized in `src/legacy_document_layer_services.*`, but that shared bridge and panel adapter still mutate legacy `Canvas` layer state, `NodeLayer`/`NodePanelLayer`, and `ActionManager` undo entries | Preserve existing UI/canvas behavior while document layer commands, panel projection, 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`; `pano_cli plan-layer-panel-view --layer-count 3 --current-index 1 --hidden-index 2 --locked-index 1 --current-opacity 0.25 --current-blend-mode 4`; `ctest --preset desktop-fast --build-config Debug` | Layer command execution and panel state projection are 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 view-model projection, timeline scrub 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`, `NodeAnimationTimeline`, `pano_cli plan-animation-operation`, `pano_cli plan-animation-panel-action`, `pano_cli plan-animation-panel-view`, `pano_cli plan-animation-timeline-scrub`, and `DocumentAnimationServices`; live execution is centralized in `src/legacy_document_animation_services.*`, but that bridge still mutates or reads legacy `Canvas`/`Layer` frame state, canvas mode, animation-panel timeline/playback fields, and uses a temporary `NodePanelAnimation` friend adapter | 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`; `pano_cli plan-animation-panel-view --layer-count 2 --frame-count 3 --total-duration 6 --current-layer 1 --current-frame 4`; `pano_cli plan-animation-timeline-scrub --total-duration 5 --cursor-x 174.99`; `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, brush preset-list add/select/move/remove/clear planning, stroke-panel slider/toggle/blend/reset planning, stroke-panel view projection, and execution dispatch now consume pure `pp_app_core` through `App::init_sidebar`, `NodePanelBrush`, `NodePanelBrushPreset`, `NodePanelStroke`, `NodePanelStroke::update_controls()`, restored/docked floating-panel callbacks, `pano_cli plan-brush-operation`, `pano_cli plan-brush-texture-list`, `pano_cli plan-brush-preset-list`, `pano_cli plan-brush-stroke-control`, `pano_cli plan-brush-stroke-panel-view`, `BrushUiServices`, `BrushTextureListServices`, `BrushPresetListServices`, and `BrushStrokeControlServices`, and live execution is centralized in `src/legacy_brush_ui_services.*` or narrow legacy service bridges where possible, but preset-list execution still mutates legacy `NodePanelBrushPreset` child nodes directly while the bridge and panel adapter still mutate/read legacy `Brush`/`Canvas::I`, load/save legacy brush texture images, refresh legacy quick/stroke/color widgets, own brush thumbnail paths and popup behavior, and use temporary `NodePanelBrush`/`NodePanelBrushPreset` friend adapters to reach private list state | Preserve existing brush UI behavior while brush commands and view projection move toward a brush/app/asset command boundary and asset-managed texture/preset 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-preset-list --kind remove --item-count 1 --current-index 0`; `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`; `pano_cli plan-brush-stroke-panel-view --tip-size 64 --jitter-scatter 0.4 --dual-disabled --tip-blend-mode 2 --pattern-blend-mode 5`; `ctest --preset desktop-fast --build-config Debug` | Brush color/texture/preset/stroke-settings, texture-list, preset-list, stroke-control execution, and stroke-panel projection are owned by injected brush/app/asset/UI services with no legacy brush/canvas adapter, direct `NodePanelBrushPreset` child mutation, brush thumbnail/popup ownership, or brush-panel friend access |
|
||||
| DEBT-0023 | Open | Modernization | Brush/color/preset/stroke-settings UI planning, texture-list add/remove/reorder planning, brush preset-list add/select/move/remove/clear planning, stroke-panel slider/toggle/blend/reset planning, stroke-panel view projection, app-level brush refresh view projection, and execution dispatch now consume pure `pp_app_core` through `App::init_sidebar`, `App::brush_update()`, `NodePanelBrush`, `NodePanelBrushPreset`, `NodePanelStroke`, `NodePanelStroke::update_controls()`, restored/docked floating-panel callbacks, `pano_cli plan-brush-operation`, `pano_cli plan-brush-refresh`, `pano_cli plan-brush-texture-list`, `pano_cli plan-brush-preset-list`, `pano_cli plan-brush-stroke-control`, `pano_cli plan-brush-stroke-panel-view`, `BrushUiServices`, `BrushTextureListServices`, `BrushPresetListServices`, and `BrushStrokeControlServices`, and live execution is centralized in `src/legacy_brush_ui_services.*` or narrow legacy service bridges where possible, but preset-list execution still mutates legacy `NodePanelBrushPreset` child nodes directly while the bridge and panel adapter still mutate/read legacy `Brush`/`Canvas::I`, load/save legacy brush texture images, apply retained legacy quick/stroke/color widget writes, own brush thumbnail paths and popup behavior, and use temporary `NodePanelBrush`/`NodePanelBrushPreset` friend adapters to reach private list state | Preserve existing brush UI behavior while brush commands and view projection move toward a brush/app/asset command boundary and asset-managed texture/preset 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-refresh --floating-picker --tip-flow 0.8 --tip-size 48 --r 0.2 --g 0.3 --b 0.4 --a 1`; `pano_cli plan-brush-texture-list --kind add --dir brushes --data-path data --source C:/Temp/soft.png`; `pano_cli plan-brush-preset-list --kind remove --item-count 1 --current-index 0`; `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`; `pano_cli plan-brush-stroke-panel-view --tip-size 64 --jitter-scatter 0.4 --dual-disabled --tip-blend-mode 2 --pattern-blend-mode 5`; `ctest --preset desktop-fast --build-config Debug` | Brush color/texture/preset/stroke-settings, texture-list, preset-list, stroke-control execution, stroke-panel projection, and brush refresh projection are owned by injected brush/app/asset/UI services with no legacy brush/canvas adapter, direct `NodePanelBrushPreset` child mutation, brush thumbnail/popup ownership, legacy quick/stroke/color widget writes, or brush-panel friend access |
|
||||
| DEBT-0024 | Open | Modernization | Grid/heightmap/lightmap UI planning and execution dispatch now consume pure `pp_app_core` through `NodePanelGrid`, `pano_cli plan-grid-operation`, and the `GridUiServices` boundary; live execution is centralized in `src/legacy_grid_ui_services.*`, and retained CPU lightmap row dispatch now uses shared `parallel_for` instead of platform-specific Win32/Apple worker APIs, but the bridge still performs legacy image loading, OpenGL texture updates, nanort lightmap baking/progress, and `Canvas::draw_objects` commit execution | 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; live execution is centralized in `src/legacy_quick_ui_services.*`, but the bridge 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 |
|
||||
|
||||
@@ -560,6 +560,9 @@ projection for brush float settings, toggles, blend modes, and thumbnail paths,
|
||||
and live `NodePanelStroke::update_controls()` now consumes that tested
|
||||
projection before applying retained slider-curve, preview, and thumbnail UI
|
||||
updates.
|
||||
`pano_cli plan-brush-refresh` exposes app-core planning for app-level brush
|
||||
refresh fan-out, and live `App::brush_update()` now consumes that view before
|
||||
applying retained stroke, quick, and floating color widget updates.
|
||||
`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
|
||||
commands. Canvas tool execution now dispatches through `CanvasToolServices`
|
||||
|
||||
@@ -212,6 +212,37 @@ struct BrushStrokePanelView {
|
||||
bool updates_thumbnails = true;
|
||||
};
|
||||
|
||||
struct BrushUiRefreshInput {
|
||||
bool update_color = false;
|
||||
bool update_brush = false;
|
||||
bool has_current_brush = true;
|
||||
bool has_floating_picker = false;
|
||||
bool has_floating_color_panel = false;
|
||||
float tip_flow = 0.0F;
|
||||
float tip_size = 0.0F;
|
||||
float r = 0.0F;
|
||||
float g = 0.0F;
|
||||
float b = 0.0F;
|
||||
float a = 1.0F;
|
||||
};
|
||||
|
||||
struct BrushUiRefreshView {
|
||||
bool updates_stroke_controls = false;
|
||||
bool updates_quick_flow = false;
|
||||
bool updates_quick_size = false;
|
||||
bool updates_quick_brush_preview = false;
|
||||
bool updates_quick_color = false;
|
||||
bool updates_floating_picker = false;
|
||||
bool updates_floating_color_panel = false;
|
||||
bool no_op = false;
|
||||
float tip_flow = 0.0F;
|
||||
float tip_size = 0.0F;
|
||||
float r = 0.0F;
|
||||
float g = 0.0F;
|
||||
float b = 0.0F;
|
||||
float a = 1.0F;
|
||||
};
|
||||
|
||||
class BrushUiServices {
|
||||
public:
|
||||
virtual ~BrushUiServices() = default;
|
||||
@@ -342,6 +373,57 @@ public:
|
||||
return pp::foundation::Result<BrushStrokePanelView>::success(std::move(view));
|
||||
}
|
||||
|
||||
[[nodiscard]] inline pp::foundation::Result<BrushUiRefreshView> plan_brush_ui_refresh(
|
||||
const BrushUiRefreshInput& input)
|
||||
{
|
||||
if (!input.update_color && !input.update_brush) {
|
||||
BrushUiRefreshView view;
|
||||
view.no_op = true;
|
||||
return pp::foundation::Result<BrushUiRefreshView>::success(view);
|
||||
}
|
||||
|
||||
if (!input.has_current_brush) {
|
||||
return pp::foundation::Result<BrushUiRefreshView>::failure(
|
||||
pp::foundation::Status::invalid_argument("brush refresh requires a current brush"));
|
||||
}
|
||||
|
||||
if (input.update_brush) {
|
||||
const auto flow_status = validate_brush_stroke_float(input.tip_flow);
|
||||
if (!flow_status.ok()) {
|
||||
return pp::foundation::Result<BrushUiRefreshView>::failure(flow_status);
|
||||
}
|
||||
const auto size_status = validate_brush_stroke_float(input.tip_size);
|
||||
if (!size_status.ok()) {
|
||||
return pp::foundation::Result<BrushUiRefreshView>::failure(size_status);
|
||||
}
|
||||
}
|
||||
|
||||
if (input.update_color) {
|
||||
for (const auto value : { input.r, input.g, input.b, input.a }) {
|
||||
const auto channel_status = validate_brush_ui_color_channel(value);
|
||||
if (!channel_status.ok()) {
|
||||
return pp::foundation::Result<BrushUiRefreshView>::failure(channel_status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BrushUiRefreshView view;
|
||||
view.updates_stroke_controls = input.update_brush;
|
||||
view.updates_quick_flow = input.update_brush;
|
||||
view.updates_quick_size = input.update_brush;
|
||||
view.updates_quick_brush_preview = input.update_brush;
|
||||
view.updates_quick_color = input.update_color;
|
||||
view.updates_floating_picker = input.update_color && input.has_floating_picker;
|
||||
view.updates_floating_color_panel = input.update_color && input.has_floating_color_panel;
|
||||
view.tip_flow = input.tip_flow;
|
||||
view.tip_size = input.tip_size;
|
||||
view.r = input.r;
|
||||
view.g = input.g;
|
||||
view.b = input.b;
|
||||
view.a = input.a;
|
||||
return pp::foundation::Result<BrushUiRefreshView>::success(view);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline pp::foundation::Result<BrushUiPlan> plan_brush_ui_color(
|
||||
float r,
|
||||
float g,
|
||||
|
||||
@@ -1398,21 +1398,54 @@ void App::brush_update(bool update_color, bool update_brush)
|
||||
// stroke->set_params(canvas->m_brush);
|
||||
render_task_async([this, update_color, update_brush]
|
||||
{
|
||||
if (update_brush)
|
||||
pp::app::BrushUiRefreshInput input;
|
||||
input.update_color = update_color;
|
||||
input.update_brush = update_brush;
|
||||
auto current_brush = Canvas::I ? Canvas::I->m_current_brush : nullptr;
|
||||
input.has_current_brush = current_brush != nullptr;
|
||||
input.has_floating_picker = floating_picker != nullptr;
|
||||
input.has_floating_color_panel = floating_color != nullptr;
|
||||
if (input.has_current_brush)
|
||||
{
|
||||
input.tip_flow = current_brush->m_tip_flow;
|
||||
input.tip_size = current_brush->m_tip_size;
|
||||
input.r = current_brush->m_tip_color.r;
|
||||
input.g = current_brush->m_tip_color.g;
|
||||
input.b = current_brush->m_tip_color.b;
|
||||
input.a = current_brush->m_tip_color.a;
|
||||
}
|
||||
|
||||
const auto view = pp::app::plan_brush_ui_refresh(input);
|
||||
if (!view) {
|
||||
LOG("Brush UI refresh failed: %s", view.status().message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (view.value().updates_stroke_controls)
|
||||
{
|
||||
stroke->update_controls();
|
||||
}
|
||||
if (view.value().updates_quick_flow)
|
||||
{
|
||||
quick->m_slider_flow->set_value(stroke->m_tip_flow->get_value());
|
||||
}
|
||||
if (view.value().updates_quick_size)
|
||||
{
|
||||
quick->m_slider_size->set_value(stroke->m_tip_size->get_value());
|
||||
*quick->m_button_brush_current_preview->m_brush = *Canvas::I->m_current_brush;
|
||||
}
|
||||
if (view.value().updates_quick_brush_preview && current_brush)
|
||||
{
|
||||
*quick->m_button_brush_current_preview->m_brush = *current_brush;
|
||||
quick->m_button_brush_current_preview->draw_stroke();
|
||||
}
|
||||
if (update_color)
|
||||
if (view.value().updates_quick_color)
|
||||
{
|
||||
quick->m_button_color_current_inner->m_color = Canvas::I->m_current_brush->m_tip_color;
|
||||
if (floating_picker)
|
||||
floating_picker->set_color(Canvas::I->m_current_brush->m_tip_color);
|
||||
if (floating_color)
|
||||
floating_color->set_color(Canvas::I->m_current_brush->m_tip_color);
|
||||
const glm::vec4 color(view.value().r, view.value().g, view.value().b, view.value().a);
|
||||
quick->m_button_color_current_inner->m_color = color;
|
||||
if (view.value().updates_floating_picker)
|
||||
floating_picker->set_color(color);
|
||||
if (view.value().updates_floating_color_panel)
|
||||
floating_color->set_color(color);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@@ -1364,6 +1364,30 @@ if(TARGET pano_cli)
|
||||
LABELS "app;paint;integration;desktop-fast;fuzz"
|
||||
WILL_FAIL TRUE)
|
||||
|
||||
add_test(NAME pano_cli_plan_brush_refresh_smoke
|
||||
COMMAND pano_cli plan-brush-refresh --floating-picker --tip-flow 0.8 --tip-size 48 --r 0.2 --g 0.3 --b 0.4 --a 1)
|
||||
set_tests_properties(pano_cli_plan_brush_refresh_smoke PROPERTIES
|
||||
LABELS "app;paint;ui;integration;desktop-fast"
|
||||
PASS_REGULAR_EXPRESSION "\"command\":\"plan-brush-refresh\".*\"updatesStrokeControls\":true.*\"updatesQuickFlow\":true.*\"updatesQuickSize\":true.*\"updatesQuickBrushPreview\":true.*\"updatesQuickColor\":true.*\"updatesFloatingPicker\":true.*\"updatesFloatingColorPanel\":false.*\"tipFlow\":0.8.*\"tipSize\":48.*\"r\":0.2.*\"g\":0.3.*\"b\":0.4.*\"a\":1")
|
||||
|
||||
add_test(NAME pano_cli_plan_brush_refresh_noop_smoke
|
||||
COMMAND pano_cli plan-brush-refresh --no-color --no-brush-update --no-brush)
|
||||
set_tests_properties(pano_cli_plan_brush_refresh_noop_smoke PROPERTIES
|
||||
LABELS "app;paint;ui;integration;desktop-fast"
|
||||
PASS_REGULAR_EXPRESSION "\"command\":\"plan-brush-refresh\".*\"updatesQuickBrushPreview\":false.*\"updatesQuickColor\":false.*\"noOp\":true")
|
||||
|
||||
add_test(NAME pano_cli_plan_brush_refresh_rejects_missing_brush
|
||||
COMMAND pano_cli plan-brush-refresh --no-brush)
|
||||
set_tests_properties(pano_cli_plan_brush_refresh_rejects_missing_brush PROPERTIES
|
||||
LABELS "app;paint;ui;integration;desktop-fast;fuzz"
|
||||
WILL_FAIL TRUE)
|
||||
|
||||
add_test(NAME pano_cli_plan_brush_refresh_rejects_bad_float
|
||||
COMMAND pano_cli plan-brush-refresh --bad-float)
|
||||
set_tests_properties(pano_cli_plan_brush_refresh_rejects_bad_float PROPERTIES
|
||||
LABELS "app;paint;ui;integration;desktop-fast;fuzz"
|
||||
WILL_FAIL TRUE)
|
||||
|
||||
add_test(NAME pano_cli_plan_brush_texture_list_add_smoke
|
||||
COMMAND pano_cli plan-brush-texture-list --kind add --dir brushes --data-path data --source C:/Temp/soft.png)
|
||||
set_tests_properties(pano_cli_plan_brush_texture_list_add_smoke PROPERTIES
|
||||
|
||||
@@ -358,6 +358,98 @@ void stroke_settings_plan_updates_brush_preview(pp::tests::Harness& harness)
|
||||
PP_EXPECT(harness, !plan.loads_brush_resources);
|
||||
}
|
||||
|
||||
void brush_ui_refresh_projects_requested_surfaces(pp::tests::Harness& harness)
|
||||
{
|
||||
const auto view = pp::app::plan_brush_ui_refresh(pp::app::BrushUiRefreshInput {
|
||||
.update_color = true,
|
||||
.update_brush = true,
|
||||
.has_current_brush = true,
|
||||
.has_floating_picker = true,
|
||||
.has_floating_color_panel = false,
|
||||
.tip_flow = 0.9F,
|
||||
.tip_size = 64.0F,
|
||||
.r = 0.25F,
|
||||
.g = 0.5F,
|
||||
.b = 0.75F,
|
||||
.a = 1.0F,
|
||||
});
|
||||
|
||||
PP_EXPECT(harness, view);
|
||||
if (view) {
|
||||
PP_EXPECT(harness, view.value().updates_stroke_controls);
|
||||
PP_EXPECT(harness, view.value().updates_quick_flow);
|
||||
PP_EXPECT(harness, view.value().updates_quick_size);
|
||||
PP_EXPECT(harness, view.value().updates_quick_brush_preview);
|
||||
PP_EXPECT(harness, view.value().updates_quick_color);
|
||||
PP_EXPECT(harness, view.value().updates_floating_picker);
|
||||
PP_EXPECT(harness, !view.value().updates_floating_color_panel);
|
||||
PP_EXPECT(harness, !view.value().no_op);
|
||||
PP_EXPECT(harness, view.value().tip_flow == 0.9F);
|
||||
PP_EXPECT(harness, view.value().tip_size == 64.0F);
|
||||
PP_EXPECT(harness, view.value().r == 0.25F);
|
||||
PP_EXPECT(harness, view.value().g == 0.5F);
|
||||
PP_EXPECT(harness, view.value().b == 0.75F);
|
||||
PP_EXPECT(harness, view.value().a == 1.0F);
|
||||
}
|
||||
|
||||
const auto color_only = pp::app::plan_brush_ui_refresh(pp::app::BrushUiRefreshInput {
|
||||
.update_color = true,
|
||||
.update_brush = false,
|
||||
.has_current_brush = true,
|
||||
.has_floating_picker = false,
|
||||
.has_floating_color_panel = true,
|
||||
.r = 0.1F,
|
||||
.g = 0.2F,
|
||||
.b = 0.3F,
|
||||
.a = 1.0F,
|
||||
});
|
||||
PP_EXPECT(harness, color_only);
|
||||
if (color_only) {
|
||||
PP_EXPECT(harness, !color_only.value().updates_stroke_controls);
|
||||
PP_EXPECT(harness, !color_only.value().updates_quick_flow);
|
||||
PP_EXPECT(harness, color_only.value().updates_quick_color);
|
||||
PP_EXPECT(harness, !color_only.value().updates_floating_picker);
|
||||
PP_EXPECT(harness, color_only.value().updates_floating_color_panel);
|
||||
}
|
||||
|
||||
const auto no_op = pp::app::plan_brush_ui_refresh(pp::app::BrushUiRefreshInput {
|
||||
.update_color = false,
|
||||
.update_brush = false,
|
||||
.has_current_brush = false,
|
||||
});
|
||||
PP_EXPECT(harness, no_op);
|
||||
if (no_op) {
|
||||
PP_EXPECT(harness, no_op.value().no_op);
|
||||
PP_EXPECT(harness, !no_op.value().updates_quick_color);
|
||||
PP_EXPECT(harness, !no_op.value().updates_quick_brush_preview);
|
||||
}
|
||||
}
|
||||
|
||||
void brush_ui_refresh_rejects_invalid_state(pp::tests::Harness& harness)
|
||||
{
|
||||
PP_EXPECT(harness, !pp::app::plan_brush_ui_refresh(pp::app::BrushUiRefreshInput {
|
||||
.update_color = true,
|
||||
.update_brush = false,
|
||||
.has_current_brush = false,
|
||||
}));
|
||||
PP_EXPECT(harness, !pp::app::plan_brush_ui_refresh(pp::app::BrushUiRefreshInput {
|
||||
.update_color = true,
|
||||
.update_brush = false,
|
||||
.has_current_brush = true,
|
||||
.r = 1.25F,
|
||||
.g = 0.0F,
|
||||
.b = 0.0F,
|
||||
.a = 1.0F,
|
||||
}));
|
||||
PP_EXPECT(harness, !pp::app::plan_brush_ui_refresh(pp::app::BrushUiRefreshInput {
|
||||
.update_color = false,
|
||||
.update_brush = true,
|
||||
.has_current_brush = true,
|
||||
.tip_flow = std::nanf(""),
|
||||
.tip_size = 64.0F,
|
||||
}));
|
||||
}
|
||||
|
||||
void stroke_control_plans_validate_values_and_reject_breaking_points(pp::tests::Harness& harness)
|
||||
{
|
||||
const auto slider = pp::app::plan_brush_stroke_float_setting(
|
||||
@@ -920,6 +1012,8 @@ int main()
|
||||
harness.run("texture plan validates path and slot", texture_plan_validates_path_and_slot);
|
||||
harness.run("preset plan preserves color and requires brush", preset_plan_preserves_color_and_requires_brush);
|
||||
harness.run("stroke settings plan updates brush preview", stroke_settings_plan_updates_brush_preview);
|
||||
harness.run("brush UI refresh projects requested surfaces", brush_ui_refresh_projects_requested_surfaces);
|
||||
harness.run("brush UI refresh rejects invalid state", brush_ui_refresh_rejects_invalid_state);
|
||||
harness.run("stroke control plans validate values and reject breaking points", stroke_control_plans_validate_values_and_reject_breaking_points);
|
||||
harness.run("stroke panel view projects brush state", stroke_panel_view_projects_brush_state);
|
||||
harness.run("stroke panel view rejects invalid brush state", stroke_panel_view_rejects_invalid_brush_state);
|
||||
|
||||
@@ -385,6 +385,21 @@ struct PlanBrushOperationArgs {
|
||||
bool has_brush = true;
|
||||
};
|
||||
|
||||
struct PlanBrushRefreshArgs {
|
||||
bool update_color = true;
|
||||
bool update_brush = true;
|
||||
bool has_brush = true;
|
||||
bool floating_picker = false;
|
||||
bool floating_color = false;
|
||||
float tip_flow = 0.9F;
|
||||
float tip_size = 64.0F;
|
||||
float r = 0.25F;
|
||||
float g = 0.5F;
|
||||
float b = 0.75F;
|
||||
float a = 1.0F;
|
||||
bool bad_float = false;
|
||||
};
|
||||
|
||||
struct PlanBrushTextureListArgs {
|
||||
std::string kind = "add";
|
||||
std::string directory_name = "brushes";
|
||||
@@ -1967,6 +1982,7 @@ void print_help()
|
||||
<< " plan-animation-panel-view [--layer-count N] [--frame-count N] [--frame-duration N] [--total-duration N] [--current-layer N] [--current-frame N] [--selected-layer-id N] [--selected-frame N] [--onion-size N] [--hidden-layer N]\n"
|
||||
<< " plan-animation-timeline-scrub [--total-duration N] [--cursor-x N] [--frame-width N]\n"
|
||||
<< " plan-brush-operation --kind color|tip|pattern|dual|preset|settings [--path FILE] [--thumb FILE] [--r N] [--g N] [--b N] [--a N] [--no-brush]\n"
|
||||
<< " plan-brush-refresh [--color|--no-color] [--brush|--no-brush-update] [--no-brush] [--floating-picker] [--floating-color] [--tip-flow N] [--tip-size N] [--r N] [--g N] [--b N] [--a N] [--bad-float]\n"
|
||||
<< " plan-brush-texture-list --kind add|remove|move [--dir NAME] [--data-path DIR] [--source FILE] [--item-count N] [--current-index N] [--offset N] [--user-texture]\n"
|
||||
<< " plan-brush-preset-list --kind add|remove|move|up|down|select|clear [--item-count N] [--current-index N] [--offset N] [--no-current-brush]\n"
|
||||
<< " plan-brush-stroke-control --kind float|bool|blend|tip-aspect-reset|default-reset [--setting NAME] [--value N] [--enabled|--disabled] [--blend-mode N]\n"
|
||||
@@ -5303,6 +5319,116 @@ int plan_brush_operation(int argc, char** argv)
|
||||
return 0;
|
||||
}
|
||||
|
||||
pp::foundation::Status parse_plan_brush_refresh_args(
|
||||
int argc,
|
||||
char** argv,
|
||||
PlanBrushRefreshArgs& args)
|
||||
{
|
||||
for (int i = 2; i < argc; ++i) {
|
||||
const std::string_view key(argv[i]);
|
||||
if (key == "--tip-flow" || key == "--tip-size" || key == "--r" || key == "--g" || key == "--b" || key == "--a") {
|
||||
if (i + 1 >= argc) {
|
||||
return pp::foundation::Status::invalid_argument("missing value for option");
|
||||
}
|
||||
const auto value = parse_float_arg(argv[++i]);
|
||||
if (!value) {
|
||||
return value.status();
|
||||
}
|
||||
if (key == "--tip-flow") {
|
||||
args.tip_flow = value.value();
|
||||
} else if (key == "--tip-size") {
|
||||
args.tip_size = value.value();
|
||||
} else if (key == "--r") {
|
||||
args.r = value.value();
|
||||
} else if (key == "--g") {
|
||||
args.g = value.value();
|
||||
} else if (key == "--b") {
|
||||
args.b = value.value();
|
||||
} else {
|
||||
args.a = value.value();
|
||||
}
|
||||
} else if (key == "--color") {
|
||||
args.update_color = true;
|
||||
} else if (key == "--no-color") {
|
||||
args.update_color = false;
|
||||
} else if (key == "--brush") {
|
||||
args.update_brush = true;
|
||||
} else if (key == "--no-brush-update") {
|
||||
args.update_brush = false;
|
||||
} else if (key == "--no-brush") {
|
||||
args.has_brush = false;
|
||||
} else if (key == "--floating-picker") {
|
||||
args.floating_picker = true;
|
||||
} else if (key == "--floating-color") {
|
||||
args.floating_color = true;
|
||||
} else if (key == "--bad-float") {
|
||||
args.bad_float = true;
|
||||
} else {
|
||||
return pp::foundation::Status::invalid_argument("unknown option");
|
||||
}
|
||||
}
|
||||
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
int plan_brush_refresh(int argc, char** argv)
|
||||
{
|
||||
PlanBrushRefreshArgs args;
|
||||
const auto status = parse_plan_brush_refresh_args(argc, argv, args);
|
||||
if (!status.ok()) {
|
||||
print_error("plan-brush-refresh", status.message);
|
||||
return 2;
|
||||
}
|
||||
|
||||
const auto view = pp::app::plan_brush_ui_refresh(pp::app::BrushUiRefreshInput {
|
||||
.update_color = args.update_color,
|
||||
.update_brush = args.update_brush,
|
||||
.has_current_brush = args.has_brush,
|
||||
.has_floating_picker = args.floating_picker,
|
||||
.has_floating_color_panel = args.floating_color,
|
||||
.tip_flow = args.bad_float ? std::nanf("") : args.tip_flow,
|
||||
.tip_size = args.tip_size,
|
||||
.r = args.r,
|
||||
.g = args.g,
|
||||
.b = args.b,
|
||||
.a = args.a,
|
||||
});
|
||||
if (!view) {
|
||||
print_error("plan-brush-refresh", view.status().message);
|
||||
return 2;
|
||||
}
|
||||
|
||||
const auto& value = view.value();
|
||||
std::cout << "{\"ok\":true,\"command\":\"plan-brush-refresh\""
|
||||
<< ",\"state\":{\"updateColor\":" << json_bool(args.update_color)
|
||||
<< ",\"updateBrush\":" << json_bool(args.update_brush)
|
||||
<< ",\"hasBrush\":" << json_bool(args.has_brush)
|
||||
<< ",\"floatingPicker\":" << json_bool(args.floating_picker)
|
||||
<< ",\"floatingColor\":" << json_bool(args.floating_color)
|
||||
<< ",\"tipFlow\":" << args.tip_flow
|
||||
<< ",\"tipSize\":" << args.tip_size
|
||||
<< ",\"r\":" << args.r
|
||||
<< ",\"g\":" << args.g
|
||||
<< ",\"b\":" << args.b
|
||||
<< ",\"a\":" << args.a
|
||||
<< "},\"view\":{\"updatesStrokeControls\":" << json_bool(value.updates_stroke_controls)
|
||||
<< ",\"updatesQuickFlow\":" << json_bool(value.updates_quick_flow)
|
||||
<< ",\"updatesQuickSize\":" << json_bool(value.updates_quick_size)
|
||||
<< ",\"updatesQuickBrushPreview\":" << json_bool(value.updates_quick_brush_preview)
|
||||
<< ",\"updatesQuickColor\":" << json_bool(value.updates_quick_color)
|
||||
<< ",\"updatesFloatingPicker\":" << json_bool(value.updates_floating_picker)
|
||||
<< ",\"updatesFloatingColorPanel\":" << json_bool(value.updates_floating_color_panel)
|
||||
<< ",\"noOp\":" << json_bool(value.no_op)
|
||||
<< ",\"tipFlow\":" << value.tip_flow
|
||||
<< ",\"tipSize\":" << value.tip_size
|
||||
<< ",\"r\":" << value.r
|
||||
<< ",\"g\":" << value.g
|
||||
<< ",\"b\":" << value.b
|
||||
<< ",\"a\":" << value.a
|
||||
<< "}}\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
pp::foundation::Status parse_plan_brush_texture_list_args(
|
||||
int argc,
|
||||
char** argv,
|
||||
@@ -9487,6 +9613,10 @@ int main(int argc, char** argv)
|
||||
return plan_brush_operation(argc, argv);
|
||||
}
|
||||
|
||||
if (command == "plan-brush-refresh") {
|
||||
return plan_brush_refresh(argc, argv);
|
||||
}
|
||||
|
||||
if (command == "plan-brush-texture-list") {
|
||||
return plan_brush_texture_list(argc, argv);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user