#pragma once #include "foundation/result.h" namespace pp::app { enum class CanvasToolOperation { select_mode, toggle_picking, toggle_touch_lock, }; enum class CanvasToolMode { draw, erase, line, camera, grid, copy, cut, fill, mask_free, mask_line, flood_fill, }; enum class CanvasToolTransformAction { none, copy, cut, }; struct CanvasToolPlan { CanvasToolOperation operation = CanvasToolOperation::select_mode; CanvasToolMode mode = CanvasToolMode::draw; CanvasToolTransformAction transform_action = CanvasToolTransformAction::none; bool selects_toolbar_button = false; bool updates_canvas_mode = false; bool toggles_picking = false; bool toggles_touch_lock = false; bool requires_draw_mode = false; bool no_op = false; }; struct CanvasToolButtonState { CanvasToolMode mode = CanvasToolMode::draw; bool pick_active = false; bool touch_lock_active = false; bool pen_active = false; bool erase_active = false; bool line_active = false; bool camera_active = false; bool grid_active = false; bool copy_active = false; bool cut_active = false; bool fill_active = false; bool mask_free_active = false; bool mask_line_active = false; 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) { return CanvasToolTransformAction::copy; } if (mode == CanvasToolMode::cut) { return CanvasToolTransformAction::cut; } return CanvasToolTransformAction::none; } [[nodiscard]] inline constexpr CanvasToolPlan plan_canvas_tool_select(CanvasToolMode mode) noexcept { CanvasToolPlan plan; plan.operation = CanvasToolOperation::select_mode; plan.mode = mode; plan.transform_action = transform_action_for_mode(mode); plan.selects_toolbar_button = true; plan.updates_canvas_mode = true; return plan; } [[nodiscard]] inline constexpr CanvasToolPlan plan_canvas_tool_pick_toggle(bool current_mode_is_draw) noexcept { CanvasToolPlan plan; plan.operation = CanvasToolOperation::toggle_picking; plan.mode = CanvasToolMode::draw; plan.requires_draw_mode = true; plan.toggles_picking = current_mode_is_draw; plan.no_op = !current_mode_is_draw; return plan; } [[nodiscard]] inline constexpr CanvasToolPlan plan_canvas_tool_touch_lock_toggle() noexcept { CanvasToolPlan plan; plan.operation = CanvasToolOperation::toggle_touch_lock; plan.toggles_touch_lock = true; return plan; } [[nodiscard]] inline constexpr CanvasToolButtonState plan_canvas_tool_button_state( CanvasToolMode mode, bool picking, bool touch_lock) noexcept { CanvasToolButtonState state; state.mode = mode; state.pick_active = mode == CanvasToolMode::draw && picking; state.touch_lock_active = touch_lock; state.pen_active = mode == CanvasToolMode::draw; state.erase_active = mode == CanvasToolMode::erase; state.line_active = mode == CanvasToolMode::line; state.camera_active = mode == CanvasToolMode::camera; state.grid_active = mode == CanvasToolMode::grid; state.copy_active = mode == CanvasToolMode::copy; state.cut_active = mode == CanvasToolMode::cut; state.fill_active = mode == CanvasToolMode::fill; state.mask_free_active = mode == CanvasToolMode::mask_free; state.mask_line_active = mode == CanvasToolMode::mask_line; state.flood_fill_active = mode == CanvasToolMode::flood_fill; 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