Improve multiple drag and drop
This commit is contained in:
parent
1742018696
commit
ed727198f1
|
@ -848,19 +848,10 @@ void LimboAIEditor::_on_tasks_dragged(const TypedArray<BTTask> &p_tasks, Ref<BTT
|
|||
return;
|
||||
}
|
||||
|
||||
// Filter tasks
|
||||
// Remove descendants of selected.
|
||||
Vector<Ref<BTTask>> tasks_list;
|
||||
int no_effect = 0;
|
||||
for (int i = 0; i < p_tasks.size(); 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;
|
||||
for (int s_idx = 0; s_idx < p_tasks.size(); 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);
|
||||
}
|
||||
}
|
||||
if (tasks_list.is_empty() || p_tasks.size() == no_effect) {
|
||||
return;
|
||||
}
|
||||
|
||||
EditorUndoRedoManager *undo_redo = _new_undo_redo_action(TTR("Drag BT Task"));
|
||||
|
||||
// Apply changes in the task hierarchy.
|
||||
int drop_idx = p_to_pos;
|
||||
for (const Ref<BTTask> &task : tasks_list) {
|
||||
if (task->get_parent() == p_to_task && drop_idx > task->get_index()) {
|
||||
drop_idx -= 1;
|
||||
// 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;
|
||||
}
|
||||
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(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);
|
||||
|
||||
++drop_idx;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
++drop_idx;
|
||||
}
|
||||
|
||||
_commit_action_with_update(undo_redo);
|
||||
|
|
|
@ -266,15 +266,7 @@ Vector<Ref<BTTask>> TaskTree::get_selected_tasks() const {
|
|||
}
|
||||
|
||||
void TaskTree::clear_selection() {
|
||||
Vector<TreeItem*> selected_tasks;
|
||||
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);
|
||||
}
|
||||
tree->deselect_all();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (!item->get_parent() && section != 0) { // Before/after root item.
|
||||
if (!item->get_parent() && section < 0) { // Before root item.
|
||||
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()) {
|
||||
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++) {
|
||||
Ref<BTTask> task = tasks[i];
|
||||
const Ref<BTTask> to_task = item->get_metadata(0);
|
||||
if (to_task->is_descendant_of(task) || task == to_task ||
|
||||
(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.
|
||||
if (to_task->is_descendant_of(task) || task == to_task) {
|
||||
return false; // Don't drop as child of selected tasks.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
Dictionary d = p_data;
|
||||
if (!d.has("tasks")) {
|
||||
return;
|
||||
}
|
||||
|
||||
TreeItem *item = tree->get_item_at_position(p_point);
|
||||
int type = tree->get_drop_section_at_position(p_point);
|
||||
ERR_FAIL_NULL(item);
|
||||
ERR_FAIL_COND(type < -1 || type > 1);
|
||||
|
||||
if (item && d.has("tasks")) {
|
||||
TypedArray<BTTask> tasks = d["tasks"];
|
||||
int to_pos = -1;
|
||||
Ref<BTTask> to_task = item->get_metadata(0);
|
||||
ERR_FAIL_COND(to_task.is_null());
|
||||
// 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);
|
||||
}
|
||||
|
||||
// The drop behavior depends on the TreeItem's state.
|
||||
// Normalize and emit the parent task and position instead of exposing TreeItem.
|
||||
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 = MAX(0, to_task->get_index());
|
||||
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 = MAX(0, to_task->get_index() - 1);
|
||||
{
|
||||
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;
|
||||
case 1: // Drop below target.
|
||||
if (item->get_child_count() == 0) {
|
||||
to_pos = to_task->get_index() + 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();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
emit_signal(LW_NAME(tasks_dragged), tasks, to_task, to_pos);
|
||||
to_task = to_task->get_parent();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -75,6 +75,7 @@ private:
|
|||
Variant _get_drag_data_fw(const Point2 &p_point);
|
||||
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 _normalize_drop(TreeItem *item, int type, int &to_pos, Ref<BTTask> &to_task) const;
|
||||
|
||||
void _draw_probability(Object *item_obj, Rect2 rect);
|
||||
|
||||
|
|
Loading…
Reference in New Issue