From 792502db84bd49f59ec6e6b33b40a89c89c1e12d Mon Sep 17 00:00:00 2001 From: Serhii Snitsaruk Date: Tue, 28 May 2024 17:21:35 +0200 Subject: [PATCH] Editor: Add tab context menu option "Open owner scene" --- editor/limbo_ai_editor_plugin.cpp | 12 +++ editor/limbo_ai_editor_plugin.h | 3 + editor/owner_picker.cpp | 141 ++++++++++++++++++++++++++++++ editor/owner_picker.h | 46 ++++++++++ register_types.cpp | 1 + 5 files changed, 203 insertions(+) create mode 100644 editor/owner_picker.cpp create mode 100644 editor/owner_picker.h diff --git a/editor/limbo_ai_editor_plugin.cpp b/editor/limbo_ai_editor_plugin.cpp index e635956..4ce9ead 100644 --- a/editor/limbo_ai_editor_plugin.cpp +++ b/editor/limbo_ai_editor_plugin.cpp @@ -1014,6 +1014,7 @@ void LimboAIEditor::_tab_input(const Ref &p_input) { void LimboAIEditor::_show_tab_context_menu() { tab_menu->clear(); tab_menu->add_item(TTR("Show in FileSystem"), TabMenu::TAB_SHOW_IN_FILESYSTEM); + tab_menu->add_item(TTR("Open Owner Scene"), TabMenu::TAB_OPEN_OWNER); tab_menu->add_separator(); tab_menu->add_item(TTR("Close Tab"), TabMenu::TAB_CLOSE); tab_menu->add_item(TTR("Close Other Tabs"), TabMenu::TAB_CLOSE_OTHER); @@ -1034,6 +1035,14 @@ void LimboAIEditor::_tab_menu_option_selected(int p_id) { FS_DOCK_SELECT_FILE(path.get_slice("::", 0)); } } break; + case TAB_OPEN_OWNER: { + Ref bt = history[idx_history]; + ERR_FAIL_NULL(bt); + String bt_path = bt->get_path(); + if (!bt_path.is_empty()) { + owner_picker->show(bt_path); + } + } break; case TAB_CLOSE: { _tab_closed(idx_history); } break; @@ -1450,6 +1459,9 @@ LimboAIEditor::LimboAIEditor() { tab_menu = memnew(PopupMenu); add_child(tab_menu); + owner_picker = memnew(OwnerPicker); + add_child(owner_picker); + hsc = memnew(HSplitContainer); hsc->set_h_size_flags(SIZE_EXPAND_FILL); hsc->set_v_size_flags(SIZE_EXPAND_FILL); diff --git a/editor/limbo_ai_editor_plugin.h b/editor/limbo_ai_editor_plugin.h index fce4a28..e372552 100644 --- a/editor/limbo_ai_editor_plugin.h +++ b/editor/limbo_ai_editor_plugin.h @@ -17,6 +17,7 @@ #include "../bt/behavior_tree.h" #include "../bt/tasks/bt_task.h" #include "editor_property_variable_name.h" +#include "owner_picker.h" #include "task_palette.h" #include "task_tree.h" @@ -101,6 +102,7 @@ private: enum TabMenu { TAB_SHOW_IN_FILESYSTEM, + TAB_OPEN_OWNER, TAB_CLOSE, TAB_CLOSE_OTHER, TAB_CLOSE_RIGHT, @@ -139,6 +141,7 @@ private: HBoxContainer *tab_bar_container; TabBar *tab_bar; PopupMenu *tab_menu; + OwnerPicker *owner_picker; HSplitContainer *hsc; TaskTree *task_tree; VBoxContainer *banners; diff --git a/editor/owner_picker.cpp b/editor/owner_picker.cpp new file mode 100644 index 0000000..04f8d8a --- /dev/null +++ b/editor/owner_picker.cpp @@ -0,0 +1,141 @@ +/** + * owner_picker.cpp + * ============================================================================= + * Copyright 2021-2024 Serhii Snitsaruk + * + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. + * ============================================================================= + */ + +#ifdef TOOLS_ENABLED + +#include "owner_picker.h" + +#include "../util/limbo_compat.h" + +#ifdef LIMBOAI_MODULE +#include "editor/editor_file_system.h" +#include "editor/editor_interface.h" +#elif LIMBOAI_GDEXTENSION +#include +#include +#include +#include +#endif + +Vector OwnerPicker::find_owners(const String &p_path) const { + Vector owners; + List dirs; + + dirs.push_back(EDITOR_FILE_SYSTEM()->get_filesystem()); + + while (dirs.size() > 0) { + EditorFileSystemDirectory *efd = dirs.front()->get(); + dirs.pop_front(); + + for (int i = 0; i < efd->get_file_count(); i++) { + String file_path = efd->get_file_path(i); + + Vector deps; +#ifdef LIMBOAI_MODULE + deps = efd->get_file_deps(i); +#elif LIMBOAI_GDEXTENSION + PackedStringArray res_deps = ResourceLoader::get_singleton()->get_dependencies(file_path); + for (String dep : res_deps) { + if (dep.begins_with("uid://")) { + dep = dep.get_slice("::", 2); + } + deps.append(dep); + } +#endif // LIMBOAI_MODULE + + for (int j = 0; j < deps.size(); j++) { + if (deps[j] == p_path) { + owners.append(file_path); + break; + } + } + } + + for (int k = 0; k < efd->get_subdir_count(); k++) { + dirs.push_back(efd->get_subdir(k)); + } + } + + return owners; +} + +void OwnerPicker::show(const String &p_path) { + if (p_path.is_empty()) { + return; + } + + owners_item_list->clear(); + + Vector owners = find_owners(p_path); + for (int i = 0; i < owners.size(); i++) { + owners_item_list->add_item(owners[i]); + } + + if (owners_item_list->get_item_count() > 0) { + owners_item_list->select(0); + owners_item_list->ensure_current_is_visible(); + } + + if (owners_item_list->get_item_count() == 1) { + // Open scene immediately if there is only one owner scene. + _selection_confirmed(); + } else if (owners_item_list->get_item_count() == 0) { + owners_item_list->hide(); + set_title(TTR("Alert!")); + set_text(TTR("This behavior tree is not used by any scene.")); + reset_size(); + popup_centered(); + } else { + owners_item_list->show(); + set_title(TTR("Pick owner")); + set_text(""); + reset_size(); + popup_centered_ratio(0.3); + owners_item_list->grab_focus(); + } +} + +void OwnerPicker::_item_activated(int p_item) { + hide(); + emit_signal("confirmed"); +} + +void OwnerPicker::_selection_confirmed() { + for (int idx : owners_item_list->get_selected_items()) { + String owner_path = owners_item_list->get_item_text(idx); + if (RESOURCE_EXISTS(owner_path, "PackedScene")) { + EditorInterface::get_singleton()->open_scene_from_path(owner_path); + } else { + RESOURCE_LOAD(owner_path, ""); + } + } +} + +void OwnerPicker::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_READY: { + owners_item_list->connect("item_activated", callable_mp(this, &OwnerPicker::_item_activated)); + connect("confirmed", callable_mp(this, &OwnerPicker::_selection_confirmed)); + } break; + } +} + +void OwnerPicker::_bind_methods() { +} + +OwnerPicker::OwnerPicker() { + owners_item_list = memnew(ItemList); + // Note: In my tests, editor couldn't process open request for multiple scenes at once. + owners_item_list->set_select_mode(ItemList::SELECT_SINGLE); + add_child(owners_item_list); +} + +#endif // TOOLS_ENABLED \ No newline at end of file diff --git a/editor/owner_picker.h b/editor/owner_picker.h new file mode 100644 index 0000000..3841d62 --- /dev/null +++ b/editor/owner_picker.h @@ -0,0 +1,46 @@ +/** + * owner_picker.h + * ============================================================================= + * Copyright 2021-2024 Serhii Snitsaruk + * + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. + * ============================================================================= + */ + +#ifndef OWNER_PICKER_H +#define OWNER_PICKER_H + +#ifdef LIMBOAI_MODULE +#include "scene/gui/dialogs.h" +#include "scene/gui/item_list.h" +#elif LIMBOAI_GDEXTENSION +#include +#include +#include +using namespace godot; +#endif + +class OwnerPicker : public AcceptDialog { + GDCLASS(OwnerPicker, AcceptDialog); + +private: + ItemList *owners_item_list; + + void _item_activated(int p_item); + void _selection_confirmed(); + +protected: + static void _bind_methods(); + + void _notification(int p_what); + +public: + Vector find_owners(const String &p_path) const; + void show(const String &p_path); + + OwnerPicker(); +}; + +#endif // OWNER_PICKER_H diff --git a/register_types.cpp b/register_types.cpp index d209c9f..967f7e9 100644 --- a/register_types.cpp +++ b/register_types.cpp @@ -263,6 +263,7 @@ void initialize_limboai_module(ModuleInitializationLevel p_level) { GDREGISTER_CLASS(EditorInspectorPluginBBPlan); GDREGISTER_CLASS(EditorPropertyVariableName); GDREGISTER_CLASS(EditorInspectorPluginVariableName); + GDREGISTER_CLASS(OwnerPicker); GDREGISTER_CLASS(LimboAIEditor); GDREGISTER_CLASS(LimboAIEditorPlugin); #endif // LIMBOAI_GDEXTENSION