diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 8a9118a03ed..5ac5dd6ee6a 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -1344,22 +1344,33 @@ bool CanvasItemEditor::_gui_input_pivot(const Ref &p_event) { // Drag the pivot (in pivot mode / with V key) if (drag_type == DRAG_NONE) { + bool move_temp_pivot = ((b.is_valid() && b->is_shift_pressed()) || (k.is_valid() && k->is_shift_pressed())); + if ((b.is_valid() && b->is_pressed() && b->get_button_index() == MouseButton::LEFT && tool == TOOL_EDIT_PIVOT) || - (k.is_valid() && k->is_pressed() && !k->is_echo() && k->get_keycode() == Key::V && tool == TOOL_SELECT && k->get_modifiers_mask().is_empty())) { + (k.is_valid() && k->is_pressed() && !k->is_echo() && k->get_keycode() == Key::V && tool == TOOL_SELECT && (k->get_modifiers_mask().is_empty() || move_temp_pivot))) { List selection = _get_edited_canvas_items(); // Filters the selection with nodes that allow setting the pivot drag_selection = List(); for (CanvasItem *ci : selection) { - if (ci->_edit_use_pivot()) { + if (ci->_edit_use_pivot() || move_temp_pivot) { drag_selection.push_back(ci); } } // Start dragging if we still have nodes if (drag_selection.size() > 0) { + Vector2 event_pos = (b.is_valid()) ? b->get_position() : viewport->get_local_mouse_position(); + + if (move_temp_pivot) { + drag_type = DRAG_TEMP_PIVOT; + temp_pivot = transform.affine_inverse().xform(event_pos); + viewport->queue_redraw(); + return true; + } + _save_canvas_item_state(drag_selection); - drag_from = transform.affine_inverse().xform((b.is_valid()) ? b->get_position() : viewport->get_local_mouse_position()); + drag_from = transform.affine_inverse().xform(event_pos); Vector2 new_pos; if (drag_selection.size() == 1) { new_pos = snap_point(drag_from, SNAP_NODE_SIDES | SNAP_NODE_CENTER | SNAP_NODE_ANCHORS | SNAP_OTHER_NODES | SNAP_GRID | SNAP_PIXEL, 0, drag_selection[0]); @@ -1416,6 +1427,20 @@ bool CanvasItemEditor::_gui_input_pivot(const Ref &p_event) { return true; } } + + if (drag_type == DRAG_TEMP_PIVOT) { + if (m.is_valid()) { + temp_pivot = transform.affine_inverse().xform(m->get_position()); + viewport->queue_redraw(); + return true; + } + + if ((b.is_valid() && !b->is_pressed() && b->get_button_index() == MouseButton::LEFT && tool == TOOL_EDIT_PIVOT) || + (k.is_valid() && !k->is_pressed() && k->get_keycode() == Key::V)) { + drag_type = DRAG_NONE; + return true; + } + } return false; } @@ -1441,7 +1466,9 @@ bool CanvasItemEditor::_gui_input_rotate(const Ref &p_event) { drag_type = DRAG_ROTATE; drag_from = transform.affine_inverse().xform(b->get_position()); CanvasItem *ci = drag_selection[0]; - if (ci->_edit_use_pivot()) { + if (!Math::is_inf(temp_pivot.x) || !Math::is_inf(temp_pivot.y)) { + drag_rotation_center = temp_pivot; + } else if (ci->_edit_use_pivot()) { drag_rotation_center = ci->get_global_transform_with_canvas().xform(ci->_edit_get_pivot()); } else { drag_rotation_center = ci->get_global_transform_with_canvas().get_origin(); @@ -1461,7 +1488,16 @@ bool CanvasItemEditor::_gui_input_rotate(const Ref &p_event) { drag_to = transform.affine_inverse().xform(m->get_position()); //Rotate the opposite way if the canvas item's compounded scale has an uneven number of negative elements bool opposite = (ci->get_global_transform().get_scale().sign().dot(ci->get_transform().get_scale().sign()) == 0); - ci->_edit_set_rotation(snap_angle(ci->_edit_get_rotation() + (opposite ? -1 : 1) * (drag_from - drag_rotation_center).angle_to(drag_to - drag_rotation_center), ci->_edit_get_rotation())); + real_t prev_rotation = ci->_edit_get_rotation(); + real_t new_rotation = snap_angle(ci->_edit_get_rotation() + (opposite ? -1 : 1) * (drag_from - drag_rotation_center).angle_to(drag_to - drag_rotation_center), prev_rotation); + + ci->_edit_set_rotation(new_rotation); + if (!Math::is_inf(temp_pivot.x) || !Math::is_inf(temp_pivot.y)) { + Transform2D xform = ci->get_global_transform_with_canvas() * ci->get_transform().affine_inverse(); + Vector2 radius = xform.xform(ci->_edit_get_position()) - temp_pivot; + radius = radius.rotated(new_rotation - prev_rotation); + ci->_edit_set_position(xform.affine_inverse().xform(temp_pivot + radius)); + } viewport->queue_redraw(); } return true; @@ -3161,7 +3197,7 @@ void CanvasItemEditor::_draw_ruler_tool() { } else { if (grid_snap_active) { Ref position_icon = get_editor_theme_icon(SNAME("EditorPosition")); - viewport->draw_texture(get_editor_theme_icon(SNAME("EditorPosition")), (ruler_tool_origin - view_offset) * zoom - position_icon->get_size() / 2); + viewport->draw_texture(position_icon, (ruler_tool_origin - view_offset) * zoom - position_icon->get_size() / 2); } } } @@ -3583,6 +3619,10 @@ void CanvasItemEditor::_draw_selection() { get_theme_color(SNAME("accent_color"), EditorStringName(Editor)) * Color(1, 1, 1, 0.6), Math::round(2 * EDSCALE)); } + + if (!Math::is_inf(temp_pivot.x) || !Math::is_inf(temp_pivot.y)) { + viewport->draw_texture(pivot_icon, (temp_pivot - view_offset) * zoom - (pivot_icon->get_size() / 2).floor(), get_theme_color(SNAME("accent_color"), SNAME("Editor"))); + } } void CanvasItemEditor::_draw_straight_line(Point2 p_from, Point2 p_to, Color p_color) { @@ -3931,8 +3971,6 @@ void CanvasItemEditor::_notification(int p_what) { } break; case NOTIFICATION_PROCESS: { - int nb_having_pivot = 0; - // Update the viewport if the canvas_item changes List selection = _get_edited_canvas_items(true); for (CanvasItem *ci : selection) { @@ -3972,14 +4010,10 @@ void CanvasItemEditor::_notification(int p_what) { viewport->queue_redraw(); } } - - if (ci->_edit_use_pivot()) { - nb_having_pivot++; - } } - // Activate / Deactivate the pivot tool - pivot_button->set_disabled(nb_having_pivot == 0); + // Activate / Deactivate the pivot tool. + pivot_button->set_disabled(selection.is_empty()); // Update the viewport if bones changes for (KeyValue &E : bone_list) { @@ -4048,6 +4082,11 @@ void CanvasItemEditor::_selection_changed() { _reset_drag(); } selected_from_canvas = false; + + if (temp_pivot != Vector2(INFINITY, INFINITY)) { + temp_pivot = Vector2(INFINITY, INFINITY); + viewport->queue_redraw(); + } } void CanvasItemEditor::edit(CanvasItem *p_canvas_item) { @@ -4202,6 +4241,18 @@ void CanvasItemEditor::_button_tool_select(int p_index) { tool = (Tool)p_index; + if (p_index == TOOL_EDIT_PIVOT && Input::get_singleton()->is_key_pressed(Key::SHIFT)) { + // Special action that places temporary rotation pivot in the middle of the selection. + List selection = _get_edited_canvas_items(); + if (!selection.is_empty()) { + Vector2 center; + for (const CanvasItem *ci : selection) { + center += ci->_edit_get_position(); + } + temp_pivot = center / selection.size(); + } + } + viewport->queue_redraw(); _update_cursor(); } @@ -5279,7 +5330,7 @@ CanvasItemEditor::CanvasItemEditor() { main_menu_hbox->add_child(pivot_button); pivot_button->set_toggle_mode(true); pivot_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_button_tool_select).bind(TOOL_EDIT_PIVOT)); - pivot_button->set_tooltip_text(TTR("Click to change object's rotation pivot.")); + pivot_button->set_tooltip_text(TTR("Click to change object's rotation pivot.") + "\n" + TTR("Shift: Set temporary rotation pivot.") + "\n" + TTR("Click this button while holding Shift to put the rotation pivot in the center of the selected nodes.")); pan_button = memnew(Button); pan_button->set_theme_type_variation("FlatButton"); diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index c4b995b048d..f52ad3dc4f5 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -174,6 +174,7 @@ private: DRAG_SCALE_BOTH, DRAG_ROTATE, DRAG_PIVOT, + DRAG_TEMP_PIVOT, DRAG_V_GUIDE, DRAG_H_GUIDE, DRAG_DOUBLE_GUIDE, @@ -251,6 +252,7 @@ private: bool key_scale = false; bool pan_pressed = false; + Vector2 temp_pivot = Vector2(INFINITY, INFINITY); bool ruler_tool_active = false; Point2 ruler_tool_origin;