Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 107 additions & 41 deletions editor/editor_help_search.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ void EditorHelpSearch::_update_results() {
search_flags |= SEARCH_SHOW_HIERARCHY;
}

search = Ref<Runner>(memnew(Runner(results_tree, results_tree, term, search_flags)));
search = Ref<Runner>(memnew(Runner(results_tree, results_tree, &tree_cache, term, search_flags)));
set_process(true);
}

Expand Down Expand Up @@ -96,6 +96,7 @@ void EditorHelpSearch::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_VISIBILITY_CHANGED: {
if (!is_visible()) {
tree_cache.clear();
callable_mp(results_tree, &Tree::clear).call_deferred(); // Wait for the Tree's mouse event propagation.
get_ok_button()->set_disabled(true);
EditorSettings::get_singleton()->set_project_metadata("dialog_bounds", "search_help", Rect2(get_position(), get_size()));
Expand Down Expand Up @@ -258,6 +259,13 @@ EditorHelpSearch::EditorHelpSearch() {
vbox->add_child(results_tree, true);
}

void EditorHelpSearch::TreeCache::clear() {
for (const KeyValue<String, TreeItem *> &E : item_cache) {
memdelete(E.value);
}
item_cache.clear();
}

bool EditorHelpSearch::Runner::_is_class_disabled_by_feature_profile(const StringName &p_class) {
Ref<EditorFeatureProfile> profile = EditorFeatureProfileManager::get_singleton()->get_current_profile();
if (profile.is_null()) {
Expand Down Expand Up @@ -436,11 +444,42 @@ bool EditorHelpSearch::Runner::_phase_match_classes() {
return iterator_stack.is_empty();
}

void EditorHelpSearch::Runner::_populate_cache() {
root_item = results_tree->get_root();

if (root_item) {
LocalVector<TreeItem *> stack;

// Add children of root item to stack.
for (TreeItem *child = root_item->get_first_child(); child; child = child->get_next()) {
stack.push_back(child);
}

// Traverse stack and cache items.
while (!stack.is_empty()) {
TreeItem *cur_item = stack[stack.size() - 1];
stack.resize(stack.size() - 1);

// Add to the cache.
tree_cache->item_cache.insert(cur_item->get_metadata(0).operator String(), cur_item);

// Add any children to the stack.
for (TreeItem *child = cur_item->get_first_child(); child; child = child->get_next()) {
stack.push_back(child);
}

// Remove from parent.
cur_item->get_parent()->remove_child(cur_item);
}
} else {
root_item = results_tree->create_item();
}
}

bool EditorHelpSearch::Runner::_phase_class_items_init() {
iterator_match = matches.begin();

results_tree->clear();
root_item = results_tree->create_item();
_populate_cache();
class_items.clear();

return true;
Expand Down Expand Up @@ -618,27 +657,54 @@ TreeItem *EditorHelpSearch::Runner::_create_class_hierarchy(const ClassMatch &p_
return class_item;
}

bool EditorHelpSearch::Runner::_find_or_create_item(TreeItem *p_parent, const String &p_item_meta, TreeItem *&r_item) {
// Attempt to find in cache.
if (tree_cache->item_cache.has(p_item_meta)) {
r_item = tree_cache->item_cache[p_item_meta];

// Remove from cache.
tree_cache->item_cache.erase(p_item_meta);

@YuriSizov YuriSizov Jan 16, 2024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't sure why we would clear the cache here, since the HashMap should update existing items anyway on insert. But removing this line doesn't introduce any performance improvements (and I may have introduced a leak with this, didn't debug much).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would risk double deleting items on close as any item already in the tree is deleted when clearing it, but might work out as it's deferred I realize, but unsure

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, doesn't matter if removing it doesn't help. Just thought I'd mention it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you 🙂


// Add to tree.
p_parent->add_child(r_item);

return false;
} else {
// Otherwise create item.
r_item = results_tree->create_item(p_parent);

return true;
}
}

TreeItem *EditorHelpSearch::Runner::_create_class_item(TreeItem *p_parent, const DocData::ClassDoc *p_doc, bool p_gray) {
String tooltip = DTR(p_doc->brief_description.strip_edges());

TreeItem *item = results_tree->create_item(p_parent);
item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_doc->name));
item->set_text(0, p_doc->name);
item->set_text(1, TTR("Class"));
item->set_tooltip_text(0, tooltip);
item->set_tooltip_text(1, tooltip);
item->set_metadata(0, "class_name:" + p_doc->name);
const String item_meta = "class_name:" + p_doc->name;

TreeItem *item = nullptr;
if (_find_or_create_item(p_parent, item_meta, item)) {
item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_doc->name));
item->set_text(0, p_doc->name);
item->set_text(1, TTR("Class"));
item->set_tooltip_text(0, tooltip);
item->set_tooltip_text(1, tooltip);
item->set_metadata(0, item_meta);
if (p_doc->is_deprecated) {
Ref<Texture2D> error_icon = ui_service->get_editor_theme_icon(SNAME("StatusError"));
item->add_button(0, error_icon, 0, false, TTR("This class is marked as deprecated."));
} else if (p_doc->is_experimental) {
Ref<Texture2D> warning_icon = ui_service->get_editor_theme_icon(SNAME("NodeWarning"));
item->add_button(0, warning_icon, 0, false, TTR("This class is marked as experimental."));
}
}

if (p_gray) {
item->set_custom_color(0, disabled_color);
item->set_custom_color(1, disabled_color);
}

if (p_doc->is_deprecated) {
Ref<Texture2D> error_icon = ui_service->get_editor_theme_icon("StatusError");
item->add_button(0, error_icon, 0, false, TTR("This class is marked as deprecated."));
} else if (p_doc->is_experimental) {
Ref<Texture2D> warning_icon = ui_service->get_editor_theme_icon("NodeWarning");
item->add_button(0, warning_icon, 0, false, TTR("This class is marked as experimental."));
} else {
Comment thread
YuriSizov marked this conversation as resolved.
Outdated
item->clear_custom_color(0);
item->clear_custom_color(1);
}

_match_item(item, p_doc->name);
Expand Down Expand Up @@ -679,30 +745,29 @@ TreeItem *EditorHelpSearch::Runner::_create_theme_property_item(TreeItem *p_pare
}

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_text, const String &p_type, const String &p_metatype, const String &p_tooltip, bool is_deprecated, bool is_experimental) {
Ref<Texture2D> icon;
String text;
if (search_flags & SEARCH_SHOW_HIERARCHY) {
icon = ui_service->get_editor_theme_icon(p_icon);
text = p_text;
} else {
icon = ui_service->get_editor_theme_icon(p_icon);
text = p_class_name + "." + p_text;
const String item_meta = "class_" + p_metatype + ":" + p_class_name + ":" + p_name;

TreeItem *item = nullptr;
if (_find_or_create_item(p_parent, item_meta, item)) {
item->set_icon(0, ui_service->get_editor_theme_icon(p_icon));
item->set_text(1, TTRGET(p_type));
item->set_tooltip_text(0, p_tooltip);
item->set_tooltip_text(1, p_tooltip);
item->set_metadata(0, item_meta);

if (is_deprecated) {
Ref<Texture2D> error_icon = ui_service->get_editor_theme_icon(SNAME("StatusError"));
item->add_button(0, error_icon, 0, false, TTR("This member is marked as deprecated."));
} else if (is_experimental) {
Ref<Texture2D> warning_icon = ui_service->get_editor_theme_icon(SNAME("NodeWarning"));
item->add_button(0, warning_icon, 0, false, TTR("This member is marked as experimental."));
}
}

TreeItem *item = results_tree->create_item(p_parent);
item->set_icon(0, icon);
item->set_text(0, text);
item->set_text(1, TTRGET(p_type));
item->set_tooltip_text(0, p_tooltip);
item->set_tooltip_text(1, p_tooltip);
item->set_metadata(0, "class_" + p_metatype + ":" + p_class_name + ":" + p_name);

if (is_deprecated) {
Ref<Texture2D> error_icon = ui_service->get_editor_theme_icon("StatusError");
item->add_button(0, error_icon, 0, false, TTR("This member is marked as deprecated."));
} else if (is_experimental) {
Ref<Texture2D> warning_icon = ui_service->get_editor_theme_icon("NodeWarning");
item->add_button(0, warning_icon, 0, false, TTR("This member is marked as experimental."));
if (search_flags & SEARCH_SHOW_HIERARCHY) {
item->set_text(0, p_text);
} else {
item->set_text(0, p_class_name + "." + p_text);
}

_match_item(item, p_name);
Expand All @@ -721,9 +786,10 @@ bool EditorHelpSearch::Runner::work(uint64_t slot) {
return true;
}

EditorHelpSearch::Runner::Runner(Control *p_icon_service, Tree *p_results_tree, const String &p_term, int p_search_flags) :
EditorHelpSearch::Runner::Runner(Control *p_icon_service, Tree *p_results_tree, TreeCache *p_tree_cache, const String &p_term, int p_search_flags) :
ui_service(p_icon_service),
results_tree(p_results_tree),
tree_cache(p_tree_cache),
term((p_search_flags & SEARCH_CASE_SENSITIVE) == 0 ? p_term.strip_edges().to_lower() : p_term.strip_edges()),
search_flags(p_search_flags),
disabled_color(ui_service->get_theme_color(SNAME("font_disabled_color"), EditorStringName(Editor))) {
Expand Down
16 changes: 15 additions & 1 deletion editor/editor_help_search.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,16 @@ class EditorHelpSearch : public ConfirmationDialog {
class Runner;
Ref<Runner> search;

struct TreeCache {
HashMap<String, TreeItem *> item_cache;

void clear();

~TreeCache() {
clear();
}
} tree_cache;

void _update_results();

void _search_box_gui_input(const Ref<InputEvent> &p_event);
Expand Down Expand Up @@ -117,6 +127,7 @@ class EditorHelpSearch::Runner : public RefCounted {

Control *ui_service = nullptr;
Tree *results_tree = nullptr;
TreeCache *tree_cache = nullptr;
String term;
Vector<String> terms;
int search_flags;
Expand All @@ -134,6 +145,9 @@ class EditorHelpSearch::Runner : public RefCounted {

bool _is_class_disabled_by_feature_profile(const StringName &p_class);

void _populate_cache();
bool _find_or_create_item(TreeItem *p_parent, const String &p_item_meta, TreeItem *&r_item);

bool _slice();
bool _phase_match_classes_init();
bool _phase_match_classes();
Expand Down Expand Up @@ -162,7 +176,7 @@ class EditorHelpSearch::Runner : public RefCounted {
public:
bool work(uint64_t slot = 100000);

Runner(Control *p_icon_service, Tree *p_results_tree, const String &p_term, int p_search_flags);
Runner(Control *p_icon_service, Tree *p_results_tree, TreeCache *p_tree_cache, const String &p_term, int p_search_flags);
};

#endif // EDITOR_HELP_SEARCH_H