Merge pull request #122 from limbonaut/open-owner-scene
Editor: Add tab context menu option "Jump to Owner"
This commit is contained in:
commit
08ad6c1d99
|
@ -345,48 +345,63 @@ void LimboAIEditor::_process_shortcut_input(const Ref<InputEvent> &p_event) {
|
|||
return;
|
||||
}
|
||||
|
||||
bool handled = false;
|
||||
|
||||
// * Global shortcuts.
|
||||
|
||||
if (LW_IS_SHORTCUT("limbo_ai/open_debugger", p_event)) {
|
||||
_misc_option_selected(MISC_OPEN_DEBUGGER);
|
||||
handled = true;
|
||||
}
|
||||
|
||||
// * When editor is on screen.
|
||||
|
||||
if (!handled && is_visible_in_tree()) {
|
||||
if (LW_IS_SHORTCUT("limbo_ai/jump_to_owner", p_event)) {
|
||||
_tab_menu_option_selected(TAB_JUMP_TO_OWNER);
|
||||
handled = true;
|
||||
} else if (LW_IS_SHORTCUT("limbo_ai/close_tab", p_event)) {
|
||||
_tab_menu_option_selected(TAB_CLOSE);
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
// * When editor is focused.
|
||||
|
||||
if (!handled && (has_focus() || (get_viewport()->gui_get_focus_owner() && is_ancestor_of(get_viewport()->gui_get_focus_owner())))) {
|
||||
handled = true;
|
||||
if (LW_IS_SHORTCUT("limbo_ai/rename_task", p_event)) {
|
||||
_action_selected(ACTION_RENAME);
|
||||
} else if (LW_IS_SHORTCUT("limbo_ai/cut_task", p_event)) {
|
||||
_action_selected(ACTION_CUT);
|
||||
} else if (LW_IS_SHORTCUT("limbo_ai/copy_task", p_event)) {
|
||||
_action_selected(ACTION_COPY);
|
||||
} else if (LW_IS_SHORTCUT("limbo_ai/paste_task", p_event)) {
|
||||
_action_selected(ACTION_PASTE);
|
||||
} else if (LW_IS_SHORTCUT("limbo_ai/paste_task_after", p_event)) {
|
||||
_action_selected(ACTION_PASTE_AFTER);
|
||||
} else if (LW_IS_SHORTCUT("limbo_ai/move_task_up", p_event)) {
|
||||
_action_selected(ACTION_MOVE_UP);
|
||||
} else if (LW_IS_SHORTCUT("limbo_ai/move_task_down", p_event)) {
|
||||
_action_selected(ACTION_MOVE_DOWN);
|
||||
} else if (LW_IS_SHORTCUT("limbo_ai/duplicate_task", p_event)) {
|
||||
_action_selected(ACTION_DUPLICATE);
|
||||
} else if (LW_IS_SHORTCUT("limbo_ai/remove_task", p_event)) {
|
||||
_action_selected(ACTION_REMOVE);
|
||||
} else if (LW_IS_SHORTCUT("limbo_ai/new_behavior_tree", p_event)) {
|
||||
_new_bt();
|
||||
} else if (LW_IS_SHORTCUT("limbo_ai/save_behavior_tree", p_event)) {
|
||||
_on_save_pressed();
|
||||
} else if (LW_IS_SHORTCUT("limbo_ai/load_behavior_tree", p_event)) {
|
||||
_popup_file_dialog(load_dialog);
|
||||
} else {
|
||||
handled = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (handled) {
|
||||
get_viewport()->set_input_as_handled();
|
||||
}
|
||||
|
||||
// * Local shortcuts.
|
||||
|
||||
if (!(has_focus() || get_viewport()->gui_get_focus_owner() == nullptr || is_ancestor_of(get_viewport()->gui_get_focus_owner()))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (LW_IS_SHORTCUT("limbo_ai/rename_task", p_event)) {
|
||||
_action_selected(ACTION_RENAME);
|
||||
} else if (LW_IS_SHORTCUT("limbo_ai/cut_task", p_event)) {
|
||||
_action_selected(ACTION_CUT);
|
||||
} else if (LW_IS_SHORTCUT("limbo_ai/copy_task", p_event)) {
|
||||
_action_selected(ACTION_COPY);
|
||||
} else if (LW_IS_SHORTCUT("limbo_ai/paste_task", p_event)) {
|
||||
_action_selected(ACTION_PASTE);
|
||||
} else if (LW_IS_SHORTCUT("limbo_ai/paste_task_after", p_event)) {
|
||||
_action_selected(ACTION_PASTE_AFTER);
|
||||
} else if (LW_IS_SHORTCUT("limbo_ai/move_task_up", p_event)) {
|
||||
_action_selected(ACTION_MOVE_UP);
|
||||
} else if (LW_IS_SHORTCUT("limbo_ai/move_task_down", p_event)) {
|
||||
_action_selected(ACTION_MOVE_DOWN);
|
||||
} else if (LW_IS_SHORTCUT("limbo_ai/duplicate_task", p_event)) {
|
||||
_action_selected(ACTION_DUPLICATE);
|
||||
} else if (LW_IS_SHORTCUT("limbo_ai/remove_task", p_event)) {
|
||||
_action_selected(ACTION_REMOVE);
|
||||
} else if (LW_IS_SHORTCUT("limbo_ai/new_behavior_tree", p_event)) {
|
||||
_new_bt();
|
||||
} else if (LW_IS_SHORTCUT("limbo_ai/save_behavior_tree", p_event)) {
|
||||
_on_save_pressed();
|
||||
} else if (LW_IS_SHORTCUT("limbo_ai/load_behavior_tree", p_event)) {
|
||||
_popup_file_dialog(load_dialog);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
get_viewport()->set_input_as_handled();
|
||||
}
|
||||
|
||||
void LimboAIEditor::_on_tree_rmb(const Vector2 &p_menu_pos) {
|
||||
|
@ -1013,9 +1028,10 @@ void LimboAIEditor::_tab_input(const Ref<InputEvent> &p_input) {
|
|||
|
||||
void LimboAIEditor::_show_tab_context_menu() {
|
||||
tab_menu->clear();
|
||||
tab_menu->add_shortcut(LW_GET_SHORTCUT("limbo_ai/jump_to_owner"), TabMenu::TAB_JUMP_TO_OWNER);
|
||||
tab_menu->add_item(TTR("Show in FileSystem"), TabMenu::TAB_SHOW_IN_FILESYSTEM);
|
||||
tab_menu->add_separator();
|
||||
tab_menu->add_item(TTR("Close Tab"), TabMenu::TAB_CLOSE);
|
||||
tab_menu->add_shortcut(LW_GET_SHORTCUT("limbo_ai/close_tab"), TabMenu::TAB_CLOSE);
|
||||
tab_menu->add_item(TTR("Close Other Tabs"), TabMenu::TAB_CLOSE_OTHER);
|
||||
tab_menu->add_item(TTR("Close Tabs to the Right"), TabMenu::TAB_CLOSE_RIGHT);
|
||||
tab_menu->add_item(TTR("Close All Tabs"), TabMenu::TAB_CLOSE_ALL);
|
||||
|
@ -1025,7 +1041,12 @@ void LimboAIEditor::_show_tab_context_menu() {
|
|||
}
|
||||
|
||||
void LimboAIEditor::_tab_menu_option_selected(int p_id) {
|
||||
if (history.size() == 0) {
|
||||
// No tabs open, returning.
|
||||
return;
|
||||
}
|
||||
ERR_FAIL_INDEX(idx_history, history.size());
|
||||
|
||||
switch (p_id) {
|
||||
case TAB_SHOW_IN_FILESYSTEM: {
|
||||
Ref<BehaviorTree> bt = history[idx_history];
|
||||
|
@ -1034,6 +1055,14 @@ void LimboAIEditor::_tab_menu_option_selected(int p_id) {
|
|||
FS_DOCK_SELECT_FILE(path.get_slice("::", 0));
|
||||
}
|
||||
} break;
|
||||
case TAB_JUMP_TO_OWNER: {
|
||||
Ref<BehaviorTree> bt = history[idx_history];
|
||||
ERR_FAIL_NULL(bt);
|
||||
String bt_path = bt->get_path();
|
||||
if (!bt_path.is_empty()) {
|
||||
owner_picker->pick_and_open_owner_of_resource(bt_path);
|
||||
}
|
||||
} break;
|
||||
case TAB_CLOSE: {
|
||||
_tab_closed(idx_history);
|
||||
} break;
|
||||
|
@ -1347,6 +1376,8 @@ LimboAIEditor::LimboAIEditor() {
|
|||
LW_SHORTCUT("limbo_ai/save_behavior_tree", TTR("Save Behavior Tree"), (Key)(LW_KEY_MASK(CMD_OR_CTRL) | LW_KEY_MASK(ALT) | LW_KEY(S)));
|
||||
LW_SHORTCUT("limbo_ai/load_behavior_tree", TTR("Load Behavior Tree"), (Key)(LW_KEY_MASK(CMD_OR_CTRL) | LW_KEY_MASK(ALT) | LW_KEY(L)));
|
||||
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)));
|
||||
|
||||
set_process_shortcut_input(true);
|
||||
|
||||
|
@ -1450,6 +1481,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);
|
||||
|
|
|
@ -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_JUMP_TO_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;
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
/**
|
||||
* 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 <godot_cpp/classes/editor_file_system.hpp>
|
||||
#include <godot_cpp/classes/editor_file_system_directory.hpp>
|
||||
#include <godot_cpp/classes/editor_interface.hpp>
|
||||
#include <godot_cpp/classes/resource_loader.hpp>
|
||||
#endif
|
||||
|
||||
Vector<String> OwnerPicker::_find_owners(const String &p_path) const {
|
||||
Vector<String> owners;
|
||||
|
||||
if (RESOURCE_PATH_IS_BUILT_IN(p_path)) {
|
||||
// For built-in resources we use the path to the containing resource.
|
||||
String owner_path = p_path.substr(0, p_path.rfind("::"));
|
||||
owners.append(owner_path);
|
||||
return owners;
|
||||
}
|
||||
|
||||
List<EditorFileSystemDirectory *> 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<String> 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::pick_and_open_owner_of_resource(const String &p_path) {
|
||||
if (p_path.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
owners_item_list->clear();
|
||||
|
||||
Vector<String> 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 owner immediately if there is only one owner.
|
||||
_selection_confirmed();
|
||||
} else if (owners_item_list->get_item_count() == 0) {
|
||||
owners_item_list->hide();
|
||||
set_title(TTR("Alert!"));
|
||||
set_text(TTR("Couldn't find owner. Looks like it's not used by any other resource."));
|
||||
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_IS_SCENE_FILE(owner_path)) {
|
||||
EditorInterface::get_singleton()->open_scene_from_path(owner_path);
|
||||
} else {
|
||||
EditorInterface::get_singleton()->edit_resource(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 packed scenes at once.
|
||||
owners_item_list->set_select_mode(ItemList::SELECT_SINGLE);
|
||||
add_child(owners_item_list);
|
||||
}
|
||||
|
||||
#endif // TOOLS_ENABLED
|
|
@ -0,0 +1,47 @@
|
|||
/**
|
||||
* 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 <godot_cpp/classes/accept_dialog.hpp>
|
||||
#include <godot_cpp/classes/item_list.hpp>
|
||||
#include <godot_cpp/templates/vector.hpp>
|
||||
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);
|
||||
|
||||
Vector<String> _find_owners(const String &p_path) const;
|
||||
|
||||
public:
|
||||
void pick_and_open_owner_of_resource(const String &p_path);
|
||||
|
||||
OwnerPicker();
|
||||
};
|
||||
|
||||
#endif // OWNER_PICKER_H
|
|
@ -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
|
||||
|
|
|
@ -42,8 +42,8 @@
|
|||
#define RESOURCE_LOAD_NO_CACHE(m_path, m_hint) ResourceLoader::load(m_path, m_hint, ResourceFormatLoader::CACHE_MODE_IGNORE)
|
||||
#define RESOURCE_SAVE(m_res, m_path, m_flags) ResourceSaver::save(m_res, m_path, m_flags)
|
||||
#define RESOURCE_IS_CACHED(m_path) (ResourceCache::has(m_path))
|
||||
#define RESOURCE_GET_TYPE(m_path) (ResourceLoader::get_resource_type(m_path))
|
||||
#define RESOURCE_EXISTS(m_path, m_type_hint) (ResourceLoader::exists(m_path, m_type_hint))
|
||||
#define RESOURCE_IS_SCENE_FILE(m_path) (ResourceLoader::get_resource_type(m_path) == "PackedScene")
|
||||
#define GET_PROJECT_SETTINGS_DIR() EditorPaths::get_singleton()->get_project_settings_dir()
|
||||
#define EDIT_RESOURCE(m_res) EditorNode::get_singleton()->edit_resource(m_res)
|
||||
#define INSPECTOR_GET_EDITED_OBJECT() (InspectorDock::get_inspector_singleton()->get_edited_object())
|
||||
|
@ -127,7 +127,7 @@ using namespace godot;
|
|||
#define RESOURCE_LOAD_NO_CACHE(m_path, m_hint) ResourceLoader::get_singleton()->load(m_path, m_hint, ResourceLoader::CACHE_MODE_IGNORE)
|
||||
#define RESOURCE_SAVE(m_res, m_path, m_flags) ResourceSaver::get_singleton()->save(m_res, m_path, m_flags)
|
||||
#define RESOURCE_IS_CACHED(m_path) (ResourceLoader::get_singleton()->has_cached(res_path))
|
||||
#define RESOURCE_GET_TYPE(m_path) (ResourceLoader::get_resource_type(m_path))
|
||||
#define RESOURCE_IS_SCENE_FILE(m_path) (ResourceLoader::get_singleton()->get_recognized_extensions_for_type("PackedScene").has(m_path.get_extension()))
|
||||
#define RESOURCE_EXISTS(m_path, m_type_hint) (ResourceLoader::get_singleton()->exists(m_path, m_type_hint))
|
||||
#define GET_PROJECT_SETTINGS_DIR() EditorInterface::get_singleton()->get_editor_paths()->get_project_settings_dir()
|
||||
#define EDIT_RESOURCE(m_res) EditorInterface::get_singleton()->edit_resource(m_res)
|
||||
|
@ -237,6 +237,7 @@ Variant VARIANT_DEFAULT(Variant::Type p_type);
|
|||
#define IS_RESOURCE_FILE(m_path) (m_path.begins_with("res://") && m_path.find("::") == -1)
|
||||
#define RESOURCE_TYPE_HINT(m_type) vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, m_type)
|
||||
#define RESOURCE_IS_BUILT_IN(m_res) (m_res->get_path().is_empty() || m_res->get_path().contains("::"))
|
||||
#define RESOURCE_PATH_IS_BUILT_IN(m_path) (m_path.is_empty() || m_path.contains("::"))
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
||||
|
|
Loading…
Reference in New Issue