1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-23 17:45:07 +00:00
serenity/Userland/Applications/PixelPaint/RectangleTool.cpp
Mustafa Quraish d6348e1b32 PixelPaint: Use correct thickness in RectangleTool::on_second_paint()
Previously, we were ignoring the scale of the editor in the second
paint step. If you were zoomed in, the size while you were drawing
was not the same as the size of the final shape.
2021-09-07 16:53:40 +02:00

173 lines
5.6 KiB
C++

/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2021, Mustafa Quraish <mustafa@cs.toronto.edu>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "RectangleTool.h"
#include "ImageEditor.h"
#include "Layer.h"
#include <LibGUI/Action.h>
#include <LibGUI/BoxLayout.h>
#include <LibGUI/Label.h>
#include <LibGUI/Menu.h>
#include <LibGUI/Painter.h>
#include <LibGUI/RadioButton.h>
#include <LibGUI/ValueSlider.h>
#include <LibGfx/Rect.h>
namespace PixelPaint {
RectangleTool::RectangleTool()
{
}
RectangleTool::~RectangleTool()
{
}
void RectangleTool::draw_using(GUI::Painter& painter, Gfx::IntPoint const& start_position, Gfx::IntPoint const& end_position, int thickness)
{
Gfx::IntRect rect;
if (m_draw_mode == DrawMode::FromCenter) {
auto delta = end_position - start_position;
rect = Gfx::IntRect::from_two_points(start_position - delta, end_position);
} else {
rect = Gfx::IntRect::from_two_points(start_position, end_position);
}
switch (m_fill_mode) {
case FillMode::Fill:
painter.fill_rect(rect, m_editor->color_for(m_drawing_button));
break;
case FillMode::Outline:
painter.draw_rect_with_thickness(rect, m_editor->color_for(m_drawing_button), thickness);
break;
case FillMode::Gradient:
painter.fill_rect_with_gradient(rect, m_editor->primary_color(), m_editor->secondary_color());
break;
default:
VERIFY_NOT_REACHED();
}
}
void RectangleTool::on_mousedown(Layer* layer, MouseEvent& event)
{
if (!layer)
return;
auto& layer_event = event.layer_event();
if (layer_event.button() != GUI::MouseButton::Left && layer_event.button() != GUI::MouseButton::Right)
return;
if (m_drawing_button != GUI::MouseButton::None)
return;
m_drawing_button = layer_event.button();
m_rectangle_start_position = layer_event.position();
m_rectangle_end_position = layer_event.position();
m_editor->update();
}
void RectangleTool::on_mouseup(Layer* layer, MouseEvent& event)
{
if (!layer)
return;
if (event.layer_event().button() == m_drawing_button) {
GUI::Painter painter(layer->bitmap());
draw_using(painter, m_rectangle_start_position, m_rectangle_end_position, m_thickness);
m_drawing_button = GUI::MouseButton::None;
layer->did_modify_bitmap();
m_editor->did_complete_action();
}
}
void RectangleTool::on_mousemove(Layer* layer, MouseEvent& event)
{
if (!layer)
return;
if (m_drawing_button == GUI::MouseButton::None)
return;
m_draw_mode = event.layer_event().alt() ? DrawMode::FromCenter : DrawMode::FromCorner;
m_rectangle_end_position = event.layer_event().position();
m_editor->update();
}
void RectangleTool::on_second_paint(Layer const* layer, GUI::PaintEvent& event)
{
if (!layer || m_drawing_button == GUI::MouseButton::None)
return;
GUI::Painter painter(*m_editor);
painter.add_clip_rect(event.rect());
auto start_position = m_editor->layer_position_to_editor_position(*layer, m_rectangle_start_position).to_type<int>();
auto end_position = m_editor->layer_position_to_editor_position(*layer, m_rectangle_end_position).to_type<int>();
draw_using(painter, start_position, end_position);
}
void RectangleTool::on_keydown(GUI::KeyEvent& event)
{
if (event.key() == Key_Escape && m_drawing_button != GUI::MouseButton::None) {
m_drawing_button = GUI::MouseButton::None;
m_editor->update();
event.accept();
}
}
GUI::Widget* RectangleTool::get_properties_widget()
{
if (!m_properties_widget) {
m_properties_widget = GUI::Widget::construct();
m_properties_widget->set_layout<GUI::VerticalBoxLayout>();
auto& thickness_container = m_properties_widget->add<GUI::Widget>();
thickness_container.set_fixed_height(20);
thickness_container.set_layout<GUI::HorizontalBoxLayout>();
auto& thickness_label = thickness_container.add<GUI::Label>("Thickness:");
thickness_label.set_text_alignment(Gfx::TextAlignment::CenterLeft);
thickness_label.set_fixed_size(80, 20);
auto& thickness_slider = thickness_container.add<GUI::ValueSlider>(Orientation::Horizontal, "px");
thickness_slider.set_range(1, 10);
thickness_slider.set_value(m_thickness);
thickness_slider.on_change = [&](int value) {
m_thickness = value;
};
auto& mode_container = m_properties_widget->add<GUI::Widget>();
mode_container.set_fixed_height(70);
mode_container.set_layout<GUI::HorizontalBoxLayout>();
auto& mode_label = mode_container.add<GUI::Label>("Mode:");
mode_label.set_text_alignment(Gfx::TextAlignment::CenterLeft);
mode_label.set_fixed_size(80, 20);
auto& mode_radio_container = mode_container.add<GUI::Widget>();
mode_radio_container.set_layout<GUI::VerticalBoxLayout>();
auto& outline_mode_radio = mode_radio_container.add<GUI::RadioButton>("Outline");
auto& fill_mode_radio = mode_radio_container.add<GUI::RadioButton>("Fill");
auto& gradient_mode_radio = mode_radio_container.add<GUI::RadioButton>("Gradient");
outline_mode_radio.on_checked = [&](bool) {
m_fill_mode = FillMode::Outline;
};
fill_mode_radio.on_checked = [&](bool) {
m_fill_mode = FillMode::Fill;
};
gradient_mode_radio.on_checked = [&](bool) {
m_fill_mode = FillMode::Gradient;
};
outline_mode_radio.set_checked(true);
}
return m_properties_widget.ptr();
}
}