Merge pull request #104860 from YeldhamDev/2d_selection_config

Allow changing the color of canvas selection
This commit is contained in:
Rémi Verschelde 2026-03-09 11:49:13 +01:00
commit ef8a050a76
No known key found for this signature in database
GPG key ID: C3336907360768E1
7 changed files with 54 additions and 48 deletions

View file

@ -352,9 +352,15 @@
<member name="editors/2d/guides_color" type="Color" setter="" getter="">
The guides color to use in the 2D editor. Guides can be created by dragging the mouse cursor from the rulers.
</member>
<member name="editors/2d/locked_selection_rectangle_color" type="Color" setter="" getter="">
The color to use for the selection rectangle that surrounds selected locked nodes in the 2D editor viewport.
</member>
<member name="editors/2d/ruler_width" type="float" setter="" getter="">
The thickness of the coordinate ruler in the 2D editor. Increasing this will also increase the size of the ruler font, improving readability when using a lower editor scale. The editor may force a minimum size to keep the ruler numbers legible.
</member>
<member name="editors/2d/selection_rectangle_color" type="Color" setter="" getter="">
The color to use for the selection rectangle outlines that surrounds selected nodes in the 2D editor viewport.
</member>
<member name="editors/2d/smart_snapping_line_color" type="Color" setter="" getter="">
The color to use when drawing smart snapping lines in the 2D editor. The smart snapping lines will automatically display when moving 2D nodes if smart snapping is enabled in the Snapping Options menu at the top of the 2D editor viewport.
</member>
@ -368,7 +374,7 @@
The factor to use when zooming in or out in the 2D editor. For example, [code]1.1[/code] will zoom in by 10% with every step. If set to [code]2.0[/code], zooming will only cycle through powers of two.
</member>
<member name="editors/3d/active_selection_box_color" type="Color" setter="" getter="">
The color to use for the active selection box that surrounds selected nodes in the 3D editor viewport. The color's alpha channel influences the selection box's opacity.
The color to use for the active selection box that surrounds selected nodes in the 3D editor viewport.
[b]Note:[/b] The term "active" indicates that this object is the primary selection used as the basis for certain operations. This is the last selected [Node3D], which can be reordered with [kbd]Shift + Left mouse button[/kbd].
</member>
<member name="editors/3d/default_fov" type="float" setter="" getter="">
@ -507,7 +513,7 @@
The color to use for the secondary 3D grid. This is generally a less visible color than [member editors/3d/primary_grid_color]. The color's alpha channel affects the grid's opacity.
</member>
<member name="editors/3d/selection_box_color" type="Color" setter="" getter="">
The color to use for the selection box that surrounds selected nodes in the 3D editor viewport. The color's alpha channel influences the selection box's opacity.
The color to use for the selection box that surrounds selected nodes in the 3D editor viewport.
</member>
<member name="editors/3d/show_gizmo_during_rotation" type="int" setter="" getter="">
If checked, the transform gizmo remains visible during rotation in that transform mode.

View file

@ -66,6 +66,7 @@ void GameViewDebugger::_session_started(Ref<EditorDebuggerSession> p_session) {
Dictionary settings;
settings["debugger/max_node_selection"] = EDITOR_GET("debugger/max_node_selection");
settings["editors/2d/selection_rectangle_color"] = EDITOR_GET("editors/2d/selection_rectangle_color");
settings["editors/panning/2d_editor_panning_scheme"] = EDITOR_GET("editors/panning/2d_editor_panning_scheme");
settings["editors/panning/simple_panning"] = EDITOR_GET("editors/panning/simple_panning");
settings["editors/panning/warped_mouse_panning"] = EDITOR_GET("editors/panning/warped_mouse_panning");
@ -1623,8 +1624,7 @@ GameViewPluginBase::GameViewPluginBase() {
#endif
}
GameViewPlugin::GameViewPlugin() :
GameViewPluginBase() {
GameViewPlugin::GameViewPlugin() {
#ifndef ANDROID_ENABLED
Ref<GameViewDebugger> game_view_debugger;
game_view_debugger.instantiate();

View file

@ -3807,14 +3807,8 @@ void CanvasItemEditor::_draw_selection() {
xform.xform(rect.position + Vector2(0, rect.size.y))
};
Color c = Color(1, 0.6, 0.4, 0.7);
if (item_locked) {
c = Color(0.7, 0.7, 0.7, 0.7);
}
for (int i = 0; i < 4; i++) {
viewport->draw_line(endpoints[i], endpoints[(i + 1) % 4], c, Math::round(2 * EDSCALE));
viewport->draw_line(endpoints[i], endpoints[(i + 1) % 4], item_locked ? locked_selection_rectangle_color : selection_rectangle_color, Math::round(2 * EDSCALE));
}
} else {
Transform2D unscaled_transform = (xform * ci->get_transform().affine_inverse() * ci->_edit_get_transform()).orthonormalized();
@ -4348,6 +4342,9 @@ void CanvasItemEditor::_update_editor_settings() {
key_auto_insert_button->add_theme_color_override("icon_pressed_color", key_auto_color.lerp(Color(1, 0, 0), 0.55));
animation_menu->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl")));
selection_rectangle_color = EDITOR_GET("editors/2d/selection_rectangle_color");
locked_selection_rectangle_color = EDITOR_GET("editors/2d/locked_selection_rectangle_color");
context_toolbar_panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("ContextualToolbar"), EditorStringName(EditorStyles)));
simple_panning = EDITOR_GET("editors/panning/simple_panning");

View file

@ -244,6 +244,9 @@ private:
Vector2i primary_grid_step;
int grid_step_multiplier = 0;
Color selection_rectangle_color;
Color locked_selection_rectangle_color;
real_t snap_rotation_step = 0.0;
real_t snap_rotation_offset = 0.0;
real_t snap_scale_step = 0.0;
@ -279,7 +282,7 @@ private:
real_t grab_distance = 0.0;
bool simple_panning = false;
MenuOption last_option;
MenuOption last_option = SNAP_USE;
public:
struct SelectResult {

View file

@ -993,6 +993,8 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_initial_set("editors/2d/grid_color", Color(1.0, 1.0, 1.0, 0.07), true);
_initial_set("editors/2d/guides_color", Color(0.6, 0.0, 0.8), true);
_initial_set("editors/2d/smart_snapping_line_color", Color(0.9, 0.1, 0.1), true);
_initial_set("editors/2d/selection_rectangle_color", Color(1, 0.6, 0.4, 0.7), true);
_initial_set("editors/2d/locked_selection_rectangle_color", Color(0.7, 0.7, 0.7, 0.7), true);
EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "editors/2d/bone_width", 5.0, "0.01,20,0.01,or_greater")
_initial_set("editors/2d/bone_color1", Color(1.0, 1.0, 1.0, 0.7));
_initial_set("editors/2d/bone_color2", Color(0.6, 0.6, 0.6, 0.7));

View file

@ -78,7 +78,7 @@ RuntimeNodeSelect::~RuntimeNodeSelect() {
if (draw_canvas.is_valid()) {
RS::get_singleton()->free_rid(sel_drag_ci);
RS::get_singleton()->free_rid(sbox_2d_ci);
RS::get_singleton()->free_rid(srect_ci);
RS::get_singleton()->free_rid(draw_canvas);
}
}
@ -114,12 +114,14 @@ void RuntimeNodeSelect::_setup(const Dictionary &p_settings) {
draw_canvas = RS::get_singleton()->canvas_create();
sel_drag_ci = RS::get_singleton()->canvas_item_create();
/// 2D Selection Box Generation
/// 2D Selection Rectangle Generation
sbox_2d_ci = RS::get_singleton()->canvas_item_create();
srect_color = p_settings.get("editors/2d/selection_rectangle_color", Color());
srect_ci = RS::get_singleton()->canvas_item_create();
RS::get_singleton()->viewport_attach_canvas(root->get_viewport_rid(), draw_canvas);
RS::get_singleton()->canvas_item_set_parent(sel_drag_ci, draw_canvas);
RS::get_singleton()->canvas_item_set_parent(sbox_2d_ci, draw_canvas);
RS::get_singleton()->canvas_item_set_parent(srect_ci, draw_canvas);
#ifndef _3D_DISABLED
camera_fov = p_settings.get("editors/3d/default_fov", 70);
@ -199,7 +201,7 @@ void RuntimeNodeSelect::_setup(const Dictionary &p_settings) {
/// 3D Selection Box Generation
// Copied from the Node3DEditor implementation.
sbox_3d_color = p_settings.get("editors/3d/selection_box_color", Color());
sbox_color = p_settings.get("editors/3d/selection_box_color", Color());
// Use two AABBs to create the illusion of a slightly thicker line.
AABB aabb(Vector3(), Vector3(1, 1, 1));
@ -225,19 +227,19 @@ void RuntimeNodeSelect::_setup(const Dictionary &p_settings) {
Ref<StandardMaterial3D> mat = memnew(StandardMaterial3D);
mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
mat->set_albedo(sbox_3d_color);
mat->set_albedo(sbox_color);
mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
st->set_material(mat);
sbox_3d_mesh = st->commit();
sbox_mesh = st->commit();
Ref<StandardMaterial3D> mat_xray = memnew(StandardMaterial3D);
mat_xray->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
mat_xray->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
mat_xray->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true);
mat_xray->set_albedo(sbox_3d_color * Color(1, 1, 1, 0.15));
mat_xray->set_albedo(sbox_color * Color(1, 1, 1, 0.15));
mat_xray->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
st_xray->set_material(mat_xray);
sbox_3d_mesh_xray = st_xray->commit();
sbox_mesh_xray = st_xray->commit();
#endif // _3D_DISABLED
SceneTree::get_singleton()->connect("process_frame", callable_mp(this, &RuntimeNodeSelect::_process_frame));
@ -633,7 +635,7 @@ void RuntimeNodeSelect::_send_ids(const Vector<Node *> &p_picked_nodes, bool p_i
nodes.push_back(ObjectDB::get_instance<Node>(id));
}
#ifndef _3D_DISABLED
for (const KeyValue<ObjectID, Ref<SelectionBox3D>> &KV : selected_3d_nodes) {
for (const KeyValue<ObjectID, Ref<SelectionBox>> &KV : selected_3d_nodes) {
ids.push_back(KV.key);
nodes.push_back(ObjectDB::get_instance<Node>(KV.key));
}
@ -664,7 +666,7 @@ void RuntimeNodeSelect::_set_selected_nodes(const Vector<Node *> &p_nodes) {
bool changed = false;
LocalVector<ObjectID> nodes_ci;
#ifndef _3D_DISABLED
HashMap<ObjectID, Ref<SelectionBox3D>> nodes_3d;
HashMap<ObjectID, Ref<SelectionBox>> nodes_3d;
#endif // _3D_DISABLED
for (Node *node : p_nodes) {
@ -692,18 +694,18 @@ void RuntimeNodeSelect::_set_selected_nodes(const Vector<Node *> &p_nodes) {
continue;
}
if (sbox_3d_mesh.is_null() || sbox_3d_mesh_xray.is_null()) {
if (sbox_mesh.is_null() || sbox_mesh_xray.is_null()) {
continue;
}
Ref<SelectionBox3D> sb;
Ref<SelectionBox> sb;
sb.instantiate();
nodes_3d[id] = sb;
RID scenario = node_3d->get_world_3d()->get_scenario();
sb->instance = RS::get_singleton()->instance_create2(sbox_3d_mesh->get_rid(), scenario);
sb->instance_ofs = RS::get_singleton()->instance_create2(sbox_3d_mesh->get_rid(), scenario);
sb->instance = RS::get_singleton()->instance_create2(sbox_mesh->get_rid(), scenario);
sb->instance_ofs = RS::get_singleton()->instance_create2(sbox_mesh->get_rid(), scenario);
RS::get_singleton()->instance_geometry_set_cast_shadows_setting(sb->instance, RSE::SHADOW_CASTING_SETTING_OFF);
RS::get_singleton()->instance_geometry_set_cast_shadows_setting(sb->instance_ofs, RSE::SHADOW_CASTING_SETTING_OFF);
RS::get_singleton()->instance_geometry_set_flag(sb->instance, RSE::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true);
@ -711,8 +713,8 @@ void RuntimeNodeSelect::_set_selected_nodes(const Vector<Node *> &p_nodes) {
RS::get_singleton()->instance_geometry_set_flag(sb->instance_ofs, RSE::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true);
RS::get_singleton()->instance_geometry_set_flag(sb->instance_ofs, RSE::INSTANCE_FLAG_USE_BAKED_LIGHT, false);
sb->instance_xray = RS::get_singleton()->instance_create2(sbox_3d_mesh_xray->get_rid(), scenario);
sb->instance_xray_ofs = RS::get_singleton()->instance_create2(sbox_3d_mesh_xray->get_rid(), scenario);
sb->instance_xray = RS::get_singleton()->instance_create2(sbox_mesh_xray->get_rid(), scenario);
sb->instance_xray_ofs = RS::get_singleton()->instance_create2(sbox_mesh_xray->get_rid(), scenario);
RS::get_singleton()->instance_geometry_set_cast_shadows_setting(sb->instance_xray, RSE::SHADOW_CASTING_SETTING_OFF);
RS::get_singleton()->instance_geometry_set_cast_shadows_setting(sb->instance_xray_ofs, RSE::SHADOW_CASTING_SETTING_OFF);
RS::get_singleton()->instance_geometry_set_flag(sb->instance_xray, RSE::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true);
@ -758,8 +760,8 @@ void RuntimeNodeSelect::_queue_selection_update() {
}
void RuntimeNodeSelect::_update_selection() {
RS::get_singleton()->canvas_item_clear(sbox_2d_ci);
RS::get_singleton()->canvas_item_set_visible(sbox_2d_ci, selection_visible);
RS::get_singleton()->canvas_item_clear(srect_ci);
RS::get_singleton()->canvas_item_set_visible(srect_ci, selection_visible);
for (LocalVector<ObjectID>::Iterator E = selected_ci_nodes.begin(); E != selected_ci_nodes.end(); ++E) {
ObjectID id = *E;
@ -800,14 +802,13 @@ void RuntimeNodeSelect::_update_selection() {
xform.xform(rect.position + Point2(0, rect.size.y))
};
const Color selection_color_2d = Color(1, 0.6, 0.4, 0.7);
for (int i = 0; i < 4; i++) {
RS::get_singleton()->canvas_item_add_line(sbox_2d_ci, endpoints[i], endpoints[(i + 1) % 4], selection_color_2d, sel_2d_scale);
RS::get_singleton()->canvas_item_add_line(srect_ci, endpoints[i], endpoints[(i + 1) % 4], srect_color, sel_2d_scale);
}
}
#ifndef _3D_DISABLED
for (HashMap<ObjectID, Ref<SelectionBox3D>>::ConstIterator KV = selected_3d_nodes.begin(); KV != selected_3d_nodes.end(); ++KV) {
for (HashMap<ObjectID, Ref<SelectionBox>>::ConstIterator KV = selected_3d_nodes.begin(); KV != selected_3d_nodes.end(); ++KV) {
ObjectID id = KV->key;
Node3D *node_3d = ObjectDB::get_instance<Node3D>(id);
if (!node_3d) {
@ -842,7 +843,7 @@ void RuntimeNodeSelect::_update_selection() {
bounds = xform_to_top_level_parent_space.xform(bounds);
Transform3D t = node_3d->get_global_transform();
Ref<SelectionBox3D> sb = KV->value;
Ref<SelectionBox> sb = KV->value;
if (t == sb->transform && bounds == sb->bounds) {
continue; // Nothing changed.
}
@ -883,7 +884,7 @@ void RuntimeNodeSelect::_update_selection() {
void RuntimeNodeSelect::_clear_selection() {
selected_ci_nodes.clear();
if (draw_canvas.is_valid()) {
RS::get_singleton()->canvas_item_clear(sbox_2d_ci);
RS::get_singleton()->canvas_item_clear(srect_ci);
}
#ifndef _3D_DISABLED
@ -1489,7 +1490,7 @@ void RuntimeNodeSelect::_reset_camera_3d() {
}
}
RuntimeNodeSelect::SelectionBox3D::~SelectionBox3D() {
RuntimeNodeSelect::SelectionBox::~SelectionBox() {
if (instance.is_valid()) {
RS::get_singleton()->free_rid(instance);
RS::get_singleton()->free_rid(instance_ofs);

View file

@ -119,7 +119,8 @@ private:
real_t sel_2d_grab_dist = 0;
int sel_2d_scale = 1;
RID sbox_2d_ci;
Color srect_color;
RID srect_ci;
#ifndef _3D_DISABLED
Ref<View3DController> view_3d_controller;
@ -128,7 +129,7 @@ private:
real_t camera_znear = 0;
real_t camera_zfar = 0;
struct SelectionBox3D : public RefCounted {
struct SelectionBox : public RefCounted {
RID instance;
RID instance_ofs;
RID instance_xray;
@ -137,17 +138,13 @@ private:
Transform3D transform;
AABB bounds;
~SelectionBox3D();
~SelectionBox();
};
HashMap<ObjectID, Ref<SelectionBox3D>> selected_3d_nodes;
HashMap<ObjectID, Ref<SelectionBox>> selected_3d_nodes;
Color sbox_3d_color;
Ref<ArrayMesh> sbox_3d_mesh;
Ref<ArrayMesh> sbox_3d_mesh_xray;
RID sbox_3d;
RID sbox_3d_ofs;
RID sbox_3d_xray;
RID sbox_3d_xray_ofs;
Color sbox_color;
Ref<ArrayMesh> sbox_mesh;
Ref<ArrayMesh> sbox_mesh_xray;
#endif // _3D_DISABLED
void _setup(const Dictionary &p_settings);