Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions editor/run/game_view_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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: {
Expand Down Expand Up @@ -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")));
Expand Down Expand Up @@ -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);
Expand Down
13 changes: 13 additions & 0 deletions editor/run/game_view_plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ class GameViewDebugger : public EditorDebuggerPlugin {
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();

Expand Down Expand Up @@ -90,6 +93,9 @@ class GameViewDebugger : public EditorDebuggerPlugin {

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);
Expand All @@ -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 {
Expand Down Expand Up @@ -153,13 +161,17 @@ 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;

Button *node_type_button[RuntimeNodeSelect::NODE_TYPE_MAX];
Button *select_mode_button[RuntimeNodeSelect::SELECT_MODE_MAX];

Button *hide_selection = nullptr;
MenuButton *selection_options_menu = nullptr;

Button *debug_mute_audio_button = nullptr;

Expand Down Expand Up @@ -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();
Expand Down
111 changes: 98 additions & 13 deletions scene/debugger/scene_debugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand All @@ -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)) {

@AThousandShips AThousandShips Dec 12, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This broke 3D disabled builds, needs to be in #ifndef _3D_DISABLED checks, will make a fix tomorrow if someone hasn't gotten to it before then

Edit: Will write a fix now, pushing momentarily

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) {
Expand Down Expand Up @@ -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);
}

Expand All @@ -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)) {
Expand Down
7 changes: 7 additions & 0 deletions scene/debugger/scene_debugger.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ class SceneDebugger {
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);
Expand Down Expand Up @@ -280,6 +282,9 @@ class RuntimeNodeSelect : public Object {
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;
Expand Down Expand Up @@ -396,6 +401,8 @@ class RuntimeNodeSelect : public Object {
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();
Expand Down