diff --git a/editor/icons/ToolTransform.svg b/editor/icons/ToolTransform.svg
new file mode 100644
index 00000000000..617a1638dc9
--- /dev/null
+++ b/editor/icons/ToolTransform.svg
@@ -0,0 +1 @@
+
diff --git a/editor/scene/3d/node_3d_editor_plugin.cpp b/editor/scene/3d/node_3d_editor_plugin.cpp
index bd840740063..98d6b9f9503 100644
--- a/editor/scene/3d/node_3d_editor_plugin.cpp
+++ b/editor/scene/3d/node_3d_editor_plugin.cpp
@@ -1314,7 +1314,7 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b
Transform3D gt = spatial_editor->get_gizmo_transform();
- if (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_MOVE) {
+ if (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_TRANSFORM || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_MOVE) {
int col_axis = -1;
real_t col_d = 1e20;
@@ -1380,7 +1380,7 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b
}
}
- if (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_ROTATE) {
+ if (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_TRANSFORM || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_ROTATE) {
int col_axis = -1;
bool view_rotation_selected = false;
@@ -2076,7 +2076,7 @@ void Node3DEditorViewport::_sinput(const Ref &p_event) {
clicked = _select_ray(b->get_position());
if (clicked.is_valid() && !editor_selection->is_selected(ObjectDB::get_instance(clicked))) {
- if (!node_selected || (!b->is_alt_pressed() && !(spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT && b->is_command_or_control_pressed()))) {
+ if (!node_selected || (!b->is_alt_pressed() && !(spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_TRANSFORM && b->is_command_or_control_pressed()))) {
selection_in_progress = true;
break;
}
@@ -2086,7 +2086,7 @@ void Node3DEditorViewport::_sinput(const Ref &p_event) {
if (node_selected) {
TransformMode mode = TRANSFORM_NONE;
- if (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT) {
+ if (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_TRANSFORM) {
if (b->is_command_or_control_pressed()) {
mode = TRANSFORM_ROTATE;
} else if (b->is_alt_pressed()) {
@@ -2127,7 +2127,7 @@ void Node3DEditorViewport::_sinput(const Ref &p_event) {
TransformMode mode = TRANSFORM_NONE;
switch (spatial_editor->get_tool_mode()) {
- case Node3DEditor::TOOL_MODE_SELECT:
+ case Node3DEditor::TOOL_MODE_TRANSFORM:
if (b->is_command_or_control_pressed() && node_selected) {
mode = TRANSFORM_ROTATE;
} else if (b->is_alt_pressed() && node_selected) {
@@ -2188,13 +2188,13 @@ void Node3DEditorViewport::_sinput(const Ref &p_event) {
if (clicked.is_valid() && _edit.mode == TRANSFORM_NONE) {
_select_clicked(false);
- }
-
- if (cursor.region_select) {
+ } else if (cursor.region_select) {
_select_region();
- cursor.region_select = false;
surface->queue_redraw();
}
+
+ movement_threshold_passed = false;
+ cursor.region_select = false;
}
if (!_edit.instant && _edit.mode != TRANSFORM_NONE) {
@@ -2306,9 +2306,9 @@ void Node3DEditorViewport::_sinput(const Ref &p_event) {
if (change_nav_from_shortcut != NAVIGATION_NONE) {
nav_mode = change_nav_from_shortcut;
} else {
- const bool movement_threshold_passed = _edit.original_mouse_pos.distance_to(_edit.mouse_pos) > 8 * EDSCALE;
+ movement_threshold_passed = _edit.original_mouse_pos.distance_to(_edit.mouse_pos) > 8 * EDSCALE;
- if ((selection_in_progress || clicked_wants_append) && movement_threshold_passed && clicked.is_valid()) {
+ if ((selection_in_progress || clicked_wants_append || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT) && movement_threshold_passed && clicked.is_valid()) {
cursor.region_select = true;
cursor.region_begin = _edit.original_mouse_pos;
clicked = ObjectID();
@@ -2320,8 +2320,8 @@ void Node3DEditorViewport::_sinput(const Ref &p_event) {
return;
}
- if (clicked.is_valid() && movement_threshold_passed && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_MOVE)) {
- bool is_select_mode = (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT);
+ if (clicked.is_valid() && movement_threshold_passed && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_TRANSFORM || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_MOVE)) {
+ bool is_select_mode = (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_TRANSFORM);
bool is_clicked_selected = editor_selection->is_selected(ObjectDB::get_instance(clicked));
if (_edit.mode == TRANSFORM_NONE && (is_select_mode || is_clicked_selected)) {
@@ -3678,7 +3678,7 @@ void Node3DEditorViewport::_draw() {
get_theme_stylebox(SNAME("FocusViewport"), EditorStringName(EditorStyles))->draw(surface->get_canvas_item(), r);
}
- if (cursor.region_select) {
+ if (cursor.region_select && movement_threshold_passed) {
const Rect2 selection_rect = Rect2(cursor.region_begin, cursor.region_end - cursor.region_begin);
surface->draw_rect(
@@ -4582,7 +4582,7 @@ void Node3DEditorViewport::update_transform_gizmo_view() {
bool hide_during_rotation = _is_rotation_arc_visible();
bool show_gizmo = spatial_editor->is_gizmo_visible() && !_edit.instant && transform_gizmo_visible && !collision_reposition && !hide_during_rotation;
- bool show_rotate_gizmo = show_gizmo && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_ROTATE);
+ bool show_rotate_gizmo = show_gizmo && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_TRANSFORM || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_ROTATE);
for (int i = 0; i < 3; i++) {
Transform3D axis_angle;
@@ -4592,9 +4592,9 @@ void Node3DEditorViewport::update_transform_gizmo_view() {
axis_angle.basis.scale(scale);
axis_angle.origin = xform.origin;
RenderingServer::get_singleton()->instance_set_transform(move_gizmo_instance[i], axis_angle);
- RenderingServer::get_singleton()->instance_set_visible(move_gizmo_instance[i], show_gizmo && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_MOVE));
+ RenderingServer::get_singleton()->instance_set_visible(move_gizmo_instance[i], show_gizmo && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_TRANSFORM || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_MOVE));
RenderingServer::get_singleton()->instance_set_transform(move_plane_gizmo_instance[i], axis_angle);
- RenderingServer::get_singleton()->instance_set_visible(move_plane_gizmo_instance[i], show_gizmo && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_MOVE));
+ RenderingServer::get_singleton()->instance_set_visible(move_plane_gizmo_instance[i], show_gizmo && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_TRANSFORM || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_MOVE));
RenderingServer::get_singleton()->instance_set_transform(rotate_gizmo_instance[i], axis_angle);
RenderingServer::get_singleton()->instance_set_visible(rotate_gizmo_instance[i], show_rotate_gizmo);
RenderingServer::get_singleton()->instance_set_transform(scale_gizmo_instance[i], axis_angle);
@@ -7355,6 +7355,7 @@ void Node3DEditor::_menu_item_pressed(int p_option) {
case MENU_TOOL_MOVE:
case MENU_TOOL_ROTATE:
case MENU_TOOL_SCALE:
+ case MENU_TOOL_TRANSFORM:
case MENU_TOOL_LIST_SELECT: {
for (int i = 0; i < TOOL_MAX; i++) {
tool_button[i]->set_pressed(i == p_option);
@@ -8843,6 +8844,7 @@ void Node3DEditor::_update_theme() {
tool_button[TOOL_MODE_MOVE]->set_button_icon(get_editor_theme_icon(SNAME("ToolMove")));
tool_button[TOOL_MODE_ROTATE]->set_button_icon(get_editor_theme_icon(SNAME("ToolRotate")));
tool_button[TOOL_MODE_SCALE]->set_button_icon(get_editor_theme_icon(SNAME("ToolScale")));
+ tool_button[TOOL_MODE_TRANSFORM]->set_button_icon(get_editor_theme_icon(SNAME("ToolTransform")));
tool_button[TOOL_MODE_LIST_SELECT]->set_button_icon(get_editor_theme_icon(SNAME("ListSelect")));
tool_button[TOOL_LOCK_SELECTED]->set_button_icon(get_editor_theme_icon(SNAME("Lock")));
tool_button[TOOL_UNLOCK_SELECTED]->set_button_icon(get_editor_theme_icon(SNAME("Unlock")));
@@ -8877,7 +8879,8 @@ void Node3DEditor::_update_theme() {
void Node3DEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_TRANSLATION_CHANGED: {
- tool_button[TOOL_MODE_SELECT]->set_tooltip_text(keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL) + TTR("Drag: Rotate selected node around pivot.") + "\n" + TTR("Alt+RMB: Show list of all nodes at position clicked, including locked.") + "\n" + TTR("(Available in all modes.)"));
+ tool_button[TOOL_MODE_SELECT]->set_tooltip_text(TTR("Alt+RMB: Show list of all nodes at position clicked, including locked.") + "\n" + TTR("(Available in all modes.)"));
+ tool_button[TOOL_MODE_TRANSFORM]->set_tooltip_text(keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL) + TTR("Drag: Rotate selected node around pivot.") + "\n" + TTR("Alt+RMB: Show list of all nodes at position clicked, including locked.") + "\n" + TTR("(Available in all modes.)"));
tool_button[TOOL_MODE_MOVE]->set_tooltip_text(keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL) + TTR("Drag: Use snap.") + "\n" + TTR("Alt+RMB: Show list of all nodes at position clicked, including locked."));
tool_button[TOOL_MODE_ROTATE]->set_tooltip_text(keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL) + TTR("Drag: Use snap.") + "\n" + TTR("Alt+RMB: Show list of all nodes at position clicked, including locked."));
tool_button[TOOL_MODE_SCALE]->set_tooltip_text(keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL) + TTR("Drag: Use snap.") + "\n" + TTR("Alt+RMB: Show list of all nodes at position clicked, including locked."));
@@ -9706,6 +9709,7 @@ Node3DEditor::Node3DEditor() {
tool_button[TOOL_MODE_SELECT]->set_shortcut(ED_SHORTCUT("spatial_editor/tool_select", TTRC("Select Mode"), Key::Q, true));
tool_button[TOOL_MODE_SELECT]->set_shortcut_context(this);
tool_button[TOOL_MODE_SELECT]->set_accessibility_name(TTRC("Select Mode"));
+
main_menu_hbox->add_child(memnew(VSeparator));
tool_button[TOOL_MODE_MOVE] = memnew(Button);
@@ -9739,6 +9743,15 @@ Node3DEditor::Node3DEditor() {
tool_button[TOOL_MODE_SCALE]->set_shortcut_context(this);
tool_button[TOOL_MODE_SCALE]->set_accessibility_name(TTRC("Scale Mode"));
+ tool_button[TOOL_MODE_TRANSFORM] = memnew(Button);
+ main_menu_hbox->add_child(tool_button[TOOL_MODE_TRANSFORM]);
+ tool_button[TOOL_MODE_TRANSFORM]->set_toggle_mode(true);
+ tool_button[TOOL_MODE_TRANSFORM]->set_theme_type_variation(SceneStringName(FlatButton));
+ tool_button[TOOL_MODE_TRANSFORM]->connect(SceneStringName(pressed), callable_mp(this, &Node3DEditor::_menu_item_pressed).bind(MENU_TOOL_TRANSFORM));
+ tool_button[TOOL_MODE_TRANSFORM]->set_shortcut(ED_SHORTCUT("spatial_editor/tool_transform", TTRC("Transform Mode"), Key::V, true));
+ tool_button[TOOL_MODE_TRANSFORM]->set_shortcut_context(this);
+ tool_button[TOOL_MODE_TRANSFORM]->set_accessibility_name(TTRC("Transform Mode"));
+
main_menu_hbox->add_child(memnew(VSeparator));
tool_button[TOOL_MODE_LIST_SELECT] = memnew(Button);
diff --git a/editor/scene/3d/node_3d_editor_plugin.h b/editor/scene/3d/node_3d_editor_plugin.h
index 7989e88da2f..957d7e20d07 100644
--- a/editor/scene/3d/node_3d_editor_plugin.h
+++ b/editor/scene/3d/node_3d_editor_plugin.h
@@ -334,6 +334,7 @@ private:
Vector selection_results_menu;
bool clicked_wants_append = false;
bool selection_in_progress = false;
+ bool movement_threshold_passed = false;
PopupMenu *selection_menu = nullptr;
@@ -662,6 +663,7 @@ public:
TOOL_MODE_MOVE,
TOOL_MODE_ROTATE,
TOOL_MODE_SCALE,
+ TOOL_MODE_TRANSFORM,
TOOL_MODE_LIST_SELECT,
TOOL_LOCK_SELECTED,
TOOL_UNLOCK_SELECTED,
@@ -762,6 +764,7 @@ private:
MENU_TOOL_MOVE,
MENU_TOOL_ROTATE,
MENU_TOOL_SCALE,
+ MENU_TOOL_TRANSFORM,
MENU_TOOL_LIST_SELECT,
MENU_TOOL_LOCAL_COORDS,
MENU_TOOL_USE_SNAP,