Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/editor/settings/editor_build_profile.cpp
20850 views
1
/**************************************************************************/
2
/* editor_build_profile.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#include "editor_build_profile.h"
32
33
#include "core/config/project_settings.h"
34
#include "core/io/json.h"
35
#include "editor/editor_node.h"
36
#include "editor/editor_string_names.h"
37
#include "editor/file_system/editor_file_system.h"
38
#include "editor/file_system/editor_paths.h"
39
#include "editor/gui/editor_file_dialog.h"
40
#include "editor/settings/editor_settings.h"
41
#include "editor/themes/editor_scale.h"
42
#include "scene/gui/line_edit.h"
43
#include "scene/gui/margin_container.h"
44
#include "scene/gui/separator.h"
45
46
#include "modules/modules_enabled.gen.h" // For mono.
47
48
const char *EditorBuildProfile::build_option_identifiers[BUILD_OPTION_MAX] = {
49
// This maps to SCons build options.
50
"disable_3d",
51
"disable_navigation_2d",
52
"disable_navigation_3d",
53
"accesskit",
54
"sdl",
55
"disable_xr",
56
"module_openxr_enabled",
57
"wayland",
58
"x11",
59
"pulseaudio",
60
"alsa",
61
"rendering_device", // FIXME: There's no scons option to disable rendering device.
62
"forward_plus_renderer",
63
"forward_mobile_renderer",
64
"vulkan",
65
"d3d12",
66
"metal",
67
"opengl3",
68
"disable_physics_2d",
69
"module_godot_physics_2d_enabled",
70
"disable_physics_3d",
71
"module_godot_physics_3d_enabled",
72
"module_jolt_physics_enabled",
73
"module_text_server_fb_enabled",
74
"module_text_server_adv_enabled",
75
"module_freetype_enabled",
76
"brotli",
77
"graphite",
78
"module_msdfgen_enabled",
79
};
80
81
const bool EditorBuildProfile::build_option_disabled_by_default[BUILD_OPTION_MAX] = {
82
// This maps to SCons build options.
83
false, // 3D
84
false, // NAVIGATION_2D
85
false, // NAVIGATION_3D
86
false, // ACCESSKIT
87
false, // SDL
88
false, // XR
89
false, // OPENXR
90
false, // WAYLAND
91
false, // X11
92
false, // PULSEAUDIO
93
false, // ALSA
94
false, // RENDERING_DEVICE
95
false, // FORWARD_RENDERER
96
false, // MOBILE_RENDERER
97
false, // VULKAN
98
false, // D3D12
99
false, // METAL
100
false, // OPENGL
101
false, // PHYSICS_2D
102
false, // PHYSICS_GODOT_2D
103
false, // PHYSICS_3D
104
false, // PHYSICS_GODOT_3D
105
false, // PHYSICS_JOLT
106
true, // TEXT_SERVER_FALLBACK
107
false, // TEXT_SERVER_ADVANCED
108
false, // DYNAMIC_FONTS
109
false, // WOFF2_FONTS
110
false, // GRAPHITE_FONTS
111
false, // MSDFGEN
112
};
113
114
const bool EditorBuildProfile::build_option_disable_values[BUILD_OPTION_MAX] = {
115
// This maps to SCons build options.
116
true, // 3D
117
true, // NAVIGATION_2D
118
true, // NAVIGATION_3D
119
false, // ACCESSKIT
120
false, // SDL
121
true, // XR
122
false, // OPENXR
123
false, // WAYLAND
124
false, // X11
125
false, // PULSEAUDIO
126
false, // ALSA
127
false, // RENDERING_DEVICE
128
false, // FORWARD_RENDERER
129
false, // MOBILE_RENDERER
130
false, // VULKAN
131
false, // D3D12
132
false, // METAL
133
false, // OPENGL
134
true, // PHYSICS_2D
135
false, // PHYSICS_GODOT_2D
136
true, // PHYSICS_3D
137
false, // PHYSICS_GODOT_3D
138
false, // PHYSICS_JOLT
139
false, // TEXT_SERVER_FALLBACK
140
false, // TEXT_SERVER_ADVANCED
141
false, // DYNAMIC_FONTS
142
false, // WOFF2_FONTS
143
false, // GRAPHITE_FONTS
144
false, // MSDFGEN
145
};
146
147
// Options that require some resource explicitly asking for them when detecting from the project.
148
const bool EditorBuildProfile::build_option_explicit_use[BUILD_OPTION_MAX] = {
149
false, // 3D
150
false, // NAVIGATION_2D
151
false, // NAVIGATION_3D
152
false, // ACCESSKIT
153
false, // SDL
154
false, // XR
155
false, // OPENXR
156
false, // WAYLAND
157
false, // X11
158
false, // PULSEAUDIO
159
false, // ALSA
160
false, // RENDERING_DEVICE
161
false, // FORWARD_RENDERER
162
false, // MOBILE_RENDERER
163
false, // VULKAN
164
false, // D3D12
165
false, // METAL
166
false, // OPENGL
167
false, // PHYSICS_2D
168
false, // PHYSICS_GODOT_2D
169
false, // PHYSICS_3D
170
false, // PHYSICS_GODOT_3D
171
false, // PHYSICS_JOLT
172
false, // TEXT_SERVER_FALLBACK
173
false, // TEXT_SERVER_ADVANCED
174
false, // DYNAMIC_FONTS
175
false, // WOFF2_FONTS
176
false, // GRAPHITE_FONTS
177
true, // MSDFGEN
178
};
179
180
const EditorBuildProfile::BuildOptionCategory EditorBuildProfile::build_option_category[BUILD_OPTION_MAX] = {
181
BUILD_OPTION_CATEGORY_GENERAL, // 3D
182
BUILD_OPTION_CATEGORY_GENERAL, // NAVIGATION_2D
183
BUILD_OPTION_CATEGORY_GENERAL, // NAVIGATION_3D
184
BUILD_OPTION_CATEGORY_GENERAL, // ACCESSKIT
185
BUILD_OPTION_CATEGORY_GENERAL, // SDL
186
BUILD_OPTION_CATEGORY_GENERAL, // XR
187
BUILD_OPTION_CATEGORY_GENERAL, // OPENXR
188
BUILD_OPTION_CATEGORY_GENERAL, // WAYLAND
189
BUILD_OPTION_CATEGORY_GENERAL, // X11
190
BUILD_OPTION_CATEGORY_GENERAL, // PULSEAUDIO
191
BUILD_OPTION_CATEGORY_GENERAL, // ALSA
192
BUILD_OPTION_CATEGORY_GRAPHICS, // RENDERING_DEVICE
193
BUILD_OPTION_CATEGORY_GRAPHICS, // FORWARD_RENDERER
194
BUILD_OPTION_CATEGORY_GRAPHICS, // MOBILE_RENDERER
195
BUILD_OPTION_CATEGORY_GRAPHICS, // VULKAN
196
BUILD_OPTION_CATEGORY_GRAPHICS, // D3D12
197
BUILD_OPTION_CATEGORY_GRAPHICS, // METAL
198
BUILD_OPTION_CATEGORY_GRAPHICS, // OPENGL
199
BUILD_OPTION_CATEGORY_PHYSICS, // PHYSICS_2D
200
BUILD_OPTION_CATEGORY_PHYSICS, // PHYSICS_GODOT_2D
201
BUILD_OPTION_CATEGORY_PHYSICS, // PHYSICS_3D
202
BUILD_OPTION_CATEGORY_PHYSICS, // PHYSICS_GODOT_3D
203
BUILD_OPTION_CATEGORY_PHYSICS, // PHYSICS_JOLT
204
BUILD_OPTION_CATEGORY_TEXT_SERVER, // TEXT_SERVER_FALLBACK
205
BUILD_OPTION_CATEGORY_TEXT_SERVER, // TEXT_SERVER_ADVANCED
206
BUILD_OPTION_CATEGORY_TEXT_SERVER, // DYNAMIC_FONTS
207
BUILD_OPTION_CATEGORY_TEXT_SERVER, // WOFF2_FONTS
208
BUILD_OPTION_CATEGORY_TEXT_SERVER, // GRAPHITE_FONTS
209
BUILD_OPTION_CATEGORY_TEXT_SERVER, // MSDFGEN
210
};
211
212
// Can't assign HashMaps to a HashMap at declaration, so do it in the class' constructor.
213
HashMap<EditorBuildProfile::BuildOption, HashMap<String, LocalVector<Variant>>> EditorBuildProfile::build_option_settings = {};
214
215
/* clang-format off */
216
217
const HashMap<EditorBuildProfile::BuildOption, LocalVector<EditorBuildProfile::BuildOption>> EditorBuildProfile::build_option_dependencies = {
218
{ BUILD_OPTION_OPENXR, {
219
BUILD_OPTION_XR,
220
} },
221
{ BUILD_OPTION_FORWARD_RENDERER, {
222
BUILD_OPTION_RENDERING_DEVICE,
223
} },
224
{ BUILD_OPTION_MOBILE_RENDERER, {
225
BUILD_OPTION_RENDERING_DEVICE,
226
} },
227
{ BUILD_OPTION_VULKAN, {
228
BUILD_OPTION_FORWARD_RENDERER,
229
BUILD_OPTION_MOBILE_RENDERER,
230
} },
231
{ BUILD_OPTION_D3D12, {
232
BUILD_OPTION_FORWARD_RENDERER,
233
BUILD_OPTION_MOBILE_RENDERER,
234
} },
235
{ BUILD_OPTION_METAL, {
236
BUILD_OPTION_FORWARD_RENDERER,
237
BUILD_OPTION_MOBILE_RENDERER,
238
} },
239
{ BUILD_OPTION_PHYSICS_GODOT_2D, {
240
BUILD_OPTION_PHYSICS_2D,
241
} },
242
{ BUILD_OPTION_PHYSICS_GODOT_3D, {
243
BUILD_OPTION_PHYSICS_3D,
244
} },
245
{ BUILD_OPTION_PHYSICS_JOLT, {
246
BUILD_OPTION_PHYSICS_3D,
247
} },
248
{ BUILD_OPTION_DYNAMIC_FONTS, {
249
BUILD_OPTION_TEXT_SERVER_ADVANCED,
250
} },
251
{ BUILD_OPTION_WOFF2_FONTS, {
252
BUILD_OPTION_TEXT_SERVER_ADVANCED,
253
} },
254
{ BUILD_OPTION_GRAPHITE_FONTS, {
255
BUILD_OPTION_TEXT_SERVER_ADVANCED,
256
} },
257
};
258
259
const HashMap<EditorBuildProfile::BuildOption, LocalVector<String>> EditorBuildProfile::build_option_classes = {
260
{ BUILD_OPTION_3D, {
261
"Node3D",
262
} },
263
{ BUILD_OPTION_NAVIGATION_2D, {
264
"NavigationAgent2D",
265
"NavigationLink2D",
266
"NavigationMeshSourceGeometryData2D",
267
"NavigationObstacle2D",
268
"NavigationPolygon",
269
"NavigationRegion2D",
270
} },
271
{ BUILD_OPTION_NAVIGATION_3D, {
272
"NavigationAgent3D",
273
"NavigationLink3D",
274
"NavigationMeshSourceGeometryData3D",
275
"NavigationObstacle3D",
276
"NavigationRegion3D",
277
} },
278
{ BUILD_OPTION_XR, {
279
"XRBodyModifier3D",
280
"XRBodyTracker",
281
"XRControllerTracker",
282
"XRFaceModifier3D",
283
"XRFaceTracker",
284
"XRHandModifier3D",
285
"XRHandTracker",
286
"XRInterface",
287
"XRInterfaceExtension",
288
"XRNode3D",
289
"XROrigin3D",
290
"XRPose",
291
"XRPositionalTracker",
292
"XRServer",
293
"XRTracker",
294
"XRVRS",
295
} },
296
{ BUILD_OPTION_RENDERING_DEVICE, {
297
"RenderingDevice",
298
} },
299
{ BUILD_OPTION_PHYSICS_2D, {
300
"CollisionObject2D",
301
"CollisionPolygon2D",
302
"CollisionShape2D",
303
"Joint2D",
304
"PhysicsServer2D",
305
"PhysicsServer2DManager",
306
"ShapeCast2D",
307
"RayCast2D",
308
"TouchScreenButton",
309
} },
310
{ BUILD_OPTION_PHYSICS_3D, {
311
"CollisionObject3D",
312
"CollisionPolygon3D",
313
"CollisionShape3D",
314
"CSGShape3D",
315
"GPUParticlesAttractor3D",
316
"GPUParticlesCollision3D",
317
"Joint3D",
318
"PhysicalBoneSimulator3D",
319
"PhysicsServer3D",
320
"PhysicsServer3DManager",
321
"PhysicsServer3DRenderingServerHandler",
322
"RayCast3D",
323
"SoftBody3D",
324
"SpringArm3D",
325
"VehicleWheel3D",
326
} },
327
{ BUILD_OPTION_TEXT_SERVER_ADVANCED, {
328
"CanvasItem",
329
"Label3D",
330
"TextServerAdvanced",
331
} },
332
};
333
334
/* clang-format on */
335
336
void EditorBuildProfile::set_disable_class(const StringName &p_class, bool p_disabled) {
337
if (p_disabled) {
338
disabled_classes.insert(p_class);
339
} else {
340
disabled_classes.erase(p_class);
341
}
342
}
343
344
bool EditorBuildProfile::is_class_disabled(const StringName &p_class) const {
345
if (p_class == StringName()) {
346
return false;
347
}
348
return disabled_classes.has(p_class) || is_class_disabled(ClassDB::get_parent_class_nocheck(p_class));
349
}
350
351
void EditorBuildProfile::set_item_collapsed(const StringName &p_class, bool p_collapsed) {
352
if (p_collapsed) {
353
collapsed_classes.insert(p_class);
354
} else {
355
collapsed_classes.erase(p_class);
356
}
357
}
358
359
bool EditorBuildProfile::is_item_collapsed(const StringName &p_class) const {
360
return collapsed_classes.has(p_class);
361
}
362
363
void EditorBuildProfile::set_disable_build_option(BuildOption p_build_option, bool p_disable) {
364
ERR_FAIL_INDEX(p_build_option, BUILD_OPTION_MAX);
365
build_options_disabled[p_build_option] = p_disable;
366
}
367
368
void EditorBuildProfile::clear_disabled_classes() {
369
disabled_classes.clear();
370
collapsed_classes.clear();
371
}
372
373
bool EditorBuildProfile::is_build_option_disabled(BuildOption p_build_option) const {
374
ERR_FAIL_INDEX_V(p_build_option, BUILD_OPTION_MAX, false);
375
return build_options_disabled[p_build_option];
376
}
377
378
bool EditorBuildProfile::get_build_option_disable_value(BuildOption p_build_option) {
379
ERR_FAIL_INDEX_V(p_build_option, BUILD_OPTION_MAX, false);
380
return build_option_disable_values[p_build_option];
381
}
382
383
bool EditorBuildProfile::get_build_option_explicit_use(BuildOption p_build_option) {
384
ERR_FAIL_INDEX_V(p_build_option, BUILD_OPTION_MAX, false);
385
return build_option_explicit_use[p_build_option];
386
}
387
388
void EditorBuildProfile::reset_build_options() {
389
for (int i = 0; i < EditorBuildProfile::BUILD_OPTION_MAX; i++) {
390
build_options_disabled[i] = build_option_disabled_by_default[i];
391
}
392
}
393
394
void EditorBuildProfile::set_force_detect_classes(const String &p_classes) {
395
force_detect_classes = p_classes;
396
}
397
398
String EditorBuildProfile::get_force_detect_classes() const {
399
return force_detect_classes;
400
}
401
402
String EditorBuildProfile::get_build_option_name(BuildOption p_build_option) {
403
ERR_FAIL_INDEX_V(p_build_option, BUILD_OPTION_MAX, String());
404
const char *build_option_names[BUILD_OPTION_MAX] = {
405
TTRC("3D Engine"),
406
TTRC("Navigation (2D)"),
407
TTRC("Navigation (3D)"),
408
TTRC("Accessibility Support (AccessKit)"),
409
TTRC("Improved Gamepad Support (SDL)"),
410
TTRC("XR"),
411
TTRC("OpenXR"),
412
TTRC("Wayland"),
413
TTRC("X11"),
414
TTRC("PulseAudio"),
415
TTRC("ALSA"),
416
TTRC("RenderingDevice"),
417
TTRC("Forward+ Renderer"),
418
TTRC("Mobile Renderer"),
419
TTRC("Vulkan"),
420
TTRC("D3D12"),
421
TTRC("Metal"),
422
TTRC("OpenGL"),
423
TTRC("Physics Server (2D)"),
424
TTRC("Godot Physics (2D)"),
425
TTRC("Physics Server (3D)"),
426
TTRC("Godot Physics (3D)"),
427
TTRC("Jolt Physics"),
428
TTRC("Text Server: Fallback"),
429
TTRC("Text Server: Advanced"),
430
TTRC("TTF, OTF, Type 1, WOFF1 Fonts"),
431
TTRC("WOFF2 Fonts"),
432
TTRC("SIL Graphite Fonts"),
433
TTRC("Multi-channel Signed Distance Field Font Rendering"),
434
};
435
return TTRGET(build_option_names[p_build_option]);
436
}
437
438
String EditorBuildProfile::get_build_option_description(BuildOption p_build_option) {
439
ERR_FAIL_INDEX_V(p_build_option, BUILD_OPTION_MAX, String());
440
441
const char *build_option_descriptions[BUILD_OPTION_MAX] = {
442
TTRC("3D Nodes as well as RenderingServer access to 3D features.\nNote that the Geometry3D singleton remains available even with this item disabled."),
443
TTRC("NavigationServer and capabilities for 2D."),
444
TTRC("NavigationServer and capabilities for 3D."),
445
TTRC("Support for screen readers using the AccessKit library."),
446
TTRC("Improved gamepad support on Windows, macOS, and Linux using the SDL library.\nIf disabled, built-in custom code is used for gamepad support instead, which may be less reliable for certain controller models."),
447
TTRC("XR (AR and VR)."),
448
TTRC("OpenXR standard implementation (requires XR to be enabled)."),
449
TTRC("Wayland display server support (Linux only)."),
450
TTRC("X11 display server support (Linux only)."),
451
TTRC("PulseAudio audio driver (Linux only)."),
452
TTRC("ALSA audio driver (Linux only)."),
453
TTRC("RenderingDevice-based rendering (if disabled, the OpenGL backend is required)."),
454
TTRC("Forward+ renderer for advanced 3D graphics."),
455
TTRC("Mobile renderer for less advanced 3D graphics."),
456
TTRC("Vulkan backend of RenderingDevice."),
457
TTRC("Direct3D 12 backend of RenderingDevice."),
458
TTRC("Metal backend of RenderingDevice (Apple arm64 only)."),
459
TTRC("OpenGL backend (if disabled, the RenderingDevice backend is required)."),
460
TTRC("PhysicsServer and capabilities for 2D."),
461
TTRC("Godot Physics backend (2D)."),
462
TTRC("PhysicsServer and capabilities for 3D."),
463
TTRC("Godot Physics backend (3D)."),
464
TTRC("Jolt Physics backend (3D only)."),
465
TTRC("Fallback implementation of Text Server\nSupports basic text layouts."),
466
TTRC("Text Server implementation powered by ICU and HarfBuzz libraries.\nSupports complex text layouts, BiDi, and contextual OpenType font features."),
467
TTRC("TrueType, OpenType, Type 1, and WOFF1 font format support using FreeType library (if disabled, WOFF2 support is also disabled)."),
468
TTRC("WOFF2 font format support using FreeType and Brotli libraries."),
469
TTRC("SIL Graphite smart font technology support (supported by Advanced Text Server only)."),
470
TTRC("Multi-channel signed distance field font rendering support using msdfgen library (pre-rendered MSDF fonts can be used even if this option is disabled)."),
471
};
472
473
return TTRGET(build_option_descriptions[p_build_option]);
474
}
475
476
String EditorBuildProfile::get_build_option_identifier(BuildOption p_build_option) {
477
ERR_FAIL_INDEX_V(p_build_option, BUILD_OPTION_MAX, String());
478
return build_option_identifiers[p_build_option];
479
}
480
481
EditorBuildProfile::BuildOptionCategory EditorBuildProfile::get_build_option_category(BuildOption p_build_option) {
482
ERR_FAIL_INDEX_V(p_build_option, BUILD_OPTION_MAX, BUILD_OPTION_CATEGORY_GENERAL);
483
return build_option_category[p_build_option];
484
}
485
486
LocalVector<EditorBuildProfile::BuildOption> EditorBuildProfile::get_build_option_dependencies(BuildOption p_build_option) {
487
ERR_FAIL_INDEX_V(p_build_option, BUILD_OPTION_MAX, LocalVector<EditorBuildProfile::BuildOption>());
488
return build_option_dependencies.has(p_build_option) ? LocalVector<EditorBuildProfile::BuildOption>(build_option_dependencies[p_build_option]) : LocalVector<EditorBuildProfile::BuildOption>();
489
}
490
491
HashMap<String, LocalVector<Variant>> EditorBuildProfile::get_build_option_settings(BuildOption p_build_option) {
492
ERR_FAIL_INDEX_V(p_build_option, BUILD_OPTION_MAX, (HashMap<String, LocalVector<Variant>>()));
493
return build_option_settings.has(p_build_option) ? HashMap<String, LocalVector<Variant>>(build_option_settings[p_build_option]) : HashMap<String, LocalVector<Variant>>();
494
}
495
496
LocalVector<String> EditorBuildProfile::get_build_option_classes(BuildOption p_build_option) {
497
ERR_FAIL_INDEX_V(p_build_option, BUILD_OPTION_MAX, LocalVector<String>());
498
return build_option_classes.has(p_build_option) ? LocalVector<String>(build_option_classes[p_build_option]) : LocalVector<String>();
499
}
500
501
String EditorBuildProfile::get_build_option_category_name(BuildOptionCategory p_build_option_category) {
502
ERR_FAIL_INDEX_V(p_build_option_category, BUILD_OPTION_CATEGORY_MAX, String());
503
504
const char *build_option_subcategories[BUILD_OPTION_CATEGORY_MAX]{
505
TTRC("General Features:"),
506
TTRC("Graphics and Rendering:"),
507
TTRC("Physics Systems:"),
508
TTRC("Text Rendering and Font Options:"),
509
};
510
511
return TTRGET(build_option_subcategories[p_build_option_category]);
512
}
513
514
Error EditorBuildProfile::save_to_file(const String &p_path) {
515
Dictionary data;
516
data["type"] = "build_profile";
517
Array dis_classes;
518
for (const StringName &E : disabled_classes) {
519
dis_classes.push_back(String(E));
520
}
521
dis_classes.sort();
522
data["disabled_classes"] = dis_classes;
523
524
Dictionary dis_build_options;
525
for (int i = 0; i < BUILD_OPTION_MAX; i++) {
526
if (build_options_disabled[i] != build_option_disabled_by_default[i]) {
527
if (build_options_disabled[i]) {
528
dis_build_options[build_option_identifiers[i]] = build_option_disable_values[i];
529
} else {
530
dis_build_options[build_option_identifiers[i]] = !build_option_disable_values[i];
531
}
532
}
533
}
534
535
data["disabled_build_options"] = dis_build_options;
536
537
if (!force_detect_classes.is_empty()) {
538
data["force_detect_classes"] = force_detect_classes;
539
}
540
541
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::WRITE);
542
ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_CREATE, "Cannot create file '" + p_path + "'.");
543
544
String text = JSON::stringify(data, "\t");
545
f->store_string(text);
546
return OK;
547
}
548
549
Error EditorBuildProfile::load_from_file(const String &p_path) {
550
Error err;
551
String text = FileAccess::get_file_as_string(p_path, &err);
552
if (err != OK) {
553
return err;
554
}
555
556
JSON json;
557
err = json.parse(text);
558
if (err != OK) {
559
ERR_PRINT("Error parsing '" + p_path + "' on line " + itos(json.get_error_line()) + ": " + json.get_error_message());
560
return ERR_PARSE_ERROR;
561
}
562
563
Dictionary data = json.get_data();
564
565
if (!data.has("type") || String(data["type"]) != "build_profile") {
566
ERR_PRINT("Error parsing '" + p_path + "', it's not a build profile.");
567
return ERR_PARSE_ERROR;
568
}
569
570
disabled_classes.clear();
571
572
if (data.has("disabled_classes")) {
573
Array disabled_classes_arr = data["disabled_classes"];
574
for (int i = 0; i < disabled_classes_arr.size(); i++) {
575
disabled_classes.insert(disabled_classes_arr[i]);
576
}
577
}
578
579
for (int i = 0; i < BUILD_OPTION_MAX; i++) {
580
build_options_disabled[i] = build_option_disabled_by_default[i];
581
}
582
583
if (data.has("disabled_build_options")) {
584
Dictionary disabled_build_options_arr = data["disabled_build_options"];
585
586
for (const KeyValue<Variant, Variant> &kv : disabled_build_options_arr) {
587
String key = kv.key;
588
589
for (int i = 0; i < BUILD_OPTION_MAX; i++) {
590
String f = build_option_identifiers[i];
591
if (f == key) {
592
build_options_disabled[i] = true;
593
break;
594
}
595
}
596
}
597
}
598
599
if (data.has("force_detect_classes")) {
600
force_detect_classes = data["force_detect_classes"];
601
}
602
603
return OK;
604
}
605
606
void EditorBuildProfile::_bind_methods() {
607
ClassDB::bind_method(D_METHOD("set_disable_class", "class_name", "disable"), &EditorBuildProfile::set_disable_class);
608
ClassDB::bind_method(D_METHOD("is_class_disabled", "class_name"), &EditorBuildProfile::is_class_disabled);
609
610
ClassDB::bind_method(D_METHOD("set_disable_build_option", "build_option", "disable"), &EditorBuildProfile::set_disable_build_option);
611
ClassDB::bind_method(D_METHOD("is_build_option_disabled", "build_option"), &EditorBuildProfile::is_build_option_disabled);
612
613
ClassDB::bind_method(D_METHOD("get_build_option_name", "build_option"), &EditorBuildProfile::_get_build_option_name);
614
615
ClassDB::bind_method(D_METHOD("save_to_file", "path"), &EditorBuildProfile::save_to_file);
616
ClassDB::bind_method(D_METHOD("load_from_file", "path"), &EditorBuildProfile::load_from_file);
617
618
BIND_ENUM_CONSTANT(BUILD_OPTION_3D);
619
BIND_ENUM_CONSTANT(BUILD_OPTION_NAVIGATION_2D);
620
BIND_ENUM_CONSTANT(BUILD_OPTION_NAVIGATION_3D);
621
BIND_ENUM_CONSTANT(BUILD_OPTION_XR);
622
BIND_ENUM_CONSTANT(BUILD_OPTION_OPENXR);
623
BIND_ENUM_CONSTANT(BUILD_OPTION_WAYLAND);
624
BIND_ENUM_CONSTANT(BUILD_OPTION_X11);
625
BIND_ENUM_CONSTANT(BUILD_OPTION_RENDERING_DEVICE);
626
BIND_ENUM_CONSTANT(BUILD_OPTION_FORWARD_RENDERER);
627
BIND_ENUM_CONSTANT(BUILD_OPTION_MOBILE_RENDERER);
628
BIND_ENUM_CONSTANT(BUILD_OPTION_VULKAN);
629
BIND_ENUM_CONSTANT(BUILD_OPTION_D3D12);
630
BIND_ENUM_CONSTANT(BUILD_OPTION_METAL);
631
BIND_ENUM_CONSTANT(BUILD_OPTION_OPENGL);
632
BIND_ENUM_CONSTANT(BUILD_OPTION_PHYSICS_2D);
633
BIND_ENUM_CONSTANT(BUILD_OPTION_PHYSICS_GODOT_2D);
634
BIND_ENUM_CONSTANT(BUILD_OPTION_PHYSICS_3D);
635
BIND_ENUM_CONSTANT(BUILD_OPTION_PHYSICS_GODOT_3D);
636
BIND_ENUM_CONSTANT(BUILD_OPTION_PHYSICS_JOLT);
637
BIND_ENUM_CONSTANT(BUILD_OPTION_TEXT_SERVER_FALLBACK);
638
BIND_ENUM_CONSTANT(BUILD_OPTION_TEXT_SERVER_ADVANCED);
639
BIND_ENUM_CONSTANT(BUILD_OPTION_DYNAMIC_FONTS);
640
BIND_ENUM_CONSTANT(BUILD_OPTION_WOFF2_FONTS);
641
BIND_ENUM_CONSTANT(BUILD_OPTION_GRAPHITE_FONTS);
642
BIND_ENUM_CONSTANT(BUILD_OPTION_MSDFGEN);
643
BIND_ENUM_CONSTANT(BUILD_OPTION_MAX);
644
645
BIND_ENUM_CONSTANT(BUILD_OPTION_CATEGORY_GENERAL);
646
BIND_ENUM_CONSTANT(BUILD_OPTION_CATEGORY_GRAPHICS);
647
BIND_ENUM_CONSTANT(BUILD_OPTION_CATEGORY_PHYSICS);
648
BIND_ENUM_CONSTANT(BUILD_OPTION_CATEGORY_TEXT_SERVER);
649
BIND_ENUM_CONSTANT(BUILD_OPTION_CATEGORY_MAX);
650
}
651
652
EditorBuildProfile::EditorBuildProfile() {
653
reset_build_options();
654
655
HashMap<String, LocalVector<Variant>> settings_openxr = {
656
{ "xr/openxr/enabled", { true } },
657
};
658
build_option_settings.insert(BUILD_OPTION_OPENXR, settings_openxr);
659
660
HashMap<String, LocalVector<Variant>> settings_wayland = {
661
{ "display/display_server/driver.linuxbsd", { "default", "wayland" } },
662
};
663
build_option_settings.insert(BUILD_OPTION_WAYLAND, settings_wayland);
664
665
HashMap<String, LocalVector<Variant>> settings_x11 = {
666
{ "display/display_server/driver.linuxbsd", { "default", "x11" } },
667
};
668
build_option_settings.insert(BUILD_OPTION_X11, settings_x11);
669
670
HashMap<String, LocalVector<Variant>> settings_rd = {
671
{ "rendering/renderer/rendering_method", { "forward_plus", "mobile" } },
672
{ "rendering/renderer/rendering_method.mobile", { "forward_plus", "mobile" } },
673
{ "rendering/renderer/rendering_method.web", { "forward_plus", "mobile" } },
674
};
675
build_option_settings.insert(BUILD_OPTION_RENDERING_DEVICE, settings_rd);
676
677
HashMap<String, LocalVector<Variant>> settings_vulkan = {
678
{ "rendering/rendering_device/driver", { "vulkan" } },
679
{ "rendering/rendering_device/driver.windows", { "vulkan" } },
680
{ "rendering/rendering_device/driver.linuxbsd", { "vulkan" } },
681
{ "rendering/rendering_device/driver.android", { "vulkan" } },
682
{ "rendering/rendering_device/driver.ios", { "vulkan" } },
683
{ "rendering/rendering_device/driver.macos", { "vulkan" } },
684
{ "rendering/rendering_device/fallback_to_vulkan", { true } },
685
};
686
build_option_settings.insert(BUILD_OPTION_VULKAN, settings_vulkan);
687
688
HashMap<String, LocalVector<Variant>> settings_d3d12 = {
689
{ "rendering/rendering_device/driver", { "d3d12" } },
690
{ "rendering/rendering_device/driver.windows", { "d3d12" } },
691
{ "rendering/rendering_device/driver.linuxbsd", { "d3d12" } },
692
{ "rendering/rendering_device/driver.android", { "d3d12" } },
693
{ "rendering/rendering_device/driver.ios", { "d3d12" } },
694
{ "rendering/rendering_device/driver.macos", { "d3d12" } },
695
{ "rendering/rendering_device/fallback_to_d3d12", { true } },
696
};
697
build_option_settings.insert(BUILD_OPTION_D3D12, settings_d3d12);
698
699
HashMap<String, LocalVector<Variant>> settings_metal = {
700
{ "rendering/rendering_device/driver", { "metal" } },
701
{ "rendering/rendering_device/driver.ios", { "metal" } },
702
{ "rendering/rendering_device/driver.macos", { "metal" } },
703
};
704
build_option_settings.insert(BUILD_OPTION_METAL, settings_metal);
705
706
HashMap<String, LocalVector<Variant>> settings_opengl = {
707
{ "rendering/renderer/rendering_method", { "gl_compatibility" } },
708
{ "rendering/renderer/rendering_method.mobile", { "gl_compatibility" } },
709
{ "rendering/renderer/rendering_method.web", { "gl_compatibility" } },
710
{ "rendering/rendering_device/fallback_to_opengl3", { true } },
711
};
712
build_option_settings.insert(BUILD_OPTION_OPENGL, settings_opengl);
713
714
HashMap<String, LocalVector<Variant>> settings_phy_godot_3d = {
715
{ "physics/3d/physics_engine", { "DEFAULT", "GodotPhysics3D" } },
716
};
717
build_option_settings.insert(BUILD_OPTION_PHYSICS_GODOT_3D, settings_phy_godot_3d);
718
719
HashMap<String, LocalVector<Variant>> settings_jolt = {
720
{ "physics/3d/physics_engine", { "Jolt Physics" } },
721
};
722
build_option_settings.insert(BUILD_OPTION_PHYSICS_JOLT, settings_jolt);
723
724
HashMap<String, LocalVector<Variant>> settings_msdfgen = {
725
{ "gui/theme/default_font_multichannel_signed_distance_field", { true } },
726
};
727
build_option_settings.insert(BUILD_OPTION_MSDFGEN, settings_msdfgen);
728
}
729
730
//////////////////////////
731
732
void EditorBuildProfileManager::_notification(int p_what) {
733
switch (p_what) {
734
case NOTIFICATION_READY: {
735
String last_file = EditorSettings::get_singleton()->get_project_metadata("build_profile", "last_file_path", "");
736
if (!last_file.is_empty()) {
737
_import_profile(last_file);
738
}
739
if (edited.is_null()) {
740
edited.instantiate();
741
_update_edited_profile();
742
}
743
744
} break;
745
}
746
}
747
748
void EditorBuildProfileManager::_profile_action(int p_action) {
749
last_action = Action(p_action);
750
751
switch (p_action) {
752
case ACTION_RESET: {
753
confirm_dialog->set_text(TTR("Reset the edited profile?"));
754
confirm_dialog->popup_centered();
755
} break;
756
case ACTION_LOAD: {
757
import_profile->popup_file_dialog();
758
} break;
759
case ACTION_SAVE: {
760
if (!profile_path->get_text().is_empty()) {
761
Error err = edited->save_to_file(profile_path->get_text());
762
if (err != OK) {
763
EditorNode::get_singleton()->show_warning(TTR("File saving failed."));
764
}
765
break;
766
}
767
[[fallthrough]];
768
}
769
case ACTION_SAVE_AS: {
770
export_profile->popup_file_dialog();
771
export_profile->set_current_file(profile_path->get_text());
772
} break;
773
case ACTION_NEW: {
774
confirm_dialog->set_text(TTR("Create a new profile?"));
775
confirm_dialog->popup_centered();
776
} break;
777
case ACTION_DETECT: {
778
String text = TTR("This will scan all files in the current project to detect used classes.\nNote that the first scan may take a while, specially in larger projects.");
779
#ifdef MODULE_MONO_ENABLED
780
text += "\n\n" + TTR("Warning: Class detection for C# scripts is not currently available, and such files will be ignored.");
781
#endif // MODULE_MONO_ENABLED
782
confirm_dialog->set_text(text);
783
confirm_dialog->popup_centered();
784
} break;
785
case ACTION_MAX: {
786
} break;
787
}
788
}
789
790
void EditorBuildProfileManager::_find_files(EditorFileSystemDirectory *p_dir, const HashMap<String, DetectedFile> &p_cache, HashMap<String, DetectedFile> &r_detected) {
791
if (p_dir == nullptr || p_dir->get_path().get_file().begins_with(".")) {
792
return;
793
}
794
795
for (int i = 0; i < p_dir->get_file_count(); i++) {
796
String p = p_dir->get_file_path(i);
797
798
if (EditorNode::get_singleton()->progress_task_step("detect_classes_from_project", p, 1)) {
799
project_scan_canceled = true;
800
return;
801
}
802
803
String p_check = p;
804
// Make so that the import file is the one checked if available,
805
// so the cache can be updated when it changes.
806
if (ResourceFormatImporter::get_singleton()->exists(p_check)) {
807
p_check += ".import";
808
}
809
810
uint64_t timestamp = 0;
811
String md5;
812
813
if (p_cache.has(p)) {
814
const DetectedFile &cache = p_cache[p];
815
// Check if timestamp and MD5 match.
816
timestamp = FileAccess::get_modified_time(p_check);
817
bool cache_valid = true;
818
if (cache.timestamp != timestamp) {
819
md5 = FileAccess::get_md5(p_check);
820
if (md5 != cache.md5) {
821
cache_valid = false;
822
}
823
}
824
825
if (cache_valid) {
826
r_detected.insert(p, cache);
827
continue;
828
}
829
}
830
831
// Not cached, or cache invalid.
832
833
DetectedFile cache;
834
835
HashSet<StringName> classes;
836
ResourceLoader::get_classes_used(p, &classes);
837
for (const StringName &E : classes) {
838
cache.classes.push_back(E);
839
}
840
841
HashSet<String> build_deps;
842
ResourceFormatImporter::get_singleton()->get_build_dependencies(p, &build_deps);
843
for (const String &E : build_deps) {
844
cache.build_deps.push_back(E);
845
}
846
847
if (md5.is_empty()) {
848
cache.timestamp = FileAccess::get_modified_time(p_check);
849
cache.md5 = FileAccess::get_md5(p_check);
850
} else {
851
cache.timestamp = timestamp;
852
cache.md5 = md5;
853
}
854
855
r_detected.insert(p, cache);
856
}
857
858
for (int i = 0; i < p_dir->get_subdir_count(); i++) {
859
_find_files(p_dir->get_subdir(i), p_cache, r_detected);
860
}
861
}
862
863
void EditorBuildProfileManager::_detect_from_project() {
864
EditorNode::get_singleton()->progress_add_task("detect_classes_from_project", TTRC("Scanning Project for Used Classes"), 3, true);
865
866
HashMap<String, DetectedFile> previous_file_cache;
867
868
Ref<FileAccess> f = FileAccess::open(EditorPaths::get_singleton()->get_project_settings_dir().path_join("used_class_cache"), FileAccess::READ);
869
if (f.is_valid()) {
870
while (!f->eof_reached()) {
871
String l = f->get_line();
872
Vector<String> fields = l.split("::");
873
if (fields.size() == 5) {
874
const String &path = fields[0];
875
DetectedFile df;
876
df.timestamp = fields[1].to_int();
877
df.md5 = fields[2];
878
df.classes = fields[3].split(",", false);
879
df.build_deps = fields[4].split(",", false);
880
previous_file_cache.insert(path, df);
881
}
882
}
883
f.unref();
884
}
885
886
HashMap<String, DetectedFile> updated_file_cache;
887
888
_find_files(EditorFileSystem::get_singleton()->get_filesystem(), previous_file_cache, updated_file_cache);
889
890
if (project_scan_canceled) {
891
project_scan_canceled = false;
892
EditorNode::get_singleton()->progress_end_task("detect_classes_from_project");
893
894
return;
895
}
896
897
EditorNode::get_singleton()->progress_task_step("detect_classes_from_project", TTRC("Processing Classes Found"), 2);
898
899
HashSet<StringName> used_classes;
900
LocalVector<String> used_build_deps;
901
902
// Find classes and update the disk cache in the process.
903
f = FileAccess::open(EditorPaths::get_singleton()->get_project_settings_dir().path_join("used_class_cache"), FileAccess::WRITE);
904
905
for (const KeyValue<String, DetectedFile> &E : updated_file_cache) {
906
String l = E.key + "::" + itos(E.value.timestamp) + "::" + E.value.md5 + "::";
907
for (int i = 0; i < E.value.classes.size(); i++) {
908
String c = E.value.classes[i];
909
if (i > 0) {
910
l += ",";
911
}
912
l += c;
913
used_classes.insert(c);
914
}
915
l += "::";
916
for (int i = 0; i < E.value.build_deps.size(); i++) {
917
String c = E.value.build_deps[i];
918
if (i > 0) {
919
l += ",";
920
}
921
l += c;
922
used_build_deps.push_back(c);
923
}
924
f->store_line(l);
925
}
926
927
f.unref();
928
929
// Add classes that are either necessary for the engine to work properly, or there isn't a way to infer their use.
930
931
// HACK: Some classes are included due to creating clashes with unrelated when disabled.
932
// Until that is fixed, they need to always be enabled.
933
const LocalVector<String> hardcoded_classes = {
934
"Font",
935
"InputEvent",
936
"MainLoop",
937
"Mutex",
938
"ShaderInclude",
939
"ShaderIncludeDB",
940
"StyleBox",
941
"Time",
942
"Window",
943
};
944
945
for (const String &hc_class : hardcoded_classes) {
946
used_classes.insert(hc_class);
947
948
LocalVector<StringName> inheriters;
949
ClassDB::get_inheriters_from_class(hc_class, inheriters);
950
for (const StringName &inheriter : inheriters) {
951
used_classes.insert(inheriter);
952
}
953
}
954
955
// Add forced classes typed by the user.
956
957
const Vector<String> force_detect = edited->get_force_detect_classes().split(",");
958
for (const String &class_name : force_detect) {
959
const String class_stripped = class_name.strip_edges();
960
if (!class_stripped.is_empty()) {
961
used_classes.insert(class_stripped);
962
}
963
}
964
965
// Filter all classes to discard inherited ones.
966
967
HashSet<StringName> all_used_classes;
968
969
for (const StringName &E : used_classes) {
970
StringName c = E;
971
if (!ClassDB::class_exists(c)) {
972
// Maybe this is an old class that got replaced? Try getting compat class.
973
c = ClassDB::get_compatibility_class(c);
974
if (!c) {
975
// No luck, skip.
976
continue;
977
}
978
}
979
980
List<StringName> dependencies;
981
ClassDB::get_class_dependencies(E, &dependencies);
982
for (const StringName &dep : dependencies) {
983
if (!all_used_classes.has(dep)) {
984
// Add classes which this class depends upon.
985
all_used_classes.insert(dep);
986
}
987
}
988
989
while (c) {
990
all_used_classes.insert(c);
991
c = ClassDB::get_parent_class(c);
992
}
993
}
994
995
edited->clear_disabled_classes();
996
997
LocalVector<StringName> all_classes;
998
ClassDB::get_class_list(all_classes);
999
1000
for (const StringName &class_name : all_classes) {
1001
if (String(class_name).begins_with("Editor") || ClassDB::get_api_type(class_name) != ClassDB::API_CORE || all_used_classes.has(class_name)) {
1002
// This class is valid or editor-only, do nothing.
1003
continue;
1004
}
1005
1006
StringName p = ClassDB::get_parent_class(class_name);
1007
if (!p || all_used_classes.has(p)) {
1008
// If no parent, or if the parent is enabled, then add to disabled classes.
1009
// This way we avoid disabling redundant classes.
1010
edited->set_disable_class(class_name, true);
1011
}
1012
}
1013
1014
edited->reset_build_options();
1015
1016
for (int i = 0; i < EditorBuildProfile::BUILD_OPTION_MAX; i++) {
1017
// Check if the build option requires other options that are currently disabled.
1018
LocalVector<EditorBuildProfile::BuildOption> dependencies = EditorBuildProfile::get_build_option_dependencies(EditorBuildProfile::BuildOption(i));
1019
if (!dependencies.is_empty()) {
1020
bool disable = true;
1021
for (EditorBuildProfile::BuildOption dependency : dependencies) {
1022
if (!edited->is_build_option_disabled(dependency)) {
1023
disable = false;
1024
break;
1025
}
1026
}
1027
1028
if (disable) {
1029
edited->set_disable_build_option(EditorBuildProfile::BuildOption(i), true);
1030
continue;
1031
}
1032
}
1033
1034
bool skip = false;
1035
bool ignore = true;
1036
1037
// Check if the build option has enabled classes using it.
1038
const LocalVector<String> classes = EditorBuildProfile::get_build_option_classes(EditorBuildProfile::BuildOption(i));
1039
if (!classes.is_empty()) {
1040
for (StringName class_name : classes) {
1041
if (!edited->is_class_disabled(class_name)) {
1042
skip = true;
1043
break;
1044
}
1045
}
1046
1047
if (skip) {
1048
continue;
1049
}
1050
1051
ignore = false;
1052
}
1053
1054
// Check if there's project settings requiring it.
1055
const HashMap<String, LocalVector<Variant>> settings_list = EditorBuildProfile::get_build_option_settings(EditorBuildProfile::BuildOption(i));
1056
if (!settings_list.is_empty()) {
1057
for (KeyValue<String, LocalVector<Variant>> KV : settings_list) {
1058
Variant proj_value = GLOBAL_GET(KV.key);
1059
for (Variant value : KV.value) {
1060
if (proj_value == value) {
1061
skip = true;
1062
break;
1063
}
1064
}
1065
1066
if (skip) {
1067
break;
1068
}
1069
}
1070
1071
if (skip) {
1072
continue;
1073
}
1074
1075
ignore = false;
1076
}
1077
1078
// Check if a resource setting depends on it.
1079
if (used_build_deps.has(EditorBuildProfile::get_build_option_identifier(EditorBuildProfile::BuildOption(i)))) {
1080
continue;
1081
} else if (EditorBuildProfile::get_build_option_explicit_use(EditorBuildProfile::BuildOption(i))) {
1082
ignore = false;
1083
}
1084
1085
if (!skip && !ignore) {
1086
edited->set_disable_build_option(EditorBuildProfile::BuildOption(i), true);
1087
}
1088
}
1089
1090
if (edited->is_build_option_disabled(EditorBuildProfile::BUILD_OPTION_TEXT_SERVER_ADVANCED)) {
1091
edited->set_disable_build_option(EditorBuildProfile::BUILD_OPTION_TEXT_SERVER_FALLBACK, false);
1092
}
1093
1094
EditorNode::get_singleton()->progress_end_task("detect_classes_from_project");
1095
}
1096
1097
void EditorBuildProfileManager::_action_confirm() {
1098
switch (last_action) {
1099
case ACTION_RESET: {
1100
edited.instantiate();
1101
_update_edited_profile();
1102
} break;
1103
case ACTION_LOAD: {
1104
} break;
1105
case ACTION_SAVE: {
1106
} break;
1107
case ACTION_SAVE_AS: {
1108
} break;
1109
case ACTION_NEW: {
1110
profile_path->set_text("");
1111
edited.instantiate();
1112
_update_edited_profile();
1113
} break;
1114
case ACTION_DETECT: {
1115
_detect_from_project();
1116
_update_edited_profile();
1117
} break;
1118
case ACTION_MAX: {
1119
} break;
1120
}
1121
}
1122
1123
void EditorBuildProfileManager::_hide_requested() {
1124
_cancel_pressed(); // From AcceptDialog.
1125
}
1126
1127
void EditorBuildProfileManager::_fill_classes_from(TreeItem *p_parent, const String &p_class, const String &p_selected) {
1128
TreeItem *class_item = class_list->create_item(p_parent);
1129
class_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
1130
class_item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_class));
1131
const String &text = p_class;
1132
1133
bool disabled = edited->is_class_disabled(p_class);
1134
if (disabled) {
1135
class_item->set_custom_color(0, class_list->get_theme_color(SNAME("font_disabled_color"), EditorStringName(Editor)));
1136
}
1137
1138
class_item->set_text(0, text);
1139
class_item->set_editable(0, true);
1140
class_item->set_selectable(0, true);
1141
class_item->set_metadata(0, p_class);
1142
1143
bool collapsed = edited->is_item_collapsed(p_class);
1144
class_item->set_collapsed(collapsed);
1145
1146
if (p_class == p_selected) {
1147
class_item->select(0);
1148
}
1149
if (disabled) {
1150
// Class disabled, do nothing else (do not show further).
1151
return;
1152
}
1153
1154
class_item->set_checked(0, true); // If it's not disabled, its checked.
1155
1156
List<StringName> child_classes;
1157
ClassDB::get_direct_inheriters_from_class(p_class, &child_classes);
1158
child_classes.sort_custom<StringName::AlphCompare>();
1159
1160
for (const StringName &name : child_classes) {
1161
if (String(name).begins_with("Editor") || ClassDB::get_api_type(name) != ClassDB::API_CORE) {
1162
continue;
1163
}
1164
_fill_classes_from(class_item, name, p_selected);
1165
}
1166
}
1167
1168
void EditorBuildProfileManager::_class_list_item_selected() {
1169
if (updating_build_options) {
1170
return;
1171
}
1172
1173
TreeItem *item = class_list->get_selected();
1174
if (!item) {
1175
return;
1176
}
1177
1178
Variant md = item->get_metadata(0);
1179
if (md.is_string()) {
1180
description_bit->parse_symbol("class|" + md.operator String() + "|");
1181
} else if (md.get_type() == Variant::INT) {
1182
String build_option_description = EditorBuildProfile::get_build_option_description(EditorBuildProfile::BuildOption((int)md));
1183
description_bit->set_custom_text(TTR(item->get_text(0)), String(), TTRGET(build_option_description));
1184
}
1185
}
1186
1187
void EditorBuildProfileManager::_class_list_item_edited() {
1188
if (updating_build_options) {
1189
return;
1190
}
1191
1192
TreeItem *item = class_list->get_edited();
1193
if (!item) {
1194
return;
1195
}
1196
1197
bool checked = item->is_checked(0);
1198
1199
Variant md = item->get_metadata(0);
1200
if (md.is_string()) {
1201
String class_selected = md;
1202
edited->set_disable_class(class_selected, !checked);
1203
_update_edited_profile();
1204
} else if (md.get_type() == Variant::INT) {
1205
int build_option_selected = md;
1206
edited->set_disable_build_option(EditorBuildProfile::BuildOption(build_option_selected), !checked);
1207
}
1208
}
1209
1210
void EditorBuildProfileManager::_class_list_item_collapsed(Object *p_item) {
1211
if (updating_build_options) {
1212
return;
1213
}
1214
1215
TreeItem *item = Object::cast_to<TreeItem>(p_item);
1216
if (!item) {
1217
return;
1218
}
1219
1220
Variant md = item->get_metadata(0);
1221
if (!md.is_string()) {
1222
return;
1223
}
1224
1225
String class_name = md;
1226
bool collapsed = item->is_collapsed();
1227
edited->set_item_collapsed(class_name, collapsed);
1228
}
1229
1230
void EditorBuildProfileManager::_update_edited_profile() {
1231
String class_selected;
1232
int build_option_selected = -1;
1233
1234
if (class_list->get_selected()) {
1235
Variant md = class_list->get_selected()->get_metadata(0);
1236
if (md.is_string()) {
1237
class_selected = md;
1238
} else if (md.get_type() == Variant::INT) {
1239
build_option_selected = md;
1240
}
1241
}
1242
1243
class_list->clear();
1244
1245
updating_build_options = true;
1246
1247
TreeItem *root = class_list->create_item();
1248
1249
HashMap<EditorBuildProfile::BuildOptionCategory, TreeItem *> subcats;
1250
for (int i = 0; i < EditorBuildProfile::BUILD_OPTION_CATEGORY_MAX; i++) {
1251
TreeItem *build_cat;
1252
build_cat = class_list->create_item(root);
1253
1254
build_cat->set_text(0, EditorBuildProfile::get_build_option_category_name(EditorBuildProfile::BuildOptionCategory(i)));
1255
subcats[EditorBuildProfile::BuildOptionCategory(i)] = build_cat;
1256
}
1257
1258
for (int i = 0; i < EditorBuildProfile::BUILD_OPTION_MAX; i++) {
1259
TreeItem *build_option;
1260
build_option = class_list->create_item(subcats[EditorBuildProfile::get_build_option_category(EditorBuildProfile::BuildOption(i))]);
1261
1262
build_option->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
1263
build_option->set_text(0, EditorBuildProfile::get_build_option_name(EditorBuildProfile::BuildOption(i)));
1264
build_option->set_selectable(0, true);
1265
build_option->set_editable(0, true);
1266
build_option->set_metadata(0, i);
1267
if (!edited->is_build_option_disabled(EditorBuildProfile::BuildOption(i))) {
1268
build_option->set_checked(0, true);
1269
}
1270
1271
if (i == build_option_selected) {
1272
build_option->select(0);
1273
}
1274
}
1275
1276
TreeItem *classes = class_list->create_item(root);
1277
classes->set_text(0, TTR("Nodes and Classes:"));
1278
1279
_fill_classes_from(classes, "Node", class_selected);
1280
_fill_classes_from(classes, "Resource", class_selected);
1281
1282
force_detect_classes->set_text(edited->get_force_detect_classes());
1283
1284
updating_build_options = false;
1285
1286
_class_list_item_selected();
1287
}
1288
1289
void EditorBuildProfileManager::_force_detect_classes_changed(const String &p_text) {
1290
if (updating_build_options) {
1291
return;
1292
}
1293
edited->set_force_detect_classes(force_detect_classes->get_text());
1294
}
1295
1296
void EditorBuildProfileManager::_import_profile(const String &p_path) {
1297
Ref<EditorBuildProfile> profile;
1298
profile.instantiate();
1299
Error err = profile->load_from_file(p_path);
1300
String basefile = p_path.get_file();
1301
if (err != OK) {
1302
EditorNode::get_singleton()->show_warning(vformat(TTR("File '%s' format is invalid, import aborted."), basefile));
1303
return;
1304
}
1305
1306
profile_path->set_text(p_path);
1307
EditorSettings::get_singleton()->set_project_metadata("build_profile", "last_file_path", p_path);
1308
1309
edited = profile;
1310
_update_edited_profile();
1311
}
1312
1313
void EditorBuildProfileManager::_export_profile(const String &p_path) {
1314
ERR_FAIL_COND(edited.is_null());
1315
Error err = edited->save_to_file(p_path);
1316
if (err != OK) {
1317
EditorNode::get_singleton()->show_warning(vformat(TTR("Error saving profile to path: '%s'."), p_path));
1318
} else {
1319
profile_path->set_text(p_path);
1320
EditorSettings::get_singleton()->set_project_metadata("build_profile", "last_file_path", p_path);
1321
}
1322
}
1323
1324
Ref<EditorBuildProfile> EditorBuildProfileManager::get_current_profile() {
1325
return edited;
1326
}
1327
1328
EditorBuildProfileManager *EditorBuildProfileManager::singleton = nullptr;
1329
1330
void EditorBuildProfileManager::_bind_methods() {
1331
ClassDB::bind_method("_update_selected_profile", &EditorBuildProfileManager::_update_edited_profile);
1332
}
1333
1334
EditorBuildProfileManager::EditorBuildProfileManager() {
1335
VBoxContainer *main_vbc = memnew(VBoxContainer);
1336
add_child(main_vbc);
1337
1338
HBoxContainer *path_hbc = memnew(HBoxContainer);
1339
profile_path = memnew(LineEdit);
1340
path_hbc->add_child(profile_path);
1341
profile_path->set_accessibility_name(TTRC("Profile Path"));
1342
profile_path->set_editable(true);
1343
profile_path->set_h_size_flags(Control::SIZE_EXPAND_FILL);
1344
1345
profile_actions[ACTION_NEW] = memnew(Button(TTR("New")));
1346
path_hbc->add_child(profile_actions[ACTION_NEW]);
1347
profile_actions[ACTION_NEW]->connect(SceneStringName(pressed), callable_mp(this, &EditorBuildProfileManager::_profile_action).bind(ACTION_NEW));
1348
1349
profile_actions[ACTION_LOAD] = memnew(Button(TTR("Load")));
1350
path_hbc->add_child(profile_actions[ACTION_LOAD]);
1351
profile_actions[ACTION_LOAD]->connect(SceneStringName(pressed), callable_mp(this, &EditorBuildProfileManager::_profile_action).bind(ACTION_LOAD));
1352
1353
profile_actions[ACTION_SAVE] = memnew(Button(TTR("Save")));
1354
path_hbc->add_child(profile_actions[ACTION_SAVE]);
1355
profile_actions[ACTION_SAVE]->connect(SceneStringName(pressed), callable_mp(this, &EditorBuildProfileManager::_profile_action).bind(ACTION_SAVE));
1356
1357
profile_actions[ACTION_SAVE_AS] = memnew(Button(TTR("Save As")));
1358
path_hbc->add_child(profile_actions[ACTION_SAVE_AS]);
1359
profile_actions[ACTION_SAVE_AS]->connect(SceneStringName(pressed), callable_mp(this, &EditorBuildProfileManager::_profile_action).bind(ACTION_SAVE_AS));
1360
1361
main_vbc->add_margin_child(TTR("Profile:"), path_hbc);
1362
1363
main_vbc->add_child(memnew(HSeparator));
1364
1365
HBoxContainer *profiles_hbc = memnew(HBoxContainer);
1366
1367
profile_actions[ACTION_RESET] = memnew(Button(TTR("Reset to Defaults")));
1368
profiles_hbc->add_child(profile_actions[ACTION_RESET]);
1369
profile_actions[ACTION_RESET]->connect(SceneStringName(pressed), callable_mp(this, &EditorBuildProfileManager::_profile_action).bind(ACTION_RESET));
1370
1371
profile_actions[ACTION_DETECT] = memnew(Button(TTR("Detect from Project")));
1372
profiles_hbc->add_child(profile_actions[ACTION_DETECT]);
1373
profile_actions[ACTION_DETECT]->connect(SceneStringName(pressed), callable_mp(this, &EditorBuildProfileManager::_profile_action).bind(ACTION_DETECT));
1374
1375
main_vbc->add_margin_child(TTR("Actions:"), profiles_hbc);
1376
1377
class_list = memnew(Tree);
1378
class_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
1379
class_list->set_hide_root(true);
1380
class_list->set_edit_checkbox_cell_only_when_checkbox_is_pressed(true);
1381
class_list->set_scroll_hint_mode(Tree::SCROLL_HINT_MODE_BOTH);
1382
class_list->connect("cell_selected", callable_mp(this, &EditorBuildProfileManager::_class_list_item_selected));
1383
class_list->connect("item_edited", callable_mp(this, &EditorBuildProfileManager::_class_list_item_edited), CONNECT_DEFERRED);
1384
class_list->connect("item_collapsed", callable_mp(this, &EditorBuildProfileManager::_class_list_item_collapsed));
1385
1386
// It will be displayed once the user creates or chooses a profile.
1387
MarginContainer *mc = main_vbc->add_margin_child(TTRC("Configure Engine Compilation Profile:"), class_list, true);
1388
mc->set_theme_type_variation("NoBorderHorizontalWindow");
1389
mc->set_v_size_flags(Control::SIZE_EXPAND_FILL);
1390
1391
description_bit = memnew(EditorHelpBit);
1392
description_bit->set_content_height_limits(80 * EDSCALE, 80 * EDSCALE);
1393
description_bit->connect("request_hide", callable_mp(this, &EditorBuildProfileManager::_hide_requested));
1394
main_vbc->add_margin_child(TTR("Description:"), description_bit, false);
1395
1396
confirm_dialog = memnew(ConfirmationDialog);
1397
add_child(confirm_dialog);
1398
confirm_dialog->set_title(TTR("Please Confirm:"));
1399
confirm_dialog->connect(SceneStringName(confirmed), callable_mp(this, &EditorBuildProfileManager::_action_confirm));
1400
1401
import_profile = memnew(EditorFileDialog);
1402
add_child(import_profile);
1403
import_profile->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
1404
import_profile->add_filter("*.gdbuild,*.build", TTR("Engine Compilation Profile"));
1405
import_profile->connect("file_selected", callable_mp(this, &EditorBuildProfileManager::_import_profile));
1406
import_profile->set_title(TTR("Load Profile"));
1407
import_profile->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
1408
1409
export_profile = memnew(EditorFileDialog);
1410
add_child(export_profile);
1411
export_profile->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
1412
export_profile->add_filter("*.gdbuild,*.build", TTR("Engine Compilation Profile"));
1413
export_profile->connect("file_selected", callable_mp(this, &EditorBuildProfileManager::_export_profile));
1414
export_profile->set_title(TTR("Export Profile"));
1415
export_profile->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
1416
1417
force_detect_classes = memnew(LineEdit);
1418
force_detect_classes->set_accessibility_name(TTRC("Forced Classes on Detect:"));
1419
main_vbc->add_margin_child(TTR("Forced Classes on Detect:"), force_detect_classes);
1420
force_detect_classes->connect(SceneStringName(text_changed), callable_mp(this, &EditorBuildProfileManager::_force_detect_classes_changed));
1421
1422
set_title(TTR("Edit Compilation Configuration Profile"));
1423
1424
singleton = this;
1425
}
1426
1427