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);  }  |