Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Tree Search Functionality with Highlighting and Filtering #229

Merged
merged 3 commits into from
Oct 13, 2024
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
34 changes: 34 additions & 0 deletions editor/limbo_ai_editor_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,10 @@ void LimboAIEditor::edit_bt(const Ref<BehaviorTree> &p_behavior_tree, bool p_for
p_behavior_tree->editor_set_section_unfold("blackboard_plan", true);
p_behavior_tree->notify_property_list_changed();
#endif // LIMBOAI_MODULE
// Remember current search info.
if (idx_history >= 0 && idx_history < history.size() && task_tree->get_bt() == history[idx_history]) {
tab_search_context.insert(history[idx_history], task_tree->tree_search_get_search_info());
}

task_tree->load_bt(p_behavior_tree);

Expand All @@ -280,6 +284,16 @@ void LimboAIEditor::edit_bt(const Ref<BehaviorTree> &p_behavior_tree, bool p_for
task_tree->show();
task_palette->show();

// Restore search info from [tab_search_context].
if (idx_history >= 0 && idx_history < history.size()) {
if (tab_search_context.has(history[idx_history])) {
task_tree->tree_search_set_search_info(tab_search_context[history[idx_history]]);
}
else {
task_tree->tree_search_set_search_info(TreeSearch::SearchInfo());
}
}

_update_tabs();
}

Expand Down Expand Up @@ -457,6 +471,8 @@ void LimboAIEditor::_process_shortcut_input(const Ref<InputEvent> &p_event) {
_on_save_pressed();
} else if (LW_IS_SHORTCUT("limbo_ai/load_behavior_tree", p_event)) {
_popup_file_dialog(load_dialog);
} else if (LW_IS_SHORTCUT("limbo_ai/find_task", p_event)) {
task_tree->tree_search_show_and_focus();
} else {
handled = false;
}
Expand Down Expand Up @@ -799,6 +815,9 @@ void LimboAIEditor::_misc_option_selected(int p_id) {
EDITOR_FILE_SYSTEM()->scan();
EDIT_SCRIPT(template_path);
} break;
case MISC_SEARCH_TREE: {
task_tree->tree_search_show_and_focus();
} break;
}
}

Expand Down Expand Up @@ -1045,13 +1064,22 @@ void LimboAIEditor::_tab_closed(int p_tab) {
if (history_bt.is_valid() && history_bt->is_connected(LW_NAME(changed), callable_mp(this, &LimboAIEditor::_mark_as_dirty))) {
history_bt->disconnect(LW_NAME(changed), callable_mp(this, &LimboAIEditor::_mark_as_dirty));
}
if (tab_search_context.has(history_bt)) {
tab_search_context.erase(history_bt);
}

history.remove_at(p_tab);
idx_history = MIN(idx_history, history.size() - 1);
TreeSearch::SearchInfo search_info_opened_tab;
if (idx_history < 0) {
_disable_editing();
} else {
EDIT_RESOURCE(history[idx_history]);
ERR_FAIL_COND(!tab_search_context.has(history[idx_history]));
search_info_opened_tab = tab_search_context[history[idx_history]];
}

task_tree->tree_search_set_search_info(search_info_opened_tab);
_update_tabs();
}

Expand Down Expand Up @@ -1319,6 +1347,9 @@ void LimboAIEditor::_update_misc_menu() {
misc_menu->add_item(
FILE_EXISTS(_get_script_template_path()) ? TTR("Edit Script Template") : TTR("Create Script Template"),
MISC_CREATE_SCRIPT_TEMPLATE);

misc_menu->add_separator();
misc_menu->add_icon_shortcut(theme_cache.search_icon, LW_GET_SHORTCUT("limbo_ai/find_task"), MISC_SEARCH_TREE);
}

void LimboAIEditor::_update_banners() {
Expand Down Expand Up @@ -1381,6 +1412,7 @@ void LimboAIEditor::_do_update_theme_item_cache() {
theme_cache.cut_icon = get_theme_icon(LW_NAME(ActionCut), LW_NAME(EditorIcons));
theme_cache.copy_icon = get_theme_icon(LW_NAME(ActionCopy), LW_NAME(EditorIcons));
theme_cache.paste_icon = get_theme_icon(LW_NAME(ActionPaste), LW_NAME(EditorIcons));
theme_cache.search_icon = get_theme_icon(LW_NAME(Search), LW_NAME(EditorIcons));

theme_cache.behavior_tree_icon = LimboUtility::get_singleton()->get_task_icon("BehaviorTree");
theme_cache.percent_icon = LimboUtility::get_singleton()->get_task_icon("LimboPercent");
Expand Down Expand Up @@ -1512,6 +1544,8 @@ LimboAIEditor::LimboAIEditor() {
LW_SHORTCUT("limbo_ai/open_debugger", TTR("Open Debugger"), (Key)(LW_KEY_MASK(CMD_OR_CTRL) | LW_KEY_MASK(ALT) | LW_KEY(D)));
LW_SHORTCUT("limbo_ai/jump_to_owner", TTR("Jump to Owner"), (Key)(LW_KEY_MASK(CMD_OR_CTRL) | LW_KEY(J)));
LW_SHORTCUT("limbo_ai/close_tab", TTR("Close Tab"), (Key)(LW_KEY_MASK(CMD_OR_CTRL) | LW_KEY(W)));
LW_SHORTCUT("limbo_ai/find_task", TTR("Find Task"), (Key)(LW_KEY_MASK(CMD_OR_CTRL) | LW_KEY(F)));
LW_SHORTCUT("limbo_ai/hide_tree_search", TTR("Close Search"), (Key)(LW_KEY(ESCAPE)));

set_process_shortcut_input(true);

Expand Down
6 changes: 5 additions & 1 deletion editor/limbo_ai_editor_plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "owner_picker.h"
#include "task_palette.h"
#include "task_tree.h"
#include "tree_search.h"

#ifdef LIMBOAI_MODULE
#include "core/object/class_db.h"
Expand Down Expand Up @@ -47,6 +48,7 @@

#ifdef LIMBOAI_GDEXTENSION
#include "godot_cpp/classes/accept_dialog.hpp"
#include <godot_cpp/classes/config_file.hpp>
#include <godot_cpp/classes/control.hpp>
#include <godot_cpp/classes/editor_plugin.hpp>
#include <godot_cpp/classes/editor_spin_slider.hpp>
Expand All @@ -63,7 +65,6 @@
#include <godot_cpp/classes/texture2d.hpp>
#include <godot_cpp/variant/packed_string_array.hpp>
#include <godot_cpp/variant/variant.hpp>
#include <godot_cpp/classes/config_file.hpp>

using namespace godot;

Expand Down Expand Up @@ -100,6 +101,7 @@ class LimboAIEditor : public Control {
MISC_LAYOUT_WIDESCREEN_OPTIMIZED,
MISC_PROJECT_SETTINGS,
MISC_CREATE_SCRIPT_TEMPLATE,
MISC_SEARCH_TREE
};

enum TabMenu {
Expand Down Expand Up @@ -134,12 +136,14 @@ class LimboAIEditor : public Control {
Ref<Texture2D> cut_icon;
Ref<Texture2D> copy_icon;
Ref<Texture2D> paste_icon;
Ref<Texture2D> search_icon;
} theme_cache;

EditorPlugin *plugin;
EditorLayout editor_layout;
Vector<Ref<BehaviorTree>> history;
int idx_history;
HashMap<Ref<BehaviorTree>, TreeSearch::SearchInfo> tab_search_context;
bool updating_tabs = false;
bool request_update_tabs = false;
HashSet<Ref<BehaviorTree>> dirty;
Expand Down
52 changes: 46 additions & 6 deletions editor/task_tree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,23 @@
#include "../bt/tasks/composites/bt_probability_selector.h"
#include "../util/limbo_compat.h"
#include "../util/limbo_utility.h"
#include "tree_search.h"

#ifdef LIMBOAI_MODULE
#include "core/object/script_language.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/box_container.h"
#include "scene/gui/texture_rect.h"
#include "scene/gui/label.h"
#include "scene/gui/texture_rect.h"
#endif // LIMBOAI_MODULE

#ifdef LIMBOAI_GDEXTENSION
#include <godot_cpp/classes/editor_interface.hpp>
#include <godot_cpp/classes/script.hpp>
#include <godot_cpp/classes/h_box_container.hpp>
#include <godot_cpp/classes/v_box_container.hpp>
#include <godot_cpp/classes/texture_rect.hpp>
#include <godot_cpp/classes/label.hpp>
#include <godot_cpp/classes/script.hpp>
#include <godot_cpp/classes/texture_rect.hpp>
#include <godot_cpp/classes/v_box_container.hpp>
using namespace godot;
#endif // LIMBOAI_GDEXTENSION

Expand All @@ -46,6 +47,12 @@ TreeItem *TaskTree::_create_tree(const Ref<BTTask> &p_task, TreeItem *p_parent,
_create_tree(p_task->get_child(i), item);
}
_update_item(item);

// update TreeSearch if root task was created
if (tree->get_root() == item) {
tree_search->update_search(tree);
}

return item;
}

Expand Down Expand Up @@ -105,6 +112,7 @@ void TaskTree::_update_item(TreeItem *p_item) {
if (!warning_text.is_empty()) {
p_item->add_button(0, theme_cache.task_warning_icon, 0, false, warning_text);
}
tree_search->notify_item_edited(p_item); // this is necessary to preserve custom drawing from tree search.
}

void TaskTree::_update_tree() {
Expand Down Expand Up @@ -434,7 +442,7 @@ void TaskTree::_normalize_drop(TreeItem *item, int type, int &to_pos, Ref<BTTask
to_pos = to_task->get_index();
{
Vector<Ref<BTTask>> selected = get_selected_tasks();
if (to_task == selected[selected.size()-1]) {
if (to_task == selected[selected.size() - 1]) {
to_pos += 1;
}
}
Expand Down Expand Up @@ -530,6 +538,8 @@ void TaskTree::_notification(int p_what) {
tree->connect("multi_selected", callable_mp(this, &TaskTree::_on_item_selected).unbind(3), CONNECT_DEFERRED);
tree->connect("item_activated", callable_mp(this, &TaskTree::_on_item_activated));
tree->connect("item_collapsed", callable_mp(this, &TaskTree::_on_item_collapsed));
tree_search_panel->connect("update_requested", callable_mp(tree_search.ptr(), &TreeSearch::update_search).bind(tree));
tree_search_panel->connect("visibility_changed", callable_mp(tree_search.ptr(), &TreeSearch::update_search).bind(tree));
} break;
case NOTIFICATION_THEME_CHANGED: {
_do_update_theme_item_cache();
Expand Down Expand Up @@ -562,12 +572,38 @@ void TaskTree::_bind_methods() {
PropertyInfo(Variant::INT, "type")));
}

// TreeSearch API
void TaskTree::tree_search_show_and_focus() {
ERR_FAIL_NULL(tree_search);
tree_search_panel->set_visible(true);
tree_search_panel->focus_editor();
}

TreeSearch::SearchInfo TaskTree::tree_search_get_search_info() const {
if (!tree_search.is_valid()) {
return TreeSearch::SearchInfo();
}
return tree_search_panel->get_search_info();
}

void TaskTree::tree_search_set_search_info(const TreeSearch::SearchInfo &p_search_info) {
ERR_FAIL_NULL(tree_search);
tree_search_panel->set_search_info(p_search_info);
}

// TreeSearch Api ^

TaskTree::TaskTree() {
editable = true;
updating_tree = false;

VBoxContainer *vbox_container = memnew(VBoxContainer);
add_child(vbox_container);
vbox_container->set_anchors_preset(PRESET_FULL_RECT);

tree = memnew(Tree);
add_child(tree);
tree->set_v_size_flags(Control::SIZE_EXPAND_FILL);
vbox_container->add_child(tree);
tree->set_columns(2);
tree->set_column_expand(0, true);
tree->set_column_expand(1, false);
Expand All @@ -578,6 +614,10 @@ TaskTree::TaskTree() {
tree->set_select_mode(Tree::SelectMode::SELECT_MULTI);

tree->set_drag_forwarding(callable_mp(this, &TaskTree::_get_drag_data_fw), callable_mp(this, &TaskTree::_can_drop_data_fw), callable_mp(this, &TaskTree::_drop_data_fw));

tree_search_panel = memnew(TreeSearchPanel);
tree_search = Ref(memnew(TreeSearch(tree_search_panel)));
vbox_container->add_child(tree_search_panel);
}

TaskTree::~TaskTree() {
Expand Down
14 changes: 13 additions & 1 deletion editor/task_tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@
* =============================================================================
*/

#ifndef TASK_TREE_H
#define TASK_TREE_H

#ifdef TOOLS_ENABLED

#include "../bt/behavior_tree.h"
#include "tree_search.h"

#ifdef LIMBOAI_MODULE
#include "scene/gui/control.h"
Expand Down Expand Up @@ -43,6 +47,9 @@ class TaskTree : public Control {
bool updating_tree;
HashMap<RECT_CACHE_KEY, Rect2> probability_rect_cache;

Ref<TreeSearch> tree_search;
TreeSearchPanel *tree_search_panel;

struct ThemeCache {
Ref<Font> comment_font;
Ref<Font> name_font;
Expand Down Expand Up @@ -96,16 +103,21 @@ class TaskTree : public Control {
Ref<BTTask> get_selected() const;
Vector<Ref<BTTask>> get_selected_tasks() const;
void clear_selection();

Rect2 get_selected_probability_rect() const;
double get_selected_probability_weight() const;
double get_selected_probability_percent() const;
bool selected_has_probability() const;

// TreeSearch API
void tree_search_show_and_focus();
TreeSearch::SearchInfo tree_search_get_search_info() const;
void tree_search_set_search_info(const TreeSearch::SearchInfo &p_search_info);

virtual bool editor_can_reload_from_file() { return false; }

TaskTree();
~TaskTree();
};

#endif // ! TOOLS_ENABLED
#endif // ! TASK_TREE_H
Loading
Loading