/**************************************************************************/ /* create_dialog.cpp */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /**************************************************************************/ /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ /* "Software"), to deal in the Software without restriction, including */ /* without limitation the rights to use, copy, modify, merge, publish, */ /* distribute, sublicense, and/or sell copies of the Software, and to */ /* permit persons to whom the Software is furnished to do so, subject to */ /* the following conditions: */ /* */ /* The above copyright notice and this permission notice shall be */ /* included in all copies or substantial portions of the Software. */ /* */ /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ #include "create_dialog.h" #include "core/object/class_db.h" #include "core/os/keyboard.h" #include "editor/editor_feature_profile.h" #include "editor/editor_node.h" #include "editor/editor_paths.h" #include "editor/editor_settings.h" #include "editor/editor_string_names.h" #include "editor/themes/editor_scale.h" void CreateDialog::popup_create(bool p_dont_clear, bool p_replace_mode, const String &p_current_type, const String &p_current_name) { _fill_type_list(); icon_fallback = search_options->has_theme_icon(base_type, EditorStringName(EditorIcons)) ? base_type : "Object"; if (p_dont_clear) { search_box->select_all(); } else { search_box->clear(); } if (p_replace_mode) { search_box->set_text(p_current_type); } search_box->grab_focus(); _update_search(); if (p_replace_mode) { set_title(vformat(TTR("Change Type of \"%s\""), p_current_name)); set_ok_button_text(TTR("Change")); } else { set_title(vformat(TTR("Create New %s"), base_type)); set_ok_button_text(TTR("Create")); } _load_favorites_and_history(); _save_and_update_favorite_list(); // Restore valid window bounds or pop up at default size. Rect2 saved_size = EditorSettings::get_singleton()->get_project_metadata("dialog_bounds", "create_new_node", Rect2()); if (saved_size != Rect2()) { popup(saved_size); } else { popup_centered_clamped(Size2(900, 700) * EDSCALE, 0.8); } } void CreateDialog::_fill_type_list() { List complete_type_list; ClassDB::get_class_list(&complete_type_list); ScriptServer::get_global_class_list(&complete_type_list); EditorData &ed = EditorNode::get_editor_data(); for (List::Element *I = complete_type_list.front(); I; I = I->next()) { StringName type = I->get(); if (!_should_hide_type(type)) { type_list.push_back(type); if (!ed.get_custom_types().has(type)) { continue; } const Vector &ct = ed.get_custom_types()[type]; for (int i = 0; i < ct.size(); i++) { custom_type_parents[ct[i].name] = type; custom_type_indices[ct[i].name] = i; type_list.push_back(ct[i].name); } } } type_list.sort_custom(); } bool CreateDialog::_is_type_preferred(const String &p_type) const { if (ClassDB::class_exists(p_type)) { return ClassDB::is_parent_class(p_type, preferred_search_result_type); } return EditorNode::get_editor_data().script_class_is_parent(p_type, preferred_search_result_type); } bool CreateDialog::_is_class_disabled_by_feature_profile(const StringName &p_class) const { Ref profile = EditorFeatureProfileManager::get_singleton()->get_current_profile(); return !profile.is_null() && profile->is_class_disabled(p_class); } bool CreateDialog::_should_hide_type(const StringName &p_type) const { if (_is_class_disabled_by_feature_profile(p_type)) { return true; } if (is_base_type_node && p_type.operator String().begins_with("Editor")) { return true; // Do not show editor nodes. } if (ClassDB::class_exists(p_type)) { if (!ClassDB::can_instantiate(p_type) || ClassDB::is_virtual(p_type)) { return true; // Can't create abstract or virtual class. } if (!ClassDB::is_parent_class(p_type, base_type)) { return true; // Wrong inheritance. } if (!ClassDB::is_class_exposed(p_type)) { return true; // Unexposed types. } for (const StringName &E : type_blacklist) { if (ClassDB::is_parent_class(p_type, E)) { return true; // Parent type is blacklisted. } } } else { if (!ScriptServer::is_global_class(p_type)) { return true; } if (!EditorNode::get_editor_data().script_class_is_parent(p_type, base_type)) { return true; // Wrong inheritance. } StringName native_type = ScriptServer::get_global_class_native_base(p_type); if (ClassDB::class_exists(native_type) && !ClassDB::can_instantiate(native_type)) { return true; } String script_path = ScriptServer::get_global_class_path(p_type); if (script_path.begins_with("res://addons/")) { if (!EditorNode::get_singleton()->is_addon_plugin_enabled(script_path.get_slicec('/', 3))) { return true; // Plugin is not enabled. } } } return false; } void CreateDialog::_update_search() { search_options->clear(); search_options_types.clear(); TreeItem *root = search_options->create_item(); root->set_text(0, base_type); root->set_icon(0, search_options->get_editor_theme_icon(icon_fallback)); search_options_types[base_type] = root; _configure_search_option_item(root, base_type, ClassDB::class_exists(base_type) ? TypeCategory::CPP_TYPE : TypeCategory::OTHER_TYPE); const String search_text = search_box->get_text(); bool empty_search = search_text.is_empty(); float highest_score = 0.0f; StringName best_match; for (List::Element *I = type_list.front(); I; I = I->next()) { StringName candidate = I->get(); if (empty_search || search_text.is_subsequence_ofn(candidate)) { _add_type(candidate, ClassDB::class_exists(candidate) ? TypeCategory::CPP_TYPE : TypeCategory::OTHER_TYPE); // Determine the best match for an non-empty search. if (!empty_search) { float score = _score_type(candidate.operator String().get_slicec(' ', 0), search_text); if (score > highest_score) { highest_score = score; best_match = candidate; } } } } // Select the best result. if (empty_search) { select_type(base_type); } else if (best_match != StringName()) { select_type(best_match); } else { favorite->set_disabled(true); help_bit->set_custom_text(String(), String(), vformat(TTR("No results for \"%s\"."), search_text.replace("[", "[lb]"))); get_ok_button()->set_disabled(true); search_options->deselect_all(); } } void CreateDialog::_add_type(const StringName &p_type, TypeCategory p_type_category) { if (search_options_types.has(p_type)) { return; } TypeCategory inherited_type = TypeCategory::OTHER_TYPE; StringName inherits; if (p_type_category == TypeCategory::CPP_TYPE) { inherits = ClassDB::get_parent_class(p_type); inherited_type = TypeCategory::CPP_TYPE; } else { if (p_type_category == TypeCategory::PATH_TYPE || ScriptServer::is_global_class(p_type)) { Ref