diff --git a/editor/run/game_view_plugin.cpp b/editor/run/game_view_plugin.cpp index 12ba56afb61..6fcef775457 100644 --- a/editor/run/game_view_plugin.cpp +++ b/editor/run/game_view_plugin.cpp @@ -95,6 +95,12 @@ void GameViewDebugger::_session_started(Ref p_session) { Array mode; mode.append(select_mode); p_session->send_message("scene:runtime_node_select_set_mode", mode); + Array avoid_locked; + avoid_locked.append(selection_avoid_locked); + p_session->send_message("scene:runtime_node_select_set_avoid_locked", avoid_locked); + Array prefer_group; + prefer_group.append(selection_prefer_group); + p_session->send_message("scene:runtime_node_select_set_prefer_group", prefer_group); Array mute_audio_data; mute_audio_data.append(mute_audio); p_session->send_message("scene:debug_mute_audio", mute_audio_data); @@ -183,6 +189,32 @@ void GameViewDebugger::set_selection_visible(bool p_visible) { } } +void GameViewDebugger::set_selection_avoid_locked(bool p_enabled) { + selection_avoid_locked = p_enabled; + + Array message; + message.append(p_enabled); + + for (Ref &I : sessions) { + if (I->is_active()) { + I->send_message("scene:runtime_node_select_set_avoid_locked", message); + } + } +} + +void GameViewDebugger::set_selection_prefer_group(bool p_enabled) { + selection_prefer_group = p_enabled; + + Array message; + message.append(p_enabled); + + for (Ref &I : sessions) { + if (I->is_active()) { + I->send_message("scene:runtime_node_select_set_prefer_group", message); + } + } +} + void GameViewDebugger::set_select_mode(int p_mode) { select_mode = p_mode; @@ -596,6 +628,25 @@ void GameView::_select_mode_pressed(int p_option) { EditorSettings::get_singleton()->set_project_metadata("game_view", "select_mode", mode); } +void GameView::_selection_options_menu_id_pressed(int p_id) { + switch (p_id) { + case SELECTION_AVOID_LOCKED: { + selection_avoid_locked = !selection_avoid_locked; + debugger->set_selection_avoid_locked(selection_avoid_locked); + EditorSettings::get_singleton()->set_project_metadata("game_view", "selection_avoid_locked", selection_avoid_locked); + } break; + case SELECTION_PREFER_GROUP: { + selection_prefer_group = !selection_prefer_group; + debugger->set_selection_prefer_group(selection_prefer_group); + EditorSettings::get_singleton()->set_project_metadata("game_view", "selection_prefer_group", selection_prefer_group); + } break; + } + + PopupMenu *menu = selection_options_menu->get_popup(); + menu->set_item_checked(menu->get_item_index(SELECTION_AVOID_LOCKED), selection_avoid_locked); + menu->set_item_checked(menu->get_item_index(SELECTION_PREFER_GROUP), selection_prefer_group); +} + void GameView::_embed_options_menu_menu_id_pressed(int p_id) { switch (p_id) { case EMBED_RUN_GAME_EMBEDDED: { @@ -880,6 +931,7 @@ void GameView::_notification(int p_what) { select_mode_button[RuntimeNodeSelect::SELECT_MODE_LIST]->set_button_icon(get_editor_theme_icon(SNAME("ListSelect"))); hide_selection->set_button_icon(get_editor_theme_icon(hide_selection->is_pressed() ? SNAME("GuiVisibilityHidden") : SNAME("GuiVisibilityVisible"))); + selection_options_menu->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); embed_options_menu->set_button_icon(get_editor_theme_icon(SNAME("KeepAspect"))); debug_mute_audio_button->set_button_icon(get_editor_theme_icon(debug_mute_audio ? SNAME("AudioMute") : SNAME("AudioStreamPlayer"))); @@ -1272,6 +1324,26 @@ GameView::GameView(Ref p_debugger, EmbeddedProcessBase *p_embe } hide_selection->connect(SceneStringName(toggled), callable_mp(this, &GameView::_hide_selection_toggled)); + selection_options_menu = memnew(MenuButton); + selection_hb->add_child(selection_options_menu); + selection_options_menu->set_flat(false); + selection_options_menu->set_theme_type_variation("FlatMenuButton"); + selection_options_menu->set_h_size_flags(SIZE_SHRINK_END); + selection_options_menu->set_tooltip_text(TTRC("Selection Options")); + + PopupMenu *selection_menu = selection_options_menu->get_popup(); + selection_menu->connect(SceneStringName(id_pressed), callable_mp(this, &GameView::_selection_options_menu_id_pressed)); + selection_menu->add_check_item(TTRC("Don't Select Locked Nodes"), SELECTION_AVOID_LOCKED); + selection_menu->add_check_item(TTRC("Select Group Over Children"), SELECTION_PREFER_GROUP); + + selection_avoid_locked = EditorSettings::get_singleton()->get_project_metadata("game_view", "selection_avoid_locked", false); + selection_prefer_group = EditorSettings::get_singleton()->get_project_metadata("game_view", "selection_prefer_group", false); + selection_menu->set_item_checked(selection_menu->get_item_index(SELECTION_AVOID_LOCKED), selection_avoid_locked); + selection_menu->set_item_checked(selection_menu->get_item_index(SELECTION_PREFER_GROUP), selection_prefer_group); + + debugger->set_selection_avoid_locked(selection_avoid_locked); + debugger->set_selection_prefer_group(selection_prefer_group); + selection_hb->add_child(memnew(VSeparator)); HBoxContainer *audio_hb = memnew(HBoxContainer); diff --git a/editor/run/game_view_plugin.h b/editor/run/game_view_plugin.h index cfb00f5ba21..c4e31e388dc 100644 --- a/editor/run/game_view_plugin.h +++ b/editor/run/game_view_plugin.h @@ -55,6 +55,9 @@ private: bool mute_audio = false; EditorDebuggerNode::CameraOverride camera_override_mode = EditorDebuggerNode::OVERRIDE_INGAME; + bool selection_avoid_locked = false; + bool selection_prefer_group = false; + void _session_started(Ref p_session); void _session_stopped(); @@ -90,6 +93,9 @@ public: void set_selection_visible(bool p_visible); + void set_selection_avoid_locked(bool p_enabled); + void set_selection_prefer_group(bool p_enabled); + void set_debug_mute_audio(bool p_enabled); void set_camera_override(bool p_enabled); @@ -113,6 +119,8 @@ class GameView : public VBoxContainer { CAMERA_MODE_EDITORS, EMBED_RUN_GAME_EMBEDDED, EMBED_MAKE_FLOATING_ON_PLAY, + SELECTION_AVOID_LOCKED, + SELECTION_PREFER_GROUP, }; enum EmbedSizeMode { @@ -153,6 +161,9 @@ class GameView : public VBoxContainer { bool debug_mute_audio = false; + bool selection_avoid_locked = false; + bool selection_prefer_group = false; + Button *suspend_button = nullptr; Button *next_frame_button = nullptr; @@ -160,6 +171,7 @@ class GameView : public VBoxContainer { Button *select_mode_button[RuntimeNodeSelect::SELECT_MODE_MAX]; Button *hide_selection = nullptr; + MenuButton *selection_options_menu = nullptr; Button *debug_mute_audio_button = nullptr; @@ -191,6 +203,7 @@ class GameView : public VBoxContainer { void _node_type_pressed(int p_option); void _select_mode_pressed(int p_option); + void _selection_options_menu_id_pressed(int p_id); void _embed_options_menu_menu_id_pressed(int p_id); void _reset_time_scales(); diff --git a/scene/debugger/scene_debugger.cpp b/scene/debugger/scene_debugger.cpp index 637c9298e4e..00ec53c676b 100644 --- a/scene/debugger/scene_debugger.cpp +++ b/scene/debugger/scene_debugger.cpp @@ -415,6 +415,20 @@ Error SceneDebugger::_msg_runtime_node_select_set_visible(const Array &p_args) { return OK; } +Error SceneDebugger::_msg_runtime_node_select_set_avoid_locked(const Array &p_args) { + ERR_FAIL_COND_V(p_args.is_empty(), ERR_INVALID_DATA); + bool avoid_locked = p_args[0]; + RuntimeNodeSelect::get_singleton()->_set_avoid_locked(avoid_locked); + return OK; +} + +Error SceneDebugger::_msg_runtime_node_select_set_prefer_group(const Array &p_args) { + ERR_FAIL_COND_V(p_args.is_empty(), ERR_INVALID_DATA); + bool prefer_group = p_args[0]; + RuntimeNodeSelect::get_singleton()->_set_prefer_group(prefer_group); + return OK; +} + Error SceneDebugger::_msg_runtime_node_select_reset_camera_2d(const Array &p_args) { RuntimeNodeSelect::get_singleton()->_reset_camera_2d(); return OK; @@ -567,6 +581,8 @@ void SceneDebugger::_init_message_handlers() { message_handlers["runtime_node_select_set_type"] = _msg_runtime_node_select_set_type; message_handlers["runtime_node_select_set_mode"] = _msg_runtime_node_select_set_mode; message_handlers["runtime_node_select_set_visible"] = _msg_runtime_node_select_set_visible; + message_handlers["runtime_node_select_set_avoid_locked"] = _msg_runtime_node_select_set_avoid_locked; + message_handlers["runtime_node_select_set_prefer_group"] = _msg_runtime_node_select_set_prefer_group; message_handlers["runtime_node_select_reset_camera_2d"] = _msg_runtime_node_select_reset_camera_2d; #ifndef _3D_DISABLED message_handlers["runtime_node_select_reset_camera_3d"] = _msg_runtime_node_select_reset_camera_3d; @@ -1859,18 +1875,6 @@ void RuntimeNodeSelect::_physics_frame() { } } - // Remove possible duplicates. - for (int i = 0; i < items.size(); i++) { - Node *item = items[i].item; - for (int j = 0; j < i; j++) { - if (items[j].item == item) { - items.remove_at(i); - i--; - - break; - } - } - } #ifndef _3D_DISABLED } else if (node_select_type == NODE_TYPE_3D) { if (selection_drag_valid) { @@ -1881,6 +1885,57 @@ void RuntimeNodeSelect::_physics_frame() { #endif // _3D_DISABLED } + if ((prefer_group_selection || avoid_locked_nodes) && !list_shortcut_pressed && node_select_mode == SELECT_MODE_SINGLE) { + for (int i = 0; i < items.size(); i++) { + Node *node = items[i].item; + Node *final_node = node; + real_t order = items[i].order; + + // Replace the node by the group if grouped. + if (prefer_group_selection) { + while (node && node != root) { + if (node->has_meta("_edit_group_")) { + final_node = node; + + if (Object::cast_to(final_node)) { + CanvasItem *ci_tmp = Object::cast_to(final_node); + order = ci_tmp->get_effective_z_index() + ci_tmp->get_canvas_layer(); + } else if (Object::cast_to(final_node)) { + Node3D *node3d_tmp = Object::cast_to(final_node); + Camera3D *camera = root->get_camera_3d(); + Vector3 pos = camera->project_ray_origin(selection_position); + order = -pos.distance_to(node3d_tmp->get_global_transform().origin); + } + } + node = node->get_parent(); + } + } + + // Filter out locked nodes. + if (avoid_locked_nodes && final_node->get_meta("_edit_lock_", false)) { + items.remove_at(i); + i--; + continue; + } + + items.write[i].item = final_node; + items.write[i].order = order; + } + } + + // Remove possible duplicates. + for (int i = 0; i < items.size(); i++) { + Node *item = items[i].item; + for (int j = 0; j < i; j++) { + if (items[j].item == item) { + items.remove_at(i); + i--; + + break; + } + } + } + items.sort(); switch (selection_drag_state) { @@ -2334,7 +2389,29 @@ void RuntimeNodeSelect::_open_selection_list(const Vector &p_items root->add_child(selection_list); for (const SelectResult &I : p_items) { - selection_list->add_item(I.item->get_name()); + int locked = 0; + if (I.item->get_meta("_edit_lock_", false)) { + locked = 1; + } else { + Node *scene = SceneTree::get_singleton()->get_root(); + Node *node = I.item; + + while (node && node != scene->get_parent()) { + if (node->has_meta("_edit_group_")) { + locked = 2; + } + node = node->get_parent(); + } + } + + String suffix; + if (locked == 1) { + suffix = " (" + RTR("Locked") + ")"; + } else if (locked == 2) { + suffix = " (" + RTR("Grouped") + ")"; + } + + selection_list->add_item((String)I.item->get_name() + suffix); selection_list->set_item_metadata(-1, I.item); } @@ -2358,6 +2435,14 @@ void RuntimeNodeSelect::_set_selection_visible(bool p_visible) { } } +void RuntimeNodeSelect::_set_avoid_locked(bool p_enabled) { + avoid_locked_nodes = p_enabled; +} + +void RuntimeNodeSelect::_set_prefer_group(bool p_enabled) { + prefer_group_selection = p_enabled; +} + // Copied and trimmed from the CanvasItemEditor implementation. void RuntimeNodeSelect::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, Vector &r_items, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) { if (!p_node || Object::cast_to(p_node)) { diff --git a/scene/debugger/scene_debugger.h b/scene/debugger/scene_debugger.h index 1f183ec0a92..f4e1f477ece 100644 --- a/scene/debugger/scene_debugger.h +++ b/scene/debugger/scene_debugger.h @@ -115,6 +115,8 @@ private: static Error _msg_runtime_node_select_set_type(const Array &p_args); static Error _msg_runtime_node_select_set_mode(const Array &p_args); static Error _msg_runtime_node_select_set_visible(const Array &p_args); + static Error _msg_runtime_node_select_set_avoid_locked(const Array &p_args); + static Error _msg_runtime_node_select_set_prefer_group(const Array &p_args); static Error _msg_rq_screenshot(const Array &p_args); static Error _msg_runtime_node_select_reset_camera_2d(const Array &p_args); @@ -280,6 +282,9 @@ private: bool selection_visible = true; bool selection_update_queued = false; + bool avoid_locked_nodes = false; + bool prefer_group_selection = false; + bool multi_shortcut_pressed = false; bool list_shortcut_pressed = false; RID draw_canvas; @@ -396,6 +401,8 @@ private: void _clear_selection(); void _update_selection_drag(const Point2 &p_end_pos = Point2()); void _set_selection_visible(bool p_visible); + void _set_avoid_locked(bool p_enabled); + void _set_prefer_group(bool p_enabled); void _open_selection_list(const Vector &p_items, const Point2 &p_pos); void _close_selection_list();