@@ -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
28802883void 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
31883193void 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+
45444571void 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.
0 commit comments