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.
This commit is contained in:
Tobias Christiansen 2021-08-05 14:57:38 +02:00 committed by Ali Mohammad Pur
parent d4cf4b74c1
commit 7923929a4d
3 changed files with 171 additions and 0 deletions

View file

@ -17,6 +17,7 @@ set(SOURCES
CreateNewLayerDialog.cpp
EllipseTool.cpp
EraseTool.cpp
GuideTool.cpp
Image.cpp
ImageEditor.cpp
Layer.cpp

View file

@ -0,0 +1,135 @@
/*
* Copyright (c) 2021, Tobias Christiansen <tobi@tobyase.de>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "GuideTool.h"
#include "ImageEditor.h"
#include <LibGUI/Menu.h>
namespace PixelPaint {
GuideTool::GuideTool()
{
}
GuideTool::~GuideTool()
{
}
RefPtr<Guide> GuideTool::closest_guide(const Gfx::IntPoint& point)
{
auto guides = editor()->guides();
Guide* closest_guide = nullptr;
int closest_guide_distance = NumericLimits<int>::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<Guide> 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());
}
}

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2021, Tobias Christiansen <tobi@tobyase.de>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include "Guide.h"
#include "Tool.h"
#include <AK/RefPtr.h>
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<Guide> closest_guide(Gfx::IntPoint const&);
RefPtr<Guide> m_selected_guide;
RefPtr<Guide> m_context_menu_guide;
Gfx::IntPoint m_event_origin;
float m_guide_origin { 0 };
RefPtr<GUI::Menu> m_context_menu;
};
}