From a30b956e94f25084f8411ee65399c68897341f20 Mon Sep 17 00:00:00 2001 From: Tim Ledbetter Date: Tue, 20 Dec 2022 04:31:45 +0000 Subject: [PATCH] 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. --- .../PixelPaint/Tools/MoveTool.cpp | 103 ++++++++++++------ .../Applications/PixelPaint/Tools/MoveTool.h | 12 +- 2 files changed, 80 insertions(+), 35 deletions(-) diff --git a/Userland/Applications/PixelPaint/Tools/MoveTool.cpp b/Userland/Applications/PixelPaint/Tools/MoveTool.cpp index 89a1e8db36..c01ebdc0af 100644 --- a/Userland/Applications/PixelPaint/Tools/MoveTool.cpp +++ b/Userland/Applications/PixelPaint/Tools/MoveTool.cpp @@ -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(); - 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(); + auto rect_in_editor = m_editor->content_to_frame_rect(m_new_layer_rect).to_rounded(); 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 MoveTool::update_cached_preview_bitmap(Layer const* layer) return {}; } +Optional 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(); + 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> 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; } diff --git a/Userland/Applications/PixelPaint/Tools/MoveTool.h b/Userland/Applications/PixelPaint/Tools/MoveTool.h index 516c2ac2fe..9c1c80946d 100644 --- a/Userland/Applications/PixelPaint/Tools/MoveTool.h +++ b/Userland/Applications/PixelPaint/Tools/MoveTool.h @@ -12,6 +12,12 @@ namespace PixelPaint { +enum class ResizeAnchorLocation { + BottomLeft, + BottomRight, + TopLeft, + TopRight +}; class MoveTool final : public Tool { public: MoveTool() = default; @@ -28,14 +34,14 @@ public: private: virtual StringView tool_name() const override { return "Move Tool"sv; } ErrorOr update_cached_preview_bitmap(Layer const* layer); + Optional resize_anchor_location_from_cursor_position(Layer const*, MouseEvent&); RefPtr m_layer_being_moved; Gfx::IntPoint m_event_origin; Gfx::IntPoint m_layer_origin; - Gfx::IntPoint m_new_scaled_layer_location; - Gfx::IntSize m_new_layer_size; + Gfx::IntRect m_new_layer_rect; bool m_scaling { false }; - bool m_mouse_in_resize_corner { false }; + Optional m_resize_anchor_location {}; bool m_keep_ascept_ratio { false }; RefPtr m_cached_preview_bitmap { nullptr };