nodeview: fix issues when deleting nodes while dragging
This commit is contained in:
@@ -477,77 +477,7 @@ void NodeView::mouseReleaseEvent(QMouseEvent *event)
|
||||
if (HandRelease(event)) return;
|
||||
|
||||
if (create_edge_) {
|
||||
// Check if the edge was reconnected to the same place as before
|
||||
MultiUndoCommand* command = new MultiUndoCommand();
|
||||
|
||||
bool reconnected_to_itself = false;
|
||||
|
||||
if (create_edge_already_exists_) {
|
||||
if (create_edge_output_item_ == create_edge_->from_item() && create_edge_->input() == create_edge_input_) {
|
||||
reconnected_to_itself = true;
|
||||
} else {
|
||||
// We are moving (or removing) an existing edge
|
||||
command->add_child(new NodeEdgeRemoveCommand(create_edge_->output(), create_edge_->input()));
|
||||
}
|
||||
} else {
|
||||
// We're creating a new edge, which means this UI object is only temporary
|
||||
delete create_edge_;
|
||||
}
|
||||
|
||||
create_edge_ = nullptr;
|
||||
|
||||
// Clear highlight if we set one
|
||||
if (create_edge_output_item_) {
|
||||
create_edge_output_item_->SetHighlighted(false);
|
||||
}
|
||||
if (create_edge_input_item_) {
|
||||
create_edge_input_item_->SetHighlighted(false);
|
||||
}
|
||||
|
||||
if (create_edge_output_item_ && create_edge_input_item_) {
|
||||
NodeInput &creating_input = create_edge_input_;
|
||||
if (creating_input.IsValid()) {
|
||||
// Make connection
|
||||
if (!reconnected_to_itself) {
|
||||
Node *creating_output = create_edge_output_item_->GetNode();
|
||||
|
||||
while (NodeGroup *output_group = dynamic_cast<NodeGroup*>(creating_output)) {
|
||||
creating_output = output_group->GetOutputPassthrough();
|
||||
}
|
||||
|
||||
while (NodeGroup *input_group = dynamic_cast<NodeGroup*>(creating_input.node())) {
|
||||
creating_input = input_group->GetInputPassthroughs().value(creating_input.input());
|
||||
}
|
||||
|
||||
if (creating_input.IsConnected()) {
|
||||
Node::OutputConnection existing_edge_to_remove = {creating_input.GetConnectedOutput(), creating_input};
|
||||
command->add_child(new NodeEdgeRemoveCommand(existing_edge_to_remove.first, existing_edge_to_remove.second));
|
||||
}
|
||||
|
||||
command->add_child(new NodeEdgeAddCommand(creating_output, creating_input));
|
||||
|
||||
// If the output is not in the input's context, add it now. We check the item rather than
|
||||
// the node itself, because sometimes a node may not be in the context but another node
|
||||
// representing it will be (e.g. groups)
|
||||
if (!scene_.context_map().value(create_edge_input_item_->GetContext())->GetItemFromMap(creating_output)) {
|
||||
command->add_child(new NodeSetPositionCommand(creating_output, create_edge_input_item_->GetContext(), scene_.context_map().value(create_edge_input_item_->GetContext())->MapScenePosToNodePosInContext(create_edge_output_item_->scenePos())));
|
||||
}
|
||||
}
|
||||
|
||||
creating_input.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
create_edge_output_item_ = nullptr;
|
||||
create_edge_input_item_ = nullptr;
|
||||
|
||||
// Collapse any items we expanded
|
||||
for (auto it=create_edge_expanded_items_.crbegin(); it!=create_edge_expanded_items_.crend(); it++) {
|
||||
CollapseItem(*it);
|
||||
}
|
||||
create_edge_expanded_items_.clear();
|
||||
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||
EndEdgeDrag();
|
||||
}
|
||||
|
||||
MultiUndoCommand* command = new MultiUndoCommand();
|
||||
@@ -1122,7 +1052,7 @@ void NodeView::PositionNewEdge(const QPoint &pos)
|
||||
NodeViewItem* nvi = create_edge_expanded_items_.at(i);
|
||||
QPointF local_pt = nvi->mapFromScene(scene_pt);
|
||||
|
||||
if (nvi->contains(local_pt) || (!nvi->IsOutputItem() && nvi->parentItem()->contains(nvi->parentItem()->mapFromScene(scene_pt)) && local_pt.y() > nvi->rect().bottom())) {
|
||||
if (nvi->scene() == &scene_ && (nvi->contains(local_pt) || (!nvi->IsOutputItem() && nvi->parentItem()->contains(nvi->parentItem()->mapFromScene(scene_pt)) && local_pt.y() > nvi->rect().bottom()))) {
|
||||
break;
|
||||
} else {
|
||||
// Collapsing an item will destroy its children, so if the cursor item happens to be a child
|
||||
@@ -1310,6 +1240,31 @@ void NodeView::LabelSelectedNodes()
|
||||
Core::instance()->LabelNodes(selected_nodes_);
|
||||
}
|
||||
|
||||
void NodeView::ItemAboutToBeDeleted(NodeViewItem *item)
|
||||
{
|
||||
dragging_items_.remove(item);
|
||||
|
||||
if (create_edge_) {
|
||||
// Item should be removed from scene, but not yet deleted, allowing a safe PositionNewEdge call
|
||||
// to disconnect
|
||||
PositionNewEdge(mapFromGlobal(QCursor::pos()));
|
||||
|
||||
QGraphicsItem *test = item;
|
||||
do {
|
||||
if (test == item) {
|
||||
break;
|
||||
}
|
||||
|
||||
test = test->parentItem();
|
||||
} while (test);
|
||||
|
||||
if (test == item) {
|
||||
// Cancel edge function
|
||||
EndEdgeDrag(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeView::PasteNodesInternal(const QVector<Node *> &duplicate_nodes)
|
||||
{
|
||||
// If no graph, do nothing
|
||||
@@ -1381,7 +1336,10 @@ void NodeView::PasteNodesInternal(const QVector<Node *> &duplicate_nodes)
|
||||
|
||||
void NodeView::AddContext(Node *n)
|
||||
{
|
||||
scene_.AddContext(n);
|
||||
NodeViewContext *ctx = scene_.AddContext(n);
|
||||
|
||||
connect(ctx, &NodeViewContext::ItemAboutToBeDeleted, this, &NodeView::ItemAboutToBeDeleted);
|
||||
|
||||
connect(n, &Node::RemovedFromGraph, this, &NodeView::NodeRemovedFromGraph);
|
||||
}
|
||||
|
||||
@@ -1414,6 +1372,83 @@ void NodeView::CollapseItem(NodeViewItem *item)
|
||||
item->setZValue(0);
|
||||
}
|
||||
|
||||
void NodeView::EndEdgeDrag(bool cancel)
|
||||
{
|
||||
// Check if the edge was reconnected to the same place as before
|
||||
MultiUndoCommand* command = new MultiUndoCommand();
|
||||
|
||||
bool reconnected_to_itself = false;
|
||||
|
||||
if (create_edge_already_exists_) {
|
||||
if (!cancel) {
|
||||
if (create_edge_output_item_ == create_edge_->from_item() && create_edge_->input() == create_edge_input_) {
|
||||
reconnected_to_itself = true;
|
||||
} else {
|
||||
// We are moving (or removing) an existing edge
|
||||
command->add_child(new NodeEdgeRemoveCommand(create_edge_->output(), create_edge_->input()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// We're creating a new edge, which means this UI object is only temporary
|
||||
delete create_edge_;
|
||||
}
|
||||
|
||||
create_edge_ = nullptr;
|
||||
|
||||
// Clear highlight if we set one
|
||||
if (create_edge_output_item_) {
|
||||
create_edge_output_item_->SetHighlighted(false);
|
||||
}
|
||||
if (create_edge_input_item_) {
|
||||
create_edge_input_item_->SetHighlighted(false);
|
||||
}
|
||||
|
||||
if (create_edge_output_item_ && create_edge_input_item_ && !cancel) {
|
||||
NodeInput &creating_input = create_edge_input_;
|
||||
if (creating_input.IsValid()) {
|
||||
// Make connection
|
||||
if (!reconnected_to_itself) {
|
||||
Node *creating_output = create_edge_output_item_->GetNode();
|
||||
|
||||
while (NodeGroup *output_group = dynamic_cast<NodeGroup*>(creating_output)) {
|
||||
creating_output = output_group->GetOutputPassthrough();
|
||||
}
|
||||
|
||||
while (NodeGroup *input_group = dynamic_cast<NodeGroup*>(creating_input.node())) {
|
||||
creating_input = input_group->GetInputPassthroughs().value(creating_input.input());
|
||||
}
|
||||
|
||||
if (creating_input.IsConnected()) {
|
||||
Node::OutputConnection existing_edge_to_remove = {creating_input.GetConnectedOutput(), creating_input};
|
||||
command->add_child(new NodeEdgeRemoveCommand(existing_edge_to_remove.first, existing_edge_to_remove.second));
|
||||
}
|
||||
|
||||
command->add_child(new NodeEdgeAddCommand(creating_output, creating_input));
|
||||
|
||||
// If the output is not in the input's context, add it now. We check the item rather than
|
||||
// the node itself, because sometimes a node may not be in the context but another node
|
||||
// representing it will be (e.g. groups)
|
||||
if (!scene_.context_map().value(create_edge_input_item_->GetContext())->GetItemFromMap(creating_output)) {
|
||||
command->add_child(new NodeSetPositionCommand(creating_output, create_edge_input_item_->GetContext(), scene_.context_map().value(create_edge_input_item_->GetContext())->MapScenePosToNodePosInContext(create_edge_output_item_->scenePos())));
|
||||
}
|
||||
}
|
||||
|
||||
creating_input.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
create_edge_output_item_ = nullptr;
|
||||
create_edge_input_item_ = nullptr;
|
||||
|
||||
// Collapse any items we expanded
|
||||
for (auto it=create_edge_expanded_items_.crbegin(); it!=create_edge_expanded_items_.crend(); it++) {
|
||||
CollapseItem(*it);
|
||||
}
|
||||
create_edge_expanded_items_.clear();
|
||||
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||
}
|
||||
|
||||
void NodeView::SetAttachedItems(const QVector<AttachedItem> &items)
|
||||
{
|
||||
// Detach anything currently attached
|
||||
|
||||
Reference in New Issue
Block a user