Add canvas tool service boundary
This commit is contained in:
@@ -44,7 +44,7 @@ agent or engineer to remove them without reconstructing context from chat.
|
||||
| DEBT-0024 | Open | Modernization | Grid/heightmap/lightmap UI planning now consumes pure `pp_app_core` through `NodePanelGrid` and `pano_cli plan-grid-operation`, but live execution still performs legacy image loading, OpenGL texture updates, nanort lightmap baking, progress UI, and `Canvas::draw_objects` commit directly | Preserve grid/lightmap behavior while moving renderable grid commands toward app/renderer/document boundaries | `pp_app_core_grid_ui_tests`; `pano_cli plan-grid-operation --kind render --float32 --texture-resolution 1024 --samples 32`; `ctest --preset desktop-fast --build-config Debug` | Grid heightmap/lightmap execution is owned by app/renderer/document services with `NodePanelGrid` acting only as UI adapter |
|
||||
| DEBT-0025 | Open | Modernization | Quick brush/color slot and mini-state planning and execution dispatch now consume pure `pp_app_core` through `NodePanelQuick`, `pano_cli plan-quick-operation`, and the `QuickUiServices` boundary, but the live adapter still mutates legacy quick UI widgets, `Brush` previews, color picker popup state, and preset popup state | Preserve quick-panel behavior while quick brush/color commands move toward a brush/app command boundary with safer automation coverage | `pp_app_core_quick_ui_tests`; `pano_cli plan-quick-operation --kind brush --current-index 0 --slot-index 2`; `pano_cli plan-quick-operation --kind restore --brush-index 2 --color-index 1 --fire-event`; `ctest --preset desktop-fast --build-config Debug` | Quick-panel selection, popup, restore, reset, brush preview, and color execution are owned by injected app/brush/UI services with no legacy quick-panel adapter |
|
||||
| DEBT-0026 | Open | Modernization | Toolbar history command planning and execution dispatch now consume pure `pp_app_core` through `App::init_toolbar_main`, `NodeCanvas`, `pano_cli plan-history-operation`, and the `HistoryUiServices` boundary, but the live adapter still mutates legacy `ActionManager` stacks directly | Preserve undo/redo/clear behavior while moving action history toward document/app command services | `pp_app_core_history_ui_tests`; `pano_cli plan-history-operation --kind undo --undo-count 2`; `pano_cli plan-history-operation --kind clear --undo-count 2 --redo-count 1 --memory-bytes 4096`; `ctest --preset desktop-fast --build-config Debug` | Undo/redo/clear execution is owned by injected document/app history services with no legacy `ActionManager` adapter |
|
||||
| DEBT-0027 | Open | Modernization | Canvas draw-tool toolbar command, canvas input mode switching, and active-state planning now consume pure `pp_app_core` through `App::init_toolbar_draw`, `App::update`, `NodeCanvas`, `pano_cli plan-canvas-tool`, and `pano_cli plan-canvas-tool-state`, but live execution/state storage still mutates or reads legacy `Canvas` mode state, pen picking state, touch-lock state, and transform copy/cut action objects directly | Preserve current toolbar, stylus eraser, and keyboard draw/erase behavior while canvas input/tools move toward an app/document command boundary | `pp_app_core_canvas_tool_ui_tests`; `pano_cli plan-canvas-tool --kind copy`; `pano_cli plan-canvas-tool-state --mode draw --picking --touch-lock`; `ctest --preset desktop-fast --build-config Debug` | Canvas tool selection, toolbar state refresh, picking, touch lock, stylus eraser/key mode switching, and transform action execution are owned by app/document/canvas services with toolbar/canvas callbacks acting only as adapters |
|
||||
| DEBT-0027 | Open | Modernization | Canvas draw-tool toolbar command, canvas input mode switching, and active-state planning/execution dispatch now consume pure `pp_app_core` through `App::init_toolbar_draw`, `App::update`, `NodeCanvas`, `pano_cli plan-canvas-tool`, `pano_cli plan-canvas-tool-state`, and the `CanvasToolServices` boundary, but live adapters still mutate or read legacy `Canvas` mode state, pen picking state, touch-lock state, and transform copy/cut action objects | Preserve current toolbar, stylus eraser, and keyboard draw/erase behavior while canvas input/tools move toward an app/document command boundary | `pp_app_core_canvas_tool_ui_tests`; `pano_cli plan-canvas-tool --kind copy`; `pano_cli plan-canvas-tool-state --mode draw --picking --touch-lock`; `ctest --preset desktop-fast --build-config Debug` | Canvas tool selection, toolbar state refresh, picking, touch lock, stylus eraser/key mode switching, and transform action execution are owned by injected app/document/canvas services with no legacy toolbar/canvas adapter |
|
||||
| DEBT-0028 | Open | Modernization | Canvas clear command planning and execution dispatch now consume pure `pp_app_core` through `App::init_toolbar_main`, `pano_cli plan-canvas-clear`, and the `DocumentCanvasClearServices` boundary, but the live adapter still calls legacy `Canvas::clear`, which records `ActionLayerClear`, clears the current layer/frame, and marks legacy `Canvas::I` unsaved | Preserve clear-current-layer behavior while canvas/document commands move toward document/app command services | `pp_app_core_document_canvas_tests`; `pano_cli plan-canvas-clear --r 0 --g 0.1 --b 0.2 --a 0.3`; `pano_cli plan-canvas-clear --no-canvas`; `ctest --preset desktop-fast --build-config Debug` | Canvas clear execution, undo recording, dirty-state updates, and clear color handling are owned by injected document/app services with no legacy canvas-clear adapter |
|
||||
| DEBT-0029 | Open | Modernization | Image import route planning and execution dispatch now consume pure `pp_app_core` through the File menu, `pano_cli plan-image-import`, and the `DocumentImageImportServices` boundary, but the live adapter still loads images with legacy `Image`, calls legacy `Canvas::import_equirectangular`, or configures legacy import transform mode directly | Preserve current File > Import behavior while image import moves toward document/app/asset command services | `pp_app_core_document_import_tests`; `pano_cli plan-image-import --width 4096 --height 2048`; `pano_cli plan-image-import --width 1024 --height 1024`; `ctest --preset desktop-fast --build-config Debug` | Image loading, equirectangular import, transform-placement import, and failure reporting are owned by injected document/app/asset services with File-menu callbacks acting only as adapters and no legacy image-import adapter |
|
||||
| DEBT-0030 | Open | Modernization | File export menu action planning and execution dispatch now consume pure `pp_app_core` through the File menu, `pano_cli plan-export-menu`, and the `DocumentExportMenuServices` boundary, but the live adapter still opens legacy export dialogs and then reaches legacy canvas/render/video export code | Preserve current export menu behavior while export command execution moves toward document/app/renderer/video services | `pp_app_core_document_export_tests`; `pano_cli plan-export-menu --kind png`; `pano_cli plan-export-menu --kind animation-mp4 --demo`; `pano_cli plan-export-menu --kind layers --no-canvas`; `ctest --preset desktop-fast --build-config Debug` | Export menu routing, license gating, target creation, image/layer/cube/depth/animation/timelapse execution, and error reporting are owned by injected document/app/renderer/video services with File-menu callbacks acting only as UI adapters and no legacy export adapter |
|
||||
|
||||
@@ -507,12 +507,13 @@ callbacks. Brush UI execution now dispatches through `BrushUiServices` before
|
||||
the legacy `Brush`/panel adapter mutates brush state or loads brush resources.
|
||||
`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 before legacy `Canvas` mode, pen picking, touch-lock, and transform
|
||||
state mutation continue. `pano_cli plan-canvas-tool-state` exposes the matching
|
||||
toolbar active-state refresh used by `App::update` before legacy `Canvas` mode
|
||||
state remains the source of truth. `NodeCanvas` stylus eraser and `E` key
|
||||
draw/erase mode switching also consume the same app-core command planner before
|
||||
legacy canvas mode execution continues.
|
||||
commands. Canvas tool execution now dispatches through `CanvasToolServices`
|
||||
before legacy toolbar selection, `Canvas` mode, pen picking, touch-lock, and
|
||||
transform state adapters continue. `pano_cli plan-canvas-tool-state` exposes
|
||||
the matching toolbar active-state refresh used by `App::update` before legacy
|
||||
`Canvas` mode state remains the source of truth. `NodeCanvas` stylus eraser
|
||||
and `E` key draw/erase mode switching also consume the same app-core executor
|
||||
before legacy canvas mode execution continues.
|
||||
`pano_cli plan-canvas-clear` exposes app-core planning for the main toolbar
|
||||
clear-current-layer command, including clear color validation, no-canvas
|
||||
handling, undo recording intent, and dirty-state intent; live toolbar execution
|
||||
@@ -1229,7 +1230,8 @@ Results:
|
||||
- `pp_app_core_canvas_tool_ui_tests` passed, covering toolbar mode selection,
|
||||
copy/cut transform action planning, pick no-op outside draw mode, and
|
||||
touch-lock toggling, plus toolbar active-state derivation for draw, copy, and
|
||||
bucket modes.
|
||||
bucket modes, service dispatch ordering, pick no-op execution, and malformed
|
||||
execution payload rejection.
|
||||
- `pano_cli_plan_canvas_tool_draw_smoke`,
|
||||
`pano_cli_plan_canvas_tool_copy_smoke`,
|
||||
`pano_cli_plan_canvas_tool_pick_noop_smoke`,
|
||||
|
||||
@@ -59,6 +59,17 @@ struct CanvasToolButtonState {
|
||||
bool flood_fill_active = false;
|
||||
};
|
||||
|
||||
class CanvasToolServices {
|
||||
public:
|
||||
virtual ~CanvasToolServices() = default;
|
||||
|
||||
virtual void select_toolbar_button(CanvasToolMode mode) = 0;
|
||||
virtual void set_transform_action(CanvasToolTransformAction action) = 0;
|
||||
virtual void set_canvas_mode(CanvasToolMode mode) = 0;
|
||||
virtual void toggle_picking() = 0;
|
||||
virtual void toggle_touch_lock() = 0;
|
||||
};
|
||||
|
||||
[[nodiscard]] inline constexpr CanvasToolTransformAction transform_action_for_mode(CanvasToolMode mode) noexcept
|
||||
{
|
||||
if (mode == CanvasToolMode::copy) {
|
||||
@@ -123,4 +134,47 @@ struct CanvasToolButtonState {
|
||||
return state;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline pp::foundation::Status execute_canvas_tool_plan(
|
||||
const CanvasToolPlan& plan,
|
||||
CanvasToolServices& services)
|
||||
{
|
||||
switch (plan.operation) {
|
||||
case CanvasToolOperation::select_mode:
|
||||
if (!plan.selects_toolbar_button || !plan.updates_canvas_mode) {
|
||||
return pp::foundation::Status::invalid_argument("canvas tool select plan must select toolbar and update mode");
|
||||
}
|
||||
if (plan.transform_action != transform_action_for_mode(plan.mode)) {
|
||||
return pp::foundation::Status::invalid_argument("canvas tool select plan has mismatched transform action");
|
||||
}
|
||||
services.select_toolbar_button(plan.mode);
|
||||
if (plan.transform_action != CanvasToolTransformAction::none) {
|
||||
services.set_transform_action(plan.transform_action);
|
||||
}
|
||||
services.set_canvas_mode(plan.mode);
|
||||
return pp::foundation::Status::success();
|
||||
|
||||
case CanvasToolOperation::toggle_picking:
|
||||
if (!plan.requires_draw_mode) {
|
||||
return pp::foundation::Status::invalid_argument("canvas pick plan must require draw mode");
|
||||
}
|
||||
if (plan.no_op) {
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
if (!plan.toggles_picking) {
|
||||
return pp::foundation::Status::invalid_argument("canvas pick plan must toggle picking or be a no-op");
|
||||
}
|
||||
services.toggle_picking();
|
||||
return pp::foundation::Status::success();
|
||||
|
||||
case CanvasToolOperation::toggle_touch_lock:
|
||||
if (!plan.toggles_touch_lock || plan.no_op) {
|
||||
return pp::foundation::Status::invalid_argument("canvas touch-lock plan must toggle touch lock");
|
||||
}
|
||||
services.toggle_touch_lock();
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
return pp::foundation::Status::invalid_argument("unknown canvas tool operation");
|
||||
}
|
||||
|
||||
} // namespace pp::app
|
||||
|
||||
@@ -1136,8 +1136,19 @@ void App::init_sidebar()
|
||||
};
|
||||
}
|
||||
}
|
||||
template<class T>
|
||||
void select_button(Node* main, T* button) {
|
||||
void set_canvas_tool_button_active(Node* button, bool active)
|
||||
{
|
||||
if (auto* custom = dynamic_cast<NodeButtonCustom*>(button)) {
|
||||
custom->set_active(active);
|
||||
return;
|
||||
}
|
||||
if (auto* regular = dynamic_cast<NodeButton*>(button)) {
|
||||
regular->set_active(active);
|
||||
}
|
||||
}
|
||||
|
||||
void select_canvas_tool_button(Node* main, Node* button)
|
||||
{
|
||||
main->find<NodeButtonCustom>("btn-pen")->set_active(false);
|
||||
main->find<NodeButtonCustom>("btn-erase")->set_active(false);
|
||||
main->find<NodeButtonCustom>("btn-line")->set_active(false);
|
||||
@@ -1145,12 +1156,11 @@ void select_button(Node* main, T* button) {
|
||||
main->find<NodeButton>("btn-grid")->set_active(false);
|
||||
main->find<NodeButton>("btn-copy")->set_active(false);
|
||||
main->find<NodeButton>("btn-cut")->set_active(false);
|
||||
//main->find<NodeButton>("btn-fill")->set_color(color_button_normal);
|
||||
main->find<NodeButtonCustom>("btn-mask-free")->set_active(false);
|
||||
main->find<NodeButtonCustom>("btn-mask-line")->set_active(false);
|
||||
main->find<NodeButtonCustom>("btn-bucket")->set_active(false);
|
||||
button->set_active(false);
|
||||
};
|
||||
set_canvas_tool_button_active(button, false);
|
||||
}
|
||||
|
||||
kCanvasMode canvas_mode_from_tool(pp::app::CanvasToolMode mode)
|
||||
{
|
||||
@@ -1181,25 +1191,73 @@ kCanvasMode canvas_mode_from_tool(pp::app::CanvasToolMode mode)
|
||||
return kCanvasMode::Draw;
|
||||
}
|
||||
|
||||
class LegacyCanvasToolServices final : public pp::app::CanvasToolServices {
|
||||
public:
|
||||
LegacyCanvasToolServices(App& app, Node* toolbar_button = nullptr) noexcept
|
||||
: app_(app)
|
||||
, toolbar_button_(toolbar_button)
|
||||
{
|
||||
}
|
||||
|
||||
void select_toolbar_button(pp::app::CanvasToolMode) override
|
||||
{
|
||||
if (toolbar_button_)
|
||||
select_canvas_tool_button(app_.layout[app_.main_id], toolbar_button_);
|
||||
}
|
||||
|
||||
void set_transform_action(pp::app::CanvasToolTransformAction action) override
|
||||
{
|
||||
if (!app_.canvas || !app_.canvas->m_canvas)
|
||||
return;
|
||||
|
||||
if (action == pp::app::CanvasToolTransformAction::copy) {
|
||||
auto* transform = static_cast<CanvasModeTransform*>(
|
||||
app_.canvas->m_canvas->modes[(int)kCanvasMode::Copy][0]);
|
||||
transform->m_action = CanvasModeTransform::ActionType::Copy;
|
||||
} else if (action == pp::app::CanvasToolTransformAction::cut) {
|
||||
auto* transform = static_cast<CanvasModeTransform*>(
|
||||
app_.canvas->m_canvas->modes[(int)kCanvasMode::Cut][0]);
|
||||
transform->m_action = CanvasModeTransform::ActionType::Cut;
|
||||
}
|
||||
}
|
||||
|
||||
void set_canvas_mode(pp::app::CanvasToolMode mode) override
|
||||
{
|
||||
Canvas::set_mode(canvas_mode_from_tool(mode));
|
||||
}
|
||||
|
||||
void toggle_picking() override
|
||||
{
|
||||
if (!app_.canvas || !app_.canvas->m_canvas)
|
||||
return;
|
||||
|
||||
auto* mode = static_cast<CanvasModePen*>(
|
||||
app_.canvas->m_canvas->modes[(int)kCanvasMode::Draw][0]);
|
||||
if (mode)
|
||||
mode->m_picking = !mode->m_picking;
|
||||
}
|
||||
|
||||
void toggle_touch_lock() override
|
||||
{
|
||||
if (!app_.canvas || !app_.canvas->m_canvas)
|
||||
return;
|
||||
|
||||
app_.canvas->m_canvas->m_touch_lock = !app_.canvas->m_canvas->m_touch_lock;
|
||||
}
|
||||
|
||||
private:
|
||||
App& app_;
|
||||
Node* toolbar_button_ = nullptr;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
void apply_canvas_tool_select(App& app, T* button, pp::app::CanvasToolMode mode)
|
||||
{
|
||||
const auto plan = pp::app::plan_canvas_tool_select(mode);
|
||||
if (plan.selects_toolbar_button)
|
||||
select_button(app.layout[app.main_id], button);
|
||||
|
||||
if (plan.transform_action == pp::app::CanvasToolTransformAction::copy) {
|
||||
auto* transform = static_cast<CanvasModeTransform*>(
|
||||
app.canvas->m_canvas->modes[(int)kCanvasMode::Copy][0]);
|
||||
transform->m_action = CanvasModeTransform::ActionType::Copy;
|
||||
} else if (plan.transform_action == pp::app::CanvasToolTransformAction::cut) {
|
||||
auto* transform = static_cast<CanvasModeTransform*>(
|
||||
app.canvas->m_canvas->modes[(int)kCanvasMode::Cut][0]);
|
||||
transform->m_action = CanvasModeTransform::ActionType::Cut;
|
||||
}
|
||||
|
||||
if (plan.updates_canvas_mode)
|
||||
Canvas::set_mode(canvas_mode_from_tool(plan.mode));
|
||||
LegacyCanvasToolServices services(app, button);
|
||||
const auto status = pp::app::execute_canvas_tool_plan(plan, services);
|
||||
if (!status.ok())
|
||||
LOG("Canvas tool select action failed: %s", status.message);
|
||||
}
|
||||
|
||||
void App::init_toolbar_draw()
|
||||
@@ -1211,27 +1269,30 @@ void App::init_toolbar_draw()
|
||||
};
|
||||
//button->set_active(true);
|
||||
const auto plan = pp::app::plan_canvas_tool_select(pp::app::CanvasToolMode::draw);
|
||||
if (plan.updates_canvas_mode)
|
||||
Canvas::set_mode(canvas_mode_from_tool(plan.mode));
|
||||
LegacyCanvasToolServices services(*this);
|
||||
const auto status = pp::app::execute_canvas_tool_plan(plan, services);
|
||||
if (!status.ok())
|
||||
LOG("Canvas default tool action failed: %s", status.message);
|
||||
}
|
||||
if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-pick"))
|
||||
{
|
||||
button->on_click = [this](Node*) {
|
||||
CanvasModePen* mode = (CanvasModePen*)canvas->m_canvas->modes[(int)kCanvasMode::Draw][0];
|
||||
const auto plan = pp::app::plan_canvas_tool_pick_toggle(
|
||||
canvas->m_canvas->m_current_mode == kCanvasMode::Draw);
|
||||
if (mode && plan.toggles_picking)
|
||||
{
|
||||
mode->m_picking = !mode->m_picking;
|
||||
}
|
||||
LegacyCanvasToolServices services(*this);
|
||||
const auto status = pp::app::execute_canvas_tool_plan(plan, services);
|
||||
if (!status.ok())
|
||||
LOG("Canvas pick action failed: %s", status.message);
|
||||
};
|
||||
}
|
||||
if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-touchlock"))
|
||||
{
|
||||
button->on_click = [this](Node*) {
|
||||
const auto plan = pp::app::plan_canvas_tool_touch_lock_toggle();
|
||||
if (plan.toggles_touch_lock)
|
||||
canvas->m_canvas->m_touch_lock = !canvas->m_canvas->m_touch_lock;
|
||||
LegacyCanvasToolServices services(*this);
|
||||
const auto status = pp::app::execute_canvas_tool_plan(plan, services);
|
||||
if (!status.ok())
|
||||
LOG("Canvas touch-lock action failed: %s", status.message);
|
||||
};
|
||||
}
|
||||
if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-erase"))
|
||||
|
||||
@@ -37,22 +37,46 @@ void run_history_redo_if_available()
|
||||
ActionManager::redo();
|
||||
}
|
||||
|
||||
class LegacyNodeCanvasToolServices final : public pp::app::CanvasToolServices {
|
||||
public:
|
||||
void select_toolbar_button(pp::app::CanvasToolMode) override
|
||||
{
|
||||
}
|
||||
|
||||
void set_transform_action(pp::app::CanvasToolTransformAction) override
|
||||
{
|
||||
}
|
||||
|
||||
void set_canvas_mode(pp::app::CanvasToolMode mode) override
|
||||
{
|
||||
switch (mode) {
|
||||
case pp::app::CanvasToolMode::draw:
|
||||
Canvas::set_mode(kCanvasMode::Draw);
|
||||
return;
|
||||
case pp::app::CanvasToolMode::erase:
|
||||
Canvas::set_mode(kCanvasMode::Erase);
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void toggle_picking() override
|
||||
{
|
||||
}
|
||||
|
||||
void toggle_touch_lock() override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
void run_canvas_tool_mode(pp::app::CanvasToolMode mode)
|
||||
{
|
||||
const auto plan = pp::app::plan_canvas_tool_select(mode);
|
||||
if (!plan.updates_canvas_mode)
|
||||
return;
|
||||
|
||||
switch (plan.mode) {
|
||||
case pp::app::CanvasToolMode::draw:
|
||||
Canvas::set_mode(kCanvasMode::Draw);
|
||||
return;
|
||||
case pp::app::CanvasToolMode::erase:
|
||||
Canvas::set_mode(kCanvasMode::Erase);
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
LegacyNodeCanvasToolServices services;
|
||||
const auto status = pp::app::execute_canvas_tool_plan(plan, services);
|
||||
if (!status.ok())
|
||||
LOG("Canvas input tool action failed: %s", status.message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,8 +1,55 @@
|
||||
#include "app_core/canvas_tool_ui.h"
|
||||
#include "test_harness.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace {
|
||||
|
||||
class FakeCanvasToolServices final : public pp::app::CanvasToolServices {
|
||||
public:
|
||||
void select_toolbar_button(pp::app::CanvasToolMode mode) override
|
||||
{
|
||||
toolbar_selections += 1;
|
||||
last_mode = mode;
|
||||
call_order += "select;";
|
||||
}
|
||||
|
||||
void set_transform_action(pp::app::CanvasToolTransformAction action) override
|
||||
{
|
||||
transform_sets += 1;
|
||||
last_transform_action = action;
|
||||
call_order += "transform;";
|
||||
}
|
||||
|
||||
void set_canvas_mode(pp::app::CanvasToolMode mode) override
|
||||
{
|
||||
mode_sets += 1;
|
||||
last_mode = mode;
|
||||
call_order += "mode;";
|
||||
}
|
||||
|
||||
void toggle_picking() override
|
||||
{
|
||||
picking_toggles += 1;
|
||||
call_order += "pick;";
|
||||
}
|
||||
|
||||
void toggle_touch_lock() override
|
||||
{
|
||||
touch_lock_toggles += 1;
|
||||
call_order += "touch;";
|
||||
}
|
||||
|
||||
int toolbar_selections = 0;
|
||||
int transform_sets = 0;
|
||||
int mode_sets = 0;
|
||||
int picking_toggles = 0;
|
||||
int touch_lock_toggles = 0;
|
||||
pp::app::CanvasToolMode last_mode = pp::app::CanvasToolMode::draw;
|
||||
pp::app::CanvasToolTransformAction last_transform_action = pp::app::CanvasToolTransformAction::none;
|
||||
std::string call_order;
|
||||
};
|
||||
|
||||
void selection_plans_canvas_modes(pp::tests::Harness& harness)
|
||||
{
|
||||
const auto draw = pp::app::plan_canvas_tool_select(pp::app::CanvasToolMode::draw);
|
||||
@@ -81,6 +128,74 @@ void button_state_tracks_active_mode_and_toggles(pp::tests::Harness& harness)
|
||||
PP_EXPECT(harness, !bucket.mask_line_active);
|
||||
}
|
||||
|
||||
void executor_dispatches_tool_actions(pp::tests::Harness& harness)
|
||||
{
|
||||
FakeCanvasToolServices services;
|
||||
|
||||
PP_EXPECT(harness, pp::app::execute_canvas_tool_plan(
|
||||
pp::app::plan_canvas_tool_select(pp::app::CanvasToolMode::copy),
|
||||
services).ok());
|
||||
PP_EXPECT(harness, pp::app::execute_canvas_tool_plan(
|
||||
pp::app::plan_canvas_tool_pick_toggle(true),
|
||||
services).ok());
|
||||
PP_EXPECT(harness, pp::app::execute_canvas_tool_plan(
|
||||
pp::app::plan_canvas_tool_touch_lock_toggle(),
|
||||
services).ok());
|
||||
|
||||
PP_EXPECT(harness, services.toolbar_selections == 1);
|
||||
PP_EXPECT(harness, services.transform_sets == 1);
|
||||
PP_EXPECT(harness, services.mode_sets == 1);
|
||||
PP_EXPECT(harness, services.picking_toggles == 1);
|
||||
PP_EXPECT(harness, services.touch_lock_toggles == 1);
|
||||
PP_EXPECT(harness, services.last_mode == pp::app::CanvasToolMode::copy);
|
||||
PP_EXPECT(harness, services.last_transform_action == pp::app::CanvasToolTransformAction::copy);
|
||||
PP_EXPECT(harness, services.call_order == "select;transform;mode;pick;touch;");
|
||||
}
|
||||
|
||||
void executor_no_ops_pick_when_not_in_draw_mode(pp::tests::Harness& harness)
|
||||
{
|
||||
FakeCanvasToolServices services;
|
||||
|
||||
PP_EXPECT(harness, pp::app::execute_canvas_tool_plan(
|
||||
pp::app::plan_canvas_tool_pick_toggle(false),
|
||||
services).ok());
|
||||
|
||||
PP_EXPECT(harness, services.toolbar_selections == 0);
|
||||
PP_EXPECT(harness, services.mode_sets == 0);
|
||||
PP_EXPECT(harness, services.picking_toggles == 0);
|
||||
PP_EXPECT(harness, services.call_order.empty());
|
||||
}
|
||||
|
||||
void executor_rejects_malformed_plans(pp::tests::Harness& harness)
|
||||
{
|
||||
FakeCanvasToolServices services;
|
||||
|
||||
auto select = pp::app::plan_canvas_tool_select(pp::app::CanvasToolMode::copy);
|
||||
select.transform_action = pp::app::CanvasToolTransformAction::cut;
|
||||
PP_EXPECT(harness, !pp::app::execute_canvas_tool_plan(select, services).ok());
|
||||
|
||||
auto missing_update = pp::app::plan_canvas_tool_select(pp::app::CanvasToolMode::draw);
|
||||
missing_update.updates_canvas_mode = false;
|
||||
PP_EXPECT(harness, !pp::app::execute_canvas_tool_plan(missing_update, services).ok());
|
||||
|
||||
pp::app::CanvasToolPlan pick;
|
||||
pick.operation = pp::app::CanvasToolOperation::toggle_picking;
|
||||
pick.requires_draw_mode = false;
|
||||
pick.toggles_picking = true;
|
||||
PP_EXPECT(harness, !pp::app::execute_canvas_tool_plan(pick, services).ok());
|
||||
|
||||
pp::app::CanvasToolPlan touch;
|
||||
touch.operation = pp::app::CanvasToolOperation::toggle_touch_lock;
|
||||
touch.toggles_touch_lock = false;
|
||||
PP_EXPECT(harness, !pp::app::execute_canvas_tool_plan(touch, services).ok());
|
||||
|
||||
PP_EXPECT(harness, services.toolbar_selections == 0);
|
||||
PP_EXPECT(harness, services.transform_sets == 0);
|
||||
PP_EXPECT(harness, services.mode_sets == 0);
|
||||
PP_EXPECT(harness, services.picking_toggles == 0);
|
||||
PP_EXPECT(harness, services.touch_lock_toggles == 0);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main()
|
||||
@@ -90,5 +205,8 @@ int main()
|
||||
harness.run("transform tools plan copy and cut actions", transform_tools_plan_copy_and_cut_actions);
|
||||
harness.run("pick and touch lock toggle state", pick_and_touch_lock_toggle_state);
|
||||
harness.run("button state tracks active mode and toggles", button_state_tracks_active_mode_and_toggles);
|
||||
harness.run("executor dispatches tool actions", executor_dispatches_tool_actions);
|
||||
harness.run("executor no-ops pick when not in draw mode", executor_no_ops_pick_when_not_in_draw_mode);
|
||||
harness.run("executor rejects malformed plans", executor_rejects_malformed_plans);
|
||||
return harness.finish();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user