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 };