diff options
Diffstat (limited to 'editor/create_dialog.cpp')
-rw-r--r-- | editor/create_dialog.cpp | 866 |
1 files changed, 386 insertions, 480 deletions
diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp index 4adb3844bc..1e3dc01112 100644 --- a/editor/create_dialog.cpp +++ b/editor/create_dialog.cpp @@ -32,70 +32,15 @@ #include "core/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) { + _fill_type_list(); - 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); - - 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)); - } - } - - memdelete(f); - } - - 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 (l != String()) { - favorite_list.push_back(l); - } - } - - memdelete(f); - } - - _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); - } + icon_fallback = search_options->has_theme_icon(base_type, "EditorIcons") ? base_type : "Object"; if (p_dont_clear) { search_box->select_all(); @@ -103,349 +48,288 @@ void CreateDialog::popup_create(bool p_dont_clear, bool p_replace_mode, const St search_box->clear(); } - search_box->grab_focus(); + if (p_replace_mode) { + search_box->set_text(p_select_type); + } + search_box->grab_focus(); _update_search(); - is_replace_mode = p_replace_mode; - 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")); } -} - -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() && (k->get_scancode() == KEY_UP || - k->get_scancode() == KEY_DOWN || - k->get_scancode() == KEY_PAGEUP || - k->get_scancode() == KEY_PAGEDOWN)) { + _load_favorites_and_history(); + _save_and_update_favorite_list(); - search_options->call("_gui_input", k); - search_box->accept_event(); + // 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::add_type(const String &p_type, HashMap<String, TreeItem *> &p_types, TreeItem *p_root, TreeItem **to_select) { - - if (p_types.has(p_type)) - return; +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); - bool cpp_type = ClassDB::class_exists(p_type); EditorData &ed = EditorNode::get_editor_data(); - if (p_type == base_type) - return; - - if (cpp_type) { - if (!ClassDB::is_parent_class(p_type, base_type)) - return; - } else { - if (!search_loaded_scripts.has(p_type)) { - search_loaded_scripts[p_type] = ed.script_class_load_script(p_type); - } - - if (!ScriptServer::is_global_class(p_type) || !ed.script_class_is_parent(p_type, base_type)) - return; - - String script_path = ScriptServer::get_global_class_path(p_type); - if (script_path.find("res://addons/", 0) != -1) { - if (!EditorNode::get_singleton()->is_addon_plugin_enabled(script_path.get_slicec('/', 3))) - return; - } - } - - 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); - } - - if (p_types.has(inherits)) - parent = p_types[inherits]; - else if (ScriptServer::is_global_class(inherits)) - return; - } - - bool can_instance = (cpp_type && ClassDB::can_instance(p_type)) || ScriptServer::is_global_class(p_type); + 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); - 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, get_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; + if (!ed.get_custom_types().has(type)) { + continue; } - 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; - } - } - } + 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>(); +} - 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); +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); } - const String &description = EditorHelp::get_doc_data()->class_list[p_type].brief_description; - item->set_tooltip(0, description); + return EditorNode::get_editor_data().script_class_is_parent(p_type, preferred_search_result_type); +} - item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_type, base_type)); +bool CreateDialog::_is_class_disabled_by_feature_profile(const StringName &p_class) const { + Ref<EditorFeatureProfile> profile = EditorFeatureProfileManager::get_singleton()->get_current_profile(); - p_types[p_type] = item; + return !profile.is_null() && profile->is_class_disabled(p_class); } -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); +bool CreateDialog::_should_hide_type(const String &p_type) const { + if (_is_class_disabled_by_feature_profile(p_type)) { + return true; } - return ed.script_class_is_parent(type, preferred_search_result_type); -} -bool CreateDialog::_is_class_disabled_by_feature_profile(const StringName &p_class) { + if (base_type == "Node" && p_type.begins_with("Editor")) { + return true; // Do not show editor nodes. + } - Ref<EditorFeatureProfile> profile = EditorFeatureProfileManager::get_singleton()->get_current_profile(); - if (profile.is_null()) { - return false; + if (p_type == base_type) { + return true; // Root is already added. } - return profile->is_class_disabled(p_class); -} + if (ClassDB::class_exists(p_type)) { + if (!ClassDB::can_instance(p_type)) { + return true; // Can't create abstract class. + } -void CreateDialog::select_type(const String &p_type) { + if (!ClassDB::is_parent_class(p_type, base_type)) { + return true; // Wrong inheritance. + } - TreeItem *to_select; - if (search_options_types.has(p_type)) { - to_select = search_options_types[p_type]; + 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 { - to_select = search_options->get_root(); - } + if (!EditorNode::get_editor_data().script_class_is_parent(p_type, base_type)) { + return true; // Wrong inheritance. + } + if (!ScriptServer::is_global_class(p_type)) { + return true; + } - // 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 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. + } + } } - to_select->select(0); - - search_options->scroll_to_item(to_select); + return false; } 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 (has_icon(base_type, "EditorIcons")) { - root->set_icon(0, get_icon(base_type, "EditorIcons")); - } + root->set_icon(0, search_options->get_theme_icon(icon_fallback, "EditorIcons")); + search_options_types[base_type] = root; - TreeItem *to_select = search_box->get_text() == base_type ? root : NULL; + const String search_text = search_box->get_text(); + bool empty_search = search_text == ""; + // Filter all candidate results. + Vector<String> candidates; for (List<StringName>::Element *I = type_list.front(); I; I = I->next()) { - - String type = I->get(); - - if (_is_class_disabled_by_feature_profile(type)) { - continue; + if (empty_search || search_text.is_subsequence_ofi(I->get())) { + candidates.push_back(I->get()); } - bool cpp_type = ClassDB::class_exists(type); + } - if (base_type == "Node" && type.begins_with("Editor")) - continue; // do not show editor nodes + // Build the type tree. + for (int i = 0; i < candidates.size(); i++) { + _add_type(candidates[i], ClassDB::class_exists(candidates[i])); + } - if (cpp_type && !ClassDB::can_instance(type)) - continue; // can't create what can't be instanced + // 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()->set_disabled(true); + search_options->deselect_all(); + } +} - if (cpp_type) { - bool skip = false; +void CreateDialog::_add_type(const String &p_type, bool p_cpp_type) { + if (search_options_types.has(p_type)) { + return; + } - 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 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]; + } - if (search_box->get_text() == "") { - add_type(type, search_options_types, root, &to_select); - } else { + _add_type(inherits, p_cpp_type || ClassDB::class_exists(inherits)); - bool found = false; - String type2 = type; + 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); +} - if (!cpp_type && !search_loaded_scripts.has(type)) { - search_loaded_scripts[type] = ed.script_class_load_script(type); - } +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); + } - 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)) { + bool can_instance = (p_cpp_type && ClassDB::can_instance(p_type)) || !p_cpp_type; + if (!can_instance) { + r_item->set_custom_color(0, search_options->get_theme_color("disabled_font_color", "Editor")); + r_item->set_selectable(0, false); + } - found = true; - break; - } + 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_instance); - type2 = cpp_type ? ClassDB::get_parent_class(type2) : ed.script_class_get_base(type2); + if (should_collapse && bool(EditorSettings::get_singleton()->get("docks/scene_tree/start_create_dialog_fully_expanded"))) { + should_collapse = false; // Collapse all nodes anyway. + } + r_item->set_collapsed(should_collapse); + } - if (!cpp_type && !search_loaded_scripts.has(type2)) { - search_loaded_scripts[type2] = ed.script_class_load_script(type2); - } - } + const String &description = DTR(EditorHelp::get_doc_data()->class_list[p_type].brief_description); + r_item->set_tooltip(0, description); + r_item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_type, icon_fallback)); - if (found) { - add_type(type, search_options_types, root, &to_select); - } + 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 (EditorNode::get_editor_data().get_custom_types().has(type) && ClassDB::is_parent_class(type, base_type)) { - //there are custom types based on this... cool. +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; + } + } - const Vector<EditorData::CustomType> &ct = EditorNode::get_editor_data().get_custom_types()[type]; - for (int i = 0; i < ct.size(); i++) { + return p_candidates[highest_index]; +} - bool show = search_box->get_text().is_subsequence_ofi(ct[i].name); +float CreateDialog::_score_type(const String &p_type, const String &p_search) const { + float inverse_length = 1.f / float(p_type.length()); - if (!show) - continue; + // 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); - if (!search_options_types.has(type)) - add_type(type, search_options_types, root, &to_select); + // Favor shorter items: they resemble the search term more. + w = 0.1f; + score *= (1 - w) + w * (p_search.length() * inverse_length); - TreeItem *ti; - if (search_options_types.has(type)) - ti = search_options_types[type]; - else - ti = search_options->get_root(); + score *= _is_type_preferred(p_type) ? 1.0f : 0.8f; - 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); - } + // Add score for being a favorite type. + score *= (favorite_list.find(p_type) > -1) ? 1.0f : 0.7f; - 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() == NULL); +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); @@ -453,119 +337,134 @@ void CreateDialog::_confirmed() { emit_signal("create"); hide(); + _cleanup(); } -void CreateDialog::_notification(int p_what) { +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) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { - connect("confirmed", this, "_confirmed"); - search_box->set_right_icon(get_icon("Search", "EditorIcons")); + connect("confirmed", callable_mp(this, &CreateDialog::_confirmed)); + search_box->set_right_icon(search_options->get_theme_icon("Search", "EditorIcons")); search_box->set_clear_button_enabled(true); - favorite->set_icon(get_icon("Favorites", "EditorIcons")); + favorite->set_icon(search_options->get_theme_icon("Favorites", "EditorIcons")); } break; case NOTIFICATION_EXIT_TREE: { - disconnect("confirmed", this, "_confirmed"); + disconnect("confirmed", callable_mp(this, &CreateDialog::_confirmed)); } break; case NOTIFICATION_VISIBILITY_CHANGED: { - if (is_visible_in_tree()) { + if (is_visible()) { search_box->call_deferred("grab_focus"); // still not visible search_box->select_all(); + } else { + EditorSettings::get_singleton()->get_project_metadata("dialog_bounds", "create_new_node", Rect2(get_position(), get_size())); } } break; - case NOTIFICATION_POPUP_HIDE: { - EditorSettings::get_singleton()->get_project_metadata("dialog_bounds", "create_new_node", get_rect()); - 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)); - - _update_search(); -} +void CreateDialog::select_type(const String &p_type) { + if (!search_options_types.has(p_type)) { + return; + } -String CreateDialog::get_base_type() const { + TreeItem *to_select = search_options_types[p_type]; + to_select->select(0); + search_options->scroll_to_item(to_select); - return base_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)); + } -void CreateDialog::set_preferred_search_result_type(const String &p_preferred_type) { - preferred_search_result_type = p_preferred_type; + favorite->set_disabled(false); + favorite->set_pressed(favorite_list.find(p_type) != -1); + get_ok()->set_disabled(false); } -String CreateDialog::get_preferred_search_result_type() { - - return preferred_search_result_type; -} 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() { - 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 nullptr; + } - 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); + Object *obj = nullptr; + 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::instance(selected->get_text(0)); } - return NULL; -} - -void CreateDialog::_item_selected() { + // Check if any Object-type property should be instantiated. + List<PropertyInfo> pinfo; + obj->get_property_list(&pinfo); - TreeItem *item = search_options->get_selected(); - if (!item) - return; - - String name = item->get_text(0); + 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::instance(pi.class_name); + obj->set(pi.name, prop); + } + } - favorite->set_disabled(false); - favorite->set_pressed(favorite_list.find(name) != -1); + return obj; +} - if (!EditorHelp::get_doc_data()->class_list.has(name)) - return; +void CreateDialog::_item_selected() { + String name = get_selected_type(); + select_type(name); +} - help_bit->set_text(EditorHelp::get_doc_data()->class_list[name].brief_description); +void CreateDialog::_hide_requested() { + _cancel_pressed(); // From AcceptDialog. +} - get_ok()->set_disabled(false); +void CreateDialog::cancel_pressed() { + _cleanup(); } void CreateDialog::_favorite_toggled() { - TreeItem *item = search_options->get_selected(); - if (!item) + if (!item) { return; + } String name = item->get_text(0); @@ -580,85 +479,45 @@ 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(); } void CreateDialog::_favorite_selected() { - TreeItem *item = favorites->get_selected(); - if (!item) + if (!item) { return; + } search_box->set_text(item->get_text(0).get_slicec(' ', 0)); - recent->deselect_all(); + recent->unselect_all(); _update_search(); } -void CreateDialog::_history_activated() { - - _history_selected(); +void CreateDialog::_history_activated(int p_idx) { + _history_selected(p_idx); _confirmed(); } void CreateDialog::_favorite_activated() { - _favorite_selected(); _confirmed(); } Variant CreateDialog::get_drag_data_fw(const Point2 &p_point, Control *p_from) { - TreeItem *ti = favorites->get_item_at_position(p_point); if (ti) { Dictionary d; 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)); - set_drag_preview(tb); + favorites->set_drag_preview(tb); return d; } @@ -667,7 +526,6 @@ Variant CreateDialog::get_drag_data_fw(const Point2 &p_point, Control *p_from) { } bool CreateDialog::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const { - Dictionary d = p_data; if (d.has("type") && String(d["type"]) == "create_favorite_drag") { favorites->set_drop_mode_flags(Tree::DROP_MODE_INBETWEEN); @@ -676,26 +534,29 @@ bool CreateDialog::can_drop_data_fw(const Point2 &p_point, const Variant &p_data return false; } -void CreateDialog::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) { +void CreateDialog::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) { Dictionary d = p_data; TreeItem *ti = favorites->get_item_at_position(p_point); - if (!ti) + if (!ti) { return; + } String drop_at = ti->get_text(0); int ds = favorites->get_drop_section_at_position(p_point); int drop_idx = favorite_list.find(drop_at); - if (drop_idx < 0) + if (drop_idx < 0) { return; + } String type = d["class"]; int from_idx = favorite_list.find(type); - if (from_idx < 0) + if (from_idx < 0) { return; + } if (drop_idx == from_idx) { ds = -1; //cause it will be gone @@ -719,21 +580,64 @@ 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::_bind_methods() { +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); + } - ClassDB::bind_method(D_METHOD("_text_changed"), &CreateDialog::_text_changed); - ClassDB::bind_method(D_METHOD("_confirmed"), &CreateDialog::_confirmed); - ClassDB::bind_method(D_METHOD("_sbox_input"), &CreateDialog::_sbox_input); - ClassDB::bind_method(D_METHOD("_item_selected"), &CreateDialog::_item_selected); - ClassDB::bind_method(D_METHOD("_favorite_toggled"), &CreateDialog::_favorite_toggled); - ClassDB::bind_method(D_METHOD("_history_selected"), &CreateDialog::_history_selected); - ClassDB::bind_method(D_METHOD("_favorite_selected"), &CreateDialog::_favorite_selected); - ClassDB::bind_method(D_METHOD("_history_activated"), &CreateDialog::_history_activated); - ClassDB::bind_method(D_METHOD("_favorite_activated"), &CreateDialog::_favorite_activated); + 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); @@ -745,10 +649,11 @@ void CreateDialog::_bind_methods() { } CreateDialog::CreateDialog() { + base_type = "Object"; + preferred_search_result_type = ""; - is_replace_mode = false; - - set_resizable(true); + 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); @@ -757,64 +662,65 @@ 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(SIZE_EXPAND_FILL); + 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", this, "_favorite_selected"); - favorites->connect("item_activated", this, "_favorite_activated"); - favorites->set_drag_forwarding(this); - favorites->add_constant_override("draw_guides", 1); + 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 cannot forward drag data to a non control, must be fixed +#endif + //favorites->set_drag_forwarding(this); + 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(SIZE_EXPAND_FILL); + 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", this, "_history_selected"); - recent->connect("item_activated", this, "_history_activated"); - recent->add_constant_override("draw_guides", 1); + 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(SIZE_EXPAND_FILL); - HBoxContainer *search_hb = memnew(HBoxContainer); + vbc->set_h_size_flags(Control::SIZE_EXPAND_FILL); + hsc->add_child(vbc); + search_box = memnew(LineEdit); - search_box->set_h_size_flags(SIZE_EXPAND_FILL); + 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); + favorite->set_tooltip(TTR("(Un)favorite selected item.")); + favorite->connect("pressed", callable_mp(this, &CreateDialog::_favorite_toggled)); search_hb->add_child(favorite); - favorite->connect("pressed", this, "_favorite_toggled"); vbc->add_margin_child(TTR("Search:"), search_hb); - search_box->connect("text_changed", this, "_text_changed"); - search_box->connect("gui_input", this, "_sbox_input"); + search_options = memnew(Tree); + search_options->connect("item_activated", callable_mp(this, &CreateDialog::_confirmed)); + search_options->connect("cell_selected", callable_mp(this, &CreateDialog::_item_selected)); 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", this, "_confirmed"); - search_options->connect("cell_selected", this, "_item_selected"); - base_type = "Object"; - preferred_search_result_type = ""; help_bit = memnew(EditorHelpBit); + help_bit->connect("request_hide", callable_mp(this, &CreateDialog::_hide_requested)); vbc->add_margin_child(TTR("Description:"), help_bit); - help_bit->connect("request_hide", this, "_closed"); - 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); } |