1
0
Fork 0

Merge pull request #113915 from ydeltastar/game-view-node-lock

Prevent locked and child nodes of selection groups from being selected in the Game view
This commit is contained in:
Thaddeus Crews 2025-12-12 14:15:12 -06:00
commit 08e6cd181f
No known key found for this signature in database
GPG Key ID: 8C6E5FEB5FC03CCC
4 changed files with 190 additions and 13 deletions

View File

@ -95,6 +95,12 @@ void GameViewDebugger::_session_started(Ref<EditorDebuggerSession> 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<EditorDebuggerSession> &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<EditorDebuggerSession> &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<GameViewDebugger> 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);

View File

@ -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<EditorDebuggerSession> 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();

View File

@ -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<CanvasItem>(final_node)) {
CanvasItem *ci_tmp = Object::cast_to<CanvasItem>(final_node);
order = ci_tmp->get_effective_z_index() + ci_tmp->get_canvas_layer();
} else if (Object::cast_to<Node3D>(final_node)) {
Node3D *node3d_tmp = Object::cast_to<Node3D>(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<SelectResult> &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<SelectResult> &r_items, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) {
if (!p_node || Object::cast_to<Viewport>(p_node)) {

View File

@ -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<SelectResult> &p_items, const Point2 &p_pos);
void _close_selection_list();