diff options
Diffstat (limited to 'editor/script_create_dialog.cpp')
-rw-r--r-- | editor/script_create_dialog.cpp | 579 |
1 files changed, 363 insertions, 216 deletions
diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp index 0e96a1d247..2098fa2c85 100644 --- a/editor/script_create_dialog.cpp +++ b/editor/script_create_dialog.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,7 +33,6 @@ #include "core/config/project_settings.h" #include "core/io/file_access.h" #include "core/io/resource_saver.h" -#include "core/object/script_language.h" #include "core/string/string_builder.h" #include "editor/create_dialog.h" #include "editor/editor_node.h" @@ -45,17 +44,16 @@ void ScriptCreateDialog::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { for (int i = 0; i < ScriptServer::get_language_count(); i++) { - String lang = ScriptServer::get_language(i)->get_type(); - Ref<Texture2D> lang_icon = get_theme_icon(lang, SNAME("EditorIcons")); - if (lang_icon.is_valid()) { - language_menu->set_item_icon(i, lang_icon); + Ref<Texture2D> language_icon = get_theme_icon(ScriptServer::get_language(i)->get_type(), SNAME("EditorIcons")); + if (language_icon.is_valid()) { + language_menu->set_item_icon(i, language_icon); } } - String last_lang = EditorSettings::get_singleton()->get_project_metadata("script_setup", "last_selected_language", ""); - if (!last_lang.is_empty()) { + String last_language = EditorSettings::get_singleton()->get_project_metadata("script_setup", "last_selected_language", ""); + if (!last_language.is_empty()) { for (int i = 0; i < language_menu->get_item_count(); i++) { - if (language_menu->get_item_text(i) == last_lang) { + if (language_menu->get_item_text(i) == last_language) { language_menu->select(i); current_language = i; break; @@ -64,6 +62,10 @@ void ScriptCreateDialog::_notification(int p_what) { } else { language_menu->select(default_language); } + if (EditorSettings::get_singleton()->has_meta("script_setup/use_script_templates")) { + is_using_templates = bool(EditorSettings::get_singleton()->get_meta("script_setup/use_script_templates")); + use_templates->set_pressed(is_using_templates); + } path_button->set_icon(get_theme_icon(SNAME("Folder"), SNAME("EditorIcons"))); parent_browse_button->set_icon(get_theme_icon(SNAME("Folder"), SNAME("EditorIcons"))); @@ -114,7 +116,7 @@ void ScriptCreateDialog::config(const String &p_base_name, const String &p_base_ built_in_enabled = p_built_in_enabled; load_enabled = p_load_enabled; - _lang_changed(current_language); + _language_changed(current_language); _class_name_changed(""); _path_changed(file_path->get_text()); } @@ -145,8 +147,9 @@ bool ScriptCreateDialog::_validate_class(const String &p_string) { for (int i = 0; i < p_string.length(); i++) { if (i == 0) { + // Cannot start with a number. if (p_string[0] >= '0' && p_string[0] <= '9') { - return false; // no start with number plz + return false; } } @@ -170,6 +173,10 @@ String ScriptCreateDialog::_validate_path(const String &p_path, bool p_file_must return TTR("Filename is empty."); } + if (!p.get_file().get_basename().is_valid_filename()) { + return TTR("Filename is invalid."); + } + p = ProjectSettings::get_singleton()->localize_path(p); if (!p.begins_with("res://")) { return TTR("Path is not local."); @@ -178,11 +185,11 @@ String ScriptCreateDialog::_validate_path(const String &p_path, bool p_file_must DirAccess *d = DirAccess::create(DirAccess::ACCESS_RESOURCES); if (d->change_dir(p.get_base_dir()) != OK) { memdelete(d); - return TTR("Invalid base path."); + return TTR("Base path is invalid."); } memdelete(d); - /* Does file already exist */ + // Check if file exists. DirAccess *f = DirAccess::create(DirAccess::ACCESS_RESOURCES); if (f->dir_exists(p)) { memdelete(f); @@ -193,11 +200,11 @@ String ScriptCreateDialog::_validate_path(const String &p_path, bool p_file_must } memdelete(f); - /* Check file extension */ + // Check file extension. String extension = p.get_extension(); List<String> extensions; - // get all possible extensions for script + // Get all possible extensions for script. for (int l = 0; l < language_menu->get_item_count(); l++) { ScriptServer::get_language(l)->get_recognized_extensions(&extensions); } @@ -207,8 +214,6 @@ String ScriptCreateDialog::_validate_path(const String &p_path, bool p_file_must int index = 0; for (const String &E : extensions) { if (E.nocasecmp_to(extension) == 0) { - //FIXME (?) - changing language this way doesn't update controls, needs rework - //language_menu->select(index); // change Language option by extension found = true; if (E == ScriptServer::get_language(language_menu->get_selected())->get_extension()) { match = true; @@ -222,16 +227,16 @@ String ScriptCreateDialog::_validate_path(const String &p_path, bool p_file_must return TTR("Invalid extension."); } if (!match) { - return TTR("Wrong extension chosen."); + return TTR("Extension doesn't match chosen language."); } - /* Let ScriptLanguage do custom validation */ + // Let ScriptLanguage do custom validation. String path_error = ScriptServer::get_language(language_menu->get_selected())->validate_path(p); if (!path_error.is_empty()) { return path_error; } - /* All checks passed */ + // All checks passed. return ""; } @@ -244,40 +249,49 @@ String ScriptCreateDialog::_get_class_name() const { } void ScriptCreateDialog::_class_name_changed(const String &p_name) { - if (_validate_class(class_name->get_text())) { - is_class_name_valid = true; - } else { - is_class_name_valid = false; - } + is_class_name_valid = _validate_class(class_name->get_text()); _update_dialog(); } void ScriptCreateDialog::_parent_name_changed(const String &p_parent) { - if (_validate_parent(parent_name->get_text())) { - is_parent_name_valid = true; - } else { - is_parent_name_valid = false; - } + is_parent_name_valid = _validate_parent(parent_name->get_text()); _update_dialog(); } void ScriptCreateDialog::_template_changed(int p_template) { - 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; - } - 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; + const ScriptLanguage::ScriptTemplate &sinfo = _get_current_template(); + // Update last used dictionaries + if (is_using_templates && !parent_name->get_text().begins_with("\"res:")) { + if (sinfo.origin == ScriptLanguage::TemplateLocation::TEMPLATE_PROJECT) { + // Save the last used template for this node into the project dictionary. + Dictionary dic_templates_project = EditorSettings::get_singleton()->get_project_metadata("script_setup", "templates_dictionary", Dictionary()); + dic_templates_project[parent_name->get_text()] = sinfo.get_hash(); + EditorSettings::get_singleton()->set_project_metadata("script_setup", "templates_dictionary", dic_templates_project); + } else { + // Save template into to editor dictionary (not a project template). + Dictionary dic_templates; + if (EditorSettings::get_singleton()->has_meta("script_setup/templates_dictionary")) { + dic_templates = (Dictionary)EditorSettings::get_singleton()->get_meta("script_setup/templates_dictionary"); + } + dic_templates[parent_name->get_text()] = sinfo.get_hash(); + EditorSettings::get_singleton()->set_meta("script_setup/templates_dictionary", dic_templates); + // Remove template from project dictionary as we last used an editor level template. + Dictionary dic_templates_project = EditorSettings::get_singleton()->get_project_metadata("script_setup", "templates_dictionary", Dictionary()); + if (dic_templates_project.has(parent_name->get_text())) { + dic_templates_project.erase(parent_name->get_text()); + EditorSettings::get_singleton()->set_project_metadata("script_setup", "templates_dictionary", dic_templates_project); + } } } + // Update template label information. + String template_info = String::utf8("• "); + template_info += TTR("Template:"); + template_info += " " + sinfo.name; + if (!sinfo.description.is_empty()) { + template_info += " - " + sinfo.description; + } + template_info_label->set_text(template_info); + template_info_label->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor"))); } void ScriptCreateDialog::ok_pressed() { @@ -287,6 +301,7 @@ void ScriptCreateDialog::ok_pressed() { _load_exist(); } + EditorSettings::get_singleton()->save(); is_new_script_created = true; _update_dialog(); } @@ -295,18 +310,10 @@ void ScriptCreateDialog::_create_new() { String cname_param = _get_class_name(); Ref<Script> scr; - if (!script_template.is_empty()) { - scr = ResourceLoader::load(script_template); - if (scr.is_null()) { - alert->set_text(vformat(TTR("Error loading template '%s'"), script_template)); - alert->popup_centered(); - return; - } - scr = scr->duplicate(); - ScriptServer::get_language(language_menu->get_selected())->make_template(cname_param, parent_name->get_text(), scr); - } else { - scr = ScriptServer::get_language(language_menu->get_selected())->get_template(cname_param, parent_name->get_text()); - } + + const ScriptLanguage::ScriptTemplate sinfo = _get_current_template(); + + scr = ScriptServer::get_language(language_menu->get_selected())->make_template(sinfo.content, cname_param, parent_name->get_text()); if (has_named_classes) { String cname = class_name->get_text(); @@ -345,8 +352,20 @@ void ScriptCreateDialog::_load_exist() { hide(); } -void ScriptCreateDialog::_lang_changed(int l) { - ScriptLanguage *language = ScriptServer::get_language(l); +Vector<String> ScriptCreateDialog::get_hierarchy(String p_object) const { + Vector<String> hierarchy; + hierarchy.append(p_object); + + String parent_class = ClassDB::get_parent_class(p_object); + while (parent_class.is_valid_identifier()) { + hierarchy.append(parent_class); + parent_class = ClassDB::get_parent_class(parent_class); + } + return hierarchy; +} + +void ScriptCreateDialog::_language_changed(int l) { + language = ScriptServer::get_language(l); has_named_classes = language->has_named_classes(); can_inherit_from_file = language->can_inherit_from_file(); @@ -364,13 +383,13 @@ void ScriptCreateDialog::_lang_changed(int l) { } if (extension.length() == 0) { - // add extension if none + // Add extension if none. path += selected_ext; _path_changed(path); } else { - // change extension by selected language + // Change extension by selected language. List<String> extensions; - // get all possible extensions for script + // Get all possible extensions for script. for (int m = 0; m < language_menu->get_item_count(); m++) { ScriptServer::get_language(m)->get_recognized_extensions(&extensions); } @@ -389,123 +408,12 @@ void ScriptCreateDialog::_lang_changed(int l) { } file_path->set_text(path); - bool use_templates = language->is_using_templates(); - template_menu->set_disabled(!use_templates); - template_menu->clear(); - - 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<String> 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++) { - 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 (const KeyValue<String, Vector<int>> &E : template_overrides) { - const Vector<int> &overrides = E.value; - - 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_theme_icon(SNAME("Override"), SNAME("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 = ""; - } - - _template_changed(template_menu->get_selected()); EditorSettings::get_singleton()->set_project_metadata("script_setup", "last_selected_language", language_menu->get_item_text(language_menu->get_selected())); _parent_name_changed(parent_name->get_text()); _update_dialog(); } -void ScriptCreateDialog::_update_script_templates(const String &p_extension) { - template_list.clear(); - template_overrides.clear(); - - Vector<String> 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<String> 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<int> overrides; - overrides.push_back(template_list.size() - 1); // first one - template_overrides.insert(sinfo.name, overrides); - } else { - Vector<int> &overrides = template_overrides[sinfo.name]; - overrides.push_back(template_list.size() - 1); - } - } - } -} - void ScriptCreateDialog::_built_in_pressed() { if (internal->is_pressed()) { is_built_in = true; @@ -517,6 +425,12 @@ void ScriptCreateDialog::_built_in_pressed() { _update_dialog(); } +void ScriptCreateDialog::_use_template_pressed() { + is_using_templates = use_templates->is_pressed(); + EditorSettings::get_singleton()->set_meta("script_setup/use_script_templates", is_using_templates); + _update_dialog(); +} + void ScriptCreateDialog::_browse_path(bool browse_parent, bool p_save) { is_browsing_parent = browse_parent; @@ -545,16 +459,16 @@ void ScriptCreateDialog::_browse_path(bool browse_parent, bool p_save) { } void ScriptCreateDialog::_file_selected(const String &p_file) { - String p = ProjectSettings::get_singleton()->localize_path(p_file); + String path = ProjectSettings::get_singleton()->localize_path(p_file); if (is_browsing_parent) { - parent_name->set_text("\"" + p + "\""); + parent_name->set_text("\"" + path + "\""); _parent_name_changed(parent_name->get_text()); } else { - file_path->set_text(p); - _path_changed(p); + file_path->set_text(path); + _path_changed(path); - String filename = p.get_file().get_basename(); - int select_start = p.rfind(filename); + String filename = path.get_file().get_basename(); + int select_start = path.rfind(filename); file_path->select(select_start, select_start + filename.length()); file_path->set_caret_column(select_start + filename.length()); file_path->grab_focus(); @@ -588,7 +502,7 @@ void ScriptCreateDialog::_path_changed(const String &p_path) { return; } - /* Does file already exist */ + // Check if file exists. DirAccess *f = DirAccess::create(DirAccess::ACCESS_RESOURCES); String p = ProjectSettings::get_singleton()->localize_path(p_path.strip_edges()); if (f->file_exists(p)) { @@ -623,9 +537,98 @@ void ScriptCreateDialog::_msg_path_valid(bool valid, const String &p_msg) { } } -void ScriptCreateDialog::_update_dialog() { - /* "Add Script Dialog" GUI logic and script checks. */ +void ScriptCreateDialog::_update_template_menu() { + bool is_language_using_templates = language->is_using_templates(); + template_menu->set_disabled(false); + template_menu->clear(); + template_list.clear(); + if (is_language_using_templates) { + // Get the latest templates used for each type of node from project settings then global settings. + Dictionary last_local_templates = EditorSettings::get_singleton()->get_project_metadata("script_setup", "templates_dictionary", Dictionary()); + Dictionary last_global_templates; + if (EditorSettings::get_singleton()->has_meta("script_setup/templates_dictionary")) { + last_global_templates = (Dictionary)EditorSettings::get_singleton()->get_meta("script_setup/templates_dictionary"); + } + String inherits_base_type = parent_name->get_text(); + + // If it inherits from a script, select Object instead. + if (inherits_base_type[0] == '"') { + inherits_base_type = "Object"; + } + + // Get all ancestor node for selected base node. + // There templates will also fit the base node. + Vector<String> hierarchy = get_hierarchy(inherits_base_type); + int last_used_template = -1; + int preselected_template = -1; + int previous_ancestor_level = -1; + + // Templates can be stored in tree different locations. + Vector<ScriptLanguage::TemplateLocation> template_locations; + template_locations.append(ScriptLanguage::TEMPLATE_PROJECT); + template_locations.append(ScriptLanguage::TEMPLATE_EDITOR); + template_locations.append(ScriptLanguage::TEMPLATE_BUILT_IN); + + for (const ScriptLanguage::TemplateLocation &template_location : template_locations) { + String display_name = _get_script_origin_label(template_location); + bool separator = false; + int ancestor_level = 0; + for (const String ¤t_node : hierarchy) { + Vector<ScriptLanguage::ScriptTemplate> templates_found; + if (template_location == ScriptLanguage::TEMPLATE_BUILT_IN) { + templates_found = language->get_built_in_templates(current_node); + } else { + String template_directory; + if (template_location == ScriptLanguage::TEMPLATE_PROJECT) { + template_directory = EditorSettings::get_singleton()->get_project_script_templates_dir(); + } else { + template_directory = EditorSettings::get_singleton()->get_script_templates_dir(); + } + templates_found = _get_user_templates(language, current_node, template_directory, template_location); + } + if (!templates_found.is_empty()) { + if (!separator) { + template_menu->add_separator(); + template_menu->set_item_text(template_menu->get_item_count() - 1, display_name); + separator = true; + } + for (ScriptLanguage::ScriptTemplate &t : templates_found) { + template_menu->add_item(t.inherit + ": " + t.name); + int id = template_menu->get_item_count() - 1; + // Check if this template should be preselected if node isn't in the last used dictionary. + if (ancestor_level < previous_ancestor_level || previous_ancestor_level == -1) { + previous_ancestor_level = ancestor_level; + preselected_template = id; + } + // Check for last used template for this node in project settings then in global settings. + if (last_local_templates.has(parent_name->get_text()) && t.get_hash() == String(last_local_templates[parent_name->get_text()])) { + last_used_template = id; + } else if (last_used_template == -1 && last_global_templates.has(parent_name->get_text()) && t.get_hash() == String(last_global_templates[parent_name->get_text()])) { + last_used_template = id; + } + t.id = id; + template_list.push_back(t); + String icon = has_theme_icon(t.inherit, SNAME("EditorIcons")) ? t.inherit : "Object"; + template_menu->set_item_icon(id, get_theme_icon(icon, SNAME("EditorIcons"))); + } + } + ancestor_level++; + } + } + + if (last_used_template != -1) { + template_menu->select(last_used_template); + } else if (preselected_template != -1) { + template_menu->select(preselected_template); + } + } + _template_changed(template_menu->get_selected()); +} + +void ScriptCreateDialog::_update_dialog() { + // "Add Script Dialog" GUI logic and script checks. + _update_template_menu(); bool script_ok = true; // Is script path/name valid (order from top to bottom)? @@ -697,41 +700,51 @@ void ScriptCreateDialog::_update_dialog() { // This warning isn't relevant if the script is built-in. script_name_warning_label->set_visible(!is_built_in && _get_class_name() == parent_name->get_text()); - if (is_built_in) { - get_ok_button()->set_text(TTR("Create")); - parent_name->set_editable(true); - parent_search_button->set_disabled(false); - parent_browse_button->set_disabled(!can_inherit_from_file); - _msg_path_valid(true, TTR("Built-in script (into scene file).")); - } else if (is_new_script_created) { - // New script created. - - get_ok_button()->set_text(TTR("Create")); - parent_name->set_editable(true); - parent_search_button->set_disabled(false); - parent_browse_button->set_disabled(!can_inherit_from_file); - if (is_path_valid) { + bool is_new_file = is_built_in || is_new_script_created; + + parent_name->set_editable(is_new_file); + parent_search_button->set_disabled(!is_new_file); + parent_browse_button->set_disabled(!is_new_file || !can_inherit_from_file); + template_inactive_message = ""; + String button_text = is_new_file ? TTR("Create") : TTR("Load"); + get_ok_button()->set_text(button_text); + + if (is_new_file) { + if (is_built_in) { + _msg_path_valid(true, TTR("Built-in script (into scene file).")); + } + if (is_new_script_created && is_path_valid) { _msg_path_valid(true, TTR("Will create a new script file.")); } - } else if (load_enabled) { - // Script loaded. - - get_ok_button()->set_text(TTR("Load")); - parent_name->set_editable(false); - parent_search_button->set_disabled(true); - parent_browse_button->set_disabled(true); - if (is_path_valid) { - _msg_path_valid(true, TTR("Will load an existing script file.")); + } else { + if (load_enabled) { + template_inactive_message = TTR("Using existing script file."); + if (is_path_valid) { + _msg_path_valid(true, TTR("Will load an existing script file.")); + } + } else { + template_inactive_message = TTR("Using existing script file."); + _msg_path_valid(false, TTR("Script file already exists.")); + script_ok = false; + } + } + + // Show templates list if needed. + if (is_using_templates) { + // Check if at least one suitable template has been found. + if (template_menu->get_item_count() == 0 && template_inactive_message.is_empty()) { + template_inactive_message = TTR("No suitable template."); } } else { - get_ok_button()->set_text(TTR("Create")); - parent_name->set_editable(true); - parent_search_button->set_disabled(false); - parent_browse_button->set_disabled(!can_inherit_from_file); - _msg_path_valid(false, TTR("Script file already exists.")); + template_inactive_message = TTR("Empty"); + } - script_ok = false; + if (!template_inactive_message.is_empty()) { + template_menu->set_disabled(true); + template_menu->clear(); + template_menu->add_item(template_inactive_message); } + template_info_label->set_visible(!template_menu->is_disabled()); get_ok_button()->set_disabled(!script_ok); @@ -745,6 +758,122 @@ void ScriptCreateDialog::_update_dialog() { } } +ScriptLanguage::ScriptTemplate ScriptCreateDialog::_get_current_template() const { + int selected_id = template_menu->get_selected_id(); + for (const ScriptLanguage::ScriptTemplate &t : template_list) { + if (is_using_templates) { + if (t.id == selected_id) { + return t; + } + } else { + // Using empty built-in template if templates are disabled. + if (t.origin == ScriptLanguage::TemplateLocation::TEMPLATE_BUILT_IN && t.name == "Empty") { + return t; + } + } + } + return ScriptLanguage::ScriptTemplate(); +} + +Vector<ScriptLanguage::ScriptTemplate> ScriptCreateDialog::_get_user_templates(const ScriptLanguage *language, const StringName &p_object, const String &p_dir, const ScriptLanguage::TemplateLocation &p_origin) const { + Vector<ScriptLanguage::ScriptTemplate> user_templates; + String extension = language->get_extension(); + + String dir_path = p_dir.plus_file(p_object); + + DirAccess *d = DirAccess::open(dir_path); + if (d) { + d->list_dir_begin(); + String file = d->get_next(); + while (file != String()) { + if (file.get_extension() == extension) { + user_templates.append(_parse_template(language, dir_path, file, p_origin, p_object)); + } + file = d->get_next(); + } + d->list_dir_end(); + memdelete(d); + } + return user_templates; +} + +ScriptLanguage::ScriptTemplate ScriptCreateDialog::_parse_template(const ScriptLanguage *language, const String &p_path, const String &p_filename, const ScriptLanguage::TemplateLocation &p_origin, const String &p_inherits) const { + ScriptLanguage::ScriptTemplate script_template = ScriptLanguage::ScriptTemplate(); + script_template.origin = p_origin; + script_template.inherit = p_inherits; + String space_indent = " "; + // Get meta delimiter + String meta_delimiter = String(); + List<String> comment_delimiters; + language->get_comment_delimiters(&comment_delimiters); + for (const String &script_delimiter : comment_delimiters) { + if (script_delimiter.find(" ") == -1) { + meta_delimiter = script_delimiter; + break; + } + } + String meta_prefix = meta_delimiter + " meta-"; + + // Parse file for meta-information and script content + Error err; + FileAccess *file = FileAccess::open(p_path.plus_file(p_filename), FileAccess::READ, &err); + if (!err) { + while (!file->eof_reached()) { + String line = file->get_line(); + if (line.begins_with(meta_prefix)) { + // Store meta information + line = line.substr(meta_prefix.length(), -1); + if (line.begins_with("name")) { + script_template.name = line.substr(5, -1).strip_edges(); + } + if (line.begins_with("description")) { + script_template.description = line.substr(12, -1).strip_edges(); + } + if (line.begins_with("space-indent")) { + String indent_value = line.substr(17, -1).strip_edges(); + if (indent_value.is_valid_int()) { + space_indent = ""; + for (int i = 0; i < indent_value.to_int(); i++) { + space_indent += " "; + } + } else { + WARN_PRINT(vformat("Template meta-use_space_indent need to be a valid integer value. Found %s.", indent_value)); + } + } + } else { + // Store script + if (space_indent != "") { + line = line.replace(space_indent, "_TS_"); + } + script_template.content += line.replace("\t", "_TS_") + "\n"; + } + } + file->close(); + memdelete(file); + } + + script_template.content = script_template.content.lstrip("\n"); + + // Get name from file name if no name in meta information + if (script_template.name == String()) { + script_template.name = p_filename.get_basename().replace("_", " ").capitalize(); + } + + return script_template; +} + +String ScriptCreateDialog::_get_script_origin_label(const ScriptLanguage::TemplateLocation &p_origin) const { + switch (p_origin) { + case ScriptLanguage::TEMPLATE_BUILT_IN: + return TTR("Built-in"); + case ScriptLanguage::TEMPLATE_EDITOR: + return TTR("Editor"); + case ScriptLanguage::TEMPLATE_PROJECT: + return TTR("Project"); + } + return ""; +} + void ScriptCreateDialog::_bind_methods() { ClassDB::bind_method(D_METHOD("config", "inherits", "path", "built_in_enabled", "load_enabled"), &ScriptCreateDialog::config, DEFVAL(true), DEFVAL(true)); @@ -757,7 +886,7 @@ ScriptCreateDialog::ScriptCreateDialog() { GridContainer *gc = memnew(GridContainer); gc->set_columns(2); - /* Error Messages Field */ + /* Information Messages Field */ VBoxContainer *vb = memnew(VBoxContainer); @@ -782,6 +911,10 @@ ScriptCreateDialog::ScriptCreateDialog() { script_name_warning_label->set_autowrap_mode(Label::AUTOWRAP_WORD_SMART); script_name_warning_label->hide(); + template_info_label = memnew(Label); + vb->add_child(template_info_label); + template_info_label->set_autowrap_mode(Label::AUTOWRAP_WORD_SMART); + status_panel = memnew(PanelContainer); status_panel->set_h_size_flags(Control::SIZE_FILL); status_panel->set_v_size_flags(Control::SIZE_EXPAND_FILL); @@ -801,7 +934,7 @@ ScriptCreateDialog::ScriptCreateDialog() { /* Language */ language_menu = memnew(OptionButton); - language_menu->set_custom_minimum_size(Size2(250, 0) * EDSCALE); + language_menu->set_custom_minimum_size(Size2(350, 0) * EDSCALE); language_menu->set_h_size_flags(Control::SIZE_EXPAND_FILL); gc->add_child(memnew(Label(TTR("Language:")))); gc->add_child(language_menu); @@ -819,7 +952,7 @@ ScriptCreateDialog::ScriptCreateDialog() { } current_language = default_language; - language_menu->connect("item_selected", callable_mp(this, &ScriptCreateDialog::_lang_changed)); + language_menu->connect("item_selected", callable_mp(this, &ScriptCreateDialog::_language_changed)); /* Inherits */ @@ -851,10 +984,24 @@ ScriptCreateDialog::ScriptCreateDialog() { /* Templates */ - template_menu = memnew(OptionButton); + is_using_templates = true; gc->add_child(memnew(Label(TTR("Template:")))); - gc->add_child(template_menu); + HBoxContainer *template_hb = memnew(HBoxContainer); + template_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL); + + use_templates = memnew(CheckBox); + use_templates->set_pressed(is_using_templates); + use_templates->connect("pressed", callable_mp(this, &ScriptCreateDialog::_use_template_pressed)); + template_hb->add_child(use_templates); + + template_inactive_message = ""; + + template_menu = memnew(OptionButton); + template_menu->set_h_size_flags(Control::SIZE_EXPAND_FILL); template_menu->connect("item_selected", callable_mp(this, &ScriptCreateDialog::_template_changed)); + template_hb->add_child(template_menu); + + gc->add_child(template_hb); /* Built-in Script */ |