Merge pull request #210 from ydeltastar/multi-select
Implement task multiple selection and drag and drop
This commit is contained in:
commit
bfcc2f8e1b
|
@ -809,7 +809,7 @@ void LimboAIEditor::_on_visibility_changed() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void LimboAIEditor::_on_header_pressed() {
|
void LimboAIEditor::_on_header_pressed() {
|
||||||
task_tree->deselect();
|
task_tree->clear_selection();
|
||||||
#ifdef LIMBOAI_MODULE
|
#ifdef LIMBOAI_MODULE
|
||||||
if (task_tree->get_bt().is_valid()) {
|
if (task_tree->get_bt().is_valid()) {
|
||||||
task_tree->get_bt()->editor_set_section_unfold("blackboard_plan", true);
|
task_tree->get_bt()->editor_set_section_unfold("blackboard_plan", true);
|
||||||
|
@ -842,35 +842,50 @@ void LimboAIEditor::_on_history_forward() {
|
||||||
EDIT_RESOURCE(history[idx_history]);
|
EDIT_RESOURCE(history[idx_history]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LimboAIEditor::_on_task_dragged(Ref<BTTask> p_task, Ref<BTTask> p_to_task, int p_type) {
|
void LimboAIEditor::_on_tasks_dragged(const TypedArray<BTTask> &p_tasks, Ref<BTTask> p_to_task, int p_to_pos) {
|
||||||
ERR_FAIL_COND(p_type < -1 || p_type > 1);
|
ERR_FAIL_COND(p_to_task.is_null());
|
||||||
ERR_FAIL_COND(p_type != 0 && p_to_task->get_parent().is_null());
|
if (p_tasks.is_empty()) {
|
||||||
|
|
||||||
if (p_task == p_to_task) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
EditorUndoRedoManager *undo_redo = _new_undo_redo_action(TTR("Drag BT Task"));
|
// Remove descendants of selected.
|
||||||
undo_redo->add_do_method(p_task->get_parent().ptr(), LW_NAME(remove_child), p_task);
|
Vector<Ref<BTTask>> tasks_list;
|
||||||
|
for (int i = 0; i < p_tasks.size(); i++) {
|
||||||
if (p_type == 0) {
|
Ref<BTTask> task = p_tasks[i];
|
||||||
undo_redo->add_do_method(p_to_task.ptr(), LW_NAME(add_child), p_task);
|
bool remove = false;
|
||||||
undo_redo->add_undo_method(p_to_task.ptr(), LW_NAME(remove_child), p_task);
|
for (int s_idx = 0; s_idx < p_tasks.size(); s_idx++) {
|
||||||
} else {
|
Ref<BTTask> selected = p_tasks[s_idx];
|
||||||
int drop_idx = p_to_task->get_index();
|
if (task->is_descendant_of(selected)) {
|
||||||
if (p_to_task->get_parent() == p_task->get_parent() && drop_idx > p_task->get_index()) {
|
remove = true;
|
||||||
drop_idx -= 1;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (p_type == -1) {
|
if (!remove) {
|
||||||
undo_redo->add_do_method(p_to_task->get_parent().ptr(), LW_NAME(add_child_at_index), p_task, drop_idx);
|
tasks_list.push_back(task);
|
||||||
undo_redo->add_undo_method(p_to_task->get_parent().ptr(), LW_NAME(remove_child), p_task);
|
|
||||||
} else if (p_type == 1) {
|
|
||||||
undo_redo->add_do_method(p_to_task->get_parent().ptr(), LW_NAME(add_child_at_index), p_task, drop_idx + 1);
|
|
||||||
undo_redo->add_undo_method(p_to_task->get_parent().ptr(), LW_NAME(remove_child), p_task);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
undo_redo->add_undo_method(p_task->get_parent().ptr(), "add_child_at_index", p_task, p_task->get_index());
|
EditorUndoRedoManager *undo_redo = _new_undo_redo_action(TTR("Drag BT Task"));
|
||||||
|
|
||||||
|
// Remove all tasks first so adding ordering is stable.
|
||||||
|
int before_pos = 0;
|
||||||
|
for (const Ref<BTTask> task : tasks_list) {
|
||||||
|
if (task->get_parent() == p_to_task && p_to_pos > task->get_index()) {
|
||||||
|
before_pos += 1;
|
||||||
|
}
|
||||||
|
undo_redo->add_do_method(task->get_parent().ptr(), LW_NAME(remove_child), task);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < tasks_list.size(); i++) {
|
||||||
|
Ref<BTTask> task = tasks_list[i];
|
||||||
|
undo_redo->add_do_method(p_to_task.ptr(), LW_NAME(add_child_at_index), task, p_to_pos + i - before_pos);
|
||||||
|
undo_redo->add_undo_method(p_to_task.ptr(), LW_NAME(remove_child), task);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-add tasks in later undo action so indexes match the old order.
|
||||||
|
for (const Ref<BTTask> task : tasks_list) {
|
||||||
|
undo_redo->add_undo_method(task->get_parent().ptr(), LW_NAME(add_child_at_index), task, task->get_index());
|
||||||
|
}
|
||||||
|
|
||||||
_commit_action_with_update(undo_redo);
|
_commit_action_with_update(undo_redo);
|
||||||
}
|
}
|
||||||
|
@ -1383,7 +1398,7 @@ void LimboAIEditor::_notification(int p_what) {
|
||||||
load_btn->connect(LW_NAME(pressed), callable_mp(this, &LimboAIEditor::_popup_file_dialog).bind(load_dialog));
|
load_btn->connect(LW_NAME(pressed), callable_mp(this, &LimboAIEditor::_popup_file_dialog).bind(load_dialog));
|
||||||
task_tree->connect("rmb_pressed", callable_mp(this, &LimboAIEditor::_on_tree_rmb));
|
task_tree->connect("rmb_pressed", callable_mp(this, &LimboAIEditor::_on_tree_rmb));
|
||||||
task_tree->connect("task_selected", callable_mp(this, &LimboAIEditor::_on_tree_task_selected));
|
task_tree->connect("task_selected", callable_mp(this, &LimboAIEditor::_on_tree_task_selected));
|
||||||
task_tree->connect("task_dragged", callable_mp(this, &LimboAIEditor::_on_task_dragged));
|
task_tree->connect("tasks_dragged", callable_mp(this, &LimboAIEditor::_on_tasks_dragged));
|
||||||
task_tree->connect("task_activated", callable_mp(this, &LimboAIEditor::_on_tree_task_activated));
|
task_tree->connect("task_activated", callable_mp(this, &LimboAIEditor::_on_tree_task_activated));
|
||||||
task_tree->connect("probability_clicked", callable_mp(this, &LimboAIEditor::_action_selected).bind(ACTION_EDIT_PROBABILITY));
|
task_tree->connect("probability_clicked", callable_mp(this, &LimboAIEditor::_action_selected).bind(ACTION_EDIT_PROBABILITY));
|
||||||
task_tree->connect("visibility_changed", callable_mp(this, &LimboAIEditor::_on_visibility_changed));
|
task_tree->connect("visibility_changed", callable_mp(this, &LimboAIEditor::_on_visibility_changed));
|
||||||
|
|
|
@ -239,7 +239,7 @@ private:
|
||||||
void _on_save_pressed();
|
void _on_save_pressed();
|
||||||
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);
|
void _on_tasks_dragged(const TypedArray<BTTask> &p_tasks, Ref<BTTask> p_to_task, int p_to_pos);
|
||||||
void _on_resources_reload(const PackedStringArray &p_resources);
|
void _on_resources_reload(const PackedStringArray &p_resources);
|
||||||
void _on_filesystem_changed();
|
void _on_filesystem_changed();
|
||||||
void _on_new_script_pressed();
|
void _on_new_script_pressed();
|
||||||
|
|
|
@ -21,11 +21,18 @@
|
||||||
#ifdef LIMBOAI_MODULE
|
#ifdef LIMBOAI_MODULE
|
||||||
#include "core/object/script_language.h"
|
#include "core/object/script_language.h"
|
||||||
#include "editor/themes/editor_scale.h"
|
#include "editor/themes/editor_scale.h"
|
||||||
|
#include "scene/gui/box_container.h"
|
||||||
|
#include "scene/gui/texture_rect.h"
|
||||||
|
#include "scene/gui/label.h"
|
||||||
#endif // LIMBOAI_MODULE
|
#endif // LIMBOAI_MODULE
|
||||||
|
|
||||||
#ifdef LIMBOAI_GDEXTENSION
|
#ifdef LIMBOAI_GDEXTENSION
|
||||||
#include <godot_cpp/classes/editor_interface.hpp>
|
#include <godot_cpp/classes/editor_interface.hpp>
|
||||||
#include <godot_cpp/classes/script.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>
|
||||||
using namespace godot;
|
using namespace godot;
|
||||||
#endif // LIMBOAI_GDEXTENSION
|
#endif // LIMBOAI_GDEXTENSION
|
||||||
|
|
||||||
|
@ -101,10 +108,7 @@ void TaskTree::_update_item(TreeItem *p_item) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void TaskTree::_update_tree() {
|
void TaskTree::_update_tree() {
|
||||||
Ref<BTTask> sel;
|
Vector<Ref<BTTask>> selection = get_selected_tasks();
|
||||||
if (tree->get_selected()) {
|
|
||||||
sel = tree->get_selected()->get_metadata(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
tree->clear();
|
tree->clear();
|
||||||
if (bt.is_null()) {
|
if (bt.is_null()) {
|
||||||
|
@ -117,9 +121,8 @@ void TaskTree::_update_tree() {
|
||||||
updating_tree = false;
|
updating_tree = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
TreeItem *item = _find_item(sel);
|
for (const Ref<BTTask> &task : selection) {
|
||||||
if (item) {
|
add_selection(task);
|
||||||
item->select(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,6 +228,22 @@ void TaskTree::update_task(const Ref<BTTask> &p_task) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TaskTree::add_selection(const Ref<BTTask> &p_task) {
|
||||||
|
ERR_FAIL_COND(p_task.is_null());
|
||||||
|
TreeItem *item = _find_item(p_task);
|
||||||
|
if (item) {
|
||||||
|
item->select(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskTree::remove_selection(const Ref<BTTask> &p_task) {
|
||||||
|
ERR_FAIL_COND(p_task.is_null());
|
||||||
|
TreeItem *item = _find_item(p_task);
|
||||||
|
if (item) {
|
||||||
|
item->deselect(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ref<BTTask> TaskTree::get_selected() const {
|
Ref<BTTask> TaskTree::get_selected() const {
|
||||||
if (tree->get_selected()) {
|
if (tree->get_selected()) {
|
||||||
return tree->get_selected()->get_metadata(0);
|
return tree->get_selected()->get_metadata(0);
|
||||||
|
@ -232,11 +251,22 @@ Ref<BTTask> TaskTree::get_selected() const {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TaskTree::deselect() {
|
Vector<Ref<BTTask>> TaskTree::get_selected_tasks() const {
|
||||||
TreeItem *sel = tree->get_selected();
|
Vector<Ref<BTTask>> selected_tasks;
|
||||||
if (sel) {
|
TreeItem *next = tree->get_next_selected(nullptr);
|
||||||
sel->deselect(0);
|
while (next) {
|
||||||
|
Ref<BTTask> task = next->get_metadata(0);
|
||||||
|
if (task.is_valid()) {
|
||||||
|
selected_tasks.push_back(task);
|
||||||
|
}
|
||||||
|
next = tree->get_next_selected(next);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return selected_tasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskTree::clear_selection() {
|
||||||
|
tree->deselect_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
Rect2 TaskTree::get_selected_probability_rect() const {
|
Rect2 TaskTree::get_selected_probability_rect() const {
|
||||||
|
@ -280,9 +310,50 @@ bool TaskTree::selected_has_probability() const {
|
||||||
|
|
||||||
Variant TaskTree::_get_drag_data_fw(const Point2 &p_point) {
|
Variant TaskTree::_get_drag_data_fw(const Point2 &p_point) {
|
||||||
if (editable && tree->get_item_at_position(p_point)) {
|
if (editable && tree->get_item_at_position(p_point)) {
|
||||||
|
TypedArray<BTTask> selected_tasks;
|
||||||
|
Vector<Ref<Texture2D>> icons;
|
||||||
|
TreeItem *next = tree->get_next_selected(nullptr);
|
||||||
|
while (next) {
|
||||||
|
Ref<BTTask> task = next->get_metadata(0);
|
||||||
|
if (task.is_valid()) {
|
||||||
|
selected_tasks.push_back(task);
|
||||||
|
icons.push_back(next->get_icon(0));
|
||||||
|
}
|
||||||
|
next = tree->get_next_selected(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selected_tasks.is_empty()) {
|
||||||
|
return Variant();
|
||||||
|
}
|
||||||
|
|
||||||
|
VBoxContainer *vb = memnew(VBoxContainer);
|
||||||
|
int list_max = 10;
|
||||||
|
float opacity_step = 1.0f / list_max;
|
||||||
|
float opacity_item = 1.0f;
|
||||||
|
for (int i = 0; i < selected_tasks.size(); i++) {
|
||||||
|
Ref<BTTask> task = Object::cast_to<BTTask>(selected_tasks[i]);
|
||||||
|
if (i < list_max) {
|
||||||
|
HBoxContainer *hb = memnew(HBoxContainer);
|
||||||
|
TextureRect *tf = memnew(TextureRect);
|
||||||
|
int icon_size = get_theme_constant(LW_NAME(class_icon_size), LW_NAME(Editor));
|
||||||
|
tf->set_custom_minimum_size(Size2(icon_size, icon_size));
|
||||||
|
tf->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED);
|
||||||
|
tf->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE);
|
||||||
|
tf->set_texture(icons[i]);
|
||||||
|
hb->add_child(tf);
|
||||||
|
Label *label = memnew(Label);
|
||||||
|
label->set_text(task->get_task_name());
|
||||||
|
hb->add_child(label);
|
||||||
|
vb->add_child(hb);
|
||||||
|
hb->set_modulate(Color(1, 1, 1, opacity_item));
|
||||||
|
opacity_item -= opacity_step;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set_drag_preview(vb);
|
||||||
|
|
||||||
Dictionary drag_data;
|
Dictionary drag_data;
|
||||||
drag_data["type"] = "task";
|
drag_data["type"] = "task";
|
||||||
drag_data["task"] = tree->get_item_at_position(p_point)->get_metadata(0);
|
drag_data["tasks"] = selected_tasks;
|
||||||
tree->set_drop_mode_flags(Tree::DROP_MODE_INBETWEEN | Tree::DROP_MODE_ON_ITEM);
|
tree->set_drop_mode_flags(Tree::DROP_MODE_INBETWEEN | Tree::DROP_MODE_ON_ITEM);
|
||||||
return drag_data;
|
return drag_data;
|
||||||
}
|
}
|
||||||
|
@ -295,7 +366,7 @@ bool TaskTree::_can_drop_data_fw(const Point2 &p_point, const Variant &p_data) c
|
||||||
}
|
}
|
||||||
|
|
||||||
Dictionary d = p_data;
|
Dictionary d = p_data;
|
||||||
if (!d.has("type") || !d.has("task")) {
|
if (!d.has("type") || !d.has("tasks")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -305,27 +376,98 @@ bool TaskTree::_can_drop_data_fw(const Point2 &p_point, const Variant &p_data) c
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!item->get_parent() && section != 0) { // before/after root item
|
if (!item->get_parent() && section < 0) { // Before root item.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (String(d["type"]) == "task") {
|
if (String(d["type"]) == "task") {
|
||||||
Ref<BTTask> task = d["task"];
|
TypedArray<BTTask> tasks = d["tasks"];
|
||||||
const Ref<BTTask> to_task = item->get_metadata(0);
|
if (tasks.is_empty()) {
|
||||||
if (task != to_task && !to_task->is_descendant_of(task)) {
|
return false; // No tasks.
|
||||||
return true;
|
}
|
||||||
|
|
||||||
|
Ref<BTTask> to_task = item->get_metadata(0);
|
||||||
|
int to_pos = -1;
|
||||||
|
int type = tree->get_drop_section_at_position(p_point);
|
||||||
|
_normalize_drop(item, type, to_pos, to_task);
|
||||||
|
if (to_task.is_null()) {
|
||||||
|
return false; // Outside root.
|
||||||
|
}
|
||||||
|
for (int i = 0; i < tasks.size(); i++) {
|
||||||
|
Ref<BTTask> task = tasks[i];
|
||||||
|
if (to_task->is_descendant_of(task) || task == to_task) {
|
||||||
|
return false; // Don't drop as child of selected tasks.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TaskTree::_drop_data_fw(const Point2 &p_point, const Variant &p_data) {
|
void TaskTree::_drop_data_fw(const Point2 &p_point, const Variant &p_data) {
|
||||||
Dictionary d = p_data;
|
Dictionary d = p_data;
|
||||||
|
if (!d.has("tasks")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
TreeItem *item = tree->get_item_at_position(p_point);
|
TreeItem *item = tree->get_item_at_position(p_point);
|
||||||
if (item && d.has("task")) {
|
int type = tree->get_drop_section_at_position(p_point);
|
||||||
Ref<BTTask> task = d["task"];
|
ERR_FAIL_NULL(item);
|
||||||
emit_signal(LW_NAME(task_dragged), task, item->get_metadata(0), tree->get_drop_section_at_position(p_point));
|
ERR_FAIL_COND(type < -1 || type > 1);
|
||||||
|
|
||||||
|
// The drop behavior depends on the TreeItem's state.
|
||||||
|
// Normalize and emit the parent task and position instead of exposing TreeItem.
|
||||||
|
int to_pos = -1;
|
||||||
|
Ref<BTTask> to_task = item->get_metadata(0);
|
||||||
|
ERR_FAIL_COND(to_task.is_null());
|
||||||
|
_normalize_drop(item, type, to_pos, to_task);
|
||||||
|
emit_signal(LW_NAME(tasks_dragged), d["tasks"], to_task, to_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskTree::_normalize_drop(TreeItem *item, int type, int &to_pos, Ref<BTTask> &to_task) const {
|
||||||
|
switch (type) {
|
||||||
|
case 0: // Drop as last child of target.
|
||||||
|
to_pos = to_task->get_child_count();
|
||||||
|
break;
|
||||||
|
case -1: // Drop above target.
|
||||||
|
ERR_FAIL_COND_MSG(to_task->get_parent().is_null(), "Cannot perform drop above the root task!");
|
||||||
|
to_pos = to_task->get_index();
|
||||||
|
{
|
||||||
|
Vector<Ref<BTTask>> selected = get_selected_tasks();
|
||||||
|
if (to_task == selected[selected.size()-1]) {
|
||||||
|
to_pos += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
to_task = to_task->get_parent();
|
||||||
|
break;
|
||||||
|
case 1: // Drop below target.
|
||||||
|
if (item->get_child_count() == 0) {
|
||||||
|
to_pos = to_task->get_index() + 1;
|
||||||
|
if (to_task == tree->get_next_selected(nullptr)->get_metadata(0)) {
|
||||||
|
to_pos -= 1;
|
||||||
|
}
|
||||||
|
to_task = to_task->get_parent();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (to_task->get_parent().is_null() || !item->is_collapsed()) { // Insert as first child of target.
|
||||||
|
to_pos = 0;
|
||||||
|
} else { // Insert as sibling of target.
|
||||||
|
TreeItem *lower_sibling = nullptr;
|
||||||
|
for (int i = to_task->get_index() + 1; i < to_task->get_parent()->get_child_count(); i++) {
|
||||||
|
TreeItem *c = item->get_parent()->get_child(i);
|
||||||
|
if (c->is_visible_in_tree()) {
|
||||||
|
lower_sibling = c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lower_sibling) {
|
||||||
|
to_pos = lower_sibling->get_index();
|
||||||
|
}
|
||||||
|
|
||||||
|
to_task = to_task->get_parent();
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -385,7 +527,7 @@ void TaskTree::_notification(int p_what) {
|
||||||
case NOTIFICATION_READY: {
|
case NOTIFICATION_READY: {
|
||||||
tree->connect("item_mouse_selected", callable_mp(this, &TaskTree::_on_item_mouse_selected));
|
tree->connect("item_mouse_selected", callable_mp(this, &TaskTree::_on_item_mouse_selected));
|
||||||
// Note: CONNECT_DEFERRED is needed to avoid double updates with set_allow_reselect(true), which breaks folding/unfolding.
|
// Note: CONNECT_DEFERRED is needed to avoid double updates with set_allow_reselect(true), which breaks folding/unfolding.
|
||||||
tree->connect("item_selected", callable_mp(this, &TaskTree::_on_item_selected), CONNECT_DEFERRED);
|
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_activated", callable_mp(this, &TaskTree::_on_item_activated));
|
||||||
tree->connect("item_collapsed", callable_mp(this, &TaskTree::_on_item_collapsed));
|
tree->connect("item_collapsed", callable_mp(this, &TaskTree::_on_item_collapsed));
|
||||||
} break;
|
} break;
|
||||||
|
@ -401,8 +543,10 @@ void TaskTree::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("get_bt"), &TaskTree::get_bt);
|
ClassDB::bind_method(D_METHOD("get_bt"), &TaskTree::get_bt);
|
||||||
ClassDB::bind_method(D_METHOD("update_tree"), &TaskTree::update_tree);
|
ClassDB::bind_method(D_METHOD("update_tree"), &TaskTree::update_tree);
|
||||||
ClassDB::bind_method(D_METHOD("update_task", "task"), &TaskTree::update_task);
|
ClassDB::bind_method(D_METHOD("update_task", "task"), &TaskTree::update_task);
|
||||||
|
ClassDB::bind_method(D_METHOD("add_selection", "task"), &TaskTree::add_selection);
|
||||||
|
ClassDB::bind_method(D_METHOD("remove_selection", "task"), &TaskTree::remove_selection);
|
||||||
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("clear_selection"), &TaskTree::clear_selection);
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("_get_drag_data_fw"), &TaskTree::_get_drag_data_fw);
|
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("_can_drop_data_fw"), &TaskTree::_can_drop_data_fw);
|
||||||
|
@ -413,8 +557,7 @@ void TaskTree::_bind_methods() {
|
||||||
ADD_SIGNAL(MethodInfo("task_selected"));
|
ADD_SIGNAL(MethodInfo("task_selected"));
|
||||||
ADD_SIGNAL(MethodInfo("task_activated"));
|
ADD_SIGNAL(MethodInfo("task_activated"));
|
||||||
ADD_SIGNAL(MethodInfo("probability_clicked"));
|
ADD_SIGNAL(MethodInfo("probability_clicked"));
|
||||||
ADD_SIGNAL(MethodInfo("task_dragged",
|
ADD_SIGNAL(MethodInfo("tasks_dragged", PropertyInfo(Variant::ARRAY, "tasks", PROPERTY_HINT_ARRAY_TYPE, RESOURCE_TYPE_HINT("BTTask")),
|
||||||
PropertyInfo(Variant::OBJECT, "task", PROPERTY_HINT_RESOURCE_TYPE, "BTTask"),
|
|
||||||
PropertyInfo(Variant::OBJECT, "to_task", PROPERTY_HINT_RESOURCE_TYPE, "BTTask"),
|
PropertyInfo(Variant::OBJECT, "to_task", PROPERTY_HINT_RESOURCE_TYPE, "BTTask"),
|
||||||
PropertyInfo(Variant::INT, "type")));
|
PropertyInfo(Variant::INT, "type")));
|
||||||
}
|
}
|
||||||
|
@ -432,6 +575,7 @@ TaskTree::TaskTree() {
|
||||||
tree->set_anchor(SIDE_BOTTOM, ANCHOR_END);
|
tree->set_anchor(SIDE_BOTTOM, ANCHOR_END);
|
||||||
tree->set_allow_rmb_select(true);
|
tree->set_allow_rmb_select(true);
|
||||||
tree->set_allow_reselect(true);
|
tree->set_allow_reselect(true);
|
||||||
|
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->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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,7 @@ private:
|
||||||
Variant _get_drag_data_fw(const Point2 &p_point);
|
Variant _get_drag_data_fw(const Point2 &p_point);
|
||||||
bool _can_drop_data_fw(const Point2 &p_point, const Variant &p_data) const;
|
bool _can_drop_data_fw(const Point2 &p_point, const Variant &p_data) const;
|
||||||
void _drop_data_fw(const Point2 &p_point, const Variant &p_data);
|
void _drop_data_fw(const Point2 &p_point, const Variant &p_data);
|
||||||
|
void _normalize_drop(TreeItem *item, int type, int &to_pos, Ref<BTTask> &to_task) const;
|
||||||
|
|
||||||
void _draw_probability(Object *item_obj, Rect2 rect);
|
void _draw_probability(Object *item_obj, Rect2 rect);
|
||||||
|
|
||||||
|
@ -90,8 +91,11 @@ public:
|
||||||
Ref<BehaviorTree> get_bt() const { return bt; }
|
Ref<BehaviorTree> get_bt() const { return bt; }
|
||||||
void update_tree() { _update_tree(); }
|
void update_tree() { _update_tree(); }
|
||||||
void update_task(const Ref<BTTask> &p_task);
|
void update_task(const Ref<BTTask> &p_task);
|
||||||
|
void add_selection(const Ref<BTTask> &p_task);
|
||||||
|
void remove_selection(const Ref<BTTask> &p_task);
|
||||||
Ref<BTTask> get_selected() const;
|
Ref<BTTask> get_selected() const;
|
||||||
void deselect();
|
Vector<Ref<BTTask>> get_selected_tasks() const;
|
||||||
|
void clear_selection();
|
||||||
|
|
||||||
Rect2 get_selected_probability_rect() const;
|
Rect2 get_selected_probability_rect() const;
|
||||||
double get_selected_probability_weight() const;
|
double get_selected_probability_weight() const;
|
||||||
|
|
|
@ -78,6 +78,7 @@ LimboStringNames::LimboStringNames() {
|
||||||
HeaderSmall = SN("HeaderSmall");
|
HeaderSmall = SN("HeaderSmall");
|
||||||
Help = SN("Help");
|
Help = SN("Help");
|
||||||
icon_max_width = SN("icon_max_width");
|
icon_max_width = SN("icon_max_width");
|
||||||
|
class_icon_size = SN("class_icon_size");
|
||||||
id_pressed = SN("id_pressed");
|
id_pressed = SN("id_pressed");
|
||||||
Info = SN("Info");
|
Info = SN("Info");
|
||||||
item_collapsed = SN("item_collapsed");
|
item_collapsed = SN("item_collapsed");
|
||||||
|
@ -127,7 +128,7 @@ LimboStringNames::LimboStringNames() {
|
||||||
task_activated = SN("task_activated");
|
task_activated = SN("task_activated");
|
||||||
task_button_pressed = SN("task_button_pressed");
|
task_button_pressed = SN("task_button_pressed");
|
||||||
task_button_rmb = SN("task_button_rmb");
|
task_button_rmb = SN("task_button_rmb");
|
||||||
task_dragged = SN("task_dragged");
|
tasks_dragged = SN("tasks_dragged");
|
||||||
task_meta = SN("task_meta");
|
task_meta = SN("task_meta");
|
||||||
task_selected = SN("task_selected");
|
task_selected = SN("task_selected");
|
||||||
text_changed = SN("text_changed");
|
text_changed = SN("text_changed");
|
||||||
|
|
|
@ -94,6 +94,7 @@ public:
|
||||||
StringName HeaderSmall;
|
StringName HeaderSmall;
|
||||||
StringName Help;
|
StringName Help;
|
||||||
StringName icon_max_width;
|
StringName icon_max_width;
|
||||||
|
StringName class_icon_size;
|
||||||
StringName id_pressed;
|
StringName id_pressed;
|
||||||
StringName Info;
|
StringName Info;
|
||||||
StringName item_collapsed;
|
StringName item_collapsed;
|
||||||
|
@ -143,7 +144,7 @@ public:
|
||||||
StringName task_activated;
|
StringName task_activated;
|
||||||
StringName task_button_pressed;
|
StringName task_button_pressed;
|
||||||
StringName task_button_rmb;
|
StringName task_button_rmb;
|
||||||
StringName task_dragged;
|
StringName tasks_dragged;
|
||||||
StringName task_meta;
|
StringName task_meta;
|
||||||
StringName task_selected;
|
StringName task_selected;
|
||||||
StringName text_changed;
|
StringName text_changed;
|
||||||
|
|
Loading…
Reference in New Issue