Improve multiple drag and drop
This commit is contained in:
parent
1742018696
commit
4df6647a75
|
@ -848,19 +848,10 @@ void LimboAIEditor::_on_tasks_dragged(const TypedArray<BTTask> &p_tasks, Ref<BTT
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter tasks
|
// Remove descendants of selected.
|
||||||
Vector<Ref<BTTask>> tasks_list;
|
Vector<Ref<BTTask>> tasks_list;
|
||||||
int no_effect = 0;
|
|
||||||
for (int i = 0; i < p_tasks.size(); i++) {
|
for (int i = 0; i < p_tasks.size(); i++) {
|
||||||
Ref<BTTask> task = p_tasks[i];
|
Ref<BTTask> task = p_tasks[i];
|
||||||
// Count tasks that don't change position
|
|
||||||
if (task == p_to_task) {
|
|
||||||
if (Math::abs(task->get_index() - p_to_pos) <= 1) {
|
|
||||||
++no_effect;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove descendants of selected
|
|
||||||
bool remove = false;
|
bool remove = false;
|
||||||
for (int s_idx = 0; s_idx < p_tasks.size(); s_idx++) {
|
for (int s_idx = 0; s_idx < p_tasks.size(); s_idx++) {
|
||||||
Ref<BTTask> selected = p_tasks[s_idx];
|
Ref<BTTask> selected = p_tasks[s_idx];
|
||||||
|
@ -873,48 +864,27 @@ void LimboAIEditor::_on_tasks_dragged(const TypedArray<BTTask> &p_tasks, Ref<BTT
|
||||||
tasks_list.push_back(task);
|
tasks_list.push_back(task);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (tasks_list.is_empty() || p_tasks.size() == no_effect) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
EditorUndoRedoManager *undo_redo = _new_undo_redo_action(TTR("Drag BT Task"));
|
EditorUndoRedoManager *undo_redo = _new_undo_redo_action(TTR("Drag BT Task"));
|
||||||
|
|
||||||
// Apply changes in the task hierarchy.
|
// Remove all tasks first so adding ordering is stable.
|
||||||
int drop_idx = p_to_pos;
|
int before_pos = 0;
|
||||||
for (const Ref<BTTask> &task : tasks_list) {
|
for (const Ref<BTTask> task : tasks_list) {
|
||||||
if (task->get_parent() == p_to_task && drop_idx > task->get_index()) {
|
if (task->get_parent() == p_to_task && p_to_pos > task->get_index()) {
|
||||||
drop_idx -= 1;
|
before_pos += 1;
|
||||||
}
|
}
|
||||||
if (task == p_to_task) {
|
|
||||||
if (Math::abs(task->get_index() - p_to_pos) <= 1) {
|
|
||||||
++drop_idx;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
undo_redo->add_do_method(task->get_parent().ptr(), LW_NAME(remove_child), task);
|
undo_redo->add_do_method(task->get_parent().ptr(), LW_NAME(remove_child), task);
|
||||||
|
}
|
||||||
|
|
||||||
undo_redo->add_do_method(p_to_task.ptr(), LW_NAME(add_child_at_index), task, drop_idx);
|
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);
|
undo_redo->add_undo_method(p_to_task.ptr(), LW_NAME(remove_child), task);
|
||||||
|
|
||||||
++drop_idx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-add tasks in later undo action so indexes match the old order.
|
// Re-add tasks in later undo action so indexes match the old order.
|
||||||
drop_idx = p_to_pos;
|
for (const Ref<BTTask> task : tasks_list) {
|
||||||
for (const Ref<BTTask> &task : tasks_list) {
|
|
||||||
if (task->get_parent() == p_to_task && drop_idx > task->get_index()) {
|
|
||||||
drop_idx -= 1;
|
|
||||||
}
|
|
||||||
if (task == p_to_task) {
|
|
||||||
if (Math::abs(task->get_index() - p_to_pos) <= 1) {
|
|
||||||
++drop_idx;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
undo_redo->add_undo_method(task->get_parent().ptr(), LW_NAME(add_child_at_index), task, task->get_index());
|
undo_redo->add_undo_method(task->get_parent().ptr(), LW_NAME(add_child_at_index), task, task->get_index());
|
||||||
++drop_idx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_commit_action_with_update(undo_redo);
|
_commit_action_with_update(undo_redo);
|
||||||
|
|
|
@ -266,15 +266,7 @@ Vector<Ref<BTTask>> TaskTree::get_selected_tasks() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void TaskTree::clear_selection() {
|
void TaskTree::clear_selection() {
|
||||||
Vector<TreeItem*> selected_tasks;
|
tree->deselect_all();
|
||||||
TreeItem *next = tree->get_next_selected(nullptr);
|
|
||||||
while (next) {
|
|
||||||
Ref<BTTask> task = next->get_metadata(0);
|
|
||||||
if (task.is_valid()) {
|
|
||||||
remove_selection(task);
|
|
||||||
}
|
|
||||||
next = tree->get_next_selected(next);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rect2 TaskTree::get_selected_probability_rect() const {
|
Rect2 TaskTree::get_selected_probability_rect() const {
|
||||||
|
@ -384,7 +376,7 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,12 +385,18 @@ bool TaskTree::_can_drop_data_fw(const Point2 &p_point, const Variant &p_data) c
|
||||||
if (tasks.is_empty()) {
|
if (tasks.is_empty()) {
|
||||||
return false; // No tasks.
|
return false; // No tasks.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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++) {
|
for (int i = 0; i < tasks.size(); i++) {
|
||||||
Ref<BTTask> task = tasks[i];
|
Ref<BTTask> task = tasks[i];
|
||||||
const Ref<BTTask> to_task = item->get_metadata(0);
|
if (to_task->is_descendant_of(task) || task == to_task) {
|
||||||
if (to_task->is_descendant_of(task) || task == to_task ||
|
return false; // Don't drop as child of selected tasks.
|
||||||
(task == to_task && task->get_index() + section >= to_task->get_index() && !item->is_collapsed() && item->get_child_count() > 0)) {
|
|
||||||
return false; // Don't drop as child of itself.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -408,55 +406,68 @@ bool TaskTree::_can_drop_data_fw(const Point2 &p_point, const Variant &p_data) c
|
||||||
|
|
||||||
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);
|
||||||
int type = tree->get_drop_section_at_position(p_point);
|
int type = tree->get_drop_section_at_position(p_point);
|
||||||
ERR_FAIL_NULL(item);
|
ERR_FAIL_NULL(item);
|
||||||
ERR_FAIL_COND(type < -1 || type > 1);
|
ERR_FAIL_COND(type < -1 || type > 1);
|
||||||
|
|
||||||
if (item && d.has("tasks")) {
|
// The drop behavior depends on the TreeItem's state.
|
||||||
TypedArray<BTTask> tasks = d["tasks"];
|
// Normalize and emit the parent task and position instead of exposing TreeItem.
|
||||||
int to_pos = -1;
|
int to_pos = -1;
|
||||||
Ref<BTTask> to_task = item->get_metadata(0);
|
Ref<BTTask> to_task = item->get_metadata(0);
|
||||||
ERR_FAIL_COND(to_task.is_null());
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
// The drop behavior depends on the TreeItem's state.
|
void TaskTree::_normalize_drop(TreeItem *item, int type, int &to_pos, Ref<BTTask> &to_task) const {
|
||||||
// Normalize and emit the parent task and position instead of exposing TreeItem.
|
switch (type) {
|
||||||
switch (type) {
|
case 0: // Drop as last child of target.
|
||||||
case 0: // Drop as last child of target.
|
to_pos = to_task->get_child_count();
|
||||||
to_pos = to_task->get_child_count();
|
break;
|
||||||
break;
|
case -1: // Drop above target.
|
||||||
case -1: // Drop above target.
|
ERR_FAIL_COND_MSG(to_task->get_parent().is_null(), "Cannot perform drop above the root task!");
|
||||||
ERR_FAIL_COND_MSG(to_task->get_parent().is_null(), "Cannot perform drop above the root task!");
|
to_pos = to_task->get_index();
|
||||||
to_pos = MAX(0, 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();
|
to_task = to_task->get_parent();
|
||||||
break;
|
break;
|
||||||
case 1: // Drop below target.
|
}
|
||||||
if (item->get_child_count() == 0) {
|
|
||||||
to_pos = to_task->get_index() + 1;
|
if (to_task->get_parent().is_null() || !item->is_collapsed()) { // Insert as first child of target.
|
||||||
to_task = to_task->get_parent();
|
to_pos = 0;
|
||||||
break;
|
} 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();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (to_task->get_parent().is_null() || !item->is_collapsed()) { // Insert as first child of target.
|
to_task = to_task->get_parent();
|
||||||
to_pos = 0;
|
}
|
||||||
} else { // Insert as sibling of target.
|
break;
|
||||||
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;
|
|
||||||
}
|
|
||||||
emit_signal(LW_NAME(tasks_dragged), tasks, to_task, to_pos);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue