diff options
29 files changed, 940 insertions, 524 deletions
diff --git a/core/object/script_language.h b/core/object/script_language.h index 8d76cbf479..4754b01a07 100644 --- a/core/object/script_language.h +++ b/core/object/script_language.h @@ -274,13 +274,32 @@ public: String message; }; + enum TemplateLocation { + TEMPLATE_BUILT_IN, + TEMPLATE_EDITOR, + TEMPLATE_PROJECT + }; + + struct ScriptTemplate { + String inherit = "Object"; + String name; + String description; + String content; + int id = 0; + TemplateLocation origin = TemplateLocation::TEMPLATE_BUILT_IN; + + String get_hash() const { + return itos(origin) + inherit + name; + } + }; + void get_core_type_words(List<String> *p_core_type_words) const; virtual void get_reserved_words(List<String> *p_words) const = 0; virtual bool is_control_flow_keyword(String p_string) const = 0; virtual void get_comment_delimiters(List<String> *p_delimiters) const = 0; virtual void get_string_delimiters(List<String> *p_delimiters) const = 0; - virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const = 0; - virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) {} + virtual Ref<Script> make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const { return Ref<Script>(); } + virtual Vector<ScriptTemplate> get_built_in_templates(StringName p_object) { return Vector<ScriptTemplate>(); } virtual bool is_using_templates() { return false; } virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptError> *r_errors = nullptr, List<Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const = 0; virtual String validate_path(const String &p_path) const { return ""; } diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 1ac1d6f048..fabf497d9c 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -781,43 +781,6 @@ bool EditorSettings::_is_default_text_editor_theme(String p_theme_name) { return p_theme_name == "default" || p_theme_name == "godot 2" || p_theme_name == "custom"; } -static Dictionary _get_builtin_script_templates() { - Dictionary templates; - - // No Comments - templates["no_comments.gd"] = - "extends %BASE%\n" - "\n" - "\n" - "func _ready()%VOID_RETURN%:\n" - "%TS%pass\n"; - - // Empty - templates["empty.gd"] = - "extends %BASE%" - "\n" - "\n"; - - return templates; -} - -static void _create_script_templates(const String &p_path) { - Dictionary templates = _get_builtin_script_templates(); - List<Variant> keys; - templates.get_key_list(&keys); - FileAccessRef file = FileAccess::create(FileAccess::ACCESS_FILESYSTEM); - DirAccessRef dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - dir->change_dir(p_path); - for (int i = 0; i < keys.size(); i++) { - if (!dir->file_exists(keys[i])) { - Error err = file->reopen(p_path.plus_file((String)keys[i]), FileAccess::WRITE); - ERR_FAIL_COND(err != OK); - file->store_string(templates[keys[i]]); - file->close(); - } - } -} - // PUBLIC METHODS EditorSettings *EditorSettings::get_singleton() { @@ -852,10 +815,7 @@ void EditorSettings::create() { } if (EditorPaths::get_singleton()->are_paths_valid()) { - _create_script_templates(EditorPaths::get_singleton()->get_config_dir().plus_file("script_templates")); - // Validate editor config file. - DirAccessRef dir = DirAccess::open(EditorPaths::get_singleton()->get_config_dir()); String config_file_name = "editor_settings-" + itos(VERSION_MAJOR) + ".tres"; config_file_path = EditorPaths::get_singleton()->get_config_dir().plus_file(config_file_name); diff --git a/editor/plugin_config_dialog.cpp b/editor/plugin_config_dialog.cpp index ad22aafb2b..e6e53d2e29 100644 --- a/editor/plugin_config_dialog.cpp +++ b/editor/plugin_config_dialog.cpp @@ -36,12 +36,6 @@ #include "editor/editor_plugin.h" #include "editor/editor_scale.h" #include "editor/project_settings_editor.h" -#include "scene/gui/grid_container.h" - -#include "modules/modules_enabled.gen.h" // For gdscript. -#ifdef MODULE_GDSCRIPT_ENABLED -#include "modules/gdscript/gdscript.h" -#endif void PluginConfigDialog::_clear_fields() { name_edit->set_text(""); @@ -76,42 +70,16 @@ void PluginConfigDialog::_on_confirmed() { String lang_name = ScriptServer::get_language(lang_idx)->get_name(); Ref<Script> script; - - // TODO Use script templates. Right now, this code won't add the 'tool' annotation to other languages. - // TODO Better support script languages with named classes (has_named_classes). - - // FIXME: It's hacky to have hardcoded access to the GDScript module here. - // The editor code should not have to know what languages are enabled. -#ifdef MODULE_GDSCRIPT_ENABLED - if (lang_name == GDScriptLanguage::get_singleton()->get_name()) { - // Hard-coded GDScript template to keep usability until we use script templates. - Ref<Script> gdscript = memnew(GDScript); - gdscript->set_source_code( - "@tool\n" - "extends EditorPlugin\n" - "\n" - "\n" - "func _enter_tree()%VOID_RETURN%:\n" - "%TS%pass\n" - "\n" - "\n" - "func _exit_tree()%VOID_RETURN%:\n" - "%TS%pass\n"); - GDScriptLanguage::get_singleton()->make_template("", "", gdscript); - String script_path = path.plus_file(script_edit->get_text()); - gdscript->set_path(script_path); - ResourceSaver::save(script_path, gdscript); - script = gdscript; - } else { -#endif - String script_path = path.plus_file(script_edit->get_text()); - String class_name = script_path.get_file().get_basename(); - script = ScriptServer::get_language(lang_idx)->get_template(class_name, "EditorPlugin"); - script->set_path(script_path); - ResourceSaver::save(script_path, script); -#ifdef MODULE_GDSCRIPT_ENABLED + String script_path = path.plus_file(script_edit->get_text()); + String class_name = script_path.get_file().get_basename(); + String template_content = ""; + Vector<ScriptLanguage::ScriptTemplate> templates = ScriptServer::get_language(lang_idx)->get_built_in_templates("EditorPlugin"); + if (templates.size() > 0) { + template_content = templates.get(0).content; } -#endif + script = ScriptServer::get_language(lang_idx)->make_template(template_content, class_name, "EditorPlugin"); + script->set_path(script_path); + ResourceSaver::save(script_path, script); emit_signal(SNAME("plugin_ready"), script.operator->(), active_edit->is_pressed() ? _to_absolute_plugin_path(subfolder_edit->get_text()) : ""); } else { @@ -331,11 +299,9 @@ PluginConfigDialog::PluginConfigDialog() { for (int i = 0; i < ScriptServer::get_language_count(); i++) { ScriptLanguage *lang = ScriptServer::get_language(i); script_option_edit->add_item(lang->get_name()); -#ifdef MODULE_GDSCRIPT_ENABLED - if (lang == GDScriptLanguage::get_singleton()) { + if (lang->get_name() == "GDScript") { default_lang = i; } -#endif } script_option_edit->select(default_lang); grid->add_child(script_option_edit); diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp index 0e96a1d247..a1e3f5aabe 100644 --- a/editor/script_create_dialog.cpp +++ b/editor/script_create_dialog.cpp @@ -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> hierachy; + hierachy.append(p_object); + + String parent_class = ClassDB::get_parent_class(p_object); + while (parent_class.is_valid_identifier()) { + hierachy.append(parent_class); + parent_class = ClassDB::get_parent_class(parent_class); + } + return hierachy; +} + +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 lastest 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 */ diff --git a/editor/script_create_dialog.h b/editor/script_create_dialog.h index dba798eea7..2c980f6e6e 100644 --- a/editor/script_create_dialog.h +++ b/editor/script_create_dialog.h @@ -31,6 +31,7 @@ #ifndef SCRIPT_CREATE_DIALOG_H #define SCRIPT_CREATE_DIALOG_H +#include "core/object/script_language.h" #include "editor/editor_file_dialog.h" #include "editor/editor_settings.h" #include "scene/gui/check_box.h" @@ -50,6 +51,7 @@ class ScriptCreateDialog : public ConfirmationDialog { Label *path_error_label; Label *builtin_warning_label; Label *script_name_warning_label; + Label *template_info_label; PanelContainer *status_panel; LineEdit *parent_name; Button *parent_browse_button; @@ -61,12 +63,14 @@ class ScriptCreateDialog : public ConfirmationDialog { Button *path_button; EditorFileDialog *file_browse; CheckBox *internal; + CheckBox *use_templates; VBoxContainer *path_vb; AcceptDialog *alert; CreateDialog *select_class; bool path_valid; bool create_new; bool is_browsing_parent; + String template_inactive_message; String initial_bp; bool is_new_script_created; bool is_path_valid; @@ -76,6 +80,7 @@ class ScriptCreateDialog : public ConfirmationDialog { bool is_parent_name_valid; bool is_class_name_valid; bool is_built_in; + bool is_using_templates; bool built_in_enabled; bool load_enabled; int current_language; @@ -85,23 +90,8 @@ class ScriptCreateDialog : public ConfirmationDialog { Control *path_controls[2]; Control *name_controls[2]; - enum ScriptOrigin { - SCRIPT_ORIGIN_PROJECT, - SCRIPT_ORIGIN_EDITOR, - }; - struct ScriptTemplateInfo { - int id = 0; - ScriptOrigin origin = ScriptOrigin::SCRIPT_ORIGIN_EDITOR; - String dir; - String name; - String extension; - }; - - String script_template; - Vector<ScriptTemplateInfo> template_list; - Map<String, Vector<int>> template_overrides; // name : indices - - void _update_script_templates(const String &p_extension); + Vector<ScriptLanguage::ScriptTemplate> template_list; + ScriptLanguage *language; String base_type; @@ -109,8 +99,9 @@ class ScriptCreateDialog : public ConfirmationDialog { bool _can_be_built_in(); void _path_changed(const String &p_path = String()); void _path_submitted(const String &p_path = String()); - void _lang_changed(int l = 0); + void _language_changed(int l = 0); void _built_in_pressed(); + void _use_template_pressed(); bool _validate_parent(const String &p_string); bool _validate_class(const String &p_string); String _validate_path(const String &p_path, bool p_file_must_exist); @@ -125,9 +116,15 @@ class ScriptCreateDialog : public ConfirmationDialog { virtual void ok_pressed() override; void _create_new(); void _load_exist(); + Vector<String> get_hierarchy(String p_object) const; void _msg_script_valid(bool valid, const String &p_msg = String()); void _msg_path_valid(bool valid, const String &p_msg = String()); + void _update_template_menu(); void _update_dialog(); + ScriptLanguage::ScriptTemplate _get_current_template() const; + Vector<ScriptLanguage::ScriptTemplate> _get_user_templates(const ScriptLanguage *language, const StringName &p_object, const String &p_dir, const ScriptLanguage::TemplateLocation &p_origin) const; + ScriptLanguage::ScriptTemplate _parse_template(const ScriptLanguage *language, const String &p_path, const String &p_filename, const ScriptLanguage::TemplateLocation &p_origin, const String &p_inherits) const; + String _get_script_origin_label(const ScriptLanguage::TemplateLocation &p_origin) const; protected: void _notification(int p_what); diff --git a/editor/template_builders.py b/editor/template_builders.py new file mode 100644 index 0000000000..efed567d46 --- /dev/null +++ b/editor/template_builders.py @@ -0,0 +1,95 @@ +"""Functions used to generate source files during build time +All such functions are invoked in a subprocess on Windows to prevent build flakiness. +""" + +import os +from io import StringIO +from platform_methods import subprocess_main + + +def parse_template(inherits, source, delimiter): + script_template = { + "inherits": inherits, + "name": "", + "description": "", + "version": "", + "script": "", + "space-indent": "4", + } + meta_prefix = delimiter + " meta-" + meta = ["name", "description", "version", "space-indent"] + + with open(source) as f: + lines = f.readlines() + for line in lines: + if line.startswith(meta_prefix): + line = line[len(meta_prefix) :] + for m in meta: + if line.startswith(m): + strip_lenght = len(m) + 1 + script_template[m] = line[strip_lenght:].strip() + else: + script_template["script"] += line + if script_template["space-indent"] != "": + indent = " " * int(script_template["space-indent"]) + script_template["script"] = script_template["script"].replace(indent, "_TS_") + if script_template["name"] == "": + script_template["name"] = os.path.splitext(os.path.basename(source))[0].replace("_", " ").title() + script_template["script"] = ( + script_template["script"].replace('"', '\\"').lstrip().replace("\n", "\\n").replace("\t", "_TS_") + ) + return ( + '{ String("' + + script_template["inherits"] + + '"), String("' + + script_template["name"] + + '"), String("' + + script_template["description"] + + '"), String("' + + script_template["script"] + + '")' + + " },\n" + ) + + +def make_templates(target, source, env): + dst = target[0] + s = StringIO() + s.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n\n") + s.write("#ifndef _CODE_TEMPLATES_H\n") + s.write("#define _CODE_TEMPLATES_H\n\n") + s.write('#include "core/object/object.h"\n') + s.write('#include "core/object/script_language.h"\n') + + delimiter = "#" # GDScript single line comment delimiter by default. + if source: + ext = os.path.splitext(source[0])[1] + if ext == ".cs": + delimiter = "//" + + parsed_template_string = "" + number_of_templates = 0 + + for filepath in source: + node_name = os.path.basename(os.path.dirname(filepath)) + parsed_template = parse_template(node_name, filepath, delimiter) + parsed_template_string += "\t" + parsed_template + number_of_templates += 1 + + s.write("\nstatic const int TEMPLATES_ARRAY_SIZE = " + str(number_of_templates) + ";\n") + s.write("\nstatic const struct ScriptLanguage::ScriptTemplate TEMPLATES[" + str(number_of_templates) + "] = {\n") + + s.write(parsed_template_string) + + s.write("};\n") + + s.write("\n#endif\n") + + with open(dst, "w") as f: + f.write(s.getvalue()) + + s.close() + + +if __name__ == "__main__": + subprocess_main(globals()) diff --git a/modules/gdscript/SCsub b/modules/gdscript/SCsub index 5c8cbdf869..c6121ec7fe 100644 --- a/modules/gdscript/SCsub +++ b/modules/gdscript/SCsub @@ -21,3 +21,5 @@ if env["tools"]: if env["tests"]: env_gdscript.Append(CPPDEFINES=["TESTS_ENABLED"]) env_gdscript.add_source_files(env.modules_sources, "./tests/*.cpp") + +SConscript("editor_templates/SCsub") diff --git a/modules/gdscript/editor_templates/CharacterBody2D/basic_movement.gd b/modules/gdscript/editor_templates/CharacterBody2D/basic_movement.gd new file mode 100644 index 0000000000..0824d894c5 --- /dev/null +++ b/modules/gdscript/editor_templates/CharacterBody2D/basic_movement.gd @@ -0,0 +1,29 @@ +# meta-description: Classic movement for gravity games (platformer, ...) + +extends _BASE_ + +const SPEED: float = 300.0 +const JUMP_FORCE: float = -400.0 + +# Get the gravity from the project settings to be synced with RigidDynamicBody nodes. +var gravity: int = ProjectSettings.get_setting("physics/2d/default_gravity") + + +func _physics_process(delta: float) -> void: + # Add the gravity. + if not is_on_floor(): + motion_velocity.y += gravity * delta + + # Handle Jump. + if Input.is_action_just_pressed("ui_accept") and is_on_floor(): + motion_velocity.y = JUMP_FORCE + + # Get the input direction and handle the movement/deceleration. + # As good practice, you should replace UI actions with custom gameplay actions. + var direction := Input.get_axis("ui_left", "ui_right") + if direction: + motion_velocity.x = direction * SPEED + else: + motion_velocity.x = move_toward(motion_velocity.x, 0, SPEED) + + move_and_slide() diff --git a/modules/gdscript/editor_templates/CharacterBody3D/basic_movement.gd b/modules/gdscript/editor_templates/CharacterBody3D/basic_movement.gd new file mode 100644 index 0000000000..ce6d67ae84 --- /dev/null +++ b/modules/gdscript/editor_templates/CharacterBody3D/basic_movement.gd @@ -0,0 +1,32 @@ +# meta-description: Classic movement for gravity games (FPS, TPS, ...) + +extends _BASE_ + +const SPEED: float = 5.0 +const JUMP_FORCE: float = 4.5 + +# Get the gravity from the project settings to be synced with RigidDynamicBody nodes. +var gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity") + + +func _physics_process(delta: float) -> void: + # Add the gravity. + if not is_on_floor(): + motion_velocity.y -= gravity * delta + + # Handle Jump. + if Input.is_action_just_pressed("ui_accept") and is_on_floor(): + motion_velocity.y = JUMP_FORCE + + # Get the input direction and handle the movement/deceleration. + # As good practice, you should replace UI actions with custom gameplay actions. + var input_dir := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down") + var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized() + if direction: + motion_velocity.x = direction.x * SPEED + motion_velocity.z = direction.z * SPEED + else: + motion_velocity.x = move_toward(motion_velocity.x, 0, SPEED) + motion_velocity.z = move_toward(motion_velocity.z, 0, SPEED) + + move_and_slide() diff --git a/modules/gdscript/editor_templates/EditorPlugin/plugin.gd b/modules/gdscript/editor_templates/EditorPlugin/plugin.gd new file mode 100644 index 0000000000..8614bb8b17 --- /dev/null +++ b/modules/gdscript/editor_templates/EditorPlugin/plugin.gd @@ -0,0 +1,11 @@ +# meta-description: Basic plugin template +@tool +extends EditorPlugin + +func _enter_tree() -> void: + # Initialization of the plugin goes here. + pass + +func _exit_tree() -> void: + # Clean-up of the plugin goes here. + pass diff --git a/modules/gdscript/editor_templates/EditorScript/basic_editor_script.gd b/modules/gdscript/editor_templates/EditorScript/basic_editor_script.gd new file mode 100644 index 0000000000..fdb174c7ed --- /dev/null +++ b/modules/gdscript/editor_templates/EditorScript/basic_editor_script.gd @@ -0,0 +1,7 @@ +# meta-description: Basic editor script template +@tool +extends EditorScript + +func _run() -> void: + # Called when the script is executed (using File -> Run in Script Editor). + pass diff --git a/modules/gdscript/editor_templates/Node/default.gd b/modules/gdscript/editor_templates/Node/default.gd new file mode 100644 index 0000000000..ee5c0b99cc --- /dev/null +++ b/modules/gdscript/editor_templates/Node/default.gd @@ -0,0 +1,11 @@ +# meta-description: Base template for Node with default Godot cycle methods + +extends _BASE_ + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + pass # Replace with function body. + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(delta: float) -> void: + pass diff --git a/modules/gdscript/editor_templates/Object/empty.gd b/modules/gdscript/editor_templates/Object/empty.gd new file mode 100644 index 0000000000..387786b0a4 --- /dev/null +++ b/modules/gdscript/editor_templates/Object/empty.gd @@ -0,0 +1,3 @@ +# meta-description: Empty template suitable for all Objects + +extends _BASE_ diff --git a/modules/gdscript/editor_templates/SCsub b/modules/gdscript/editor_templates/SCsub new file mode 100644 index 0000000000..2266ef2d01 --- /dev/null +++ b/modules/gdscript/editor_templates/SCsub @@ -0,0 +1,16 @@ +#!/usr/bin/env python + +Import("env") + +import editor.template_builders as build_template_gd + +env["BUILDERS"]["MakeGDTemplateBuilder"] = Builder( + action=env.Run(build_template_gd.make_templates, "Generating GDScript templates header."), + suffix=".h", + src_suffix=".gd", +) + +# Template files +templates_sources = Glob("*/*.gd") + +env.Alias("editor_template_gd", [env.MakeGDTemplateBuilder("templates.gen.h", templates_sources)]) diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 4822e411ce..02c1bfb40c 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -49,6 +49,10 @@ #include "tests/gdscript_test_runner.h" #endif +#ifdef TOOLS_ENABLED +#include "editor/editor_settings.h" +#endif + /////////////////////////// GDScriptNativeClass::GDScriptNativeClass(const StringName &p_name) { @@ -817,10 +821,16 @@ Error GDScript::reload(bool p_keep_state) { basedir = basedir.get_base_dir(); } - if (source.find("%BASE%") != -1) { - //loading a template, don't parse +// Loading a template, don't parse. +#ifdef TOOLS_ENABLED + if (basedir.begins_with(EditorSettings::get_singleton()->get_project_script_templates_dir())) { return OK; } +#else + if (source.find("_BASE_") != -1) { + return OK; + } +#endif { String source_path = path; diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index ade4f247c9..2f1187e5b1 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -396,7 +396,7 @@ public: _debug_call_stack_pos--; } - virtual Vector<StackInfo> debug_get_current_stack_info() { + virtual Vector<StackInfo> debug_get_current_stack_info() override { if (Thread::get_main_id() != Thread::get_caller_id()) { return Vector<StackInfo>(); } @@ -430,77 +430,76 @@ public: _FORCE_INLINE_ static GDScriptLanguage *get_singleton() { return singleton; } - virtual String get_name() const; + virtual String get_name() const override; /* LANGUAGE FUNCTIONS */ - virtual void init(); - virtual String get_type() const; - virtual String get_extension() const; - virtual Error execute_file(const String &p_path); - virtual void finish(); + virtual void init() override; + virtual String get_type() const override; + virtual String get_extension() const override; + virtual Error execute_file(const String &p_path) override; + virtual void finish() override; /* EDITOR FUNCTIONS */ - virtual void get_reserved_words(List<String> *p_words) const; - virtual bool is_control_flow_keyword(String p_keywords) const; - virtual void get_comment_delimiters(List<String> *p_delimiters) const; - virtual void get_string_delimiters(List<String> *p_delimiters) const; - virtual String _get_processed_template(const String &p_template, const String &p_base_class_name) const; - virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const; - virtual bool is_using_templates(); - virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script); - virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const; - virtual Script *create_script() const; - virtual bool has_named_classes() const; - virtual bool supports_builtin_mode() const; - virtual bool supports_documentation() const; - virtual bool can_inherit_from_file() const { return true; } - virtual int find_function(const String &p_function, const String &p_code) const; - virtual String make_function(const String &p_class, const String &p_name, const PackedStringArray &p_args) const; - virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptCodeCompletionOption> *r_options, bool &r_forced, String &r_call_hint); + virtual void get_reserved_words(List<String> *p_words) const override; + virtual bool is_control_flow_keyword(String p_keywords) const override; + virtual void get_comment_delimiters(List<String> *p_delimiters) const override; + virtual void get_string_delimiters(List<String> *p_delimiters) const override; + virtual bool is_using_templates() override; + virtual Ref<Script> make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const override; + virtual Vector<ScriptTemplate> get_built_in_templates(StringName p_object) override; + virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const override; + virtual Script *create_script() const override; + virtual bool has_named_classes() const override; + virtual bool supports_builtin_mode() const override; + virtual bool supports_documentation() const override; + virtual bool can_inherit_from_file() const override { return true; } + virtual int find_function(const String &p_function, const String &p_code) const override; + virtual String make_function(const String &p_class, const String &p_name, const PackedStringArray &p_args) const override; + virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptCodeCompletionOption> *r_options, bool &r_forced, String &r_call_hint) override; #ifdef TOOLS_ENABLED - virtual Error lookup_code(const String &p_code, const String &p_symbol, const String &p_path, Object *p_owner, LookupResult &r_result); + virtual Error lookup_code(const String &p_code, const String &p_symbol, const String &p_path, Object *p_owner, LookupResult &r_result) override; #endif virtual String _get_indentation() const; - virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const; - virtual void add_global_constant(const StringName &p_variable, const Variant &p_value); - virtual void add_named_global_constant(const StringName &p_name, const Variant &p_value); - virtual void remove_named_global_constant(const StringName &p_name); + virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const override; + virtual void add_global_constant(const StringName &p_variable, const Variant &p_value) override; + virtual void add_named_global_constant(const StringName &p_name, const Variant &p_value) override; + virtual void remove_named_global_constant(const StringName &p_name) override; /* DEBUGGER FUNCTIONS */ - virtual String debug_get_error() const; - virtual int debug_get_stack_level_count() const; - virtual int debug_get_stack_level_line(int p_level) const; - virtual String debug_get_stack_level_function(int p_level) const; - virtual String debug_get_stack_level_source(int p_level) const; - virtual void debug_get_stack_level_locals(int p_level, List<String> *p_locals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1); - virtual void debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1); - virtual ScriptInstance *debug_get_stack_level_instance(int p_level); - virtual void debug_get_globals(List<String> *p_globals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1); - virtual String debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems = -1, int p_max_depth = -1); + virtual String debug_get_error() const override; + virtual int debug_get_stack_level_count() const override; + virtual int debug_get_stack_level_line(int p_level) const override; + virtual String debug_get_stack_level_function(int p_level) const override; + virtual String debug_get_stack_level_source(int p_level) const override; + virtual void debug_get_stack_level_locals(int p_level, List<String> *p_locals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) override; + virtual void debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) override; + virtual ScriptInstance *debug_get_stack_level_instance(int p_level) override; + virtual void debug_get_globals(List<String> *p_globals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) override; + virtual String debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems = -1, int p_max_depth = -1) override; - virtual void reload_all_scripts(); - virtual void reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload); + virtual void reload_all_scripts() override; + virtual void reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) override; - virtual void frame(); + virtual void frame() override; - virtual void get_public_functions(List<MethodInfo> *p_functions) const; - virtual void get_public_constants(List<Pair<String, Variant>> *p_constants) const; + virtual void get_public_functions(List<MethodInfo> *p_functions) const override; + virtual void get_public_constants(List<Pair<String, Variant>> *p_constants) const override; - virtual void profiling_start(); - virtual void profiling_stop(); + virtual void profiling_start() override; + virtual void profiling_stop() override; - virtual int profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max); - virtual int profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max); + virtual int profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max) override; + virtual int profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max) override; /* LOADER FUNCTIONS */ - virtual void get_recognized_extensions(List<String> *p_extensions) const; + virtual void get_recognized_extensions(List<String> *p_extensions) const override; /* GLOBAL CLASSES */ - virtual bool handles_global_class_type(const String &p_type) const; - virtual String get_global_class_name(const String &p_path, String *r_base_type = nullptr, String *r_icon_path = nullptr) const; + virtual bool handles_global_class_type(const String &p_type) const override; + virtual String get_global_class_name(const String &p_path, String *r_base_type = nullptr, String *r_icon_path = nullptr) const override; void add_orphan_subclass(const String &p_qualified_name, const ObjectID &p_subclass); Ref<GDScript> get_orphan_subclass(const String &p_qualified_name); diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index ec01c19295..3fb834590b 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -33,6 +33,7 @@ #include "core/config/engine.h" #include "core/core_constants.h" #include "core/io/file_access.h" +#include "editor_templates/templates.gen.h" #include "gdscript_analyzer.h" #include "gdscript_compiler.h" #include "gdscript_parser.h" @@ -55,68 +56,44 @@ void GDScriptLanguage::get_string_delimiters(List<String> *p_delimiters) const { p_delimiters->push_back("\"\"\" \"\"\""); } -String GDScriptLanguage::_get_processed_template(const String &p_template, const String &p_base_class_name) const { - String processed_template = p_template; +bool GDScriptLanguage::is_using_templates() { + return true; +} +Ref<Script> GDScriptLanguage::make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const { + Ref<GDScript> script; + script.instantiate(); + String processed_template = p_template; #ifdef TOOLS_ENABLED - if (EDITOR_DEF("text_editor/completion/add_type_hints", false)) { - processed_template = processed_template.replace("%INT_TYPE%", ": int"); - processed_template = processed_template.replace("%STRING_TYPE%", ": String"); - processed_template = processed_template.replace("%FLOAT_TYPE%", ": float"); - processed_template = processed_template.replace("%VOID_RETURN%", " -> void"); - } else { - processed_template = processed_template.replace("%INT_TYPE%", ""); - processed_template = processed_template.replace("%STRING_TYPE%", ""); - processed_template = processed_template.replace("%FLOAT_TYPE%", ""); - processed_template = processed_template.replace("%VOID_RETURN%", ""); + if (!EDITOR_DEF("text_editor/completion/add_type_hints", false)) { + processed_template = processed_template.replace(": int", "") + .replace(": String", "") + .replace(": float", "") + .replace(":=", "=") + .replace(" -> void", ""); } #else - processed_template = processed_template.replace("%INT_TYPE%", ""); - processed_template = processed_template.replace("%STRING_TYPE%", ""); - processed_template = processed_template.replace("%FLOAT_TYPE%", ""); - processed_template = processed_template.replace("%VOID_RETURN%", ""); + processed_template = processed_template.replace(": int", "") + .replace(": String", "") + .replace(": float", "") + .replace(" -> void", ""); #endif - processed_template = processed_template.replace("%BASE%", p_base_class_name); - processed_template = processed_template.replace("%TS%", _get_indentation()); - - return processed_template; -} - -Ref<Script> GDScriptLanguage::get_template(const String &p_class_name, const String &p_base_class_name) const { - String _template = "extends %BASE%\n" - "\n" - "\n" - "# Declare member variables here. Examples:\n" - "# var a%INT_TYPE% = 2\n" - "# var b%STRING_TYPE% = \"text\"\n" - "\n" - "\n" - "# Called when the node enters the scene tree for the first time.\n" - "func _ready()%VOID_RETURN%:\n" - "%TS%pass # Replace with function body.\n" - "\n" - "\n" - "# Called every frame. 'delta' is the elapsed time since the previous frame.\n" - "#func _process(delta%FLOAT_TYPE%)%VOID_RETURN%:\n" - "#%TS%pass\n"; - - _template = _get_processed_template(_template, p_base_class_name); - - Ref<GDScript> script; - script.instantiate(); - script->set_source_code(_template); - + processed_template = processed_template.replace("_BASE_", p_base_class_name) + .replace("_CLASS_", p_class_name) + .replace("_TS_", _get_indentation()); + script->set_source_code(processed_template); return script; } -bool GDScriptLanguage::is_using_templates() { - return true; -} - -void GDScriptLanguage::make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) { - String _template = _get_processed_template(p_script->get_source_code(), p_base_class_name); - p_script->set_source_code(_template); +Vector<ScriptLanguage::ScriptTemplate> GDScriptLanguage::get_built_in_templates(StringName p_object) { + Vector<ScriptLanguage::ScriptTemplate> templates; + for (int i = 0; i < TEMPLATES_ARRAY_SIZE; i++) { + if (TEMPLATES[i].inherit == p_object) { + templates.append(TEMPLATES[i]); + } + } + return templates; } static void get_function_names_recursively(const GDScriptParser::ClassNode *p_class, const String &p_prefix, Map<int, String> &r_funcs) { @@ -236,7 +213,7 @@ Script *GDScriptLanguage::create_script() const { /* DEBUGGER FUNCTIONS */ bool GDScriptLanguage::debug_break_parse(const String &p_file, int p_line, const String &p_error) { - //break because of parse error + // break because of parse error if (EngineDebugger::is_active() && Thread::get_caller_id() == Thread::get_main_id()) { _debug_parse_err_line = p_line; @@ -1383,8 +1360,8 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context, } if (!script.ends_with(".gd")) { - //not a script, try find the script anyway, - //may have some success + // not a script, try find the script anyway, + // may have some success script = script.get_basename() + ".gd"; } @@ -2770,7 +2747,7 @@ void GDScriptLanguage::auto_indent_code(String &p_code, int p_from_line, int p_t } if (indent_stack.size() && indent_stack.back()->get() != tc) { - indent_stack.push_back(tc); //this is not right but gets the job done + indent_stack.push_back(tc); // this is not right but gets the job done } } diff --git a/modules/mono/SCsub b/modules/mono/SCsub index 95c959235c..db35e804aa 100644 --- a/modules/mono/SCsub +++ b/modules/mono/SCsub @@ -63,3 +63,5 @@ elif env["platform"] == "android": if env["tools"]: env_mono.add_source_files(env.modules_sources, "editor/*.cpp") + +SConscript("editor_templates/SCsub") diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 0ceb45d425..bf047e76eb 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -55,6 +55,7 @@ #endif #include "editor/editor_internal_calls.h" +#include "editor_templates/templates.gen.h" #include "godotsharp_dirs.h" #include "mono_gd/gd_mono_cache.h" #include "mono_gd/gd_mono_class.h" @@ -351,57 +352,33 @@ static String get_base_class_name(const String &p_base_class_name, const String return base_class; } -Ref<Script> CSharpLanguage::get_template(const String &p_class_name, const String &p_base_class_name) const { - String script_template = "using " BINDINGS_NAMESPACE ";\n" - "using System;\n" - "\n" - "public partial class %CLASS% : %BASE%\n" - "{\n" - " // Declare member variables here. Examples:\n" - " // private int a = 2;\n" - " // private string b = \"text\";\n" - "\n" - " // Called when the node enters the scene tree for the first time.\n" - " public override void _Ready()\n" - " {\n" - " \n" - " }\n" - "\n" - "// // Called every frame. 'delta' is the elapsed time since the previous frame.\n" - "// public override void _Process(float delta)\n" - "// {\n" - "// \n" - "// }\n" - "}\n"; - - // Replaces all spaces in p_class_name with underscores to prevent - // invalid C# Script templates from being generated when the object name - // has spaces in it. - String class_name_no_spaces = p_class_name.replace(" ", "_"); - String base_class_name = get_base_class_name(p_base_class_name, class_name_no_spaces); - script_template = script_template.replace("%BASE%", base_class_name) - .replace("%CLASS%", class_name_no_spaces); +bool CSharpLanguage::is_using_templates() { + return true; +} +Ref<Script> CSharpLanguage::make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const { Ref<CSharpScript> script; script.instantiate(); - script->set_source_code(script_template); - script->set_name(class_name_no_spaces); + String class_name_no_spaces = p_class_name.replace(" ", "_"); + String base_class_name = get_base_class_name(p_base_class_name, class_name_no_spaces); + String processed_template = p_template; + processed_template = processed_template.replace("_BINDINGS_NAMESPACE_", BINDINGS_NAMESPACE) + .replace("_BASE_", base_class_name) + .replace("_CLASS_", class_name_no_spaces) + .replace("_TS_", _get_indentation()); + script->set_source_code(processed_template); return script; } -bool CSharpLanguage::is_using_templates() { - return true; -} - -void CSharpLanguage::make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) { - String src = p_script->get_source_code(); - String class_name_no_spaces = p_class_name.replace(" ", "_"); - String base_class_name = get_base_class_name(p_base_class_name, class_name_no_spaces); - src = src.replace("%BASE%", base_class_name) - .replace("%CLASS%", class_name_no_spaces) - .replace("%TS%", _get_indentation()); - p_script->set_source_code(src); +Vector<ScriptLanguage::ScriptTemplate> CSharpLanguage::get_built_in_templates(StringName p_object) { + Vector<ScriptLanguage::ScriptTemplate> templates; + for (int i = 0; i < TEMPLATES_ARRAY_SIZE; i++) { + if (TEMPLATES[i].inherit == p_object) { + templates.append(TEMPLATES[i]); + } + } + return templates; } String CSharpLanguage::validate_path(const String &p_path) const { diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index c998d9c1e4..15a0326383 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -463,9 +463,9 @@ public: bool is_control_flow_keyword(String p_keyword) const override; void get_comment_delimiters(List<String> *p_delimiters) const override; void get_string_delimiters(List<String> *p_delimiters) const override; - Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const override; bool is_using_templates() override; - void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) override; + virtual Ref<Script> make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const override; + virtual Vector<ScriptTemplate> get_built_in_templates(StringName p_object) override; /* TODO */ bool validate(const String &p_script, const String &p_path, List<String> *r_functions, List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const override { return true; diff --git a/modules/mono/editor_templates/CharacterBody2D/basic_movement.cs b/modules/mono/editor_templates/CharacterBody2D/basic_movement.cs new file mode 100644 index 0000000000..6e9547ce9b --- /dev/null +++ b/modules/mono/editor_templates/CharacterBody2D/basic_movement.cs @@ -0,0 +1,41 @@ +// meta-description: Classic movement for gravity games (platformer, ...) + +using _BINDINGS_NAMESPACE_; +using System; + +public partial class _CLASS_ : _BASE_ +{ + public const float Speed = 300.0f; + public const float JumpForce = -400.0f; + + // Get the gravity from the project settings to be synced with RigidDynamicBody nodes. + public float gravity = (float)ProjectSettings.GetSetting("physics/2d/default_gravity"); + + public override void _PhysicsProcess(float delta) + { + Vector2 motionVelocity = MotionVelocity; + + // Add the gravity. + if (!IsOnFloor()) + motionVelocity.y += gravity * delta; + + // Handle Jump. + if (Input.IsActionJustPressed("ui_accept") && IsOnFloor()) + motionVelocity.y = JumpForce; + + // Get the input direction and handle the movement/deceleration. + // As good practice, you should replace UI actions with custom gameplay actions. + Vector2 direction = Input.GetVector("ui_left", "ui_right", "ui_up", "ui_down"); + if (direction != Vector2.Zero) + { + motionVelocity.x = direction.x * Speed; + } + else + { + motionVelocity.x = Mathf.MoveToward(MotionVelocity.x, 0, Speed); + } + + MotionVelocity = motionVelocity; + MoveAndSlide(); + } +} diff --git a/modules/mono/editor_templates/CharacterBody3D/basic_movement.cs b/modules/mono/editor_templates/CharacterBody3D/basic_movement.cs new file mode 100644 index 0000000000..13be4bbcb1 --- /dev/null +++ b/modules/mono/editor_templates/CharacterBody3D/basic_movement.cs @@ -0,0 +1,44 @@ +// meta-description: Classic movement for gravity games (FPS, TPS, ...) + +using _BINDINGS_NAMESPACE_; +using System; + +public partial class _CLASS_ : _BASE_ +{ + public const float Speed = 5.0f; + public const float JumpForce = 4.5f; + + // Get the gravity from the project settings to be synced with RigidDynamicBody nodes. + public float gravity = (float)ProjectSettings.GetSetting("physics/3d/default_gravity"); + + public override void _PhysicsProcess(float delta) + { + Vector3 motionVelocity = MotionVelocity; + + // Add the gravity. + if (!IsOnFloor()) + motionVelocity.y -= gravity * delta; + + // Handle Jump. + if (Input.IsActionJustPressed("ui_accept") && IsOnFloor()) + motionVelocity.y = JumpForce; + + // Get the input direction and handle the movement/deceleration. + // As good practice, you should replace UI actions with custom gameplay actions. + Vector2 inputDir = Input.GetVector("ui_left", "ui_right", "ui_up", "ui_down"); + Vector3 direction = Transform.basis.Xform(new Vector3(inputDir.x, 0, inputDir.y)).Normalized(); + if (direction != Vector3.Zero) + { + motionVelocity.x = direction.x * Speed; + motionVelocity.z = direction.z * Speed; + } + else + { + motionVelocity.x = Mathf.MoveToward(MotionVelocity.x, 0, Speed); + motionVelocity.z = Mathf.MoveToward(MotionVelocity.z, 0, Speed); + } + + MotionVelocity = motionVelocity; + MoveAndSlide(); + } +} diff --git a/modules/mono/editor_templates/EditorPlugin/plugin.cs b/modules/mono/editor_templates/EditorPlugin/plugin.cs new file mode 100644 index 0000000000..6e6a799be6 --- /dev/null +++ b/modules/mono/editor_templates/EditorPlugin/plugin.cs @@ -0,0 +1,19 @@ +// meta-description: Basic plugin template +#if TOOLS +using _BINDINGS_NAMESPACE_; +using System; + +[Tool] +public partial class _CLASS_ : _BASE_ +{ + public override void _EnterTree() + { + // Initialization of the plugin goes here. + } + + public override void _ExitTree() + { + // Clean-up of the plugin goes here. + } +} +#endif diff --git a/modules/mono/editor_templates/EditorScript/basic_editor_script.cs b/modules/mono/editor_templates/EditorScript/basic_editor_script.cs new file mode 100644 index 0000000000..2088822890 --- /dev/null +++ b/modules/mono/editor_templates/EditorScript/basic_editor_script.cs @@ -0,0 +1,14 @@ +// meta-description: Basic editor script template +#if TOOLS +using _BINDINGS_NAMESPACE_; +using System; + +[Tool] +public partial class _CLASS_ : _BASE_ +{ + public override void _Run() + { + // Called when the script is executed (using File -> Run in Script Editor). + } +} +#endif diff --git a/modules/mono/editor_templates/Node/default.cs b/modules/mono/editor_templates/Node/default.cs new file mode 100644 index 0000000000..73d69dd993 --- /dev/null +++ b/modules/mono/editor_templates/Node/default.cs @@ -0,0 +1,19 @@ +// meta-description: Base template for Node with default Godot cycle methods + +using _BINDINGS_NAMESPACE_; +using System; + +public partial class _CLASS_ : _BASE_ +{ + // Called when the node enters the scene tree for the first time. + public override void _Ready() + { + + } + + // Called every frame. 'delta' is the elapsed time since the previous frame. + public override void _Process(float delta) + { + + } +} diff --git a/modules/mono/editor_templates/Object/empty.cs b/modules/mono/editor_templates/Object/empty.cs new file mode 100644 index 0000000000..e5bee64fe1 --- /dev/null +++ b/modules/mono/editor_templates/Object/empty.cs @@ -0,0 +1,9 @@ +// meta-description: Empty template suitable for all Objects + +using _BINDINGS_NAMESPACE_; +using System; + +public partial class _CLASS_ : _BASE_ +{ + +} diff --git a/modules/mono/editor_templates/SCsub b/modules/mono/editor_templates/SCsub new file mode 100644 index 0000000000..39f6cb5c01 --- /dev/null +++ b/modules/mono/editor_templates/SCsub @@ -0,0 +1,16 @@ +#!/usr/bin/env python + +Import("env") + +import editor.template_builders as build_template_cs + +env["BUILDERS"]["MakeCSharpTemplateBuilder"] = Builder( + action=env.Run(build_template_cs.make_templates, "Generating C# templates header."), + suffix=".h", + src_suffix=".cs", +) + +# Template files +templates_sources = Glob("*/*.cs") + +env.Alias("editor_template_cs", [env.MakeCSharpTemplateBuilder("templates.gen.h", templates_sources)]) diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp index 63bd9bbee4..50c24179ff 100644 --- a/modules/visual_script/visual_script.cpp +++ b/modules/visual_script/visual_script.cpp @@ -2241,20 +2241,15 @@ void VisualScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) co void VisualScriptLanguage::get_string_delimiters(List<String> *p_delimiters) const { } -Ref<Script> VisualScriptLanguage::get_template(const String &p_class_name, const String &p_base_class_name) const { - Ref<VisualScript> script; - script.instantiate(); - script->set_instance_base_type(p_base_class_name); - return script; -} - bool VisualScriptLanguage::is_using_templates() { - return true; + return false; } -void VisualScriptLanguage::make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) { - Ref<VisualScript> script = p_script; +Ref<Script> VisualScriptLanguage::make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const { + Ref<VisualScript> script; + script.instantiate(); script->set_instance_base_type(p_base_class_name); + return script; } bool VisualScriptLanguage::validate(const String &p_script, const String &p_path, List<String> *r_functions, List<ScriptLanguage::ScriptError> *r_errors, List<ScriptLanguage::Warning> *r_warnings, Set<int> *r_safe_lines) const { diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h index 39cef8f68b..e321ef236d 100644 --- a/modules/visual_script/visual_script.h +++ b/modules/visual_script/visual_script.h @@ -554,57 +554,56 @@ public: ////////////////////////////////////// - virtual String get_name() const; + virtual String get_name() const override; /* LANGUAGE FUNCTIONS */ - virtual void init(); - virtual String get_type() const; - virtual String get_extension() const; - virtual Error execute_file(const String &p_path); - virtual void finish(); + virtual void init() override; + virtual String get_type() const override; + virtual String get_extension() const override; + virtual Error execute_file(const String &p_path) override; + virtual void finish() override; /* EDITOR FUNCTIONS */ - virtual void get_reserved_words(List<String> *p_words) const; - virtual bool is_control_flow_keyword(String p_keyword) const; - virtual void get_comment_delimiters(List<String> *p_delimiters) const; - virtual void get_string_delimiters(List<String> *p_delimiters) const; - virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const; - virtual bool is_using_templates(); - virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script); - virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const; - virtual Script *create_script() const; - virtual bool has_named_classes() const; - virtual bool supports_builtin_mode() const; - virtual int find_function(const String &p_function, const String &p_code) const; - virtual String make_function(const String &p_class, const String &p_name, const PackedStringArray &p_args) const; - virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const; - virtual void add_global_constant(const StringName &p_variable, const Variant &p_value); + virtual void get_reserved_words(List<String> *p_words) const override; + virtual bool is_control_flow_keyword(String p_keyword) const override; + virtual void get_comment_delimiters(List<String> *p_delimiters) const override; + virtual void get_string_delimiters(List<String> *p_delimiters) const override; + virtual bool is_using_templates() override; + virtual Ref<Script> make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const override; + virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const override; + virtual Script *create_script() const override; + virtual bool has_named_classes() const override; + virtual bool supports_builtin_mode() const override; + virtual int find_function(const String &p_function, const String &p_code) const override; + virtual String make_function(const String &p_class, const String &p_name, const PackedStringArray &p_args) const override; + virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const override; + virtual void add_global_constant(const StringName &p_variable, const Variant &p_value) override; /* DEBUGGER FUNCTIONS */ - virtual String debug_get_error() const; - virtual int debug_get_stack_level_count() const; - virtual int debug_get_stack_level_line(int p_level) const; - virtual String debug_get_stack_level_function(int p_level) const; - virtual String debug_get_stack_level_source(int p_level) const; - virtual void debug_get_stack_level_locals(int p_level, List<String> *p_locals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1); - virtual void debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1); - virtual void debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1); - virtual String debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems = -1, int p_max_depth = -1); - - virtual void reload_all_scripts(); - virtual void reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload); + virtual String debug_get_error() const override; + virtual int debug_get_stack_level_count() const override; + virtual int debug_get_stack_level_line(int p_level) const override; + virtual String debug_get_stack_level_function(int p_level) const override; + virtual String debug_get_stack_level_source(int p_level) const override; + virtual void debug_get_stack_level_locals(int p_level, List<String> *p_locals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) override; + virtual void debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) override; + virtual void debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) override; + virtual String debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems = -1, int p_max_depth = -1) override; + + virtual void reload_all_scripts() override; + virtual void reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) override; /* LOADER FUNCTIONS */ - virtual void get_recognized_extensions(List<String> *p_extensions) const; - virtual void get_public_functions(List<MethodInfo> *p_functions) const; - virtual void get_public_constants(List<Pair<String, Variant>> *p_constants) const; + virtual void get_recognized_extensions(List<String> *p_extensions) const override; + virtual void get_public_functions(List<MethodInfo> *p_functions) const override; + virtual void get_public_constants(List<Pair<String, Variant>> *p_constants) const override; - virtual void profiling_start(); - virtual void profiling_stop(); + virtual void profiling_start() override; + virtual void profiling_stop() override; - virtual int profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max); - virtual int profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max); + virtual int profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max) override; + virtual int profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max) override; void add_register_func(const String &p_name, VisualScriptNodeRegisterFunc p_func); void remove_register_func(const String &p_name); |