Skip to content

Commit 778cf54

Browse files
committed
Merge pull request #99499 from Calinou/3d-editor-add-follow-selection
Add "Follow Selection" in the 3D editor by using Center Selection twice
2 parents 89f9ab0 + 040e19e commit 778cf54

File tree

2 files changed

+43
-0
lines changed

2 files changed

+43
-0
lines changed

editor/scene/3d/node_3d_editor_plugin.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2694,6 +2694,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
26942694
}
26952695
if (ED_IS_SHORTCUT("spatial_editor/focus_selection", event_mod)) {
26962696
_menu_option(VIEW_CENTER_TO_SELECTION);
2697+
times_focused_consecutively += 1;
26972698
}
26982699
if (ED_IS_SHORTCUT("spatial_editor/align_transform_with_view", event_mod)) {
26992700
_menu_option(VIEW_ALIGN_TRANSFORM_WITH_VIEW);
@@ -2875,6 +2876,8 @@ void Node3DEditorViewport::_nav_pan(Ref<InputEventWithModifiers> p_event, const
28752876
translation *= cursor.distance / DISTANCE_DEFAULT;
28762877
camera_transform.translate_local(translation);
28772878
cursor.pos = camera_transform.origin;
2879+
2880+
_disable_follow_mode();
28782881
}
28792882

28802883
void Node3DEditorViewport::_nav_zoom(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative) {
@@ -3183,6 +3186,8 @@ void Node3DEditorViewport::_update_freelook(real_t delta) {
31833186
const Vector3 motion = direction * speed * delta;
31843187
cursor.pos += motion;
31853188
cursor.eye_pos += motion;
3189+
3190+
_disable_follow_mode();
31863191
}
31873192

31883193
void Node3DEditorViewport::set_message(const String &p_message, float p_time) {
@@ -3382,6 +3387,20 @@ void Node3DEditorViewport::_notification(int p_what) {
33823387

33833388
_update_freelook(delta);
33843389

3390+
if (focused_node_id.is_valid() && get_selected_count() > 0 && times_focused_consecutively >= 2 && times_focused_consecutively % 2 == 0) {
3391+
Node *focused_node = ObjectDB::get_instance<Node>(focused_node_id);
3392+
if (focused_node) {
3393+
follow_mode->set_text(vformat(TTR("Following %s"), focused_node->get_name()));
3394+
follow_mode->set_button_icon(get_editor_theme_icon(focused_node->get_class()));
3395+
follow_mode->show();
3396+
focus_selection();
3397+
} else {
3398+
_disable_follow_mode();
3399+
}
3400+
} else {
3401+
follow_mode->hide();
3402+
}
3403+
33853404
Node *scene_root = SceneTreeDock::get_singleton()->get_editor_data()->get_edited_scene_root();
33863405
if (previewing_cinema && scene_root != nullptr) {
33873406
Camera3D *cam = scene_root->get_viewport()->get_camera_3d();
@@ -3677,6 +3696,8 @@ void Node3DEditorViewport::_notification(int p_what) {
36773696
override_label_colors(view_display_menu);
36783697
override_button_stylebox(translation_preview_button, information_3d_stylebox);
36793698
override_label_colors(translation_preview_button);
3699+
override_button_stylebox(follow_mode, information_3d_stylebox);
3700+
override_label_colors(follow_mode);
36803701
override_button_stylebox(preview_camera, information_3d_stylebox);
36813702
override_label_colors(preview_camera);
36823703

@@ -4085,6 +4106,7 @@ void Node3DEditorViewport::_menu_option(int p_option) {
40854106
} break;
40864107
case VIEW_CENTER_TO_ORIGIN: {
40874108
cursor.pos = Vector3(0, 0, 0);
4109+
_disable_follow_mode();
40884110

40894111
} break;
40904112
case VIEW_CENTER_TO_SELECTION: {
@@ -4541,6 +4563,11 @@ void Node3DEditorViewport::_finish_gizmo_instances() {
45414563
RS::get_singleton()->free_rid(trackball_sphere_instance);
45424564
}
45434565

4566+
void Node3DEditorViewport::_disable_follow_mode() {
4567+
// Exit follow mode by resetting the number of times the follow shortcut was used consecutively.
4568+
times_focused_consecutively = 0;
4569+
}
4570+
45444571
void Node3DEditorViewport::_toggle_camera_preview(bool p_activate) {
45454572
ERR_FAIL_COND(p_activate && !preview);
45464573
ERR_FAIL_COND(!p_activate && !previewing);
@@ -4965,6 +4992,10 @@ void Node3DEditorViewport::focus_selection() {
49654992
int count = 0;
49664993

49674994
const List<Node *> &selection = editor_selection->get_top_selected_node_list();
4995+
focused_node_id = ObjectID();
4996+
if (!selection.is_empty()) {
4997+
focused_node_id = selection.front()->get()->get_instance_id();
4998+
}
49684999

49695000
for (Node *node : selection) {
49705001
Node3D *node_3d = Object::cast_to<Node3D>(node);
@@ -6392,6 +6423,7 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p
63926423
view_display_menu->get_popup()->add_separator();
63936424
view_display_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/focus_origin"), VIEW_CENTER_TO_ORIGIN);
63946425
view_display_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/focus_selection"), VIEW_CENTER_TO_SELECTION);
6426+
view_display_menu->get_popup()->set_item_tooltip(-1, TTR("Press Focus Selection twice to start following the selection as it moves. Press it yet another time to stop following the selection."));
63956427
view_display_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/align_transform_with_view"), VIEW_ALIGN_TRANSFORM_WITH_VIEW);
63966428
view_display_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/align_rotation_with_view"), VIEW_ALIGN_ROTATION_WITH_VIEW);
63976429
view_display_menu->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &Node3DEditorViewport::_menu_option));
@@ -6419,6 +6451,12 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p
64196451
translation_preview_button = memnew(EditorTranslationPreviewButton);
64206452
hbox->add_child(translation_preview_button);
64216453

6454+
follow_mode = memnew(Button);
6455+
follow_mode->set_tooltip_text(TTR("Click to stop following this node as it moves."));
6456+
follow_mode->hide();
6457+
vbox->add_child(follow_mode);
6458+
follow_mode->connect(SceneStringName(pressed), callable_mp(this, &Node3DEditorViewport::_disable_follow_mode));
6459+
64226460
preview_camera = memnew(CheckBox);
64236461
preview_camera->set_text(TTRC("Preview"));
64246462
// Using Control even on macOS to avoid conflict with Quick Open shortcut.

editor/scene/3d/node_3d_editor_plugin.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,9 +244,12 @@ class Node3DEditorViewport : public Control {
244244
Node *target_node = nullptr;
245245
Point2 drop_pos;
246246

247+
ObjectID focused_node_id;
248+
247249
EditorSelection *editor_selection = nullptr;
248250

249251
Button *translation_preview_button = nullptr;
252+
Button *follow_mode = nullptr;
250253
CheckBox *preview_camera = nullptr;
251254
SubViewportContainer *subviewport_container = nullptr;
252255

@@ -502,10 +505,12 @@ class Node3DEditorViewport : public Control {
502505

503506
bool previewing_camera = false;
504507
bool previewing_cinema = false;
508+
int times_focused_consecutively = 0;
505509
bool _is_node_locked(const Node *p_node) const;
506510
void _preview_exited_scene();
507511
void _preview_camera_property_changed();
508512
void _update_centered_labels();
513+
void _disable_follow_mode();
509514
void _toggle_camera_preview(bool);
510515
void _toggle_cinema_preview(bool);
511516
void _init_gizmo_instance(int p_idx);

0 commit comments

Comments
 (0)