#include "pch.h" #include "legacy_canvas_tool_services.h" #include "app.h" #include "legacy_history_services.h" #include "legacy_ui_overlay_services.h" #include "node_canvas.h" namespace pp::panopainter { namespace { void set_canvas_tool_button_active(Node* button, bool active) { if (auto* custom = dynamic_cast(button)) { custom->set_active(active); return; } if (auto* regular = dynamic_cast(button)) { regular->set_active(active); } } void select_canvas_tool_button(Node* main, Node* button) { main->find("btn-pen")->set_active(false); main->find("btn-erase")->set_active(false); main->find("btn-line")->set_active(false); main->find("btn-cam")->set_active(false); main->find("btn-grid")->set_active(false); main->find("btn-copy")->set_active(false); main->find("btn-cut")->set_active(false); main->find("btn-mask-free")->set_active(false); main->find("btn-mask-line")->set_active(false); main->find("btn-bucket")->set_active(false); set_canvas_tool_button_active(button, false); } kCanvasMode canvas_mode_from_tool(pp::app::CanvasToolMode mode) { switch (mode) { case pp::app::CanvasToolMode::draw: return kCanvasMode::Draw; case pp::app::CanvasToolMode::erase: return kCanvasMode::Erase; case pp::app::CanvasToolMode::line: return kCanvasMode::Line; case pp::app::CanvasToolMode::camera: return kCanvasMode::Camera; case pp::app::CanvasToolMode::grid: return kCanvasMode::Grid; case pp::app::CanvasToolMode::copy: return kCanvasMode::Copy; case pp::app::CanvasToolMode::cut: return kCanvasMode::Cut; case pp::app::CanvasToolMode::fill: return kCanvasMode::Fill; case pp::app::CanvasToolMode::mask_free: return kCanvasMode::MaskFree; case pp::app::CanvasToolMode::mask_line: return kCanvasMode::MaskLine; case pp::app::CanvasToolMode::flood_fill: return kCanvasMode::FloodFill; } 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 (app_ && toolbar_button_) select_canvas_tool_button(app_->layout[app_->main_id], toolbar_button_); } void set_transform_action(pp::app::CanvasToolTransformAction action) override { if (!app_ || !app_->canvas || !app_->canvas->m_canvas) return; if (action == pp::app::CanvasToolTransformAction::copy) { auto* transform = static_cast( 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( 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_ || !app_->canvas || !app_->canvas->m_canvas) return; auto* mode = static_cast( app_->canvas->m_canvas->modes[(int)kCanvasMode::Draw][0]); if (mode) mode->m_picking = !mode->m_picking; } void toggle_touch_lock() override { if (!app_ || !app_->canvas || !app_->canvas->m_canvas) return; app_->canvas->m_canvas->m_touch_lock = !app_->canvas->m_canvas->m_touch_lock; } private: App* app_ = nullptr; Node* toolbar_button_ = nullptr; }; class LegacyCanvasInputToolServices 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 { } }; class LegacyCanvasHotkeyServices final : public pp::app::CanvasHotkeyServices { public: pp::foundation::Status execute_tool(const pp::app::CanvasToolPlan& plan) override { return execute_legacy_canvas_input_tool_plan(plan); } pp::foundation::Status execute_history(const pp::app::HistoryUiPlan& plan) override { return execute_legacy_history_plan(plan); } void save_document(pp::app::DocumentSaveIntent intent) override { if (App::I) App::I->save_document(intent); } void toggle_ui() override { if (App::I) App::I->toggle_ui(); } void adjust_brush_size(float delta) override { if (!App::I || !App::I->stroke || !App::I->stroke->m_tip_size) return; const float value = App::I->stroke->m_tip_size->get_value(); const float next_value = glm::clamp(value + delta, 0.0F, 1.0F); App::I->stroke->set_size(next_value, true, true); } void show_cursor() override { if (App::I) App::I->show_cursor(); } }; pp::app::CanvasHotkeyKey canvas_hotkey_key(kKey key) noexcept { switch (key) { case kKey::AndroidBack: return pp::app::CanvasHotkeyKey::android_back; case kKey::KeyAlt: return pp::app::CanvasHotkeyKey::alt; case kKey::KeyE: return pp::app::CanvasHotkeyKey::e; case kKey::KeyS: return pp::app::CanvasHotkeyKey::s; case kKey::KeyTab: return pp::app::CanvasHotkeyKey::tab; case kKey::KeyZ: return pp::app::CanvasHotkeyKey::z; case kKey::KeyBracketLeft: return pp::app::CanvasHotkeyKey::bracket_left; case kKey::KeyBracketRight: return pp::app::CanvasHotkeyKey::bracket_right; default: return pp::app::CanvasHotkeyKey::other; } } pp::app::CanvasHotkeyState canvas_hotkey_state(bool mouse_focused, int touch_finger_count = 0) noexcept { pp::app::CanvasHotkeyState state; state.ctrl_down = App::I && App::I->keys[(int)kKey::KeyCtrl]; state.shift_down = App::I && App::I->keys[(int)kKey::KeyShift]; state.mouse_focused = mouse_focused; const auto history = pp::panopainter::legacy_history_snapshot(); state.undo_count = history.undo_count; state.redo_count = history.redo_count; state.touch_finger_count = touch_finger_count; return state; } void execute_canvas_hotkey_plan(const pp::app::CanvasHotkeyPlan& plan) { const auto status = pp::panopainter::execute_legacy_canvas_hotkey_plan(plan); if (!status.ok()) LOG("Canvas hotkey action failed: %s", status.message); } void run_canvas_hotkey( pp::app::CanvasHotkeyEvent event, kKey key, bool mouse_focused, int touch_finger_count = 0) { const auto plan = pp::app::plan_canvas_hotkey( event, canvas_hotkey_key(key), canvas_hotkey_state(mouse_focused, touch_finger_count)); if (plan) execute_canvas_hotkey_plan(plan.value()); else LOG("Canvas hotkey planning failed: %s", plan.status().message); } void run_canvas_tool_mode(pp::app::CanvasToolMode mode) { const auto plan = pp::app::plan_canvas_tool_select(mode); const auto status = pp::panopainter::execute_legacy_canvas_input_tool_plan(plan); if (!status.ok()) LOG("Canvas input tool action failed: %s", status.message); } [[nodiscard]] kEventResult execute_node_canvas_handle_event(NodeCanvas& node_canvas, Event* e) { static bool stylus_eraser = false; MouseEvent* me = static_cast(e); KeyEvent* ke = static_cast(e); GestureEvent* ge = static_cast(e); TouchEvent* te = static_cast(e); auto loc = (me->m_pos - node_canvas.m_pos) * node_canvas.root()->m_zoom; switch (e->m_type) { case kEventType::MouseMove: if (stylus_eraser != me->m_eraser) { run_canvas_tool_mode(me->m_eraser ? pp::app::CanvasToolMode::erase : pp::app::CanvasToolMode::draw); stylus_eraser = me->m_eraser; } case kEventType::MouseScroll: case kEventType::MouseDownL: case kEventType::MouseUpL: case kEventType::MouseDownR: case kEventType::MouseUpR: case kEventType::MouseCancel: node_canvas.m_canvas->m_cur_pos = loc; node_canvas.update_cursor(); for (auto& mode : *node_canvas.m_canvas->m_mode) mode->on_MouseEvent(me, loc); break; case kEventType::MouseUnfocus: (*node_canvas.m_canvas->m_mode)[0]->m_draw_tip = false; App::I->show_cursor(); break; case kEventType::MouseFocus: node_canvas.update_cursor(); break; case kEventType::KeyDown: run_canvas_hotkey( pp::app::CanvasHotkeyEvent::key_down, ke->m_key, node_canvas.m_mouse_focus); for (auto& mode : *node_canvas.m_canvas->m_mode) mode->on_KeyEvent(ke); break; case kEventType::KeyUp: node_canvas.update_cursor(); run_canvas_hotkey( pp::app::CanvasHotkeyEvent::key_up, ke->m_key, node_canvas.m_mouse_focus); for (auto& mode : *node_canvas.m_canvas->m_mode) mode->on_KeyEvent(ke); break; case kEventType::GestureStart: node_canvas.mouse_capture(); for (auto& mode : *node_canvas.m_canvas->m_mode) mode->on_GestureEvent(ge); break; case kEventType::GestureMove: for (auto& mode : *node_canvas.m_canvas->m_mode) mode->on_GestureEvent(ge); break; case kEventType::GestureEnd: pp::panopainter::release_legacy_mouse_capture(node_canvas); for (auto& mode : *node_canvas.m_canvas->m_mode) mode->on_GestureEvent(ge); break; case kEventType::TouchTap: run_canvas_hotkey( pp::app::CanvasHotkeyEvent::touch_tap, kKey::Unknown, node_canvas.m_mouse_focus, te->m_finger_count); break; default: return kEventResult::Available; break; } return kEventResult::Consumed; } } // namespace pp::foundation::Status execute_legacy_canvas_tool_plan( App& app, const pp::app::CanvasToolPlan& plan, Node* toolbar_button) { LegacyCanvasToolServices services(&app, toolbar_button); return pp::app::execute_canvas_tool_plan(plan, services); } pp::foundation::Status execute_legacy_canvas_input_tool_plan( const pp::app::CanvasToolPlan& plan) { LegacyCanvasInputToolServices services; return pp::app::execute_canvas_tool_plan(plan, services); } pp::foundation::Status execute_legacy_canvas_hotkey_plan( const pp::app::CanvasHotkeyPlan& plan) { LegacyCanvasHotkeyServices services; return pp::app::execute_canvas_hotkey_plan(plan, services); } kEventResult handle_legacy_node_canvas_event(NodeCanvas& node_canvas, Event* e) { return execute_node_canvas_handle_event(node_canvas, e); } } // namespace pp::panopainter