From 7923929a4d65c43b145150c54132076492fbe8f0 Mon Sep 17 00:00:00 2001 From: Tobias Christiansen Date: Thu, 5 Aug 2021 14:57:38 +0200 Subject: [PATCH] PixelPaint: Add GuideTool for editing Guides This patch adds the logic for the GuideTool. Pulling in from outside the image creates a new Guide, moving a Guide outside of the image deletes it and a Guide can be deleteted via the context menu. A Guide is considered clicked when the cursor is less than 20 pixels away from the line. --- .../Applications/PixelPaint/CMakeLists.txt | 1 + .../Applications/PixelPaint/GuideTool.cpp | 135 ++++++++++++++++++ Userland/Applications/PixelPaint/GuideTool.h | 35 +++++ 3 files changed, 171 insertions(+) create mode 100644 Userland/Applications/PixelPaint/GuideTool.cpp create mode 100644 Userland/Applications/PixelPaint/GuideTool.h diff --git a/Userland/Applications/PixelPaint/CMakeLists.txt b/Userland/Applications/PixelPaint/CMakeLists.txt index 823ac3c4d17..3abfd4bd724 100644 --- a/Userland/Applications/PixelPaint/CMakeLists.txt +++ b/Userland/Applications/PixelPaint/CMakeLists.txt @@ -17,6 +17,7 @@ set(SOURCES CreateNewLayerDialog.cpp EllipseTool.cpp EraseTool.cpp + GuideTool.cpp Image.cpp ImageEditor.cpp Layer.cpp diff --git a/Userland/Applications/PixelPaint/GuideTool.cpp b/Userland/Applications/PixelPaint/GuideTool.cpp new file mode 100644 index 00000000000..4e630067fe5 --- /dev/null +++ b/Userland/Applications/PixelPaint/GuideTool.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2021, Tobias Christiansen + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "GuideTool.h" +#include "ImageEditor.h" +#include + +namespace PixelPaint { + +GuideTool::GuideTool() +{ +} + +GuideTool::~GuideTool() +{ +} + +RefPtr GuideTool::closest_guide(const Gfx::IntPoint& point) +{ + auto guides = editor()->guides(); + Guide* closest_guide = nullptr; + int closest_guide_distance = NumericLimits::max(); + + for (auto& guide : guides) { + int relevant_position = 0; + if (guide.orientation() == Guide::Orientation::Horizontal) + relevant_position = point.y(); + else if (guide.orientation() == Guide::Orientation::Vertical) + relevant_position = point.x(); + + auto distance = abs(relevant_position - (int)guide.offset()); + + if (distance < closest_guide_distance) { + closest_guide = &guide; + closest_guide_distance = distance; + } + } + + if (closest_guide_distance < 20) + return closest_guide; + return nullptr; +} + +void GuideTool::on_mousedown(Layer&, GUI::MouseEvent& mouse_event, GUI::MouseEvent& image_event) +{ + if (!m_editor) + return; + + if (mouse_event.button() != GUI::MouseButton::Left) + return; + + RefPtr new_guide; + if (image_event.position().x() < 0 || image_event.position().x() > editor()->image().size().width()) { + new_guide = Guide::construct(Guide::Orientation::Vertical, image_event.position().x()); + } else if (image_event.position().y() < 0 || image_event.position().y() > editor()->image().size().height()) { + new_guide = Guide::construct(Guide::Orientation::Horizontal, image_event.position().y()); + } + + if (new_guide) { + m_selected_guide = new_guide; + m_guide_origin = 0; + editor()->add_guide(new_guide.release_nonnull()); + return; + } + + m_event_origin = image_event.position(); + + m_selected_guide = closest_guide(image_event.position()); + + if (m_selected_guide) + m_guide_origin = m_selected_guide->offset(); +} + +void GuideTool::on_mouseup(Layer&, GUI::MouseEvent&, GUI::MouseEvent&) +{ + m_guide_origin = 0; + m_event_origin = { 0, 0 }; + + if (!m_selected_guide) + return; + + if (m_selected_guide->offset() < 0 + || (m_selected_guide->orientation() == Guide::Orientation::Horizontal && m_selected_guide->offset() > editor()->image().size().height()) + || (m_selected_guide->orientation() == Guide::Orientation::Vertical && m_selected_guide->offset() > editor()->image().size().width())) { + editor()->remove_guide(*m_selected_guide); + editor()->layers_did_change(); + } + + m_selected_guide = nullptr; +} + +void GuideTool::on_mousemove(Layer&, GUI::MouseEvent&, GUI::MouseEvent& image_event) +{ + if (!m_selected_guide) + return; + + auto delta = image_event.position() - m_event_origin; + + auto relevant_offset = 0; + if (m_selected_guide->orientation() == Guide::Orientation::Horizontal) + relevant_offset = delta.y(); + else if (m_selected_guide->orientation() == Guide::Orientation::Vertical) + relevant_offset = delta.x(); + + auto new_offset = (float)relevant_offset + m_guide_origin; + m_selected_guide->set_offset(new_offset); + + editor()->layers_did_change(); +} + +void GuideTool::on_context_menu(Layer&, GUI::ContextMenuEvent& event) +{ + if (!m_context_menu) { + m_context_menu = GUI::Menu::construct(); + m_context_menu->add_action(GUI::Action::create( + "&Delete Guide", Gfx::Bitmap::try_load_from_file("/res/icons/16x16/delete.png"), [this](auto&) { + if (!m_context_menu_guide) + return; + editor()->remove_guide(*m_context_menu_guide); + m_selected_guide = nullptr; + m_guide_origin = 0; + editor()->layers_did_change(); + }, + editor())); + } + + auto image_position = editor()->editor_position_to_image_position(event.position()); + m_context_menu_guide = closest_guide({ (int)image_position.x(), (int)image_position.y() }); + m_context_menu->popup(event.screen_position()); +} + +} diff --git a/Userland/Applications/PixelPaint/GuideTool.h b/Userland/Applications/PixelPaint/GuideTool.h new file mode 100644 index 00000000000..02f2d4543ac --- /dev/null +++ b/Userland/Applications/PixelPaint/GuideTool.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021, Tobias Christiansen + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include "Guide.h" +#include "Tool.h" +#include + +namespace PixelPaint { + +class GuideTool final : public Tool { +public: + GuideTool(); + + virtual ~GuideTool() override; + + virtual void on_mousedown(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; + virtual void on_mousemove(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; + virtual void on_mouseup(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; + virtual void on_context_menu(Layer&, GUI::ContextMenuEvent&) override; + +private: + RefPtr closest_guide(Gfx::IntPoint const&); + + RefPtr m_selected_guide; + RefPtr m_context_menu_guide; + Gfx::IntPoint m_event_origin; + float m_guide_origin { 0 }; + RefPtr m_context_menu; +}; +}