diff options
Diffstat (limited to 'editor/create_dialog.cpp')
-rw-r--r-- | editor/create_dialog.cpp | 796 |
1 files changed, 358 insertions, 438 deletions
diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp index 75fff03297..027cee3f1c 100644 --- a/editor/create_dialog.cpp +++ b/editor/create_dialog.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -30,60 +30,40 @@ #include "create_dialog.h" -#include "core/class_db.h" +#include "core/object/class_db.h" #include "core/os/keyboard.h" -#include "core/print_string.h" #include "editor_feature_profile.h" -#include "editor_help.h" #include "editor_node.h" #include "editor_scale.h" #include "editor_settings.h" -#include "scene/gui/box_container.h" void CreateDialog::popup_create(bool p_dont_clear, bool p_replace_mode, const String &p_select_type) { - type_list.clear(); - ClassDB::get_class_list(&type_list); - ScriptServer::get_global_class_list(&type_list); - type_list.sort_custom<StringName::AlphCompare>(); - - recent->clear(); - - FileAccess *f = FileAccess::open(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("create_recent." + base_type), FileAccess::READ); + _fill_type_list(); - if (f) { - TreeItem *root = recent->create_item(); - - while (!f->eof_reached()) { - String l = f->get_line().strip_edges(); - String name = l.split(" ")[0]; - if ((ClassDB::class_exists(name) || ScriptServer::is_global_class(name)) && !_is_class_disabled_by_feature_profile(name)) { - TreeItem *ti = recent->create_item(root); - ti->set_text(0, l); - ti->set_icon(0, EditorNode::get_singleton()->get_class_icon(l, base_type)); - } - } + icon_fallback = search_options->has_theme_icon(base_type, "EditorIcons") ? base_type : "Object"; - memdelete(f); + if (p_dont_clear) { + search_box->select_all(); + } else { + search_box->clear(); } - favorites->clear(); - - f = FileAccess::open(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("favorites." + base_type), FileAccess::READ); - - favorite_list.clear(); - - if (f) { - while (!f->eof_reached()) { - String l = f->get_line().strip_edges(); + if (p_replace_mode) { + search_box->set_text(p_select_type); + } - if (l != String()) { - favorite_list.push_back(l); - } - } + search_box->grab_focus(); + _update_search(); - memdelete(f); + if (p_replace_mode) { + set_title(vformat(TTR("Change %s Type"), base_type)); + get_ok_button()->set_text(TTR("Change")); + } else { + set_title(vformat(TTR("Create New %s"), base_type)); + get_ok_button()->set_text(TTR("Create")); } + _load_favorites_and_history(); _save_and_update_favorite_list(); // Restore valid window bounds or pop up at default size. @@ -93,357 +73,266 @@ void CreateDialog::popup_create(bool p_dont_clear, bool p_replace_mode, const St } else { popup_centered_clamped(Size2(900, 700) * EDSCALE, 0.8); } +} - if (p_dont_clear) { - search_box->select_all(); - } else { - search_box->clear(); - } +void CreateDialog::_fill_type_list() { + List<StringName> complete_type_list; + ClassDB::get_class_list(&complete_type_list); + ScriptServer::get_global_class_list(&complete_type_list); - search_box->grab_focus(); + EditorData &ed = EditorNode::get_editor_data(); - _update_search(); + for (List<StringName>::Element *I = complete_type_list.front(); I; I = I->next()) { + String type = I->get(); + if (!_should_hide_type(type)) { + type_list.push_back(type); - is_replace_mode = p_replace_mode; + if (!ed.get_custom_types().has(type)) { + continue; + } - if (p_replace_mode) { - select_type(p_select_type); - set_title(vformat(TTR("Change %s Type"), base_type)); - get_ok()->set_text(TTR("Change")); - } else { - set_title(vformat(TTR("Create New %s"), base_type)); - get_ok()->set_text(TTR("Create")); + const Vector<EditorData::CustomType> &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<StringName::AlphCompare>(); } -void CreateDialog::_text_changed(const String &p_newtext) { - _update_search(); +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); } -void CreateDialog::_sbox_input(const Ref<InputEvent> &p_ie) { - Ref<InputEventKey> k = p_ie; - if (k.is_valid() && (k->get_keycode() == KEY_UP || - k->get_keycode() == KEY_DOWN || - k->get_keycode() == KEY_PAGEUP || - k->get_keycode() == KEY_PAGEDOWN)) { - search_options->call("_gui_input", k); - search_box->accept_event(); - } +bool CreateDialog::_is_class_disabled_by_feature_profile(const StringName &p_class) const { + Ref<EditorFeatureProfile> profile = EditorFeatureProfileManager::get_singleton()->get_current_profile(); + + return !profile.is_null() && profile->is_class_disabled(p_class); } -void CreateDialog::add_type(const String &p_type, HashMap<String, TreeItem *> &p_types, TreeItem *p_root, TreeItem **to_select) { - if (p_types.has(p_type)) { - return; +bool CreateDialog::_should_hide_type(const String &p_type) const { + if (_is_class_disabled_by_feature_profile(p_type)) { + return true; } - bool cpp_type = ClassDB::class_exists(p_type); - EditorData &ed = EditorNode::get_editor_data(); + if (base_type == "Node" && p_type.begins_with("Editor")) { + return true; // Do not show editor nodes. + } if (p_type == base_type) { - return; + return true; // Root is already added. } - if (cpp_type) { + if (ClassDB::class_exists(p_type)) { + if (!ClassDB::can_instantiate(p_type)) { + return true; // Can't create abstract class. + } + if (!ClassDB::is_parent_class(p_type, base_type)) { - return; + return true; // Wrong inheritance. + } + + for (Set<StringName>::Element *E = type_blacklist.front(); E; E = E->next()) { + if (ClassDB::is_parent_class(p_type, E->get())) { + return true; // Parent type is blacklisted. + } } } else { - if (!search_loaded_scripts.has(p_type)) { - search_loaded_scripts[p_type] = ed.script_class_load_script(p_type); + if (!EditorNode::get_editor_data().script_class_is_parent(p_type, base_type)) { + return true; // Wrong inheritance. } - - if (!ScriptServer::is_global_class(p_type) || !ed.script_class_is_parent(p_type, base_type)) { - return; + if (!ScriptServer::is_global_class(p_type)) { + return true; } String script_path = ScriptServer::get_global_class_path(p_type); - if (script_path.find("res://addons/", 0) != -1) { + if (script_path.begins_with("res://addons/")) { if (!EditorNode::get_singleton()->is_addon_plugin_enabled(script_path.get_slicec('/', 3))) { - return; + return true; // Plugin is not enabled. } } } - String inherits = cpp_type ? ClassDB::get_parent_class(p_type) : ed.script_class_get_base(p_type); - - TreeItem *parent = p_root; - - if (inherits.length()) { - if (!p_types.has(inherits)) { - add_type(inherits, p_types, p_root, to_select); - } + return false; +} - if (p_types.has(inherits)) { - parent = p_types[inherits]; - } else if (ScriptServer::is_global_class(inherits)) { - return; - } - } +void CreateDialog::_update_search() { + search_options->clear(); + search_options_types.clear(); - bool can_instance = (cpp_type && ClassDB::can_instance(p_type)) || ScriptServer::is_global_class(p_type); + TreeItem *root = search_options->create_item(); + root->set_text(0, base_type); + root->set_icon(0, search_options->get_theme_icon(icon_fallback, "EditorIcons")); + search_options_types[base_type] = root; + _configure_search_option_item(root, base_type, ClassDB::class_exists(base_type)); - TreeItem *item = search_options->create_item(parent); - if (cpp_type) { - item->set_text(0, p_type); - } else { - item->set_metadata(0, p_type); - item->set_text(0, p_type + " (" + ScriptServer::get_global_class_path(p_type).get_file() + ")"); - } - if (!can_instance) { - item->set_custom_color(0, search_options->get_theme_color("disabled_font_color", "Editor")); - item->set_selectable(0, false); - } else if (!(*to_select && (*to_select)->get_text(0) == search_box->get_text())) { - String search_term = search_box->get_text().to_lower(); - - // if the node name matches exactly as the search, the node should be selected. - // this also fixes when the user clicks on recent nodes. - if (p_type.to_lower() == search_term) { - *to_select = item; - } else { - bool current_type_prefered = _is_type_prefered(p_type); - bool selected_type_prefered = *to_select ? _is_type_prefered((*to_select)->get_text(0).split(" ")[0]) : false; - - bool is_subsequence_of_type = search_box->get_text().is_subsequence_ofi(p_type); - bool is_substring_of_type = p_type.to_lower().find(search_term) >= 0; - bool is_substring_of_selected = false; - bool is_subsequence_of_selected = false; - bool is_selected_equal = false; - - if (*to_select) { - String name = (*to_select)->get_text(0).split(" ")[0].to_lower(); - is_substring_of_selected = name.find(search_term) >= 0; - is_subsequence_of_selected = search_term.is_subsequence_of(name); - is_selected_equal = name == search_term; - } + const String search_text = search_box->get_text(); + bool empty_search = search_text == ""; - if (is_subsequence_of_type && !is_selected_equal) { - if (is_substring_of_type) { - if (!is_substring_of_selected || (current_type_prefered && !selected_type_prefered)) { - *to_select = item; - } - } else { - // substring results weigh more than subsequences, so let's make sure we don't override them - if (!is_substring_of_selected) { - if (!is_subsequence_of_selected || (current_type_prefered && !selected_type_prefered)) { - *to_select = item; - } - } - } - } + // Filter all candidate results. + Vector<String> candidates; + for (List<StringName>::Element *I = type_list.front(); I; I = I->next()) { + if (empty_search || search_text.is_subsequence_ofi(I->get())) { + candidates.push_back(I->get()); } } - if (bool(EditorSettings::get_singleton()->get("docks/scene_tree/start_create_dialog_fully_expanded"))) { - item->set_collapsed(false); - } else { - // don't collapse search results - bool collapse = (search_box->get_text() == ""); - // don't collapse the root node - collapse &= (item != p_root); - // don't collapse abstract nodes on the first tree level - collapse &= ((parent != p_root) || (can_instance)); - item->set_collapsed(collapse); + // Build the type tree. + for (int i = 0; i < candidates.size(); i++) { + _add_type(candidates[i], ClassDB::class_exists(candidates[i])); } - const String &description = DTR(EditorHelp::get_doc_data()->class_list[p_type].brief_description); - item->set_tooltip(0, description); - - item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_type, base_type)); - - p_types[p_type] = item; -} - -bool CreateDialog::_is_type_prefered(const String &type) { - bool cpp_type = ClassDB::class_exists(type); - EditorData &ed = EditorNode::get_editor_data(); - - if (cpp_type) { - return ClassDB::is_parent_class(type, preferred_search_result_type); + // Select the best result. + if (empty_search) { + select_type(base_type); + } else if (candidates.size() > 0) { + select_type(_top_result(candidates, search_text)); + } else { + favorite->set_disabled(true); + help_bit->set_text(""); + get_ok_button()->set_disabled(true); + search_options->deselect_all(); } - return ed.script_class_is_parent(type, preferred_search_result_type); } -bool CreateDialog::_is_class_disabled_by_feature_profile(const StringName &p_class) { - Ref<EditorFeatureProfile> profile = EditorFeatureProfileManager::get_singleton()->get_current_profile(); - if (profile.is_null()) { - return false; - } - - return profile->is_class_disabled(p_class); -} - -void CreateDialog::select_type(const String &p_type) { - TreeItem *to_select; +void CreateDialog::_add_type(const String &p_type, bool p_cpp_type) { if (search_options_types.has(p_type)) { - to_select = search_options_types[p_type]; - } else { - to_select = search_options->get_root(); + return; } - // uncollapse from selected type to top level - // TODO: should this be in tree? - TreeItem *cur = to_select; - while (cur) { - cur->set_collapsed(false); - cur = cur->get_parent(); + String inherits; + if (p_cpp_type) { + inherits = ClassDB::get_parent_class(p_type); + } else if (ScriptServer::is_global_class(p_type)) { + inherits = EditorNode::get_editor_data().script_class_get_base(p_type); + } else { + inherits = custom_type_parents[p_type]; } - to_select->select(0); + _add_type(inherits, p_cpp_type || ClassDB::class_exists(inherits)); - search_options->scroll_to_item(to_select); + TreeItem *item = search_options->create_item(search_options_types[inherits]); + search_options_types[p_type] = item; + _configure_search_option_item(item, p_type, p_cpp_type); } -void CreateDialog::_update_search() { - search_options->clear(); - favorite->set_disabled(true); - - help_bit->set_text(""); - - search_options_types.clear(); - - TreeItem *root = search_options->create_item(); - EditorData &ed = EditorNode::get_editor_data(); - - root->set_text(0, base_type); - if (search_options->has_theme_icon(base_type, "EditorIcons")) { - root->set_icon(0, search_options->get_theme_icon(base_type, "EditorIcons")); +void CreateDialog::_configure_search_option_item(TreeItem *r_item, const String &p_type, const bool p_cpp_type) { + bool script_type = ScriptServer::is_global_class(p_type); + if (p_cpp_type) { + r_item->set_text(0, p_type); + } else if (script_type) { + r_item->set_metadata(0, p_type); + r_item->set_text(0, p_type + " (" + ScriptServer::get_global_class_path(p_type).get_file() + ")"); + } else { + r_item->set_metadata(0, custom_type_parents[p_type]); + r_item->set_text(0, p_type); } - TreeItem *to_select = search_box->get_text() == base_type ? root : nullptr; + bool can_instantiate = (p_cpp_type && ClassDB::can_instantiate(p_type)) || !p_cpp_type; + if (!can_instantiate) { + r_item->set_custom_color(0, search_options->get_theme_color("disabled_font_color", "Editor")); + r_item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_type, "NodeDisabled")); + r_item->set_selectable(0, false); + } else { + r_item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_type, icon_fallback)); + } - for (List<StringName>::Element *I = type_list.front(); I; I = I->next()) { - String type = I->get(); + if (search_box->get_text() != "") { + r_item->set_collapsed(false); + } else { + // Don't collapse the root node or an abstract node on the first tree level. + bool should_collapse = p_type != base_type && (r_item->get_parent()->get_text(0) != base_type || can_instantiate); - if (_is_class_disabled_by_feature_profile(type)) { - continue; + if (should_collapse && bool(EditorSettings::get_singleton()->get("docks/scene_tree/start_create_dialog_fully_expanded"))) { + should_collapse = false; // Collapse all nodes anyway. } - bool cpp_type = ClassDB::class_exists(type); + r_item->set_collapsed(should_collapse); + } - if (base_type == "Node" && type.begins_with("Editor")) { - continue; // do not show editor nodes - } + const String &description = DTR(EditorHelp::get_doc_data()->class_list[p_type].brief_description); + r_item->set_tooltip(0, description); - if (cpp_type && !ClassDB::can_instance(type)) { - continue; // can't create what can't be instanced + if (!p_cpp_type && !script_type) { + Ref<Texture2D> icon = EditorNode::get_editor_data().get_custom_types()[custom_type_parents[p_type]][custom_type_indices[p_type]].icon; + if (icon.is_valid()) { + r_item->set_icon(0, icon); } + } +} - if (cpp_type) { - bool skip = false; - - for (Set<StringName>::Element *E = type_blacklist.front(); E && !skip; E = E->next()) { - if (ClassDB::is_parent_class(type, E->get())) { - skip = true; - } - } - if (skip) { - continue; - } +String CreateDialog::_top_result(const Vector<String> p_candidates, const String &p_search_text) const { + float highest_score = 0; + int highest_index = 0; + for (int i = 0; i < p_candidates.size(); i++) { + float score = _score_type(p_candidates[i].get_slicec(' ', 0), p_search_text); + if (score > highest_score) { + highest_score = score; + highest_index = i; } + } - if (search_box->get_text() == "") { - add_type(type, search_options_types, root, &to_select); - } else { - bool found = false; - String type2 = type; + return p_candidates[highest_index]; +} - if (!cpp_type && !search_loaded_scripts.has(type)) { - search_loaded_scripts[type] = ed.script_class_load_script(type); - } +float CreateDialog::_score_type(const String &p_type, const String &p_search) const { + float inverse_length = 1.f / float(p_type.length()); - while (type2 != "" && (cpp_type ? ClassDB::is_parent_class(type2, base_type) : ed.script_class_is_parent(type2, base_type)) && type2 != base_type) { - if (search_box->get_text().is_subsequence_ofi(type2)) { - found = true; - break; - } + // Favor types where search term is a substring close to the start of the type. + float w = 0.5f; + int pos = p_type.findn(p_search); + float score = (pos > -1) ? 1.0f - w * MIN(1, 3 * pos * inverse_length) : MAX(0.f, .9f - w); - type2 = cpp_type ? ClassDB::get_parent_class(type2) : ed.script_class_get_base(type2); + // Favor shorter items: they resemble the search term more. + w = 0.1f; + score *= (1 - w) + w * (p_search.length() * inverse_length); - if (!cpp_type && !search_loaded_scripts.has(type2)) { - search_loaded_scripts[type2] = ed.script_class_load_script(type2); - } - } + score *= _is_type_preferred(p_type) ? 1.0f : 0.8f; - if (found) { - add_type(type, search_options_types, root, &to_select); - } - } + // Add score for being a favorite type. + score *= (favorite_list.find(p_type) > -1) ? 1.0f : 0.7f; - if (EditorNode::get_editor_data().get_custom_types().has(type) && ClassDB::is_parent_class(type, base_type)) { - //there are custom types based on this... cool. - - const Vector<EditorData::CustomType> &ct = EditorNode::get_editor_data().get_custom_types()[type]; - for (int i = 0; i < ct.size(); i++) { - bool show = search_box->get_text().is_subsequence_ofi(ct[i].name); - - if (!show) { - continue; - } - - if (!search_options_types.has(type)) { - add_type(type, search_options_types, root, &to_select); - } - - TreeItem *ti; - if (search_options_types.has(type)) { - ti = search_options_types[type]; - } else { - ti = search_options->get_root(); - } - - TreeItem *item = search_options->create_item(ti); - item->set_metadata(0, type); - item->set_text(0, ct[i].name); - if (ct[i].icon.is_valid()) { - item->set_icon(0, ct[i].icon); - } - - if (!to_select || ct[i].name == search_box->get_text()) { - to_select = item; - } - } + // Look through at most 5 recent items + bool in_recent = false; + for (int i = 0; i < MIN(5, recent->get_item_count()); i++) { + if (recent->get_item_text(i) == p_type) { + in_recent = true; + break; } } + score *= in_recent ? 1.0f : 0.8f; - if (search_box->get_text() == "") { - to_select = root; - } - - if (to_select) { - to_select->select(0); - search_options->scroll_to_item(to_select); - favorite->set_disabled(false); - favorite->set_pressed(favorite_list.find(to_select->get_text(0)) != -1); - } + return score; +} - get_ok()->set_disabled(root->get_children() == nullptr); +void CreateDialog::_cleanup() { + type_list.clear(); + favorite_list.clear(); + favorites->clear(); + recent->clear(); + custom_type_parents.clear(); + custom_type_indices.clear(); } void CreateDialog::_confirmed() { - TreeItem *ti = search_options->get_selected(); - if (!ti) { + String selected_item = get_selected_type(); + if (selected_item == String()) { return; } FileAccess *f = FileAccess::open(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("create_recent." + base_type), FileAccess::WRITE); - if (f) { - f->store_line(get_selected_type()); - TreeItem *t = recent->get_root(); - if (t) { - t = t->get_children(); - } - int count = 0; - while (t) { - if (t->get_text(0) != get_selected_type()) { - f->store_line(t->get_text(0)); - } + f->store_line(selected_item); - if (count > 32) { - //limit it to 32 entries.. - break; + for (int i = 0; i < MIN(32, recent->get_item_count()); i++) { + if (recent->get_item_text(i) != selected_item) { + f->store_line(recent->get_item_text(i)); } - t = t->get_next(); - count++; } memdelete(f); @@ -451,6 +340,26 @@ void CreateDialog::_confirmed() { emit_signal("create"); hide(); + _cleanup(); +} + +void CreateDialog::_text_changed(const String &p_newtext) { + _update_search(); +} + +void CreateDialog::_sbox_input(const Ref<InputEvent> &p_ie) { + Ref<InputEventKey> k = p_ie; + if (k.is_valid()) { + switch (k->get_keycode()) { + case KEY_UP: + case KEY_DOWN: + case KEY_PAGEUP: + case KEY_PAGEDOWN: { + search_options->call("_gui_input", k); + search_box->accept_event(); + } break; + } + } } void CreateDialog::_notification(int p_what) { @@ -470,97 +379,90 @@ void CreateDialog::_notification(int p_what) { search_box->select_all(); } else { EditorSettings::get_singleton()->get_project_metadata("dialog_bounds", "create_new_node", Rect2(get_position(), get_size())); - search_loaded_scripts.clear(); } } break; } } -void CreateDialog::set_base_type(const String &p_base) { - base_type = p_base; - if (is_replace_mode) { - set_title(vformat(TTR("Change %s Type"), p_base)); - } else { - set_title(vformat(TTR("Create New %s"), p_base)); +void CreateDialog::select_type(const String &p_type) { + if (!search_options_types.has(p_type)) { + return; } - _update_search(); -} - -String CreateDialog::get_base_type() const { - return base_type; -} + TreeItem *to_select = search_options_types[p_type]; + to_select->select(0); + search_options->scroll_to_item(to_select); -void CreateDialog::set_preferred_search_result_type(const String &p_preferred_type) { - preferred_search_result_type = p_preferred_type; -} + if (EditorHelp::get_doc_data()->class_list.has(p_type)) { + help_bit->set_text(DTR(EditorHelp::get_doc_data()->class_list[p_type].brief_description)); + } -String CreateDialog::get_preferred_search_result_type() { - return preferred_search_result_type; + favorite->set_disabled(false); + favorite->set_pressed(favorite_list.find(p_type) != -1); + get_ok_button()->set_disabled(false); } String CreateDialog::get_selected_type() { TreeItem *selected = search_options->get_selected(); - if (selected) { - return selected->get_text(0); - } else { + if (!selected) { return String(); } + + return selected->get_text(0); } -Object *CreateDialog::instance_selected() { +Variant CreateDialog::instance_selected() { TreeItem *selected = search_options->get_selected(); - if (selected) { - Variant md = selected->get_metadata(0); - - String custom; - if (md.get_type() != Variant::NIL) { - custom = md; - } + if (!selected) { + return Variant(); + } - if (custom != String()) { - if (ScriptServer::is_global_class(custom)) { - Object *obj = EditorNode::get_editor_data().script_class_instance(custom); - Node *n = Object::cast_to<Node>(obj); - if (n) { - n->set_name(custom); - } - return obj; + Variant md = selected->get_metadata(0); + Variant obj; + if (md.get_type() != Variant::NIL) { + String custom = md; + if (ScriptServer::is_global_class(custom)) { + obj = EditorNode::get_editor_data().script_class_instance(custom); + Node *n = Object::cast_to<Node>(obj); + if (n) { + n->set_name(custom); } - return EditorNode::get_editor_data().instance_custom_type(selected->get_text(0), custom); } else { - return ClassDB::instance(selected->get_text(0)); + obj = EditorNode::get_editor_data().instance_custom_type(selected->get_text(0), custom); } + } else { + obj = ClassDB::instantiate(selected->get_text(0)); } - return nullptr; -} - -void CreateDialog::_item_selected() { - TreeItem *item = search_options->get_selected(); - if (!item) { - return; - } - - String name = item->get_text(0); - - favorite->set_disabled(false); - favorite->set_pressed(favorite_list.find(name) != -1); + // Check if any Object-type property should be instantiated. + List<PropertyInfo> pinfo; + ((Object *)obj)->get_property_list(&pinfo); - if (!EditorHelp::get_doc_data()->class_list.has(name)) { - return; + for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) { + PropertyInfo pi = E->get(); + if (pi.type == Variant::OBJECT && pi.usage & PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT) { + Object *prop = ClassDB::instantiate(pi.class_name); + ((Object *)obj)->set(pi.name, prop); + } } - help_bit->set_text(DTR(EditorHelp::get_doc_data()->class_list[name].brief_description)); + return obj; +} - get_ok()->set_disabled(false); +void CreateDialog::_item_selected() { + String name = get_selected_type(); + select_type(name); } void CreateDialog::_hide_requested() { _cancel_pressed(); // From AcceptDialog. } +void CreateDialog::cancel_pressed() { + _cleanup(); +} + void CreateDialog::_favorite_toggled() { TreeItem *item = search_options->get_selected(); if (!item) { @@ -580,45 +482,8 @@ void CreateDialog::_favorite_toggled() { _save_and_update_favorite_list(); } -void CreateDialog::_save_favorite_list() { - FileAccess *f = FileAccess::open(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("favorites." + base_type), FileAccess::WRITE); - - if (f) { - for (int i = 0; i < favorite_list.size(); i++) { - String l = favorite_list[i]; - String name = l.split(" ")[0]; - if (!(ClassDB::class_exists(name) || ScriptServer::is_global_class(name))) { - continue; - } - f->store_line(l); - } - memdelete(f); - } -} - -void CreateDialog::_update_favorite_list() { - favorites->clear(); - TreeItem *root = favorites->create_item(); - for (int i = 0; i < favorite_list.size(); i++) { - String l = favorite_list[i]; - String name = l.split(" ")[0]; - if (!((ClassDB::class_exists(name) || ScriptServer::is_global_class(name)) && !_is_class_disabled_by_feature_profile(name))) { - continue; - } - TreeItem *ti = favorites->create_item(root); - ti->set_text(0, l); - ti->set_icon(0, EditorNode::get_singleton()->get_class_icon(l, base_type)); - } - emit_signal("favorites_updated"); -} - -void CreateDialog::_history_selected() { - TreeItem *item = recent->get_selected(); - if (!item) { - return; - } - - search_box->set_text(item->get_text(0).get_slicec(' ', 0)); +void CreateDialog::_history_selected(int p_idx) { + search_box->set_text(recent->get_item_text(p_idx).get_slicec(' ', 0)); favorites->deselect_all(); _update_search(); } @@ -634,8 +499,8 @@ void CreateDialog::_favorite_selected() { _update_search(); } -void CreateDialog::_history_activated() { - _history_selected(); +void CreateDialog::_history_activated(int p_idx) { + _history_selected(p_idx); _confirmed(); } @@ -651,7 +516,8 @@ Variant CreateDialog::get_drag_data_fw(const Point2 &p_point, Control *p_from) { d["type"] = "create_favorite_drag"; d["class"] = ti->get_text(0); - ToolButton *tb = memnew(ToolButton); + Button *tb = memnew(Button); + tb->set_flat(true); tb->set_icon(ti->get_icon(0)); tb->set_text(ti->get_text(0)); favorites->set_drag_preview(tb); @@ -717,23 +583,80 @@ void CreateDialog::drop_data_fw(const Point2 &p_point, const Variant &p_data, Co } void CreateDialog::_save_and_update_favorite_list() { - _save_favorite_list(); - _update_favorite_list(); + favorites->clear(); + TreeItem *root = favorites->create_item(); + + FileAccess *f = FileAccess::open(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("favorites." + base_type), FileAccess::WRITE); + if (f) { + for (int i = 0; i < favorite_list.size(); i++) { + String l = favorite_list[i]; + String name = l.get_slicec(' ', 0); + if (!(ClassDB::class_exists(name) || ScriptServer::is_global_class(name))) { + continue; + } + f->store_line(l); + + if (_is_class_disabled_by_feature_profile(name)) { + continue; + } + + TreeItem *ti = favorites->create_item(root); + ti->set_text(0, l); + ti->set_icon(0, EditorNode::get_singleton()->get_class_icon(name, icon_fallback)); + } + memdelete(f); + } + + emit_signal("favorites_updated"); +} + +void CreateDialog::_load_favorites_and_history() { + String dir = EditorSettings::get_singleton()->get_project_settings_dir(); + FileAccess *f = FileAccess::open(dir.plus_file("create_recent." + base_type), FileAccess::READ); + if (f) { + while (!f->eof_reached()) { + String l = f->get_line().strip_edges(); + String name = l.get_slicec(' ', 0); + + if ((ClassDB::class_exists(name) || ScriptServer::is_global_class(name)) && !_is_class_disabled_by_feature_profile(name)) { + recent->add_item(l, EditorNode::get_singleton()->get_class_icon(name, icon_fallback)); + } + } + + memdelete(f); + } + + f = FileAccess::open(dir.plus_file("favorites." + base_type), FileAccess::READ); + if (f) { + while (!f->eof_reached()) { + String l = f->get_line().strip_edges(); + + if (l != String()) { + favorite_list.push_back(l); + } + } + + memdelete(f); + } } void CreateDialog::_bind_methods() { ClassDB::bind_method(D_METHOD("_save_and_update_favorite_list"), &CreateDialog::_save_and_update_favorite_list); - ClassDB::bind_method("get_drag_data_fw", &CreateDialog::get_drag_data_fw); - ClassDB::bind_method("can_drop_data_fw", &CreateDialog::can_drop_data_fw); - ClassDB::bind_method("drop_data_fw", &CreateDialog::drop_data_fw); + ClassDB::bind_method("_get_drag_data_fw", &CreateDialog::get_drag_data_fw); + ClassDB::bind_method("_can_drop_data_fw", &CreateDialog::can_drop_data_fw); + ClassDB::bind_method("_drop_data_fw", &CreateDialog::drop_data_fw); ADD_SIGNAL(MethodInfo("create")); ADD_SIGNAL(MethodInfo("favorites_updated")); } CreateDialog::CreateDialog() { - is_replace_mode = false; + base_type = "Object"; + preferred_search_result_type = ""; + + type_blacklist.insert("PluginScript"); // PluginScript must be initialized before use, which is not possible here. + type_blacklist.insert("ScriptCreateDialog"); // This is an exposed editor Node that doesn't have an Editor prefix. HSplitContainer *hsc = memnew(HSplitContainer); add_child(hsc); @@ -742,67 +665,64 @@ CreateDialog::CreateDialog() { hsc->add_child(vsc); VBoxContainer *fav_vb = memnew(VBoxContainer); - vsc->add_child(fav_vb); fav_vb->set_custom_minimum_size(Size2(150, 100) * EDSCALE); fav_vb->set_v_size_flags(Control::SIZE_EXPAND_FILL); + vsc->add_child(fav_vb); favorites = memnew(Tree); - fav_vb->add_margin_child(TTR("Favorites:"), favorites, true); favorites->set_hide_root(true); favorites->set_hide_folding(true); favorites->set_allow_reselect(true); favorites->connect("cell_selected", callable_mp(this, &CreateDialog::_favorite_selected)); favorites->connect("item_activated", callable_mp(this, &CreateDialog::_favorite_activated)); + favorites->add_theme_constant_override("draw_guides", 1); #ifndef _MSC_VER -#warning cant forward drag data to a non control, must be fixed +#warning cannot forward drag data to a non control, must be fixed #endif //favorites->set_drag_forwarding(this); - favorites->add_theme_constant_override("draw_guides", 1); + fav_vb->add_margin_child(TTR("Favorites:"), favorites, true); VBoxContainer *rec_vb = memnew(VBoxContainer); vsc->add_child(rec_vb); rec_vb->set_custom_minimum_size(Size2(150, 100) * EDSCALE); rec_vb->set_v_size_flags(Control::SIZE_EXPAND_FILL); - recent = memnew(Tree); + recent = memnew(ItemList); rec_vb->add_margin_child(TTR("Recent:"), recent, true); - recent->set_hide_root(true); - recent->set_hide_folding(true); recent->set_allow_reselect(true); - recent->connect("cell_selected", callable_mp(this, &CreateDialog::_history_selected)); + recent->connect("item_selected", callable_mp(this, &CreateDialog::_history_selected)); recent->connect("item_activated", callable_mp(this, &CreateDialog::_history_activated)); recent->add_theme_constant_override("draw_guides", 1); VBoxContainer *vbc = memnew(VBoxContainer); - hsc->add_child(vbc); vbc->set_custom_minimum_size(Size2(300, 0) * EDSCALE); vbc->set_h_size_flags(Control::SIZE_EXPAND_FILL); - HBoxContainer *search_hb = memnew(HBoxContainer); + hsc->add_child(vbc); + search_box = memnew(LineEdit); search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL); + search_box->connect("text_changed", callable_mp(this, &CreateDialog::_text_changed)); + search_box->connect("gui_input", callable_mp(this, &CreateDialog::_sbox_input)); + + HBoxContainer *search_hb = memnew(HBoxContainer); search_hb->add_child(search_box); + favorite = memnew(Button); - favorite->set_flat(true); favorite->set_toggle_mode(true); - search_hb->add_child(favorite); + favorite->set_tooltip(TTR("(Un)favorite selected item.")); favorite->connect("pressed", callable_mp(this, &CreateDialog::_favorite_toggled)); + search_hb->add_child(favorite); vbc->add_margin_child(TTR("Search:"), search_hb); - search_box->connect("text_changed", callable_mp(this, &CreateDialog::_text_changed)); - search_box->connect("gui_input", callable_mp(this, &CreateDialog::_sbox_input)); + search_options = memnew(Tree); - vbc->add_margin_child(TTR("Matches:"), search_options, true); - get_ok()->set_disabled(true); - register_text_enter(search_box); - set_hide_on_ok(false); search_options->connect("item_activated", callable_mp(this, &CreateDialog::_confirmed)); search_options->connect("cell_selected", callable_mp(this, &CreateDialog::_item_selected)); - base_type = "Object"; - preferred_search_result_type = ""; + vbc->add_margin_child(TTR("Matches:"), search_options, true); help_bit = memnew(EditorHelpBit); - vbc->add_margin_child(TTR("Description:"), help_bit); help_bit->connect("request_hide", callable_mp(this, &CreateDialog::_hide_requested)); + vbc->add_margin_child(TTR("Description:"), help_bit); - type_blacklist.insert("PluginScript"); // PluginScript must be initialized before use, which is not possible here - type_blacklist.insert("ScriptCreateDialog"); // This is an exposed editor Node that doesn't have an Editor prefix. + register_text_enter(search_box); + set_hide_on_ok(false); } |