diff --git a/app/node/nodecopypaste.cpp b/app/node/nodecopypaste.cpp index 7ae4ff17a..190146472 100644 --- a/app/node/nodecopypaste.cpp +++ b/app/node/nodecopypaste.cpp @@ -29,7 +29,7 @@ namespace olive { -void NodeCopyPasteService::CopyNodesToClipboard(const QVector &nodes, void *userdata) +void NodeCopyPasteService::CopyNodesToClipboard(QVector nodes, void *userdata) { QString copy_str; @@ -42,11 +42,22 @@ void NodeCopyPasteService::CopyNodesToClipboard(const QVector &nodes, vo writer.writeTextElement(QStringLiteral("version"), QString::number(Core::kProjectVersion)); writer.writeStartElement(QStringLiteral("nodes")); - foreach (Node* n, nodes) { + for (int i=0; iid()); n->Save(&writer); writer.writeEndElement(); // node + + // If this is a group, add the child nodes too + if (NodeGroup *g = dynamic_cast(n)) { + for (auto it=g->GetContextPositions().cbegin(); it!=g->GetContextPositions().cend(); it++) { + if (!nodes.contains(it.key())) { + nodes.append(it.key()); + } + } + } } writer.writeEndElement(); // nodes @@ -208,9 +219,6 @@ QVector NodeCopyPasteService::PasteNodesFromClipboard(NodeGraph *graph, } } - // Make connections - xml_node_data.PostConnect(data_version, command); - // Process contexts for (auto it=pasted_contexts.cbegin(); it!=pasted_contexts.cend(); it++) { Node *context = xml_node_data.node_ptrs.value(it.key()); @@ -229,6 +237,9 @@ QVector NodeCopyPasteService::PasteNodesFromClipboard(NodeGraph *graph, } } + // Make connections + xml_node_data.PostConnect(data_version, command); + return pasted_nodes; } diff --git a/app/node/nodecopypaste.h b/app/node/nodecopypaste.h index cc29d281a..41a25302e 100644 --- a/app/node/nodecopypaste.h +++ b/app/node/nodecopypaste.h @@ -35,7 +35,7 @@ public: NodeCopyPasteService() = default; protected: - void CopyNodesToClipboard(const QVector &nodes, void* userdata = nullptr); + void CopyNodesToClipboard(QVector nodes, void* userdata = nullptr); QVector PasteNodesFromClipboard(NodeGraph *graph, MultiUndoCommand *command, void* userdata = nullptr); diff --git a/app/panel/param/param.h b/app/panel/param/param.h index 5d7928f04..95ddc9b9a 100644 --- a/app/panel/param/param.h +++ b/app/panel/param/param.h @@ -38,6 +38,11 @@ public: return static_cast(GetTimeBasedWidget()); } + const QVector &GetContexts() const + { + return GetParamView()->GetContexts(); + } + void SetCreateCheckBoxes(NodeParamViewCheckBoxBehavior e) { GetParamView()->SetCreateCheckBoxes(e); diff --git a/app/widget/nodeparamview/nodeparamview.cpp b/app/widget/nodeparamview/nodeparamview.cpp index 86732d37c..68f04e7fd 100644 --- a/app/widget/nodeparamview/nodeparamview.cpp +++ b/app/widget/nodeparamview/nodeparamview.cpp @@ -238,6 +238,7 @@ void NodeParamView::SetContexts(const QVector &contexts) ctx->Clear(); ctx->setVisible(false); } + contexts_ = contexts; if (keyframe_view_) { keyframe_view_->Clear(); diff --git a/app/widget/nodeparamview/nodeparamview.h b/app/widget/nodeparamview/nodeparamview.h index 29110deca..b36e31cc0 100644 --- a/app/widget/nodeparamview/nodeparamview.h +++ b/app/widget/nodeparamview/nodeparamview.h @@ -77,6 +77,11 @@ public: void SelectNodes(const QVector &nodes); void DeselectNodes(const QVector &nodes); + const QVector &GetContexts() const + { + return contexts_; + } + public slots: void SetInputChecked(const NodeInput &input, bool e); @@ -133,6 +138,8 @@ private: bool ignore_flags_; + QVector contexts_; + private slots: void UpdateGlobalScrollBar(); diff --git a/app/widget/nodeparamview/nodeparamviewitembase.cpp b/app/widget/nodeparamview/nodeparamviewitembase.cpp index 709514326..5116d872a 100644 --- a/app/widget/nodeparamview/nodeparamviewitembase.cpp +++ b/app/widget/nodeparamview/nodeparamviewitembase.cpp @@ -52,7 +52,7 @@ NodeParamViewItemBase::NodeParamViewItemBase(QWidget *parent) : bool NodeParamViewItemBase::IsExpanded() const { - return body_->isVisible(); + return title_bar_->IsExpanded(); } QString NodeParamViewItemBase::GetTitleBarTextFromNode(Node *n) diff --git a/app/widget/nodeview/nodeview.cpp b/app/widget/nodeview/nodeview.cpp index 53621364b..481a88b71 100644 --- a/app/widget/nodeview/nodeview.cpp +++ b/app/widget/nodeview/nodeview.cpp @@ -47,7 +47,6 @@ NodeView::NodeView(QWidget *parent) : create_edge_(nullptr), create_edge_output_item_(nullptr), create_edge_input_item_(nullptr), - paste_command_(nullptr), scale_(1.0) { setScene(&scene_); @@ -283,14 +282,6 @@ void NodeView::keyPressEvent(QKeyEvent *event) case Qt::Key_Escape: if (!attached_items_.isEmpty()) { DetachItemsFromCursor(); - - // We undo the last action which SHOULD be adding the node - if (paste_command_) { - paste_command_->undo_now(); - delete paste_command_; - paste_command_ = nullptr; - } - break; } @@ -573,23 +564,18 @@ void NodeView::mouseReleaseEvent(QMouseEvent *event) } if (context) { - if (paste_command_) { - // We've already "done" this command, but MultiUndoCommand prevents "redoing" twice, so we - // add it to this command (which may have extra commands added too) so that it all gets undone - // in the same action - command->add_child(paste_command_); - paste_command_ = nullptr; - } - { MultiUndoCommand *add_command = new MultiUndoCommand(); foreach (const AttachedItem &ai, attached_items_) { // Add node to the same graph that the context is in - add_command->add_child(new NodeAddCommand(context->parent(), ai.item->GetNode())); + add_command->add_child(new NodeAddCommand(context->parent(), ai.node)); // Add node to the context - add_command->add_child(new NodeSetPositionCommand(ai.item->GetNode(), context, scene_.context_map().value(context)->MapScenePosToNodePosInContext(ai.item->pos()))); + if (ai.item) { + qDebug() << "Placing an item!"; + add_command->add_child(new NodeSetPositionCommand(ai.node, context, scene_.context_map().value(context)->MapScenePosToNodePosInContext(ai.item->pos()))); + } } if (add_command->child_count()) { @@ -604,9 +590,16 @@ void NodeView::mouseReleaseEvent(QMouseEvent *event) // Dropped attached item onto an edge, connect it between them MultiUndoCommand *drop_edge_command = new MultiUndoCommand(); if (attached_items_.size() == 1) { - Node* dropping_node = attached_items_.first().item->GetNode(); + Node* dropping_node = nullptr; - if (drop_edge_) { + foreach (const AttachedItem &ai, attached_items_) { + if (ai.item) { + dropping_node = ai.node; + break; + } + } + + if (dropping_node && drop_edge_) { // Remove old edge drop_edge_command->add_child(new NodeEdgeRemoveCommand(drop_edge_->output(), drop_edge_->input())); @@ -625,7 +618,7 @@ void NodeView::mouseReleaseEvent(QMouseEvent *event) } } - DetachItemsFromCursor(); + DetachItemsFromCursor(false); } else { QToolTip::showText(QCursor::pos(), tr("Nodes must be placed inside a context.")); } @@ -809,7 +802,8 @@ void NodeView::CreateNodeSlot(QAction *action) NodeViewItem *new_item = new NodeViewItem(new_node, nullptr); new_item->SetFlowDirection(scene_.GetFlowDirection()); scene_.addItem(new_item); - AttachItemsToCursor({new_item}); + + SetAttachedItems({{new_item, new_node, QPointF(0, 0)}}); } } @@ -899,23 +893,14 @@ void NodeView::NodeRemovedFromGraph() contexts_.removeOne(context); } -void NodeView::AttachItemsToCursor(const QVector& items) -{ - DetachItemsFromCursor(); - - if (!items.isEmpty()) { - for (NodeViewItem* i : items) { - attached_items_.append({i, i->pos() - items.first()->pos()}); - } - - MoveAttachedNodesToCursor(mapFromGlobal(QCursor::pos())); - } -} - -void NodeView::DetachItemsFromCursor() +void NodeView::DetachItemsFromCursor(bool delete_nodes_too) { foreach (const AttachedItem &ai, attached_items_) { delete ai.item; + + if (delete_nodes_too) { + delete ai.node; + } } attached_items_.clear(); @@ -931,7 +916,9 @@ void NodeView::MoveAttachedNodesToCursor(const QPoint& p) QPointF item_pos = mapToScene(p); for (const AttachedItem& i : qAsConst(attached_items_)) { - i.item->setPos(item_pos + i.original_pos); + if (i.item) { + i.item->setPos(item_pos + i.original_pos); + } } } @@ -990,14 +977,18 @@ void NodeView::CopyNodesToClipboardInternal(QXmlStreamWriter *writer, const QVec writer->writeStartElement(QStringLiteral("pos")); for (Node *n : nodes) { - Node::Position pos = GetAssumedPositionForSelectedNode(n); + NodeViewItem *item = GetAssumedItemForSelectedNode(n); - writer->writeStartElement(QStringLiteral("node")); - writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast(n))); - writer->writeTextElement(QStringLiteral("x"), QString::number(pos.position.x())); - writer->writeTextElement(QStringLiteral("y"), QString::number(pos.position.y())); - writer->writeTextElement(QStringLiteral("expanded"), QString::number(pos.expanded)); - writer->writeEndElement(); // node + if (item) { + Node::Position pos = item->GetNodePositionData(); + + writer->writeStartElement(QStringLiteral("node")); + writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast(n))); + writer->writeTextElement(QStringLiteral("x"), QString::number(pos.position.x())); + writer->writeTextElement(QStringLiteral("y"), QString::number(pos.position.y())); + writer->writeTextElement(QStringLiteral("expanded"), QString::number(pos.expanded)); + writer->writeEndElement(); // node + } } writer->writeEndElement(); // pos @@ -1077,19 +1068,27 @@ QPointF NodeView::GetEstimatedPositionForContext(NodeViewItem *item, Node *conte return item->GetNodePosition() - context_offsets_.value(context); } -Node::Position NodeView::GetAssumedPositionForSelectedNode(Node *node) +NodeViewItem *NodeView::GetAssumedItemForSelectedNode(Node *node) { // Try to find corresponding selected item foreach (NodeViewContext *ctx, scene_.context_map()) { NodeViewItem *item = ctx->GetItemFromMap(node); - if (item && item->isSelected()) { + if (item && item->GetNode() == node && item->isSelected()) { // Good enough - return Node::Position(item->GetNodePosition(), item->IsExpanded()); + return item; } } - // Fallback - return Node::Position(); + return nullptr; +} + +Node::Position NodeView::GetAssumedPositionForSelectedNode(Node *node) +{ + if (NodeViewItem *item = GetAssumedItemForSelectedNode(node)) { + return item->GetNodePositionData(); + } else { + return Node::Position(); + } } Menu *NodeView::CreateAddMenu(Menu *parent) @@ -1338,19 +1337,44 @@ void NodeView::PasteNodesInternal(const QVector &duplicate_nodes) // If no nodes were retrieved, do nothing if (!new_nodes.isEmpty()) { - QVector items(new_nodes.size()); + QVector new_attached; + + NodeViewItem *first_item = nullptr; for (int i=0; iSetFlowDirection(scene_.GetFlowDirection()); - new_item->SetNodePosition(map.value(node)); - scene_.addItem(new_item); - items[i] = new_item; + + // Determine if item had a position, if not don't create an item for it + NodeViewItem *new_item; + + if (map.contains(node)) { + new_item = new NodeViewItem(node, nullptr); + new_item->SetFlowDirection(scene_.GetFlowDirection()); + new_item->SetNodePosition(map.value(node)); + scene_.addItem(new_item); + + if (!first_item) { + first_item = new_item; + } + } else { + new_item = nullptr; + } + + new_attached.append({new_item, node, QPointF(0, 0)}); } - // Attach nodes to cursor - AttachItemsToCursor(items); + // Correct positions + if (first_item) { + for (int i=0; ipos() - ai.item->pos(); + } + } + } + + SetAttachedItems(new_attached); } } @@ -1389,4 +1413,15 @@ void NodeView::CollapseItem(NodeViewItem *item) item->setZValue(0); } +void NodeView::SetAttachedItems(const QVector &items) +{ + // Detach anything currently attached + DetachItemsFromCursor(); + + attached_items_ = items; + + // Move to cursor + MoveAttachedNodesToCursor(mapFromGlobal(QCursor::pos())); +} + } diff --git a/app/widget/nodeview/nodeview.h b/app/widget/nodeview/nodeview.h index c3cc73f71..77a188adc 100644 --- a/app/widget/nodeview/nodeview.h +++ b/app/widget/nodeview/nodeview.h @@ -133,9 +133,7 @@ protected: virtual void changeEvent(QEvent *e) override; private: - void AttachItemsToCursor(const QVector &items); - - void DetachItemsFromCursor(); + void DetachItemsFromCursor(bool delete_nodes_too = true); void SetFlowDirection(NodeViewCommon::FlowDirection dir); @@ -150,6 +148,7 @@ private: QPointF GetEstimatedPositionForContext(NodeViewItem *item, Node *context) const; + NodeViewItem *GetAssumedItemForSelectedNode(Node *node); Node::Position GetAssumedPositionForSelectedNode(Node *node); Menu *CreateAddMenu(Menu *parent); @@ -172,10 +171,12 @@ private: struct AttachedItem { NodeViewItem* item; + Node *node; QPointF original_pos; }; - QList attached_items_; + void SetAttachedItems(const QVector &items); + QVector attached_items_; NodeViewEdge* drop_edge_; NodeInput drop_input_; @@ -191,8 +192,6 @@ private: NodeViewScene scene_; - MultiUndoCommand* paste_command_; - QVector selected_nodes_; QVector contexts_; diff --git a/app/widget/nodeview/nodeviewitem.cpp b/app/widget/nodeview/nodeviewitem.cpp index 7389d2cd8..01c1c6eb6 100644 --- a/app/widget/nodeview/nodeviewitem.cpp +++ b/app/widget/nodeview/nodeviewitem.cpp @@ -97,6 +97,11 @@ NodeViewItem::~NodeViewItem() Q_ASSERT(edges_.isEmpty()); } +Node::Position NodeViewItem::GetNodePositionData() const +{ + return Node::Position(GetNodePosition(), IsExpanded()); +} + QPointF NodeViewItem::GetNodePosition() const { return ScreenToNodePoint(pos(), flow_dir_); diff --git a/app/widget/nodeview/nodeviewitem.h b/app/widget/nodeview/nodeviewitem.h index 6ec3a8ca1..d91733dcc 100644 --- a/app/widget/nodeview/nodeviewitem.h +++ b/app/widget/nodeview/nodeviewitem.h @@ -54,6 +54,7 @@ public: virtual ~NodeViewItem() override; + Node::Position GetNodePositionData() const; QPointF GetNodePosition() const; void SetNodePosition(const QPointF& pos); void SetNodePosition(const Node::Position& pos); diff --git a/app/window/mainwindow/mainwindow.cpp b/app/window/mainwindow/mainwindow.cpp index d17ff3a0f..dfb4e34bb 100644 --- a/app/window/mainwindow/mainwindow.cpp +++ b/app/window/mainwindow/mainwindow.cpp @@ -737,10 +737,14 @@ void MainWindow::FocusedPanelChanged(PanelWidget *panel) if (NodePanel *node_panel = dynamic_cast(panel)) { // Set param view contexts to these - bool is_default_node_panel = node_panel == node_panel_; - param_panel_->SetIgnoreNodeFlags(!is_default_node_panel); - param_panel_->SetCreateCheckBoxes(is_default_node_panel ? kNoCheckBoxes : kCheckBoxesOnNonConnected); - param_panel_->SetContexts(node_panel->GetContexts()); + const QVector &new_ctxs = node_panel->GetContexts(); + + if (new_ctxs != param_panel_->GetContexts()) { + bool is_default_node_panel = node_panel == node_panel_; + param_panel_->SetIgnoreNodeFlags(!is_default_node_panel); + param_panel_->SetCreateCheckBoxes(is_default_node_panel ? kNoCheckBoxes : kCheckBoxesOnNonConnected); + param_panel_->SetContexts(node_panel->GetContexts()); + } } else if (TimelinePanel* timeline = dynamic_cast(panel)) { // Signal timeline focus TimelineFocused(timeline->GetConnectedViewer());