diff --git a/core/project_settings.cpp b/core/project_settings.cpp index 0a673fb638c..eb88143db33 100644 --- a/core/project_settings.cpp +++ b/core/project_settings.cpp @@ -1019,6 +1019,9 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF("editor/search_in_file_extensions", extensions); custom_prop_info["editor/search_in_file_extensions"] = PropertyInfo(Variant::POOL_STRING_ARRAY, "editor/search_in_file_extensions"); + GLOBAL_DEF("editor/script_templates_search_path", "res://script_templates"); + custom_prop_info["editor/script_templates_search_path"] = PropertyInfo(Variant::STRING, "editor/script_templates_search_path", PROPERTY_HINT_DIR); + action = Dictionary(); action["deadzone"] = Variant(0.5f); events = Array(); diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 61668d55be8..db353c0dfce 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -1209,6 +1209,11 @@ String EditorSettings::get_script_templates_dir() const { return get_settings_dir().plus_file("script_templates"); } +String EditorSettings::get_project_script_templates_dir() const { + + return ProjectSettings::get_singleton()->get("editor/script_templates_search_path"); +} + // Cache directory String EditorSettings::get_cache_dir() const { @@ -1429,10 +1434,14 @@ bool EditorSettings::is_default_text_editor_theme() { return _is_default_text_editor_theme(p_file.get_file().to_lower()); } -Vector EditorSettings::get_script_templates(const String &p_extension) { +Vector EditorSettings::get_script_templates(const String &p_extension, const String &p_custom_path) { Vector templates; - DirAccess *d = DirAccess::open(get_script_templates_dir()); + String template_dir = get_script_templates_dir(); + if (!p_custom_path.empty()) { + template_dir = p_custom_path; + } + DirAccess *d = DirAccess::open(template_dir); if (d) { d->list_dir_begin(); String file = d->get_next(); diff --git a/editor/editor_settings.h b/editor/editor_settings.h index 890850629e1..0738185e954 100644 --- a/editor/editor_settings.h +++ b/editor/editor_settings.h @@ -166,6 +166,7 @@ public: String get_project_settings_dir() const; String get_text_editor_themes_dir() const; String get_script_templates_dir() const; + String get_project_script_templates_dir() const; String get_cache_dir() const; String get_feature_profiles_dir() const; @@ -187,7 +188,7 @@ public: bool save_text_editor_theme_as(String p_file); bool is_default_text_editor_theme(); - Vector get_script_templates(const String &p_extension); + Vector get_script_templates(const String &p_extension, const String &p_custom_path = String()); String get_editor_layouts_config() const; void add_shortcut(const String &p_name, Ref &p_shortcut); diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp index 7d0f40fe914..ffb3f5feab6 100644 --- a/editor/script_create_dialog.cpp +++ b/editor/script_create_dialog.cpp @@ -34,6 +34,7 @@ #include "core/os/file_access.h" #include "core/project_settings.h" #include "core/script_language.h" +#include "core/string_builder.h" #include "editor/create_dialog.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" @@ -238,16 +239,22 @@ void ScriptCreateDialog::_parent_name_changed(const String &p_parent) { void ScriptCreateDialog::_template_changed(int p_template) { - String selected_template = p_template == 0 ? "" : template_menu->get_item_text(template_menu->get_selected()); + String selected_template = p_template == 0 ? "" : template_menu->get_item_text(p_template); EditorSettings::get_singleton()->set_project_metadata("script_setup", "last_selected_template", selected_template); if (p_template == 0) { //default script_template = ""; return; } - String ext = ScriptServer::get_language(language_menu->get_selected())->get_extension(); - String name = template_list[p_template - 1] + "." + ext; - script_template = EditorSettings::get_singleton()->get_script_templates_dir().plus_file(name); + int selected_id = template_menu->get_selected_id(); + + for (int i = 0; i < template_list.size(); i++) { + const ScriptTemplateInfo &sinfo = template_list[i]; + if (sinfo.id == selected_id) { + script_template = sinfo.dir.plus_file(sinfo.name + "." + sinfo.extension); + break; + } + } } void ScriptCreateDialog::ok_pressed() { @@ -368,23 +375,77 @@ void ScriptCreateDialog::_lang_changed(int l) { bool use_templates = language->is_using_templates(); template_menu->set_disabled(!use_templates); template_menu->clear(); - if (use_templates) { - template_list = EditorSettings::get_singleton()->get_script_templates(language->get_extension()); + if (use_templates) { + _update_script_templates(language->get_extension()); String last_lang = EditorSettings::get_singleton()->get_project_metadata("script_setup", "last_selected_language", ""); String last_template = EditorSettings::get_singleton()->get_project_metadata("script_setup", "last_selected_template", ""); template_menu->add_item(TTR("Default")); + + ScriptTemplateInfo *templates = template_list.ptrw(); + + Vector origin_names; + origin_names.push_back(TTR("Project")); + origin_names.push_back(TTR("Editor")); + int cur_origin = -1; + + // Populate script template items previously sorted and now grouped by origin for (int i = 0; i < template_list.size(); i++) { - String s = template_list[i].capitalize(); - template_menu->add_item(s); - if (language_menu->get_item_text(language_menu->get_selected()) == last_lang && last_template == s) { - template_menu->select(i + 1); + + if (int(templates[i].origin) != cur_origin) { + template_menu->add_separator(); + + String origin_name = origin_names[templates[i].origin]; + + int last_index = template_menu->get_item_count() - 1; + template_menu->set_item_text(last_index, origin_name); + + cur_origin = templates[i].origin; + } + String item_name = templates[i].name.capitalize(); + template_menu->add_item(item_name); + + int new_id = template_menu->get_item_count() - 1; + templates[i].id = new_id; + } + // Disable overridden + for (Map >::Element *E = template_overrides.front(); E; E = E->next()) { + const Vector &overrides = E->get(); + + if (overrides.size() == 1) { + continue; // doesn't override anything + } + const ScriptTemplateInfo &extended = template_list[overrides[0]]; + + StringBuilder override_info; + override_info += TTR("Overrides"); + override_info += ": "; + + for (int i = 1; i < overrides.size(); i++) { + const ScriptTemplateInfo &overridden = template_list[overrides[i]]; + + int disable_index = template_menu->get_item_index(overridden.id); + template_menu->set_item_disabled(disable_index, true); + + override_info += origin_names[overridden.origin]; + if (i < overrides.size() - 1) { + override_info += ", "; + } + } + template_menu->set_item_icon(extended.id, get_icon("Override", "EditorIcons")); + template_menu->get_popup()->set_item_tooltip(extended.id, override_info.as_string()); + } + // Reselect last selected template + for (int i = 0; i < template_menu->get_item_count(); i++) { + const String &ti = template_menu->get_item_text(i); + if (language_menu->get_item_text(language_menu->get_selected()) == last_lang && last_template == ti) { + template_menu->select(i); + break; } } } else { - template_menu->add_item(TTR("N/A")); script_template = ""; } @@ -396,6 +457,41 @@ void ScriptCreateDialog::_lang_changed(int l) { _update_dialog(); } +void ScriptCreateDialog::_update_script_templates(const String &p_extension) { + + template_list.clear(); + template_overrides.clear(); + + Vector dirs; + + // Ordered from local to global for correct override mechanism + dirs.push_back(EditorSettings::get_singleton()->get_project_script_templates_dir()); + dirs.push_back(EditorSettings::get_singleton()->get_script_templates_dir()); + + for (int i = 0; i < dirs.size(); i++) { + + Vector list = EditorSettings::get_singleton()->get_script_templates(p_extension, dirs[i]); + + for (int j = 0; j < list.size(); j++) { + ScriptTemplateInfo sinfo; + sinfo.origin = ScriptOrigin(i); + sinfo.dir = dirs[i]; + sinfo.name = list[j]; + sinfo.extension = p_extension; + template_list.push_back(sinfo); + + if (!template_overrides.has(sinfo.name)) { + Vector overrides; + overrides.push_back(template_list.size() - 1); // first one + template_overrides.insert(sinfo.name, overrides); + } else { + Vector &overrides = template_overrides[sinfo.name]; + overrides.push_back(template_list.size() - 1); + } + } + } +} + void ScriptCreateDialog::_built_in_pressed() { if (internal->is_pressed()) { diff --git a/editor/script_create_dialog.h b/editor/script_create_dialog.h index 202846fd3c6..31cf2478cf7 100644 --- a/editor/script_create_dialog.h +++ b/editor/script_create_dialog.h @@ -78,8 +78,25 @@ class ScriptCreateDialog : public ConfirmationDialog { int current_language; int default_language; bool re_check_path; + + enum ScriptOrigin { + SCRIPT_ORIGIN_PROJECT, + SCRIPT_ORIGIN_EDITOR, + }; + struct ScriptTemplateInfo { + int id; + ScriptOrigin origin; + String dir; + String name; + String extension; + }; + String script_template; - Vector template_list; + Vector template_list; + Map > template_overrides; // name : indices + + void _update_script_templates(const String &p_extension); + String base_type; void _path_hbox_sorted();