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:
parent
85bfeba8c6
commit
a30b956e94
2 changed files with 80 additions and 35 deletions
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue