Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/editor/settings/project_settings_editor.cpp
9902 views
1
/**************************************************************************/
2
/* project_settings_editor.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 "project_settings_editor.h"
32
33
#include "core/config/project_settings.h"
34
#include "core/input/input_map.h"
35
#include "editor/editor_node.h"
36
#include "editor/editor_string_names.h"
37
#include "editor/editor_undo_redo_manager.h"
38
#include "editor/export/editor_export.h"
39
#include "editor/gui/editor_variant_type_selectors.h"
40
#include "editor/inspector/editor_inspector.h"
41
#include "editor/settings/editor_settings.h"
42
#include "editor/themes/editor_scale.h"
43
#include "scene/gui/check_button.h"
44
#include "servers/movie_writer/movie_writer.h"
45
46
void ProjectSettingsEditor::connect_filesystem_dock_signals(FileSystemDock *p_fs_dock) {
47
localization_editor->connect_filesystem_dock_signals(p_fs_dock);
48
group_settings->connect_filesystem_dock_signals(p_fs_dock);
49
}
50
51
void ProjectSettingsEditor::popup_project_settings(bool p_clear_filter) {
52
// Restore valid window bounds or pop up at default size.
53
Rect2 saved_size = EditorSettings::get_singleton()->get_project_metadata("dialog_bounds", "project_settings", Rect2());
54
if (saved_size != Rect2()) {
55
popup(saved_size);
56
} else {
57
popup_centered_clamped(Size2(1200, 700) * EDSCALE, 0.8);
58
}
59
60
_add_feature_overrides();
61
general_settings_inspector->update_category_list();
62
set_process_shortcut_input(true);
63
64
localization_editor->update_translations();
65
autoload_settings->update_autoload();
66
group_settings->update_groups();
67
plugin_settings->update_plugins();
68
import_defaults_editor->clear();
69
70
if (p_clear_filter) {
71
search_box->clear();
72
}
73
74
_focus_current_search_box();
75
}
76
77
void ProjectSettingsEditor::popup_for_override(const String &p_override) {
78
popup_project_settings();
79
tab_container->set_current_tab(0);
80
general_settings_inspector->set_current_section(ProjectSettings::EDITOR_SETTING_OVERRIDE_PREFIX + p_override.get_slicec('/', 0));
81
}
82
83
void ProjectSettingsEditor::set_filter(const String &p_filter) {
84
search_box->set_text(p_filter);
85
}
86
87
void ProjectSettingsEditor::queue_save() {
88
settings_changed = true;
89
timer->start();
90
}
91
92
void ProjectSettingsEditor::_save() {
93
settings_changed = false;
94
if (ps) {
95
ps->save();
96
}
97
if (pending_override_notify) {
98
pending_override_notify = false;
99
EditorNode::get_singleton()->notify_settings_overrides_changed();
100
}
101
}
102
103
void ProjectSettingsEditor::set_plugins_page() {
104
tab_container->set_current_tab(tab_container->get_tab_idx_from_control(plugin_settings));
105
}
106
107
void ProjectSettingsEditor::set_general_page(const String &p_category) {
108
tab_container->set_current_tab(tab_container->get_tab_idx_from_control(general_editor));
109
general_settings_inspector->set_current_section(p_category);
110
}
111
112
void ProjectSettingsEditor::update_plugins() {
113
plugin_settings->update_plugins();
114
}
115
116
void ProjectSettingsEditor::init_autoloads() {
117
autoload_settings->init_autoloads();
118
}
119
120
void ProjectSettingsEditor::_setting_edited(const String &p_name) {
121
const String full_name = general_settings_inspector->get_full_item_path(p_name);
122
if (full_name.begins_with(ProjectSettings::EDITOR_SETTING_OVERRIDE_PREFIX)) {
123
EditorSettings::get_singleton()->mark_setting_changed(full_name.trim_prefix(ProjectSettings::EDITOR_SETTING_OVERRIDE_PREFIX));
124
pending_override_notify = true;
125
}
126
queue_save();
127
}
128
129
void ProjectSettingsEditor::_update_advanced(bool p_is_advanced) {
130
custom_properties->set_visible(p_is_advanced);
131
}
132
133
void ProjectSettingsEditor::_on_category_changed(const String &p_new_category) {
134
general_settings_inspector->get_inspector()->set_use_deletable_properties(p_new_category.begins_with(ProjectSettings::EDITOR_SETTING_OVERRIDE_PREFIX));
135
}
136
137
void ProjectSettingsEditor::_on_editor_override_deleted(const String &p_setting) {
138
const String full_name = general_settings_inspector->get_full_item_path(p_setting);
139
ERR_FAIL_COND(!full_name.begins_with(ProjectSettings::EDITOR_SETTING_OVERRIDE_PREFIX));
140
141
ProjectSettings::get_singleton()->set_setting(full_name, Variant());
142
EditorSettings::get_singleton()->mark_setting_changed(full_name.trim_prefix(ProjectSettings::EDITOR_SETTING_OVERRIDE_PREFIX));
143
pending_override_notify = true;
144
_save();
145
general_settings_inspector->update_category_list();
146
}
147
148
void ProjectSettingsEditor::_advanced_toggled(bool p_button_pressed) {
149
EditorSettings::get_singleton()->set("_project_settings_advanced_mode", p_button_pressed);
150
EditorSettings::get_singleton()->save();
151
_update_advanced(p_button_pressed);
152
}
153
154
void ProjectSettingsEditor::_setting_selected(const String &p_path) {
155
if (p_path.is_empty()) {
156
return;
157
}
158
159
property_box->set_text(general_settings_inspector->get_current_section() + "/" + p_path);
160
161
_update_property_box(); // set_text doesn't trigger text_changed
162
}
163
164
void ProjectSettingsEditor::_add_setting() {
165
String setting = _get_setting_name();
166
167
// Initialize the property with the default value for the given type.
168
Callable::CallError ce;
169
Variant value;
170
Variant::construct(type_box->get_selected_type(), value, nullptr, 0, ce);
171
172
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
173
undo_redo->create_action(TTR("Add Project Setting"));
174
undo_redo->add_do_property(ps, setting, value);
175
undo_redo->add_undo_property(ps, setting, ps->has_setting(setting) ? ps->get(setting) : Variant());
176
177
undo_redo->add_do_method(general_settings_inspector, "update_category_list");
178
undo_redo->add_undo_method(general_settings_inspector, "update_category_list");
179
undo_redo->add_do_method(this, "queue_save");
180
undo_redo->add_undo_method(this, "queue_save");
181
undo_redo->commit_action();
182
183
general_settings_inspector->set_current_section(setting.get_slicec('/', 1));
184
add_button->release_focus();
185
}
186
187
void ProjectSettingsEditor::_delete_setting() {
188
String setting = _get_setting_name();
189
Variant value = ps->get(setting);
190
int order = ps->get_order(setting);
191
192
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
193
undo_redo->create_action(TTR("Delete Item"));
194
195
undo_redo->add_do_method(ps, "clear", setting);
196
undo_redo->add_undo_method(ps, "set", setting, value);
197
undo_redo->add_undo_method(ps, "set_order", setting, order);
198
199
undo_redo->add_do_method(general_settings_inspector, "update_category_list");
200
undo_redo->add_undo_method(general_settings_inspector, "update_category_list");
201
undo_redo->add_do_method(this, "queue_save");
202
undo_redo->add_undo_method(this, "queue_save");
203
204
undo_redo->commit_action();
205
206
property_box->clear();
207
del_button->release_focus();
208
}
209
210
void ProjectSettingsEditor::_property_box_changed(const String &p_text) {
211
_update_property_box();
212
}
213
214
void ProjectSettingsEditor::_feature_selected(int p_index) {
215
const String property = property_box->get_text().strip_edges().get_slicec('.', 0);
216
if (p_index == FEATURE_ALL) {
217
property_box->set_text(property);
218
} else if (p_index == FEATURE_CUSTOM) {
219
property_box->set_text(property + ".custom");
220
const int len = property.length() + 1;
221
property_box->select(len);
222
property_box->set_caret_column(len);
223
property_box->grab_focus();
224
} else {
225
property_box->set_text(property + "." + feature_box->get_item_text(p_index));
226
};
227
_update_property_box();
228
}
229
230
void ProjectSettingsEditor::_update_property_box() {
231
const String setting = _get_setting_name();
232
int slices = setting.get_slice_count(".");
233
const String name = setting.get_slicec('.', 0);
234
const String feature = setting.get_slicec('.', 1);
235
bool feature_invalid = slices > 2 || (slices == 2 && feature.is_empty());
236
237
add_button->set_disabled(true);
238
del_button->set_disabled(true);
239
240
if (feature.is_empty() || feature_invalid) {
241
feature_box->select(FEATURE_ALL);
242
} else {
243
bool is_custom = true;
244
for (int i = FEATURE_FIRST; i < feature_box->get_item_count(); i++) {
245
if (feature == feature_box->get_item_text(i)) {
246
is_custom = false;
247
feature_box->select(i);
248
break;
249
}
250
}
251
if (is_custom) {
252
feature_box->select(FEATURE_CUSTOM);
253
}
254
}
255
256
if (property_box->get_text().is_empty()) {
257
return;
258
}
259
260
if (ps->has_setting(setting)) {
261
del_button->set_disabled(ps->is_builtin_setting(setting) || setting.begins_with(ProjectSettings::EDITOR_SETTING_OVERRIDE_PREFIX));
262
_select_type(ps->get_setting(setting).get_type());
263
} else {
264
if (ps->has_setting(name)) {
265
_select_type(ps->get_setting(name).get_type());
266
} else {
267
type_box->select(0);
268
}
269
270
if (feature_invalid || name.begins_with(ProjectSettings::EDITOR_SETTING_OVERRIDE_PREFIX)) {
271
return;
272
}
273
274
const Vector<String> names = name.split("/");
275
for (int i = 0; i < names.size(); i++) {
276
if (!names[i].is_valid_ascii_identifier()) {
277
return;
278
}
279
}
280
281
add_button->set_disabled(false);
282
}
283
}
284
285
void ProjectSettingsEditor::_select_type(Variant::Type p_type) {
286
type_box->select(type_box->get_item_index(p_type));
287
}
288
289
void ProjectSettingsEditor::shortcut_input(const Ref<InputEvent> &p_event) {
290
const Ref<InputEventKey> k = p_event;
291
if (k.is_valid() && k->is_pressed()) {
292
bool handled = false;
293
294
if (ED_IS_SHORTCUT("ui_undo", p_event)) {
295
EditorNode::get_singleton()->undo();
296
handled = true;
297
}
298
299
if (ED_IS_SHORTCUT("ui_redo", p_event)) {
300
EditorNode::get_singleton()->redo();
301
handled = true;
302
}
303
304
if (ED_IS_SHORTCUT("editor/open_search", p_event)) {
305
_focus_current_search_box();
306
handled = true;
307
}
308
309
if (ED_IS_SHORTCUT("file_dialog/focus_path", p_event)) {
310
_focus_current_path_box();
311
handled = true;
312
}
313
314
if (handled) {
315
set_input_as_handled();
316
}
317
}
318
}
319
320
String ProjectSettingsEditor::_get_setting_name() const {
321
String name = property_box->get_text().strip_edges();
322
if (!name.begins_with("_") && !name.contains_char('/')) {
323
name = "global/" + name;
324
}
325
return name;
326
}
327
328
void ProjectSettingsEditor::_add_feature_overrides() {
329
HashSet<String> presets;
330
331
presets.insert("bptc");
332
presets.insert("s3tc");
333
presets.insert("etc2");
334
presets.insert("editor");
335
presets.insert("editor_hint");
336
presets.insert("editor_runtime");
337
presets.insert("template_debug");
338
presets.insert("template_release");
339
presets.insert("debug");
340
presets.insert("release");
341
presets.insert("template");
342
presets.insert("double");
343
presets.insert("single");
344
presets.insert("32");
345
presets.insert("64");
346
presets.insert("movie");
347
348
EditorExport *ee = EditorExport::get_singleton();
349
350
for (int i = 0; i < ee->get_export_platform_count(); i++) {
351
List<String> p;
352
ee->get_export_platform(i)->get_platform_features(&p);
353
for (const String &E : p) {
354
presets.insert(E);
355
}
356
}
357
358
for (int i = 0; i < ee->get_export_preset_count(); i++) {
359
List<String> p;
360
ee->get_export_preset(i)->get_platform()->get_preset_features(ee->get_export_preset(i), &p);
361
for (const String &E : p) {
362
presets.insert(E);
363
}
364
365
String custom = ee->get_export_preset(i)->get_custom_features();
366
Vector<String> custom_list = custom.split(",");
367
for (int j = 0; j < custom_list.size(); j++) {
368
String f = custom_list[j].strip_edges();
369
if (!f.is_empty()) {
370
presets.insert(f);
371
}
372
}
373
}
374
375
feature_box->clear();
376
feature_box->add_item(TTRC("All"), FEATURE_ALL); // So it is always on top.
377
feature_box->set_item_auto_translate_mode(-1, AUTO_TRANSLATE_MODE_ALWAYS);
378
feature_box->add_item(TTRC("Custom"), FEATURE_CUSTOM);
379
feature_box->set_item_auto_translate_mode(-1, AUTO_TRANSLATE_MODE_ALWAYS);
380
feature_box->add_separator();
381
382
int id = FEATURE_FIRST;
383
for (const String &E : presets) {
384
feature_box->add_item(E, id++);
385
}
386
}
387
388
void ProjectSettingsEditor::_tabs_tab_changed(int p_tab) {
389
_focus_current_search_box();
390
}
391
392
void ProjectSettingsEditor::_focus_current_search_box() {
393
Control *tab = tab_container->get_current_tab_control();
394
LineEdit *current_search_box = nullptr;
395
if (tab == general_editor) {
396
current_search_box = search_box;
397
} else if (tab == action_map_editor) {
398
current_search_box = action_map_editor->get_search_box();
399
}
400
401
if (current_search_box) {
402
current_search_box->grab_focus();
403
current_search_box->select_all();
404
}
405
}
406
407
void ProjectSettingsEditor::_focus_current_path_box() {
408
Control *tab = tab_container->get_current_tab_control();
409
LineEdit *current_path_box = nullptr;
410
if (tab == general_editor) {
411
current_path_box = property_box;
412
} else if (tab == action_map_editor) {
413
current_path_box = action_map_editor->get_path_box();
414
} else if (tab == autoload_settings) {
415
current_path_box = autoload_settings->get_path_box();
416
} else if (tab == shaders_global_shader_uniforms_editor) {
417
current_path_box = shaders_global_shader_uniforms_editor->get_name_box();
418
} else if (tab == group_settings) {
419
current_path_box = group_settings->get_name_box();
420
}
421
422
if (current_path_box) {
423
current_path_box->grab_focus();
424
current_path_box->select_all();
425
}
426
}
427
428
void ProjectSettingsEditor::_editor_restart() {
429
ProjectSettings::get_singleton()->save();
430
EditorNode::get_singleton()->save_all_scenes();
431
EditorNode::get_singleton()->restart_editor();
432
}
433
434
void ProjectSettingsEditor::_editor_restart_request() {
435
restart_container->show();
436
}
437
438
void ProjectSettingsEditor::_editor_restart_close() {
439
restart_container->hide();
440
}
441
442
void ProjectSettingsEditor::_action_added(const String &p_name) {
443
String name = "input/" + p_name;
444
445
ERR_FAIL_COND_MSG(ProjectSettings::get_singleton()->has_setting(name),
446
"An action with this name already exists.");
447
448
Dictionary action;
449
action["events"] = Array();
450
action["deadzone"] = InputMap::DEFAULT_DEADZONE;
451
452
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
453
undo_redo->create_action(TTR("Add Input Action"));
454
undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", name, action);
455
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "clear", name);
456
457
undo_redo->add_do_method(this, "_update_action_map_editor");
458
undo_redo->add_undo_method(this, "_update_action_map_editor");
459
undo_redo->add_do_method(this, "queue_save");
460
undo_redo->add_undo_method(this, "queue_save");
461
undo_redo->commit_action();
462
}
463
464
void ProjectSettingsEditor::_action_edited(const String &p_name, const Dictionary &p_action) {
465
const String property_name = "input/" + p_name;
466
Dictionary old_val = GLOBAL_GET(property_name);
467
468
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
469
if (old_val["deadzone"] != p_action["deadzone"]) {
470
// Deadzone Changed
471
undo_redo->create_action(TTR("Change Action deadzone"));
472
undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", property_name, p_action);
473
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", property_name, old_val);
474
475
} else {
476
// Events changed
477
undo_redo->create_action(TTR("Change Input Action Event(s)"));
478
undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", property_name, p_action);
479
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", property_name, old_val);
480
}
481
482
undo_redo->add_do_method(this, "_update_action_map_editor");
483
undo_redo->add_undo_method(this, "_update_action_map_editor");
484
undo_redo->add_do_method(this, "queue_save");
485
undo_redo->add_undo_method(this, "queue_save");
486
undo_redo->commit_action();
487
}
488
489
void ProjectSettingsEditor::_action_removed(const String &p_name) {
490
const String property_name = "input/" + p_name;
491
492
Dictionary old_val = GLOBAL_GET(property_name);
493
int order = ProjectSettings::get_singleton()->get_order(property_name);
494
495
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
496
undo_redo->create_action(TTR("Erase Input Action"));
497
undo_redo->add_do_method(ProjectSettings::get_singleton(), "clear", property_name);
498
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", property_name, old_val);
499
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_order", property_name, order);
500
501
undo_redo->add_do_method(this, "_update_action_map_editor");
502
undo_redo->add_undo_method(this, "_update_action_map_editor");
503
undo_redo->add_do_method(this, "queue_save");
504
undo_redo->add_undo_method(this, "queue_save");
505
undo_redo->commit_action();
506
}
507
508
void ProjectSettingsEditor::_action_renamed(const String &p_old_name, const String &p_new_name) {
509
const String old_property_name = "input/" + p_old_name;
510
const String new_property_name = "input/" + p_new_name;
511
512
ERR_FAIL_COND_MSG(ProjectSettings::get_singleton()->has_setting(new_property_name),
513
"An action with this name already exists.");
514
515
int order = ProjectSettings::get_singleton()->get_order(old_property_name);
516
Dictionary action = GLOBAL_GET(old_property_name);
517
518
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
519
undo_redo->create_action(TTR("Rename Input Action"));
520
// Do: clear old, set new
521
undo_redo->add_do_method(ProjectSettings::get_singleton(), "clear", old_property_name);
522
undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", new_property_name, action);
523
undo_redo->add_do_method(ProjectSettings::get_singleton(), "set_order", new_property_name, order);
524
// Undo: clear new, set old
525
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "clear", new_property_name);
526
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", old_property_name, action);
527
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_order", old_property_name, order);
528
529
undo_redo->add_do_method(this, "_update_action_map_editor");
530
undo_redo->add_undo_method(this, "_update_action_map_editor");
531
undo_redo->add_do_method(this, "queue_save");
532
undo_redo->add_undo_method(this, "queue_save");
533
undo_redo->commit_action();
534
}
535
536
void ProjectSettingsEditor::_action_reordered(const String &p_action_name, const String &p_relative_to, bool p_before) {
537
const String action_name = "input/" + p_action_name;
538
const String target_name = "input/" + p_relative_to;
539
540
// It is much easier to rebuild the custom "input" properties rather than messing around with the "order" values of them.
541
Variant action_value = ps->get(action_name);
542
Variant target_value = ps->get(target_name);
543
544
List<PropertyInfo> props;
545
HashMap<String, Variant> action_values;
546
ProjectSettings::get_singleton()->get_property_list(&props);
547
548
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
549
undo_redo->create_action(TTR("Update Input Action Order"));
550
551
for (const PropertyInfo &prop : props) {
552
// Skip builtins and non-inputs
553
// Order matters here, checking for "input/" filters out properties that aren't settings and produce errors in is_builtin_setting().
554
if (!prop.name.begins_with("input/") || ProjectSettings::get_singleton()->is_builtin_setting(prop.name)) {
555
continue;
556
}
557
558
action_values.insert(prop.name, ps->get(prop.name));
559
560
undo_redo->add_do_method(ProjectSettings::get_singleton(), "clear", prop.name);
561
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "clear", prop.name);
562
}
563
564
for (const KeyValue<String, Variant> &E : action_values) {
565
String name = E.key;
566
const Variant &value = E.value;
567
568
if (name == target_name) {
569
if (p_before) {
570
// Insert before target
571
undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", action_name, action_value);
572
undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", target_name, target_value);
573
574
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", target_name, target_value);
575
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", action_name, action_value);
576
} else {
577
// Insert after target
578
undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", target_name, target_value);
579
undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", action_name, action_value);
580
581
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", action_name, action_value);
582
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", target_name, target_value);
583
}
584
585
} else if (name != action_name) {
586
undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", name, value);
587
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", name, value);
588
}
589
}
590
591
undo_redo->add_do_method(this, "_update_action_map_editor");
592
undo_redo->add_undo_method(this, "_update_action_map_editor");
593
undo_redo->add_do_method(this, "queue_save");
594
undo_redo->add_undo_method(this, "queue_save");
595
undo_redo->commit_action();
596
}
597
598
void ProjectSettingsEditor::_update_action_map_editor() {
599
Vector<ActionMapEditor::ActionInfo> actions;
600
601
List<PropertyInfo> props;
602
ProjectSettings::get_singleton()->get_property_list(&props);
603
604
const Ref<Texture2D> builtin_icon = get_editor_theme_icon(SNAME("PinPressed"));
605
for (const PropertyInfo &E : props) {
606
const String property_name = E.name;
607
608
if (!property_name.begins_with("input/")) {
609
continue;
610
}
611
612
// Strip the "input/" from the left.
613
String display_name = property_name.substr(String("input/").size() - 1);
614
Dictionary action = GLOBAL_GET(property_name);
615
616
ActionMapEditor::ActionInfo action_info;
617
action_info.action = action;
618
action_info.editable = true;
619
action_info.name = display_name;
620
621
const bool is_builtin_input = ProjectSettings::get_singleton()->get_input_presets().find(property_name) != nullptr;
622
if (is_builtin_input) {
623
action_info.editable = false;
624
action_info.icon = builtin_icon;
625
action_info.has_initial = true;
626
action_info.action_initial = ProjectSettings::get_singleton()->property_get_revert(property_name);
627
}
628
629
actions.push_back(action_info);
630
}
631
632
action_map_editor->update_action_list(actions);
633
}
634
635
void ProjectSettingsEditor::_update_theme() {
636
add_button->set_button_icon(get_editor_theme_icon(SNAME("Add")));
637
del_button->set_button_icon(get_editor_theme_icon(SNAME("Remove")));
638
search_box->set_right_icon(get_editor_theme_icon(SNAME("Search")));
639
restart_close_button->set_button_icon(get_editor_theme_icon(SNAME("Close")));
640
restart_container->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree")));
641
restart_icon->set_texture(get_editor_theme_icon(SNAME("StatusWarning")));
642
restart_label->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));
643
}
644
645
void ProjectSettingsEditor::_notification(int p_what) {
646
switch (p_what) {
647
case NOTIFICATION_VISIBILITY_CHANGED: {
648
if (is_visible()) {
649
HashMap<String, PropertyInfo> editor_settings_info;
650
651
List<PropertyInfo> infos;
652
EditorSettings::get_singleton()->get_property_list(&infos);
653
654
for (const PropertyInfo &pi : infos) {
655
editor_settings_info[pi.name] = pi;
656
}
657
ProjectSettings::get_singleton()->editor_settings_info = editor_settings_info;
658
} else {
659
EditorSettings::get_singleton()->set_project_metadata("dialog_bounds", "project_settings", Rect2(get_position(), get_size()));
660
if (settings_changed) {
661
timer->stop();
662
_save();
663
}
664
}
665
} break;
666
667
case NOTIFICATION_ENTER_TREE: {
668
general_settings_inspector->edit(ps);
669
_update_action_map_editor();
670
_update_theme();
671
} break;
672
673
case NOTIFICATION_THEME_CHANGED: {
674
_update_theme();
675
} break;
676
}
677
}
678
679
void ProjectSettingsEditor::_bind_methods() {
680
ClassDB::bind_method(D_METHOD("queue_save"), &ProjectSettingsEditor::queue_save);
681
682
ClassDB::bind_method(D_METHOD("_update_action_map_editor"), &ProjectSettingsEditor::_update_action_map_editor);
683
}
684
685
ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
686
singleton = this;
687
set_title(TTRC("Project Settings (project.godot)"));
688
set_clamp_to_embedder(true);
689
690
ps = ProjectSettings::get_singleton();
691
data = p_data;
692
693
tab_container = memnew(TabContainer);
694
tab_container->set_use_hidden_tabs_for_min_size(true);
695
tab_container->set_theme_type_variation("TabContainerOdd");
696
tab_container->connect("tab_changed", callable_mp(this, &ProjectSettingsEditor::_tabs_tab_changed));
697
add_child(tab_container);
698
699
general_editor = memnew(VBoxContainer);
700
general_editor->set_name(TTRC("General"));
701
general_editor->set_alignment(BoxContainer::ALIGNMENT_BEGIN);
702
general_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
703
tab_container->add_child(general_editor);
704
705
HBoxContainer *search_bar = memnew(HBoxContainer);
706
general_editor->add_child(search_bar);
707
708
search_box = memnew(LineEdit);
709
search_box->set_placeholder(TTRC("Filter Settings"));
710
search_box->set_accessibility_name(TTRC("Filter Settings"));
711
search_box->set_clear_button_enabled(true);
712
search_box->set_virtual_keyboard_show_on_focus(false);
713
search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL);
714
search_bar->add_child(search_box);
715
716
advanced = memnew(CheckButton);
717
advanced->set_text(TTRC("Advanced Settings"));
718
search_bar->add_child(advanced);
719
720
custom_properties = memnew(HBoxContainer);
721
general_editor->add_child(custom_properties);
722
723
property_box = memnew(LineEdit);
724
property_box->set_placeholder(TTRC("Select a Setting or Type its Name"));
725
property_box->set_h_size_flags(Control::SIZE_EXPAND_FILL);
726
property_box->connect(SceneStringName(text_changed), callable_mp(this, &ProjectSettingsEditor::_property_box_changed));
727
custom_properties->add_child(property_box);
728
729
feature_box = memnew(OptionButton);
730
feature_box->set_custom_minimum_size(Size2(120, 0) * EDSCALE);
731
feature_box->set_accessibility_name(TTRC("Feature"));
732
feature_box->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
733
feature_box->connect(SceneStringName(item_selected), callable_mp(this, &ProjectSettingsEditor::_feature_selected));
734
custom_properties->add_child(feature_box);
735
736
type_box = memnew(EditorVariantTypeOptionButton);
737
type_box->populate({ Variant::NIL, Variant::OBJECT });
738
type_box->set_custom_minimum_size(Size2(120, 0) * EDSCALE);
739
type_box->set_accessibility_name(TTRC("Type"));
740
custom_properties->add_child(type_box);
741
742
add_button = memnew(Button);
743
add_button->set_text(TTRC("Add"));
744
add_button->set_disabled(true);
745
add_button->connect(SceneStringName(pressed), callable_mp(this, &ProjectSettingsEditor::_add_setting));
746
custom_properties->add_child(add_button);
747
748
del_button = memnew(Button);
749
del_button->set_text(TTRC("Delete"));
750
del_button->set_disabled(true);
751
del_button->connect(SceneStringName(pressed), callable_mp(this, &ProjectSettingsEditor::_delete_setting));
752
custom_properties->add_child(del_button);
753
754
general_settings_inspector = memnew(SectionedInspector);
755
general_settings_inspector->set_v_size_flags(Control::SIZE_EXPAND_FILL);
756
general_settings_inspector->register_search_box(search_box);
757
general_settings_inspector->register_advanced_toggle(advanced);
758
general_settings_inspector->connect("category_changed", callable_mp(this, &ProjectSettingsEditor::_on_category_changed));
759
general_settings_inspector->get_inspector()->set_use_filter(true);
760
general_settings_inspector->get_inspector()->set_mark_unsaved(false);
761
general_settings_inspector->get_inspector()->connect("property_selected", callable_mp(this, &ProjectSettingsEditor::_setting_selected));
762
general_settings_inspector->get_inspector()->connect("property_edited", callable_mp(this, &ProjectSettingsEditor::_setting_edited));
763
general_settings_inspector->get_inspector()->connect("property_deleted", callable_mp(this, &ProjectSettingsEditor::_on_editor_override_deleted));
764
general_settings_inspector->get_inspector()->connect("restart_requested", callable_mp(this, &ProjectSettingsEditor::_editor_restart_request));
765
general_editor->add_child(general_settings_inspector);
766
767
if (EDITOR_GET("interface/touchscreen/enable_touch_optimizations")) {
768
general_settings_inspector->set_touch_dragger_enabled(true);
769
}
770
771
restart_container = memnew(PanelContainer);
772
general_editor->add_child(restart_container);
773
774
HBoxContainer *restart_hb = memnew(HBoxContainer);
775
restart_container->hide();
776
restart_container->add_child(restart_hb);
777
778
restart_icon = memnew(TextureRect);
779
restart_icon->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
780
restart_hb->add_child(restart_icon);
781
782
restart_label = memnew(Label);
783
restart_label->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
784
restart_label->set_text(TTRC("Changed settings will be applied to the editor after restarting."));
785
restart_hb->add_child(restart_label);
786
restart_hb->add_spacer();
787
788
Button *restart_button = memnew(Button);
789
restart_button->connect(SceneStringName(pressed), callable_mp(this, &ProjectSettingsEditor::_editor_restart));
790
restart_hb->add_child(restart_button);
791
restart_button->set_text(TTRC("Save & Restart"));
792
793
restart_close_button = memnew(Button);
794
restart_close_button->set_flat(true);
795
restart_close_button->connect(SceneStringName(pressed), callable_mp(this, &ProjectSettingsEditor::_editor_restart_close));
796
restart_close_button->set_accessibility_name(TTRC("Close"));
797
restart_hb->add_child(restart_close_button);
798
799
action_map_editor = memnew(ActionMapEditor);
800
action_map_editor->set_name(TTRC("Input Map"));
801
action_map_editor->connect("action_added", callable_mp(this, &ProjectSettingsEditor::_action_added));
802
action_map_editor->connect("action_edited", callable_mp(this, &ProjectSettingsEditor::_action_edited));
803
action_map_editor->connect("action_removed", callable_mp(this, &ProjectSettingsEditor::_action_removed));
804
action_map_editor->connect("action_renamed", callable_mp(this, &ProjectSettingsEditor::_action_renamed));
805
action_map_editor->connect("action_reordered", callable_mp(this, &ProjectSettingsEditor::_action_reordered));
806
tab_container->add_child(action_map_editor);
807
808
localization_editor = memnew(LocalizationEditor);
809
localization_editor->set_name(TTRC("Localization"));
810
localization_editor->connect("localization_changed", callable_mp(this, &ProjectSettingsEditor::queue_save));
811
tab_container->add_child(localization_editor);
812
813
TabContainer *globals_container = memnew(TabContainer);
814
globals_container->set_name(TTRC("Globals"));
815
tab_container->add_child(globals_container);
816
817
autoload_settings = memnew(EditorAutoloadSettings);
818
autoload_settings->set_name(TTRC("Autoload"));
819
autoload_settings->connect("autoload_changed", callable_mp(this, &ProjectSettingsEditor::queue_save));
820
globals_container->add_child(autoload_settings);
821
822
shaders_global_shader_uniforms_editor = memnew(ShaderGlobalsEditor);
823
shaders_global_shader_uniforms_editor->set_name(TTRC("Shader Globals"));
824
shaders_global_shader_uniforms_editor->connect("globals_changed", callable_mp(this, &ProjectSettingsEditor::queue_save));
825
globals_container->add_child(shaders_global_shader_uniforms_editor);
826
827
group_settings = memnew(GroupSettingsEditor);
828
group_settings->set_name(TTRC("Groups"));
829
group_settings->connect("group_changed", callable_mp(this, &ProjectSettingsEditor::queue_save));
830
globals_container->add_child(group_settings);
831
832
plugin_settings = memnew(EditorPluginSettings);
833
plugin_settings->set_name(TTRC("Plugins"));
834
tab_container->add_child(plugin_settings);
835
836
timer = memnew(Timer);
837
timer->set_wait_time(1.5);
838
timer->connect("timeout", callable_mp(this, &ProjectSettingsEditor::_save));
839
timer->set_one_shot(true);
840
add_child(timer);
841
842
set_ok_button_text(TTRC("Close"));
843
set_hide_on_ok(true);
844
845
bool use_advanced = EDITOR_DEF("_project_settings_advanced_mode", false);
846
if (use_advanced) {
847
advanced->set_pressed(true);
848
}
849
advanced->connect(SceneStringName(toggled), callable_mp(this, &ProjectSettingsEditor::_advanced_toggled));
850
851
_update_advanced(use_advanced);
852
853
import_defaults_editor = memnew(ImportDefaultsEditor);
854
import_defaults_editor->set_name(TTRC("Import Defaults"));
855
tab_container->add_child(import_defaults_editor);
856
857
MovieWriter::set_extensions_hint(); // ensure extensions are properly displayed.
858
}
859
860