Add drag and drop.
Rearrange tasks with drag and drop in editor.
This commit is contained in:
parent
29fab3a8d8
commit
aefa132290
|
@ -184,6 +184,17 @@ bool BTTask::has_child(const Ref<BTTask> &p_child) const {
|
||||||
return children.find(p_child) != -1;
|
return children.find(p_child) != -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BTTask::is_descendant_of(const Ref<BTTask> &p_task) const {
|
||||||
|
const BTTask *task = this;
|
||||||
|
while (task != nullptr) {
|
||||||
|
task = task->parent;
|
||||||
|
if (task == p_task.ptr()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
int BTTask::get_child_index(const Ref<BTTask> &p_child) const {
|
int BTTask::get_child_index(const Ref<BTTask> &p_child) const {
|
||||||
return children.find(p_child);
|
return children.find(p_child);
|
||||||
}
|
}
|
||||||
|
@ -262,6 +273,7 @@ void BTTask::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("add_child_at_index", "p_child", "p_idx"), &BTTask::add_child_at_index);
|
ClassDB::bind_method(D_METHOD("add_child_at_index", "p_child", "p_idx"), &BTTask::add_child_at_index);
|
||||||
ClassDB::bind_method(D_METHOD("remove_child", "p_child"), &BTTask::remove_child);
|
ClassDB::bind_method(D_METHOD("remove_child", "p_child"), &BTTask::remove_child);
|
||||||
ClassDB::bind_method(D_METHOD("has_child", "p_child"), &BTTask::has_child);
|
ClassDB::bind_method(D_METHOD("has_child", "p_child"), &BTTask::has_child);
|
||||||
|
ClassDB::bind_method(D_METHOD("is_descendant_of", "p_task"), &BTTask::is_descendant_of);
|
||||||
ClassDB::bind_method(D_METHOD("get_child_index", "p_child"), &BTTask::get_child_index);
|
ClassDB::bind_method(D_METHOD("get_child_index", "p_child"), &BTTask::get_child_index);
|
||||||
ClassDB::bind_method(D_METHOD("next_sibling"), &BTTask::next_sibling);
|
ClassDB::bind_method(D_METHOD("next_sibling"), &BTTask::next_sibling);
|
||||||
ClassDB::bind_method(D_METHOD("print_tree", "p_initial_tabs"), &BTTask::print_tree, Variant(0));
|
ClassDB::bind_method(D_METHOD("print_tree", "p_initial_tabs"), &BTTask::print_tree, Variant(0));
|
||||||
|
|
|
@ -66,6 +66,7 @@ public:
|
||||||
void add_child_at_index(Ref<BTTask> p_child, int p_idx);
|
void add_child_at_index(Ref<BTTask> p_child, int p_idx);
|
||||||
void remove_child(Ref<BTTask> p_child);
|
void remove_child(Ref<BTTask> p_child);
|
||||||
bool has_child(const Ref<BTTask> &p_child) const;
|
bool has_child(const Ref<BTTask> &p_child) const;
|
||||||
|
bool is_descendant_of(const Ref<BTTask> &p_task) const;
|
||||||
int get_child_index(const Ref<BTTask> &p_child) const;
|
int get_child_index(const Ref<BTTask> &p_child) const;
|
||||||
Ref<BTTask> next_sibling() const;
|
Ref<BTTask> next_sibling() const;
|
||||||
virtual String get_configuration_warning() const;
|
virtual String get_configuration_warning() const;
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "limbo_ai_editor_plugin.h"
|
#include "limbo_ai_editor_plugin.h"
|
||||||
|
|
||||||
#include "core/class_db.h"
|
#include "core/class_db.h"
|
||||||
|
#include "core/dictionary.h"
|
||||||
#include "core/error_list.h"
|
#include "core/error_list.h"
|
||||||
#include "core/error_macros.h"
|
#include "core/error_macros.h"
|
||||||
#include "core/io/resource_loader.h"
|
#include "core/io/resource_loader.h"
|
||||||
|
@ -43,6 +44,8 @@
|
||||||
#include "scene/gui/tree.h"
|
#include "scene/gui/tree.h"
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
|
||||||
|
////////////////////////////// TaskTree //////////////////////////////////////
|
||||||
|
|
||||||
TreeItem *TaskTree::_create_tree(const Ref<BTTask> &p_task, TreeItem *p_parent, int p_idx) {
|
TreeItem *TaskTree::_create_tree(const Ref<BTTask> &p_task, TreeItem *p_parent, int p_idx) {
|
||||||
ERR_FAIL_COND_V(p_task.is_null(), nullptr);
|
ERR_FAIL_COND_V(p_task.is_null(), nullptr);
|
||||||
TreeItem *item = tree->create_item(p_parent, p_idx);
|
TreeItem *item = tree->create_item(p_parent, p_idx);
|
||||||
|
@ -153,6 +156,53 @@ void TaskTree::deselect() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Variant TaskTree::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
|
||||||
|
if (editable && tree->get_item_at_position(p_point)) {
|
||||||
|
Dictionary drag_data;
|
||||||
|
drag_data["type"] = "task";
|
||||||
|
drag_data["task"] = tree->get_item_at_position(p_point)->get_metadata(0);
|
||||||
|
tree->set_drop_mode_flags(Tree::DROP_MODE_INBETWEEN | Tree::DROP_MODE_ON_ITEM);
|
||||||
|
return drag_data;
|
||||||
|
}
|
||||||
|
return Variant();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TaskTree::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
|
||||||
|
if (!editable) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dictionary d = p_data;
|
||||||
|
if (!d.has("type") || !d.has("task")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int section = tree->get_drop_section_at_position(p_point);
|
||||||
|
TreeItem *item = tree->get_item_at_position(p_point);
|
||||||
|
if (!item || section < -1 || (section == -1 && !item->get_parent())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (String(d["type"]) == "task") {
|
||||||
|
Ref<BTTask> task = d["task"];
|
||||||
|
const Ref<BTTask> to_task = item->get_metadata(0);
|
||||||
|
if (task != to_task && !to_task->is_descendant_of(task)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskTree::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
|
||||||
|
Dictionary d = p_data;
|
||||||
|
TreeItem *item = tree->get_item_at_position(p_point);
|
||||||
|
if (item && d.has("task")) {
|
||||||
|
Ref<BTTask> task = d["task"];
|
||||||
|
emit_signal("task_dragged", task, item->get_metadata(0), tree->get_drop_section_at_position(p_point));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TaskTree::_bind_methods() {
|
void TaskTree::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("_on_item_rmb_selected"), &TaskTree::_on_item_rmb_selected);
|
ClassDB::bind_method(D_METHOD("_on_item_rmb_selected"), &TaskTree::_on_item_rmb_selected);
|
||||||
ClassDB::bind_method(D_METHOD("_on_item_selected"), &TaskTree::_on_item_selected);
|
ClassDB::bind_method(D_METHOD("_on_item_selected"), &TaskTree::_on_item_selected);
|
||||||
|
@ -163,11 +213,21 @@ void TaskTree::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("get_selected"), &TaskTree::get_selected);
|
ClassDB::bind_method(D_METHOD("get_selected"), &TaskTree::get_selected);
|
||||||
ClassDB::bind_method(D_METHOD("deselect"), &TaskTree::deselect);
|
ClassDB::bind_method(D_METHOD("deselect"), &TaskTree::deselect);
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &TaskTree::get_drag_data_fw);
|
||||||
|
ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &TaskTree::can_drop_data_fw);
|
||||||
|
ClassDB::bind_method(D_METHOD("drop_data_fw"), &TaskTree::drop_data_fw);
|
||||||
|
|
||||||
ADD_SIGNAL(MethodInfo("rmb_pressed"));
|
ADD_SIGNAL(MethodInfo("rmb_pressed"));
|
||||||
ADD_SIGNAL(MethodInfo("task_selected"));
|
ADD_SIGNAL(MethodInfo("task_selected"));
|
||||||
|
ADD_SIGNAL(MethodInfo("task_dragged",
|
||||||
|
PropertyInfo(Variant::OBJECT, "p_task", PROPERTY_HINT_RESOURCE_TYPE, "BTTask"),
|
||||||
|
PropertyInfo(Variant::OBJECT, "p_to_task", PROPERTY_HINT_RESOURCE_TYPE, "BTTask"),
|
||||||
|
PropertyInfo(Variant::INT, "p_type")));
|
||||||
}
|
}
|
||||||
|
|
||||||
TaskTree::TaskTree() {
|
TaskTree::TaskTree() {
|
||||||
|
editable = true;
|
||||||
|
|
||||||
tree = memnew(Tree);
|
tree = memnew(Tree);
|
||||||
add_child(tree);
|
add_child(tree);
|
||||||
tree->set_columns(2);
|
tree->set_columns(2);
|
||||||
|
@ -179,12 +239,15 @@ TaskTree::TaskTree() {
|
||||||
tree->set_allow_rmb_select(true);
|
tree->set_allow_rmb_select(true);
|
||||||
tree->connect("item_rmb_selected", this, "_on_item_rmb_selected");
|
tree->connect("item_rmb_selected", this, "_on_item_rmb_selected");
|
||||||
tree->connect("item_selected", this, "_on_item_selected");
|
tree->connect("item_selected", this, "_on_item_selected");
|
||||||
|
tree->set_drag_forwarding(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
TaskTree::~TaskTree() {
|
TaskTree::~TaskTree() {
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////// TaskTree //////////////////////////////////////
|
||||||
|
|
||||||
|
////////////////////////////// TaskSection ////////////////////////////////////
|
||||||
|
|
||||||
void TaskSection::_on_task_button_pressed(const StringName &p_task) {
|
void TaskSection::_on_task_button_pressed(const StringName &p_task) {
|
||||||
emit_signal("task_button_pressed", p_task);
|
emit_signal("task_button_pressed", p_task);
|
||||||
|
@ -242,7 +305,9 @@ TaskSection::TaskSection(String p_category_name, EditorNode *p_editor) {
|
||||||
TaskSection::~TaskSection() {
|
TaskSection::~TaskSection() {
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////// TaskSection ////////////////////////////////////
|
||||||
|
|
||||||
|
////////////////////////////// TaskPanel /////////////////////////////////////
|
||||||
|
|
||||||
void TaskPanel::_on_task_button_pressed(const StringName &p_task) {
|
void TaskPanel::_on_task_button_pressed(const StringName &p_task) {
|
||||||
emit_signal("task_selected", p_task);
|
emit_signal("task_selected", p_task);
|
||||||
|
@ -375,7 +440,9 @@ TaskPanel::TaskPanel(EditorNode *p_editor) {
|
||||||
TaskPanel::~TaskPanel() {
|
TaskPanel::~TaskPanel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////// TaskPanel /////////////////////////////////////
|
||||||
|
|
||||||
|
//////////////////////////// LimboAIEditor ///////////////////////////////////
|
||||||
|
|
||||||
void LimboAIEditor::_add_task(const Ref<BTTask> &p_task) {
|
void LimboAIEditor::_add_task(const Ref<BTTask> &p_task) {
|
||||||
ERR_FAIL_COND(p_task.is_null());
|
ERR_FAIL_COND(p_task.is_null());
|
||||||
|
@ -600,6 +667,28 @@ void LimboAIEditor::_on_history_forward() {
|
||||||
_edit_bt(history[idx_history]);
|
_edit_bt(history[idx_history]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LimboAIEditor::_on_task_dragged(Ref<BTTask> p_task, Ref<BTTask> p_to_task, int p_type) {
|
||||||
|
if (p_task == p_to_task) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (p_type == 0) {
|
||||||
|
p_task->get_parent()->remove_child(p_task);
|
||||||
|
p_to_task->add_child(p_task);
|
||||||
|
task_tree->update_tree();
|
||||||
|
_mark_as_dirty(true);
|
||||||
|
} else if (p_type == -1 && p_to_task->get_parent().is_valid()) {
|
||||||
|
p_task->get_parent()->remove_child(p_task);
|
||||||
|
p_to_task->get_parent()->add_child_at_index(p_task, p_to_task->get_parent()->get_child_index(p_to_task));
|
||||||
|
task_tree->update_tree();
|
||||||
|
_mark_as_dirty(true);
|
||||||
|
} else if (p_type == 1 && p_to_task->get_parent().is_valid()) {
|
||||||
|
p_task->get_parent()->remove_child(p_task);
|
||||||
|
p_to_task->get_parent()->add_child_at_index(p_task, p_to_task->get_parent()->get_child_index(p_to_task) + 1);
|
||||||
|
task_tree->update_tree();
|
||||||
|
_mark_as_dirty(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void LimboAIEditor::apply_changes() {
|
void LimboAIEditor::apply_changes() {
|
||||||
for (int i = 0; i < history.size(); i++) {
|
for (int i = 0; i < history.size(); i++) {
|
||||||
Ref<BehaviorTree> bt = history.get(i);
|
Ref<BehaviorTree> bt = history.get(i);
|
||||||
|
@ -624,6 +713,7 @@ void LimboAIEditor::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("_on_save_pressed"), &LimboAIEditor::_on_save_pressed);
|
ClassDB::bind_method(D_METHOD("_on_save_pressed"), &LimboAIEditor::_on_save_pressed);
|
||||||
ClassDB::bind_method(D_METHOD("_on_history_back"), &LimboAIEditor::_on_history_back);
|
ClassDB::bind_method(D_METHOD("_on_history_back"), &LimboAIEditor::_on_history_back);
|
||||||
ClassDB::bind_method(D_METHOD("_on_history_forward"), &LimboAIEditor::_on_history_forward);
|
ClassDB::bind_method(D_METHOD("_on_history_forward"), &LimboAIEditor::_on_history_forward);
|
||||||
|
ClassDB::bind_method(D_METHOD("_on_task_dragged", "p_task", "p_to_task", "p_type"), &LimboAIEditor::_on_task_dragged);
|
||||||
ClassDB::bind_method(D_METHOD("_new_bt"), &LimboAIEditor::_new_bt);
|
ClassDB::bind_method(D_METHOD("_new_bt"), &LimboAIEditor::_new_bt);
|
||||||
ClassDB::bind_method(D_METHOD("_save_bt", "p_path"), &LimboAIEditor::_save_bt);
|
ClassDB::bind_method(D_METHOD("_save_bt", "p_path"), &LimboAIEditor::_save_bt);
|
||||||
ClassDB::bind_method(D_METHOD("_load_bt", "p_path"), &LimboAIEditor::_load_bt);
|
ClassDB::bind_method(D_METHOD("_load_bt", "p_path"), &LimboAIEditor::_load_bt);
|
||||||
|
@ -751,6 +841,7 @@ LimboAIEditor::LimboAIEditor(EditorNode *p_editor) {
|
||||||
task_tree->connect("rmb_pressed", this, "_on_tree_rmb");
|
task_tree->connect("rmb_pressed", this, "_on_tree_rmb");
|
||||||
task_tree->connect("task_selected", this, "_on_tree_task_selected");
|
task_tree->connect("task_selected", this, "_on_tree_task_selected");
|
||||||
task_tree->connect("visibility_changed", this, "_on_visibility_changed");
|
task_tree->connect("visibility_changed", this, "_on_visibility_changed");
|
||||||
|
task_tree->connect("task_dragged", this, "_on_task_dragged");
|
||||||
|
|
||||||
TaskPanel *task_panel = memnew(TaskPanel(p_editor));
|
TaskPanel *task_panel = memnew(TaskPanel(p_editor));
|
||||||
hsc->add_child(task_panel);
|
hsc->add_child(task_panel);
|
||||||
|
@ -778,7 +869,9 @@ LimboAIEditor::LimboAIEditor(EditorNode *p_editor) {
|
||||||
LimboAIEditor::~LimboAIEditor() {
|
LimboAIEditor::~LimboAIEditor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////// LimboAIEditor ///////////////////////////////////
|
||||||
|
|
||||||
|
///////////////////////// LimboAIEditorPlugin ////////////////////////////////
|
||||||
|
|
||||||
const Ref<Texture> LimboAIEditorPlugin::get_icon() const {
|
const Ref<Texture> LimboAIEditorPlugin::get_icon() const {
|
||||||
// TODO:
|
// TODO:
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "editor/editor_node.h"
|
#include "editor/editor_node.h"
|
||||||
#include "editor/editor_plugin.h"
|
#include "editor/editor_plugin.h"
|
||||||
#include "modules/limboai/bt/behavior_tree.h"
|
#include "modules/limboai/bt/behavior_tree.h"
|
||||||
|
#include "modules/limboai/bt/bt_task.h"
|
||||||
#include "scene/gui/box_container.h"
|
#include "scene/gui/box_container.h"
|
||||||
#include "scene/gui/file_dialog.h"
|
#include "scene/gui/file_dialog.h"
|
||||||
#include "scene/gui/flow_container.h"
|
#include "scene/gui/flow_container.h"
|
||||||
|
@ -22,6 +23,7 @@ private:
|
||||||
Tree *tree;
|
Tree *tree;
|
||||||
Ref<BehaviorTree> bt;
|
Ref<BehaviorTree> bt;
|
||||||
Ref<BTTask> last_selected;
|
Ref<BTTask> last_selected;
|
||||||
|
bool editable;
|
||||||
|
|
||||||
TreeItem *_create_tree(const Ref<BTTask> &p_task, TreeItem *p_parent, int p_idx = -1);
|
TreeItem *_create_tree(const Ref<BTTask> &p_task, TreeItem *p_parent, int p_idx = -1);
|
||||||
void _update_item(TreeItem *p_item);
|
void _update_item(TreeItem *p_item);
|
||||||
|
@ -31,6 +33,10 @@ private:
|
||||||
void _on_item_selected();
|
void _on_item_selected();
|
||||||
void _on_item_rmb_selected(const Vector2 &p_pos);
|
void _on_item_rmb_selected(const Vector2 &p_pos);
|
||||||
|
|
||||||
|
Variant get_drag_data_fw(const Point2 &p_point, Control *p_from);
|
||||||
|
bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
|
||||||
|
void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
|
|
||||||
|
@ -133,6 +139,7 @@ private:
|
||||||
void _on_panel_task_selected(String p_task);
|
void _on_panel_task_selected(String p_task);
|
||||||
void _on_history_back();
|
void _on_history_back();
|
||||||
void _on_history_forward();
|
void _on_history_forward();
|
||||||
|
void _on_task_dragged(Ref<BTTask> p_task, Ref<BTTask> p_to_task, int p_type);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
|
|
Loading…
Reference in New Issue