Make possible to scale multiple nodes at once in the canvas editor

And also some smaller enhancements.
This commit is contained in:
Michael Alexsander 2024-10-25 16:34:32 -03:00
parent 8004c7524f
commit baffa731ab
No known key found for this signature in database
GPG Key ID: A9C91EE110F4EABA

View File

@ -1930,37 +1930,50 @@ bool CanvasItemEditor::_gui_input_scale(const Ref<InputEvent> &p_event) {
// Drag resize handles // Drag resize handles
if (drag_type == DRAG_NONE) { if (drag_type == DRAG_NONE) {
if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && b->is_pressed() && ((b->is_alt_pressed() && b->is_command_or_control_pressed()) || tool == TOOL_SCALE)) { if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && b->is_pressed() &&
((tool == TOOL_SELECT && b->is_alt_pressed() && b->is_command_or_control_pressed()) || tool == TOOL_SCALE)) {
bool has_locked_items = false; bool has_locked_items = false;
List<CanvasItem *> selection = _get_edited_canvas_items(false, true, &has_locked_items); List<CanvasItem *> selection = _get_edited_canvas_items(false, true, &has_locked_items);
if (selection.size() == 1) {
// Remove non-movable nodes.
for (CanvasItem *ci : selection) {
if (!_is_node_movable(ci, true)) {
selection.erase(ci);
}
}
if (!selection.is_empty()) {
CanvasItem *ci = selection.front()->get(); CanvasItem *ci = selection.front()->get();
if (_is_node_movable(ci)) { Transform2D edit_transform;
Transform2D xform = transform * ci->get_global_transform_with_canvas(); if (!Math::is_inf(temp_pivot.x) || !Math::is_inf(temp_pivot.y)) {
Transform2D unscaled_transform = (xform * ci->get_transform().affine_inverse() * ci->_edit_get_transform()).orthonormalized(); edit_transform = Transform2D(ci->_edit_get_rotation(), temp_pivot);
Transform2D simple_xform = viewport->get_transform() * unscaled_transform; } else {
edit_transform = ci->_edit_get_transform();
drag_type = DRAG_SCALE_BOTH;
if (show_transformation_gizmos) {
Size2 scale_factor = Size2(SCALE_HANDLE_DISTANCE, SCALE_HANDLE_DISTANCE);
Rect2 x_handle_rect = Rect2(scale_factor.x * EDSCALE, -5 * EDSCALE, 10 * EDSCALE, 10 * EDSCALE);
if (x_handle_rect.has_point(simple_xform.affine_inverse().xform(b->get_position()))) {
drag_type = DRAG_SCALE_X;
}
Rect2 y_handle_rect = Rect2(-5 * EDSCALE, scale_factor.y * EDSCALE, 10 * EDSCALE, 10 * EDSCALE);
if (y_handle_rect.has_point(simple_xform.affine_inverse().xform(b->get_position()))) {
drag_type = DRAG_SCALE_Y;
}
}
drag_from = transform.affine_inverse().xform(b->get_position());
drag_selection = List<CanvasItem *>();
drag_selection.push_back(ci);
_save_canvas_item_state(drag_selection);
return true;
} }
Transform2D xform = transform * ci->get_global_transform_with_canvas();
Transform2D unscaled_transform = (xform * ci->get_transform().affine_inverse() * edit_transform).orthonormalized();
Transform2D simple_xform = viewport->get_transform() * unscaled_transform;
drag_type = DRAG_SCALE_BOTH;
if (show_transformation_gizmos) {
Size2 scale_factor = Size2(SCALE_HANDLE_DISTANCE, SCALE_HANDLE_DISTANCE);
Rect2 x_handle_rect = Rect2(scale_factor.x * EDSCALE, -5 * EDSCALE, 10 * EDSCALE, 10 * EDSCALE);
if (x_handle_rect.has_point(simple_xform.affine_inverse().xform(b->get_position()))) {
drag_type = DRAG_SCALE_X;
}
Rect2 y_handle_rect = Rect2(-5 * EDSCALE, scale_factor.y * EDSCALE, 10 * EDSCALE, 10 * EDSCALE);
if (y_handle_rect.has_point(simple_xform.affine_inverse().xform(b->get_position()))) {
drag_type = DRAG_SCALE_Y;
}
}
drag_from = transform.affine_inverse().xform(b->get_position());
drag_selection = selection;
_save_canvas_item_state(drag_selection);
return true;
} else { } else {
if (has_locked_items) { if (has_locked_items) {
EditorToaster::get_singleton()->popup_str(TTR(locked_transform_warning), EditorToaster::SEVERITY_WARNING); EditorToaster::get_singleton()->popup_str(TTR(locked_transform_warning), EditorToaster::SEVERITY_WARNING);
@ -1968,66 +1981,87 @@ bool CanvasItemEditor::_gui_input_scale(const Ref<InputEvent> &p_event) {
return has_locked_items; return has_locked_items;
} }
} }
} } else if (drag_type == DRAG_SCALE_BOTH || drag_type == DRAG_SCALE_X || drag_type == DRAG_SCALE_Y) {
if (drag_type == DRAG_SCALE_BOTH || drag_type == DRAG_SCALE_X || drag_type == DRAG_SCALE_Y) {
// Resize the node // Resize the node
if (m.is_valid()) { if (m.is_valid()) {
_restore_canvas_item_state(drag_selection); _restore_canvas_item_state(drag_selection);
CanvasItem *ci = drag_selection.front()->get();
drag_to = transform.affine_inverse().xform(m->get_position()); drag_to = transform.affine_inverse().xform(m->get_position());
Transform2D parent_xform = ci->get_global_transform_with_canvas() * ci->get_transform().affine_inverse(); Size2 scale_max;
Transform2D unscaled_transform = (transform * parent_xform * ci->_edit_get_transform()).orthonormalized(); if (drag_type != DRAG_SCALE_BOTH) {
Transform2D simple_xform = (viewport->get_transform() * unscaled_transform).affine_inverse() * transform; for (CanvasItem *ci : drag_selection) {
scale_max = scale_max.max(ci->_edit_get_scale());
bool uniform = m->is_shift_pressed();
bool is_ctrl = m->is_command_or_control_pressed();
Point2 drag_from_local = simple_xform.xform(drag_from);
Point2 drag_to_local = simple_xform.xform(drag_to);
Point2 offset = drag_to_local - drag_from_local;
Size2 scale = ci->_edit_get_scale();
Size2 original_scale = scale;
real_t ratio = scale.y / scale.x;
if (drag_type == DRAG_SCALE_BOTH) {
Size2 scale_factor = drag_to_local / drag_from_local;
if (uniform) {
scale *= (scale_factor.x + scale_factor.y) / 2.0;
} else {
scale *= scale_factor;
}
} else {
Size2 scale_factor = Vector2(offset.x, -offset.y) / SCALE_HANDLE_DISTANCE;
Size2 parent_scale = parent_xform.get_scale();
scale_factor *= Vector2(1.0 / parent_scale.x, 1.0 / parent_scale.y);
if (drag_type == DRAG_SCALE_X) {
scale.x += scale_factor.x;
if (uniform) {
scale.y = scale.x * ratio;
}
} else if (drag_type == DRAG_SCALE_Y) {
scale.y -= scale_factor.y;
if (uniform) {
scale.x = scale.y / ratio;
}
} }
} }
if (snap_scale && !is_ctrl) { for (CanvasItem *ci : drag_selection) {
if (snap_relative) { Transform2D edit_transform;
scale.x = original_scale.x * (roundf((scale.x / original_scale.x) / snap_scale_step) * snap_scale_step); bool using_temp_pivot = !Math::is_inf(temp_pivot.x) || !Math::is_inf(temp_pivot.y);
scale.y = original_scale.y * (roundf((scale.y / original_scale.y) / snap_scale_step) * snap_scale_step); if (using_temp_pivot) {
edit_transform = Transform2D(ci->_edit_get_rotation(), temp_pivot);
} else { } else {
scale.x = roundf(scale.x / snap_scale_step) * snap_scale_step; edit_transform = ci->_edit_get_transform();
scale.y = roundf(scale.y / snap_scale_step) * snap_scale_step; }
Transform2D parent_xform = ci->get_global_transform_with_canvas() * ci->get_transform().affine_inverse();
Transform2D unscaled_transform = (transform * parent_xform * edit_transform).orthonormalized();
Transform2D simple_xform = (viewport->get_transform() * unscaled_transform).affine_inverse() * transform;
bool uniform = m->is_shift_pressed();
bool is_ctrl = m->is_command_or_control_pressed();
Point2 drag_from_local = simple_xform.xform(drag_from);
Point2 drag_to_local = simple_xform.xform(drag_to);
Point2 offset = drag_to_local - drag_from_local;
Size2 scale = ci->_edit_get_scale();
Size2 original_scale = scale;
real_t ratio = scale.y / scale.x;
if (drag_type == DRAG_SCALE_BOTH) {
Size2 scale_factor = drag_to_local / drag_from_local;
if (uniform) {
scale *= (scale_factor.x + scale_factor.y) / 2.0;
} else {
scale *= scale_factor;
}
} else {
Size2 scale_factor = Vector2(offset.x, -offset.y) / SCALE_HANDLE_DISTANCE;
Size2 parent_scale = parent_xform.get_scale();
// Take into account the biggest scale, so all nodes are scaled uniformly.
scale_factor *= Vector2(1.0 / parent_scale.x, 1.0 / parent_scale.y) / (scale_max / original_scale);
if (drag_type == DRAG_SCALE_X) {
scale.x += scale_factor.x;
if (uniform) {
scale.y = scale.x * ratio;
}
} else if (drag_type == DRAG_SCALE_Y) {
scale.y -= scale_factor.y;
if (uniform) {
scale.x = scale.y / ratio;
}
}
}
if (snap_scale && !is_ctrl) {
if (snap_relative) {
scale.x = original_scale.x * (Math::round((scale.x / original_scale.x) / snap_scale_step) * snap_scale_step);
scale.y = original_scale.y * (Math::round((scale.y / original_scale.y) / snap_scale_step) * snap_scale_step);
} else {
scale.x = Math::round(scale.x / snap_scale_step) * snap_scale_step;
scale.y = Math::round(scale.y / snap_scale_step) * snap_scale_step;
}
}
ci->_edit_set_scale(scale);
if (using_temp_pivot) {
Point2 ci_origin = ci->_edit_get_transform().get_origin();
ci->_edit_set_position(ci_origin + (ci_origin - temp_pivot) * ((scale - original_scale) / original_scale));
} }
} }
ci->_edit_set_scale(scale);
return true; return true;
} }
@ -2075,7 +2109,7 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) {
if (drag_type == DRAG_NONE) { if (drag_type == DRAG_NONE) {
//Start moving the nodes //Start moving the nodes
if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && b->is_pressed()) { if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && b->is_pressed()) {
if ((b->is_alt_pressed() && !b->is_command_or_control_pressed()) || tool == TOOL_MOVE) { if ((tool == TOOL_SELECT && b->is_alt_pressed() && !b->is_command_or_control_pressed()) || tool == TOOL_MOVE) {
bool has_locked_items = false; bool has_locked_items = false;
List<CanvasItem *> selection = _get_edited_canvas_items(false, true, &has_locked_items); List<CanvasItem *> selection = _get_edited_canvas_items(false, true, &has_locked_items);
@ -2135,7 +2169,7 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) {
} }
Point2 drag_delta = drag_to - drag_from; Point2 drag_delta = drag_to - drag_from;
if (drag_selection.size() == 1 && (drag_type == DRAG_MOVE_X || drag_type == DRAG_MOVE_Y)) { if (drag_type == DRAG_MOVE_X || drag_type == DRAG_MOVE_Y) {
const CanvasItem *selected = drag_selection.front()->get(); const CanvasItem *selected = drag_selection.front()->get();
Transform2D parent_xform = selected->get_global_transform_with_canvas() * selected->get_transform().affine_inverse(); Transform2D parent_xform = selected->get_global_transform_with_canvas() * selected->get_transform().affine_inverse();
Transform2D unscaled_transform = (transform * parent_xform * selected->_edit_get_transform()).orthonormalized(); Transform2D unscaled_transform = (transform * parent_xform * selected->_edit_get_transform()).orthonormalized();
@ -3468,16 +3502,14 @@ void CanvasItemEditor::_draw_selection() {
Ref<Texture2D> previous_position_icon = get_editor_theme_icon(SNAME("EditorPositionPrevious")); Ref<Texture2D> previous_position_icon = get_editor_theme_icon(SNAME("EditorPositionPrevious"));
RID vp_ci = viewport->get_canvas_item(); RID vp_ci = viewport->get_canvas_item();
List<CanvasItem *> selection = _get_edited_canvas_items(true, false); List<CanvasItem *> selection = _get_edited_canvas_items(true, false);
bool single = selection.size() == 1; bool single = selection.size() == 1;
bool transform_tool = tool == TOOL_SELECT || tool == TOOL_MOVE || tool == TOOL_SCALE || tool == TOOL_ROTATE || tool == TOOL_EDIT_PIVOT;
for (CanvasItem *E : selection) { for (CanvasItem *E : selection) {
CanvasItem *ci = Object::cast_to<CanvasItem>(E); CanvasItem *ci = Object::cast_to<CanvasItem>(E);
CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(ci); CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(ci);
bool item_locked = ci->has_meta("_edit_lock_");
// Draw the previous position if we are dragging the node // Draw the previous position if we are dragging the node
if (show_helpers && if (show_helpers &&
(drag_type == DRAG_MOVE || drag_type == DRAG_ROTATE || (drag_type == DRAG_MOVE || drag_type == DRAG_ROTATE ||
@ -3502,6 +3534,7 @@ void CanvasItemEditor::_draw_selection() {
} }
} }
bool item_locked = ci->has_meta("_edit_lock_");
Transform2D xform = transform * ci->get_global_transform_with_canvas(); Transform2D xform = transform * ci->get_global_transform_with_canvas();
// Draw the selected items position / surrounding boxes // Draw the selected items position / surrounding boxes
@ -3531,7 +3564,7 @@ void CanvasItemEditor::_draw_selection() {
viewport->draw_set_transform_matrix(viewport->get_transform()); viewport->draw_set_transform_matrix(viewport->get_transform());
} }
if (single && !item_locked && (tool == TOOL_SELECT || tool == TOOL_MOVE || tool == TOOL_SCALE || tool == TOOL_ROTATE || tool == TOOL_EDIT_PIVOT)) { //kind of sucks if (single && !item_locked && transform_tool) {
// Draw the pivot // Draw the pivot
if (ci->_edit_use_pivot()) { if (ci->_edit_use_pivot()) {
// Draw the node's pivot // Draw the node's pivot
@ -3574,73 +3607,88 @@ void CanvasItemEditor::_draw_selection() {
select_handle->draw(vp_ci, (ofs - (select_handle->get_size() / 2)).floor()); select_handle->draw(vp_ci, (ofs - (select_handle->get_size() / 2)).floor());
} }
} }
}
}
// Draw the move handles // Remove non-movable nodes.
bool is_ctrl = Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL); for (CanvasItem *ci : selection) {
bool is_alt = Input::get_singleton()->is_key_pressed(Key::ALT); if (!_is_node_movable(ci, true)) {
if (tool == TOOL_MOVE && show_transformation_gizmos) { selection.erase(ci);
if (_is_node_movable(ci)) { }
Transform2D unscaled_transform = (xform * ci->get_transform().affine_inverse() * ci->_edit_get_transform()).orthonormalized(); }
Transform2D simple_xform = viewport->get_transform() * unscaled_transform;
Size2 move_factor = Size2(MOVE_HANDLE_DISTANCE, MOVE_HANDLE_DISTANCE); if (!selection.is_empty() && transform_tool && show_transformation_gizmos) {
viewport->draw_set_transform_matrix(simple_xform); CanvasItem *ci = selection.front()->get();
Vector<Point2> points = { Transform2D xform = transform * ci->get_global_transform_with_canvas();
Vector2(move_factor.x * EDSCALE, 5 * EDSCALE), bool is_ctrl = Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL);
Vector2(move_factor.x * EDSCALE, -5 * EDSCALE), bool is_alt = Input::get_singleton()->is_key_pressed(Key::ALT);
Vector2((move_factor.x + 10) * EDSCALE, 0)
};
viewport->draw_colored_polygon(points, get_theme_color(SNAME("axis_x_color"), EditorStringName(Editor))); // Draw the move handles.
viewport->draw_line(Point2(), Point2(move_factor.x * EDSCALE, 0), get_theme_color(SNAME("axis_x_color"), EditorStringName(Editor)), Math::round(EDSCALE)); if ((tool == TOOL_SELECT && is_alt && !is_ctrl) || tool == TOOL_MOVE) {
Transform2D unscaled_transform = (xform * ci->get_transform().affine_inverse() * ci->_edit_get_transform()).orthonormalized();
Transform2D simple_xform = viewport->get_transform() * unscaled_transform;
points.clear(); Size2 move_factor = Size2(MOVE_HANDLE_DISTANCE, MOVE_HANDLE_DISTANCE);
points.push_back(Vector2(5 * EDSCALE, move_factor.y * EDSCALE)); viewport->draw_set_transform_matrix(simple_xform);
points.push_back(Vector2(-5 * EDSCALE, move_factor.y * EDSCALE));
points.push_back(Vector2(0, (move_factor.y + 10) * EDSCALE));
viewport->draw_colored_polygon(points, get_theme_color(SNAME("axis_y_color"), EditorStringName(Editor))); Vector<Point2> points = {
viewport->draw_line(Point2(), Point2(0, move_factor.y * EDSCALE), get_theme_color(SNAME("axis_y_color"), EditorStringName(Editor)), Math::round(EDSCALE)); Vector2(move_factor.x * EDSCALE, 5 * EDSCALE),
Vector2(move_factor.x * EDSCALE, -5 * EDSCALE),
Vector2((move_factor.x + 10) * EDSCALE, 0)
};
viewport->draw_set_transform_matrix(viewport->get_transform()); viewport->draw_colored_polygon(points, get_theme_color(SNAME("axis_x_color"), EditorStringName(Editor)));
viewport->draw_line(Point2(), Point2(move_factor.x * EDSCALE, 0), get_theme_color(SNAME("axis_x_color"), EditorStringName(Editor)), Math::round(EDSCALE));
points.clear();
points.push_back(Vector2(5 * EDSCALE, move_factor.y * EDSCALE));
points.push_back(Vector2(-5 * EDSCALE, move_factor.y * EDSCALE));
points.push_back(Vector2(0, (move_factor.y + 10) * EDSCALE));
viewport->draw_colored_polygon(points, get_theme_color(SNAME("axis_y_color"), EditorStringName(Editor)));
viewport->draw_line(Point2(), Point2(0, move_factor.y * EDSCALE), get_theme_color(SNAME("axis_y_color"), EditorStringName(Editor)), Math::round(EDSCALE));
viewport->draw_set_transform_matrix(viewport->get_transform());
}
// Draw the rescale handles.
if ((tool == TOOL_SELECT && is_alt && is_ctrl) || tool == TOOL_SCALE || drag_type == DRAG_SCALE_X || drag_type == DRAG_SCALE_Y) {
Transform2D edit_transform;
if (!Math::is_inf(temp_pivot.x) || !Math::is_inf(temp_pivot.y)) {
edit_transform = Transform2D(ci->_edit_get_rotation(), temp_pivot);
} else {
edit_transform = ci->_edit_get_transform();
}
Transform2D unscaled_transform = (xform * ci->get_transform().affine_inverse() * edit_transform).orthonormalized();
Transform2D simple_xform = viewport->get_transform() * unscaled_transform;
Size2 scale_factor = Size2(SCALE_HANDLE_DISTANCE, SCALE_HANDLE_DISTANCE);
bool uniform = Input::get_singleton()->is_key_pressed(Key::SHIFT);
Point2 offset = (simple_xform.affine_inverse().xform(drag_to) - simple_xform.affine_inverse().xform(drag_from)) * zoom;
if (drag_type == DRAG_SCALE_X) {
scale_factor.x += offset.x;
if (uniform) {
scale_factor.y += offset.x;
}
} else if (drag_type == DRAG_SCALE_Y) {
scale_factor.y += offset.y;
if (uniform) {
scale_factor.x += offset.y;
} }
} }
// Draw the rescale handles viewport->draw_set_transform_matrix(simple_xform);
if (show_transformation_gizmos && ((is_alt && is_ctrl) || tool == TOOL_SCALE || drag_type == DRAG_SCALE_X || drag_type == DRAG_SCALE_Y)) { Rect2 x_handle_rect = Rect2(scale_factor.x * EDSCALE, -5 * EDSCALE, 10 * EDSCALE, 10 * EDSCALE);
if (_is_node_movable(ci)) { viewport->draw_rect(x_handle_rect, get_theme_color(SNAME("axis_x_color"), EditorStringName(Editor)));
Transform2D unscaled_transform = (xform * ci->get_transform().affine_inverse() * ci->_edit_get_transform()).orthonormalized(); viewport->draw_line(Point2(), Point2(scale_factor.x * EDSCALE, 0), get_theme_color(SNAME("axis_x_color"), EditorStringName(Editor)), Math::round(EDSCALE));
Transform2D simple_xform = viewport->get_transform() * unscaled_transform;
Size2 scale_factor = Size2(SCALE_HANDLE_DISTANCE, SCALE_HANDLE_DISTANCE); Rect2 y_handle_rect = Rect2(-5 * EDSCALE, scale_factor.y * EDSCALE, 10 * EDSCALE, 10 * EDSCALE);
bool uniform = Input::get_singleton()->is_key_pressed(Key::SHIFT); viewport->draw_rect(y_handle_rect, get_theme_color(SNAME("axis_y_color"), EditorStringName(Editor)));
Point2 offset = (simple_xform.affine_inverse().xform(drag_to) - simple_xform.affine_inverse().xform(drag_from)) * zoom; viewport->draw_line(Point2(), Point2(0, scale_factor.y * EDSCALE), get_theme_color(SNAME("axis_y_color"), EditorStringName(Editor)), Math::round(EDSCALE));
if (drag_type == DRAG_SCALE_X) { viewport->draw_set_transform_matrix(viewport->get_transform());
scale_factor.x += offset.x;
if (uniform) {
scale_factor.y += offset.x;
}
} else if (drag_type == DRAG_SCALE_Y) {
scale_factor.y += offset.y;
if (uniform) {
scale_factor.x += offset.y;
}
}
viewport->draw_set_transform_matrix(simple_xform);
Rect2 x_handle_rect = Rect2(scale_factor.x * EDSCALE, -5 * EDSCALE, 10 * EDSCALE, 10 * EDSCALE);
viewport->draw_rect(x_handle_rect, get_theme_color(SNAME("axis_x_color"), EditorStringName(Editor)));
viewport->draw_line(Point2(), Point2(scale_factor.x * EDSCALE, 0), get_theme_color(SNAME("axis_x_color"), EditorStringName(Editor)), Math::round(EDSCALE));
Rect2 y_handle_rect = Rect2(-5 * EDSCALE, scale_factor.y * EDSCALE, 10 * EDSCALE, 10 * EDSCALE);
viewport->draw_rect(y_handle_rect, get_theme_color(SNAME("axis_y_color"), EditorStringName(Editor)));
viewport->draw_line(Point2(), Point2(0, scale_factor.y * EDSCALE), get_theme_color(SNAME("axis_y_color"), EditorStringName(Editor)), Math::round(EDSCALE));
viewport->draw_set_transform_matrix(viewport->get_transform());
}
}
} }
} }
@ -5389,7 +5437,7 @@ CanvasItemEditor::CanvasItemEditor() {
main_menu_hbox->add_child(pivot_button); main_menu_hbox->add_child(pivot_button);
pivot_button->set_toggle_mode(true); pivot_button->set_toggle_mode(true);
pivot_button->connect(SceneStringName(pressed), callable_mp(this, &CanvasItemEditor::_button_tool_select).bind(TOOL_EDIT_PIVOT)); pivot_button->connect(SceneStringName(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.") + "\n" + TTR("Shift: Set temporary rotation pivot.") + "\n" + TTR("Click this button while holding Shift to put the temporary rotation pivot in the center of the selected nodes.")); pivot_button->set_tooltip_text(TTR("Click to change object's pivot.") + "\n" + TTR("Shift: Set temporary pivot.") + "\n" + TTR("Click this button while holding Shift to put the temporary pivot in the center of the selected nodes."));
pan_button = memnew(Button); pan_button = memnew(Button);
pan_button->set_theme_type_variation("FlatButton"); pan_button->set_theme_type_variation("FlatButton");