Browse Source

Unify editor class and reference search

Co-authored-by: Michael Alexsander Silva Dias <[email protected]>
Ray Koopa 7 years ago
parent
commit
fddffa9eb8

+ 0 - 478
editor/editor_help.cpp

@@ -40,482 +40,6 @@
 #define CONTRIBUTE2_URL "https://github.com/godotengine/godot-docs"
 #define REQUEST_URL "https://github.com/godotengine/godot-docs/issues/new"
 
-void EditorHelpSearch::popup_dialog() {
-
-	popup_centered(Size2(700, 600) * EDSCALE);
-	if (search_box->get_text() != "") {
-		search_box->select_all();
-		_update_search();
-	}
-	search_box->grab_focus();
-}
-
-void EditorHelpSearch::popup_dialog(const String &p_term) {
-
-	popup_centered(Size2(700, 600) * EDSCALE);
-	if (p_term != "") {
-		search_box->set_text(p_term);
-		search_box->select_all();
-		_update_search();
-	} else {
-		search_box->clear();
-	}
-	search_box->grab_focus();
-}
-
-void EditorHelpSearch::_text_changed(const String &p_newtext) {
-
-	_update_search();
-}
-
-void EditorHelpSearch::_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)) {
-
-		search_options->call("_gui_input", k);
-		search_box->accept_event();
-	}
-}
-
-void EditorHelpSearch::IncrementalSearch::phase1(Map<String, DocData::ClassDoc>::Element *E) {
-
-	if (E->key().findn(term) != -1) {
-
-		TreeItem *item = search_options->create_item(root);
-		item->set_metadata(0, "class_name:" + E->key());
-		item->set_text(0, E->key() + " (Class)");
-		Ref<Texture> icon = EditorNode::get_singleton()->get_class_icon(E->key(), "Node");
-		item->set_icon(0, icon);
-	}
-}
-
-void EditorHelpSearch::IncrementalSearch::phase2(Map<String, DocData::ClassDoc>::Element *E) {
-
-	DocData::ClassDoc &c = E->get();
-
-	Ref<Texture> cicon = EditorNode::get_singleton()->get_class_icon(E->key(), "Node");
-
-	for (int i = 0; i < c.methods.size(); i++) {
-		if ((term.begins_with(".") && c.methods[i].name.begins_with(term.right(1))) || (term.ends_with("(") && c.methods[i].name.ends_with(term.left(term.length() - 1).strip_edges())) || (term.begins_with(".") && term.ends_with("(") && c.methods[i].name == term.substr(1, term.length() - 2).strip_edges()) || c.methods[i].name.findn(term) != -1) {
-
-			TreeItem *item = search_options->create_item(root);
-			item->set_metadata(0, "class_method:" + E->key() + ":" + c.methods[i].name);
-			item->set_text(0, E->key() + "." + c.methods[i].name + " (Method)");
-			item->set_icon(0, cicon);
-		}
-	}
-
-	for (int i = 0; i < c.signals.size(); i++) {
-
-		if (c.signals[i].name.findn(term) != -1) {
-
-			TreeItem *item = search_options->create_item(root);
-			item->set_metadata(0, "class_signal:" + E->key() + ":" + c.signals[i].name);
-			item->set_text(0, E->key() + "." + c.signals[i].name + " (Signal)");
-			item->set_icon(0, cicon);
-		}
-	}
-
-	for (int i = 0; i < c.constants.size(); i++) {
-
-		if (c.constants[i].name.findn(term) != -1) {
-
-			TreeItem *item = search_options->create_item(root);
-			item->set_metadata(0, "class_constant:" + E->key() + ":" + c.constants[i].name);
-			item->set_text(0, E->key() + "." + c.constants[i].name + " (Constant)");
-			item->set_icon(0, cicon);
-		}
-	}
-
-	for (int i = 0; i < c.properties.size(); i++) {
-
-		if (c.properties[i].name.findn(term) != -1) {
-
-			TreeItem *item = search_options->create_item(root);
-			item->set_metadata(0, "class_property:" + E->key() + ":" + c.properties[i].name);
-			item->set_text(0, E->key() + "." + c.properties[i].name + " (Property)");
-			item->set_icon(0, cicon);
-		}
-	}
-
-	for (int i = 0; i < c.theme_properties.size(); i++) {
-
-		if (c.theme_properties[i].name.findn(term) != -1) {
-
-			TreeItem *item = search_options->create_item(root);
-			item->set_metadata(0, "class_theme_item:" + E->key() + ":" + c.theme_properties[i].name);
-			item->set_text(0, E->key() + "." + c.theme_properties[i].name + " (Theme Item)");
-			item->set_icon(0, cicon);
-		}
-	}
-}
-
-bool EditorHelpSearch::IncrementalSearch::slice() {
-
-	if (phase > 2)
-		return true;
-
-	if (iterator) {
-
-		switch (phase) {
-
-			case 1: {
-				phase1(iterator);
-			} break;
-			case 2: {
-				phase2(iterator);
-			} break;
-			default: {
-				WARN_PRINT("illegal phase in IncrementalSearch");
-				return true;
-			}
-		}
-
-		iterator = iterator->next();
-	} else {
-
-		phase += 1;
-		iterator = doc->class_list.front();
-	}
-
-	return false;
-}
-
-EditorHelpSearch::IncrementalSearch::IncrementalSearch(EditorHelpSearch *p_search, Tree *p_search_options, const String &p_term) :
-		search(p_search),
-		search_options(p_search_options) {
-
-	def_icon = search->get_icon("Node", "EditorIcons");
-	doc = EditorHelp::get_doc_data();
-
-	term = p_term;
-
-	root = search_options->create_item();
-	phase = 0;
-	iterator = 0;
-}
-
-bool EditorHelpSearch::IncrementalSearch::empty() const {
-
-	return root->get_children() == NULL;
-}
-
-bool EditorHelpSearch::IncrementalSearch::work(uint64_t slot) {
-
-	const uint64_t until = OS::get_singleton()->get_ticks_usec() + slot;
-
-	while (!slice()) {
-
-		if (OS::get_singleton()->get_ticks_usec() > until)
-			return false;
-	}
-
-	return true;
-}
-
-void EditorHelpSearch::_update_search() {
-	search_options->clear();
-
-	String term = search_box->get_text();
-	if (term.length() < 2)
-		return;
-
-	search = Ref<IncrementalSearch>(memnew(IncrementalSearch(this, search_options, term)));
-	set_process(true);
-}
-
-void EditorHelpSearch::_confirmed() {
-
-	TreeItem *ti = search_options->get_selected();
-	if (!ti)
-		return;
-
-	String mdata = ti->get_metadata(0);
-	EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT);
-	emit_signal("go_to_help", mdata);
-	// go to that
-	hide();
-}
-
-void EditorHelpSearch::_notification(int p_what) {
-
-	if (p_what == NOTIFICATION_ENTER_TREE) {
-
-		//_update_icons
-		search_box->set_right_icon(get_icon("Search", "EditorIcons"));
-		search_box->set_clear_button_enabled(true);
-
-		connect("confirmed", this, "_confirmed");
-		_update_search();
-	} else if (p_what == NOTIFICATION_EXIT_TREE) {
-		disconnect("confirmed", this, "_confirmed");
-	} else if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
-
-		if (is_visible_in_tree()) {
-
-			search_box->call_deferred("grab_focus"); // still not visible
-			search_box->select_all();
-		}
-	} else if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) {
-
-		//_update_icons
-		search_box->set_right_icon(get_icon("Search", "EditorIcons"));
-		search_box->set_clear_button_enabled(true);
-	} else if (p_what == NOTIFICATION_PROCESS) {
-
-		if (search.is_valid()) {
-
-			if (search->work()) {
-
-				get_ok()->set_disabled(search->empty());
-				search = Ref<IncrementalSearch>();
-				set_process(false);
-			}
-		} else {
-
-			set_process(false);
-		}
-	}
-}
-
-void EditorHelpSearch::_bind_methods() {
-
-	ClassDB::bind_method(D_METHOD("_text_changed"), &EditorHelpSearch::_text_changed);
-	ClassDB::bind_method(D_METHOD("_confirmed"), &EditorHelpSearch::_confirmed);
-	ClassDB::bind_method(D_METHOD("_sbox_input"), &EditorHelpSearch::_sbox_input);
-	ClassDB::bind_method(D_METHOD("_update_search"), &EditorHelpSearch::_update_search);
-
-	ADD_SIGNAL(MethodInfo("go_to_help"));
-}
-
-EditorHelpSearch::EditorHelpSearch() {
-
-	VBoxContainer *vbc = memnew(VBoxContainer);
-	add_child(vbc);
-
-	search_box = memnew(LineEdit);
-	vbc->add_child(search_box);
-	search_box->connect("text_changed", this, "_text_changed");
-	search_box->connect("gui_input", this, "_sbox_input");
-	search_options = memnew(Tree);
-	search_options->set_hide_root(true);
-	vbc->add_margin_child(TTR("Matches:"), search_options, true);
-	get_ok()->set_text(TTR("Open"));
-	get_ok()->set_disabled(true);
-	register_text_enter(search_box);
-	set_hide_on_ok(false);
-	search_options->connect("item_activated", this, "_confirmed");
-	set_title(TTR("Search Help"));
-}
-
-/////////////////////////////////
-
-void EditorHelpIndex::add_type(const String &p_type, HashMap<String, TreeItem *> &p_types, TreeItem *p_root) {
-
-	if (p_types.has(p_type))
-		return;
-
-	String inherits = EditorHelp::get_doc_data()->class_list[p_type].inherits;
-
-	TreeItem *parent = p_root;
-
-	if (inherits.length()) {
-
-		if (!p_types.has(inherits)) {
-
-			add_type(inherits, p_types, p_root);
-		}
-
-		if (p_types.has(inherits))
-			parent = p_types[inherits];
-	}
-
-	TreeItem *item = class_list->create_item(parent);
-	item->set_metadata(0, p_type);
-	item->set_tooltip(0, EditorHelp::get_doc_data()->class_list[p_type].brief_description);
-	item->set_text(0, p_type);
-
-	Ref<Texture> icon = EditorNode::get_singleton()->get_class_icon(p_type);
-	item->set_icon(0, icon);
-
-	p_types[p_type] = item;
-}
-
-void EditorHelpIndex::_tree_item_selected() {
-
-	TreeItem *s = class_list->get_selected();
-	if (!s)
-		return;
-
-	EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT);
-	emit_signal("open_class", s->get_text(0));
-	hide();
-}
-
-void EditorHelpIndex::select_class(const String &p_class) {
-
-	if (!tree_item_map.has(p_class))
-		return;
-	tree_item_map[p_class]->select(0);
-	class_list->ensure_cursor_is_visible();
-}
-
-void EditorHelpIndex::popup_dialog() {
-
-	popup_centered(Size2(500, 600) * EDSCALE);
-
-	search_box->set_text("");
-	_update_class_list();
-}
-
-void EditorHelpIndex::_notification(int p_what) {
-
-	if (p_what == NOTIFICATION_ENTER_TREE) {
-
-		//_update_icons
-		search_box->set_right_icon(get_icon("Search", "EditorIcons"));
-		search_box->set_clear_button_enabled(true);
-		_update_class_list();
-
-		connect("confirmed", this, "_tree_item_selected");
-
-	} else if (p_what == NOTIFICATION_POST_POPUP) {
-
-		search_box->call_deferred("grab_focus");
-	} else if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) {
-
-		//_update_icons
-		search_box->set_right_icon(get_icon("Search", "EditorIcons"));
-		search_box->set_clear_button_enabled(true);
-
-		bool enable_rl = EditorSettings::get_singleton()->get("docks/scene_tree/draw_relationship_lines");
-		Color rl_color = EditorSettings::get_singleton()->get("docks/scene_tree/relationship_line_color");
-
-		if (enable_rl) {
-			class_list->add_constant_override("draw_relationship_lines", 1);
-			class_list->add_color_override("relationship_line_color", rl_color);
-			class_list->add_constant_override("draw_guides", 0);
-		} else {
-			class_list->add_constant_override("draw_relationship_lines", 0);
-			class_list->add_constant_override("draw_guides", 1);
-		}
-	}
-}
-
-void EditorHelpIndex::_text_changed(const String &p_text) {
-
-	_update_class_list();
-}
-
-void EditorHelpIndex::_update_class_list() {
-
-	class_list->clear();
-	tree_item_map.clear();
-	TreeItem *root = class_list->create_item();
-
-	String filter = search_box->get_text().strip_edges();
-	String to_select = "";
-
-	for (Map<String, DocData::ClassDoc>::Element *E = EditorHelp::get_doc_data()->class_list.front(); E; E = E->next()) {
-
-		if (filter == "") {
-			add_type(E->key(), tree_item_map, root);
-		} else {
-
-			bool found = false;
-			String type = E->key();
-
-			while (type != "") {
-				if (filter.is_subsequence_ofi(type)) {
-
-					if (to_select.empty() || type.length() < to_select.length()) {
-						to_select = type;
-					}
-
-					found = true;
-				}
-
-				type = EditorHelp::get_doc_data()->class_list[type].inherits;
-			}
-
-			if (found) {
-				add_type(E->key(), tree_item_map, root);
-			}
-		}
-	}
-
-	if (tree_item_map.has(filter)) {
-		select_class(filter);
-	} else if (to_select != "") {
-		select_class(to_select);
-	}
-}
-
-void EditorHelpIndex::_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)) {
-
-		class_list->call("_gui_input", k);
-		search_box->accept_event();
-	}
-}
-
-void EditorHelpIndex::_bind_methods() {
-
-	ClassDB::bind_method("_tree_item_selected", &EditorHelpIndex::_tree_item_selected);
-	ClassDB::bind_method("_text_changed", &EditorHelpIndex::_text_changed);
-	ClassDB::bind_method("_sbox_input", &EditorHelpIndex::_sbox_input);
-	ClassDB::bind_method("select_class", &EditorHelpIndex::select_class);
-	ADD_SIGNAL(MethodInfo("open_class"));
-}
-
-EditorHelpIndex::EditorHelpIndex() {
-
-	VBoxContainer *vbc = memnew(VBoxContainer);
-	add_child(vbc);
-
-	search_box = memnew(LineEdit);
-	vbc->add_child(search_box);
-	search_box->set_h_size_flags(SIZE_EXPAND_FILL);
-
-	register_text_enter(search_box);
-
-	search_box->connect("text_changed", this, "_text_changed");
-	search_box->connect("gui_input", this, "_sbox_input");
-
-	class_list = memnew(Tree);
-	vbc->add_margin_child(TTR("Class List:") + " ", class_list, true);
-	class_list->set_hide_root(true);
-	class_list->set_v_size_flags(SIZE_EXPAND_FILL);
-
-	class_list->connect("item_activated", this, "_tree_item_selected");
-
-	bool enable_rl = EditorSettings::get_singleton()->get("docks/scene_tree/draw_relationship_lines");
-	Color rl_color = EditorSettings::get_singleton()->get("docks/scene_tree/relationship_line_color");
-
-	if (enable_rl) {
-		class_list->add_constant_override("draw_relationship_lines", 1);
-		class_list->add_color_override("relationship_line_color", rl_color);
-	} else {
-		class_list->add_constant_override("draw_relationship_lines", 0);
-	}
-
-	get_ok()->set_text(TTR("Open"));
-	set_title(TTR("Search Classes"));
-}
-
-/////////////////////////////////
-
 DocData *EditorHelp::doc = NULL;
 
 void EditorHelp::_init_colors() {
@@ -1923,8 +1447,6 @@ EditorHelp::EditorHelp() {
 EditorHelp::~EditorHelp() {
 }
 
-/////////////
-
 void EditorHelpBit::_go_to_help(String p_what) {
 
 	EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT);

+ 3 - 86
editor/editor_help.h

@@ -31,6 +31,8 @@
 #ifndef EDITOR_HELP_H
 #define EDITOR_HELP_H
 
+#include "editor/code_editor.h"
+#include "editor/doc/doc_data.h"
 #include "editor/editor_plugin.h"
 #include "scene/gui/menu_button.h"
 #include "scene/gui/panel_container.h"
@@ -38,95 +40,9 @@
 #include "scene/gui/split_container.h"
 #include "scene/gui/tab_container.h"
 #include "scene/gui/text_edit.h"
-#include "scene/gui/tree.h"
-
-#include "editor/code_editor.h"
-#include "editor/doc/doc_data.h"
 #include "scene/main/timer.h"
 
-class EditorNode;
-
-class EditorHelpSearch : public ConfirmationDialog {
-
-	GDCLASS(EditorHelpSearch, ConfirmationDialog)
-
-	LineEdit *search_box;
-	Tree *search_options;
-	String base_type;
-
-	class IncrementalSearch : public Reference {
-		String term;
-		TreeItem *root;
-
-		EditorHelpSearch *search;
-		Tree *search_options;
-
-		DocData *doc;
-		Ref<Texture> def_icon;
-
-		int phase;
-		Map<String, DocData::ClassDoc>::Element *iterator;
-
-		void phase1(Map<String, DocData::ClassDoc>::Element *E);
-		void phase2(Map<String, DocData::ClassDoc>::Element *E);
-		bool slice();
-
-	public:
-		IncrementalSearch(EditorHelpSearch *p_search, Tree *p_search_options, const String &p_term);
-
-		bool empty() const;
-		bool work(uint64_t slot = 1000000 / 10);
-	};
-
-	Ref<IncrementalSearch> search;
-
-	void _update_search();
-
-	void _sbox_input(const Ref<InputEvent> &p_ie);
-
-	void _confirmed();
-	void _text_changed(const String &p_newtext);
-
-protected:
-	void _notification(int p_what);
-	static void _bind_methods();
-
-public:
-	void popup_dialog();
-	void popup_dialog(const String &p_term);
-
-	EditorHelpSearch();
-};
-
-class EditorHelpIndex : public ConfirmationDialog {
-	GDCLASS(EditorHelpIndex, ConfirmationDialog);
-
-	LineEdit *search_box;
-	Tree *class_list;
-	HashMap<String, TreeItem *> tree_item_map;
-
-	void _tree_item_selected();
-	void _text_changed(const String &p_text);
-	void _sbox_input(const Ref<InputEvent> &p_ie);
-
-	void _update_class_list();
-
-	void add_type(const String &p_type, HashMap<String, TreeItem *> &p_types, TreeItem *p_root);
-
-protected:
-	void _notification(int p_what);
-	static void _bind_methods();
-
-public:
-	void select_class(const String &p_class);
-
-	void popup_dialog();
-
-	EditorHelpIndex();
-};
-
 class FindBar : public HBoxContainer {
-
 	GDCLASS(FindBar, HBoxContainer);
 
 	LineEdit *search_text;
@@ -172,6 +88,7 @@ public:
 };
 
 class EditorHelp : public VBoxContainer {
+
 	GDCLASS(EditorHelp, VBoxContainer);
 
 	enum Page {

+ 591 - 0
editor/editor_help_search.cpp

@@ -0,0 +1,591 @@
+/*************************************************************************/
+/*  editor_help_search.cpp                                               */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2018 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       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "editor_help_search.h"
+
+#include "core/os/keyboard.h"
+#include "editor_node.h"
+
+void EditorHelpSearch::_update_icons() {
+
+	search_box->set_right_icon(get_icon("Search", "EditorIcons"));
+	search_box->set_clear_button_enabled(true);
+	search_box->add_icon_override("right_icon", get_icon("Search", "EditorIcons"));
+	case_sensitive_button->set_icon(get_icon("MatchCase", "EditorIcons"));
+	hierarchy_button->set_icon(get_icon("ClassList", "EditorIcons"));
+
+	if (is_visible_in_tree())
+		_update_results();
+}
+
+void EditorHelpSearch::_load_settings() {
+
+	bool enable_rl = EditorSettings::get_singleton()->get("docks/scene_tree/draw_relationship_lines");
+	Color rl_color = EditorSettings::get_singleton()->get("docks/scene_tree/relationship_line_color");
+
+	if (enable_rl) {
+		results_tree->add_constant_override("draw_relationship_lines", 1);
+		results_tree->add_color_override("relationship_line_color", rl_color);
+	} else {
+		results_tree->add_constant_override("draw_relationship_lines", 0);
+		results_tree->add_constant_override("draw_guides", 1);
+	}
+}
+
+void EditorHelpSearch::_update_results() {
+
+	String term = search_box->get_text();
+
+	int search_flags = filter_combo->get_selected_id();
+	if (case_sensitive_button->is_pressed())
+		search_flags |= SEARCH_CASE_SENSITIVE;
+	if (hierarchy_button->is_pressed())
+		search_flags |= SEARCH_SHOW_HIERARCHY;
+
+	search = Ref<Runner>(memnew(Runner(this, results_tree, term, search_flags)));
+	set_process(true);
+}
+
+void EditorHelpSearch::_search_box_gui_input(const Ref<InputEvent> &p_ie) {
+
+	// Redirect up and down navigational key events to the results list.
+	Ref<InputEventKey> k = p_ie;
+	if (k.is_valid()) {
+		switch (k->get_scancode()) {
+			case KEY_UP:
+			case KEY_DOWN:
+			case KEY_PAGEUP:
+			case KEY_PAGEDOWN: {
+				results_tree->call("_gui_input", k);
+				search_box->accept_event();
+			} break;
+		}
+	}
+}
+
+void EditorHelpSearch::_search_box_text_changed(const String &p_text) {
+
+	_update_results();
+}
+
+void EditorHelpSearch::_case_sensitive_button_pressed() {
+
+	search_box->grab_focus();
+	_update_results();
+}
+
+void EditorHelpSearch::_hierarchy_button_pressed() {
+
+	search_box->grab_focus();
+	_update_results();
+}
+
+void EditorHelpSearch::_filter_combo_item_selected(int p_option) {
+
+	_update_results();
+}
+
+void EditorHelpSearch::_confirmed() {
+
+	// Activate the script editor and emit the signal with the documentation link to display.
+	EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT);
+
+	TreeItem *item = results_tree->get_selected();
+	emit_signal("go_to_help", item->get_metadata(0));
+
+	hide();
+}
+
+void EditorHelpSearch::_notification(int p_what) {
+
+	switch (p_what) {
+		case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
+
+			_load_settings();
+			_update_icons();
+		} break;
+		case NOTIFICATION_ENTER_TREE: {
+
+			connect("confirmed", this, "_confirmed");
+			_update_icons();
+		} break;
+		case NOTIFICATION_POPUP_HIDE: {
+
+			results_tree->clear();
+			EditorSettings::get_singleton()->set("interface/dialogs/search_help_bounds", get_rect());
+		} break;
+		case NOTIFICATION_PROCESS: {
+
+			// Update background search.
+			if (search.is_valid()) {
+				if (search->work()) {
+					// Search done.
+					get_ok()->set_disabled(results_tree->get_root()->get_children() == NULL);
+					search = Ref<Runner>();
+					set_process(false);
+				}
+			} else {
+				set_process(false);
+			}
+		} break;
+	}
+}
+
+void EditorHelpSearch::_bind_methods() {
+
+	ClassDB::bind_method(D_METHOD("_search_box_gui_input"), &EditorHelpSearch::_search_box_gui_input);
+	ClassDB::bind_method(D_METHOD("_search_box_text_changed"), &EditorHelpSearch::_search_box_text_changed);
+	ClassDB::bind_method(D_METHOD("_case_sensitive_button_pressed"), &EditorHelpSearch::_case_sensitive_button_pressed);
+	ClassDB::bind_method(D_METHOD("_hierarchy_button_pressed"), &EditorHelpSearch::_hierarchy_button_pressed);
+	ClassDB::bind_method(D_METHOD("_filter_combo_item_selected"), &EditorHelpSearch::_filter_combo_item_selected);
+	ClassDB::bind_method(D_METHOD("_confirmed"), &EditorHelpSearch::_confirmed);
+	ADD_SIGNAL(MethodInfo("go_to_help"));
+}
+
+void EditorHelpSearch::popup_dialog() {
+
+	popup_dialog(search_box->get_text());
+}
+
+void EditorHelpSearch::popup_dialog(const String &p_term) {
+
+	// Restore valid window bounds or pop up at default size.
+	if (EditorSettings::get_singleton()->has_setting("interface/dialogs/search_help_bounds"))
+		popup(EditorSettings::get_singleton()->get("interface/dialogs/search_help_bounds"));
+	else
+		popup_centered_ratio(0.5F);
+
+	if (p_term == "") {
+		search_box->clear();
+	} else {
+		search_box->set_text(p_term);
+		search_box->select_all();
+	}
+	search_box->grab_focus();
+	_update_results();
+}
+
+EditorHelpSearch::EditorHelpSearch() {
+
+	set_hide_on_ok(false);
+	set_resizable(true);
+	set_title(TTR("Search Help"));
+
+	get_ok()->set_disabled(true);
+	get_ok()->set_text(TTR("Open"));
+
+	// Split search and results area.
+	VBoxContainer *vbox = memnew(VBoxContainer);
+	add_child(vbox);
+
+	// Create the search box and filter controls (at the top).
+	HBoxContainer *hbox = memnew(HBoxContainer);
+	vbox->add_child(hbox);
+
+	search_box = memnew(LineEdit);
+	search_box->set_custom_minimum_size(Size2(200, 0));
+	search_box->set_h_size_flags(SIZE_EXPAND_FILL);
+	search_box->connect("gui_input", this, "_search_box_gui_input");
+	search_box->connect("text_changed", this, "_search_box_text_changed");
+	register_text_enter(search_box);
+	hbox->add_child(search_box);
+
+	case_sensitive_button = memnew(ToolButton);
+	case_sensitive_button->set_tooltip("Case Sensitive");
+	case_sensitive_button->connect("pressed", this, "_case_sensitive_button_pressed");
+	case_sensitive_button->set_toggle_mode(true);
+	hbox->add_child(case_sensitive_button);
+
+	hierarchy_button = memnew(ToolButton);
+	hierarchy_button->set_tooltip("Show Hierarchy");
+	hierarchy_button->connect("pressed", this, "_hierarchy_button_pressed");
+	hierarchy_button->set_toggle_mode(true);
+	hierarchy_button->set_pressed(true);
+	hbox->add_child(hierarchy_button);
+
+	filter_combo = memnew(OptionButton);
+	filter_combo->set_custom_minimum_size(Size2(200, 0));
+	filter_combo->set_stretch_ratio(0); // Fixed width.
+	filter_combo->add_item(TTR("Display All"), SEARCH_ALL);
+	filter_combo->add_separator();
+	filter_combo->add_item(TTR("Classes Only"), SEARCH_CLASSES);
+	filter_combo->add_item(TTR("Methods Only"), SEARCH_METHODS);
+	filter_combo->add_item(TTR("Signals Only"), SEARCH_SIGNALS);
+	filter_combo->add_item(TTR("Constants Only"), SEARCH_CONSTANTS);
+	filter_combo->add_item(TTR("Properties Only"), SEARCH_PROPERTIES);
+	filter_combo->add_item(TTR("Theme Properties Only"), SEARCH_THEME_ITEMS);
+	filter_combo->connect("item_selected", this, "_filter_combo_item_selected");
+	hbox->add_child(filter_combo);
+
+	// Create the results tree.
+	results_tree = memnew(Tree);
+	results_tree->set_v_size_flags(SIZE_EXPAND_FILL);
+	results_tree->set_columns(2);
+	results_tree->set_column_title(0, TTR("Name"));
+	results_tree->set_column_title(1, TTR("Member Type"));
+	results_tree->set_column_expand(1, false);
+	results_tree->set_column_min_width(1, 150);
+	results_tree->set_custom_minimum_size(Size2(0, 100));
+	results_tree->set_hide_root(true);
+	results_tree->set_select_mode(Tree::SELECT_ROW);
+	results_tree->connect("item_activated", this, "_confirmed");
+	vbox->add_child(results_tree, true);
+
+	_load_settings();
+}
+
+bool EditorHelpSearch::Runner::_slice() {
+
+	bool phase_done = false;
+	switch (phase) {
+		case PHASE_MATCH_CLASSES_INIT:
+			phase_done = _phase_match_classes_init();
+			break;
+		case PHASE_MATCH_CLASSES:
+			phase_done = _phase_match_classes();
+			break;
+		case PHASE_CLASS_ITEMS_INIT:
+			phase_done = _phase_class_items_init();
+			break;
+		case PHASE_CLASS_ITEMS:
+			phase_done = _phase_class_items();
+			break;
+		case PHASE_MEMBER_ITEMS_INIT:
+			phase_done = _phase_member_items_init();
+			break;
+		case PHASE_MEMBER_ITEMS:
+			phase_done = _phase_member_items();
+			break;
+		case PHASE_SELECT_MATCH:
+			phase_done = _phase_select_match();
+			break;
+		case PHASE_MAX:
+			return true;
+		default:
+			WARN_PRINTS("Invalid or unhandled phase in EditorHelpSearch::Runner, aborting search.");
+			return true;
+	};
+
+	if (phase_done)
+		phase++;
+	return false;
+}
+
+bool EditorHelpSearch::Runner::_phase_match_classes_init() {
+
+	iterator_doc = EditorHelp::get_doc_data()->class_list.front();
+	matches.clear();
+	matched_item = NULL;
+
+	return true;
+}
+
+bool EditorHelpSearch::Runner::_phase_match_classes() {
+
+	DocData::ClassDoc &class_doc = iterator_doc->value();
+
+	matches[class_doc.name] = ClassMatch();
+	ClassMatch &match = matches[class_doc.name];
+
+	match.doc = &class_doc;
+
+	// Match class name.
+	if (search_flags & SEARCH_CLASSES)
+		match.name = term == "" || _match_string(term, class_doc.name);
+
+	// Match members if the term is long enough.
+	if (term.length() > 1) {
+		if (search_flags & SEARCH_METHODS)
+			for (int i = 0; i < class_doc.methods.size(); i++) {
+				String method_name = search_flags & SEARCH_CASE_SENSITIVE ? class_doc.methods[i].name : class_doc.methods[i].name.to_lower();
+				if (method_name.find(term) > -1 ||
+						(term.begins_with(".") && method_name.begins_with(term.right(1))) ||
+						(term.ends_with("(") && method_name.ends_with(term.left(term.length() - 1).strip_edges())) ||
+						(term.begins_with(".") && term.ends_with("(") && method_name == term.substr(1, term.length() - 2).strip_edges()))
+					match.methods.push_back(const_cast<DocData::MethodDoc *>(&class_doc.methods[i]));
+			}
+		if (search_flags & SEARCH_SIGNALS)
+			for (int i = 0; i < class_doc.signals.size(); i++)
+				if (_match_string(term, class_doc.signals[i].name))
+					match.signals.push_back(const_cast<DocData::MethodDoc *>(&class_doc.signals[i]));
+		if (search_flags & SEARCH_CONSTANTS)
+			for (int i = 0; i < class_doc.constants.size(); i++)
+				if (_match_string(term, class_doc.constants[i].name))
+					match.constants.push_back(const_cast<DocData::ConstantDoc *>(&class_doc.constants[i]));
+		if (search_flags & SEARCH_PROPERTIES)
+			for (int i = 0; i < class_doc.properties.size(); i++)
+				if (_match_string(term, class_doc.properties[i].name))
+					match.properties.push_back(const_cast<DocData::PropertyDoc *>(&class_doc.properties[i]));
+		if (search_flags & SEARCH_THEME_ITEMS)
+			for (int i = 0; i < class_doc.theme_properties.size(); i++)
+				if (_match_string(term, class_doc.theme_properties[i].name))
+					match.theme_properties.push_back(const_cast<DocData::PropertyDoc *>(&class_doc.theme_properties[i]));
+	}
+
+	iterator_doc = iterator_doc->next();
+	return !iterator_doc;
+}
+
+bool EditorHelpSearch::Runner::_phase_class_items_init() {
+
+	iterator_match = matches.front();
+
+	results_tree->clear();
+	root_item = results_tree->create_item();
+	class_items.clear();
+
+	return true;
+}
+
+bool EditorHelpSearch::Runner::_phase_class_items() {
+
+	ClassMatch &match = iterator_match->value();
+
+	if (search_flags & SEARCH_SHOW_HIERARCHY) {
+		if (match.required())
+			_create_class_hierarchy(match);
+	} else {
+		if (match.name)
+			_create_class_item(root_item, match.doc, false);
+	}
+
+	iterator_match = iterator_match->next();
+	return !iterator_match;
+}
+
+bool EditorHelpSearch::Runner::_phase_member_items_init() {
+
+	iterator_match = matches.front();
+
+	return true;
+}
+
+bool EditorHelpSearch::Runner::_phase_member_items() {
+
+	ClassMatch &match = iterator_match->value();
+
+	TreeItem *parent = search_flags & SEARCH_SHOW_HIERARCHY ? class_items[match.doc->name] : root_item;
+	for (int i = 0; i < match.methods.size(); i++)
+		_create_method_item(parent, match.doc, match.methods[i]);
+	for (int i = 0; i < match.signals.size(); i++)
+		_create_signal_item(parent, match.doc, match.signals[i]);
+	for (int i = 0; i < match.constants.size(); i++)
+		_create_constant_item(parent, match.doc, match.constants[i]);
+	for (int i = 0; i < match.properties.size(); i++)
+		_create_property_item(parent, match.doc, match.properties[i]);
+	for (int i = 0; i < match.theme_properties.size(); i++)
+		_create_theme_property_item(parent, match.doc, match.theme_properties[i]);
+
+	iterator_match = iterator_match->next();
+	return !iterator_match;
+}
+
+bool EditorHelpSearch::Runner::_phase_select_match() {
+
+	if (matched_item)
+		matched_item->select(0);
+	results_tree->ensure_cursor_is_visible();
+
+	return true;
+}
+
+bool EditorHelpSearch::Runner::_match_string(const String &p_term, const String &p_string) const {
+
+	if (search_flags & SEARCH_CASE_SENSITIVE)
+		return p_string.find(p_term) > -1;
+	else
+		return p_string.findn(p_term) > -1;
+}
+
+void EditorHelpSearch::Runner::_match_item(TreeItem *p_item, const String &p_text) {
+
+	if (!matched_item) {
+		if (search_flags & SEARCH_CASE_SENSITIVE) {
+			if (p_text.casecmp_to(term) == 0)
+				matched_item = p_item;
+		} else {
+			if (p_text.nocasecmp_to(term) == 0)
+				matched_item = p_item;
+		}
+	}
+}
+
+TreeItem *EditorHelpSearch::Runner::_create_class_hierarchy(const ClassMatch &p_match) {
+
+	if (class_items.has(p_match.doc->name))
+		return class_items[p_match.doc->name];
+
+	// Ensure parent nodes are created first.
+	TreeItem *parent = root_item;
+	if (p_match.doc->inherits != "") {
+		if (class_items.has(p_match.doc->inherits)) {
+			parent = class_items[p_match.doc->inherits];
+		} else {
+			ClassMatch &base_match = matches[p_match.doc->inherits];
+			parent = _create_class_hierarchy(base_match);
+		}
+	}
+
+	TreeItem *class_item = _create_class_item(parent, p_match.doc, !p_match.name);
+	class_items[p_match.doc->name] = class_item;
+	return class_item;
+}
+
+TreeItem *EditorHelpSearch::Runner::_create_class_item(TreeItem *p_parent, const DocData::ClassDoc *p_doc, bool p_gray) {
+
+	Ref<Texture> icon = empty_icon;
+	if (ui_service->has_icon(p_doc->name, "EditorIcons"))
+		icon = ui_service->get_icon(p_doc->name, "EditorIcons");
+	else if (ClassDB::is_parent_class(p_doc->name, "Object"))
+		icon = ui_service->get_icon("Object", "EditorIcons");
+	String tooltip = p_doc->brief_description.strip_edges();
+
+	TreeItem *item = results_tree->create_item(p_parent);
+	item->set_icon(0, icon);
+	item->set_text(0, p_doc->name);
+	item->set_text(1, TTR("Class"));
+	item->set_tooltip(0, tooltip);
+	item->set_tooltip(1, tooltip);
+	item->set_metadata(0, "class_name:" + p_doc->name);
+	if (p_gray) {
+		item->set_custom_color(0, disabled_color);
+		item->set_custom_color(1, disabled_color);
+	}
+
+	_match_item(item, p_doc->name);
+
+	return item;
+}
+
+TreeItem *EditorHelpSearch::Runner::_create_method_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::MethodDoc *p_doc) {
+
+	String tooltip = p_doc->return_type + " " + p_class_doc->name + "." + p_doc->name + "(";
+	for (int i = 0; i < p_doc->arguments.size(); i++) {
+		const DocData::ArgumentDoc &arg = p_doc->arguments[i];
+		tooltip += arg.type + " " + arg.name;
+		if (arg.default_value != "")
+			tooltip += " = " + arg.default_value;
+		if (i < p_doc->arguments.size() - 1)
+			tooltip += ", ";
+	}
+	tooltip += ")";
+	return _create_member_item(p_parent, p_class_doc->name, "MemberMethod", p_doc->name, "Method", "method", tooltip);
+}
+
+TreeItem *EditorHelpSearch::Runner::_create_signal_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::MethodDoc *p_doc) {
+
+	String tooltip = p_doc->return_type + " " + p_class_doc->name + "." + p_doc->name + "(";
+	for (int i = 0; i < p_doc->arguments.size(); i++) {
+		const DocData::ArgumentDoc &arg = p_doc->arguments[i];
+		tooltip += arg.type + " " + arg.name;
+		if (arg.default_value != "")
+			tooltip += " = " + arg.default_value;
+		if (i < p_doc->arguments.size() - 1)
+			tooltip += ", ";
+	}
+	tooltip += ")";
+	return _create_member_item(p_parent, p_class_doc->name, "MemberSignal", p_doc->name, "Signal", "signal", tooltip);
+}
+
+TreeItem *EditorHelpSearch::Runner::_create_constant_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::ConstantDoc *p_doc) {
+
+	String tooltip = p_class_doc->name + "." + p_doc->name;
+	return _create_member_item(p_parent, p_class_doc->name, "MemberConstant", p_doc->name, "Constant", "constant", tooltip);
+}
+
+TreeItem *EditorHelpSearch::Runner::_create_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::PropertyDoc *p_doc) {
+
+	String tooltip = p_doc->type + " " + p_class_doc->name + "." + p_doc->name;
+	tooltip += "\n    " + p_class_doc->name + "." + p_doc->setter + "(value) setter";
+	tooltip += "\n    " + p_class_doc->name + "." + p_doc->getter + "() getter";
+	return _create_member_item(p_parent, p_class_doc->name, "MemberProperty", p_doc->name, "Property", "property", tooltip);
+}
+
+TreeItem *EditorHelpSearch::Runner::_create_theme_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::PropertyDoc *p_doc) {
+
+	String tooltip = p_doc->type + " " + p_class_doc->name + "." + p_doc->name;
+	return _create_member_item(p_parent, p_class_doc->name, "MemberTheme", p_doc->name, "Theme Property", "theme_item", tooltip);
+}
+
+TreeItem *EditorHelpSearch::Runner::_create_member_item(TreeItem *p_parent, const String &p_class_name, const String &p_icon, const String &p_name, const String &p_type, const String &p_metatype, const String &p_tooltip) {
+
+	Ref<Texture> icon;
+	String text;
+	if (search_flags & SEARCH_SHOW_HIERARCHY) {
+		icon = ui_service->get_icon(p_icon, "EditorIcons");
+		text = p_name;
+	} else {
+		icon = ui_service->get_icon(p_icon, "EditorIcons");
+		/*// In flat mode, show the class icon.
+		if (ui_service->has_icon(p_class_name, "EditorIcons"))
+			icon = ui_service->get_icon(p_class_name, "EditorIcons");
+		else if (ClassDB::is_parent_class(p_class_name, "Object"))
+			icon = ui_service->get_icon("Object", "EditorIcons");*/
+		text = p_class_name + "." + p_name;
+	}
+
+	TreeItem *item = results_tree->create_item(p_parent);
+	item->set_icon(0, icon);
+	item->set_text(0, text);
+	item->set_text(1, TTR(p_type));
+	item->set_tooltip(0, p_tooltip);
+	item->set_tooltip(1, p_tooltip);
+	item->set_metadata(0, "class_" + p_metatype + ":" + p_class_name + ":" + p_name);
+
+	_match_item(item, p_name);
+
+	return item;
+}
+
+bool EditorHelpSearch::Runner::work(uint64_t slot) {
+
+	// Return true when the search has been completed, otherwise false.
+	const uint64_t until = OS::get_singleton()->get_ticks_usec() + slot;
+	while (!_slice())
+		if (OS::get_singleton()->get_ticks_usec() > until)
+			return false;
+	return true;
+}
+
+EditorHelpSearch::Runner::Runner(Control *p_icon_service, Tree *p_results_tree, const String &p_term, int p_search_flags) {
+
+	ui_service = p_icon_service;
+	results_tree = p_results_tree;
+	term = p_term.strip_edges();
+	search_flags = p_search_flags;
+
+	if ((search_flags & SEARCH_CASE_SENSITIVE) == 0)
+		term = term.to_lower();
+
+	empty_icon = ui_service->get_icon("ArrowRight", "EditorIcons");
+	disabled_color = ui_service->get_color("disabled_font_color", "Editor");
+
+	phase = 0;
+}

+ 156 - 0
editor/editor_help_search.h

@@ -0,0 +1,156 @@
+/*************************************************************************/
+/*  editor_help_search.h                                                 */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2018 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       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifndef EDITOR_HELP_SEARCH_H
+#define EDITOR_HELP_SEARCH_H
+
+#include "core/ordered_hash_map.h"
+#include "editor/code_editor.h"
+#include "editor/editor_help.h"
+#include "editor/editor_plugin.h"
+#include "scene/gui/option_button.h"
+#include "scene/gui/tree.h"
+
+class EditorHelpSearch : public ConfirmationDialog {
+	GDCLASS(EditorHelpSearch, ConfirmationDialog);
+
+	enum SearchFlags {
+		SEARCH_CLASSES = 1 << 0,
+		SEARCH_METHODS = 1 << 1,
+		SEARCH_SIGNALS = 1 << 2,
+		SEARCH_CONSTANTS = 1 << 3,
+		SEARCH_PROPERTIES = 1 << 4,
+		SEARCH_THEME_ITEMS = 1 << 5,
+		SEARCH_ALL = SEARCH_CLASSES | SEARCH_METHODS | SEARCH_SIGNALS | SEARCH_CONSTANTS | SEARCH_PROPERTIES | SEARCH_THEME_ITEMS,
+		SEARCH_CASE_SENSITIVE = 1 << 29,
+		SEARCH_SHOW_HIERARCHY = 1 << 30
+	};
+
+	LineEdit *search_box;
+	ToolButton *case_sensitive_button;
+	ToolButton *hierarchy_button;
+	OptionButton *filter_combo;
+	Tree *results_tree;
+
+	class Runner;
+	Ref<Runner> search;
+
+	void _update_icons();
+	void _load_settings();
+	void _update_results();
+
+	void _search_box_gui_input(const Ref<InputEvent> &p_ie);
+	void _search_box_text_changed(const String &p_text);
+	void _case_sensitive_button_pressed();
+	void _hierarchy_button_pressed();
+	void _filter_combo_item_selected(int p_option);
+	void _confirmed();
+
+protected:
+	void _notification(int p_what);
+	static void _bind_methods();
+
+public:
+	void popup_dialog();
+	void popup_dialog(const String &p_term);
+
+	EditorHelpSearch();
+};
+
+class EditorHelpSearch::Runner : public Reference {
+
+	enum Phase {
+		PHASE_MATCH_CLASSES_INIT,
+		PHASE_MATCH_CLASSES,
+		PHASE_CLASS_ITEMS_INIT,
+		PHASE_CLASS_ITEMS,
+		PHASE_MEMBER_ITEMS_INIT,
+		PHASE_MEMBER_ITEMS,
+		PHASE_SELECT_MATCH,
+		PHASE_MAX
+	};
+	int phase;
+
+	struct ClassMatch {
+		DocData::ClassDoc *doc;
+		bool name;
+		Vector<DocData::MethodDoc *> methods;
+		Vector<DocData::MethodDoc *> signals;
+		Vector<DocData::ConstantDoc *> constants;
+		Vector<DocData::PropertyDoc *> properties;
+		Vector<DocData::PropertyDoc *> theme_properties;
+
+		bool required() {
+			return name || methods.size() || signals.size() || constants.size() || properties.size() || theme_properties.size();
+		}
+	};
+
+	Control *ui_service;
+	Tree *results_tree;
+	String term;
+	int search_flags;
+
+	Ref<Texture> empty_icon;
+	Color disabled_color;
+
+	Map<String, DocData::ClassDoc>::Element *iterator_doc;
+	Map<String, ClassMatch> matches;
+	Map<String, ClassMatch>::Element *iterator_match;
+	TreeItem *root_item;
+	Map<String, TreeItem *> class_items;
+	TreeItem *matched_item;
+
+	bool _slice();
+	bool _phase_match_classes_init();
+	bool _phase_match_classes();
+	bool _phase_class_items_init();
+	bool _phase_class_items();
+	bool _phase_member_items_init();
+	bool _phase_member_items();
+	bool _phase_select_match();
+
+	bool _match_string(const String &p_term, const String &p_string) const;
+	void _match_item(TreeItem *p_item, const String &p_text);
+	TreeItem *_create_class_hierarchy(const ClassMatch &p_match);
+	TreeItem *_create_class_item(TreeItem *p_parent, const DocData::ClassDoc *p_doc, bool p_gray);
+	TreeItem *_create_method_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::MethodDoc *p_doc);
+	TreeItem *_create_signal_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::MethodDoc *p_doc);
+	TreeItem *_create_constant_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::ConstantDoc *p_doc);
+	TreeItem *_create_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::PropertyDoc *p_doc);
+	TreeItem *_create_theme_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::PropertyDoc *p_doc);
+	TreeItem *_create_member_item(TreeItem *p_parent, const String &p_class_name, const String &p_icon, const String &p_name, const String &p_type, const String &p_metatype, const String &p_tooltip);
+
+public:
+	bool work(uint64_t slot = 100000);
+
+	Runner(Control *p_icon_service, Tree *p_results_tree, const String &p_term, int p_search_flags);
+};
+
+#endif // EDITOR_HELP_SEARCH_H

+ 1 - 7
editor/editor_node.cpp

@@ -384,7 +384,6 @@ void EditorNode::_notification(int p_what) {
 		update_menu->set_icon(gui_base->get_icon("Progress1", "EditorIcons"));
 
 		PopupMenu *p = help_menu->get_popup();
-		p->set_item_icon(p->get_item_index(HELP_CLASSES), gui_base->get_icon("ClassList", "EditorIcons"));
 		p->set_item_icon(p->get_item_index(HELP_SEARCH), gui_base->get_icon("HelpSearch", "EditorIcons"));
 		p->set_item_icon(p->get_item_index(HELP_DOCS), gui_base->get_icon("Instance", "EditorIcons"));
 		p->set_item_icon(p->get_item_index(HELP_QA), gui_base->get_icon("Instance", "EditorIcons"));
@@ -2262,9 +2261,6 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
 			file->popup_centered_ratio();
 
 		} break;
-		case HELP_CLASSES: {
-			emit_signal("request_help_index", "");
-		} break;
 		case HELP_SEARCH: {
 			emit_signal("request_help_search", "");
 		} break;
@@ -4654,7 +4650,6 @@ void EditorNode::_bind_methods() {
 	ADD_SIGNAL(MethodInfo("pause_pressed"));
 	ADD_SIGNAL(MethodInfo("stop_pressed"));
 	ADD_SIGNAL(MethodInfo("request_help_search"));
-	ADD_SIGNAL(MethodInfo("request_help_index"));
 	ADD_SIGNAL(MethodInfo("script_add_function_request", PropertyInfo(Variant::OBJECT, "obj"), PropertyInfo(Variant::STRING, "function"), PropertyInfo(Variant::POOL_STRING_ARRAY, "args")));
 	ADD_SIGNAL(MethodInfo("resource_saved", PropertyInfo(Variant::OBJECT, "obj")));
 }
@@ -5340,8 +5335,7 @@ EditorNode::EditorNode() {
 	p = help_menu->get_popup();
 	p->set_hide_on_window_lose_focus(true);
 	p->connect("id_pressed", this, "_menu_option");
-	p->add_icon_item(gui_base->get_icon("ClassList", "EditorIcons"), TTR("Classes"), HELP_CLASSES);
-	p->add_icon_item(gui_base->get_icon("HelpSearch", "EditorIcons"), TTR("Search"), HELP_SEARCH);
+	p->add_icon_shortcut(gui_base->get_icon("HelpSearch", "EditorIcons"), ED_SHORTCUT("editor/editor_help", TTR("Search"), KEY_F4), HELP_SEARCH);
 	p->add_separator();
 	p->add_icon_item(gui_base->get_icon("Instance", "EditorIcons"), TTR("Online Docs"), HELP_DOCS);
 	p->add_icon_item(gui_base->get_icon("Instance", "EditorIcons"), TTR("Q&A"), HELP_QA);

+ 0 - 1
editor/editor_node.h

@@ -180,7 +180,6 @@ private:
 		SETTINGS_HELP,
 		SCENE_TAB_CLOSE,
 
-		HELP_CLASSES,
 		HELP_SEARCH,
 		HELP_DOCS,
 		HELP_QA,

+ 2 - 31
editor/plugins/script_editor_plugin.cpp

@@ -50,8 +50,7 @@ void ScriptEditorBase::_bind_methods() {
 
 	ADD_SIGNAL(MethodInfo("name_changed"));
 	ADD_SIGNAL(MethodInfo("edited_script_changed"));
-	ADD_SIGNAL(MethodInfo("request_help_search", PropertyInfo(Variant::STRING, "topic")));
-	ADD_SIGNAL(MethodInfo("request_help_index"));
+	ADD_SIGNAL(MethodInfo("request_help", PropertyInfo(Variant::STRING, "topic")));
 	ADD_SIGNAL(MethodInfo("request_open_script_at_line", PropertyInfo(Variant::OBJECT, "script"), PropertyInfo(Variant::INT, "line")));
 	ADD_SIGNAL(MethodInfo("request_save_history"));
 	ADD_SIGNAL(MethodInfo("go_to_help", PropertyInfo(Variant::STRING, "what")));
@@ -975,10 +974,6 @@ void ScriptEditor::_menu_option(int p_option) {
 
 			help_search_dialog->popup_dialog();
 		} break;
-		case SEARCH_CLASSES: {
-
-			help_index->popup_dialog();
-		} break;
 		case SEARCH_WEBSITE: {
 
 			OS::get_singleton()->shell_open("https://docs.godotengine.org/");
@@ -1206,12 +1201,6 @@ void ScriptEditor::_menu_option(int p_option) {
 		if (help) {
 
 			switch (p_option) {
-
-				case SEARCH_CLASSES: {
-
-					help_index->popup_dialog();
-					help_index->call_deferred("select_class", help->get_class());
-				} break;
 				case HELP_SEARCH_FIND: {
 					help->popup_search();
 				} break;
@@ -1318,7 +1307,6 @@ void ScriptEditor::_notification(int p_what) {
 			EditorSettings::get_singleton()->connect("settings_changed", this, "_editor_settings_changed");
 			help_search->set_icon(get_icon("HelpSearch", "EditorIcons"));
 			site_search->set_icon(get_icon("Instance", "EditorIcons"));
-			class_search->set_icon(get_icon("ClassList", "EditorIcons"));
 
 			script_forward->set_icon(get_icon("Forward", "EditorIcons"));
 			script_back->set_icon(get_icon("Back", "EditorIcons"));
@@ -1330,7 +1318,6 @@ void ScriptEditor::_notification(int p_what) {
 			get_tree()->connect("tree_changed", this, "_tree_changed");
 			editor->get_inspector_dock()->connect("request_help", this, "_request_help");
 			editor->connect("request_help_search", this, "_help_search");
-			editor->connect("request_help_index", this, "_help_index");
 		} break;
 
 		case NOTIFICATION_EXIT_TREE: {
@@ -1350,7 +1337,6 @@ void ScriptEditor::_notification(int p_what) {
 
 			help_search->set_icon(get_icon("HelpSearch", "EditorIcons"));
 			site_search->set_icon(get_icon("Instance", "EditorIcons"));
-			class_search->set_icon(get_icon("ClassList", "EditorIcons"));
 
 			script_forward->set_icon(get_icon("Forward", "EditorIcons"));
 			script_back->set_icon(get_icon("Back", "EditorIcons"));
@@ -2006,7 +1992,7 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra
 	_save_layout();
 	se->connect("name_changed", this, "_update_script_names");
 	se->connect("edited_script_changed", this, "_script_changed");
-	se->connect("request_help_search", this, "_help_search");
+	se->connect("request_help", this, "_help_search");
 	se->connect("request_open_script_at_line", this, "_goto_script_line");
 	se->connect("go_to_help", this, "_help_class_goto");
 	se->connect("request_save_history", this, "_save_history");
@@ -2733,10 +2719,6 @@ void ScriptEditor::set_live_auto_reload_running_scripts(bool p_enabled) {
 	auto_reload_running_scripts = p_enabled;
 }
 
-void ScriptEditor::_help_index(String p_text) {
-	help_index->popup_dialog();
-}
-
 void ScriptEditor::_help_search(String p_text) {
 	help_search_dialog->popup_dialog(p_text);
 }
@@ -2842,7 +2824,6 @@ void ScriptEditor::_bind_methods() {
 	ClassDB::bind_method("_goto_script_line", &ScriptEditor::_goto_script_line);
 	ClassDB::bind_method("_goto_script_line2", &ScriptEditor::_goto_script_line2);
 	ClassDB::bind_method("_help_search", &ScriptEditor::_help_search);
-	ClassDB::bind_method("_help_index", &ScriptEditor::_help_index);
 	ClassDB::bind_method("_save_history", &ScriptEditor::_save_history);
 	ClassDB::bind_method("_copy_script_path", &ScriptEditor::_copy_script_path);
 
@@ -3077,12 +3058,6 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
 	menu_hb->add_child(site_search);
 	site_search->set_tooltip(TTR("Open Godot online documentation"));
 
-	class_search = memnew(ToolButton);
-	class_search->set_text(TTR("Classes"));
-	class_search->connect("pressed", this, "_menu_option", varray(SEARCH_CLASSES));
-	menu_hb->add_child(class_search);
-	class_search->set_tooltip(TTR("Search the class hierarchy."));
-
 	help_search = memnew(ToolButton);
 	help_search->set_text(TTR("Search Help"));
 	help_search->connect("pressed", this, "_menu_option", varray(SEARCH_HELP));
@@ -3168,10 +3143,6 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
 	add_child(help_search_dialog);
 	help_search_dialog->connect("go_to_help", this, "_help_class_goto");
 
-	help_index = memnew(EditorHelpIndex);
-	add_child(help_index);
-	help_index->connect("open_class", this, "_help_class_open");
-
 	find_in_files_dialog = memnew(FindInFilesDialog);
 	find_in_files_dialog->connect(FindInFilesDialog::SIGNAL_FIND_REQUESTED, this, "_start_find_in_files", varray(false));
 	find_in_files_dialog->connect(FindInFilesDialog::SIGNAL_REPLACE_REQUESTED, this, "_start_find_in_files", varray(true));

+ 2 - 3
editor/plugins/script_editor_plugin.h

@@ -34,6 +34,7 @@
 #include "core/script_language.h"
 #include "editor/code_editor.h"
 #include "editor/editor_help.h"
+#include "editor/editor_help_search.h"
 #include "editor/editor_plugin.h"
 #include "editor/script_create_dialog.h"
 #include "scene/gui/item_list.h"
@@ -155,7 +156,6 @@ class ScriptEditor : public PanelContainer {
 		DEBUG_SHOW_KEEP_OPEN,
 		DEBUG_WITH_EXTERNAL_EDITOR,
 		SEARCH_HELP,
-		SEARCH_CLASSES,
 		SEARCH_WEBSITE,
 		HELP_SEARCH_FIND,
 		HELP_SEARCH_FIND_NEXT,
@@ -200,7 +200,6 @@ class ScriptEditor : public PanelContainer {
 
 	Button *help_search;
 	Button *site_search;
-	Button *class_search;
 	EditorHelpSearch *help_search_dialog;
 
 	ItemList *script_list;
@@ -254,7 +253,7 @@ class ScriptEditor : public PanelContainer {
 	Vector<ScriptHistory> history;
 	int history_pos;
 
-	EditorHelpIndex *help_index;
+	Vector<String> previous_scripts;
 
 	void _tab_changed(int p_which);
 	void _menu_option(int p_option);

+ 1 - 1
editor/plugins/script_text_editor.cpp

@@ -1049,7 +1049,7 @@ void ScriptTextEditor::_edit_option(int p_op) {
 			if (text == "")
 				text = tx->get_word_under_cursor();
 			if (text != "") {
-				emit_signal("request_help_search", text);
+				emit_signal("request_help", text);
 			}
 		} break;
 		case LOOKUP_SYMBOL: {

+ 1 - 1
scene/gui/tree.cpp

@@ -1419,7 +1419,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
 
 		while (c) {
 
-			if (cache.draw_relationship_lines == 1 && (c->get_parent() != root || !hide_root)) {
+			if (cache.draw_relationship_lines == 1 && (!hide_root || c->parent != root)) {
 				int root_ofs = children_pos.x + ((p_item->disable_folding || hide_folding) ? cache.hseparation : cache.item_margin);
 				int parent_ofs = p_pos.x + ((p_item->disable_folding || hide_folding) ? cache.hseparation : cache.item_margin);
 				Point2i root_pos = Point2i(root_ofs, children_pos.y + label_h / 2) - cache.offset + p_draw_ofs;