Path: blob/master/editor/settings/project_settings_editor.cpp
9902 views
/**************************************************************************/1/* project_settings_editor.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "project_settings_editor.h"3132#include "core/config/project_settings.h"33#include "core/input/input_map.h"34#include "editor/editor_node.h"35#include "editor/editor_string_names.h"36#include "editor/editor_undo_redo_manager.h"37#include "editor/export/editor_export.h"38#include "editor/gui/editor_variant_type_selectors.h"39#include "editor/inspector/editor_inspector.h"40#include "editor/settings/editor_settings.h"41#include "editor/themes/editor_scale.h"42#include "scene/gui/check_button.h"43#include "servers/movie_writer/movie_writer.h"4445void ProjectSettingsEditor::connect_filesystem_dock_signals(FileSystemDock *p_fs_dock) {46localization_editor->connect_filesystem_dock_signals(p_fs_dock);47group_settings->connect_filesystem_dock_signals(p_fs_dock);48}4950void ProjectSettingsEditor::popup_project_settings(bool p_clear_filter) {51// Restore valid window bounds or pop up at default size.52Rect2 saved_size = EditorSettings::get_singleton()->get_project_metadata("dialog_bounds", "project_settings", Rect2());53if (saved_size != Rect2()) {54popup(saved_size);55} else {56popup_centered_clamped(Size2(1200, 700) * EDSCALE, 0.8);57}5859_add_feature_overrides();60general_settings_inspector->update_category_list();61set_process_shortcut_input(true);6263localization_editor->update_translations();64autoload_settings->update_autoload();65group_settings->update_groups();66plugin_settings->update_plugins();67import_defaults_editor->clear();6869if (p_clear_filter) {70search_box->clear();71}7273_focus_current_search_box();74}7576void ProjectSettingsEditor::popup_for_override(const String &p_override) {77popup_project_settings();78tab_container->set_current_tab(0);79general_settings_inspector->set_current_section(ProjectSettings::EDITOR_SETTING_OVERRIDE_PREFIX + p_override.get_slicec('/', 0));80}8182void ProjectSettingsEditor::set_filter(const String &p_filter) {83search_box->set_text(p_filter);84}8586void ProjectSettingsEditor::queue_save() {87settings_changed = true;88timer->start();89}9091void ProjectSettingsEditor::_save() {92settings_changed = false;93if (ps) {94ps->save();95}96if (pending_override_notify) {97pending_override_notify = false;98EditorNode::get_singleton()->notify_settings_overrides_changed();99}100}101102void ProjectSettingsEditor::set_plugins_page() {103tab_container->set_current_tab(tab_container->get_tab_idx_from_control(plugin_settings));104}105106void ProjectSettingsEditor::set_general_page(const String &p_category) {107tab_container->set_current_tab(tab_container->get_tab_idx_from_control(general_editor));108general_settings_inspector->set_current_section(p_category);109}110111void ProjectSettingsEditor::update_plugins() {112plugin_settings->update_plugins();113}114115void ProjectSettingsEditor::init_autoloads() {116autoload_settings->init_autoloads();117}118119void ProjectSettingsEditor::_setting_edited(const String &p_name) {120const String full_name = general_settings_inspector->get_full_item_path(p_name);121if (full_name.begins_with(ProjectSettings::EDITOR_SETTING_OVERRIDE_PREFIX)) {122EditorSettings::get_singleton()->mark_setting_changed(full_name.trim_prefix(ProjectSettings::EDITOR_SETTING_OVERRIDE_PREFIX));123pending_override_notify = true;124}125queue_save();126}127128void ProjectSettingsEditor::_update_advanced(bool p_is_advanced) {129custom_properties->set_visible(p_is_advanced);130}131132void ProjectSettingsEditor::_on_category_changed(const String &p_new_category) {133general_settings_inspector->get_inspector()->set_use_deletable_properties(p_new_category.begins_with(ProjectSettings::EDITOR_SETTING_OVERRIDE_PREFIX));134}135136void ProjectSettingsEditor::_on_editor_override_deleted(const String &p_setting) {137const String full_name = general_settings_inspector->get_full_item_path(p_setting);138ERR_FAIL_COND(!full_name.begins_with(ProjectSettings::EDITOR_SETTING_OVERRIDE_PREFIX));139140ProjectSettings::get_singleton()->set_setting(full_name, Variant());141EditorSettings::get_singleton()->mark_setting_changed(full_name.trim_prefix(ProjectSettings::EDITOR_SETTING_OVERRIDE_PREFIX));142pending_override_notify = true;143_save();144general_settings_inspector->update_category_list();145}146147void ProjectSettingsEditor::_advanced_toggled(bool p_button_pressed) {148EditorSettings::get_singleton()->set("_project_settings_advanced_mode", p_button_pressed);149EditorSettings::get_singleton()->save();150_update_advanced(p_button_pressed);151}152153void ProjectSettingsEditor::_setting_selected(const String &p_path) {154if (p_path.is_empty()) {155return;156}157158property_box->set_text(general_settings_inspector->get_current_section() + "/" + p_path);159160_update_property_box(); // set_text doesn't trigger text_changed161}162163void ProjectSettingsEditor::_add_setting() {164String setting = _get_setting_name();165166// Initialize the property with the default value for the given type.167Callable::CallError ce;168Variant value;169Variant::construct(type_box->get_selected_type(), value, nullptr, 0, ce);170171EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();172undo_redo->create_action(TTR("Add Project Setting"));173undo_redo->add_do_property(ps, setting, value);174undo_redo->add_undo_property(ps, setting, ps->has_setting(setting) ? ps->get(setting) : Variant());175176undo_redo->add_do_method(general_settings_inspector, "update_category_list");177undo_redo->add_undo_method(general_settings_inspector, "update_category_list");178undo_redo->add_do_method(this, "queue_save");179undo_redo->add_undo_method(this, "queue_save");180undo_redo->commit_action();181182general_settings_inspector->set_current_section(setting.get_slicec('/', 1));183add_button->release_focus();184}185186void ProjectSettingsEditor::_delete_setting() {187String setting = _get_setting_name();188Variant value = ps->get(setting);189int order = ps->get_order(setting);190191EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();192undo_redo->create_action(TTR("Delete Item"));193194undo_redo->add_do_method(ps, "clear", setting);195undo_redo->add_undo_method(ps, "set", setting, value);196undo_redo->add_undo_method(ps, "set_order", setting, order);197198undo_redo->add_do_method(general_settings_inspector, "update_category_list");199undo_redo->add_undo_method(general_settings_inspector, "update_category_list");200undo_redo->add_do_method(this, "queue_save");201undo_redo->add_undo_method(this, "queue_save");202203undo_redo->commit_action();204205property_box->clear();206del_button->release_focus();207}208209void ProjectSettingsEditor::_property_box_changed(const String &p_text) {210_update_property_box();211}212213void ProjectSettingsEditor::_feature_selected(int p_index) {214const String property = property_box->get_text().strip_edges().get_slicec('.', 0);215if (p_index == FEATURE_ALL) {216property_box->set_text(property);217} else if (p_index == FEATURE_CUSTOM) {218property_box->set_text(property + ".custom");219const int len = property.length() + 1;220property_box->select(len);221property_box->set_caret_column(len);222property_box->grab_focus();223} else {224property_box->set_text(property + "." + feature_box->get_item_text(p_index));225};226_update_property_box();227}228229void ProjectSettingsEditor::_update_property_box() {230const String setting = _get_setting_name();231int slices = setting.get_slice_count(".");232const String name = setting.get_slicec('.', 0);233const String feature = setting.get_slicec('.', 1);234bool feature_invalid = slices > 2 || (slices == 2 && feature.is_empty());235236add_button->set_disabled(true);237del_button->set_disabled(true);238239if (feature.is_empty() || feature_invalid) {240feature_box->select(FEATURE_ALL);241} else {242bool is_custom = true;243for (int i = FEATURE_FIRST; i < feature_box->get_item_count(); i++) {244if (feature == feature_box->get_item_text(i)) {245is_custom = false;246feature_box->select(i);247break;248}249}250if (is_custom) {251feature_box->select(FEATURE_CUSTOM);252}253}254255if (property_box->get_text().is_empty()) {256return;257}258259if (ps->has_setting(setting)) {260del_button->set_disabled(ps->is_builtin_setting(setting) || setting.begins_with(ProjectSettings::EDITOR_SETTING_OVERRIDE_PREFIX));261_select_type(ps->get_setting(setting).get_type());262} else {263if (ps->has_setting(name)) {264_select_type(ps->get_setting(name).get_type());265} else {266type_box->select(0);267}268269if (feature_invalid || name.begins_with(ProjectSettings::EDITOR_SETTING_OVERRIDE_PREFIX)) {270return;271}272273const Vector<String> names = name.split("/");274for (int i = 0; i < names.size(); i++) {275if (!names[i].is_valid_ascii_identifier()) {276return;277}278}279280add_button->set_disabled(false);281}282}283284void ProjectSettingsEditor::_select_type(Variant::Type p_type) {285type_box->select(type_box->get_item_index(p_type));286}287288void ProjectSettingsEditor::shortcut_input(const Ref<InputEvent> &p_event) {289const Ref<InputEventKey> k = p_event;290if (k.is_valid() && k->is_pressed()) {291bool handled = false;292293if (ED_IS_SHORTCUT("ui_undo", p_event)) {294EditorNode::get_singleton()->undo();295handled = true;296}297298if (ED_IS_SHORTCUT("ui_redo", p_event)) {299EditorNode::get_singleton()->redo();300handled = true;301}302303if (ED_IS_SHORTCUT("editor/open_search", p_event)) {304_focus_current_search_box();305handled = true;306}307308if (ED_IS_SHORTCUT("file_dialog/focus_path", p_event)) {309_focus_current_path_box();310handled = true;311}312313if (handled) {314set_input_as_handled();315}316}317}318319String ProjectSettingsEditor::_get_setting_name() const {320String name = property_box->get_text().strip_edges();321if (!name.begins_with("_") && !name.contains_char('/')) {322name = "global/" + name;323}324return name;325}326327void ProjectSettingsEditor::_add_feature_overrides() {328HashSet<String> presets;329330presets.insert("bptc");331presets.insert("s3tc");332presets.insert("etc2");333presets.insert("editor");334presets.insert("editor_hint");335presets.insert("editor_runtime");336presets.insert("template_debug");337presets.insert("template_release");338presets.insert("debug");339presets.insert("release");340presets.insert("template");341presets.insert("double");342presets.insert("single");343presets.insert("32");344presets.insert("64");345presets.insert("movie");346347EditorExport *ee = EditorExport::get_singleton();348349for (int i = 0; i < ee->get_export_platform_count(); i++) {350List<String> p;351ee->get_export_platform(i)->get_platform_features(&p);352for (const String &E : p) {353presets.insert(E);354}355}356357for (int i = 0; i < ee->get_export_preset_count(); i++) {358List<String> p;359ee->get_export_preset(i)->get_platform()->get_preset_features(ee->get_export_preset(i), &p);360for (const String &E : p) {361presets.insert(E);362}363364String custom = ee->get_export_preset(i)->get_custom_features();365Vector<String> custom_list = custom.split(",");366for (int j = 0; j < custom_list.size(); j++) {367String f = custom_list[j].strip_edges();368if (!f.is_empty()) {369presets.insert(f);370}371}372}373374feature_box->clear();375feature_box->add_item(TTRC("All"), FEATURE_ALL); // So it is always on top.376feature_box->set_item_auto_translate_mode(-1, AUTO_TRANSLATE_MODE_ALWAYS);377feature_box->add_item(TTRC("Custom"), FEATURE_CUSTOM);378feature_box->set_item_auto_translate_mode(-1, AUTO_TRANSLATE_MODE_ALWAYS);379feature_box->add_separator();380381int id = FEATURE_FIRST;382for (const String &E : presets) {383feature_box->add_item(E, id++);384}385}386387void ProjectSettingsEditor::_tabs_tab_changed(int p_tab) {388_focus_current_search_box();389}390391void ProjectSettingsEditor::_focus_current_search_box() {392Control *tab = tab_container->get_current_tab_control();393LineEdit *current_search_box = nullptr;394if (tab == general_editor) {395current_search_box = search_box;396} else if (tab == action_map_editor) {397current_search_box = action_map_editor->get_search_box();398}399400if (current_search_box) {401current_search_box->grab_focus();402current_search_box->select_all();403}404}405406void ProjectSettingsEditor::_focus_current_path_box() {407Control *tab = tab_container->get_current_tab_control();408LineEdit *current_path_box = nullptr;409if (tab == general_editor) {410current_path_box = property_box;411} else if (tab == action_map_editor) {412current_path_box = action_map_editor->get_path_box();413} else if (tab == autoload_settings) {414current_path_box = autoload_settings->get_path_box();415} else if (tab == shaders_global_shader_uniforms_editor) {416current_path_box = shaders_global_shader_uniforms_editor->get_name_box();417} else if (tab == group_settings) {418current_path_box = group_settings->get_name_box();419}420421if (current_path_box) {422current_path_box->grab_focus();423current_path_box->select_all();424}425}426427void ProjectSettingsEditor::_editor_restart() {428ProjectSettings::get_singleton()->save();429EditorNode::get_singleton()->save_all_scenes();430EditorNode::get_singleton()->restart_editor();431}432433void ProjectSettingsEditor::_editor_restart_request() {434restart_container->show();435}436437void ProjectSettingsEditor::_editor_restart_close() {438restart_container->hide();439}440441void ProjectSettingsEditor::_action_added(const String &p_name) {442String name = "input/" + p_name;443444ERR_FAIL_COND_MSG(ProjectSettings::get_singleton()->has_setting(name),445"An action with this name already exists.");446447Dictionary action;448action["events"] = Array();449action["deadzone"] = InputMap::DEFAULT_DEADZONE;450451EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();452undo_redo->create_action(TTR("Add Input Action"));453undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", name, action);454undo_redo->add_undo_method(ProjectSettings::get_singleton(), "clear", name);455456undo_redo->add_do_method(this, "_update_action_map_editor");457undo_redo->add_undo_method(this, "_update_action_map_editor");458undo_redo->add_do_method(this, "queue_save");459undo_redo->add_undo_method(this, "queue_save");460undo_redo->commit_action();461}462463void ProjectSettingsEditor::_action_edited(const String &p_name, const Dictionary &p_action) {464const String property_name = "input/" + p_name;465Dictionary old_val = GLOBAL_GET(property_name);466467EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();468if (old_val["deadzone"] != p_action["deadzone"]) {469// Deadzone Changed470undo_redo->create_action(TTR("Change Action deadzone"));471undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", property_name, p_action);472undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", property_name, old_val);473474} else {475// Events changed476undo_redo->create_action(TTR("Change Input Action Event(s)"));477undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", property_name, p_action);478undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", property_name, old_val);479}480481undo_redo->add_do_method(this, "_update_action_map_editor");482undo_redo->add_undo_method(this, "_update_action_map_editor");483undo_redo->add_do_method(this, "queue_save");484undo_redo->add_undo_method(this, "queue_save");485undo_redo->commit_action();486}487488void ProjectSettingsEditor::_action_removed(const String &p_name) {489const String property_name = "input/" + p_name;490491Dictionary old_val = GLOBAL_GET(property_name);492int order = ProjectSettings::get_singleton()->get_order(property_name);493494EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();495undo_redo->create_action(TTR("Erase Input Action"));496undo_redo->add_do_method(ProjectSettings::get_singleton(), "clear", property_name);497undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", property_name, old_val);498undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_order", property_name, order);499500undo_redo->add_do_method(this, "_update_action_map_editor");501undo_redo->add_undo_method(this, "_update_action_map_editor");502undo_redo->add_do_method(this, "queue_save");503undo_redo->add_undo_method(this, "queue_save");504undo_redo->commit_action();505}506507void ProjectSettingsEditor::_action_renamed(const String &p_old_name, const String &p_new_name) {508const String old_property_name = "input/" + p_old_name;509const String new_property_name = "input/" + p_new_name;510511ERR_FAIL_COND_MSG(ProjectSettings::get_singleton()->has_setting(new_property_name),512"An action with this name already exists.");513514int order = ProjectSettings::get_singleton()->get_order(old_property_name);515Dictionary action = GLOBAL_GET(old_property_name);516517EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();518undo_redo->create_action(TTR("Rename Input Action"));519// Do: clear old, set new520undo_redo->add_do_method(ProjectSettings::get_singleton(), "clear", old_property_name);521undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", new_property_name, action);522undo_redo->add_do_method(ProjectSettings::get_singleton(), "set_order", new_property_name, order);523// Undo: clear new, set old524undo_redo->add_undo_method(ProjectSettings::get_singleton(), "clear", new_property_name);525undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", old_property_name, action);526undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_order", old_property_name, order);527528undo_redo->add_do_method(this, "_update_action_map_editor");529undo_redo->add_undo_method(this, "_update_action_map_editor");530undo_redo->add_do_method(this, "queue_save");531undo_redo->add_undo_method(this, "queue_save");532undo_redo->commit_action();533}534535void ProjectSettingsEditor::_action_reordered(const String &p_action_name, const String &p_relative_to, bool p_before) {536const String action_name = "input/" + p_action_name;537const String target_name = "input/" + p_relative_to;538539// It is much easier to rebuild the custom "input" properties rather than messing around with the "order" values of them.540Variant action_value = ps->get(action_name);541Variant target_value = ps->get(target_name);542543List<PropertyInfo> props;544HashMap<String, Variant> action_values;545ProjectSettings::get_singleton()->get_property_list(&props);546547EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();548undo_redo->create_action(TTR("Update Input Action Order"));549550for (const PropertyInfo &prop : props) {551// Skip builtins and non-inputs552// Order matters here, checking for "input/" filters out properties that aren't settings and produce errors in is_builtin_setting().553if (!prop.name.begins_with("input/") || ProjectSettings::get_singleton()->is_builtin_setting(prop.name)) {554continue;555}556557action_values.insert(prop.name, ps->get(prop.name));558559undo_redo->add_do_method(ProjectSettings::get_singleton(), "clear", prop.name);560undo_redo->add_undo_method(ProjectSettings::get_singleton(), "clear", prop.name);561}562563for (const KeyValue<String, Variant> &E : action_values) {564String name = E.key;565const Variant &value = E.value;566567if (name == target_name) {568if (p_before) {569// Insert before target570undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", action_name, action_value);571undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", target_name, target_value);572573undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", target_name, target_value);574undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", action_name, action_value);575} else {576// Insert after target577undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", target_name, target_value);578undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", action_name, action_value);579580undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", action_name, action_value);581undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", target_name, target_value);582}583584} else if (name != action_name) {585undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", name, value);586undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", name, value);587}588}589590undo_redo->add_do_method(this, "_update_action_map_editor");591undo_redo->add_undo_method(this, "_update_action_map_editor");592undo_redo->add_do_method(this, "queue_save");593undo_redo->add_undo_method(this, "queue_save");594undo_redo->commit_action();595}596597void ProjectSettingsEditor::_update_action_map_editor() {598Vector<ActionMapEditor::ActionInfo> actions;599600List<PropertyInfo> props;601ProjectSettings::get_singleton()->get_property_list(&props);602603const Ref<Texture2D> builtin_icon = get_editor_theme_icon(SNAME("PinPressed"));604for (const PropertyInfo &E : props) {605const String property_name = E.name;606607if (!property_name.begins_with("input/")) {608continue;609}610611// Strip the "input/" from the left.612String display_name = property_name.substr(String("input/").size() - 1);613Dictionary action = GLOBAL_GET(property_name);614615ActionMapEditor::ActionInfo action_info;616action_info.action = action;617action_info.editable = true;618action_info.name = display_name;619620const bool is_builtin_input = ProjectSettings::get_singleton()->get_input_presets().find(property_name) != nullptr;621if (is_builtin_input) {622action_info.editable = false;623action_info.icon = builtin_icon;624action_info.has_initial = true;625action_info.action_initial = ProjectSettings::get_singleton()->property_get_revert(property_name);626}627628actions.push_back(action_info);629}630631action_map_editor->update_action_list(actions);632}633634void ProjectSettingsEditor::_update_theme() {635add_button->set_button_icon(get_editor_theme_icon(SNAME("Add")));636del_button->set_button_icon(get_editor_theme_icon(SNAME("Remove")));637search_box->set_right_icon(get_editor_theme_icon(SNAME("Search")));638restart_close_button->set_button_icon(get_editor_theme_icon(SNAME("Close")));639restart_container->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree")));640restart_icon->set_texture(get_editor_theme_icon(SNAME("StatusWarning")));641restart_label->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));642}643644void ProjectSettingsEditor::_notification(int p_what) {645switch (p_what) {646case NOTIFICATION_VISIBILITY_CHANGED: {647if (is_visible()) {648HashMap<String, PropertyInfo> editor_settings_info;649650List<PropertyInfo> infos;651EditorSettings::get_singleton()->get_property_list(&infos);652653for (const PropertyInfo &pi : infos) {654editor_settings_info[pi.name] = pi;655}656ProjectSettings::get_singleton()->editor_settings_info = editor_settings_info;657} else {658EditorSettings::get_singleton()->set_project_metadata("dialog_bounds", "project_settings", Rect2(get_position(), get_size()));659if (settings_changed) {660timer->stop();661_save();662}663}664} break;665666case NOTIFICATION_ENTER_TREE: {667general_settings_inspector->edit(ps);668_update_action_map_editor();669_update_theme();670} break;671672case NOTIFICATION_THEME_CHANGED: {673_update_theme();674} break;675}676}677678void ProjectSettingsEditor::_bind_methods() {679ClassDB::bind_method(D_METHOD("queue_save"), &ProjectSettingsEditor::queue_save);680681ClassDB::bind_method(D_METHOD("_update_action_map_editor"), &ProjectSettingsEditor::_update_action_map_editor);682}683684ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {685singleton = this;686set_title(TTRC("Project Settings (project.godot)"));687set_clamp_to_embedder(true);688689ps = ProjectSettings::get_singleton();690data = p_data;691692tab_container = memnew(TabContainer);693tab_container->set_use_hidden_tabs_for_min_size(true);694tab_container->set_theme_type_variation("TabContainerOdd");695tab_container->connect("tab_changed", callable_mp(this, &ProjectSettingsEditor::_tabs_tab_changed));696add_child(tab_container);697698general_editor = memnew(VBoxContainer);699general_editor->set_name(TTRC("General"));700general_editor->set_alignment(BoxContainer::ALIGNMENT_BEGIN);701general_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);702tab_container->add_child(general_editor);703704HBoxContainer *search_bar = memnew(HBoxContainer);705general_editor->add_child(search_bar);706707search_box = memnew(LineEdit);708search_box->set_placeholder(TTRC("Filter Settings"));709search_box->set_accessibility_name(TTRC("Filter Settings"));710search_box->set_clear_button_enabled(true);711search_box->set_virtual_keyboard_show_on_focus(false);712search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL);713search_bar->add_child(search_box);714715advanced = memnew(CheckButton);716advanced->set_text(TTRC("Advanced Settings"));717search_bar->add_child(advanced);718719custom_properties = memnew(HBoxContainer);720general_editor->add_child(custom_properties);721722property_box = memnew(LineEdit);723property_box->set_placeholder(TTRC("Select a Setting or Type its Name"));724property_box->set_h_size_flags(Control::SIZE_EXPAND_FILL);725property_box->connect(SceneStringName(text_changed), callable_mp(this, &ProjectSettingsEditor::_property_box_changed));726custom_properties->add_child(property_box);727728feature_box = memnew(OptionButton);729feature_box->set_custom_minimum_size(Size2(120, 0) * EDSCALE);730feature_box->set_accessibility_name(TTRC("Feature"));731feature_box->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);732feature_box->connect(SceneStringName(item_selected), callable_mp(this, &ProjectSettingsEditor::_feature_selected));733custom_properties->add_child(feature_box);734735type_box = memnew(EditorVariantTypeOptionButton);736type_box->populate({ Variant::NIL, Variant::OBJECT });737type_box->set_custom_minimum_size(Size2(120, 0) * EDSCALE);738type_box->set_accessibility_name(TTRC("Type"));739custom_properties->add_child(type_box);740741add_button = memnew(Button);742add_button->set_text(TTRC("Add"));743add_button->set_disabled(true);744add_button->connect(SceneStringName(pressed), callable_mp(this, &ProjectSettingsEditor::_add_setting));745custom_properties->add_child(add_button);746747del_button = memnew(Button);748del_button->set_text(TTRC("Delete"));749del_button->set_disabled(true);750del_button->connect(SceneStringName(pressed), callable_mp(this, &ProjectSettingsEditor::_delete_setting));751custom_properties->add_child(del_button);752753general_settings_inspector = memnew(SectionedInspector);754general_settings_inspector->set_v_size_flags(Control::SIZE_EXPAND_FILL);755general_settings_inspector->register_search_box(search_box);756general_settings_inspector->register_advanced_toggle(advanced);757general_settings_inspector->connect("category_changed", callable_mp(this, &ProjectSettingsEditor::_on_category_changed));758general_settings_inspector->get_inspector()->set_use_filter(true);759general_settings_inspector->get_inspector()->set_mark_unsaved(false);760general_settings_inspector->get_inspector()->connect("property_selected", callable_mp(this, &ProjectSettingsEditor::_setting_selected));761general_settings_inspector->get_inspector()->connect("property_edited", callable_mp(this, &ProjectSettingsEditor::_setting_edited));762general_settings_inspector->get_inspector()->connect("property_deleted", callable_mp(this, &ProjectSettingsEditor::_on_editor_override_deleted));763general_settings_inspector->get_inspector()->connect("restart_requested", callable_mp(this, &ProjectSettingsEditor::_editor_restart_request));764general_editor->add_child(general_settings_inspector);765766if (EDITOR_GET("interface/touchscreen/enable_touch_optimizations")) {767general_settings_inspector->set_touch_dragger_enabled(true);768}769770restart_container = memnew(PanelContainer);771general_editor->add_child(restart_container);772773HBoxContainer *restart_hb = memnew(HBoxContainer);774restart_container->hide();775restart_container->add_child(restart_hb);776777restart_icon = memnew(TextureRect);778restart_icon->set_v_size_flags(Control::SIZE_SHRINK_CENTER);779restart_hb->add_child(restart_icon);780781restart_label = memnew(Label);782restart_label->set_focus_mode(Control::FOCUS_ACCESSIBILITY);783restart_label->set_text(TTRC("Changed settings will be applied to the editor after restarting."));784restart_hb->add_child(restart_label);785restart_hb->add_spacer();786787Button *restart_button = memnew(Button);788restart_button->connect(SceneStringName(pressed), callable_mp(this, &ProjectSettingsEditor::_editor_restart));789restart_hb->add_child(restart_button);790restart_button->set_text(TTRC("Save & Restart"));791792restart_close_button = memnew(Button);793restart_close_button->set_flat(true);794restart_close_button->connect(SceneStringName(pressed), callable_mp(this, &ProjectSettingsEditor::_editor_restart_close));795restart_close_button->set_accessibility_name(TTRC("Close"));796restart_hb->add_child(restart_close_button);797798action_map_editor = memnew(ActionMapEditor);799action_map_editor->set_name(TTRC("Input Map"));800action_map_editor->connect("action_added", callable_mp(this, &ProjectSettingsEditor::_action_added));801action_map_editor->connect("action_edited", callable_mp(this, &ProjectSettingsEditor::_action_edited));802action_map_editor->connect("action_removed", callable_mp(this, &ProjectSettingsEditor::_action_removed));803action_map_editor->connect("action_renamed", callable_mp(this, &ProjectSettingsEditor::_action_renamed));804action_map_editor->connect("action_reordered", callable_mp(this, &ProjectSettingsEditor::_action_reordered));805tab_container->add_child(action_map_editor);806807localization_editor = memnew(LocalizationEditor);808localization_editor->set_name(TTRC("Localization"));809localization_editor->connect("localization_changed", callable_mp(this, &ProjectSettingsEditor::queue_save));810tab_container->add_child(localization_editor);811812TabContainer *globals_container = memnew(TabContainer);813globals_container->set_name(TTRC("Globals"));814tab_container->add_child(globals_container);815816autoload_settings = memnew(EditorAutoloadSettings);817autoload_settings->set_name(TTRC("Autoload"));818autoload_settings->connect("autoload_changed", callable_mp(this, &ProjectSettingsEditor::queue_save));819globals_container->add_child(autoload_settings);820821shaders_global_shader_uniforms_editor = memnew(ShaderGlobalsEditor);822shaders_global_shader_uniforms_editor->set_name(TTRC("Shader Globals"));823shaders_global_shader_uniforms_editor->connect("globals_changed", callable_mp(this, &ProjectSettingsEditor::queue_save));824globals_container->add_child(shaders_global_shader_uniforms_editor);825826group_settings = memnew(GroupSettingsEditor);827group_settings->set_name(TTRC("Groups"));828group_settings->connect("group_changed", callable_mp(this, &ProjectSettingsEditor::queue_save));829globals_container->add_child(group_settings);830831plugin_settings = memnew(EditorPluginSettings);832plugin_settings->set_name(TTRC("Plugins"));833tab_container->add_child(plugin_settings);834835timer = memnew(Timer);836timer->set_wait_time(1.5);837timer->connect("timeout", callable_mp(this, &ProjectSettingsEditor::_save));838timer->set_one_shot(true);839add_child(timer);840841set_ok_button_text(TTRC("Close"));842set_hide_on_ok(true);843844bool use_advanced = EDITOR_DEF("_project_settings_advanced_mode", false);845if (use_advanced) {846advanced->set_pressed(true);847}848advanced->connect(SceneStringName(toggled), callable_mp(this, &ProjectSettingsEditor::_advanced_toggled));849850_update_advanced(use_advanced);851852import_defaults_editor = memnew(ImportDefaultsEditor);853import_defaults_editor->set_name(TTRC("Import Defaults"));854tab_container->add_child(import_defaults_editor);855856MovieWriter::set_extensions_hint(); // ensure extensions are properly displayed.857}858859860