1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 16:37:35 +00:00

PixelPaint: Allow move tool to scale in all directions

It is now possible to scale the current layer using the move tool from
all four corners of the layer boundary. Previously scaling was only
possible from the bottom right of the image.
This commit is contained in:
Tim Ledbetter 2022-12-20 04:31:45 +00:00 committed by Sam Atkins
parent 85bfeba8c6
commit a30b956e94
2 changed files with 80 additions and 35 deletions

View file

@ -33,12 +33,13 @@ void MoveTool::on_mousedown(Layer* layer, MouseEvent& event)
auto& image_event = event.image_event();
if (layer_event.button() != GUI::MouseButton::Primary)
return;
if (!layer->rect().contains(layer_event.position()) && !m_mouse_in_resize_corner)
if (!layer->rect().contains(layer_event.position()) && !m_resize_anchor_location.has_value())
return;
m_scaling = m_mouse_in_resize_corner;
m_scaling = m_resize_anchor_location.has_value();
m_layer_being_moved = *layer;
m_event_origin = image_event.position();
m_layer_origin = layer->location();
m_new_layer_rect = m_editor->active_layer()->rect();
}
void MoveTool::on_mousemove(Layer* layer, MouseEvent& event)
@ -51,37 +52,49 @@ void MoveTool::on_mousemove(Layer* layer, MouseEvent& event)
if (!layer)
return;
constexpr int sensitivity = 20;
auto bottom_right_layer_coordinates = layer->relative_rect().bottom_right().translated(1);
auto bottom_right_frame_coordinates = m_editor->content_to_frame_position(bottom_right_layer_coordinates).to_rounded<int>();
auto grab_rect_top_left = bottom_right_frame_coordinates.translated(-sensitivity / 2);
auto grab_rect = Gfx::IntRect(grab_rect_top_left, Gfx::IntSize(sensitivity, sensitivity));
auto updated_is_in_lower_right_corner = grab_rect.contains(event.raw_event().position()); // check if the mouse is in the lower right corner
if (m_mouse_in_resize_corner != updated_is_in_lower_right_corner) {
m_mouse_in_resize_corner = updated_is_in_lower_right_corner;
m_editor->update_tool_cursor();
if (!m_scaling) {
auto current_resize_anchor_location = resize_anchor_location_from_cursor_position(layer, event);
if (m_resize_anchor_location != current_resize_anchor_location) {
m_resize_anchor_location = current_resize_anchor_location;
m_editor->update_tool_cursor();
}
}
if (!m_layer_being_moved)
return;
auto cursor_position = event.image_event().position();
auto delta = cursor_position - m_event_origin;
auto rect_being_moved = m_layer_being_moved->relative_rect();
Gfx::IntPoint scaling_origin;
Gfx::IntPoint opposite_corner;
if (m_scaling) {
auto cursor_location = event.image_event().position();
auto width = abs(m_layer_being_moved->location().x() - cursor_location.x());
auto height = abs(m_layer_being_moved->location().y() - cursor_location.y());
if (m_keep_ascept_ratio) {
if (abs(width - m_layer_being_moved->size().width()) > abs(height - m_layer_being_moved->size().height())) {
height = width * m_layer_being_moved->size().height() / m_layer_being_moved->size().width();
} else {
width = height * m_layer_being_moved->size().width() / m_layer_being_moved->size().height();
}
VERIFY(m_resize_anchor_location.has_value());
switch (m_resize_anchor_location.value()) {
case ResizeAnchorLocation::TopLeft:
scaling_origin = rect_being_moved.top_left();
opposite_corner = rect_being_moved.bottom_right().translated(1, 1);
break;
case ResizeAnchorLocation::BottomRight:
scaling_origin = rect_being_moved.bottom_right().translated(1, 1);
opposite_corner = rect_being_moved.top_left();
break;
case ResizeAnchorLocation::BottomLeft:
scaling_origin = rect_being_moved.bottom_left().translated(0, 1);
opposite_corner = rect_being_moved.top_right().translated(1, 0);
break;
case ResizeAnchorLocation::TopRight:
scaling_origin = rect_being_moved.top_right().translated(1, 0);
opposite_corner = rect_being_moved.bottom_left().translated(0, 1);
break;
}
m_new_layer_size = Gfx::IntSize(width, height);
// TODO: Change this according to which direction the user is scaling
m_new_scaled_layer_location = m_layer_being_moved->location();
scaling_origin.translate_by(delta);
if (m_keep_ascept_ratio) {
auto aspect_ratio = m_layer_being_moved->size().aspect_ratio();
scaling_origin = opposite_corner.end_point_for_aspect_ratio(scaling_origin, aspect_ratio);
}
m_new_layer_rect = Gfx::IntRect::from_two_points(scaling_origin, opposite_corner);
} else {
auto& image_event = event.image_event();
auto delta = image_event.position() - m_event_origin;
m_layer_being_moved->set_location(m_layer_origin.translated(delta));
}
m_editor->update();
@ -103,7 +116,7 @@ void MoveTool::on_mouseup(Layer* layer, MouseEvent& event)
return;
if (m_scaling) {
auto resized_or_error = m_editor->active_layer()->resize(m_new_layer_size, m_new_scaled_layer_location, Gfx::Painter::ScalingMode::BilinearBlend);
auto resized_or_error = m_editor->active_layer()->resize(m_new_layer_rect, Gfx::Painter::ScalingMode::BilinearBlend);
if (resized_or_error.is_error())
GUI::MessageBox::show_error(m_editor->window(), MUST(String::formatted("Failed to resize layer: {}", resized_or_error.error().string_literal())));
else
@ -169,10 +182,7 @@ void MoveTool::on_second_paint(Layer const* layer, GUI::PaintEvent& event)
GUI::Painter painter(*m_editor);
painter.add_clip_rect(event.rect());
painter.translate(editor_layer_location(*layer));
auto dst_rect = Gfx::IntRect(Gfx::IntPoint(0, 0), m_scaling ? m_new_layer_size : layer->size());
auto rect_in_editor = m_editor->content_to_frame_rect(dst_rect).to_rounded<int>();
auto rect_in_editor = m_editor->content_to_frame_rect(m_new_layer_rect).to_rounded<int>();
painter.draw_rect(rect_in_editor, Color::Black);
if (!m_cached_preview_bitmap.is_null() || !update_cached_preview_bitmap(layer).is_error()) {
painter.add_clip_rect(m_editor->content_rect());
@ -194,10 +204,39 @@ ErrorOr<void> MoveTool::update_cached_preview_bitmap(Layer const* layer)
return {};
}
Optional<ResizeAnchorLocation const> MoveTool::resize_anchor_location_from_cursor_position(Layer const* layer, MouseEvent& event)
{
auto cursor_within_resize_anchor_rect = [&](Gfx::IntPoint layer_position) {
constexpr int sensitivity = 20;
auto resize_anchor_rect_center = m_editor->content_to_frame_position(layer_position).to_rounded<int>();
auto resize_anchor_rect_top_left = resize_anchor_rect_center.translated(-sensitivity / 2);
auto resize_anchor_rect = Gfx::IntRect(resize_anchor_rect_top_left, Gfx::IntSize(sensitivity, sensitivity));
return resize_anchor_rect.contains(event.raw_event().position());
};
auto layer_rect = layer->relative_rect();
if (cursor_within_resize_anchor_rect(layer_rect.top_left()))
return ResizeAnchorLocation::TopLeft;
if (cursor_within_resize_anchor_rect(layer_rect.top_right().translated(1, 0)))
return ResizeAnchorLocation::TopRight;
if (cursor_within_resize_anchor_rect(layer_rect.bottom_left().translated(0, 1)))
return ResizeAnchorLocation::BottomLeft;
if (cursor_within_resize_anchor_rect(layer_rect.bottom_right().translated(1)))
return ResizeAnchorLocation::BottomRight;
return {};
}
Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap>> MoveTool::cursor()
{
if (m_mouse_in_resize_corner || m_scaling)
return Gfx::StandardCursor::ResizeDiagonalTLBR;
if (m_resize_anchor_location.has_value()) {
switch (m_resize_anchor_location.value()) {
case ResizeAnchorLocation::TopLeft:
case ResizeAnchorLocation::BottomRight:
return Gfx::StandardCursor::ResizeDiagonalTLBR;
case ResizeAnchorLocation::BottomLeft:
case ResizeAnchorLocation::TopRight:
return Gfx::StandardCursor::ResizeDiagonalBLTR;
}
}
return Gfx::StandardCursor::Move;
}