From 27433e0fd1fc12b9730d3cb4ce6944ae7f517b6e Mon Sep 17 00:00:00 2001 From: ReubenJCarter Date: Wed, 8 Nov 2017 17:34:24 +0000 Subject: [PATCH 01/39] Added duplicate action to the flow view. Removed shadow effect from nodes as the performance was bad. Added OpenGL rendering for flow view. --- src/FlowView.cpp | 78 ++++++++++++++++++++++++++++++++++++-- src/FlowView.hpp | 3 ++ src/NodeGraphicsObject.cpp | 12 +++--- 3 files changed, 84 insertions(+), 9 deletions(-) diff --git a/src/FlowView.cpp b/src/FlowView.cpp index 5a3b61438..1a7a636c2 100644 --- a/src/FlowView.cpp +++ b/src/FlowView.cpp @@ -48,7 +48,7 @@ FlowView(FlowScene *scene) setCacheMode(QGraphicsView::CacheBackground); - //setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers))); + setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers))); // setup actions _clearSelectionAction = new QAction(QStringLiteral("Clear Selection"), this); @@ -60,6 +60,13 @@ FlowView(FlowScene *scene) _deleteSelectionAction->setShortcut(Qt::Key_Delete); connect(_deleteSelectionAction, &QAction::triggered, this, &FlowView::deleteSelectedNodes); addAction(_deleteSelectionAction); + + + _duplicateSelectionAction = new QAction(QStringLiteral("Delete Selection"), this); + _duplicateSelectionAction->setShortcut(Qt::Key_D); + connect(_duplicateSelectionAction, &QAction::triggered, this, &FlowView::duplicateSelectedNode); + addAction(_duplicateSelectionAction); + } @@ -132,7 +139,7 @@ contextMenuEvent(QContextMenuEvent *event) treeView->expandAll(); - connect(treeView, &QTreeWidget::itemClicked, [&](QTreeWidgetItem *item, int) + connect(treeView, &QTreeWidget::itemActivated, [&](QTreeWidgetItem *item, int) { QString modelName = item->data(0, Qt::UserRole).toString(); @@ -256,6 +263,72 @@ deleteSelectedNodes() } +void FlowView::duplicateSelectedNode() +{ + //Get Bounds of all the selected items + float minx = 10000000000; + float miny = 10000000000; + float maxx = -1000000000; + float maxy = -1000000000; + for (QGraphicsItem * item : _scene->selectedItems()) + { + if (auto n = qgraphicsitem_cast(item)) + { + QPointF pos = n->pos(); + if(pos.x() < minx) minx = pos.x(); + if(pos.y() < miny) miny = pos.y(); + if(pos.x() > maxx) maxx = pos.x(); + if(pos.y() > maxy) maxy = pos.y(); + } + } + //compute centroid + float centroidX = (maxx - minx) / 2.0 + minx; + float centroidY = (maxy - miny) / 2.0 + miny; + QPointF centroid(centroidX, centroidY); + + //create nodes + std::vector createdNodes; + std::vector couterpartNode; + for (QGraphicsItem * item : _scene->selectedItems()) + { + if (auto n = qgraphicsitem_cast(item)) + { + QString modelName = n->node().nodeDataModel()->name(); + + auto& type = _scene->registry().create(modelName); + + if (type) + { + auto& node = _scene->createNode(std::move(type)); + createdNodes.push_back(&node); + couterpartNode.push_back(&(n->node())); + + QPoint viewPointMouse = this->mapFromGlobal(QCursor::pos()); + QPointF posViewMouse = this->mapToScene(viewPointMouse); + + QPointF pos = posViewMouse + (n->pos() - centroid); + + node.nodeGraphicsObject().setPos(pos); + } + else + { + qDebug() << "Model not found"; + } + } + } + + //create connections + + + //reset selection to nodes created + _scene->clearSelection(); + for(int i = 0; i < createdNodes.size(); i++) + { + createdNodes[i]->nodeGraphicsObject().setSelected(true); + } +} + + void FlowView:: keyPressEvent(QKeyEvent *event) @@ -265,7 +338,6 @@ keyPressEvent(QKeyEvent *event) case Qt::Key_Shift: setDragMode(QGraphicsView::RubberBandDrag); break; - default: break; } diff --git a/src/FlowView.hpp b/src/FlowView.hpp index 1440fe158..a16d4f15c 100644 --- a/src/FlowView.hpp +++ b/src/FlowView.hpp @@ -30,6 +30,8 @@ public slots: void scaleDown(); void deleteSelectedNodes(); + + void duplicateSelectedNode(); protected: @@ -57,6 +59,7 @@ public slots: QAction* _clearSelectionAction; QAction* _deleteSelectionAction; + QAction* _duplicateSelectionAction; QPointF _clickPos; diff --git a/src/NodeGraphicsObject.cpp b/src/NodeGraphicsObject.cpp index 19bc46aa3..185b5150c 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -43,12 +43,12 @@ NodeGraphicsObject(FlowScene &scene, auto const &nodeStyle = StyleCollection::nodeStyle(); { - auto effect = new QGraphicsDropShadowEffect; - effect->setOffset(4, 4); - effect->setBlurRadius(20); - effect->setColor(nodeStyle.ShadowColor); - - setGraphicsEffect(effect); + //auto effect = new QGraphicsDropShadowEffect; + //effect->setOffset(4, 4); + //effect->setBlurRadius(0); + //effect->setColor(nodeStyle.ShadowColor); + //auto effect = new QGraphicsColorizeEffect ; + //setGraphicsEffect(effect); } setOpacity(nodeStyle.Opacity); From 4b90d7d0c2dfd79be212d756b9ffc5eff26e99f1 Mon Sep 17 00:00:00 2001 From: ReubenJCarter Date: Wed, 8 Nov 2017 18:04:23 +0000 Subject: [PATCH 02/39] Added duplicating connections, there is a bug to handle connections that have no endpoint on selection --- src/FlowView.cpp | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/FlowView.cpp b/src/FlowView.cpp index 1a7a636c2..ca99ea3a2 100644 --- a/src/FlowView.cpp +++ b/src/FlowView.cpp @@ -318,7 +318,41 @@ void FlowView::duplicateSelectedNode() } //create connections - + std::vector > createdConnections; + for (QGraphicsItem * item : _scene->selectedItems()) + { + if (auto c = qgraphicsitem_cast(item)) + { + //if(c->connection().connectionState().) + + Node* nodeIn = c->connection().getNode(PortType::In); + PortIndex portIndexIn = c->connection().getPortIndex(PortType::In); + Node* nodeOut = c->connection().getNode(PortType::Out); + PortIndex portIndexOut = c->connection().getPortIndex(PortType::Out); + + //find index of node in couterpartNode Array + int j = -1; + for(j = 0; j < couterpartNode.size(); j++) + { + if(couterpartNode[j] == nodeIn) + break; + } + + int k = -1; + for(k = 0; k < couterpartNode.size(); k++) + { + if(couterpartNode[k] == nodeOut) + break; + } + + if(j >=0 && k>=0) + { + auto& connection = _scene->createConnection(*createdNodes[j], portIndexIn, *createdNodes[k], portIndexOut); + createdConnections.push_back(connection); + } + } + } + //reset selection to nodes created _scene->clearSelection(); @@ -326,6 +360,10 @@ void FlowView::duplicateSelectedNode() { createdNodes[i]->nodeGraphicsObject().setSelected(true); } + for(int i = 0; i < createdConnections.size(); i++) + { + createdConnections[i]->getConnectionGraphicsObject().setSelected(true); + } } From c5080282772d2f0ca7962c22b97686ff32b9ba4d Mon Sep 17 00:00:00 2001 From: ReubenJCarter Date: Thu, 9 Nov 2017 09:44:14 +0000 Subject: [PATCH 03/39] added CTRL+D key sequence for duplicate --- src/FlowView.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/FlowView.cpp b/src/FlowView.cpp index ca99ea3a2..e47995bdd 100644 --- a/src/FlowView.cpp +++ b/src/FlowView.cpp @@ -63,7 +63,7 @@ FlowView(FlowScene *scene) _duplicateSelectionAction = new QAction(QStringLiteral("Delete Selection"), this); - _duplicateSelectionAction->setShortcut(Qt::Key_D); + _duplicateSelectionAction->setShortcut(QKeySequence(tr("Ctrl+D"))); connect(_duplicateSelectionAction, &QAction::triggered, this, &FlowView::duplicateSelectedNode); addAction(_duplicateSelectionAction); @@ -345,7 +345,7 @@ void FlowView::duplicateSelectedNode() break; } - if(j >=0 && k>=0) + if(j >=0 && k>=0 && j < couterpartNode.size() && k < couterpartNode.size()) { auto& connection = _scene->createConnection(*createdNodes[j], portIndexIn, *createdNodes[k], portIndexOut); createdConnections.push_back(connection); From facd31b1d6e2e414cd209cd4591c0e766c733980 Mon Sep 17 00:00:00 2001 From: ReubenJCarter Date: Thu, 9 Nov 2017 11:04:13 +0000 Subject: [PATCH 04/39] Fixed node delete pointer error --- src/FlowView.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/FlowView.cpp b/src/FlowView.cpp index e47995bdd..8d90b74bc 100644 --- a/src/FlowView.cpp +++ b/src/FlowView.cpp @@ -248,17 +248,25 @@ void FlowView:: deleteSelectedNodes() { + std::cout << "deleteSelectedNodes" << std::endl; // delete the nodes, this will delete many of the connections - for (QGraphicsItem * item : _scene->selectedItems()) + std::vector nodeItems = _scene->selectedNodes(); + for(int i = 0; i < nodeItems.size(); i++) { - if (auto n = qgraphicsitem_cast(item)) - _scene->removeNode(n->node()); + Node* item = nodeItems[i]; + if(item) + { + _scene->removeNode(*item); + } } for (QGraphicsItem * item : _scene->selectedItems()) { - if (auto c = qgraphicsitem_cast(item)) - _scene->deleteConnection(c->connection()); + if(item) + { + if (auto c = qgraphicsitem_cast(item)) + _scene->deleteConnection(c->connection()); + } } } From a92ba580e877f2ebd0519c5266130978cb05eb15 Mon Sep 17 00:00:00 2001 From: ReubenJCarter Date: Thu, 9 Nov 2017 14:34:35 +0000 Subject: [PATCH 05/39] Added very simple Undo Redo system --- src/FlowScene.cpp | 69 +++++++++++++++++++++++++++++++ src/FlowScene.hpp | 17 ++++++++ src/FlowView.cpp | 22 +++++++++- src/FlowView.hpp | 2 + src/NodeConnectionInteraction.cpp | 6 +++ 5 files changed, 114 insertions(+), 2 deletions(-) diff --git a/src/FlowScene.cpp b/src/FlowScene.cpp index a8d854c6d..7eb70933d 100644 --- a/src/FlowScene.cpp +++ b/src/FlowScene.cpp @@ -42,6 +42,9 @@ FlowScene(std::shared_ptr registry) : _registry(registry) { setItemIndexMethod(QGraphicsScene::NoIndex); + + ResetHistory(); + UpdateHistory(); } @@ -147,6 +150,7 @@ createNode(std::unique_ptr && dataModel) _nodes[node->id()] = std::move(node); nodeCreated(*nodePtr); + return *nodePtr; } @@ -510,8 +514,73 @@ loadFromMemory(const QByteArray& data) { restoreConnection(connectionJsonArray[i].toObject()); } + + } +void FlowScene::Undo() +{ + std::cout << "Undo" << std::endl; + if(historyInx > 1) + { + writeToHistory = false; + clearScene(); + historyInx--; + loadFromMemory(history[historyInx - 1].data); + writeToHistory = true; + } +} + +void FlowScene::Redo() +{ + std::cout << "Redo" << std::endl; + writeToHistory = false; + if(historyInx < history.size()) + { + std::cout << "historyInx:" << historyInx << " history.size():" << history.size() << std::endl; + clearScene(); + loadFromMemory(history[historyInx].data); + historyInx++; + } + else + { + std::cout << "Could not redo" << std::endl; + } + writeToHistory = true; +} + +void FlowScene::UpdateHistory() +{ + if(writeToHistory) + { + std::cout << "UpdateHistory" << std::endl; + + SceneHistory sh; + sh.data = saveToMemory(); + + if(historyInx < history.size()) + { + history.resize(historyInx); + history.push_back(sh); + } + else + { + history.push_back(sh); + } + + historyInx++; + } +} + +void FlowScene::ResetHistory() +{ + historyInx = 0; + writeToHistory = true; + history.clear(); +} + + + //------------------------------------------------------------------------------ namespace QtNodes diff --git a/src/FlowScene.hpp b/src/FlowScene.hpp index aa00de901..581081332 100644 --- a/src/FlowScene.hpp +++ b/src/FlowScene.hpp @@ -23,6 +23,11 @@ class Connection; class ConnectionGraphicsObject; class NodeStyle; +struct SceneHistory +{ + QByteArray data; +}; + /// Scene holds connections and nodes. class NODE_EDITOR_PUBLIC FlowScene : public QGraphicsScene @@ -90,6 +95,14 @@ class NODE_EDITOR_PUBLIC FlowScene QByteArray saveToMemory() const; void loadFromMemory(const QByteArray& data); + + void Undo(); + + void Redo(); + + void UpdateHistory(); + + void ResetHistory(); signals: @@ -122,6 +135,10 @@ class NODE_EDITOR_PUBLIC FlowScene std::unordered_map _connections; std::unordered_map _nodes; std::shared_ptr _registry; + + int historyInx; + bool writeToHistory; + std::vector< SceneHistory > history; }; Node* diff --git a/src/FlowView.cpp b/src/FlowView.cpp index 8d90b74bc..574bde220 100644 --- a/src/FlowView.cpp +++ b/src/FlowView.cpp @@ -62,11 +62,21 @@ FlowView(FlowScene *scene) addAction(_deleteSelectionAction); - _duplicateSelectionAction = new QAction(QStringLiteral("Delete Selection"), this); + _duplicateSelectionAction = new QAction(QStringLiteral("Duplicate Selection"), this); _duplicateSelectionAction->setShortcut(QKeySequence(tr("Ctrl+D"))); connect(_duplicateSelectionAction, &QAction::triggered, this, &FlowView::duplicateSelectedNode); addAction(_duplicateSelectionAction); + _undoAction = new QAction(QStringLiteral("Undo"), this); + _undoAction->setShortcut(QKeySequence(tr("Ctrl+Z"))); + connect(_undoAction, &QAction::triggered, _scene, &FlowScene::Undo); + addAction(_undoAction); + + _redoAction = new QAction(QStringLiteral("Redo"), this); + _redoAction->setShortcut(QKeySequence(tr("Ctrl+Y"))); + connect(_redoAction, &QAction::triggered, _scene, &FlowScene::Redo); + addAction(_redoAction); + } @@ -159,6 +169,8 @@ contextMenuEvent(QContextMenuEvent *event) QPointF posView = this->mapToScene(pos); node.nodeGraphicsObject().setPos(posView); + + _scene->UpdateHistory(); } else { @@ -248,7 +260,7 @@ void FlowView:: deleteSelectedNodes() { - std::cout << "deleteSelectedNodes" << std::endl; + //std::cout << "deleteSelectedNodes" << std::endl; // delete the nodes, this will delete many of the connections std::vector nodeItems = _scene->selectedNodes(); for(int i = 0; i < nodeItems.size(); i++) @@ -268,6 +280,8 @@ deleteSelectedNodes() _scene->deleteConnection(c->connection()); } } + + _scene->UpdateHistory(); } @@ -372,6 +386,10 @@ void FlowView::duplicateSelectedNode() { createdConnections[i]->getConnectionGraphicsObject().setSelected(true); } + + + if(createdNodes.size() > 0) + _scene->UpdateHistory(); } diff --git a/src/FlowView.hpp b/src/FlowView.hpp index a16d4f15c..aed92f1d5 100644 --- a/src/FlowView.hpp +++ b/src/FlowView.hpp @@ -60,6 +60,8 @@ public slots: QAction* _clearSelectionAction; QAction* _deleteSelectionAction; QAction* _duplicateSelectionAction; + QAction* _undoAction; + QAction* _redoAction; QPointF _clickPos; diff --git a/src/NodeConnectionInteraction.cpp b/src/NodeConnectionInteraction.cpp index acab66ff0..1fd5c1160 100644 --- a/src/NodeConnectionInteraction.cpp +++ b/src/NodeConnectionInteraction.cpp @@ -114,11 +114,13 @@ tryConnect() const { _scene->createConnection(converterNode, 0, *outNode, outNodePortIndex); _scene->createConnection(*_node, portIndex, converterNode, 0); + _scene->UpdateHistory(); } else { _scene->createConnection(converterNode, 0, *_node, portIndex); _scene->createConnection(*outNode, outNodePortIndex, converterNode, 0); + _scene->UpdateHistory(); } //Delete the users connection, we already replaced it. @@ -150,6 +152,8 @@ tryConnect() const PortIndex outPortIndex = _connection->getPortIndex(PortType::Out); outNode->onDataUpdated(outPortIndex); } + + _scene->UpdateHistory(); return true; } @@ -180,6 +184,8 @@ disconnect(PortType portToDisconnect) const _connection->getConnectionGraphicsObject().grabMouse(); + _scene->UpdateHistory(); + return true; } From e42fe23222efef9e2af9089dd74895e15a783561 Mon Sep 17 00:00:00 2001 From: ReubenJCarter Date: Tue, 14 Nov 2017 18:05:17 +0000 Subject: [PATCH 06/39] Added action context WidgetWithChildrenShortcut. Added duplication of data model to nodes when duplicating --- src/FlowView.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/FlowView.cpp b/src/FlowView.cpp index 574bde220..31fde9305 100644 --- a/src/FlowView.cpp +++ b/src/FlowView.cpp @@ -58,22 +58,26 @@ FlowView(FlowScene *scene) _deleteSelectionAction = new QAction(QStringLiteral("Delete Selection"), this); _deleteSelectionAction->setShortcut(Qt::Key_Delete); + _deleteSelectionAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); connect(_deleteSelectionAction, &QAction::triggered, this, &FlowView::deleteSelectedNodes); addAction(_deleteSelectionAction); _duplicateSelectionAction = new QAction(QStringLiteral("Duplicate Selection"), this); _duplicateSelectionAction->setShortcut(QKeySequence(tr("Ctrl+D"))); + _duplicateSelectionAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); connect(_duplicateSelectionAction, &QAction::triggered, this, &FlowView::duplicateSelectedNode); addAction(_duplicateSelectionAction); _undoAction = new QAction(QStringLiteral("Undo"), this); _undoAction->setShortcut(QKeySequence(tr("Ctrl+Z"))); + _undoAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); connect(_undoAction, &QAction::triggered, _scene, &FlowScene::Undo); addAction(_undoAction); _redoAction = new QAction(QStringLiteral("Redo"), this); _redoAction->setShortcut(QKeySequence(tr("Ctrl+Y"))); + _redoAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); connect(_redoAction, &QAction::triggered, _scene, &FlowScene::Redo); addAction(_redoAction); @@ -311,6 +315,7 @@ void FlowView::duplicateSelectedNode() //create nodes std::vector createdNodes; std::vector couterpartNode; + std::vector nodeData; for (QGraphicsItem * item : _scene->selectedItems()) { if (auto n = qgraphicsitem_cast(item)) @@ -322,6 +327,7 @@ void FlowView::duplicateSelectedNode() if (type) { auto& node = _scene->createNode(std::move(type)); + node.nodeDataModel()->restore(n->node().nodeDataModel()->save()); createdNodes.push_back(&node); couterpartNode.push_back(&(n->node())); From b5f7310c0a82dddf9b588001e410f58a0e387ec0 Mon Sep 17 00:00:00 2001 From: ReubenJCarter Date: Wed, 15 Nov 2017 12:07:33 +0000 Subject: [PATCH 07/39] added updating history for node movement --- src/FlowScene.cpp | 8 +++++++- src/FlowScene.hpp | 2 ++ src/NodeGraphicsObject.cpp | 4 ++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/FlowScene.cpp b/src/FlowScene.cpp index 7eb70933d..195df72da 100644 --- a/src/FlowScene.cpp +++ b/src/FlowScene.cpp @@ -45,6 +45,12 @@ FlowScene(std::shared_ptr registry) ResetHistory(); UpdateHistory(); + + auto UpdateLamda = [this](Node& n, const QPointF& p) + { + UpdateHistory(); + }; + connect(this, &FlowScene::nodeMoveFinished, this, UpdateLamda); } @@ -105,7 +111,7 @@ createConnection(Node& nodeIn, _connections[connection->id()] = connection; connectionCreated(*connection); - + return connection; } diff --git a/src/FlowScene.hpp b/src/FlowScene.hpp index 581081332..9f34ccdad 100644 --- a/src/FlowScene.hpp +++ b/src/FlowScene.hpp @@ -114,6 +114,8 @@ class NODE_EDITOR_PUBLIC FlowScene void connectionDeleted(Connection &c); void nodeMoved(Node& n, const QPointF& newLocation); + + void nodeMoveFinished(Node& n, const QPointF& newLocation); void nodeDoubleClicked(Node& n); diff --git a/src/NodeGraphicsObject.cpp b/src/NodeGraphicsObject.cpp index 185b5150c..9097a3f23 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -312,6 +312,8 @@ mouseMoveEvent(QGraphicsSceneMouseEvent * event) moveConnections(); event->ignore(); + + } QRectF r = scene()->sceneRect(); @@ -331,6 +333,8 @@ mouseReleaseEvent(QGraphicsSceneMouseEvent* event) state.setResizing(false); QGraphicsObject::mouseReleaseEvent(event); + + _scene.nodeMoveFinished(_node, pos()); // position connections precisely after fast node move moveConnections(); From d649c82f8aacb4921f4a78cc3309e2b099224ba4 Mon Sep 17 00:00:00 2001 From: ReubenJCarter Date: Fri, 17 Nov 2017 10:27:59 +0000 Subject: [PATCH 08/39] Added Fix for Mingw "invalid initialization of non-const reference of type" --- src/FlowView.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/FlowView.cpp b/src/FlowView.cpp index 31fde9305..ff960bd42 100644 --- a/src/FlowView.cpp +++ b/src/FlowView.cpp @@ -321,12 +321,12 @@ void FlowView::duplicateSelectedNode() if (auto n = qgraphicsitem_cast(item)) { QString modelName = n->node().nodeDataModel()->name(); - - auto& type = _scene->registry().create(modelName); + auto type = _scene->registry().create(modelName); + auto& typeRef = type; - if (type) + if (typeRef) { - auto& node = _scene->createNode(std::move(type)); + auto& node = _scene->createNode(std::move(typeRef)); node.nodeDataModel()->restore(n->node().nodeDataModel()->save()); createdNodes.push_back(&node); couterpartNode.push_back(&(n->node())); @@ -375,7 +375,8 @@ void FlowView::duplicateSelectedNode() if(j >=0 && k>=0 && j < couterpartNode.size() && k < couterpartNode.size()) { - auto& connection = _scene->createConnection(*createdNodes[j], portIndexIn, *createdNodes[k], portIndexOut); + auto connection = _scene->createConnection(*createdNodes[j], portIndexIn, *createdNodes[k], portIndexOut); + auto& connectionRef = connection; createdConnections.push_back(connection); } } From c0737644d2d3f9cf27732b3868e321023cb23338 Mon Sep 17 00:00:00 2001 From: ReubenJCarter Date: Wed, 7 Feb 2018 18:04:58 +0000 Subject: [PATCH 09/39] added setViewportUpdateMode(QGraphicsView::FullViewportUpdate); to flow view. --- src/FlowView.cpp | 13 ++++++++++++- src/FlowView.hpp | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/FlowView.cpp b/src/FlowView.cpp index ff960bd42..3a691ad9b 100644 --- a/src/FlowView.cpp +++ b/src/FlowView.cpp @@ -48,8 +48,19 @@ FlowView(FlowScene *scene) setCacheMode(QGraphicsView::CacheBackground); + /* + QGLFormat* fmt = new QGLFormat; + fmt->setDoubleBuffer(false); + fmt->setDirectRendering(false); + QGLContext* ctx = new QGLContext(*fmt); + */ + setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers))); - + //setViewport(new QGLWidget); + //setViewport(new QGLWidget(ctx)); + + setViewportUpdateMode(QGraphicsView::FullViewportUpdate); + // setup actions _clearSelectionAction = new QAction(QStringLiteral("Clear Selection"), this); _clearSelectionAction->setShortcut(Qt::Key_Escape); diff --git a/src/FlowView.hpp b/src/FlowView.hpp index aed92f1d5..ebe77e428 100644 --- a/src/FlowView.hpp +++ b/src/FlowView.hpp @@ -13,7 +13,7 @@ class NODE_EDITOR_PUBLIC FlowView : public QGraphicsView { public: - + FlowView(FlowScene *scene); FlowView(const FlowView&) = delete; From 243c382ce82942023871002ceee853e0027081b8 Mon Sep 17 00:00:00 2001 From: ReubenJCarter Date: Mon, 19 Feb 2018 14:34:20 +0000 Subject: [PATCH 10/39] Turn on/off the background grid render based on scale in viewport for performance benefits --- src/FlowView.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/FlowView.cpp b/src/FlowView.cpp index 3a691ad9b..1318005ab 100644 --- a/src/FlowView.cpp +++ b/src/FlowView.cpp @@ -492,6 +492,9 @@ drawBackground(QPainter* painter, const QRectF& r) double bottom = std::floor(tl.y() / gridStep - 0.5); double top = std::floor (br.y() / gridStep + 1.0); + if(right - left > 100) + return; + // vertical lines for (int xi = int(left); xi <= int(right); ++xi) { From 7215528fe658713339076a11eed5009e6e79941a Mon Sep 17 00:00:00 2001 From: ReubenJCarter Date: Tue, 20 Feb 2018 18:02:02 +0000 Subject: [PATCH 11/39] Added the ability to set the tool tip for each node --- src/Node.cpp | 3 +++ src/NodeDataModel.cpp | 13 +++++++++++++ src/NodeDataModel.hpp | 9 +++++++++ src/NodeGraphicsObject.cpp | 4 ++++ 4 files changed, 29 insertions(+) diff --git a/src/Node.cpp b/src/Node.cpp index e72d08184..43d22ec53 100644 --- a/src/Node.cpp +++ b/src/Node.cpp @@ -35,6 +35,7 @@ Node(std::unique_ptr && dataModel) // propagate data: model => node connect(_nodeDataModel.get(), &NodeDataModel::dataUpdated, this, &Node::onDataUpdated); + } @@ -136,6 +137,8 @@ setGraphicsObject(std::unique_ptr&& graphics) _nodeGraphicsObject = std::move(graphics); _nodeGeometry.recalculateSize(); + + nodeGraphicsObject().setToolTip(_nodeDataModel->toolTipText()); } diff --git a/src/NodeDataModel.cpp b/src/NodeDataModel.cpp index 9737b345d..1f48c5108 100644 --- a/src/NodeDataModel.cpp +++ b/src/NodeDataModel.cpp @@ -1,6 +1,7 @@ #include "NodeDataModel.hpp" #include "StyleCollection.hpp" +#include using QtNodes::NodeDataModel; using QtNodes::NodeStyle; @@ -39,3 +40,15 @@ setNodeStyle(NodeStyle const& style) { _nodeStyle = style; } + + +void NodeDataModel::setToolTipText(QString text) +{ + _toolTipText = text; + emit setToolTipTextSignal(text); +} + +QString NodeDataModel::toolTipText() +{ + return _toolTipText; +} diff --git a/src/NodeDataModel.hpp b/src/NodeDataModel.hpp index 51714ad16..cedd6c5ba 100644 --- a/src/NodeDataModel.hpp +++ b/src/NodeDataModel.hpp @@ -60,6 +60,8 @@ class NODE_EDITOR_PUBLIC NodeDataModel /// Function creates instances of a model stored in DataModelRegistry virtual std::unique_ptr clone() const = 0; + + void setToolTipText(QString toolTipText); public: @@ -126,6 +128,8 @@ class NODE_EDITOR_PUBLIC NodeDataModel virtual NodePainterDelegate* painterDelegate() const { return nullptr; } + QString toolTipText(); + signals: void @@ -139,9 +143,14 @@ class NODE_EDITOR_PUBLIC NodeDataModel void computingFinished(); + + void setToolTipTextSignal(QString text); + private: + QString _toolTipText; + NodeStyle _nodeStyle; }; } diff --git a/src/NodeGraphicsObject.cpp b/src/NodeGraphicsObject.cpp index aeef3077d..cdeffea64 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -66,6 +66,10 @@ NodeGraphicsObject(FlowScene &scene, connect(this, &QGraphicsObject::xChanged, this, onMoveSlot); connect(this, &QGraphicsObject::yChanged, this, onMoveSlot); + + connect(_node.nodeDataModel(), &NodeDataModel::setToolTipTextSignal, this, [this](QString &toolTipText ){ + setToolTip(toolTipText); + }); } From 477d90457d4e706243f4f4e741f0c080f2b11cb4 Mon Sep 17 00:00:00 2001 From: ReubenJCarter Date: Thu, 22 Feb 2018 15:56:54 +0000 Subject: [PATCH 12/39] Made the signal and slot compatible for the setToolTip signal in the NodeDataModel. --- src/NodeGraphicsObject.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NodeGraphicsObject.cpp b/src/NodeGraphicsObject.cpp index cdeffea64..e87ab5002 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -67,7 +67,7 @@ NodeGraphicsObject(FlowScene &scene, connect(this, &QGraphicsObject::yChanged, this, onMoveSlot); - connect(_node.nodeDataModel(), &NodeDataModel::setToolTipTextSignal, this, [this](QString &toolTipText ){ + connect(_node.nodeDataModel(), &NodeDataModel::setToolTipTextSignal, this, [this](QString toolTipText ){ setToolTip(toolTipText); }); } From bc9c58faf484cf38f091d103b1f97f4709b904b9 Mon Sep 17 00:00:00 2001 From: jacquespillet Date: Thu, 17 Jan 2019 09:36:57 +0000 Subject: [PATCH 13/39] Added copy/paste between sheets --- .gitignore | 1 + include/nodes/internal/Connection.hpp | 3 +- include/nodes/internal/FlowScene.hpp | 6 ++ include/nodes/internal/FlowView.hpp | 6 ++ include/nodes/internal/Node.hpp | 11 ++++ src/Connection.cpp | 16 +++++ src/FlowScene.cpp | 47 +++++++++++++-- src/FlowView.cpp | 86 ++++++++++++++++++++++++++- src/Node.cpp | 35 +++++++++++ 9 files changed, 205 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index c9eb5941f..6c8dd3dc6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.py *.pyc CMakeLists.txt.user +/build \ No newline at end of file diff --git a/include/nodes/internal/Connection.hpp b/include/nodes/internal/Connection.hpp index a563c14ca..c0562d1b9 100644 --- a/include/nodes/internal/Connection.hpp +++ b/include/nodes/internal/Connection.hpp @@ -55,7 +55,8 @@ class NODE_EDITOR_PUBLIC Connection QJsonObject save() const override; - + + QJsonObject copyWithNewID(QUuid in, QUuid out); public: QUuid diff --git a/include/nodes/internal/FlowScene.hpp b/include/nodes/internal/FlowScene.hpp index 9f34ccdad..f6c7a1b4a 100644 --- a/include/nodes/internal/FlowScene.hpp +++ b/include/nodes/internal/FlowScene.hpp @@ -59,6 +59,10 @@ class NODE_EDITOR_PUBLIC FlowScene Node&restoreNode(QJsonObject const& nodeJson); + QUuid pasteNode(QJsonObject &json); + + void pasteConnection(QJsonObject const &connectionJson, QUuid newIn, QUuid newOut); + void removeNode(Node& node); DataModelRegistry®istry() const; @@ -94,6 +98,8 @@ class NODE_EDITOR_PUBLIC FlowScene QByteArray saveToMemory() const; + void saveToClipBoard(); + void loadFromMemory(const QByteArray& data); void Undo(); diff --git a/include/nodes/internal/FlowView.hpp b/include/nodes/internal/FlowView.hpp index 138bdeae2..fcf35ea54 100644 --- a/include/nodes/internal/FlowView.hpp +++ b/include/nodes/internal/FlowView.hpp @@ -37,6 +37,10 @@ public slots: void duplicateSelectedNode(); + void copySelectedNodes(); + + void pasteSelectedNodes(); + protected: void contextMenuEvent(QContextMenuEvent *event) override; @@ -64,6 +68,8 @@ public slots: QAction* _clearSelectionAction; QAction* _deleteSelectionAction; QAction* _duplicateSelectionAction; + QAction* _copymultiplenodes; + QAction* _pastemultiplenodes; QAction* _undoAction; QAction* _redoAction; diff --git a/include/nodes/internal/Node.hpp b/include/nodes/internal/Node.hpp index 793e8a854..b6aa05fbb 100644 --- a/include/nodes/internal/Node.hpp +++ b/include/nodes/internal/Node.hpp @@ -44,14 +44,25 @@ class NODE_EDITOR_PUBLIC Node QJsonObject save() const override; + + QJsonObject copyWithNewID(QUuid newId) const; + + void restore(QJsonObject const &json) override; + void + paste(QJsonObject const &json, QUuid ID); + + + public: QUuid id() const; + void setId(QUuid id); + void reactToPossibleConnection(PortType, NodeDataType, QPointF const & scenePoint); diff --git a/src/Connection.cpp b/src/Connection.cpp index 194b6cac2..93e18c1fa 100644 --- a/src/Connection.cpp +++ b/src/Connection.cpp @@ -96,6 +96,22 @@ save() const } +QJsonObject Connection::copyWithNewID(QUuid in, QUuid out) { + QJsonObject connectionJson; + + if (_inNode && _outNode) + { + connectionJson["in_id"] = in.toString(); + connectionJson["in_index"] = _inPortIndex; + + connectionJson["out_id"] = out.toString(); + connectionJson["out_index"] = _outPortIndex; + } + + return connectionJson; +} + + QUuid Connection:: id() const diff --git a/src/FlowScene.cpp b/src/FlowScene.cpp index 195df72da..609c4f797 100644 --- a/src/FlowScene.cpp +++ b/src/FlowScene.cpp @@ -132,6 +132,20 @@ restoreConnection(QJsonObject const &connectionJson) return createConnection(*nodeIn, portIndexIn, *nodeOut, portIndexOut); } +void FlowScene::pasteConnection(QJsonObject const &connectionJson, QUuid newIn, QUuid newOut) +{ + QUuid nodeInId = QUuid(connectionJson["in_id"].toString()); + QUuid nodeOutId = QUuid(connectionJson["out_id"].toString()); + + PortIndex portIndexIn = connectionJson["in_index"].toInt(); + PortIndex portIndexOut = connectionJson["out_index"].toInt(); + + auto nodeIn = _nodes[newIn].get(); + auto nodeOut = _nodes[newOut].get(); + + createConnection(*nodeIn, portIndexIn, *nodeOut, portIndexOut); +} + void FlowScene:: @@ -166,26 +180,46 @@ FlowScene:: restoreNode(QJsonObject const& nodeJson) { QString modelName = nodeJson["model"].toObject()["name"].toString(); - auto dataModel = registry().create(modelName); if (!dataModel) throw std::logic_error(std::string("No registered model with name ") + modelName.toLocal8Bit().data()); - auto node = std::make_unique(std::move(dataModel)); auto ngo = std::make_unique(*this, *node); node->setGraphicsObject(std::move(ngo)); - node->restore(nodeJson); auto nodePtr = node.get(); _nodes[node->id()] = std::move(node); - nodeCreated(*nodePtr); return *nodePtr; } +QUuid FlowScene::pasteNode(QJsonObject &nodeJson) { + QString modelName = nodeJson["model"].toObject()["name"].toString(); + auto dataModel = registry().create(modelName); + + if (!dataModel) + throw std::logic_error(std::string("No registered model with name ") + + modelName.toLocal8Bit().data()); + + auto node = std::make_unique(std::move(dataModel)); + auto ngo = std::make_unique(*this, *node); + + + node->setGraphicsObject(std::move(ngo)); + + QUuid newId = QUuid::createUuid(); + node->paste(nodeJson, newId); + + auto nodePtr = node.get(); + _nodes[node->id()] = std::move(node); + nodeCreated(*nodePtr); + return newId; +} + + void FlowScene:: @@ -501,6 +535,11 @@ saveToMemory() const } +void FlowScene::saveToClipBoard() +{ + +} + void FlowScene:: loadFromMemory(const QByteArray& data) diff --git a/src/FlowView.cpp b/src/FlowView.cpp index 36af90770..26977d47c 100644 --- a/src/FlowView.cpp +++ b/src/FlowView.cpp @@ -1,7 +1,7 @@ #include "FlowView.hpp" #include - +#include #include #include #include @@ -106,6 +106,18 @@ FlowView::setScene(FlowScene *scene) _duplicateSelectionAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); connect(_duplicateSelectionAction, &QAction::triggered, this, &FlowView::duplicateSelectedNode); addAction(_duplicateSelectionAction); + + _copymultiplenodes = new QAction(QStringLiteral("Copy Multiple Nodes"), this); + _copymultiplenodes->setShortcut(QKeySequence(tr("Ctrl+C"))); + _copymultiplenodes->setShortcutContext(Qt::WidgetWithChildrenShortcut); + connect(_copymultiplenodes, &QAction::triggered, this, &FlowView::copySelectedNodes); + addAction(_copymultiplenodes); + + _pastemultiplenodes = new QAction(QStringLiteral("Paste Multiple Nodes"), this); + _pastemultiplenodes->setShortcut(QKeySequence(tr("Ctrl+V"))); + _pastemultiplenodes->setShortcutContext(Qt::WidgetWithChildrenShortcut); + connect(_pastemultiplenodes, &QAction::triggered, this, &FlowView::pasteSelectedNodes); + addAction(_pastemultiplenodes); _undoAction = new QAction(QStringLiteral("Undo"), this); _undoAction->setShortcut(QKeySequence(tr("Ctrl+Z"))); @@ -309,6 +321,78 @@ deleteSelectedNodes() _scene->UpdateHistory(); } +void FlowView::copySelectedNodes() { + QJsonObject sceneJson; + QJsonArray nodesJsonArray; + std::vector addedIds; + + for (QGraphicsItem * item : _scene->selectedItems()) + { + if (auto n = qgraphicsitem_cast(item)) + { + Node& node = n->node(); + nodesJsonArray.append(node.save()); + addedIds.push_back(node.id()); + } + } + + QJsonArray connectionJsonArray; + for (QGraphicsItem * item : _scene->selectedItems()) + { + if (auto c = qgraphicsitem_cast(item)) { + Connection& connection = c->connection(); + + if( std::find(addedIds.begin(), addedIds.end(), connection.getNode(PortType::In)->id()) != addedIds.end() && + std::find(addedIds.begin(), addedIds.end(), connection.getNode(PortType::Out)->id()) != addedIds.end() ) { + QJsonObject connectionJson = connection.save(); + + if (!connectionJson.isEmpty()) + connectionJsonArray.append(connectionJson); + } + } + } + + sceneJson["nodes"] = nodesJsonArray; + sceneJson["connections"] = connectionJsonArray; + + QJsonDocument document(sceneJson); + std::string json = document.toJson().toStdString(); + + QClipboard *p_Clipboard = QApplication::clipboard(); + p_Clipboard->setText( QString::fromStdString(json)); +} + +void FlowView::pasteSelectedNodes() { + QClipboard *p_Clipboard = QApplication::clipboard(); + QByteArray text = p_Clipboard->text().toUtf8(); + + QJsonObject const jsonDocument = QJsonDocument::fromJson(text).object(); + QJsonArray nodesJsonArray = jsonDocument["nodes"].toArray(); + + std::map addedIds; + + for (int i = 0; i < nodesJsonArray.size(); ++i) + { + QUuid currentId = QUuid( nodesJsonArray[i].toObject()["id"].toString() ); + QUuid newId = _scene->pasteNode(nodesJsonArray[i].toObject()); + + addedIds.insert(std::pair(currentId, newId)); + } + + QJsonArray connectionJsonArray = jsonDocument["connections"].toArray(); + for (int i = 0; i < connectionJsonArray.size(); ++i) + { + QUuid in = QUuid(connectionJsonArray[i].toObject()["in_id"].toString()); + QUuid newIn = addedIds[in]; + + QUuid out = QUuid(connectionJsonArray[i].toObject()["out_id"].toString()); + QUuid newOut = addedIds[out]; + + _scene->pasteConnection(connectionJsonArray[i].toObject(), newIn, newOut ); + } + + _scene->UpdateHistory(); +} void FlowView::duplicateSelectedNode() { diff --git a/src/Node.cpp b/src/Node.cpp index 43d22ec53..1984f66b5 100644 --- a/src/Node.cpp +++ b/src/Node.cpp @@ -60,6 +60,22 @@ save() const return nodeJson; } +QJsonObject Node::copyWithNewID(QUuid newId) const +{ + QJsonObject nodeJson; + + nodeJson["id"] = newId.toString(); + nodeJson["model"] = _nodeDataModel->save(); + + QJsonObject obj; + obj["x"] = _nodeGraphicsObject->pos().x(); + obj["y"] = _nodeGraphicsObject->pos().y(); + nodeJson["position"] = obj; + + return nodeJson; +} + + void Node:: @@ -75,6 +91,20 @@ restore(QJsonObject const& json) _nodeDataModel->restore(json["model"].toObject()); } +void +Node:: +paste(QJsonObject const& json, QUuid ID) +{ + _id = ID; + + QJsonObject positionJson = json["position"].toObject(); + QPointF point(positionJson["x"].toDouble(), + positionJson["y"].toDouble()); + _nodeGraphicsObject->setPos(point); + + _nodeDataModel->restore(json["model"].toObject()); +} + QUuid Node:: @@ -83,6 +113,11 @@ id() const return _id; } +void Node::setId(QUuid id) { + this->_id = id; +} + + void Node:: From a0adb17e032b11c11c05154782813621b412fe83 Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Fri, 1 Nov 2019 09:58:24 +0000 Subject: [PATCH 14/39] Started groups, added anchors, fixed position of nodes when pasting --- CMakeLists.txt | 2 + include/nodes/internal/FlowScene.hpp | 17 +++- include/nodes/internal/FlowView.hpp | 8 ++ include/nodes/internal/Group.hpp | 54 +++++++++++ .../nodes/internal/GroupGraphicsObject.hpp | 28 ++++++ include/nodes/internal/Node.hpp | 1 + src/FlowScene.cpp | 46 ++++++++-- src/FlowView.cpp | 90 ++++++++++++++++++- src/Group.cpp | 0 src/GroupGraphicsObject.cpp | 0 10 files changed, 237 insertions(+), 9 deletions(-) create mode 100644 include/nodes/internal/Group.hpp create mode 100644 include/nodes/internal/GroupGraphicsObject.hpp create mode 100644 src/Group.cpp create mode 100644 src/GroupGraphicsObject.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 23328dbbb..71390c589 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,8 @@ set(CPP_SOURCE_FILES src/FlowView.cpp src/FlowViewStyle.cpp src/Node.cpp + src/Group.cpp + src/GroupGraphicsObject.cpp src/NodeConnectionInteraction.cpp src/NodeDataModel.cpp src/NodeGeometry.cpp diff --git a/include/nodes/internal/FlowScene.hpp b/include/nodes/internal/FlowScene.hpp index f6c7a1b4a..fe109ecbc 100644 --- a/include/nodes/internal/FlowScene.hpp +++ b/include/nodes/internal/FlowScene.hpp @@ -28,6 +28,12 @@ struct SceneHistory QByteArray data; }; + +struct Anchor { + QPointF position; + double scale; +}; + /// Scene holds connections and nodes. class NODE_EDITOR_PUBLIC FlowScene : public QGraphicsScene @@ -57,9 +63,11 @@ class NODE_EDITOR_PUBLIC FlowScene Node&createNode(std::unique_ptr && dataModel); + void createGroup(); + Node&restoreNode(QJsonObject const& nodeJson); - QUuid pasteNode(QJsonObject &json); + QUuid pasteNode(QJsonObject &json, QPointF nodeGroupCentroid, QPointF mousePos); void pasteConnection(QJsonObject const &connectionJson, QUuid newIn, QUuid newOut); @@ -135,6 +143,10 @@ class NODE_EDITOR_PUBLIC FlowScene void nodeContextMenu(Node& n, const QPointF& pos); +public: + + std::vector anchors; + private: using SharedConnection = std::shared_ptr; @@ -147,6 +159,9 @@ class NODE_EDITOR_PUBLIC FlowScene int historyInx; bool writeToHistory; std::vector< SceneHistory > history; + + +private: }; Node* diff --git a/include/nodes/internal/FlowView.hpp b/include/nodes/internal/FlowView.hpp index fcf35ea54..9db7606a7 100644 --- a/include/nodes/internal/FlowView.hpp +++ b/include/nodes/internal/FlowView.hpp @@ -41,6 +41,8 @@ public slots: void pasteSelectedNodes(); + void createGroup(); + protected: void contextMenuEvent(QContextMenuEvent *event) override; @@ -59,6 +61,9 @@ public slots: void showEvent(QShowEvent *event) override; + void addAnchor(int index); + void goToAnchor(int index); + protected: FlowScene * scene(); @@ -70,9 +75,12 @@ public slots: QAction* _duplicateSelectionAction; QAction* _copymultiplenodes; QAction* _pastemultiplenodes; + QAction* _createGroup; QAction* _undoAction; QAction* _redoAction; + std::vector anchorActions; + QPointF _clickPos; FlowScene* _scene; diff --git a/include/nodes/internal/Group.hpp b/include/nodes/internal/Group.hpp new file mode 100644 index 000000000..d1fab312c --- /dev/null +++ b/include/nodes/internal/Group.hpp @@ -0,0 +1,54 @@ + +#pragma once + +#include + +#include +#include + +#include + +#include "PortType.hpp" + +#include "Export.hpp" +#include "NodeState.hpp" +#include "NodeGeometry.hpp" +#include "NodeData.hpp" +#include "NodeGraphicsObject.hpp" +#include "GroupGraphicsObject.hpp" +#include "ConnectionGraphicsObject.hpp" +#include "Serializable.hpp" + +namespace QtNodes +{ + +class NODE_EDITOR_PUBLIC Group + : public QObject + , public Serializable +{ + Q_OBJECT +public: + + Group(FlowScene &scene): _scene(scene){ + // GroupGraphicsObject groupGraphicsObject; + } + + + virtual + ~Group(){}; + +public: + + QJsonObject save() const override{ + QJsonObject object; + return object; + }; + + void restore(QJsonObject const &json) override{}; + +private: + FlowScene & _scene; + std::unique_ptr _groupGraphicsObject; + +}; +} \ No newline at end of file diff --git a/include/nodes/internal/GroupGraphicsObject.hpp b/include/nodes/internal/GroupGraphicsObject.hpp new file mode 100644 index 000000000..fb49f4f72 --- /dev/null +++ b/include/nodes/internal/GroupGraphicsObject.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +#include "Connection.hpp" + +#include "NodeGeometry.hpp" +#include "NodeState.hpp" + +class QGraphicsProxyWidget; + +namespace QtNodes +{ + +class FlowScene; +class FlowItemEntry; + +/// Class reacts on GUI events, mouse clicks and +/// forwards painting operation. +class GroupGraphicsObject : public QGraphicsObject +{ + Q_OBJECT + +public: + GroupGraphicsObject(){} +}; +} \ No newline at end of file diff --git a/include/nodes/internal/Node.hpp b/include/nodes/internal/Node.hpp index b6aa05fbb..78bdc5017 100644 --- a/include/nodes/internal/Node.hpp +++ b/include/nodes/internal/Node.hpp @@ -36,6 +36,7 @@ class NODE_EDITOR_PUBLIC Node /// NodeDataModel should be an rvalue and is moved into the Node Node(std::unique_ptr && dataModel); + virtual ~Node(); diff --git a/src/FlowScene.cpp b/src/FlowScene.cpp index 609c4f797..8b4d005c2 100644 --- a/src/FlowScene.cpp +++ b/src/FlowScene.cpp @@ -17,6 +17,7 @@ #include #include "Node.hpp" +#include "Group.hpp" #include "NodeGraphicsObject.hpp" #include "NodeGraphicsObject.hpp" @@ -51,6 +52,8 @@ FlowScene(std::shared_ptr registry) UpdateHistory(); }; connect(this, &FlowScene::nodeMoveFinished, this, UpdateLamda); + + anchors.resize(10); } @@ -174,13 +177,17 @@ createNode(std::unique_ptr && dataModel) return *nodePtr; } +void FlowScene::createGroup() { + Group group(*this); +} + Node& FlowScene:: restoreNode(QJsonObject const& nodeJson) { QString modelName = nodeJson["model"].toObject()["name"].toString(); - auto dataModel = registry().create(modelName); + auto dataModel = registry().create(modelName); //This is where it looks for the node by name if (!dataModel) throw std::logic_error(std::string("No registered model with name ") + @@ -196,7 +203,7 @@ restoreNode(QJsonObject const& nodeJson) return *nodePtr; } -QUuid FlowScene::pasteNode(QJsonObject &nodeJson) { +QUuid FlowScene::pasteNode(QJsonObject &nodeJson, QPointF nodeGroupCentroid, QPointF mousePos) { QString modelName = nodeJson["model"].toObject()["name"].toString(); auto dataModel = registry().create(modelName); @@ -207,12 +214,18 @@ QUuid FlowScene::pasteNode(QJsonObject &nodeJson) { auto node = std::make_unique(std::move(dataModel)); auto ngo = std::make_unique(*this, *node); - node->setGraphicsObject(std::move(ngo)); + QUuid newId = QUuid::createUuid(); node->paste(nodeJson, newId); + QPointF offset = node->nodeGraphicsObject().pos() - nodeGroupCentroid; + + + QPointF pos = mousePos + (node->nodeGraphicsObject().pos() - nodeGroupCentroid); + node->nodeGraphicsObject().setPos(pos); + auto nodePtr = node.get(); _nodes[node->id()] = std::move(node); nodeCreated(*nodePtr); @@ -529,6 +542,17 @@ saveToMemory() const sceneJson["connections"] = connectionJsonArray; + QJsonArray anchorsJsonArray; + for(int i=0; ianchors[index] = a; +} + +void +FlowView::goToAnchor(int index) +{ + qreal x1, y1, x2, y2; + sceneRect().getCoords(&x1, &y1, &x2, &y2); + QPointF currentPosition = QPointF((x2 + x1) * 0.5, (y1 + y2) * 0.5); + + QPointF difference = _scene->anchors[index].position - currentPosition; + + setSceneRect(sceneRect().translated(difference.x(), difference.y())); +} void FlowView::setScene(FlowScene *scene) @@ -87,6 +111,7 @@ FlowView::setScene(FlowScene *scene) _scene = scene; QGraphicsView::setScene(_scene); + // setup actions delete _clearSelectionAction; _clearSelectionAction = new QAction(QStringLiteral("Clear Selection"), this); @@ -118,7 +143,13 @@ FlowView::setScene(FlowScene *scene) _pastemultiplenodes->setShortcutContext(Qt::WidgetWithChildrenShortcut); connect(_pastemultiplenodes, &QAction::triggered, this, &FlowView::pasteSelectedNodes); addAction(_pastemultiplenodes); - + + _createGroup = new QAction(QStringLiteral("Create Node Group"), this); + _createGroup->setShortcut(QKeySequence(tr("Ctrl+G"))); + _createGroup->setShortcutContext(Qt::WidgetWithChildrenShortcut); + connect(_createGroup, &QAction::triggered, this, &FlowView::createGroup); + addAction(_createGroup); + _undoAction = new QAction(QStringLiteral("Undo"), this); _undoAction->setShortcut(QKeySequence(tr("Ctrl+Z"))); _undoAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); @@ -130,8 +161,28 @@ FlowView::setScene(FlowScene *scene) _redoAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); connect(_redoAction, &QAction::triggered, _scene, &FlowScene::Redo); addAction(_redoAction); -} + for(int i=0; i<10; i++) { + QAction* _addAnchor = new QAction(QStringLiteral("Add Anchor"), this); + QString sequenceString = QString("Ctrl+") + QString::number(i); + _addAnchor->setShortcut(QKeySequence(sequenceString)); + _addAnchor->setShortcutContext(Qt::WidgetWithChildrenShortcut ); + connect(_addAnchor, &QAction::triggered, _scene, [this, i]() { + addAnchor(i); + }); + addAction(_addAnchor); + anchorActions.push_back(_addAnchor); + + QAction* _goToAnchor = new QAction(QStringLiteral("Go to Anchor"), this); + _goToAnchor->setShortcut(QKeySequence(tr(std::to_string(i).c_str()))); + _goToAnchor->setShortcutContext(Qt::WidgetWithChildrenShortcut); + connect(_goToAnchor, &QAction::triggered, _scene, [this, i]() { + goToAnchor(i); + }); + addAction(_goToAnchor); + anchorActions.push_back(_goToAnchor); + } +} void FlowView:: @@ -222,12 +273,14 @@ contextMenuEvent(QContextMenuEvent *event) { for (auto& topLvlItem : topLevelItems) { + bool shouldHideCategory = true; for (int i = 0; i < topLvlItem->childCount(); ++i) { auto child = topLvlItem->child(i); auto modelName = child->data(0, Qt::UserRole).toString(); if (modelName.contains(text, Qt::CaseInsensitive)) { + shouldHideCategory = false; child->setHidden(false); } else @@ -235,6 +288,7 @@ contextMenuEvent(QContextMenuEvent *event) child->setHidden(true); } } + topLvlItem->setHidden(shouldHideCategory); } }); @@ -371,10 +425,35 @@ void FlowView::pasteSelectedNodes() { std::map addedIds; + //Get Bounds of all the selected items + float minx = 10000000000; + float miny = 10000000000; + float maxx = -1000000000; + float maxy = -1000000000; + for (int i = 0; i < nodesJsonArray.size(); ++i) + { + QJsonObject nodeJsonObject = nodesJsonArray[i].toObject(); + QJsonObject positionJson = nodeJsonObject["position"].toObject(); + QPointF pos(positionJson["x"].toDouble(), positionJson["y"].toDouble()); + + if(pos.x() < minx) minx = pos.x(); + if(pos.y() < miny) miny = pos.y(); + if(pos.x() > maxx) maxx = pos.x(); + if(pos.y() > maxy) maxy = pos.y(); + } + + float centroidX = (maxx - minx) / 2.0 + minx; + float centroidY = (maxy - miny) / 2.0 + miny; + QPointF centroid(centroidX, centroidY); + + + QPoint viewPointMouse = this->mapFromGlobal(QCursor::pos()); + QPointF posViewMouse = this->mapToScene(viewPointMouse); + for (int i = 0; i < nodesJsonArray.size(); ++i) { QUuid currentId = QUuid( nodesJsonArray[i].toObject()["id"].toString() ); - QUuid newId = _scene->pasteNode(nodesJsonArray[i].toObject()); + QUuid newId = _scene->pasteNode(nodesJsonArray[i].toObject(), centroid, posViewMouse); addedIds.insert(std::pair(currentId, newId)); } @@ -394,6 +473,11 @@ void FlowView::pasteSelectedNodes() { _scene->UpdateHistory(); } +void FlowView::createGroup() { + _scene->createGroup(); +} + + void FlowView::duplicateSelectedNode() { //Get Bounds of all the selected items diff --git a/src/Group.cpp b/src/Group.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/src/GroupGraphicsObject.cpp b/src/GroupGraphicsObject.cpp new file mode 100644 index 000000000..e69de29bb From 02c5c7d76c0172a0b8b1281d973930357a27cbc7 Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Wed, 6 Nov 2019 16:06:57 +0000 Subject: [PATCH 15/39] Added getter for history index, added resizing nodes --- include/nodes/internal/FlowScene.hpp | 2 ++ include/nodes/internal/NodeDataModel.hpp | 2 +- src/FlowScene.cpp | 5 +++++ src/NodeGeometry.cpp | 4 ++-- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/include/nodes/internal/FlowScene.hpp b/include/nodes/internal/FlowScene.hpp index fe109ecbc..a115ac65d 100644 --- a/include/nodes/internal/FlowScene.hpp +++ b/include/nodes/internal/FlowScene.hpp @@ -118,6 +118,8 @@ class NODE_EDITOR_PUBLIC FlowScene void ResetHistory(); + int GetHistoryIndex(); + signals: void nodeCreated(Node &n); diff --git a/include/nodes/internal/NodeDataModel.hpp b/include/nodes/internal/NodeDataModel.hpp index cedd6c5ba..a48298b00 100644 --- a/include/nodes/internal/NodeDataModel.hpp +++ b/include/nodes/internal/NodeDataModel.hpp @@ -115,7 +115,7 @@ class NODE_EDITOR_PUBLIC NodeDataModel virtual bool - resizable() const { return false; } + resizable() const { return true; } virtual NodeValidationState diff --git a/src/FlowScene.cpp b/src/FlowScene.cpp index 8b4d005c2..ce074e77b 100644 --- a/src/FlowScene.cpp +++ b/src/FlowScene.cpp @@ -653,6 +653,7 @@ void FlowScene::UpdateHistory() } } + void FlowScene::ResetHistory() { historyInx = 0; @@ -660,6 +661,10 @@ void FlowScene::ResetHistory() history.clear(); } +int FlowScene::GetHistoryIndex() { + return historyInx; +} + diff --git a/src/NodeGeometry.cpp b/src/NodeGeometry.cpp index 8309abf9b..d120e1c99 100644 --- a/src/NodeGeometry.cpp +++ b/src/NodeGeometry.cpp @@ -217,8 +217,8 @@ resizeRect() const { unsigned int rectSize = 7; - return QRect(_width - rectSize, - _height - rectSize, + return QRect(_width + rectSize, + _height + rectSize, rectSize, rectSize); } From ddbe4a805ec826631565d601528bd4bdb0b08041 Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Mon, 6 Jan 2020 13:57:42 +0000 Subject: [PATCH 16/39] Started groups --- include/nodes/internal/FlowScene.hpp | 10 +- include/nodes/internal/Group.hpp | 19 ++- .../nodes/internal/GroupGraphicsObject.hpp | 58 ++++++- include/nodes/internal/NodeDataModel.hpp | 5 +- src/FlowScene.cpp | 22 ++- src/FlowView.cpp | 28 +-- src/Group.cpp | 10 ++ src/GroupGraphicsObject.cpp | 159 ++++++++++++++++++ src/NodeGeometry.cpp | 3 +- 9 files changed, 283 insertions(+), 31 deletions(-) diff --git a/include/nodes/internal/FlowScene.hpp b/include/nodes/internal/FlowScene.hpp index a115ac65d..c7764c63c 100644 --- a/include/nodes/internal/FlowScene.hpp +++ b/include/nodes/internal/FlowScene.hpp @@ -18,6 +18,7 @@ namespace QtNodes class NodeDataModel; class FlowItemInterface; class Node; +class Group; class NodeGraphicsObject; class Connection; class ConnectionGraphicsObject; @@ -90,11 +91,11 @@ class NODE_EDITOR_PUBLIC FlowScene QSizeF getNodeSize(const Node& node) const; public: - std::unordered_map > const &nodes() const; + std::unordered_map > const &nodes() const; std::unordered_map > const &connections() const; - std::vectorselectedNodes() const; + std::vector>selectedNodes() const; public: @@ -152,12 +153,13 @@ class NODE_EDITOR_PUBLIC FlowScene private: using SharedConnection = std::shared_ptr; - using UniqueNode = std::unique_ptr; + using UniqueNode = std::shared_ptr; std::unordered_map _connections; std::unordered_map _nodes; std::shared_ptr _registry; - + std::unordered_map> _groups; + int historyInx; bool writeToHistory; std::vector< SceneHistory > history; diff --git a/include/nodes/internal/Group.hpp b/include/nodes/internal/Group.hpp index d1fab312c..5627ac61d 100644 --- a/include/nodes/internal/Group.hpp +++ b/include/nodes/internal/Group.hpp @@ -18,7 +18,7 @@ #include "GroupGraphicsObject.hpp" #include "ConnectionGraphicsObject.hpp" #include "Serializable.hpp" - +#include "Node.hpp" namespace QtNodes { @@ -29,8 +29,14 @@ class NODE_EDITOR_PUBLIC Group Q_OBJECT public: - Group(FlowScene &scene): _scene(scene){ - // GroupGraphicsObject groupGraphicsObject; + Group(FlowScene &scene): _scene(scene), + _id(QUuid::createUuid()) + { + _groupGraphicsObject = std::make_unique(scene); + } + + void AddNode(std::shared_ptr node) { + nodes.push_back(node); } @@ -39,6 +45,10 @@ class NODE_EDITOR_PUBLIC Group public: + + QUuid + id() const; + QJsonObject save() const override{ QJsonObject object; return object; @@ -47,8 +57,11 @@ class NODE_EDITOR_PUBLIC Group void restore(QJsonObject const &json) override{}; private: + FlowScene & _scene; std::unique_ptr _groupGraphicsObject; + std::vector> nodes; + QUuid _id; }; } \ No newline at end of file diff --git a/include/nodes/internal/GroupGraphicsObject.hpp b/include/nodes/internal/GroupGraphicsObject.hpp index fb49f4f72..cef9d0937 100644 --- a/include/nodes/internal/GroupGraphicsObject.hpp +++ b/include/nodes/internal/GroupGraphicsObject.hpp @@ -23,6 +23,60 @@ class GroupGraphicsObject : public QGraphicsObject Q_OBJECT public: - GroupGraphicsObject(){} + GroupGraphicsObject(FlowScene &scene); + + virtual + ~GroupGraphicsObject(); + + QRectF + boundingRect() const override; + + void + setGeometryChanged(); + + enum { Type = UserType + 1 }; + + int + type() const override { return Type; } + + void + lock(bool locked); + +protected: + void + paint(QPainter* painter, + QStyleOptionGraphicsItem const* option, + QWidget* widget = 0) override; + + QVariant + itemChange(GraphicsItemChange change, const QVariant &value) override; + + void + mousePressEvent(QGraphicsSceneMouseEvent* event) override; + + void + mouseMoveEvent(QGraphicsSceneMouseEvent* event) override; + + void + mouseReleaseEvent(QGraphicsSceneMouseEvent* event) override; + + void + hoverEnterEvent(QGraphicsSceneHoverEvent* event) override; + + void + hoverLeaveEvent(QGraphicsSceneHoverEvent* event) override; + + void + hoverMoveEvent(QGraphicsSceneHoverEvent *) override; + + void + mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) override; + + void + contextMenuEvent(QGraphicsSceneContextMenuEvent* event) override; + + +private: + FlowScene & _scene; }; -} \ No newline at end of file +} diff --git a/include/nodes/internal/NodeDataModel.hpp b/include/nodes/internal/NodeDataModel.hpp index a48298b00..16fc55ac9 100644 --- a/include/nodes/internal/NodeDataModel.hpp +++ b/include/nodes/internal/NodeDataModel.hpp @@ -115,7 +115,10 @@ class NODE_EDITOR_PUBLIC NodeDataModel virtual bool - resizable() const { return true; } + resizable() const { + + return true; + } virtual NodeValidationState diff --git a/src/FlowScene.cpp b/src/FlowScene.cpp index ce074e77b..98b5012d2 100644 --- a/src/FlowScene.cpp +++ b/src/FlowScene.cpp @@ -178,7 +178,15 @@ createNode(std::unique_ptr && dataModel) } void FlowScene::createGroup() { - Group group(*this); + + std::vector> selection = selectedNodes(); + auto group = std::make_unique(*this); + + for(int i=0; iAddNode(selection[i]); + } + + _groups[group->id()] = std::move(group); } @@ -191,7 +199,7 @@ restoreNode(QJsonObject const& nodeJson) if (!dataModel) throw std::logic_error(std::string("No registered model with name ") + - modelName.toLocal8Bit().data()); + modelName.toLocal8Bit().data()); auto node = std::make_unique(std::move(dataModel)); auto ngo = std::make_unique(*this, *node); node->setGraphicsObject(std::move(ngo)); @@ -399,7 +407,7 @@ getNodeSize(const Node& node) const } -std::unordered_map > const & +std::unordered_map > const & FlowScene:: nodes() const { @@ -415,13 +423,13 @@ connections() const } -std::vector +std::vector> FlowScene:: selectedNodes() const { QList graphicsItems = selectedItems(); - std::vector ret; + std::vector> ret; ret.reserve(graphicsItems.size()); for (QGraphicsItem* item : graphicsItems) @@ -430,7 +438,9 @@ selectedNodes() const if (ngo != nullptr) { - ret.push_back(&ngo->node()); + Node* n = &(ngo->node()); + std::shared_ptr ptr(n); + ret.push_back(ptr); } } diff --git a/src/FlowView.cpp b/src/FlowView.cpp index 163eeb149..3ffff3f96 100644 --- a/src/FlowView.cpp +++ b/src/FlowView.cpp @@ -353,24 +353,24 @@ deleteSelectedNodes() { //std::cout << "deleteSelectedNodes" << std::endl; // delete the nodes, this will delete many of the connections - std::vector nodeItems = _scene->selectedNodes(); + std::vector> nodeItems = _scene->selectedNodes(); for(int i = 0; i < nodeItems.size(); i++) { - Node* item = nodeItems[i]; - if(item) - { - _scene->removeNode(*item); - } + Node* item = nodeItems[i].get(); + if(item) + { + _scene->removeNode(*item); + } } - for (QGraphicsItem * item : _scene->selectedItems()) - { - if(item) - { - if (auto c = qgraphicsitem_cast(item)) - _scene->deleteConnection(c->connection()); - } - } + // for (QGraphicsItem * item : _scene->selectedItems()) + // { + // if(item) + // { + // if (auto c = qgraphicsitem_cast(item)) + // _scene->deleteConnection(c->connection()); + // } + // } _scene->UpdateHistory(); } diff --git a/src/Group.cpp b/src/Group.cpp index e69de29bb..c5cb605ff 100644 --- a/src/Group.cpp +++ b/src/Group.cpp @@ -0,0 +1,10 @@ +#include "Group.hpp" + +using QtNodes::Group; + +QUuid +Group:: +id() const +{ + return _id; +} \ No newline at end of file diff --git a/src/GroupGraphicsObject.cpp b/src/GroupGraphicsObject.cpp index e69de29bb..3dc2737cb 100644 --- a/src/GroupGraphicsObject.cpp +++ b/src/GroupGraphicsObject.cpp @@ -0,0 +1,159 @@ +#include "GroupGraphicsObject.hpp" + +#include +#include + +#include +#include + +#include "ConnectionGraphicsObject.hpp" +#include "ConnectionState.hpp" + +#include "FlowScene.hpp" +#include "NodePainter.hpp" + +#include "Node.hpp" +#include "NodeDataModel.hpp" +#include "NodeConnectionInteraction.hpp" + +#include "StyleCollection.hpp" + +using QtNodes::GroupGraphicsObject; +using QtNodes::Node; +using QtNodes::FlowScene; + +GroupGraphicsObject:: +GroupGraphicsObject(FlowScene &scene) + : _scene(scene) +{ + _scene.addItem(this); + + setFlag(QGraphicsItem::ItemDoesntPropagateOpacityToChildren, true); + setFlag(QGraphicsItem::ItemIsMovable, true); + setFlag(QGraphicsItem::ItemIsFocusable, true); + setFlag(QGraphicsItem::ItemIsSelectable, true); + setFlag(QGraphicsItem::ItemSendsScenePositionChanges, true); + + setCacheMode( QGraphicsItem::DeviceCoordinateCache ); + + setAcceptHoverEvents(true); + setAcceptTouchEvents(true); + + setZValue(0); +} + + +GroupGraphicsObject:: +~GroupGraphicsObject() +{ + _scene.removeItem(this); +} + + +QRectF +GroupGraphicsObject:: +boundingRect() const +{ + return QRectF(0, 0, 1000, 1000); +} + + +void +GroupGraphicsObject:: +setGeometryChanged() +{ + prepareGeometryChange(); +} + + + +void +GroupGraphicsObject:: +paint(QPainter * painter, + QStyleOptionGraphicsItem const* option, + QWidget* ) +{ + painter->setClipRect(option->exposedRect); + QRect rect(0, 0, 1000, 1000); + auto color = QColor(255, 0, 0, 255); + painter->drawRect(rect); + painter->fillRect(rect, QBrush(color)); +} + + +QVariant +GroupGraphicsObject:: +itemChange(GraphicsItemChange change, const QVariant &value) +{ + return QGraphicsItem::itemChange(change, value); +} + + +void +GroupGraphicsObject:: +mousePressEvent(QGraphicsSceneMouseEvent * event) +{ +} + + +void +GroupGraphicsObject:: +mouseMoveEvent(QGraphicsSceneMouseEvent * event) +{ + // setPos(mapFromScene(event->scenePos())); + QGraphicsObject::mouseMoveEvent(event); + // std::cout << scene()->scale() << std::endl; + // event->ignore(); + // //DO SNAPPING HERE + // QRectF r = scene()->sceneRect(); + // r = r.united(mapToScene(boundingRect()).boundingRect()); + // scene()->setSceneRect(r); +} + + +void +GroupGraphicsObject:: +mouseReleaseEvent(QGraphicsSceneMouseEvent* event) +{ + QGraphicsObject::mouseReleaseEvent(event); +} + + +void +GroupGraphicsObject:: +hoverEnterEvent(QGraphicsSceneHoverEvent * event) +{ + update(); + event->accept(); +} + + +void +GroupGraphicsObject:: +hoverLeaveEvent(QGraphicsSceneHoverEvent * event) +{ + update(); + event->accept(); +} + + +void +GroupGraphicsObject:: +hoverMoveEvent(QGraphicsSceneHoverEvent * event) +{ + event->accept(); +} + + +void +GroupGraphicsObject:: +mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) +{ + QGraphicsItem::mouseDoubleClickEvent(event); +} + +void +GroupGraphicsObject:: +contextMenuEvent(QGraphicsSceneContextMenuEvent* event) +{ +} diff --git a/src/NodeGeometry.cpp b/src/NodeGeometry.cpp index d120e1c99..0c8d5318f 100644 --- a/src/NodeGeometry.cpp +++ b/src/NodeGeometry.cpp @@ -58,7 +58,8 @@ boundingRect() const { auto const &nodeStyle = StyleCollection::nodeStyle(); - double addon = 4 * nodeStyle.ConnectionPointDiameter; + double addon = nodeStyle.ConnectionPointDiameter * 1.6; + // double addon = 0; return QRectF(0 - addon, 0 - addon, From 4c2812ff6e5a425889c46ce206fbfed8e2f80e1f Mon Sep 17 00:00:00 2001 From: jacquespillet Date: Wed, 7 Apr 2021 10:30:59 +0100 Subject: [PATCH 17/39] continued groups --- include/nodes/internal/FlowScene.hpp | 18 +- include/nodes/internal/Group.hpp | 65 ++++++- .../nodes/internal/GroupGraphicsObject.hpp | 26 ++- src/FlowScene.cpp | 160 ++++++++++++++++-- src/FlowView.cpp | 39 +++-- src/Group.cpp | 21 ++- src/GroupGraphicsObject.cpp | 151 +++++++++++++++-- 7 files changed, 423 insertions(+), 57 deletions(-) diff --git a/include/nodes/internal/FlowScene.hpp b/include/nodes/internal/FlowScene.hpp index c7764c63c..fa9e8119a 100644 --- a/include/nodes/internal/FlowScene.hpp +++ b/include/nodes/internal/FlowScene.hpp @@ -67,6 +67,8 @@ class NODE_EDITOR_PUBLIC FlowScene void createGroup(); Node&restoreNode(QJsonObject const& nodeJson); + + Group& restoreGroup(QJsonObject const& nodeJson); QUuid pasteNode(QJsonObject &json, QPointF nodeGroupCentroid, QPointF mousePos); @@ -89,6 +91,11 @@ class NODE_EDITOR_PUBLIC FlowScene void setNodePosition(Node& node, const QPointF& pos) const; QSizeF getNodeSize(const Node& node) const; + + void resolveGroups(Group& node); + + void resolveGroups(Node& n); + public: std::unordered_map > const &nodes() const; @@ -124,17 +131,26 @@ class NODE_EDITOR_PUBLIC FlowScene signals: void nodeCreated(Node &n); + + void groupCreated(Group &g); void nodeDeleted(Node &n); void connectionCreated(Connection &c); + void connectionDeleted(Connection &c); void nodeMoved(Node& n, const QPointF& newLocation); + + void groupMoved(Group& n, const QPointF& newLocation); void nodeMoveFinished(Node& n, const QPointF& newLocation); + + void groupMoveFinished(Group& g, const QPointF& newLocation); void nodeDoubleClicked(Node& n); + + void groupDoubleClicked(Group& g); void connectionHovered(Connection& c, QPoint screenPos); @@ -158,7 +174,7 @@ class NODE_EDITOR_PUBLIC FlowScene std::unordered_map _connections; std::unordered_map _nodes; std::shared_ptr _registry; - std::unordered_map> _groups; + std::unordered_map> _groups; int historyInx; bool writeToHistory; diff --git a/include/nodes/internal/Group.hpp b/include/nodes/internal/Group.hpp index 5627ac61d..286cf8acf 100644 --- a/include/nodes/internal/Group.hpp +++ b/include/nodes/internal/Group.hpp @@ -14,7 +14,6 @@ #include "NodeState.hpp" #include "NodeGeometry.hpp" #include "NodeData.hpp" -#include "NodeGraphicsObject.hpp" #include "GroupGraphicsObject.hpp" #include "ConnectionGraphicsObject.hpp" #include "Serializable.hpp" @@ -22,27 +21,48 @@ namespace QtNodes { +class GroupGraphicsObject; + class NODE_EDITOR_PUBLIC Group : public QObject , public Serializable { Q_OBJECT public: + GroupGraphicsObject & + groupGraphicsObject() { + return *_groupGraphicsObject.get(); + } + + GroupGraphicsObject const & + groupGraphicsObject() const { + return *_groupGraphicsObject.get(); + } Group(FlowScene &scene): _scene(scene), - _id(QUuid::createUuid()) - { - _groupGraphicsObject = std::make_unique(scene); - } + _id(QUuid::createUuid()), + _groupGraphicsObject(nullptr), + _name("New Group") + { } void AddNode(std::shared_ptr node) { nodes.push_back(node); } + void + setGraphicsObject(std::shared_ptr graphics) { + // _groupGraphicsObject = std::move(graphics); + _groupGraphicsObject = graphics; + } + + virtual ~Group(){}; + void SetName(QString _name); + QString GetName(); + public: @@ -50,16 +70,43 @@ class NODE_EDITOR_PUBLIC Group id() const; QJsonObject save() const override{ - QJsonObject object; - return object; + QJsonObject groupJson; + groupJson["name"] = _name; + + QJsonObject posObj; + posObj["x"] = _groupGraphicsObject->pos().x(); + posObj["y"] = _groupGraphicsObject->pos().y(); + groupJson["position"] = posObj; + + QJsonObject colObj; + colObj["r"] = _groupGraphicsObject->r; + colObj["g"] = _groupGraphicsObject->g; + colObj["b"] = _groupGraphicsObject->b; + groupJson["color"] = colObj; + return groupJson; }; - void restore(QJsonObject const &json) override{}; + void restore(QJsonObject const &json) override{ + _id = QUuid(json["id"].toString()); + + QJsonObject positionJson = json["position"].toObject(); + QPointF point(positionJson["x"].toDouble(), + positionJson["y"].toDouble()); + _groupGraphicsObject->setPos(point); + + QJsonObject colorJson = json["color"].toObject(); + _groupGraphicsObject->r = colorJson["r"].toInt(); + _groupGraphicsObject->g = colorJson["g"].toInt(); + _groupGraphicsObject->b = colorJson["b"].toInt(); + + _name = json["name"].toString(); + }; + std::shared_ptr _groupGraphicsObject; + QString _name; private: FlowScene & _scene; - std::unique_ptr _groupGraphicsObject; std::vector> nodes; QUuid _id; diff --git a/include/nodes/internal/GroupGraphicsObject.hpp b/include/nodes/internal/GroupGraphicsObject.hpp index cef9d0937..2777078bb 100644 --- a/include/nodes/internal/GroupGraphicsObject.hpp +++ b/include/nodes/internal/GroupGraphicsObject.hpp @@ -15,6 +15,7 @@ namespace QtNodes class FlowScene; class FlowItemEntry; +class Group; /// Class reacts on GUI events, mouse clicks and /// forwards painting operation. @@ -23,17 +24,29 @@ class GroupGraphicsObject : public QGraphicsObject Q_OBJECT public: - GroupGraphicsObject(FlowScene &scene); + GroupGraphicsObject(FlowScene &scene, Group& group); virtual ~GroupGraphicsObject(); + Group& + group(); + + Group const& + group() const; + + QRectF boundingRect() const override; void setGeometryChanged(); + /// Visits all attached connections and corrects + /// their corresponding end points. + void + moveConnections() const; + enum { Type = UserType + 1 }; int @@ -42,6 +55,7 @@ class GroupGraphicsObject : public QGraphicsObject void lock(bool locked); + uint8_t r, g, b; protected: void paint(QPainter* painter, @@ -75,8 +89,16 @@ class GroupGraphicsObject : public QGraphicsObject void contextMenuEvent(QGraphicsSceneContextMenuEvent* event) override; - private: FlowScene & _scene; + Group& _group; + + bool isResizingX=false; + bool isResizingY=false; + bool isResizingXY=false; + + int sizeX=1000; + int sizeY=1000; + }; } diff --git a/src/FlowScene.cpp b/src/FlowScene.cpp index 98b5012d2..0d23fba8d 100644 --- a/src/FlowScene.cpp +++ b/src/FlowScene.cpp @@ -30,6 +30,7 @@ using QtNodes::FlowScene; using QtNodes::Node; +using QtNodes::Group; using QtNodes::NodeGraphicsObject; using QtNodes::Connection; using QtNodes::DataModelRegistry; @@ -49,9 +50,17 @@ FlowScene(std::shared_ptr registry) auto UpdateLamda = [this](Node& n, const QPointF& p) { + resolveGroups(n); UpdateHistory(); }; connect(this, &FlowScene::nodeMoveFinished, this, UpdateLamda); + + auto GroupUpdateLamda = [this](Group& g, const QPointF& p) + { + resolveGroups(g); + UpdateHistory(); + }; + connect(this, &FlowScene::groupMoveFinished, this, GroupUpdateLamda); anchors.resize(10); } @@ -132,6 +141,14 @@ restoreConnection(QJsonObject const &connectionJson) auto nodeIn = _nodes[nodeInId].get(); auto nodeOut = _nodes[nodeOutId].get(); + std::vector connIn = nodeIn->nodeState().getEntries(PortType::In); + std::vector connOut = nodeOut->nodeState().getEntries(PortType::Out); + int numConnectionsIn = connIn.size(); + int numConnectionsOut = connOut.size(); + + portIndexIn = std::min(numConnectionsIn - 1, portIndexIn); + portIndexOut = std::min(numConnectionsOut - 1, portIndexOut); + return createConnection(*nodeIn, portIndexIn, *nodeOut, portIndexOut); } @@ -147,6 +164,7 @@ void FlowScene::pasteConnection(QJsonObject const &connectionJson, QUuid newIn, auto nodeOut = _nodes[newOut].get(); createConnection(*nodeIn, portIndexIn, *nodeOut, portIndexOut); + } @@ -178,18 +196,34 @@ createNode(std::unique_ptr && dataModel) } void FlowScene::createGroup() { + auto group = std::make_shared(*this); + auto ggo = std::make_shared(*this, *group); - std::vector> selection = selectedNodes(); - auto group = std::make_unique(*this); - - for(int i=0; iAddNode(selection[i]); - } - - _groups[group->id()] = std::move(group); + QUuid id = group->id(); + auto groupPtr = group.get(); + group->setGraphicsObject(ggo); + _groups[id] = group; + + // return *groupPtr; } +Group& +FlowScene:: +restoreGroup(QJsonObject const& nodeJson) { + auto group = std::make_shared(*this); + auto ggo = std::make_shared(*this, *group); + + group->setGraphicsObject(ggo); + group->restore(nodeJson); + + QUuid id = group->id(); + _groups[id] = group; + + auto groupPtr = group.get(); + return *groupPtr; +} + Node& FlowScene:: restoreNode(QJsonObject const& nodeJson) @@ -200,13 +234,16 @@ restoreNode(QJsonObject const& nodeJson) if (!dataModel) throw std::logic_error(std::string("No registered model with name ") + modelName.toLocal8Bit().data()); - auto node = std::make_unique(std::move(dataModel)); + auto node = std::make_shared(std::move(dataModel)); auto ngo = std::make_unique(*this, *node); node->setGraphicsObject(std::move(ngo)); node->restore(nodeJson); auto nodePtr = node.get(); _nodes[node->id()] = std::move(node); + + resolveGroups(*nodePtr); + nodeCreated(*nodePtr); return *nodePtr; } @@ -265,6 +302,7 @@ removeNode(Node& node) deleteConnections(PortType::In); deleteConnections(PortType::Out); + _nodes.erase(node.id()); } @@ -406,6 +444,93 @@ getNodeSize(const Node& node) const return QSizeF(node.nodeGeometry().width(), node.nodeGeometry().height()); } +void +FlowScene:: +resolveGroups(Group& group) { + GroupGraphicsObject& ggo = group.groupGraphicsObject(); + QRectF groupRect = ggo.mapRectToScene(ggo.boundingRect()); + + //Check if the nodes that were inside the group still are (When resizing) + for(int i=ggo.childItems().size()-1; i>=0; i--) { + QGraphicsObject* node = (QGraphicsObject*) ggo.childItems()[i]; + QRectF nodeRect = node->mapRectToScene(node->boundingRect()); + if(!groupRect.contains(nodeRect)) { + QPointF scenePos = node->scenePos(); + node->setParentItem(nullptr); + ggo.childItems().removeAt(i); + node->setPos(scenePos); + } + } + + //Check all the the other things that collide the group at its new location + QListothers = collidingItems(&ggo, Qt::IntersectsItemBoundingRect); + for(int i=0; i(other); + GroupGraphicsObject* ggo1 = dynamic_cast(other); + if(ngo || ggo1) { + std::cout << " OK " << std::endl; + QRectF otherRect = other->mapRectToScene(other->boundingRect()); + + //checks what is inside + if(groupRect.contains(otherRect)) { + QPointF scenePos = other->scenePos(); + QPointF parentPos = ggo.mapFromScene(scenePos); + if(!other->isAncestorOf(&ggo)) { + other->setParentItem(&ggo); + other->setPos(parentPos); + } + } else if(otherRect.contains(groupRect)) { // Checks inside of what it is + QPointF scenePos = ggo.scenePos(); + QPointF parentPos = other->mapFromScene(scenePos); + if(!ggo.isAncestorOf(other)) { + ggo.setParentItem(other); + ggo.setPos(parentPos); + } + } + } else { + std::cout << "NO OK " << std::endl; + } + + + } + ggo.moveConnections(); +} + +void +FlowScene:: +resolveGroups(Node& n) { + NodeGraphicsObject& c = n.nodeGraphicsObject(); + bool hasIntersect = false; + + //Check if the final position is inside a group + for (auto& group : _groups) + { + auto groupPtr = group.second.get(); + GroupGraphicsObject* ggo = (GroupGraphicsObject*) groupPtr->_groupGraphicsObject.get(); + + QRectF groupRect = ggo->mapRectToScene(ggo->boundingRect()); + QRectF nodeRect = c.mapRectToScene(c.boundingRect()); + + if(groupRect.contains(nodeRect)) { + hasIntersect = true; + if(c.parentItem() != ggo) { + QPointF scenePos = c.scenePos(); + QPointF parentPos = ggo->mapFromScene(scenePos); + c.setParentItem(ggo); + c.setPos(parentPos); + } + } + } + + //Check if it went out of a group + if(!hasIntersect && c.parentItem() != nullptr ) { + QPointF newPos = c.parentItem()->mapToScene(c.pos()); + c.setParentItem(nullptr); + c.setPos(newPos); + } +} + std::unordered_map > const & FlowScene:: @@ -431,6 +556,7 @@ selectedNodes() const std::vector> ret; ret.reserve(graphicsItems.size()); + for (QGraphicsItem* item : graphicsItems) { @@ -528,15 +654,21 @@ saveToMemory() const { QJsonObject sceneJson; - QJsonArray nodesJsonArray; + QJsonArray groupsJsonArray; + for (auto const & pair : _groups) + { + auto const &group = pair.second; + groupsJsonArray.append(group->save()); + } + sceneJson["groups"] = groupsJsonArray; + QJsonArray nodesJsonArray; for (auto const & pair : _nodes) { auto const &node = pair.second; nodesJsonArray.append(node->save()); } - sceneJson["nodes"] = nodesJsonArray; QJsonArray connectionJsonArray; @@ -580,6 +712,12 @@ loadFromMemory(const QByteArray& data) { QJsonObject const jsonDocument = QJsonDocument::fromJson(data).object(); + QJsonArray groupsJsonArray = jsonDocument["groups"].toArray(); + for (int i = 0; i < groupsJsonArray.size(); ++i) + { + restoreGroup(groupsJsonArray[i].toObject()); + } + QJsonArray nodesJsonArray = jsonDocument["nodes"].toArray(); for (int i = 0; i < nodesJsonArray.size(); ++i) diff --git a/src/FlowView.cpp b/src/FlowView.cpp index 3ffff3f96..87d236283 100644 --- a/src/FlowView.cpp +++ b/src/FlowView.cpp @@ -23,8 +23,13 @@ #include "ConnectionGraphicsObject.hpp" #include "StyleCollection.hpp" +#include "Connection.hpp" +#include "NodeConnectionInteraction.hpp" + using QtNodes::FlowView; using QtNodes::FlowScene; +using QtNodes::Connection; +using QtNodes::NodeConnectionInteraction; FlowView:: FlowView(QWidget *parent) @@ -184,7 +189,7 @@ FlowView::setScene(FlowScene *scene) } } -void +void FlowView:: contextMenuEvent(QContextMenuEvent *event) { @@ -193,7 +198,7 @@ contextMenuEvent(QContextMenuEvent *event) QGraphicsView::contextMenuEvent(event); return; } - + QMenu modelMenu; auto skipText = QStringLiteral("skip me"); @@ -255,9 +260,8 @@ contextMenuEvent(QContextMenuEvent *event) QPoint pos = event->pos(); QPointF posView = this->mapToScene(pos); - node.nodeGraphicsObject().setPos(posView); - + _scene->UpdateHistory(); } else @@ -294,7 +298,6 @@ contextMenuEvent(QContextMenuEvent *event) // make sure the text box gets focus so the user doesn't have to click on it txtBox->setFocus(); - modelMenu.exec(event->globalPos()); } @@ -351,26 +354,22 @@ void FlowView:: deleteSelectedNodes() { - //std::cout << "deleteSelectedNodes" << std::endl; - // delete the nodes, this will delete many of the connections - std::vector> nodeItems = _scene->selectedNodes(); - for(int i = 0; i < nodeItems.size(); i++) + + for (QGraphicsItem * item : _scene->selectedItems()) { - Node* item = nodeItems[i].get(); if(item) { - _scene->removeNode(*item); + if (auto c = qgraphicsitem_cast(item)) + { + _scene->removeNode(c->node()); + } + else if (auto c = qgraphicsitem_cast(item)) + { + _scene->deleteConnection(c->connection()); + } + } } - - // for (QGraphicsItem * item : _scene->selectedItems()) - // { - // if(item) - // { - // if (auto c = qgraphicsitem_cast(item)) - // _scene->deleteConnection(c->connection()); - // } - // } _scene->UpdateHistory(); } diff --git a/src/Group.cpp b/src/Group.cpp index c5cb605ff..45f15b72e 100644 --- a/src/Group.cpp +++ b/src/Group.cpp @@ -1,4 +1,5 @@ #include "Group.hpp" +#include "GroupGraphicsObject.hpp" using QtNodes::Group; @@ -7,4 +8,22 @@ Group:: id() const { return _id; -} \ No newline at end of file +} + +void +Group:: +SetName(QString _name) { + this->_name = _name; +} + +QString +Group:: +GetName() { + return _name; +} + + +// GroupGraphicsObject& Group::groupGraphicsObject(){ +// return *_groupGraphicsObject.get(); +// } + diff --git a/src/GroupGraphicsObject.cpp b/src/GroupGraphicsObject.cpp index 3dc2737cb..e59c816b1 100644 --- a/src/GroupGraphicsObject.cpp +++ b/src/GroupGraphicsObject.cpp @@ -12,7 +12,7 @@ #include "FlowScene.hpp" #include "NodePainter.hpp" -#include "Node.hpp" +#include "Group.hpp" #include "NodeDataModel.hpp" #include "NodeConnectionInteraction.hpp" @@ -20,11 +20,13 @@ using QtNodes::GroupGraphicsObject; using QtNodes::Node; +using QtNodes::Group; using QtNodes::FlowScene; GroupGraphicsObject:: -GroupGraphicsObject(FlowScene &scene) +GroupGraphicsObject(FlowScene &scene, Group& group) : _scene(scene) + , _group(group) { _scene.addItem(this); @@ -39,7 +41,38 @@ GroupGraphicsObject(FlowScene &scene) setAcceptHoverEvents(true); setAcceptTouchEvents(true); - setZValue(0); + setZValue(-2); + + r = g = b = 135; + + QGraphicsProxyWidget* _proxyWidget = new QGraphicsProxyWidget(this); + + QString name = group.GetName(); + QLineEdit* l = new QLineEdit(name); + l->setAlignment(Qt::AlignHCenter); + l->setStyleSheet("\ + QLineEdit {\ + border: 0px solid gray;\ + border-radius: 0px;\ + padding: 0 0px;\ + background: rgb(135, 135, 135);\ + selection-background-color: darkgray;\ + color: rgb(220, 220, 220);\ + font-size: 30px;\ + }\ + QLineEdit:hover {\ + background: rgb(90, 90, 90);\ + }\ + "); + + connect(l, &QLineEdit::textChanged, this, [&group](const QString &text) { + group.SetName("wuh"); + std::cout << "HERE" << std::endl; + }); + + + _proxyWidget->setWidget(l); + _proxyWidget->setPos(pos() + QPointF(sizeX/2 - _proxyWidget->size().width()/2, 0)); } @@ -50,11 +83,24 @@ GroupGraphicsObject:: } +Group& +GroupGraphicsObject:: +group() { + return _group; +} + +Group const& +GroupGraphicsObject:: +group() const { + return _group; +} + + QRectF GroupGraphicsObject:: boundingRect() const { - return QRectF(0, 0, 1000, 1000); + return QRectF(0, 0, sizeX, sizeY); } @@ -65,6 +111,22 @@ setGeometryChanged() prepareGeometryChange(); } +void +GroupGraphicsObject:: +moveConnections() const { + for(int i=0; i(childItems()[i]); + if (ngo) { + ngo->moveConnections(); + } + + GroupGraphicsObject* ggo = dynamic_cast(childItems()[i]); + if (ggo) { + ggo->moveConnections(); + } + } +} + void @@ -74,8 +136,8 @@ paint(QPainter * painter, QWidget* ) { painter->setClipRect(option->exposedRect); - QRect rect(0, 0, 1000, 1000); - auto color = QColor(255, 0, 0, 255); + QRect rect(0, 0, sizeX, sizeY); + auto color = QColor(r, g, b, 255); painter->drawRect(rect); painter->fillRect(rect, QBrush(color)); } @@ -93,6 +155,24 @@ void GroupGraphicsObject:: mousePressEvent(QGraphicsSceneMouseEvent * event) { + // deselect all other items after this one is selected + if (!isSelected() && + !(event->modifiers() & Qt::ControlModifier)) + { + _scene.clearSelection(); + } + + auto mousePos = event->pos(); + + if (abs(mousePos.x() - sizeX) < 20 && abs(mousePos.y() - sizeY) < 20) + { + isResizingXY=true; + } else if (abs(mousePos.x() - sizeX) < 20) + { + isResizingX=true; + } else if (abs(mousePos.y() - sizeY) < 20) { + isResizingY=true; + } } @@ -100,14 +180,34 @@ void GroupGraphicsObject:: mouseMoveEvent(QGraphicsSceneMouseEvent * event) { - // setPos(mapFromScene(event->scenePos())); + if(isResizingX) { + int diff = event->pos().x() - event->lastPos().x(); + prepareGeometryChange(); + sizeX += diff; + update(); + // ngo->moveConnections(); + event->accept(); + } + else if(isResizingY) { + int diff = event->pos().y() - event->lastPos().y(); + prepareGeometryChange(); + sizeY += diff; + update(); + // ngo->moveConnections(); + event->accept(); + } else if(isResizingXY) { + auto diff = event->pos() - event->lastPos(); + prepareGeometryChange(); + sizeX += diff.x(); + sizeY += diff.y(); + update(); + // ngo->moveConnections(); + event->accept(); + } else { + _scene.groupMoved(_group, pos()); QGraphicsObject::mouseMoveEvent(event); - // std::cout << scene()->scale() << std::endl; - // event->ignore(); - // //DO SNAPPING HERE - // QRectF r = scene()->sceneRect(); - // r = r.united(mapToScene(boundingRect()).boundingRect()); - // scene()->setSceneRect(r); + moveConnections(); + } } @@ -116,6 +216,11 @@ GroupGraphicsObject:: mouseReleaseEvent(QGraphicsSceneMouseEvent* event) { QGraphicsObject::mouseReleaseEvent(event); + _scene.groupMoveFinished(_group, pos()); + + isResizingX=false; + isResizingY=false; + isResizingXY=false; } @@ -123,6 +228,8 @@ void GroupGraphicsObject:: hoverEnterEvent(QGraphicsSceneHoverEvent * event) { + auto mousePos = event->pos(); + setCursor(QCursor()); update(); event->accept(); } @@ -132,6 +239,7 @@ void GroupGraphicsObject:: hoverLeaveEvent(QGraphicsSceneHoverEvent * event) { + // std::cout << "hverleave" << std::endl; update(); event->accept(); } @@ -141,6 +249,21 @@ void GroupGraphicsObject:: hoverMoveEvent(QGraphicsSceneHoverEvent * event) { + auto mousePos = event->pos(); + + if (abs(mousePos.x() - sizeX) < 20 && abs(mousePos.y() - sizeY) < 20) + { + setCursor(QCursor(Qt::SizeFDiagCursor)); + } else if (abs(mousePos.x() - sizeX) < 20) + { + setCursor(QCursor(Qt::SizeHorCursor)); + } else if (abs(mousePos.y() - sizeY) < 20) { + setCursor(QCursor(Qt::SizeVerCursor)); + } else + { + setCursor(QCursor()); + } + event->accept(); } @@ -149,6 +272,8 @@ void GroupGraphicsObject:: mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) { + _scene.groupDoubleClicked(group()); + // std::cout << "doubleclick" << std::endl; QGraphicsItem::mouseDoubleClickEvent(event); } From d341631a79655348cb04ca8525f52593dabd7fc1 Mon Sep 17 00:00:00 2001 From: jacquespillet Date: Mon, 19 Apr 2021 07:46:45 +0100 Subject: [PATCH 18/39] added deleted node --- src/FlowScene.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/FlowScene.cpp b/src/FlowScene.cpp index 0d23fba8d..a84b2135c 100644 --- a/src/FlowScene.cpp +++ b/src/FlowScene.cpp @@ -232,8 +232,12 @@ restoreNode(QJsonObject const& nodeJson) auto dataModel = registry().create(modelName); //This is where it looks for the node by name if (!dataModel) - throw std::logic_error(std::string("No registered model with name ") + - modelName.toLocal8Bit().data()); + { + dataModel = registry().create("DeletedNode"); + } + /*throw std::logic_error(std::string("No registered model with name ") + + modelName.toLocal8Bit().data()); +*/ auto node = std::make_shared(std::move(dataModel)); auto ngo = std::make_unique(*this, *node); node->setGraphicsObject(std::move(ngo)); From f6fb7b8f5325e0c36a03b28f99ec74238f6e7917 Mon Sep 17 00:00:00 2001 From: jacquespillet Date: Fri, 27 Aug 2021 09:08:52 +0100 Subject: [PATCH 19/39] Added node templates --- include/nodes/internal/DataModelRegistry.hpp | 22 +++- include/nodes/internal/FlowView.hpp | 8 +- src/DataModelRegistry.cpp | 8 ++ src/FlowView.cpp | 122 +++++++++++++------ 4 files changed, 120 insertions(+), 40 deletions(-) diff --git a/include/nodes/internal/DataModelRegistry.hpp b/include/nodes/internal/DataModelRegistry.hpp index 674bc67b9..6adab92b8 100644 --- a/include/nodes/internal/DataModelRegistry.hpp +++ b/include/nodes/internal/DataModelRegistry.hpp @@ -24,6 +24,8 @@ class NODE_EDITOR_PUBLIC DataModelRegistry using RegisteredModelsCategoryMap = std::unordered_map; using CategoriesSet = std::set; + using RegisteredTemplatesMap = std::unordered_map; + struct TypeConverterItem { RegistryItemPtr Model{}; @@ -82,10 +84,23 @@ class NODE_EDITOR_PUBLIC DataModelRegistry converter->DestinationType = converter->Model->dataType(PortType::Out, 0); auto typeConverterKey = std::make_pair(converter->SourceType.id, converter->DestinationType.id); - _registeredTypeConverters[typeConverterKey] = std::move(converter); + _registeredTypeConverters[typeConverterKey] = std::move(converter); } } + void + registerTemplate(QString templateName, QString templateFilePath) + { + _registeredTemplates[templateName] = templateFilePath; + } + + void + removeTemplate(QString templateName) + { + _registeredTemplates.erase(templateName); + } + + //Parameter order alias, so a category can be set without forcing to manually pass a model instance template void @@ -103,6 +118,9 @@ class NODE_EDITOR_PUBLIC DataModelRegistry RegisteredModelsCategoryMap const & registeredModelsCategoryAssociation() const; + RegisteredTemplatesMap & + RegisteredTemplates(); + CategoriesSet const & categories() const; @@ -116,5 +134,7 @@ class NODE_EDITOR_PUBLIC DataModelRegistry CategoriesSet _categories{}; RegisteredModelsMap _registeredModels{}; RegisteredTypeConvertersMap _registeredTypeConverters{}; + + RegisteredTemplatesMap _registeredTemplates{}; }; } diff --git a/include/nodes/internal/FlowView.hpp b/include/nodes/internal/FlowView.hpp index 9db7606a7..a92203e35 100644 --- a/include/nodes/internal/FlowView.hpp +++ b/include/nodes/internal/FlowView.hpp @@ -3,7 +3,7 @@ #include #include "Export.hpp" - + namespace QtNodes { @@ -27,12 +27,16 @@ class NODE_EDITOR_PUBLIC FlowView void setScene(FlowScene *scene); + QJsonObject selectionToJson(); + + void jsonToScene(QJsonObject object); + public slots: void scaleUp(); void scaleDown(); - + void deleteSelectedNodes(); void duplicateSelectedNode(); diff --git a/src/DataModelRegistry.cpp b/src/DataModelRegistry.cpp index 8a1426ac9..028cfed76 100644 --- a/src/DataModelRegistry.cpp +++ b/src/DataModelRegistry.cpp @@ -29,6 +29,14 @@ registeredModels() const } +DataModelRegistry::RegisteredTemplatesMap & +DataModelRegistry::RegisteredTemplates() +{ + return _registeredTemplates; +} + + + DataModelRegistry::RegisteredModelsCategoryMap const & DataModelRegistry:: registeredModelsCategoryAssociation() const diff --git a/src/FlowView.cpp b/src/FlowView.cpp index 87d236283..f2e383c5f 100644 --- a/src/FlowView.cpp +++ b/src/FlowView.cpp @@ -232,6 +232,12 @@ contextMenuEvent(QContextMenuEvent *event) topLevelItems[cat] = item; } + //Add templates category + auto templatesCategory = new QTreeWidgetItem(treeView); + templatesCategory->setText(0, "Templates"); + templatesCategory->setData(0, Qt::UserRole, "Templates"); + topLevelItems["Templates"] = templatesCategory; + for (auto const &assoc : _scene->registry().registeredModelsCategoryAssociation()) { auto parent = topLevelItems[assoc.second]; @@ -239,9 +245,19 @@ contextMenuEvent(QContextMenuEvent *event) item->setText(0, assoc.first); item->setData(0, Qt::UserRole, assoc.first); } + + for (auto const &assoc : _scene->registry().RegisteredTemplates()) + { + QString name = assoc.first; + auto item = new QTreeWidgetItem(templatesCategory); + item->setText(0, name); + item->setData(0, Qt::UserRole, name); + } + treeView->expandAll(); + connect(treeView, &QTreeWidget::itemActivated, [&](QTreeWidgetItem *item, int) { QString modelName = item->data(0, Qt::UserRole).toString(); @@ -251,6 +267,26 @@ contextMenuEvent(QContextMenuEvent *event) return; } + QString parent = item->parent()->data(0, Qt::UserRole).toString(); + std::cout << parent.toStdString() << std::endl; + if(parent == "Templates") + { + DataModelRegistry::RegisteredTemplatesMap map = _scene->registry().RegisteredTemplates(); + QString fileName = map[modelName]; + + QFile file; + file.setFileName(fileName); + file.open(QIODevice::ReadOnly | QIODevice::Text); + QString val = file.readAll(); + file.close(); + qWarning() << val; + QJsonDocument d = QJsonDocument::fromJson(val.toUtf8()); + QJsonObject sett2 = d.object(); + jsonToScene(sett2); + + modelMenu.close(); + } + auto type = _scene->registry().create(modelName); if (type) @@ -375,38 +411,7 @@ deleteSelectedNodes() } void FlowView::copySelectedNodes() { - QJsonObject sceneJson; - QJsonArray nodesJsonArray; - std::vector addedIds; - - for (QGraphicsItem * item : _scene->selectedItems()) - { - if (auto n = qgraphicsitem_cast(item)) - { - Node& node = n->node(); - nodesJsonArray.append(node.save()); - addedIds.push_back(node.id()); - } - } - - QJsonArray connectionJsonArray; - for (QGraphicsItem * item : _scene->selectedItems()) - { - if (auto c = qgraphicsitem_cast(item)) { - Connection& connection = c->connection(); - - if( std::find(addedIds.begin(), addedIds.end(), connection.getNode(PortType::In)->id()) != addedIds.end() && - std::find(addedIds.begin(), addedIds.end(), connection.getNode(PortType::Out)->id()) != addedIds.end() ) { - QJsonObject connectionJson = connection.save(); - - if (!connectionJson.isEmpty()) - connectionJsonArray.append(connectionJson); - } - } - } - - sceneJson["nodes"] = nodesJsonArray; - sceneJson["connections"] = connectionJsonArray; + QJsonObject sceneJson = selectionToJson(); QJsonDocument document(sceneJson); std::string json = document.toJson().toStdString(); @@ -415,11 +420,8 @@ void FlowView::copySelectedNodes() { p_Clipboard->setText( QString::fromStdString(json)); } -void FlowView::pasteSelectedNodes() { - QClipboard *p_Clipboard = QApplication::clipboard(); - QByteArray text = p_Clipboard->text().toUtf8(); - - QJsonObject const jsonDocument = QJsonDocument::fromJson(text).object(); +void FlowView::jsonToScene(QJsonObject jsonDocument) +{ QJsonArray nodesJsonArray = jsonDocument["nodes"].toArray(); std::map addedIds; @@ -472,6 +474,52 @@ void FlowView::pasteSelectedNodes() { _scene->UpdateHistory(); } +QJsonObject FlowView::selectionToJson() +{ + QJsonObject sceneJson; + QJsonArray nodesJsonArray; + std::vector addedIds; + + for (QGraphicsItem * item : _scene->selectedItems()) + { + if (auto n = qgraphicsitem_cast(item)) + { + Node& node = n->node(); + nodesJsonArray.append(node.save()); + addedIds.push_back(node.id()); + } + } + + QJsonArray connectionJsonArray; + for (QGraphicsItem * item : _scene->selectedItems()) + { + if (auto c = qgraphicsitem_cast(item)) { + Connection& connection = c->connection(); + + if( std::find(addedIds.begin(), addedIds.end(), connection.getNode(PortType::In)->id()) != addedIds.end() && + std::find(addedIds.begin(), addedIds.end(), connection.getNode(PortType::Out)->id()) != addedIds.end() ) { + QJsonObject connectionJson = connection.save(); + + if (!connectionJson.isEmpty()) + connectionJsonArray.append(connectionJson); + } + } + } + + sceneJson["nodes"] = nodesJsonArray; + sceneJson["connections"] = connectionJsonArray; + + return sceneJson; +} + +void FlowView::pasteSelectedNodes() { + QClipboard *p_Clipboard = QApplication::clipboard(); + QByteArray text = p_Clipboard->text().toUtf8(); + + QJsonObject const jsonDocument = QJsonDocument::fromJson(text).object(); + jsonToScene(jsonDocument); +} + void FlowView::createGroup() { _scene->createGroup(); } From 0eb037a3245b7a93ac6fad499f0402ad07fe1cd4 Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Wed, 22 Sep 2021 11:06:18 +0100 Subject: [PATCH 20/39] Fixes on groups --- include/nodes/internal/FlowScene.hpp | 4 +- include/nodes/internal/FlowView.hpp | 3 - include/nodes/internal/Group.hpp | 15 +++- .../nodes/internal/GroupGraphicsObject.hpp | 12 +++- src/FlowScene.cpp | 29 +++++--- src/FlowView.cpp | 68 +++++++++++++------ src/GroupGraphicsObject.cpp | 29 ++++---- src/Node.cpp | 4 +- 8 files changed, 115 insertions(+), 49 deletions(-) diff --git a/include/nodes/internal/FlowScene.hpp b/include/nodes/internal/FlowScene.hpp index fa9e8119a..cf2a983cd 100644 --- a/include/nodes/internal/FlowScene.hpp +++ b/include/nodes/internal/FlowScene.hpp @@ -64,7 +64,7 @@ class NODE_EDITOR_PUBLIC FlowScene Node&createNode(std::unique_ptr && dataModel); - void createGroup(); + Group& createGroup(); Node&restoreNode(QJsonObject const& nodeJson); @@ -76,6 +76,8 @@ class NODE_EDITOR_PUBLIC FlowScene void removeNode(Node& node); + void removeGroup(Group& node); + DataModelRegistry®istry() const; void setRegistry(std::shared_ptr registry); diff --git a/include/nodes/internal/FlowView.hpp b/include/nodes/internal/FlowView.hpp index a92203e35..9320e304b 100644 --- a/include/nodes/internal/FlowView.hpp +++ b/include/nodes/internal/FlowView.hpp @@ -45,8 +45,6 @@ public slots: void pasteSelectedNodes(); - void createGroup(); - protected: void contextMenuEvent(QContextMenuEvent *event) override; @@ -79,7 +77,6 @@ public slots: QAction* _duplicateSelectionAction; QAction* _copymultiplenodes; QAction* _pastemultiplenodes; - QAction* _createGroup; QAction* _undoAction; QAction* _redoAction; diff --git a/include/nodes/internal/Group.hpp b/include/nodes/internal/Group.hpp index 286cf8acf..78fba58ca 100644 --- a/include/nodes/internal/Group.hpp +++ b/include/nodes/internal/Group.hpp @@ -78,6 +78,11 @@ class NODE_EDITOR_PUBLIC Group posObj["y"] = _groupGraphicsObject->pos().y(); groupJson["position"] = posObj; + QJsonObject sizeObj; + sizeObj["x"] = _groupGraphicsObject->getSizeX(); + sizeObj["y"] = _groupGraphicsObject->getSizeY(); + groupJson["size"] = sizeObj; + QJsonObject colObj; colObj["r"] = _groupGraphicsObject->r; colObj["g"] = _groupGraphicsObject->g; @@ -87,19 +92,23 @@ class NODE_EDITOR_PUBLIC Group }; void restore(QJsonObject const &json) override{ - _id = QUuid(json["id"].toString()); - QJsonObject positionJson = json["position"].toObject(); QPointF point(positionJson["x"].toDouble(), positionJson["y"].toDouble()); _groupGraphicsObject->setPos(point); + + QJsonObject sizeJson = json["size"].toObject(); + _groupGraphicsObject->setSizeX(sizeJson["x"].toDouble()); + _groupGraphicsObject->setSizeY(sizeJson["y"].toDouble()); + QJsonObject colorJson = json["color"].toObject(); _groupGraphicsObject->r = colorJson["r"].toInt(); _groupGraphicsObject->g = colorJson["g"].toInt(); _groupGraphicsObject->b = colorJson["b"].toInt(); - _name = json["name"].toString(); + SetName(json["name"].toString()); + _groupGraphicsObject->nameLineEdit->setText(_name); }; std::shared_ptr _groupGraphicsObject; diff --git a/include/nodes/internal/GroupGraphicsObject.hpp b/include/nodes/internal/GroupGraphicsObject.hpp index 2777078bb..90b46cbb0 100644 --- a/include/nodes/internal/GroupGraphicsObject.hpp +++ b/include/nodes/internal/GroupGraphicsObject.hpp @@ -7,6 +7,7 @@ #include "NodeGeometry.hpp" #include "NodeState.hpp" +#include class QGraphicsProxyWidget; @@ -47,7 +48,7 @@ class GroupGraphicsObject : public QGraphicsObject void moveConnections() const; - enum { Type = UserType + 1 }; + enum { Type = UserType + 3 }; int type() const override { return Type; } @@ -56,6 +57,15 @@ class GroupGraphicsObject : public QGraphicsObject lock(bool locked); uint8_t r, g, b; + QLineEdit* nameLineEdit; + QGraphicsProxyWidget* _proxyWidget; + + int getSizeX() {return sizeX;} + int getSizeY() {return sizeY;} + + void setSizeX(int size) {sizeX = size;} + void setSizeY(int size) {sizeY = size;} + protected: void paint(QPainter* painter, diff --git a/src/FlowScene.cpp b/src/FlowScene.cpp index a84b2135c..bac90197f 100644 --- a/src/FlowScene.cpp +++ b/src/FlowScene.cpp @@ -195,7 +195,7 @@ createNode(std::unique_ptr && dataModel) return *nodePtr; } -void FlowScene::createGroup() { +Group& FlowScene::createGroup() { auto group = std::make_shared(*this); auto ggo = std::make_shared(*this, *group); @@ -204,7 +204,7 @@ void FlowScene::createGroup() { group->setGraphicsObject(ggo); _groups[id] = group; - // return *groupPtr; + return *groupPtr; } @@ -214,13 +214,15 @@ restoreGroup(QJsonObject const& nodeJson) { auto group = std::make_shared(*this); auto ggo = std::make_shared(*this, *group); - group->setGraphicsObject(ggo); - group->restore(nodeJson); - QUuid id = group->id(); + auto groupPtr = group.get(); + + group->setGraphicsObject(ggo); _groups[id] = group; + + + group->restore(nodeJson); - auto groupPtr = group.get(); return *groupPtr; } @@ -310,6 +312,13 @@ removeNode(Node& node) _nodes.erase(node.id()); } +void +FlowScene:: +removeGroup(Group& group) +{ + _groups.erase(group.id()); +} + DataModelRegistry& FlowScene:: @@ -473,7 +482,6 @@ resolveGroups(Group& group) { NodeGraphicsObject* ngo = dynamic_cast(other); GroupGraphicsObject* ggo1 = dynamic_cast(other); if(ngo || ggo1) { - std::cout << " OK " << std::endl; QRectF otherRect = other->mapRectToScene(other->boundingRect()); //checks what is inside @@ -493,7 +501,6 @@ resolveGroups(Group& group) { } } } else { - std::cout << "NO OK " << std::endl; } @@ -597,6 +604,12 @@ clearScene() { removeNode(*node); } + + _groups.clear(); + // for (auto& group : _groups) + // { + // _groups.erase(group.first); + // } } diff --git a/src/FlowView.cpp b/src/FlowView.cpp index f2e383c5f..a718de113 100644 --- a/src/FlowView.cpp +++ b/src/FlowView.cpp @@ -19,6 +19,7 @@ #include "FlowScene.hpp" #include "DataModelRegistry.hpp" #include "Node.hpp" +#include "Group.hpp" #include "NodeGraphicsObject.hpp" #include "ConnectionGraphicsObject.hpp" #include "StyleCollection.hpp" @@ -149,12 +150,6 @@ FlowView::setScene(FlowScene *scene) connect(_pastemultiplenodes, &QAction::triggered, this, &FlowView::pasteSelectedNodes); addAction(_pastemultiplenodes); - _createGroup = new QAction(QStringLiteral("Create Node Group"), this); - _createGroup->setShortcut(QKeySequence(tr("Ctrl+G"))); - _createGroup->setShortcutContext(Qt::WidgetWithChildrenShortcut); - connect(_createGroup, &QAction::triggered, this, &FlowView::createGroup); - addAction(_createGroup); - _undoAction = new QAction(QStringLiteral("Undo"), this); _undoAction->setShortcut(QKeySequence(tr("Ctrl+Z"))); _undoAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); @@ -253,6 +248,17 @@ contextMenuEvent(QContextMenuEvent *event) item->setText(0, name); item->setData(0, Qt::UserRole, name); } + + //Add templates category + // auto GroupsCategory = new QTreeWidgetItem(treeView); + // GroupsCategory->setText(0, "Groups"); + // GroupsCategory->setData(0, Qt::UserRole, "Groups"); + // topLevelItems["Groups"] = GroupsCategory; + + auto groupItem = new QTreeWidgetItem(treeView); + groupItem->setText(0, "Group"); + groupItem->setData(0, Qt::UserRole, "Group"); + topLevelItems["Group"] = groupItem; treeView->expandAll(); @@ -267,6 +273,16 @@ contextMenuEvent(QContextMenuEvent *event) return; } + if(modelName == "Group") + { + Group& group = _scene->createGroup(); + QPoint pos = event->pos(); + QPointF posView = this->mapToScene(pos); + group.groupGraphicsObject().setPos(posView); + modelMenu.close(); + return; + } + QString parent = item->parent()->data(0, Qt::UserRole).toString(); std::cout << parent.toStdString() << std::endl; if(parent == "Templates") @@ -284,7 +300,7 @@ contextMenuEvent(QContextMenuEvent *event) QJsonObject sett2 = d.object(); jsonToScene(sett2); - modelMenu.close(); + modelMenu.close(); } auto type = _scene->registry().create(modelName); @@ -328,6 +344,12 @@ contextMenuEvent(QContextMenuEvent *event) child->setHidden(true); } } + auto catName = topLvlItem->data(0, Qt::UserRole).toString(); + if(catName.contains(text, Qt::CaseInsensitive)) + { + shouldHideCategory=false; + } + topLvlItem->setHidden(shouldHideCategory); } }); @@ -391,23 +413,34 @@ FlowView:: deleteSelectedNodes() { + std::cout << "DELETEGROUP 0" << _scene->selectedItems().size() << std::endl; for (QGraphicsItem * item : _scene->selectedItems()) { + std::cout << "DELETEGROUP 1" << std::endl; if(item) { - if (auto c = qgraphicsitem_cast(item)) - { - _scene->removeNode(c->node()); - } - else if (auto c = qgraphicsitem_cast(item)) - { - _scene->deleteConnection(c->connection()); - } - + std::cout << "DELETEGROUP 2" << std::endl; + if (auto c = qgraphicsitem_cast(item)) + { + std::cout << "DELETEGROUP 5" << std::endl; + _scene->removeGroup(c->group()); + } + else if (auto c = qgraphicsitem_cast(item)) + { + std::cout << "DELETEGROUP 3" << std::endl; + _scene->removeNode(c->node()); + } + else if (auto c = qgraphicsitem_cast(item)) + { + std::cout << "DELETEGROUP 4" << std::endl; + _scene->deleteConnection(c->connection()); + } } } + std::cout << "DELETEGROUP 6" << std::endl; _scene->UpdateHistory(); + std::cout << "DELETEGROUP 7" << std::endl; } void FlowView::copySelectedNodes() { @@ -520,9 +553,6 @@ void FlowView::pasteSelectedNodes() { jsonToScene(jsonDocument); } -void FlowView::createGroup() { - _scene->createGroup(); -} void FlowView::duplicateSelectedNode() diff --git a/src/GroupGraphicsObject.cpp b/src/GroupGraphicsObject.cpp index e59c816b1..2d981ebb2 100644 --- a/src/GroupGraphicsObject.cpp +++ b/src/GroupGraphicsObject.cpp @@ -45,12 +45,12 @@ GroupGraphicsObject(FlowScene &scene, Group& group) r = g = b = 135; - QGraphicsProxyWidget* _proxyWidget = new QGraphicsProxyWidget(this); + _proxyWidget = new QGraphicsProxyWidget(this); QString name = group.GetName(); - QLineEdit* l = new QLineEdit(name); - l->setAlignment(Qt::AlignHCenter); - l->setStyleSheet("\ + nameLineEdit = new QLineEdit(name); + nameLineEdit->setAlignment(Qt::AlignHCenter); + nameLineEdit->setStyleSheet("\ QLineEdit {\ border: 0px solid gray;\ border-radius: 0px;\ @@ -65,13 +65,11 @@ GroupGraphicsObject(FlowScene &scene, Group& group) }\ "); - connect(l, &QLineEdit::textChanged, this, [&group](const QString &text) { - group.SetName("wuh"); - std::cout << "HERE" << std::endl; + connect(nameLineEdit, &QLineEdit::textChanged, this, [&group](const QString &text) { + group.SetName(text); }); - - _proxyWidget->setWidget(l); + _proxyWidget->setWidget(nameLineEdit); _proxyWidget->setPos(pos() + QPointF(sizeX/2 - _proxyWidget->size().width()/2, 0)); } @@ -155,6 +153,13 @@ void GroupGraphicsObject:: mousePressEvent(QGraphicsSceneMouseEvent * event) { + std::cout << "OEFIJWOEFJW " << std::endl; + if(QApplication::keyboardModifiers().testFlag(Qt::ShiftModifier)) + { + event->ignore(); + return; + } + // deselect all other items after this one is selected if (!isSelected() && !(event->modifiers() & Qt::ControlModifier)) @@ -185,7 +190,6 @@ mouseMoveEvent(QGraphicsSceneMouseEvent * event) prepareGeometryChange(); sizeX += diff; update(); - // ngo->moveConnections(); event->accept(); } else if(isResizingY) { @@ -193,7 +197,7 @@ mouseMoveEvent(QGraphicsSceneMouseEvent * event) prepareGeometryChange(); sizeY += diff; update(); - // ngo->moveConnections(); + _proxyWidget->setPos(pos() + QPointF(sizeX/2 - _proxyWidget->size().width()/2, 0)); event->accept(); } else if(isResizingXY) { auto diff = event->pos() - event->lastPos(); @@ -201,7 +205,7 @@ mouseMoveEvent(QGraphicsSceneMouseEvent * event) sizeX += diff.x(); sizeY += diff.y(); update(); - // ngo->moveConnections(); + _proxyWidget->setPos(pos() + QPointF(sizeX/2 - _proxyWidget->size().width()/2, 0)); event->accept(); } else { _scene.groupMoved(_group, pos()); @@ -281,4 +285,5 @@ void GroupGraphicsObject:: contextMenuEvent(QGraphicsSceneContextMenuEvent* event) { + } diff --git a/src/Node.cpp b/src/Node.cpp index 1984f66b5..93d627dfc 100644 --- a/src/Node.cpp +++ b/src/Node.cpp @@ -53,8 +53,8 @@ save() const nodeJson["model"] = _nodeDataModel->save(); QJsonObject obj; - obj["x"] = _nodeGraphicsObject->pos().x(); - obj["y"] = _nodeGraphicsObject->pos().y(); + obj["x"] = _nodeGraphicsObject->scenePos().x(); + obj["y"] = _nodeGraphicsObject->scenePos().y(); nodeJson["position"] = obj; return nodeJson; From b91389bd30ce2ebb359c25627760da5794ccc5d6 Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Mon, 15 Nov 2021 11:55:05 +0000 Subject: [PATCH 21/39] Added snapping --- include/nodes/internal/FlowScene.hpp | 3 +++ src/NodeGraphicsObject.cpp | 26 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/include/nodes/internal/FlowScene.hpp b/include/nodes/internal/FlowScene.hpp index cf2a983cd..ab304201f 100644 --- a/include/nodes/internal/FlowScene.hpp +++ b/include/nodes/internal/FlowScene.hpp @@ -168,6 +168,9 @@ class NODE_EDITOR_PUBLIC FlowScene std::vector anchors; + int gridSize=1; + bool snapping=false; + private: using SharedConnection = std::shared_ptr; diff --git a/src/NodeGraphicsObject.cpp b/src/NodeGraphicsObject.cpp index e87ab5002..45ead5a1c 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -182,14 +182,40 @@ paint(QPainter * painter, NodePainter::paint(painter, _node, _scene); } +int closestMultiple(int n, int x) +{ + int sign = (n < 0) ? -1 : 1; + int absN = std::abs(n); + + if(x>absN) + return x; + + absN = absN + x/2; + absN = absN - (absN%x); + return absN * sign; +} QVariant NodeGraphicsObject:: itemChange(GraphicsItemChange change, const QVariant &value) { + if (change == ItemPositionChange && scene()) { moveConnections(); + QPointF newPos = value.toPointF(); + if(_scene.snapping) + { + int xi = (int)round(newPos.x()); + int yi = (int)round(newPos.y()); + qreal xF = (qreal)closestMultiple(xi, 15 * _scene.gridSize) - 2; + qreal yF = (qreal)closestMultiple(yi, 15 * _scene.gridSize) - 2; + return QPointF(xF, yF); + } + else + { + return newPos; + } } return QGraphicsItem::itemChange(change, value); From 51804f16f42b604dace36bb5e9513196921912f8 Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Wed, 26 Jan 2022 14:38:22 +0000 Subject: [PATCH 22/39] Added Collapsable group --- include/nodes/internal/Connection.hpp | 17 + include/nodes/internal/FlowScene.hpp | 5 + include/nodes/internal/Group.hpp | 33 +- .../nodes/internal/GroupGraphicsObject.hpp | 52 ++- src/Connection.cpp | 63 +++ src/ConnectionGraphicsObject.cpp | 80 +++- src/FlowScene.cpp | 108 ++++- src/FlowView.cpp | 58 ++- src/Group.cpp | 64 +++ src/GroupGraphicsObject.cpp | 380 +++++++++++++++++- 10 files changed, 796 insertions(+), 64 deletions(-) diff --git a/include/nodes/internal/Connection.hpp b/include/nodes/internal/Connection.hpp index c0562d1b9..216f6b395 100644 --- a/include/nodes/internal/Connection.hpp +++ b/include/nodes/internal/Connection.hpp @@ -23,6 +23,7 @@ namespace QtNodes class Node; class NodeData; class ConnectionGraphicsObject; +class Group; /// class NODE_EDITOR_PUBLIC Connection @@ -79,6 +80,10 @@ class NODE_EDITOR_PUBLIC Connection setNodeToPort(Node& node, PortType portType, PortIndex portIndex); + void + setGroup(Group* group, + PortType portType, + PortIndex portIndex); void removeFromNodes() const; @@ -105,8 +110,14 @@ class NODE_EDITOR_PUBLIC Connection Node*& getNode(PortType portType); + Group* + getGroup(PortType portType) const; + PortIndex getPortIndex(PortType portType) const; + + PortIndex + getGroupPortIndex(PortType portType) const; void clearNode(PortType portType); @@ -133,6 +144,12 @@ class NODE_EDITOR_PUBLIC Connection PortIndex _outPortIndex; PortIndex _inPortIndex; + Group* _outGroup = nullptr; + Group* _inGroup = nullptr; + + PortIndex _outGroupPortIndex; + PortIndex _inGroupPortIndex; + private: ConnectionState _connectionState; diff --git a/include/nodes/internal/FlowScene.hpp b/include/nodes/internal/FlowScene.hpp index ab304201f..8ad26daae 100644 --- a/include/nodes/internal/FlowScene.hpp +++ b/include/nodes/internal/FlowScene.hpp @@ -65,6 +65,8 @@ class NODE_EDITOR_PUBLIC FlowScene Node&createNode(std::unique_ptr && dataModel); Group& createGroup(); + + Group& pasteGroup(QJsonObject const& nodeJson, QPointF nodeGroupCentroid, QPointF mousePos); Node&restoreNode(QJsonObject const& nodeJson); @@ -192,4 +194,7 @@ class NODE_EDITOR_PUBLIC FlowScene Node* locateNodeAt(QPointF scenePoint, FlowScene &scene, QTransform viewTransform); +Group* +locateGroupAt(QPointF scenePoint, FlowScene &scene, + QTransform viewTransform); } diff --git a/include/nodes/internal/Group.hpp b/include/nodes/internal/Group.hpp index 78fba58ca..cb3723539 100644 --- a/include/nodes/internal/Group.hpp +++ b/include/nodes/internal/Group.hpp @@ -18,6 +18,8 @@ #include "ConnectionGraphicsObject.hpp" #include "Serializable.hpp" #include "Node.hpp" +#include "NodeDataModel.hpp" + namespace QtNodes { @@ -43,7 +45,9 @@ class NODE_EDITOR_PUBLIC Group _id(QUuid::createUuid()), _groupGraphicsObject(nullptr), _name("New Group") - { } + { + + } void AddNode(std::shared_ptr node) { nodes.push_back(node); @@ -52,7 +56,6 @@ class NODE_EDITOR_PUBLIC Group void setGraphicsObject(std::shared_ptr graphics) { - // _groupGraphicsObject = std::move(graphics); _groupGraphicsObject = graphics; } @@ -72,6 +75,8 @@ class NODE_EDITOR_PUBLIC Group QJsonObject save() const override{ QJsonObject groupJson; groupJson["name"] = _name; + bool collapsed = _groupGraphicsObject->isCollapsed(); + groupJson["collapsed"] = (int)collapsed; QJsonObject posObj; posObj["x"] = _groupGraphicsObject->pos().x(); @@ -79,8 +84,8 @@ class NODE_EDITOR_PUBLIC Group groupJson["position"] = posObj; QJsonObject sizeObj; - sizeObj["x"] = _groupGraphicsObject->getSizeX(); - sizeObj["y"] = _groupGraphicsObject->getSizeY(); + sizeObj["x"] = collapsed ? _groupGraphicsObject->getSavedSizeX() : _groupGraphicsObject->getSizeX(); + sizeObj["y"] = collapsed ? _groupGraphicsObject->getSavedSizeY() : _groupGraphicsObject->getSizeY(); groupJson["size"] = sizeObj; QJsonObject colObj; @@ -91,25 +96,9 @@ class NODE_EDITOR_PUBLIC Group return groupJson; }; - void restore(QJsonObject const &json) override{ - QJsonObject positionJson = json["position"].toObject(); - QPointF point(positionJson["x"].toDouble(), - positionJson["y"].toDouble()); - _groupGraphicsObject->setPos(point); - - QJsonObject sizeJson = json["size"].toObject(); - _groupGraphicsObject->setSizeX(sizeJson["x"].toDouble()); - _groupGraphicsObject->setSizeY(sizeJson["y"].toDouble()); + void restore(QJsonObject const &json) override; - - QJsonObject colorJson = json["color"].toObject(); - _groupGraphicsObject->r = colorJson["r"].toInt(); - _groupGraphicsObject->g = colorJson["g"].toInt(); - _groupGraphicsObject->b = colorJson["b"].toInt(); - - SetName(json["name"].toString()); - _groupGraphicsObject->nameLineEdit->setText(_name); - }; + void restoreAtPosition(QJsonObject const &json, QPointF position); std::shared_ptr _groupGraphicsObject; QString _name; diff --git a/include/nodes/internal/GroupGraphicsObject.hpp b/include/nodes/internal/GroupGraphicsObject.hpp index 90b46cbb0..cb5dc046c 100644 --- a/include/nodes/internal/GroupGraphicsObject.hpp +++ b/include/nodes/internal/GroupGraphicsObject.hpp @@ -3,12 +3,13 @@ #include #include -#include "Connection.hpp" #include "NodeGeometry.hpp" #include "NodeState.hpp" +#include "PortType.hpp" #include - +#include +#include class QGraphicsProxyWidget; namespace QtNodes @@ -17,6 +18,7 @@ namespace QtNodes class FlowScene; class FlowItemEntry; class Group; +class Connection; /// Class reacts on GUI events, mouse clicks and /// forwards painting operation. @@ -40,6 +42,9 @@ class GroupGraphicsObject : public QGraphicsObject QRectF boundingRect() const override; + bool + isCollapsed() {return collapsed;} + void setGeometryChanged(); @@ -48,7 +53,7 @@ class GroupGraphicsObject : public QGraphicsObject void moveConnections() const; - enum { Type = UserType + 3 }; + enum { Type = UserType + 5 }; int type() const override { return Type; } @@ -59,13 +64,34 @@ class GroupGraphicsObject : public QGraphicsObject uint8_t r, g, b; QLineEdit* nameLineEdit; QGraphicsProxyWidget* _proxyWidget; + QGraphicsProxyWidget* _collapseButton; + QPushButton *collapseButtonWidget; int getSizeX() {return sizeX;} int getSizeY() {return sizeY;} + int getSavedSizeX() {return savedSizeX;} + int getSavedSizeY() {return savedSizeY;} void setSizeX(int size) {sizeX = size;} void setSizeY(int size) {sizeY = size;} + void Collapse(); + + QPointF portScenePosition(int i, PortType type) const; + + QPointF portPosition(int i, PortType type) const; + + int + checkHitScenePoint(PortType portType, + QPointF const point, + QTransform t = QTransform()) const; + + + std::array, 3> inOutNodes; + std::array, 3> inOutPorts; + + //Store pointers to connections from in and out. + std::array, 3> inOutConnections; protected: void paint(QPainter* painter, @@ -108,7 +134,25 @@ class GroupGraphicsObject : public QGraphicsObject bool isResizingXY=false; int sizeX=1000; - int sizeY=1000; + int sizeY=1000; + + + float inputSize = 10; + float spacing = 40; + float topPadding = 30; + bool collapsed=false; + int savedSizeX, savedSizeY; + + //Store connections that stay strictly inside group's bound, to set them invisible when collapsing + std::vector unusedConnections; + + //Store the labels of the in and out when collapsed + std::array, 3> inOutLabels; + + + +signals: + void CollapseTriggered(bool state); }; } diff --git a/src/Connection.cpp b/src/Connection.cpp index 93e18c1fa..72ab154a7 100644 --- a/src/Connection.cpp +++ b/src/Connection.cpp @@ -14,6 +14,8 @@ #include "NodeGraphicsObject.hpp" #include "NodeDataModel.hpp" +#include "Group.hpp" + #include "ConnectionState.hpp" #include "ConnectionGeometry.hpp" #include "ConnectionGraphicsObject.hpp" @@ -27,6 +29,7 @@ using QtNodes::NodeData; using QtNodes::NodeDataType; using QtNodes::ConnectionGraphicsObject; using QtNodes::ConnectionGeometry; +using QtNodes::Group; Connection:: Connection(PortType portType, @@ -212,6 +215,30 @@ getPortIndex(PortType portType) const return result; } +PortIndex +Connection:: +getGroupPortIndex(PortType portType) const +{ + PortIndex result = INVALID; + + switch (portType) + { + case PortType::In: + result = _inGroupPortIndex; + break; + + case PortType::Out: + result = _outGroupPortIndex; + + break; + + default: + break; + } + + return result; +} + void Connection:: @@ -233,6 +260,21 @@ setNodeToPort(Node& node, updated(*this); } +void +Connection:: +setGroup(Group* group, PortType portType, PortIndex portIndex) +{ + if(portType==PortType::In) { + _inGroup = group; + _inGroupPortIndex = portIndex; + } + else + { + _outGroup = group; + _outGroupPortIndex = portIndex; + } +} + void Connection:: @@ -307,6 +349,27 @@ getNode(PortType portType) const return nullptr; } +Group* +Connection:: +getGroup(PortType portType) const +{ + switch (portType) + { + case PortType::In: + return _inGroup; + break; + + case PortType::Out: + return _outGroup; + break; + + default: + // not possible + break; + } + return nullptr; +} + Node*& Connection:: diff --git a/src/ConnectionGraphicsObject.cpp b/src/ConnectionGraphicsObject.cpp index a0b86358c..a07d7de28 100644 --- a/src/ConnectionGraphicsObject.cpp +++ b/src/ConnectionGraphicsObject.cpp @@ -15,12 +15,15 @@ #include "ConnectionBlurEffect.hpp" #include "NodeGraphicsObject.hpp" +#include "GroupGraphicsObject.hpp" #include "NodeConnectionInteraction.hpp" #include "Node.hpp" +#include "Group.hpp" using QtNodes::ConnectionGraphicsObject; +using QtNodes::GroupGraphicsObject; using QtNodes::Connection; using QtNodes::FlowScene; @@ -129,8 +132,49 @@ move() } }; - moveEndPoint(PortType::In); - moveEndPoint(PortType::Out); + auto MoveEndPointGroup = + [this] (PortType portType) + { + if (auto group = _connection.getGroup(portType)) + { + GroupGraphicsObject const &groupGraphics = group->groupGraphicsObject(); + + QPointF scenePos = groupGraphics.portScenePosition(_connection.getGroupPortIndex(portType), + portType); + + { + //localToScene transform + QTransform sceneTransform = this->sceneTransform(); + + //Map from scene position to local position + QPointF connectionPos = sceneTransform.inverted().map(scenePos); + _connection.connectionGeometry().setEndPoint(portType, + connectionPos); + + _connection.getConnectionGraphicsObject().setGeometryChanged(); + _connection.getConnectionGraphicsObject().update(); + } + } + }; + + if(_connection.getGroup(PortType::In)!=nullptr) + { + MoveEndPointGroup(PortType::In); + } + else + { + moveEndPoint(PortType::In); + } + + if(_connection.getGroup(PortType::Out)!=nullptr) + { + MoveEndPointGroup(PortType::Out); + } + else + { + moveEndPoint(PortType::Out); + } + } void ConnectionGraphicsObject::lock(bool locked) @@ -218,11 +262,39 @@ mouseReleaseEvent(QGraphicsSceneMouseEvent* event) if (node && interaction.tryConnect()) { node->resetReactionToConnection(); + return; } - else if (_connection.connectionState().requiresPort()) + + // Get node in the group it's connected to + auto group = locateGroupAt(event->scenePos(), _scene, _scene.views()[0]->transform()); + if(group != nullptr) { + int hitPoint = group->groupGraphicsObject().checkHitScenePoint(PortType::In, + event->scenePos(), + this->sceneTransform()); + + if(hitPoint>=0) + { + Node *outNode = group->groupGraphicsObject().inOutNodes[(int)PortType::In][hitPoint]; + int outPort = group->groupGraphicsObject().inOutPorts[(int)PortType::In][hitPoint]; + Node *inNode = _connection.getNode(PortType::Out); + int inPort = _connection.getPortIndex(PortType::Out); + + _scene.createConnection(*outNode, outPort, *inNode, inPort); + + //Expands + group->groupGraphicsObject().Collapse(); + + //Collapses back + group->groupGraphicsObject().Collapse(); + _scene.deleteConnection(_connection); + return; + } + } - _scene.deleteConnection(_connection); + if (_connection.connectionState().requiresPort()) + { + _scene.deleteConnection(_connection); } } diff --git a/src/FlowScene.cpp b/src/FlowScene.cpp index bac90197f..15f777ad7 100644 --- a/src/FlowScene.cpp +++ b/src/FlowScene.cpp @@ -208,6 +208,8 @@ Group& FlowScene::createGroup() { } + + Group& FlowScene:: restoreGroup(QJsonObject const& nodeJson) { @@ -223,6 +225,41 @@ restoreGroup(QJsonObject const& nodeJson) { group->restore(nodeJson); + + return *groupPtr; +} + +Group& +FlowScene:: +pasteGroup(QJsonObject const& groupJson, QPointF nodeGroupCentroid, QPointF mousePos) { + auto group = std::make_shared(*this); + auto ggo = std::make_shared(*this, *group); + + QUuid id = group->id(); + auto groupPtr = group.get(); + + group->setGraphicsObject(ggo); + _groups[id] = group; + + QJsonObject positionJson = groupJson["position"].toObject(); + QPointF point(positionJson["x"].toDouble(), + positionJson["y"].toDouble()); + QPointF pos = mousePos + (point - nodeGroupCentroid); + group->restoreAtPosition(groupJson, pos); + + + + // group->groupGraphicsObject().setPos(pos); + + // Group &groupRef = *(group); + // resolveGroups(groupRef); + + // bool collapsed = (bool)groupJson["collapsed"].toInt(); + // if(collapsed) + // { + // group->groupGraphicsObject().Collapse(); + // } + return *groupPtr; } @@ -271,9 +308,6 @@ QUuid FlowScene::pasteNode(QJsonObject &nodeJson, QPointF nodeGroupCentroid, QPo QUuid newId = QUuid::createUuid(); node->paste(nodeJson, newId); - QPointF offset = node->nodeGraphicsObject().pos() - nodeGroupCentroid; - - QPointF pos = mousePos + (node->nodeGraphicsObject().pos() - nodeGroupCentroid); node->nodeGraphicsObject().setPos(pos); @@ -315,8 +349,22 @@ removeNode(Node& node) void FlowScene:: removeGroup(Group& group) -{ - _groups.erase(group.id()); +{ + + GroupGraphicsObject &ggo = group.groupGraphicsObject(); + for(int i=ggo.childItems().size()-1; i>=0; i--) + { + QGraphicsItem *child = ggo.childItems()[i]; + NodeGraphicsObject* n = qgraphicsitem_cast(child); + if(n != nullptr) + { + QPointF position = n->scenePos(); + ggo.childItems()[i]->setParentItem(0); + n->setPos(position); + n->moveConnections(); + } + } + _groups.erase(group.id()); } @@ -460,6 +508,8 @@ getNodeSize(const Node& node) const void FlowScene:: resolveGroups(Group& group) { + if(group.groupGraphicsObject().isCollapsed()) return; + GroupGraphicsObject& ggo = group.groupGraphicsObject(); QRectF groupRect = ggo.mapRectToScene(ggo.boundingRect()); @@ -729,12 +779,6 @@ loadFromMemory(const QByteArray& data) { QJsonObject const jsonDocument = QJsonDocument::fromJson(data).object(); - QJsonArray groupsJsonArray = jsonDocument["groups"].toArray(); - for (int i = 0; i < groupsJsonArray.size(); ++i) - { - restoreGroup(groupsJsonArray[i].toObject()); - } - QJsonArray nodesJsonArray = jsonDocument["nodes"].toArray(); for (int i = 0; i < nodesJsonArray.size(); ++i) @@ -749,6 +793,13 @@ loadFromMemory(const QByteArray& data) restoreConnection(connectionJsonArray[i].toObject()); } + QJsonArray groupsJsonArray = jsonDocument["groups"].toArray(); + for (int i = 0; i < groupsJsonArray.size(); ++i) + { + restoreGroup(groupsJsonArray[i].toObject()); + } + + if(jsonDocument.contains("anchors")) { QJsonArray anchorsJsonArray = jsonDocument["anchors"].toArray(); for(int i=0; i items = + scene.items(scenePoint, + Qt::IntersectsItemShape, + Qt::DescendingOrder, + viewTransform); + + //// items convertable to GroupGraphicsObject + std::vector filteredItems; + + std::copy_if(items.begin(), + items.end(), + std::back_inserter(filteredItems), + [] (QGraphicsItem * item) + { + return (dynamic_cast(item) != nullptr); + }); + + Group* resultGroup = nullptr; + + if (!filteredItems.empty()) + { + QGraphicsItem* graphicsItem = filteredItems.front(); + auto ggo = dynamic_cast(graphicsItem); + + resultGroup = &ggo->group(); + } + + return resultGroup; +} } diff --git a/src/FlowView.cpp b/src/FlowView.cpp index a718de113..c926d0f0e 100644 --- a/src/FlowView.cpp +++ b/src/FlowView.cpp @@ -284,7 +284,6 @@ contextMenuEvent(QContextMenuEvent *event) } QString parent = item->parent()->data(0, Qt::UserRole).toString(); - std::cout << parent.toStdString() << std::endl; if(parent == "Templates") { DataModelRegistry::RegisteredTemplatesMap map = _scene->registry().RegisteredTemplates(); @@ -412,35 +411,25 @@ void FlowView:: deleteSelectedNodes() { - - std::cout << "DELETEGROUP 0" << _scene->selectedItems().size() << std::endl; for (QGraphicsItem * item : _scene->selectedItems()) { - std::cout << "DELETEGROUP 1" << std::endl; if(item) { - std::cout << "DELETEGROUP 2" << std::endl; if (auto c = qgraphicsitem_cast(item)) { - std::cout << "DELETEGROUP 5" << std::endl; _scene->removeGroup(c->group()); } else if (auto c = qgraphicsitem_cast(item)) { - std::cout << "DELETEGROUP 3" << std::endl; _scene->removeNode(c->node()); } else if (auto c = qgraphicsitem_cast(item)) { - std::cout << "DELETEGROUP 4" << std::endl; _scene->deleteConnection(c->connection()); } } - } - - std::cout << "DELETEGROUP 6" << std::endl; + } _scene->UpdateHistory(); - std::cout << "DELETEGROUP 7" << std::endl; } void FlowView::copySelectedNodes() { @@ -504,6 +493,12 @@ void FlowView::jsonToScene(QJsonObject jsonDocument) _scene->pasteConnection(connectionJsonArray[i].toObject(), newIn, newOut ); } + QJsonArray groupsJsonArray = jsonDocument["groups"].toArray(); + for (int i = 0; i < groupsJsonArray.size(); ++i) + { + _scene->pasteGroup(groupsJsonArray[i].toObject(), centroid, posViewMouse); + } + _scene->UpdateHistory(); } @@ -538,9 +533,48 @@ QJsonObject FlowView::selectionToJson() } } } + + QJsonArray groupJsonArray; + for (QGraphicsItem * item : _scene->selectedItems()) + { + if (auto c = qgraphicsitem_cast(item)) { + Group& group = c->group(); + + //If collapsed + if(group.groupGraphicsObject().isCollapsed()) + { + //Add all child items to nodes array + for(int i=0; i(group.groupGraphicsObject().childItems()[i]); + if(ngo!=nullptr) + { + Node& node = ngo->node(); + nodesJsonArray.append(node.save()); + } + } + + //Add all connections to nodes array + for(int i=0; isave(); + if (!connectionJson.isEmpty()) + connectionJsonArray.append(connectionJson); + } + } + } + + QJsonObject groupJson = group.save(); + if (!groupJson.isEmpty()) + groupJsonArray.append(groupJson); + } + } sceneJson["nodes"] = nodesJsonArray; sceneJson["connections"] = connectionJsonArray; + sceneJson["groups"] = groupJsonArray; return sceneJson; } diff --git a/src/Group.cpp b/src/Group.cpp index 45f15b72e..5c50653df 100644 --- a/src/Group.cpp +++ b/src/Group.cpp @@ -1,5 +1,6 @@ #include "Group.hpp" #include "GroupGraphicsObject.hpp" +#include "FlowScene.hpp" using QtNodes::Group; @@ -23,6 +24,69 @@ GetName() { } +void +Group:: +restore(QJsonObject const &json){ + QJsonObject positionJson = json["position"].toObject(); + QPointF point(positionJson["x"].toDouble(), + positionJson["y"].toDouble()); + _groupGraphicsObject->setPos(point); + + QJsonObject sizeJson = json["size"].toObject(); + _groupGraphicsObject->setSizeX(sizeJson["x"].toDouble()); + _groupGraphicsObject->setSizeY(sizeJson["y"].toDouble()); + + + QJsonObject colorJson = json["color"].toObject(); + _groupGraphicsObject->r = colorJson["r"].toInt(); + _groupGraphicsObject->g = colorJson["g"].toInt(); + _groupGraphicsObject->b = colorJson["b"].toInt(); + + SetName(json["name"].toString()); + _groupGraphicsObject->nameLineEdit->setText(_name); + + + Group &groupRef = *(this); + _scene.resolveGroups(groupRef); + + bool collapsed = (bool)json["collapsed"].toInt(); + if(collapsed) + { + _groupGraphicsObject->Collapse(); + } +} + +void +Group:: +restoreAtPosition(QJsonObject const &json, QPointF position){ + _groupGraphicsObject->setPos(position); + + QJsonObject sizeJson = json["size"].toObject(); + _groupGraphicsObject->setSizeX(sizeJson["x"].toDouble()); + _groupGraphicsObject->setSizeY(sizeJson["y"].toDouble()); + + + QJsonObject colorJson = json["color"].toObject(); + _groupGraphicsObject->r = colorJson["r"].toInt(); + _groupGraphicsObject->g = colorJson["g"].toInt(); + _groupGraphicsObject->b = colorJson["b"].toInt(); + + SetName(json["name"].toString()); + _groupGraphicsObject->nameLineEdit->setText(_name); + + // "Problem : when restoring the group, it doesn't see the nodes that are within it." + + Group &groupRef = *(this); + _scene.resolveGroups(groupRef); + + bool collapsed = (bool)json["collapsed"].toInt(); + if(collapsed) + { + _groupGraphicsObject->Collapse(); + } +}; + + // GroupGraphicsObject& Group::groupGraphicsObject(){ // return *_groupGraphicsObject.get(); // } diff --git a/src/GroupGraphicsObject.cpp b/src/GroupGraphicsObject.cpp index 2d981ebb2..eccb00031 100644 --- a/src/GroupGraphicsObject.cpp +++ b/src/GroupGraphicsObject.cpp @@ -8,6 +8,7 @@ #include "ConnectionGraphicsObject.hpp" #include "ConnectionState.hpp" +#include "Connection.hpp" #include "FlowScene.hpp" #include "NodePainter.hpp" @@ -22,6 +23,8 @@ using QtNodes::GroupGraphicsObject; using QtNodes::Node; using QtNodes::Group; using QtNodes::FlowScene; +using QtNodes::Connection; +using QtNodes::PortType; GroupGraphicsObject:: GroupGraphicsObject(FlowScene &scene, Group& group) @@ -46,6 +49,7 @@ GroupGraphicsObject(FlowScene &scene, Group& group) r = g = b = 135; _proxyWidget = new QGraphicsProxyWidget(this); + _collapseButton = new QGraphicsProxyWidget(this); QString name = group.GetName(); nameLineEdit = new QLineEdit(name); @@ -70,7 +74,260 @@ GroupGraphicsObject(FlowScene &scene, Group& group) }); _proxyWidget->setWidget(nameLineEdit); - _proxyWidget->setPos(pos() + QPointF(sizeX/2 - _proxyWidget->size().width()/2, 0)); + _proxyWidget->setPos(QPointF(sizeX/2 - _proxyWidget->size().width()/2, 0)); + + collapseButtonWidget = new QPushButton("X"); + collapseButtonWidget->setCheckable(true); + _collapseButton->setWidget(collapseButtonWidget); + _collapseButton->setPos(QPointF(sizeX - _collapseButton->size().width(), 0)); + + connect(collapseButtonWidget, &QPushButton::clicked, this, [this](int state){ + Collapse(); + }); +} + +void +GroupGraphicsObject:: +Collapse() +{ + unusedConnections.clear(); + + ////1. Identify all the nodes that have external inputs + inOutLabels[(int)PortType::In].clear(); + inOutConnections[(int)PortType::In].clear(); + inOutNodes[(int)PortType::In].clear(); + inOutPorts[(int)PortType::In].clear(); + //for all the nodes in the group + for (int i = 0; i < childItems().size(); i++) + { + NodeGraphicsObject* nodeGO = qgraphicsitem_cast(childItems()[i]); + if (nodeGO != nullptr) + { + Node &node = nodeGO->node(); + int numEntries = node.nodeState().getEntries(PortType::In).size(); + + //For each input of the current node + for (int j = 0; j < numEntries; j++) + { + NodeState::ConnectionPtrSet connections = node.nodeState().connections(PortType::In, j); + //Get the connection + for (std::pair connectionPair : connections) + { + Connection *connection = connectionPair.second; + Node *sourceNode = connection->getNode(PortType::Out); + Node *destNode = connection->getNode(PortType::In); + NodeGraphicsObject &sourceNodeGO = sourceNode->nodeGraphicsObject(); + + //Check if the the input is inside the group + if (childItems().indexOf(&sourceNodeGO) == -1) { //if it's not inside + inOutLabels[(int)PortType::In].push_back( + node.nodeDataModel()->name() + "." + node.nodeDataModel()->portCaption(PortType::In, j) + ); + inOutConnections[(int)PortType::In].push_back(connection); + inOutNodes[(int)PortType::In].push_back(destNode); + inOutPorts[(int)PortType::In].push_back(j); + } + else { //it's inside + unusedConnections.push_back(connection); + } + } + } + } + } + + ////2. Identify all the nodes that have external outputs + inOutLabels[(int)PortType::Out].clear(); + inOutConnections[(int)PortType::Out].clear(); + inOutNodes[(int)PortType::Out].clear(); + inOutPorts[(int)PortType::Out].clear(); + //for all the nodes in the group + for (int i = 0; i < childItems().size(); i++) + { + NodeGraphicsObject* nodeGO = qgraphicsitem_cast(childItems()[i]); + if (nodeGO != nullptr) + { + Node &node = nodeGO->node(); + int numOutputs = node.nodeState().getEntries(PortType::Out).size(); + for (int j = 0; j < numOutputs; j++) + { + NodeState::ConnectionPtrSet connections = node.nodeState().connections(PortType::Out, j); + for (std::pair connectionPair : connections) + { + Connection *connection = connectionPair.second; + Node *destNode = connection->getNode(PortType::In); + NodeGraphicsObject &destNodeGO = destNode->nodeGraphicsObject(); + + if (childItems().indexOf(&destNodeGO) == -1) { + inOutLabels[(int)PortType::Out].push_back( + node.nodeDataModel()->name() + "." + node.nodeDataModel()->portCaption(PortType::Out, j) + ); + inOutConnections[(int)PortType::Out].push_back(connection); + inOutNodes[(int)PortType::Out].push_back(destNode); + inOutPorts[(int)PortType::Out].push_back(j); + } + else { + unusedConnections.push_back(connection); + } + } + } + } + } + + + if(!collapsed) + { + int numInOut = std::max(inOutLabels[(int)PortType::In].size(), inOutLabels[(int)PortType::Out].size()); + + savedSizeX = sizeX; + savedSizeY = sizeY; + + //Do resize + sizeX = 500; + sizeY = numInOut * spacing; + _proxyWidget->setPos(QPointF(sizeX/2 - _proxyWidget->size().width()/2, 0)); + _collapseButton->setPos(QPointF(sizeX - _collapseButton->size().width(), 0)); + + + //Sets the inside nodes invisible + for(int i=0; i(childItems()[i]); + if(ngo!=nullptr) + { + childItems()[i]->setVisible(false); + } + } + + //Sets the inside connections invisible + for(int i=0; igetConnectionGraphicsObject().setVisible(false); + } + + //Change the input connection positions + for(int i=0;igetConnectionGraphicsObject().sceneTransform().inverted().map(position); + inOutConnections[(int)PortType::In][i]->connectionGeometry().setEndPoint(PortType::In, connectionPos); + + + Group &thisGroup = group(); + inOutConnections[(int)PortType::In][i]->setGroup(&thisGroup, PortType::In, i); + } + + //Change the output connection positions + for(int i=0;igetConnectionGraphicsObject().sceneTransform().inverted().map(position); + inOutConnections[(int)PortType::Out][i]->connectionGeometry().setEndPoint(PortType::Out, connectionPos); + + Group &thisGroup = group(); + inOutConnections[(int)PortType::Out][i]->setGroup(&thisGroup, PortType::Out, i); + } + + collapsed=true; + } + else + { + sizeX = savedSizeX; + sizeY = savedSizeY; + + _proxyWidget->setPos(QPointF(sizeX/2 - _proxyWidget->size().width()/2, 0)); + _collapseButton->setPos(QPointF(sizeX - _collapseButton->size().width(), 0)); + + //Sets the inside nodes invisible + for(int i=0; i(childItems()[i]); + if(ngo!=nullptr) + { + childItems()[i]->setVisible(true); + } + } + + for(int i=0; igetConnectionGraphicsObject().setVisible(true); + } + + for(int i=0; isetGroup(nullptr, PortType::In, 0); + } + + for(int i=0; isetGroup(nullptr, PortType::Out, 0); + } + + collapsed=false; + unusedConnections.clear();; + inOutLabels[(int)PortType::In].clear(); + inOutLabels[(int)PortType::Out].clear(); + inOutConnections[(int)PortType::In].clear(); + inOutConnections[(int)PortType::Out].clear(); + moveConnections(); + } +} + +QPointF GroupGraphicsObject::portScenePosition(int i, PortType type) const +{ + + if(type == PortType::In) + { + return scenePos() +QPointF(inputSize, topPadding + i * spacing); + } + else + { + return scenePos() +QPointF(sizeX - inputSize, topPadding + i * spacing); + } +} + +QPointF GroupGraphicsObject::portPosition(int i, PortType type) const +{ + + if(type == PortType::In) + { + return QPointF(inputSize, topPadding + i * spacing); + } + else + { + return QPointF(sizeX - inputSize, topPadding + i * spacing); + } +} + +int GroupGraphicsObject::checkHitScenePoint(PortType portType, + QPointF const scenePoint, + QTransform sceneTransform) const +{ + auto const &nodeStyle = StyleCollection::nodeStyle(); + + PortIndex result = INVALID; + + if (portType == PortType::None) + return result; + + double const tolerance = 2.0 * nodeStyle.ConnectionPointDiameter; + + size_t const nItems = inOutLabels[(int)portType].size(); + + for (size_t i = 0; i < nItems; ++i) + { + auto pp = portScenePosition(i, portType); + + QPointF p = pp - scenePoint; + auto distance = std::sqrt(QPointF::dotProduct(p, p)); + + if (distance < tolerance) + { + result = PortIndex(i); + break; + } + } + + return result; } @@ -133,11 +390,50 @@ paint(QPainter * painter, QStyleOptionGraphicsItem const* option, QWidget* ) { - painter->setClipRect(option->exposedRect); - QRect rect(0, 0, sizeX, sizeY); - auto color = QColor(r, g, b, 255); - painter->drawRect(rect); - painter->fillRect(rect, QBrush(color)); + painter->setClipRect(option->exposedRect); + QRect rect(0, 0, sizeX, sizeY); + auto color = QColor(r, g, b, 255); + painter->drawRect(rect); + painter->fillRect(rect, QBrush(color)); + + QFontMetrics const & metrics = + painter->fontMetrics(); + + auto drawPoints = + [&](PortType portType) + { + size_t n = inOutLabels[(int)portType].size(); + for (size_t i = 0; i < n; ++i) + { + QPointF p = portPosition(i, portType); + + painter->setPen(QColor(255, 255, 255, 255)); + + QString s = inOutLabels[(int)portType][i]; + + auto rect = metrics.boundingRect(s); + + p.setY(p.y() + rect.height() / 4.0); + switch (portType) + { + case PortType::In: + p.setX(inputSize + 5.0); + break; + + case PortType::Out: + p.setX(sizeX - 5.0 - rect.width() - inputSize); + break; + + default: + break; + } + + painter->drawText(p, s); + } + }; + + drawPoints(PortType::Out); + drawPoints(PortType::In); } @@ -153,7 +449,6 @@ void GroupGraphicsObject:: mousePressEvent(QGraphicsSceneMouseEvent * event) { - std::cout << "OEFIJWOEFJW " << std::endl; if(QApplication::keyboardModifiers().testFlag(Qt::ShiftModifier)) { event->ignore(); @@ -178,6 +473,66 @@ mousePressEvent(QGraphicsSceneMouseEvent * event) } else if (abs(mousePos.y() - sizeY) < 20) { isResizingY=true; } + + auto clickPort = + [&](PortType portToCheck) + { + + // TODO do not pass sceneTransform + int portIndex = checkHitScenePoint(portToCheck, + event->scenePos(), + sceneTransform()); + if (portIndex != INVALID) + { + // start dragging existing connection + if (portToCheck == PortType::In) + { + auto con = inOutConnections[(int)PortType::In][portIndex]; + Node *currentNode = inOutConnections[(int)PortType::In][portIndex]->getNode(PortType::In); + Node ¤tNodeRef = *currentNode; + NodeConnectionInteraction interaction(currentNodeRef, *con, _scene); + interaction.disconnect(portToCheck); + } + else // initialize new Connection + { + //Get the node from which the connection is created + Node *currentNode = inOutConnections[(int)PortType::Out][portIndex]->getNode(PortType::Out); + int nodePortIndex = inOutConnections[(int)PortType::Out][portIndex]->getPortIndex(PortType::Out); + Node ¤tNodeRef = *currentNode; + + //Create the group connection + auto connection = _scene.createConnection(portToCheck, + currentNodeRef, + nodePortIndex); + + //Set connection position + QPointF position = portScenePosition(portIndex, PortType::Out); + QPointF connectionPos = connection->getConnectionGraphicsObject().sceneTransform().inverted().map(position); + connection->connectionGeometry().setEndPoint(PortType::In, connectionPos); + connection->connectionGeometry().setEndPoint(PortType::Out, connectionPos); + + //Set the group to the connection + Group &thisGroup = group(); + connection->setGroup(&thisGroup, PortType::Out, portIndex); + + + //Add to the connections + inOutConnections[(int)PortType::Out].push_back(connection.get()); + + //Set the connection to the node + currentNodeRef.nodeState().setConnection(portToCheck, + nodePortIndex, + *connection); + + + connection->getConnectionGraphicsObject().grabMouse(); + connection->getConnectionGraphicsObject().move(); + } + } + }; + + clickPort(PortType::In); + clickPort(PortType::Out); } @@ -190,6 +545,8 @@ mouseMoveEvent(QGraphicsSceneMouseEvent * event) prepareGeometryChange(); sizeX += diff; update(); + _proxyWidget->setPos(QPointF(sizeX/2 - _proxyWidget->size().width()/2, 0)); + _collapseButton->setPos(QPointF(sizeX - _collapseButton->size().width(), 0)); event->accept(); } else if(isResizingY) { @@ -197,7 +554,8 @@ mouseMoveEvent(QGraphicsSceneMouseEvent * event) prepareGeometryChange(); sizeY += diff; update(); - _proxyWidget->setPos(pos() + QPointF(sizeX/2 - _proxyWidget->size().width()/2, 0)); + _proxyWidget->setPos(QPointF(sizeX/2 - _proxyWidget->size().width()/2, 0)); + _collapseButton->setPos(QPointF(sizeX - _collapseButton->size().width(), 0)); event->accept(); } else if(isResizingXY) { auto diff = event->pos() - event->lastPos(); @@ -205,7 +563,8 @@ mouseMoveEvent(QGraphicsSceneMouseEvent * event) sizeX += diff.x(); sizeY += diff.y(); update(); - _proxyWidget->setPos(pos() + QPointF(sizeX/2 - _proxyWidget->size().width()/2, 0)); + _proxyWidget->setPos(QPointF(sizeX/2 - _proxyWidget->size().width()/2, 0)); + _collapseButton->setPos(QPointF(sizeX - _collapseButton->size().width(), 0)); event->accept(); } else { _scene.groupMoved(_group, pos()); @@ -243,7 +602,6 @@ void GroupGraphicsObject:: hoverLeaveEvent(QGraphicsSceneHoverEvent * event) { - // std::cout << "hverleave" << std::endl; update(); event->accept(); } @@ -277,7 +635,7 @@ GroupGraphicsObject:: mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) { _scene.groupDoubleClicked(group()); - // std::cout << "doubleclick" << std::endl; + QGraphicsItem::mouseDoubleClickEvent(event); } From d198287475b2c399a0802f0bf3e5179b07b9b5e5 Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Mon, 8 Aug 2022 10:14:15 +0100 Subject: [PATCH 23/39] Update view on geometry --- include/nodes/internal/FlowScene.hpp | 1 + include/nodes/internal/Node.hpp | 6 +++++ include/nodes/internal/NodeGeometry.hpp | 1 + include/nodes/internal/NodeGraphicsObject.hpp | 3 +++ include/nodes/internal/NodeState.hpp | 13 +++++++++- src/FlowScene.cpp | 15 ++++++++++-- src/FlowView.cpp | 14 +++++++---- src/Node.cpp | 24 +++++++++++++++++++ src/NodeGeometry.cpp | 8 +++++++ src/NodeGraphicsObject.cpp | 8 +++++++ src/NodeState.cpp | 20 +++++++++++++++- 11 files changed, 105 insertions(+), 8 deletions(-) diff --git a/include/nodes/internal/FlowScene.hpp b/include/nodes/internal/FlowScene.hpp index 8ad26daae..c1829b5ca 100644 --- a/include/nodes/internal/FlowScene.hpp +++ b/include/nodes/internal/FlowScene.hpp @@ -61,6 +61,7 @@ class NODE_EDITOR_PUBLIC FlowScene std::shared_ptrrestoreConnection(QJsonObject const &connectionJson); void deleteConnection(Connection& connection); + void deleteConnection(Connection* connection); Node&createNode(std::unique_ptr && dataModel); diff --git a/include/nodes/internal/Node.hpp b/include/nodes/internal/Node.hpp index 78bdc5017..de1169622 100644 --- a/include/nodes/internal/Node.hpp +++ b/include/nodes/internal/Node.hpp @@ -56,6 +56,11 @@ class NODE_EDITOR_PUBLIC Node paste(QJsonObject const &json, QUuid ID); + void + updateView(); + + void + eraseInputAtIndex(int portIndex); public: @@ -97,6 +102,7 @@ class NODE_EDITOR_PUBLIC Node NodeDataModel* nodeDataModel() const; + public slots: // data propagation /// Propagates incoming data to the underlying model. diff --git a/include/nodes/internal/NodeGeometry.hpp b/include/nodes/internal/NodeGeometry.hpp index 062459020..5ee5f8c16 100644 --- a/include/nodes/internal/NodeGeometry.hpp +++ b/include/nodes/internal/NodeGeometry.hpp @@ -73,6 +73,7 @@ class NODE_EDITOR_PUBLIC NodeGeometry setDraggingPosition(QPointF const& pos) { _draggingPos = pos; } + void recalculateInOut(); public: QRectF diff --git a/include/nodes/internal/NodeGraphicsObject.hpp b/include/nodes/internal/NodeGraphicsObject.hpp index fe87c6c80..02bba6d2d 100644 --- a/include/nodes/internal/NodeGraphicsObject.hpp +++ b/include/nodes/internal/NodeGraphicsObject.hpp @@ -35,6 +35,9 @@ class NodeGraphicsObject : public QGraphicsObject Node const& node() const; + FlowScene& + flowScene(); + QRectF boundingRect() const override; diff --git a/include/nodes/internal/NodeState.hpp b/include/nodes/internal/NodeState.hpp index fc1b175f1..b1071ad10 100644 --- a/include/nodes/internal/NodeState.hpp +++ b/include/nodes/internal/NodeState.hpp @@ -45,7 +45,7 @@ class NodeState ConnectionPtrSet connections(PortType portType, PortIndex portIndex) const; - + void setConnection(PortType portType, PortIndex portIndex, @@ -56,6 +56,11 @@ class NodeState PortIndex portIndex, QUuid id); + + + void + eraseInputAtIndex(PortIndex portIndex); + ReactToConnectionState reaction() const; @@ -81,6 +86,9 @@ class NodeState bool resizing() const; + void + updateEntries(); + private: std::vector _inConnections; @@ -90,6 +98,9 @@ class NodeState PortType _reactingPortType; NodeDataType _reactingDataType; + + std::unique_ptr const &_model; + bool _resizing; }; } diff --git a/src/FlowScene.cpp b/src/FlowScene.cpp index 15f777ad7..e98cf9ca6 100644 --- a/src/FlowScene.cpp +++ b/src/FlowScene.cpp @@ -177,6 +177,15 @@ deleteConnection(Connection& connection) _connections.erase(connection.id()); } +void +FlowScene:: +deleteConnection(Connection* connection) +{ + // connectionDeleted(connection); + connection->removeFromNodes(); + _connections.erase(connection->id()); +} + Node& FlowScene:: @@ -280,14 +289,16 @@ restoreNode(QJsonObject const& nodeJson) auto node = std::make_shared(std::move(dataModel)); auto ngo = std::make_unique(*this, *node); node->setGraphicsObject(std::move(ngo)); + + auto nodePtr = node.get(); + nodeCreated(*nodePtr); + node->restore(nodeJson); - auto nodePtr = node.get(); _nodes[node->id()] = std::move(node); resolveGroups(*nodePtr); - nodeCreated(*nodePtr); return *nodePtr; } diff --git a/src/FlowView.cpp b/src/FlowView.cpp index c926d0f0e..e7f52815f 100644 --- a/src/FlowView.cpp +++ b/src/FlowView.cpp @@ -411,6 +411,16 @@ void FlowView:: deleteSelectedNodes() { + for (QGraphicsItem * item : _scene->selectedItems()) + { + if(item) + { + if (auto c = qgraphicsitem_cast(item)) + { + _scene->deleteConnection(c->connection()); + } + } + } for (QGraphicsItem * item : _scene->selectedItems()) { if(item) @@ -423,10 +433,6 @@ deleteSelectedNodes() { _scene->removeNode(c->node()); } - else if (auto c = qgraphicsitem_cast(item)) - { - _scene->deleteConnection(c->connection()); - } } } _scene->UpdateHistory(); diff --git a/src/Node.cpp b/src/Node.cpp index 93d627dfc..c0bc6aa91 100644 --- a/src/Node.cpp +++ b/src/Node.cpp @@ -11,6 +11,7 @@ #include "ConnectionGraphicsObject.hpp" #include "ConnectionState.hpp" +#include using QtNodes::Node; using QtNodes::NodeGeometry; @@ -76,6 +77,29 @@ QJsonObject Node::copyWithNewID(QUuid newId) const } +void +Node:: +updateView() +{ + nodeGeometry().recalculateInOut(); + nodeState().updateEntries(); + + nodeGraphicsObject().update(); + // QGraphicsView *view = nodeGraphicsObject().scene()->views().first(); + // view->viewport()->repaint(); +} + + +void +Node:: +eraseInputAtIndex(int portIndex) +{ + std::unordered_map connections = nodeState().connections(PortType::In, portIndex); + for (auto& connection : connections) { + nodeGraphicsObject().flowScene().deleteConnection(connection.second); + } +} + void Node:: diff --git a/src/NodeGeometry.cpp b/src/NodeGeometry.cpp index 0c8d5318f..6875c1753 100644 --- a/src/NodeGeometry.cpp +++ b/src/NodeGeometry.cpp @@ -39,6 +39,14 @@ NodeGeometry(std::unique_ptr const &dataModel) } +void +NodeGeometry:: +recalculateInOut() +{ + _nSources = _dataModel->nPorts(PortType::Out); + _nSinks = _dataModel->nPorts(PortType::In); +} + QRectF NodeGeometry:: entryBoundingRect() const diff --git a/src/NodeGraphicsObject.cpp b/src/NodeGraphicsObject.cpp index 45ead5a1c..35fbc738f 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -88,6 +88,14 @@ node() } +FlowScene& +NodeGraphicsObject:: +flowScene() +{ + return _scene; +} + + Node const& NodeGraphicsObject:: node() const diff --git a/src/NodeState.cpp b/src/NodeState.cpp index 670ea3cde..c4db5ebc8 100644 --- a/src/NodeState.cpp +++ b/src/NodeState.cpp @@ -18,6 +18,7 @@ NodeState(std::unique_ptr const &model) , _reaction(NOT_REACTING) , _reactingPortType(PortType::None) , _resizing(false) + , _model(model) {} @@ -41,7 +42,7 @@ getEntries(PortType portType) else return _outConnections; } - + NodeState::ConnectionPtrSet NodeState:: @@ -76,6 +77,13 @@ eraseConnection(PortType portType, } +void +NodeState:: +eraseInputAtIndex(PortIndex portIndex) +{ + +} + NodeState::ReactToConnectionState NodeState:: reaction() const @@ -136,3 +144,13 @@ resizing() const { return _resizing; } + + + +void +NodeState:: +updateEntries() +{ + _inConnections.resize(_model->nPorts(PortType::In)); + _outConnections.resize(_model->nPorts(PortType::Out)); +} \ No newline at end of file From a0318ec01f073929242fd5f1590baded7cb80616 Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Thu, 22 Sep 2022 15:33:30 +0100 Subject: [PATCH 24/39] Loading optimizations --- include/nodes/internal/Node.hpp | 3 +++ src/FlowScene.cpp | 15 ++++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/include/nodes/internal/Node.hpp b/include/nodes/internal/Node.hpp index de1169622..781f015e5 100644 --- a/include/nodes/internal/Node.hpp +++ b/include/nodes/internal/Node.hpp @@ -102,6 +102,9 @@ class NODE_EDITOR_PUBLIC Node NodeDataModel* nodeDataModel() const; + int targetInputConnections=0; + int currentInputConnections=0; + public slots: // data propagation diff --git a/src/FlowScene.cpp b/src/FlowScene.cpp index e98cf9ca6..8e5cbe615 100644 --- a/src/FlowScene.cpp +++ b/src/FlowScene.cpp @@ -149,6 +149,8 @@ restoreConnection(QJsonObject const &connectionJson) portIndexIn = std::min(numConnectionsIn - 1, portIndexIn); portIndexOut = std::min(numConnectionsOut - 1, portIndexOut); + nodeIn->currentInputConnections++; + return createConnection(*nodeIn, portIndexIn, *nodeOut, portIndexOut); } @@ -283,9 +285,7 @@ restoreNode(QJsonObject const& nodeJson) { dataModel = registry().create("DeletedNode"); } - /*throw std::logic_error(std::string("No registered model with name ") + - modelName.toLocal8Bit().data()); -*/ + auto node = std::make_shared(std::move(dataModel)); auto ngo = std::make_unique(*this, *node); node->setGraphicsObject(std::move(ngo)); @@ -799,6 +799,15 @@ loadFromMemory(const QByteArray& data) QJsonArray connectionJsonArray = jsonDocument["connections"].toArray(); + for (int i = 0; i < connectionJsonArray.size(); ++i) + { + QJsonObject jsonObject = connectionJsonArray[i].toObject(); + QUuid nodeInId = QUuid(jsonObject["in_id"].toString()); + auto nodeIn = _nodes[nodeInId].get(); + nodeIn->targetInputConnections++; + } + + for (int i = 0; i < connectionJsonArray.size(); ++i) { restoreConnection(connectionJsonArray[i].toObject()); From 54b5cf942b50a3957770db7543769de2fa289aeb Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Mon, 21 Nov 2022 09:37:35 +0000 Subject: [PATCH 25/39] Optimized loading --- include/nodes/internal/Node.hpp | 8 ++++++ src/FlowScene.cpp | 48 +++++++++++++++++++++++++++++-- src/Node.cpp | 11 +++++++ src/NodeConnectionInteraction.cpp | 3 +- 4 files changed, 66 insertions(+), 4 deletions(-) diff --git a/include/nodes/internal/Node.hpp b/include/nodes/internal/Node.hpp index de1169622..0736800ed 100644 --- a/include/nodes/internal/Node.hpp +++ b/include/nodes/internal/Node.hpp @@ -102,6 +102,9 @@ class NODE_EDITOR_PUBLIC Node NodeDataModel* nodeDataModel() const; + int targetInputConnections=0; + int currentInputConnections=0; + public slots: // data propagation @@ -115,6 +118,11 @@ public slots: // data propagation void onDataUpdated(PortIndex index); + /// Fetches data from model's OUT #index port + /// and propagates it to the connection + void + onDataUpdatedConnection(PortIndex index, Connection* connection); + private: // addressing diff --git a/src/FlowScene.cpp b/src/FlowScene.cpp index e98cf9ca6..a0695fdc9 100644 --- a/src/FlowScene.cpp +++ b/src/FlowScene.cpp @@ -149,6 +149,8 @@ restoreConnection(QJsonObject const &connectionJson) portIndexIn = std::min(numConnectionsIn - 1, portIndexIn); portIndexOut = std::min(numConnectionsOut - 1, portIndexOut); + nodeIn->currentInputConnections++; + return createConnection(*nodeIn, portIndexIn, *nodeOut, portIndexOut); } @@ -283,9 +285,7 @@ restoreNode(QJsonObject const& nodeJson) { dataModel = registry().create("DeletedNode"); } - /*throw std::logic_error(std::string("No registered model with name ") + - modelName.toLocal8Bit().data()); -*/ + auto node = std::make_shared(std::move(dataModel)); auto ngo = std::make_unique(*this, *node); node->setGraphicsObject(std::move(ngo)); @@ -799,6 +799,48 @@ loadFromMemory(const QByteArray& data) QJsonArray connectionJsonArray = jsonDocument["connections"].toArray(); + for (int i = 0; i < connectionJsonArray.size(); ++i) + { + QJsonObject jsonObject = connectionJsonArray[i].toObject(); + QUuid nodeInId = QUuid(jsonObject["in_id"].toString()); + auto nodeIn = _nodes[nodeInId].get(); + nodeIn->targetInputConnections++; + } + + std::stable_sort( connectionJsonArray.begin( ), connectionJsonArray.end( ), [this]( const auto& lhs, const auto& rhs ) + { + QPointF posA, posB; + QString nameA, nameB; + { + QJsonObject objA = lhs.toObject(); + QUuid nodeInId = QUuid(objA["in_id"].toString()); + PortIndex portIndexIn = objA["in_index"].toInt(); + auto nodeIn = _nodes[nodeInId].get(); + NodeGraphicsObject & ngo = nodeIn->nodeGraphicsObject(); + posA = ngo.scenePos(); + + NodeDataModel *dm = nodeIn->nodeDataModel(); + nameA = dm->name(); + } + if(nameA == "InputVariable") return true; + + { + QJsonObject objB = rhs.toObject(); + QUuid nodeInId = QUuid(objB["in_id"].toString()); + PortIndex portIndexIn = objB["in_index"].toInt(); + auto nodeIn = _nodes[nodeInId].get(); + NodeGraphicsObject & ngo = nodeIn->nodeGraphicsObject(); + posB = ngo.scenePos(); + + NodeDataModel *dm = nodeIn->nodeDataModel(); + nameB = dm->name(); + } + if(nameB == "InputVariable") return false; + + return posA.x() < posB.x(); + }); + + for (int i = 0; i < connectionJsonArray.size(); ++i) { restoreConnection(connectionJsonArray[i].toObject()); diff --git a/src/Node.cpp b/src/Node.cpp index c0bc6aa91..f829cf0ac 100644 --- a/src/Node.cpp +++ b/src/Node.cpp @@ -11,6 +11,7 @@ #include "ConnectionGraphicsObject.hpp" #include "ConnectionState.hpp" +#include "Connection.hpp" #include using QtNodes::Node; @@ -22,6 +23,8 @@ using QtNodes::NodeDataModel; using QtNodes::NodeGraphicsObject; using QtNodes::PortIndex; using QtNodes::PortType; +using QtNodes::Connection; + Node:: Node(std::unique_ptr && dataModel) @@ -268,3 +271,11 @@ onDataUpdated(PortIndex index) for (auto const & c : connections) c.second->propagateData(nodeData); } + +void +Node:: +onDataUpdatedConnection(PortIndex index, Connection* connection) +{ + auto nodeData = _nodeDataModel->outData(index); + connection->propagateData(nodeData); +} diff --git a/src/NodeConnectionInteraction.cpp b/src/NodeConnectionInteraction.cpp index 1fd5c1160..b6ada3adc 100644 --- a/src/NodeConnectionInteraction.cpp +++ b/src/NodeConnectionInteraction.cpp @@ -150,7 +150,8 @@ tryConnect() const if (outNode) { PortIndex outPortIndex = _connection->getPortIndex(PortType::Out); - outNode->onDataUpdated(outPortIndex); + // outNode->onDataUpdated(outPortIndex); + outNode->onDataUpdatedConnection(outPortIndex, _connection); } _scene->UpdateHistory(); From 410c8233832138c57c6ae0042f07c844a27f60ec Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Mon, 21 Nov 2022 09:38:00 +0000 Subject: [PATCH 26/39] started undoRedo --- include/nodes/internal/FlowScene.hpp | 13 +++++++++++++ src/FlowScene.cpp | 15 ++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/include/nodes/internal/FlowScene.hpp b/include/nodes/internal/FlowScene.hpp index c1829b5ca..73078d57f 100644 --- a/include/nodes/internal/FlowScene.hpp +++ b/include/nodes/internal/FlowScene.hpp @@ -11,6 +11,7 @@ #include "QUuidStdHash.hpp" #include "Export.hpp" #include "DataModelRegistry.hpp" +#include namespace QtNodes { @@ -30,6 +31,17 @@ struct SceneHistory }; +struct UndoRedoAction { + + std::function undoAction; + std::function redoAction; + UndoRedoAction(std::function undoAction, std::function redoAction) { + this->undoAction = undoAction; + this->redoAction = redoAction; + }; +}; + + struct Anchor { QPointF position; double scale; @@ -47,6 +59,7 @@ class NODE_EDITOR_PUBLIC FlowScene ~FlowScene(); + std::stack actionsHistory; public: std::shared_ptrcreateConnection(PortType connectedPort, diff --git a/src/FlowScene.cpp b/src/FlowScene.cpp index a0695fdc9..c8ef12b72 100644 --- a/src/FlowScene.cpp +++ b/src/FlowScene.cpp @@ -39,6 +39,8 @@ using QtNodes::NodeDataModel; using QtNodes::PortType; using QtNodes::PortIndex; + + FlowScene:: FlowScene(std::shared_ptr registry) : _registry(registry) @@ -51,7 +53,18 @@ FlowScene(std::shared_ptr registry) auto UpdateLamda = [this](Node& n, const QPointF& p) { resolveGroups(n); - UpdateHistory(); + actionsHistory.push(UndoRedoAction( + [](double v) + { + return 0; + }, + [](double v) + { + return 0; + } + )); + + // UpdateHistory(); }; connect(this, &FlowScene::nodeMoveFinished, this, UpdateLamda); From 09d79269d033d6733b7f3d2fb241046ac1efeb2d Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Tue, 22 Nov 2022 11:28:30 +0000 Subject: [PATCH 27/39] progress on undo/redo system --- include/nodes/internal/FlowScene.hpp | 23 ++- include/nodes/internal/FlowView.hpp | 6 +- .../nodes/internal/GroupGraphicsObject.hpp | 1 + include/nodes/internal/NodeGraphicsObject.hpp | 2 +- include/nodes/internal/NodeState.hpp | 3 + src/FlowScene.cpp | 191 +++++++++++++----- src/FlowView.cpp | 159 +++++++++++++-- src/GroupGraphicsObject.cpp | 3 +- src/NodeConnectionInteraction.cpp | 8 +- src/NodeGraphicsObject.cpp | 5 +- src/NodeState.cpp | 26 +++ 11 files changed, 334 insertions(+), 93 deletions(-) diff --git a/include/nodes/internal/FlowScene.hpp b/include/nodes/internal/FlowScene.hpp index 73078d57f..5bf5dadc4 100644 --- a/include/nodes/internal/FlowScene.hpp +++ b/include/nodes/internal/FlowScene.hpp @@ -35,9 +35,11 @@ struct UndoRedoAction { std::function undoAction; std::function redoAction; - UndoRedoAction(std::function undoAction, std::function redoAction) { + std::string name; + UndoRedoAction(std::function undoAction, std::function redoAction, std::string name) { this->undoAction = undoAction; this->redoAction = redoAction; + this->name = name; }; }; @@ -59,7 +61,9 @@ class NODE_EDITOR_PUBLIC FlowScene ~FlowScene(); - std::stack actionsHistory; + std::vector undoActions; + std::vector redoActions; + void PrintActions(); public: std::shared_ptrcreateConnection(PortType connectedPort, @@ -77,12 +81,14 @@ class NODE_EDITOR_PUBLIC FlowScene void deleteConnection(Connection* connection); Node&createNode(std::unique_ptr && dataModel); + + Node&createNodeWithID(std::unique_ptr && dataModel, QUuid id); Group& createGroup(); Group& pasteGroup(QJsonObject const& nodeJson, QPointF nodeGroupCentroid, QPointF mousePos); - Node&restoreNode(QJsonObject const& nodeJson); + Node&restoreNode(QJsonObject const& nodeJson, bool keepId=false); Group& restoreGroup(QJsonObject const& nodeJson); @@ -92,6 +98,8 @@ class NODE_EDITOR_PUBLIC FlowScene void removeNode(Node& node); + void removeNodeWithID(QUuid id); + void removeGroup(Group& node); DataModelRegistry®istry() const; @@ -117,6 +125,7 @@ class NODE_EDITOR_PUBLIC FlowScene public: std::unordered_map > const &nodes() const; + std::unordered_map > &nodes(); std::unordered_map > const &connections() const; @@ -136,12 +145,12 @@ class NODE_EDITOR_PUBLIC FlowScene void loadFromMemory(const QByteArray& data); + void AddAction(UndoRedoAction action); + void Undo(); void Redo(); - void UpdateHistory(); - void ResetHistory(); int GetHistoryIndex(); @@ -162,9 +171,9 @@ class NODE_EDITOR_PUBLIC FlowScene void groupMoved(Group& n, const QPointF& newLocation); - void nodeMoveFinished(Node& n, const QPointF& newLocation); + void nodeMoveFinished(Node& n, const QPointF& newLocation, const QPointF &oldLocation); - void groupMoveFinished(Group& g, const QPointF& newLocation); + void groupMoveFinished(Group& g, const QPointF& newLocation, const QPointF& oldLocation); void nodeDoubleClicked(Node& n); diff --git a/include/nodes/internal/FlowView.hpp b/include/nodes/internal/FlowView.hpp index 9320e304b..d14df6338 100644 --- a/include/nodes/internal/FlowView.hpp +++ b/include/nodes/internal/FlowView.hpp @@ -27,10 +27,10 @@ class NODE_EDITOR_PUBLIC FlowView void setScene(FlowScene *scene); - QJsonObject selectionToJson(); - + QJsonObject selectionToJson(bool includePartialConnections=false); void jsonToScene(QJsonObject object); - + void jsonToSceneMousePos(QJsonObject object); + void deleteJsonElements(const QJsonObject &object); public slots: void scaleUp(); diff --git a/include/nodes/internal/GroupGraphicsObject.hpp b/include/nodes/internal/GroupGraphicsObject.hpp index cb5dc046c..a28da495c 100644 --- a/include/nodes/internal/GroupGraphicsObject.hpp +++ b/include/nodes/internal/GroupGraphicsObject.hpp @@ -126,6 +126,7 @@ class GroupGraphicsObject : public QGraphicsObject contextMenuEvent(QGraphicsSceneContextMenuEvent* event) override; private: + QPointF oldPosition; FlowScene & _scene; Group& _group; diff --git a/include/nodes/internal/NodeGraphicsObject.hpp b/include/nodes/internal/NodeGraphicsObject.hpp index 02bba6d2d..77d7ab8a0 100644 --- a/include/nodes/internal/NodeGraphicsObject.hpp +++ b/include/nodes/internal/NodeGraphicsObject.hpp @@ -97,7 +97,7 @@ class NodeGraphicsObject : public QGraphicsObject private: FlowScene & _scene; - + QPointF oldPosition; Node& _node; bool _locked; diff --git a/include/nodes/internal/NodeState.hpp b/include/nodes/internal/NodeState.hpp index b1071ad10..f3f6052f5 100644 --- a/include/nodes/internal/NodeState.hpp +++ b/include/nodes/internal/NodeState.hpp @@ -46,6 +46,9 @@ class NodeState ConnectionPtrSet connections(PortType portType, PortIndex portIndex) const; + std::vector + allConnections() const; + void setConnection(PortType portType, PortIndex portIndex, diff --git a/src/FlowScene.cpp b/src/FlowScene.cpp index c8ef12b72..eb72220bc 100644 --- a/src/FlowScene.cpp +++ b/src/FlowScene.cpp @@ -48,30 +48,49 @@ FlowScene(std::shared_ptr registry) setItemIndexMethod(QGraphicsScene::NoIndex); ResetHistory(); - UpdateHistory(); - auto UpdateLamda = [this](Node& n, const QPointF& p) + auto UpdateLamda = [this](Node& n, const QPointF& newPosition, const QPointF &oldPosition) { resolveGroups(n); - actionsHistory.push(UndoRedoAction( - [](double v) + + undoActions.push_back(UndoRedoAction( + [&n, oldPosition](double v) { + n.nodeGraphicsObject().setPos(oldPosition); return 0; }, - [](double v) + [&n, newPosition](double v) { + n.nodeGraphicsObject().setPos(newPosition); return 0; - } + }, + "Moved Node " + n.nodeDataModel()->name().toStdString() )); + redoActions.clear(); - // UpdateHistory(); + PrintActions(); }; connect(this, &FlowScene::nodeMoveFinished, this, UpdateLamda); - auto GroupUpdateLamda = [this](Group& g, const QPointF& p) + auto GroupUpdateLamda = [this](Group& g, const QPointF& newPosition, const QPointF& oldPosition) { resolveGroups(g); - UpdateHistory(); + + + undoActions.push_back(UndoRedoAction( + [&g, oldPosition](double v) + { + g.groupGraphicsObject().setPos(oldPosition); + return 0; + }, + [&g, newPosition](double v) + { + g.groupGraphicsObject().setPos(newPosition); + return 0; + }, + "Moved Group " + g.GetName().toStdString() + )); + redoActions.clear(); }; connect(this, &FlowScene::groupMoveFinished, this, GroupUpdateLamda); @@ -219,6 +238,27 @@ createNode(std::unique_ptr && dataModel) return *nodePtr; } + +Node& +FlowScene:: +createNodeWithID(std::unique_ptr && dataModel, QUuid id) +{ + auto node = std::make_unique(std::move(dataModel)); + node->setId(id); + + auto ngo = std::make_unique(*this, *node); + + node->setGraphicsObject(std::move(ngo)); + + auto nodePtr = node.get(); + _nodes[node->id()] = std::move(node); + + nodeCreated(*nodePtr); + + return *nodePtr; +} + + Group& FlowScene::createGroup() { auto group = std::make_shared(*this); auto ggo = std::make_shared(*this, *group); @@ -289,7 +329,7 @@ pasteGroup(QJsonObject const& groupJson, QPointF nodeGroupCentroid, QPointF mous Node& FlowScene:: -restoreNode(QJsonObject const& nodeJson) +restoreNode(QJsonObject const& nodeJson, bool keepId) { QString modelName = nodeJson["model"].toObject()["name"].toString(); auto dataModel = registry().create(modelName); //This is where it looks for the node by name @@ -300,6 +340,7 @@ restoreNode(QJsonObject const& nodeJson) } auto node = std::make_shared(std::move(dataModel)); + if(keepId) node->setId(QUuid( nodeJson["id"].toString() )); auto ngo = std::make_unique(*this, *node); node->setGraphicsObject(std::move(ngo)); @@ -370,6 +411,38 @@ removeNode(Node& node) _nodes.erase(node.id()); } + + +void +FlowScene:: +removeNodeWithID(QUuid id) +{ + UniqueNode nodePtr = _nodes[id]; + Node &node = *nodePtr; + // call signal + nodeDeleted(node); + + auto deleteConnections = + [&node, this] (PortType portType) + { + auto nodeState = node.nodeState(); + auto const & nodeEntries = nodeState.getEntries(portType); + + for (auto &connections : nodeEntries) + { + for (auto const &pair : connections) + deleteConnection(*pair.second); + } + }; + + deleteConnections(PortType::In); + deleteConnections(PortType::Out); + + + _nodes.erase(node.id()); +} + + void FlowScene:: removeGroup(Group& group) @@ -624,6 +697,13 @@ nodes() const return _nodes; } +std::unordered_map > & +FlowScene:: +nodes() +{ + return _nodes; +} + std::unordered_map > const & FlowScene:: @@ -881,61 +961,62 @@ loadFromMemory(const QByteArray& data) } } + +void FlowScene::PrintActions() +{ + std::cout << "ACTIONS " << std::endl; + for(int i=0; i 1) - { - writeToHistory = false; - clearScene(); - historyInx--; - loadFromMemory(history[historyInx - 1].data); - writeToHistory = true; - } + PrintActions(); + if(undoActions.size()>0) + { + writeToHistory = false; + UndoRedoAction action = undoActions[undoActions.size()-1]; + undoActions.pop_back(); + action.undoAction(0); + + redoActions.push_back(action); + writeToHistory = true; + } } void FlowScene::Redo() { - std::cout << "Redo" << std::endl; - writeToHistory = false; - if(historyInx < history.size()) - { - std::cout << "historyInx:" << historyInx << " history.size():" << history.size() << std::endl; - clearScene(); - loadFromMemory(history[historyInx].data); - historyInx++; - } - else - { - std::cout << "Could not redo" << std::endl; - } - writeToHistory = true; -} + PrintActions(); + if(redoActions.size()>0) + { + writeToHistory = false; + UndoRedoAction action = redoActions[redoActions.size()-1]; + redoActions.pop_back(); + action.redoAction(0); -void FlowScene::UpdateHistory() -{ - if(writeToHistory) - { - std::cout << "UpdateHistory" << std::endl; - - SceneHistory sh; - sh.data = saveToMemory(); - - if(historyInx < history.size()) - { - history.resize(historyInx); - history.push_back(sh); - } - else - { - history.push_back(sh); - } - - historyInx++; - } + undoActions.push_back(action); + writeToHistory = true; + } } + void FlowScene::ResetHistory() { historyInx = 0; diff --git a/src/FlowView.cpp b/src/FlowView.cpp index e7f52815f..c843dc299 100644 --- a/src/FlowView.cpp +++ b/src/FlowView.cpp @@ -297,7 +297,7 @@ contextMenuEvent(QContextMenuEvent *event) qWarning() << val; QJsonDocument d = QJsonDocument::fromJson(val.toUtf8()); QJsonObject sett2 = d.object(); - jsonToScene(sett2); + jsonToSceneMousePos(sett2); modelMenu.close(); } @@ -306,14 +306,28 @@ contextMenuEvent(QContextMenuEvent *event) if (type) { - auto& node = _scene->createNode(std::move(type)); - + Node& node = _scene->createNode(std::move(type)); QPoint pos = event->pos(); - QPointF posView = this->mapToScene(pos); node.nodeGraphicsObject().setPos(posView); - _scene->UpdateHistory(); + QUuid id = node.id(); + _scene->AddAction(UndoRedoAction( + [this, id](double v) + { + _scene->removeNodeWithID(id); + return 0; + }, + [this, pos, modelName, id](double v) + { + auto type = _scene->registry().create(modelName); + auto& node = _scene->createNodeWithID(std::move(type), id); + QPointF posView = this->mapToScene(pos); + node.nodeGraphicsObject().setPos(posView); + return 0; + }, + "Created Node " + node.nodeDataModel()->name().toStdString() + )); } else { @@ -411,6 +425,10 @@ void FlowView:: deleteSelectedNodes() { + QJsonObject sceneJson = selectionToJson(true); + + std::vector nodeIds; + std::vector connectionsIds; for (QGraphicsItem * item : _scene->selectedItems()) { if(item) @@ -434,8 +452,37 @@ deleteSelectedNodes() _scene->removeNode(c->node()); } } - } - _scene->UpdateHistory(); + } + + + _scene->AddAction(UndoRedoAction( + //Undo action + [this, sceneJson](double v) + { + jsonToScene(sceneJson); + return 0; + }, + //Redo action + [this, sceneJson](double v) + { + deleteJsonElements(sceneJson); + return 0; + }, + //Name + "Deletes nodes" + )); +} + +void FlowView::deleteJsonElements(const QJsonObject &jsonDocument) +{ + QJsonArray nodesJsonArray = jsonDocument["nodes"].toArray(); + for (int i = 0; i < nodesJsonArray.size(); ++i) + { + QJsonObject nodeJson = nodesJsonArray[i].toObject(); + QUuid id = QUuid( nodeJson["id"].toString() ); + _scene->removeNodeWithID(id); + } + } void FlowView::copySelectedNodes() { @@ -452,6 +499,34 @@ void FlowView::jsonToScene(QJsonObject jsonDocument) { QJsonArray nodesJsonArray = jsonDocument["nodes"].toArray(); + + + for (int i = 0; i < nodesJsonArray.size(); ++i) + { + _scene->restoreNode(nodesJsonArray[i].toObject(), true); + } + + QJsonArray connectionJsonArray = jsonDocument["connections"].toArray(); + for (int i = 0; i < connectionJsonArray.size(); ++i) + { + QUuid in = QUuid(connectionJsonArray[i].toObject()["in_id"].toString()); + QUuid out = QUuid(connectionJsonArray[i].toObject()["out_id"].toString()); + + _scene->pasteConnection(connectionJsonArray[i].toObject(), in, out ); + } + + QJsonArray groupsJsonArray = jsonDocument["groups"].toArray(); + for (int i = 0; i < groupsJsonArray.size(); ++i) + { + _scene->restoreGroup(groupsJsonArray[i].toObject()); + } +} + + +void FlowView::jsonToSceneMousePos(QJsonObject jsonDocument) +{ + QJsonArray nodesJsonArray = jsonDocument["nodes"].toArray(); + std::map addedIds; //Get Bounds of all the selected items @@ -475,28 +550,38 @@ void FlowView::jsonToScene(QJsonObject jsonDocument) float centroidY = (maxy - miny) / 2.0 + miny; QPointF centroid(centroidX, centroidY); - QPoint viewPointMouse = this->mapFromGlobal(QCursor::pos()); QPointF posViewMouse = this->mapToScene(viewPointMouse); + + + for (int i = 0; i < nodesJsonArray.size(); ++i) { - QUuid currentId = QUuid( nodesJsonArray[i].toObject()["id"].toString() ); + QJsonObject nodeJson = nodesJsonArray[i].toObject(); + QUuid currentId = QUuid( nodeJson["id"].toString() ); QUuid newId = _scene->pasteNode(nodesJsonArray[i].toObject(), centroid, posViewMouse); addedIds.insert(std::pair(currentId, newId)); + nodesJsonArray[i] = _scene->nodes()[newId]->save(); } QJsonArray connectionJsonArray = jsonDocument["connections"].toArray(); for (int i = 0; i < connectionJsonArray.size(); ++i) { - QUuid in = QUuid(connectionJsonArray[i].toObject()["in_id"].toString()); + QJsonObject connectionJson = connectionJsonArray[i].toObject(); + + QUuid in = QUuid(connectionJson["in_id"].toString()); QUuid newIn = addedIds[in]; - QUuid out = QUuid(connectionJsonArray[i].toObject()["out_id"].toString()); + QUuid out = QUuid(connectionJson["out_id"].toString()); QUuid newOut = addedIds[out]; - _scene->pasteConnection(connectionJsonArray[i].toObject(), newIn, newOut ); + _scene->pasteConnection(connectionJson, newIn, newOut ); + + connectionJson["in_id"] = newIn.toString(); + connectionJson["out_id"] = newOut.toString(); + connectionJsonArray[i] = connectionJson; } QJsonArray groupsJsonArray = jsonDocument["groups"].toArray(); @@ -504,14 +589,36 @@ void FlowView::jsonToScene(QJsonObject jsonDocument) { _scene->pasteGroup(groupsJsonArray[i].toObject(), centroid, posViewMouse); } + + + _scene->AddAction(UndoRedoAction( + [this, addedIds](double v) + { + //Delete all the created nodes + for(auto &id: addedIds) + { + _scene->removeNodeWithID(id.second); + } + return 0; + }, + [this, jsonDocument](double v) + { + QJsonDocument doc(jsonDocument); + std::string strJson= (doc.toJson(QJsonDocument::Compact)).toStdString(); + std::cout << strJson << std::endl; + jsonToScene(jsonDocument); //jsonDocument has now been updated with new IDs and new positions + return 0; + }, + "Created Node " + )); - _scene->UpdateHistory(); } -QJsonObject FlowView::selectionToJson() +QJsonObject FlowView::selectionToJson(bool includePartialConnections) { QJsonObject sceneJson; QJsonArray nodesJsonArray; + QJsonArray connectionJsonArray; std::vector addedIds; for (QGraphicsItem * item : _scene->selectedItems()) @@ -521,17 +628,27 @@ QJsonObject FlowView::selectionToJson() Node& node = n->node(); nodesJsonArray.append(node.save()); addedIds.push_back(node.id()); + + if(includePartialConnections) + { + std::vector allConnections = node.nodeState().allConnections(); + for(int i=0; isave(); + if (!connectionJson.isEmpty()) + connectionJsonArray.append(connectionJson); + } + } } } - QJsonArray connectionJsonArray; for (QGraphicsItem * item : _scene->selectedItems()) { if (auto c = qgraphicsitem_cast(item)) { Connection& connection = c->connection(); - if( std::find(addedIds.begin(), addedIds.end(), connection.getNode(PortType::In)->id()) != addedIds.end() && - std::find(addedIds.begin(), addedIds.end(), connection.getNode(PortType::Out)->id()) != addedIds.end() ) { + if((std::find(addedIds.begin(), addedIds.end(), connection.getNode(PortType::In)->id()) != addedIds.end() && + std::find(addedIds.begin(), addedIds.end(), connection.getNode(PortType::Out)->id()) != addedIds.end()) || includePartialConnections) { QJsonObject connectionJson = connection.save(); if (!connectionJson.isEmpty()) @@ -590,7 +707,9 @@ void FlowView::pasteSelectedNodes() { QByteArray text = p_Clipboard->text().toUtf8(); QJsonObject const jsonDocument = QJsonDocument::fromJson(text).object(); - jsonToScene(jsonDocument); + jsonToSceneMousePos(jsonDocument); + + } @@ -701,8 +820,8 @@ void FlowView::duplicateSelectedNode() } - if(createdNodes.size() > 0) - _scene->UpdateHistory(); + // if(createdNodes.size() > 0) + // _scene->UpdateHistory(); } diff --git a/src/GroupGraphicsObject.cpp b/src/GroupGraphicsObject.cpp index eccb00031..facd4d782 100644 --- a/src/GroupGraphicsObject.cpp +++ b/src/GroupGraphicsObject.cpp @@ -449,6 +449,7 @@ void GroupGraphicsObject:: mousePressEvent(QGraphicsSceneMouseEvent * event) { + oldPosition = pos(); if(QApplication::keyboardModifiers().testFlag(Qt::ShiftModifier)) { event->ignore(); @@ -579,7 +580,7 @@ GroupGraphicsObject:: mouseReleaseEvent(QGraphicsSceneMouseEvent* event) { QGraphicsObject::mouseReleaseEvent(event); - _scene.groupMoveFinished(_group, pos()); + _scene.groupMoveFinished(_group, pos(), oldPosition); isResizingX=false; isResizingY=false; diff --git a/src/NodeConnectionInteraction.cpp b/src/NodeConnectionInteraction.cpp index b6ada3adc..884605089 100644 --- a/src/NodeConnectionInteraction.cpp +++ b/src/NodeConnectionInteraction.cpp @@ -114,13 +114,13 @@ tryConnect() const { _scene->createConnection(converterNode, 0, *outNode, outNodePortIndex); _scene->createConnection(*_node, portIndex, converterNode, 0); - _scene->UpdateHistory(); + // _scene->UpdateHistory(); } else { _scene->createConnection(converterNode, 0, *_node, portIndex); _scene->createConnection(*outNode, outNodePortIndex, converterNode, 0); - _scene->UpdateHistory(); + // _scene->UpdateHistory(); } //Delete the users connection, we already replaced it. @@ -154,7 +154,7 @@ tryConnect() const outNode->onDataUpdatedConnection(outPortIndex, _connection); } - _scene->UpdateHistory(); + // _scene->UpdateHistory(); return true; } @@ -185,7 +185,7 @@ disconnect(PortType portToDisconnect) const _connection->getConnectionGraphicsObject().grabMouse(); - _scene->UpdateHistory(); + // _scene->UpdateHistory(); return true; } diff --git a/src/NodeGraphicsObject.cpp b/src/NodeGraphicsObject.cpp index 35fbc738f..b07d878c6 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -234,8 +234,9 @@ void NodeGraphicsObject:: mousePressEvent(QGraphicsSceneMouseEvent * event) { + if(_locked) return; - + oldPosition = pos(); // deselect all other items after this one is selected if (!isSelected() && !(event->modifiers() & Qt::ControlModifier)) @@ -372,7 +373,7 @@ mouseReleaseEvent(QGraphicsSceneMouseEvent* event) QGraphicsObject::mouseReleaseEvent(event); - _scene.nodeMoveFinished(_node, pos()); + _scene.nodeMoveFinished(_node, pos(), oldPosition); // position connections precisely after fast node move moveConnections(); diff --git a/src/NodeState.cpp b/src/NodeState.cpp index c4db5ebc8..91d80f5f5 100644 --- a/src/NodeState.cpp +++ b/src/NodeState.cpp @@ -54,6 +54,32 @@ connections(PortType portType, PortIndex portIndex) const } +std::vector +NodeState:: +allConnections() const +{ + std::vector res; + std::vector ins = getEntries(PortType::In); + for(int i=0; i outs = getEntries(PortType::Out); + for(int i=0; i Date: Tue, 6 Dec 2022 18:21:44 +0000 Subject: [PATCH 28/39] UndoRedo for paste, duplicate and create connection --- include/nodes/internal/Connection.hpp | 3 +- include/nodes/internal/FlowScene.hpp | 4 +- src/Connection.cpp | 10 ++- src/FlowScene.cpp | 15 +++- src/FlowView.cpp | 117 ++------------------------ src/Node.cpp | 13 +-- src/NodeConnectionInteraction.cpp | 26 +++++- 7 files changed, 64 insertions(+), 124 deletions(-) diff --git a/include/nodes/internal/Connection.hpp b/include/nodes/internal/Connection.hpp index 216f6b395..1fca7f872 100644 --- a/include/nodes/internal/Connection.hpp +++ b/include/nodes/internal/Connection.hpp @@ -45,7 +45,8 @@ class NODE_EDITOR_PUBLIC Connection Connection(Node& nodeIn, PortIndex portIndexIn, Node& nodeOut, - PortIndex portIndexOut); + PortIndex portIndexOut, + QUuid *id = nullptr); Connection(const Connection&) = delete; Connection operator=(const Connection&) = delete; diff --git a/include/nodes/internal/FlowScene.hpp b/include/nodes/internal/FlowScene.hpp index 5bf5dadc4..6933b1850 100644 --- a/include/nodes/internal/FlowScene.hpp +++ b/include/nodes/internal/FlowScene.hpp @@ -73,12 +73,14 @@ class NODE_EDITOR_PUBLIC FlowScene std::shared_ptrcreateConnection(Node& nodeIn, PortIndex portIndexIn, Node& nodeOut, - PortIndex portIndexOut); + PortIndex portIndexOut, + QUuid *id=nullptr); std::shared_ptrrestoreConnection(QJsonObject const &connectionJson); void deleteConnection(Connection& connection); void deleteConnection(Connection* connection); + void deleteConnectionWithID(QUuid id); Node&createNode(std::unique_ptr && dataModel); diff --git a/src/Connection.cpp b/src/Connection.cpp index 72ab154a7..38a460590 100644 --- a/src/Connection.cpp +++ b/src/Connection.cpp @@ -50,8 +50,9 @@ Connection:: Connection(Node& nodeIn, PortIndex portIndexIn, Node& nodeOut, - PortIndex portIndexOut) - : _id(QUuid::createUuid()) + PortIndex portIndexOut, + QUuid *id) + : _id((id ==nullptr) ? QUuid::createUuid() : *id) , _outNode(&nodeOut) , _inNode(&nodeIn) , _outPortIndex(portIndexOut) @@ -63,6 +64,7 @@ Connection(Node& nodeIn, } + Connection:: ~Connection() { @@ -77,6 +79,7 @@ Connection:: { _outNode->nodeGraphicsObject().update(); } + _connectionState.setLastHoveredNode(nullptr); } @@ -260,6 +263,9 @@ setNodeToPort(Node& node, updated(*this); } + + + void Connection:: setGroup(Group* group, PortType portType, PortIndex portIndex) diff --git a/src/FlowScene.cpp b/src/FlowScene.cpp index eb72220bc..28e0e3be9 100644 --- a/src/FlowScene.cpp +++ b/src/FlowScene.cpp @@ -132,14 +132,16 @@ FlowScene:: createConnection(Node& nodeIn, PortIndex portIndexIn, Node& nodeOut, - PortIndex portIndexOut) + PortIndex portIndexOut, + QUuid *id) { auto connection = std::make_shared(nodeIn, portIndexIn, nodeOut, - portIndexOut); + portIndexOut, + id); auto cgo = std::make_unique(*this, *connection); @@ -221,6 +223,15 @@ deleteConnection(Connection* connection) } +void +FlowScene:: +deleteConnectionWithID(QUuid id) +{ + Connection *connection = _connections[id].get(); + deleteConnection(connection); +} + + Node& FlowScene:: createNode(std::unique_ptr && dataModel) diff --git a/src/FlowView.cpp b/src/FlowView.cpp index c843dc299..b33301309 100644 --- a/src/FlowView.cpp +++ b/src/FlowView.cpp @@ -565,6 +565,7 @@ void FlowView::jsonToSceneMousePos(QJsonObject jsonDocument) addedIds.insert(std::pair(currentId, newId)); nodesJsonArray[i] = _scene->nodes()[newId]->save(); } + jsonDocument["nodes"] = nodesJsonArray; QJsonArray connectionJsonArray = jsonDocument["connections"].toArray(); for (int i = 0; i < connectionJsonArray.size(); ++i) @@ -583,6 +584,7 @@ void FlowView::jsonToSceneMousePos(QJsonObject jsonDocument) connectionJson["out_id"] = newOut.toString(); connectionJsonArray[i] = connectionJson; } + jsonDocument["connections"] =connectionJsonArray; QJsonArray groupsJsonArray = jsonDocument["groups"].toArray(); for (int i = 0; i < groupsJsonArray.size(); ++i) @@ -594,7 +596,7 @@ void FlowView::jsonToSceneMousePos(QJsonObject jsonDocument) _scene->AddAction(UndoRedoAction( [this, addedIds](double v) { - //Delete all the created nodes + //Delete all the created nodes (and their connections) for(auto &id: addedIds) { _scene->removeNodeWithID(id.second); @@ -602,10 +604,7 @@ void FlowView::jsonToSceneMousePos(QJsonObject jsonDocument) return 0; }, [this, jsonDocument](double v) - { - QJsonDocument doc(jsonDocument); - std::string strJson= (doc.toJson(QJsonDocument::Compact)).toStdString(); - std::cout << strJson << std::endl; + { jsonToScene(jsonDocument); //jsonDocument has now been updated with new IDs and new positions return 0; }, @@ -716,112 +715,8 @@ void FlowView::pasteSelectedNodes() { void FlowView::duplicateSelectedNode() { - //Get Bounds of all the selected items - float minx = 10000000000; - float miny = 10000000000; - float maxx = -1000000000; - float maxy = -1000000000; - for (QGraphicsItem * item : _scene->selectedItems()) - { - if (auto n = qgraphicsitem_cast(item)) - { - QPointF pos = n->pos(); - if(pos.x() < minx) minx = pos.x(); - if(pos.y() < miny) miny = pos.y(); - if(pos.x() > maxx) maxx = pos.x(); - if(pos.y() > maxy) maxy = pos.y(); - } - } - //compute centroid - float centroidX = (maxx - minx) / 2.0 + minx; - float centroidY = (maxy - miny) / 2.0 + miny; - QPointF centroid(centroidX, centroidY); - - //create nodes - std::vector createdNodes; - std::vector couterpartNode; - std::vector nodeData; - for (QGraphicsItem * item : _scene->selectedItems()) - { - if (auto n = qgraphicsitem_cast(item)) - { - QString modelName = n->node().nodeDataModel()->name(); - auto type = _scene->registry().create(modelName); - auto& typeRef = type; - - if (typeRef) - { - auto& node = _scene->createNode(std::move(typeRef)); - node.nodeDataModel()->restore(n->node().nodeDataModel()->save()); - createdNodes.push_back(&node); - couterpartNode.push_back(&(n->node())); - - QPoint viewPointMouse = this->mapFromGlobal(QCursor::pos()); - QPointF posViewMouse = this->mapToScene(viewPointMouse); - - QPointF pos = posViewMouse + (n->pos() - centroid); - - node.nodeGraphicsObject().setPos(pos); - } - else - { - qDebug() << "Model not found"; - } - } - } - - //create connections - std::vector > createdConnections; - for (QGraphicsItem * item : _scene->selectedItems()) - { - if (auto c = qgraphicsitem_cast(item)) - { - //if(c->connection().connectionState().) - - Node* nodeIn = c->connection().getNode(PortType::In); - PortIndex portIndexIn = c->connection().getPortIndex(PortType::In); - Node* nodeOut = c->connection().getNode(PortType::Out); - PortIndex portIndexOut = c->connection().getPortIndex(PortType::Out); - - //find index of node in couterpartNode Array - int j = -1; - for(j = 0; j < couterpartNode.size(); j++) - { - if(couterpartNode[j] == nodeIn) - break; - } - - int k = -1; - for(k = 0; k < couterpartNode.size(); k++) - { - if(couterpartNode[k] == nodeOut) - break; - } - - if(j >=0 && k>=0 && j < couterpartNode.size() && k < couterpartNode.size()) - { - auto connection = _scene->createConnection(*createdNodes[j], portIndexIn, *createdNodes[k], portIndexOut); - auto& connectionRef = connection; - createdConnections.push_back(connection); - } - } - } - - - //reset selection to nodes created - _scene->clearSelection(); - for(int i = 0; i < createdNodes.size(); i++) - { - createdNodes[i]->nodeGraphicsObject().setSelected(true); - } - for(int i = 0; i < createdConnections.size(); i++) - { - createdConnections[i]->getConnectionGraphicsObject().setSelected(true); - } - - - // if(createdNodes.size() > 0) - // _scene->UpdateHistory(); + QJsonObject selectionJson = selectionToJson(); + jsonToSceneMousePos(selectionJson); } diff --git a/src/Node.cpp b/src/Node.cpp index f829cf0ac..4d4df6df3 100644 --- a/src/Node.cpp +++ b/src/Node.cpp @@ -265,11 +265,14 @@ onDataUpdated(PortIndex index) { auto nodeData = _nodeDataModel->outData(index); - auto connections = - _nodeState.connections(PortType::Out, index); - - for (auto const & c : connections) - c.second->propagateData(nodeData); + if (_nodeState.getEntries(PortType::Out).size() > 0) + { + auto connections = + _nodeState.connections(PortType::Out, index); + + for (auto const & c : connections) + c.second->propagateData(nodeData); + } } void diff --git a/src/NodeConnectionInteraction.cpp b/src/NodeConnectionInteraction.cpp index 884605089..9bc956ce1 100644 --- a/src/NodeConnectionInteraction.cpp +++ b/src/NodeConnectionInteraction.cpp @@ -114,13 +114,11 @@ tryConnect() const { _scene->createConnection(converterNode, 0, *outNode, outNodePortIndex); _scene->createConnection(*_node, portIndex, converterNode, 0); - // _scene->UpdateHistory(); } else { _scene->createConnection(converterNode, 0, *_node, portIndex); _scene->createConnection(*outNode, outNodePortIndex, converterNode, 0); - // _scene->UpdateHistory(); } //Delete the users connection, we already replaced it. @@ -155,6 +153,30 @@ tryConnect() const } // _scene->UpdateHistory(); + QUuid connectionID = _connection->id(); + FlowScene *scene = _scene; + Node *nodeIn = _connection->getNode(PortType::In); + Node *nodeOut = _connection->getNode(PortType::Out); + PortIndex portIn = _connection->getPortIndex(PortType::In); + PortIndex portOut = _connection->getPortIndex(PortType::Out); + _scene->AddAction(UndoRedoAction( + [scene, connectionID](double v) + { + scene->deleteConnectionWithID(connectionID); + return 0; + }, + [scene, nodeIn, portIn, nodeOut, portOut, connectionID](double v) + { + QUuid id = connectionID; + scene->createConnection(*nodeIn, + portIn, + *nodeOut, + portOut, + &id); + return 0; + }, + "Created Connection " + )); return true; } From 54c1f5b5964aed074fdb2f8f58fd1d58280437b6 Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Fri, 9 Dec 2022 12:19:50 +0000 Subject: [PATCH 29/39] fixed undo redo --- include/nodes/internal/FlowScene.hpp | 15 +++---- src/FlowScene.cpp | 58 +++++++++++++++------------- src/FlowView.cpp | 51 ++++++++++++------------ src/NodeConnectionInteraction.cpp | 38 +++++++++++++++--- 4 files changed, 95 insertions(+), 67 deletions(-) diff --git a/include/nodes/internal/FlowScene.hpp b/include/nodes/internal/FlowScene.hpp index 6933b1850..0729495ac 100644 --- a/include/nodes/internal/FlowScene.hpp +++ b/include/nodes/internal/FlowScene.hpp @@ -25,18 +25,14 @@ class Connection; class ConnectionGraphicsObject; class NodeStyle; -struct SceneHistory -{ - QByteArray data; -}; struct UndoRedoAction { - std::function undoAction; - std::function redoAction; + std::function undoAction; + std::function redoAction; std::string name; - UndoRedoAction(std::function undoAction, std::function redoAction, std::string name) { + UndoRedoAction(std::function undoAction, std::function redoAction, std::string name) { this->undoAction = undoAction; this->redoAction = redoAction; this->name = name; @@ -64,6 +60,7 @@ class NODE_EDITOR_PUBLIC FlowScene std::vector undoActions; std::vector redoActions; void PrintActions(); + int historyInx; public: std::shared_ptrcreateConnection(PortType connectedPort, @@ -208,10 +205,8 @@ class NODE_EDITOR_PUBLIC FlowScene std::shared_ptr _registry; std::unordered_map> _groups; - int historyInx; bool writeToHistory; - std::vector< SceneHistory > history; - + private: }; diff --git a/src/FlowScene.cpp b/src/FlowScene.cpp index 28e0e3be9..8351af4bf 100644 --- a/src/FlowScene.cpp +++ b/src/FlowScene.cpp @@ -52,23 +52,25 @@ FlowScene(std::shared_ptr registry) auto UpdateLamda = [this](Node& n, const QPointF& newPosition, const QPointF &oldPosition) { resolveGroups(n); - - undoActions.push_back(UndoRedoAction( - [&n, oldPosition](double v) - { - n.nodeGraphicsObject().setPos(oldPosition); - return 0; - }, - [&n, newPosition](double v) - { - n.nodeGraphicsObject().setPos(newPosition); - return 0; - }, - "Moved Node " + n.nodeDataModel()->name().toStdString() - )); - redoActions.clear(); + QUuid id = n.id(); + AddAction( + UndoRedoAction( + [this, id, oldPosition](void *) + { + UniqueNode n = _nodes[id]; + n->nodeGraphicsObject().setPos(oldPosition); + return 0; + }, + [this, id, newPosition](void *) + { - PrintActions(); + UniqueNode n = _nodes[id]; + n->nodeGraphicsObject().setPos(newPosition); + return 0; + }, + "Moved Node " + n.nodeDataModel()->name().toStdString() + ) + ); }; connect(this, &FlowScene::nodeMoveFinished, this, UpdateLamda); @@ -77,20 +79,19 @@ FlowScene(std::shared_ptr registry) resolveGroups(g); - undoActions.push_back(UndoRedoAction( - [&g, oldPosition](double v) + AddAction(UndoRedoAction( + [&g, oldPosition](void *ptr) { g.groupGraphicsObject().setPos(oldPosition); return 0; }, - [&g, newPosition](double v) + [&g, newPosition](void *ptr) { g.groupGraphicsObject().setPos(newPosition); return 0; }, "Moved Group " + g.GetName().toStdString() )); - redoActions.clear(); }; connect(this, &FlowScene::groupMoveFinished, this, GroupUpdateLamda); @@ -926,7 +927,7 @@ loadFromMemory(const QByteArray& data) NodeDataModel *dm = nodeIn->nodeDataModel(); nameA = dm->name(); } - if(nameA == "InputVariable") return true; + if(QString::compare(nameA, QString("InputVariable")) == 0) return true; { QJsonObject objB = rhs.toObject(); @@ -939,7 +940,8 @@ loadFromMemory(const QByteArray& data) NodeDataModel *dm = nodeIn->nodeDataModel(); nameB = dm->name(); } - if(nameB == "InputVariable") return false; + if(QString::compare(nameB, QString("InputVariable")) == 0) return false; + //if(nameB == "InputVariable") return false; return posA.x() < posB.x(); }); @@ -993,6 +995,7 @@ void FlowScene::AddAction(QtNodes::UndoRedoAction action) undoActions.push_back(action); redoActions.clear(); PrintActions(); + historyInx++; } } @@ -1007,6 +1010,7 @@ void FlowScene::Undo() action.undoAction(0); redoActions.push_back(action); + historyInx--; writeToHistory = true; } } @@ -1023,6 +1027,7 @@ void FlowScene::Redo() undoActions.push_back(action); writeToHistory = true; + historyInx++; } } @@ -1030,18 +1035,17 @@ void FlowScene::Redo() void FlowScene::ResetHistory() { - historyInx = 0; - writeToHistory = true; - history.clear(); + historyInx=0; + undoActions.clear(); + redoActions.clear(); } + int FlowScene::GetHistoryIndex() { return historyInx; } - - //------------------------------------------------------------------------------ namespace QtNodes { diff --git a/src/FlowView.cpp b/src/FlowView.cpp index b33301309..edef2ae17 100644 --- a/src/FlowView.cpp +++ b/src/FlowView.cpp @@ -283,24 +283,27 @@ contextMenuEvent(QContextMenuEvent *event) return; } - QString parent = item->parent()->data(0, Qt::UserRole).toString(); - if(parent == "Templates") - { - DataModelRegistry::RegisteredTemplatesMap map = _scene->registry().RegisteredTemplates(); - QString fileName = map[modelName]; - - QFile file; - file.setFileName(fileName); - file.open(QIODevice::ReadOnly | QIODevice::Text); - QString val = file.readAll(); - file.close(); - qWarning() << val; - QJsonDocument d = QJsonDocument::fromJson(val.toUtf8()); - QJsonObject sett2 = d.object(); - jsonToSceneMousePos(sett2); - - modelMenu.close(); - } + if (item->parent() != nullptr) + { + QString parent = item->parent()->data(0, Qt::UserRole).toString(); + if(parent == "Templates") + { + DataModelRegistry::RegisteredTemplatesMap map = _scene->registry().RegisteredTemplates(); + QString fileName = map[modelName]; + + QFile file; + file.setFileName(fileName); + file.open(QIODevice::ReadOnly | QIODevice::Text); + QString val = file.readAll(); + file.close(); + qWarning() << val; + QJsonDocument d = QJsonDocument::fromJson(val.toUtf8()); + QJsonObject sett2 = d.object(); + jsonToSceneMousePos(sett2); + + modelMenu.close(); + } + } auto type = _scene->registry().create(modelName); @@ -313,12 +316,12 @@ contextMenuEvent(QContextMenuEvent *event) QUuid id = node.id(); _scene->AddAction(UndoRedoAction( - [this, id](double v) + [this, id](void *ptr) { _scene->removeNodeWithID(id); return 0; }, - [this, pos, modelName, id](double v) + [this, pos, modelName, id](void *ptr) { auto type = _scene->registry().create(modelName); auto& node = _scene->createNodeWithID(std::move(type), id); @@ -457,13 +460,13 @@ deleteSelectedNodes() _scene->AddAction(UndoRedoAction( //Undo action - [this, sceneJson](double v) + [this, sceneJson](void *ptr) { jsonToScene(sceneJson); return 0; }, //Redo action - [this, sceneJson](double v) + [this, sceneJson](void *ptr) { deleteJsonElements(sceneJson); return 0; @@ -594,7 +597,7 @@ void FlowView::jsonToSceneMousePos(QJsonObject jsonDocument) _scene->AddAction(UndoRedoAction( - [this, addedIds](double v) + [this, addedIds](void *ptr) { //Delete all the created nodes (and their connections) for(auto &id: addedIds) @@ -603,7 +606,7 @@ void FlowView::jsonToSceneMousePos(QJsonObject jsonDocument) } return 0; }, - [this, jsonDocument](double v) + [this, jsonDocument](void *ptr) { jsonToScene(jsonDocument); //jsonDocument has now been updated with new IDs and new positions return 0; diff --git a/src/NodeConnectionInteraction.cpp b/src/NodeConnectionInteraction.cpp index 9bc956ce1..c1aa73131 100644 --- a/src/NodeConnectionInteraction.cpp +++ b/src/NodeConnectionInteraction.cpp @@ -152,22 +152,23 @@ tryConnect() const outNode->onDataUpdatedConnection(outPortIndex, _connection); } - // _scene->UpdateHistory(); QUuid connectionID = _connection->id(); FlowScene *scene = _scene; - Node *nodeIn = _connection->getNode(PortType::In); - Node *nodeOut = _connection->getNode(PortType::Out); + QUuid nodeInID = _connection->getNode(PortType::In)->id(); + QUuid nodeOutID = _connection->getNode(PortType::Out)->id(); PortIndex portIn = _connection->getPortIndex(PortType::In); PortIndex portOut = _connection->getPortIndex(PortType::Out); _scene->AddAction(UndoRedoAction( - [scene, connectionID](double v) + [scene, connectionID](void *ptr) { scene->deleteConnectionWithID(connectionID); return 0; }, - [scene, nodeIn, portIn, nodeOut, portOut, connectionID](double v) + [scene, nodeInID, portIn, nodeOutID, portOut, connectionID](void *ptr) { QUuid id = connectionID; + Node *nodeIn = scene->nodes()[nodeInID].get(); + Node *nodeOut = scene->nodes()[nodeOutID].get(); scene->createConnection(*nodeIn, portIn, *nodeOut, @@ -189,6 +190,14 @@ bool NodeConnectionInteraction:: disconnect(PortType portToDisconnect) const { + + QUuid connectionID = _connection->id(); + FlowScene *scene = _scene; + Node *nodeIn = _connection->getNode(PortType::In); + Node *nodeOut = _connection->getNode(PortType::Out); + PortIndex portIn = _connection->getPortIndex(PortType::In); + PortIndex portOut = _connection->getPortIndex(PortType::Out); + PortIndex portIndex = _connection->getPortIndex(portToDisconnect); @@ -207,7 +216,24 @@ disconnect(PortType portToDisconnect) const _connection->getConnectionGraphicsObject().grabMouse(); - // _scene->UpdateHistory(); + _scene->AddAction(UndoRedoAction( + [scene, nodeIn, portIn, nodeOut, portOut, connectionID](void *ptr) + { + QUuid id = connectionID; + scene->createConnection(*nodeIn, + portIn, + *nodeOut, + portOut, + &id); + return 0; + }, + [scene, connectionID](void *ptr) + { + scene->deleteConnectionWithID(connectionID); + return 0; + }, + "Removed Connection " + )); return true; } From c0706451526b5f7e377e1164f3825bc4695a1ef0 Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Thu, 23 Nov 2023 16:44:14 +0000 Subject: [PATCH 30/39] added gotonode function --- include/nodes/internal/FlowView.hpp | 6 ++++++ src/FlowScene.cpp | 14 ++++++++++---- src/FlowView.cpp | 19 +++++++++++++++++++ src/NodeGraphicsObject.cpp | 2 +- 4 files changed, 36 insertions(+), 5 deletions(-) diff --git a/include/nodes/internal/FlowView.hpp b/include/nodes/internal/FlowView.hpp index d14df6338..159865712 100644 --- a/include/nodes/internal/FlowView.hpp +++ b/include/nodes/internal/FlowView.hpp @@ -8,6 +8,7 @@ namespace QtNodes { class FlowScene; +class NodeGraphicsObject; class NODE_EDITOR_PUBLIC FlowView : public QGraphicsView @@ -31,6 +32,10 @@ class NODE_EDITOR_PUBLIC FlowView void jsonToScene(QJsonObject object); void jsonToSceneMousePos(QJsonObject object); void deleteJsonElements(const QJsonObject &object); + + void goToNode(NodeGraphicsObject *node); + + public slots: void scaleUp(); @@ -66,6 +71,7 @@ public slots: void addAnchor(int index); void goToAnchor(int index); + protected: FlowScene * scene(); diff --git a/src/FlowScene.cpp b/src/FlowScene.cpp index 8351af4bf..1329712a0 100644 --- a/src/FlowScene.cpp +++ b/src/FlowScene.cpp @@ -927,7 +927,6 @@ loadFromMemory(const QByteArray& data) NodeDataModel *dm = nodeIn->nodeDataModel(); nameA = dm->name(); } - if(QString::compare(nameA, QString("InputVariable")) == 0) return true; { QJsonObject objB = rhs.toObject(); @@ -940,10 +939,17 @@ loadFromMemory(const QByteArray& data) NodeDataModel *dm = nodeIn->nodeDataModel(); nameB = dm->name(); } - if(QString::compare(nameB, QString("InputVariable")) == 0) return false; - //if(nameB == "InputVariable") return false; - return posA.x() < posB.x(); + if (QString::compare(nameA, QString("InputVariable")) == 0 && QString::compare(nameB, QString("InputVariable")) != 0) { + return false; // "InputVariable" goes at the end + } + else if (QString::compare(nameB, QString("InputVariable")) == 0 && QString::compare(nameA, QString("InputVariable")) != 0) { + return true; // "InputVariable" goes at the end + } + else { + return posA.x() < posB.x(); + } + }); diff --git a/src/FlowView.cpp b/src/FlowView.cpp index edef2ae17..c390b6909 100644 --- a/src/FlowView.cpp +++ b/src/FlowView.cpp @@ -31,6 +31,7 @@ using QtNodes::FlowView; using QtNodes::FlowScene; using QtNodes::Connection; using QtNodes::NodeConnectionInteraction; +using QtNodes::NodeGraphicsObject; FlowView:: FlowView(QWidget *parent) @@ -111,6 +112,22 @@ FlowView::goToAnchor(int index) setSceneRect(sceneRect().translated(difference.x(), difference.y())); } +void FlowView::goToNode(NodeGraphicsObject *node) +{ + qreal x1, y1, x2, y2; + sceneRect().getCoords(&x1, &y1, &x2, &y2); + QPointF currentPosition = QPointF((x2 + x1) * 0.5, (y1 + y2) * 0.5); + + QPointF difference = node->pos() - currentPosition; + + setSceneRect(sceneRect().translated(difference.x(), difference.y())); + + + float scaleX = 1.2f / transform().m11(); + float scaleY = 1.2f / transform().m22(); + scale(scaleX, scaleY); +} + void FlowView::setScene(FlowScene *scene) { @@ -394,6 +411,7 @@ wheelEvent(QWheelEvent *event) scaleUp(); else scaleDown(); + } @@ -410,6 +428,7 @@ scaleUp() return; scale(factor, factor); + } diff --git a/src/NodeGraphicsObject.cpp b/src/NodeGraphicsObject.cpp index b07d878c6..6ce2e6c14 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -122,7 +122,7 @@ embedQWidget() _proxyWidget->setPos(geom.widgetPosition()); update(); - + _proxyWidget->setContentsMargins(0, 0, 0, 0); _proxyWidget->setOpacity(1.0); _proxyWidget->setFlag(QGraphicsItem::ItemIgnoresParentOpacity); } From f5a177f47c452de37586eff2419a2dfad38f2ee7 Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Mon, 10 Jun 2024 12:00:23 +0100 Subject: [PATCH 31/39] added go to node id --- include/nodes/internal/FlowView.hpp | 1 + src/FlowScene.cpp | 5 +++-- src/FlowView.cpp | 6 ++++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/include/nodes/internal/FlowView.hpp b/include/nodes/internal/FlowView.hpp index 159865712..904fba717 100644 --- a/include/nodes/internal/FlowView.hpp +++ b/include/nodes/internal/FlowView.hpp @@ -34,6 +34,7 @@ class NODE_EDITOR_PUBLIC FlowView void deleteJsonElements(const QJsonObject &object); void goToNode(NodeGraphicsObject *node); + void goToNodeID(QUuid ID); public slots: diff --git a/src/FlowScene.cpp b/src/FlowScene.cpp index 1329712a0..e62ded465 100644 --- a/src/FlowScene.cpp +++ b/src/FlowScene.cpp @@ -940,11 +940,12 @@ loadFromMemory(const QByteArray& data) nameB = dm->name(); } + //We put input variables at the end, so we only compute them when all the data is loaded. if (QString::compare(nameA, QString("InputVariable")) == 0 && QString::compare(nameB, QString("InputVariable")) != 0) { - return false; // "InputVariable" goes at the end + return false; } else if (QString::compare(nameB, QString("InputVariable")) == 0 && QString::compare(nameA, QString("InputVariable")) != 0) { - return true; // "InputVariable" goes at the end + return true; } else { return posA.x() < posB.x(); diff --git a/src/FlowView.cpp b/src/FlowView.cpp index c390b6909..c54c92afa 100644 --- a/src/FlowView.cpp +++ b/src/FlowView.cpp @@ -128,6 +128,12 @@ void FlowView::goToNode(NodeGraphicsObject *node) scale(scaleX, scaleY); } +void +FlowView::goToNodeID(QUuid ID) +{ + goToNode(&(_scene->nodes()[ID]->nodeGraphicsObject())); +} + void FlowView::setScene(FlowScene *scene) { From 7e892614cd33a63236a22575821718506b7d0c04 Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Wed, 25 Sep 2024 17:22:38 +0100 Subject: [PATCH 32/39] added input colour to node --- include/nodes/internal/FlowScene.hpp | 3 +++ include/nodes/internal/Node.hpp | 4 ++++ include/nodes/internal/NodeStyle.hpp | 1 + resources/DefaultStyle.json | 1 + src/Node.cpp | 8 ++++++++ src/NodeGraphicsObject.cpp | 2 ++ src/NodePainter.cpp | 12 ++++++++---- src/NodePainter.hpp | 3 ++- src/NodeStyle.cpp | 1 + 9 files changed, 30 insertions(+), 5 deletions(-) diff --git a/include/nodes/internal/FlowScene.hpp b/include/nodes/internal/FlowScene.hpp index 0729495ac..9b59d50ff 100644 --- a/include/nodes/internal/FlowScene.hpp +++ b/include/nodes/internal/FlowScene.hpp @@ -174,6 +174,8 @@ class NODE_EDITOR_PUBLIC FlowScene void groupMoveFinished(Group& g, const QPointF& newLocation, const QPointF& oldLocation); + void nodeClicked(Node& n); + void nodeDoubleClicked(Node& n); void groupDoubleClicked(Group& g); @@ -187,6 +189,7 @@ class NODE_EDITOR_PUBLIC FlowScene void nodeHoverLeft(Node& n); void nodeContextMenu(Node& n, const QPointF& pos); + public: diff --git a/include/nodes/internal/Node.hpp b/include/nodes/internal/Node.hpp index 0736800ed..8659f81cc 100644 --- a/include/nodes/internal/Node.hpp +++ b/include/nodes/internal/Node.hpp @@ -102,9 +102,12 @@ class NODE_EDITOR_PUBLIC Node NodeDataModel* nodeDataModel() const; + void setInputSelected(int inx, bool selected); + int targetInputConnections=0; int currentInputConnections=0; + std::vector inputSelected; public slots: // data propagation @@ -137,6 +140,7 @@ public slots: // data propagation // painting + NodeGeometry _nodeGeometry; std::unique_ptr _nodeGraphicsObject; diff --git a/include/nodes/internal/NodeStyle.hpp b/include/nodes/internal/NodeStyle.hpp index 5f0518880..43f5d6b5d 100644 --- a/include/nodes/internal/NodeStyle.hpp +++ b/include/nodes/internal/NodeStyle.hpp @@ -39,6 +39,7 @@ class NODE_EDITOR_PUBLIC NodeStyle : public Style QColor ShadowColor; QColor FontColor; QColor FontColorFaded; + QColor FontColorSelected; QColor ConnectionPointColor; QColor FilledConnectionPointColor; diff --git a/resources/DefaultStyle.json b/resources/DefaultStyle.json index 8375b4a15..90ba80d0b 100644 --- a/resources/DefaultStyle.json +++ b/resources/DefaultStyle.json @@ -14,6 +14,7 @@ "ShadowColor": [20, 20, 20], "FontColor" : "white", "FontColorFaded" : "gray", + "FontColorSelected" : "yellow", "ConnectionPointColor": [169, 169, 169], "FilledConnectionPointColor": "cyan", "ErrorColor": "red", diff --git a/src/Node.cpp b/src/Node.cpp index 4d4df6df3..0e94c053c 100644 --- a/src/Node.cpp +++ b/src/Node.cpp @@ -39,9 +39,17 @@ Node(std::unique_ptr && dataModel) // propagate data: model => node connect(_nodeDataModel.get(), &NodeDataModel::dataUpdated, this, &Node::onDataUpdated); + + this->inputSelected.resize(_nodeDataModel->nPorts(PortType::In)); } +void +Node:: +setInputSelected(int inx, bool selected) +{ + this->inputSelected[inx] = selected; +} Node:: ~Node() {} diff --git a/src/NodeGraphicsObject.cpp b/src/NodeGraphicsObject.cpp index 6ce2e6c14..2aa927070 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -244,6 +244,8 @@ mousePressEvent(QGraphicsSceneMouseEvent * event) _scene.clearSelection(); } + _scene.nodeClicked(node()); + auto clickPort = [&](PortType portToCheck) { diff --git a/src/NodePainter.cpp b/src/NodePainter.cpp index c2e55712d..5147a07b8 100644 --- a/src/NodePainter.cpp +++ b/src/NodePainter.cpp @@ -46,7 +46,7 @@ paint(QPainter* painter, drawModelName(painter, geom, state, model); - drawEntryLabels(painter, geom, state, model); + drawEntryLabels(painter, geom, state, model, node.inputSelected); drawResizeRect(painter, geom, model); @@ -281,13 +281,14 @@ NodePainter:: drawEntryLabels(QPainter * painter, NodeGeometry const & geom, NodeState const & state, - NodeDataModel const * model) + NodeDataModel const * model, + std::vector &inputSelected) { QFontMetrics const & metrics = painter->fontMetrics(); auto drawPoints = - [&](PortType portType) + [&](PortType portType, std::vector *inputSelected=nullptr) { auto const &nodeStyle = model->nodeStyle(); @@ -304,6 +305,9 @@ drawEntryLabels(QPainter * painter, else painter->setPen(nodeStyle.FontColor); + if(inputSelected != nullptr && inputSelected->at(i)) + painter->setPen(nodeStyle.FontColorSelected); + QString s; if (model->portCaptionVisible(portType, i)) @@ -339,7 +343,7 @@ drawEntryLabels(QPainter * painter, drawPoints(PortType::Out); - drawPoints(PortType::In); + drawPoints(PortType::In, &inputSelected); } diff --git a/src/NodePainter.hpp b/src/NodePainter.hpp index a226d79c3..9ad1cd293 100644 --- a/src/NodePainter.hpp +++ b/src/NodePainter.hpp @@ -48,7 +48,8 @@ class NodePainter drawEntryLabels(QPainter* painter, NodeGeometry const& geom, NodeState const& state, - NodeDataModel const * model); + NodeDataModel const * model, + std::vector &inputColors); static void diff --git a/src/NodeStyle.cpp b/src/NodeStyle.cpp index c62e0ac9e..37efc0c25 100644 --- a/src/NodeStyle.cpp +++ b/src/NodeStyle.cpp @@ -123,6 +123,7 @@ loadJsonFromByteArray(QByteArray const &byteArray) NODE_STYLE_READ_COLOR(obj, ShadowColor); NODE_STYLE_READ_COLOR(obj, FontColor); NODE_STYLE_READ_COLOR(obj, FontColorFaded); + NODE_STYLE_READ_COLOR(obj, FontColorSelected); NODE_STYLE_READ_COLOR(obj, ConnectionPointColor); NODE_STYLE_READ_COLOR(obj, FilledConnectionPointColor); NODE_STYLE_READ_COLOR(obj, WarningColor); From 6f8c72d91ad23985706d840e8b937d64dc9a1bf7 Mon Sep 17 00:00:00 2001 From: Tom Date: Fri, 27 Sep 2024 16:47:19 +0100 Subject: [PATCH 33/39] fix crash --- src/NodeGraphicsObject.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/NodeGraphicsObject.cpp b/src/NodeGraphicsObject.cpp index 2aa927070..91703704c 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -185,6 +185,10 @@ paint(QPainter * painter, QStyleOptionGraphicsItem const* option, QWidget* ) { + if(_node.inputSelected.size() != _node.nodeDataModel()->nPorts(PortType::In)) + { + _node.inputSelected.resize(_node.nodeDataModel()->nPorts(PortType::In)); + } painter->setClipRect(option->exposedRect); NodePainter::paint(painter, _node, _scene); From bdd4e90fd6a7636dc2f21e62d6458c81213a1834 Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Fri, 30 May 2025 10:20:48 +0100 Subject: [PATCH 34/39] crash fix --- CMakeLists.txt | 2 +- src/FlowScene.cpp | 1 + src/Node.cpp | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 71390c589..c9a018b11 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.2) +cmake_minimum_required(VERSION 3.5) # version 3.4 is required as other do not work with C++14 and clang project(NodeEditor CXX) diff --git a/src/FlowScene.cpp b/src/FlowScene.cpp index e62ded465..f71f501ec 100644 --- a/src/FlowScene.cpp +++ b/src/FlowScene.cpp @@ -391,6 +391,7 @@ QUuid FlowScene::pasteNode(QJsonObject &nodeJson, QPointF nodeGroupCentroid, QPo auto nodePtr = node.get(); _nodes[node->id()] = std::move(node); nodeCreated(*nodePtr); + nodePtr->updateView(); return newId; } diff --git a/src/Node.cpp b/src/Node.cpp index 0e94c053c..04e6b6ab6 100644 --- a/src/Node.cpp +++ b/src/Node.cpp @@ -138,6 +138,7 @@ paste(QJsonObject const& json, QUuid ID) _nodeGraphicsObject->setPos(point); _nodeDataModel->restore(json["model"].toObject()); + // this->updateView(); } From 73ef40bd6667cc4f9cb4a099a1429468850eb9f7 Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Tue, 3 Jun 2025 09:04:15 +0100 Subject: [PATCH 35/39] added node not found signal --- include/nodes/internal/FlowView.hpp | 2 ++ src/FlowView.cpp | 48 ++++++++++++++++------------- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/include/nodes/internal/FlowView.hpp b/include/nodes/internal/FlowView.hpp index 904fba717..302e4bf1a 100644 --- a/include/nodes/internal/FlowView.hpp +++ b/include/nodes/internal/FlowView.hpp @@ -72,6 +72,8 @@ public slots: void addAnchor(int index); void goToAnchor(int index); +signals: + void nodeNotFound(const QString &str); protected: diff --git a/src/FlowView.cpp b/src/FlowView.cpp index c54c92afa..2308b32d5 100644 --- a/src/FlowView.cpp +++ b/src/FlowView.cpp @@ -306,27 +306,27 @@ contextMenuEvent(QContextMenuEvent *event) return; } - if (item->parent() != nullptr) - { - QString parent = item->parent()->data(0, Qt::UserRole).toString(); - if(parent == "Templates") - { - DataModelRegistry::RegisteredTemplatesMap map = _scene->registry().RegisteredTemplates(); - QString fileName = map[modelName]; - - QFile file; - file.setFileName(fileName); - file.open(QIODevice::ReadOnly | QIODevice::Text); - QString val = file.readAll(); - file.close(); - qWarning() << val; - QJsonDocument d = QJsonDocument::fromJson(val.toUtf8()); - QJsonObject sett2 = d.object(); - jsonToSceneMousePos(sett2); - - modelMenu.close(); - } - } + if (item->parent() != nullptr) + { + QString parent = item->parent()->data(0, Qt::UserRole).toString(); + if(parent == "Templates") + { + DataModelRegistry::RegisteredTemplatesMap map = _scene->registry().RegisteredTemplates(); + QString fileName = map[modelName]; + + QFile file; + file.setFileName(fileName); + file.open(QIODevice::ReadOnly | QIODevice::Text); + QString val = file.readAll(); + file.close(); + qWarning() << val; + QJsonDocument d = QJsonDocument::fromJson(val.toUtf8()); + QJsonObject sett2 = d.object(); + jsonToSceneMousePos(sett2); + + modelMenu.close(); + } + } auto type = _scene->registry().create(modelName); @@ -393,6 +393,12 @@ contextMenuEvent(QContextMenuEvent *event) } }); + connect(txtBox, &QLineEdit::returnPressed, [&]() { + QString text = txtBox->text(); + emit nodeNotFound(text); + modelMenu.close(); + }); + // make sure the text box gets focus so the user doesn't have to click on it txtBox->setFocus(); modelMenu.exec(event->globalPos()); From 42a5316c5cf804ae0d257eaba064b907f2b381ea Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Mon, 7 Jul 2025 08:58:34 +0100 Subject: [PATCH 36/39] added action signal --- include/nodes/internal/FlowScene.hpp | 6 ++++-- src/FlowScene.cpp | 12 ++++++------ src/FlowView.cpp | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/include/nodes/internal/FlowScene.hpp b/include/nodes/internal/FlowScene.hpp index 9b59d50ff..4f243791b 100644 --- a/include/nodes/internal/FlowScene.hpp +++ b/include/nodes/internal/FlowScene.hpp @@ -31,8 +31,8 @@ struct UndoRedoAction { std::function undoAction; std::function redoAction; - std::string name; - UndoRedoAction(std::function undoAction, std::function redoAction, std::string name) { + QString name; + UndoRedoAction(std::function undoAction, std::function redoAction, const QString &name) { this->undoAction = undoAction; this->redoAction = redoAction; this->name = name; @@ -189,6 +189,8 @@ class NODE_EDITOR_PUBLIC FlowScene void nodeHoverLeft(Node& n); void nodeContextMenu(Node& n, const QPointF& pos); + + void ActionAdded(const QString actionName); public: diff --git a/src/FlowScene.cpp b/src/FlowScene.cpp index f71f501ec..55e2e5236 100644 --- a/src/FlowScene.cpp +++ b/src/FlowScene.cpp @@ -68,7 +68,7 @@ FlowScene(std::shared_ptr registry) n->nodeGraphicsObject().setPos(newPosition); return 0; }, - "Moved Node " + n.nodeDataModel()->name().toStdString() + "Moved Node " + n.nodeDataModel()->name() ) ); }; @@ -90,7 +90,7 @@ FlowScene(std::shared_ptr registry) g.groupGraphicsObject().setPos(newPosition); return 0; }, - "Moved Group " + g.GetName().toStdString() + "Moved Group " + g.GetName() )); }; connect(this, &FlowScene::groupMoveFinished, this, GroupUpdateLamda); @@ -985,14 +985,14 @@ loadFromMemory(const QByteArray& data) void FlowScene::PrintActions() { - std::cout << "ACTIONS " << std::endl; + qDebug() << "ACTIONS "; for(int i=0; iname().toStdString() + "Created Node " + node.nodeDataModel()->name() )); } else From 21eee6e0a959015fb7bad56849ae0eecd6acbf28 Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Mon, 21 Jul 2025 14:07:45 +0100 Subject: [PATCH 37/39] Prevent duplicate connections in scene serialization Added logic to avoid saving duplicate connections when serializing the scene and when exporting selected nodes and connections. This ensures that each connection is only included once in the resulting JSON, improving data integrity and preventing redundant entries. --- src/FlowScene.cpp | 22 +++++++++++++++++++++- src/FlowView.cpp | 23 +++++++++++++++-------- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/FlowScene.cpp b/src/FlowScene.cpp index 55e2e5236..32f25a331 100644 --- a/src/FlowScene.cpp +++ b/src/FlowScene.cpp @@ -2,6 +2,8 @@ #include #include +#include +#include #include #include @@ -856,6 +858,8 @@ saveToMemory() const sceneJson["nodes"] = nodesJsonArray; QJsonArray connectionJsonArray; + std::set> uniqueConnections; + for (auto const & pair : _connections) { auto const &connection = pair.second; @@ -863,7 +867,23 @@ saveToMemory() const QJsonObject connectionJson = connection->save(); if (!connectionJson.isEmpty()) - connectionJsonArray.append(connectionJson); + { + // Extract connection details for duplicate detection + QUuid nodeInId = QUuid(connectionJson["in_id"].toString()); + QUuid nodeOutId = QUuid(connectionJson["out_id"].toString()); + PortIndex portIndexIn = connectionJson["in_index"].toInt(); + PortIndex portIndexOut = connectionJson["out_index"].toInt(); + + // Create a unique identifier tuple + auto connectionKey = std::make_tuple(nodeInId, portIndexIn, nodeOutId, portIndexOut); + + // Only add if we haven't seen this connection before + if (uniqueConnections.find(connectionKey) == uniqueConnections.end()) + { + uniqueConnections.insert(connectionKey); + connectionJsonArray.append(connectionJson); + } + } } sceneJson["connections"] = connectionJsonArray; diff --git a/src/FlowView.cpp b/src/FlowView.cpp index 33f8335d7..35d9f35e9 100644 --- a/src/FlowView.cpp +++ b/src/FlowView.cpp @@ -460,7 +460,7 @@ FlowView:: deleteSelectedNodes() { QJsonObject sceneJson = selectionToJson(true); - + qDebug() << sceneJson; std::vector nodeIds; std::vector connectionsIds; for (QGraphicsItem * item : _scene->selectedItems()) @@ -652,7 +652,8 @@ QJsonObject FlowView::selectionToJson(bool includePartialConnections) QJsonObject sceneJson; QJsonArray nodesJsonArray; QJsonArray connectionJsonArray; - std::vector addedIds; + std::set addedNodeIds; + std::set addedConnectionIds; for (QGraphicsItem * item : _scene->selectedItems()) { @@ -660,35 +661,41 @@ QJsonObject FlowView::selectionToJson(bool includePartialConnections) { Node& node = n->node(); nodesJsonArray.append(node.save()); - addedIds.push_back(node.id()); + addedNodeIds.insert(node.id()); - if(includePartialConnections) + if(includePartialConnections) //find all connections of that node, even if the other node is not selected { std::vector allConnections = node.nodeState().allConnections(); for(int i=0; iid()) != addedConnectionIds.end()) + continue; //Already added this connection + QJsonObject connectionJson = allConnections[i]->save(); if (!connectionJson.isEmpty()) + { connectionJsonArray.append(connectionJson); + addedConnectionIds.insert(allConnections[i]->id()); + } } } } } for (QGraphicsItem * item : _scene->selectedItems()) - { + { if (auto c = qgraphicsitem_cast(item)) { Connection& connection = c->connection(); - if((std::find(addedIds.begin(), addedIds.end(), connection.getNode(PortType::In)->id()) != addedIds.end() && - std::find(addedIds.begin(), addedIds.end(), connection.getNode(PortType::Out)->id()) != addedIds.end()) || includePartialConnections) { + if(addedConnectionIds.find(connection.id()) == addedConnectionIds.end()) { QJsonObject connectionJson = connection.save(); - if (!connectionJson.isEmpty()) connectionJsonArray.append(connectionJson); + addedConnectionIds.insert(connection.id()); } } } + QJsonArray groupJsonArray; for (QGraphicsItem * item : _scene->selectedItems()) From f0fada85297e6ed40fee5728f5e1b187bb05246a Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Mon, 8 Sep 2025 10:22:35 +0100 Subject: [PATCH 38/39] fixed line return on error messages --- src/FlowScene.cpp | 3 +++ src/NodeGeometry.cpp | 43 +++++++++++++++++++++++++++++++++++++++++-- src/NodePainter.cpp | 14 +++++++++----- 3 files changed, 53 insertions(+), 7 deletions(-) diff --git a/src/FlowScene.cpp b/src/FlowScene.cpp index 32f25a331..2d1955e1b 100644 --- a/src/FlowScene.cpp +++ b/src/FlowScene.cpp @@ -202,6 +202,9 @@ void FlowScene::pasteConnection(QJsonObject const &connectionJson, QUuid newIn, auto nodeIn = _nodes[newIn].get(); auto nodeOut = _nodes[newOut].get(); + if (!nodeIn || !nodeOut) + return; + createConnection(*nodeIn, portIndexIn, *nodeOut, portIndexOut); } diff --git a/src/NodeGeometry.cpp b/src/NodeGeometry.cpp index 6875c1753..36258c4f2 100644 --- a/src/NodeGeometry.cpp +++ b/src/NodeGeometry.cpp @@ -285,7 +285,25 @@ validationHeight() const { QString msg = _dataModel->validationMessage(); - return _boldFontMetrics.boundingRect(msg).height(); + if (msg.isEmpty()) + return 0; + + // Calculate height for multi-line text with word wrapping + // Use the node width minus padding for text wrapping + double padding = 8.0; // 4.0 padding on each side + int availableWidth = _width - static_cast(padding); + + // Ensure we have a minimum width for the calculation + if (availableWidth < 50) + availableWidth = 50; + + // Use boundingRect with width constraint to get the actual height needed for wrapped text + QRect boundingRect = _boldFontMetrics.boundingRect(0, 0, availableWidth, 0, + Qt::AlignCenter | Qt::TextWordWrap, + msg); + + // Add some padding for better visual appearance + return boundingRect.height() + static_cast(padding); } @@ -295,7 +313,28 @@ validationWidth() const { QString msg = _dataModel->validationMessage(); - return _boldFontMetrics.boundingRect(msg).width(); + if (msg.isEmpty()) + return 0; + + // Instead of using the full text width, return a reasonable maximum width + // that allows for text wrapping. This prevents the node from becoming too wide. + double padding = 8.0; // 4.0 padding on each side + int availableWidth = _width - static_cast(padding); + + // Ensure we have a minimum width for the calculation + if (availableWidth < 50) + availableWidth = 50; + QRect boundingRect = _boldFontMetrics.boundingRect(0, 0, availableWidth, 0, + Qt::AlignCenter | Qt::TextWordWrap, + msg); + int singleLineWidth = boundingRect.width(); + + // Set a maximum width - this can be adjusted based on your UI preferences + // int maxWidth = 300; // Adjust this value as needed + + // // Use the smaller of the two: either the natural text width or our maximum + // return std::min(singleLineWidth, maxWidth); + return singleLineWidth; } diff --git a/src/NodePainter.cpp b/src/NodePainter.cpp index 5147a07b8..d6bd07257 100644 --- a/src/NodePainter.cpp +++ b/src/NodePainter.cpp @@ -420,13 +420,17 @@ drawValidationRect(QPainter * painter, QFontMetrics metrics(f); - auto rect = metrics.boundingRect(errorMsg); - - QPointF position((geom.width() - rect.width()) / 2.0, - geom.height() - (geom.validationHeight() - diam) / 2.0); + // Calculate the available area for text with some padding + double padding = 4.0; + QRectF textRect(padding, + geom.height() - geom.validationHeight() + padding, + geom.width() - 2 * padding, + geom.validationHeight() - 2 * padding); painter->setFont(f); painter->setPen(nodeStyle.FontColor); - painter->drawText(position, errorMsg); + + // Use drawText with rectangle and flags to enable word wrapping and line breaks + painter->drawText(textRect, Qt::AlignCenter | Qt::TextWordWrap, errorMsg); } } From 24c2a790f0f3a907c46652c809c70acdad0f868c Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Thu, 18 Sep 2025 10:22:45 +0100 Subject: [PATCH 39/39] added placeholder nodes --- include/nodes/internal/FlowScene.hpp | 14 +- include/nodes/internal/FlowView.hpp | 7 +- include/nodes/internal/Node.hpp | 9 +- src/FlowScene.cpp | 303 ++++++++++++++++++++++++++- src/FlowView.cpp | 282 +------------------------ src/Node.cpp | 9 + src/NodeGraphicsObject.cpp | 11 +- 7 files changed, 348 insertions(+), 287 deletions(-) diff --git a/include/nodes/internal/FlowScene.hpp b/include/nodes/internal/FlowScene.hpp index 4f243791b..4a3d1d371 100644 --- a/include/nodes/internal/FlowScene.hpp +++ b/include/nodes/internal/FlowScene.hpp @@ -103,6 +103,8 @@ class NODE_EDITOR_PUBLIC FlowScene DataModelRegistry®istry() const; + std::shared_ptrregistryShared() const; + void setRegistry(std::shared_ptr registry); void iterateOverNodes(std::function visitor); @@ -128,7 +130,7 @@ class NODE_EDITOR_PUBLIC FlowScene std::unordered_map > const &connections() const; - std::vector>selectedNodes() const; + std::vector selectedNodes() const; public: @@ -154,6 +156,16 @@ class NODE_EDITOR_PUBLIC FlowScene int GetHistoryIndex(); + void deleteSelectedNodes(); + + QJsonObject selectionToJson(bool includePartialConnections=false); + + void jsonToScene(QJsonObject object); + + void jsonToSceneMousePos(QJsonObject object, QPointF mousePos); + + void deleteJsonElements(const QJsonObject &object); + signals: void nodeCreated(Node &n); diff --git a/include/nodes/internal/FlowView.hpp b/include/nodes/internal/FlowView.hpp index 302e4bf1a..dfde0dd2e 100644 --- a/include/nodes/internal/FlowView.hpp +++ b/include/nodes/internal/FlowView.hpp @@ -28,12 +28,10 @@ class NODE_EDITOR_PUBLIC FlowView void setScene(FlowScene *scene); - QJsonObject selectionToJson(bool includePartialConnections=false); - void jsonToScene(QJsonObject object); void jsonToSceneMousePos(QJsonObject object); - void deleteJsonElements(const QJsonObject &object); - + void goToNode(NodeGraphicsObject *node); + void goToNodeID(QUuid ID); @@ -43,7 +41,6 @@ public slots: void scaleDown(); - void deleteSelectedNodes(); void duplicateSelectedNode(); diff --git a/include/nodes/internal/Node.hpp b/include/nodes/internal/Node.hpp index 8659f81cc..c3a5000ae 100644 --- a/include/nodes/internal/Node.hpp +++ b/include/nodes/internal/Node.hpp @@ -56,9 +56,6 @@ class NODE_EDITOR_PUBLIC Node paste(QJsonObject const &json, QUuid ID); - void - updateView(); - void eraseInputAtIndex(int portIndex); @@ -102,6 +99,12 @@ class NODE_EDITOR_PUBLIC Node NodeDataModel* nodeDataModel() const; + void + updateView(); + + void + updateEntries(); + void setInputSelected(int inx, bool selected); int targetInputConnections=0; diff --git a/src/FlowScene.cpp b/src/FlowScene.cpp index 32f25a331..00926f238 100644 --- a/src/FlowScene.cpp +++ b/src/FlowScene.cpp @@ -148,11 +148,12 @@ createConnection(Node& nodeIn, auto cgo = std::make_unique(*this, *connection); + // after this function connection points are set to node port + connection->setGraphicsObject(std::move(cgo)); + nodeIn.nodeState().setConnection(PortType::In, portIndexIn, *connection); nodeOut.nodeState().setConnection(PortType::Out, portIndexOut, *connection); - // after this function connection points are set to node port - connection->setGraphicsObject(std::move(cgo)); // trigger data propagation nodeOut.onDataUpdated(portIndexOut); @@ -487,6 +488,12 @@ registry() const return *_registry; } +std::shared_ptr +FlowScene::registryShared() const +{ + return _registry; +} + void FlowScene:: @@ -728,13 +735,13 @@ connections() const } -std::vector> +std::vector FlowScene:: selectedNodes() const { QList graphicsItems = selectedItems(); - std::vector> ret; + std::vector ret; ret.reserve(graphicsItems.size()); @@ -745,8 +752,7 @@ selectedNodes() const if (ngo != nullptr) { Node* n = &(ngo->node()); - std::shared_ptr ptr(n); - ret.push_back(ptr); + ret.push_back(n); } } @@ -1074,6 +1080,291 @@ int FlowScene::GetHistoryIndex() { } + +void +FlowScene:: +deleteSelectedNodes() +{ + QJsonObject sceneJson = selectionToJson(true); + qDebug() << sceneJson; + std::vector nodeIds; + std::vector connectionsIds; + for (QGraphicsItem * item : selectedItems()) + { + if(item) + { + if (auto c = qgraphicsitem_cast(item)) + { + deleteConnection(c->connection()); + } + } + } + for (QGraphicsItem * item : selectedItems()) + { + if(item) + { + if (auto c = qgraphicsitem_cast(item)) + { + removeGroup(c->group()); + } + else if (auto c = qgraphicsitem_cast(item)) + { + removeNode(c->node()); + } + } + } + + + AddAction(UndoRedoAction( + //Undo action + [this, sceneJson](void *ptr) + { + jsonToScene(sceneJson); + return 0; + }, + //Redo action + [this, sceneJson](void *ptr) + { + deleteJsonElements(sceneJson); + return 0; + }, + //Name + "Deletes nodes" + )); +} + +QJsonObject FlowScene::selectionToJson(bool includePartialConnections) +{ + QJsonObject sceneJson; + QJsonArray nodesJsonArray; + QJsonArray connectionJsonArray; + std::set addedNodeIds; + std::set addedConnectionIds; + + for (QGraphicsItem * item : selectedItems()) + { + if (auto n = qgraphicsitem_cast(item)) + { + Node& node = n->node(); + nodesJsonArray.append(node.save()); + addedNodeIds.insert(node.id()); + + if(includePartialConnections) //find all connections of that node, even if the other node is not selected + { + std::vector allConnections = node.nodeState().allConnections(); + for(int i=0; iid()) != addedConnectionIds.end()) + continue; //Already added this connection + + QJsonObject connectionJson = allConnections[i]->save(); + if (!connectionJson.isEmpty()) + { + connectionJsonArray.append(connectionJson); + addedConnectionIds.insert(allConnections[i]->id()); + } + } + } + } + } + + if(!includePartialConnections) + { + for (QGraphicsItem * item : selectedItems()) + { + if (auto c = qgraphicsitem_cast(item)) { + Connection& connection = c->connection(); + QUuid inNodeId = connection.getNode(PortType::In)->id(); + QUuid outNodeId = connection.getNode(PortType::Out)->id(); + if(addedNodeIds.find(inNodeId) == addedNodeIds.end()) continue; //The input node is not selected + if(addedNodeIds.find(outNodeId) == addedNodeIds.end()) continue; //The output node is not selected + + if(addedConnectionIds.find(connection.id()) == addedConnectionIds.end()) { + QJsonObject connectionJson = connection.save(); + if (!connectionJson.isEmpty()) + connectionJsonArray.append(connectionJson); + addedConnectionIds.insert(connection.id()); + } + } + } + } + + + QJsonArray groupJsonArray; + for (QGraphicsItem * item : selectedItems()) + { + if (auto c = qgraphicsitem_cast(item)) { + Group& group = c->group(); + + //If collapsed + if(group.groupGraphicsObject().isCollapsed()) + { + //Add all child items to nodes array + for(int i=0; i(group.groupGraphicsObject().childItems()[i]); + if(ngo!=nullptr) + { + Node& node = ngo->node(); + nodesJsonArray.append(node.save()); + } + } + + //Add all connections to nodes array + for(int i=0; isave(); + if (!connectionJson.isEmpty()) + connectionJsonArray.append(connectionJson); + } + } + } + + QJsonObject groupJson = group.save(); + if (!groupJson.isEmpty()) + groupJsonArray.append(groupJson); + } + } + + sceneJson["nodes"] = nodesJsonArray; + sceneJson["connections"] = connectionJsonArray; + sceneJson["groups"] = groupJsonArray; + + return sceneJson; +} + +void FlowScene::jsonToScene(QJsonObject jsonDocument) +{ + QJsonArray nodesJsonArray = jsonDocument["nodes"].toArray(); + + + + for (int i = 0; i < nodesJsonArray.size(); ++i) + { + restoreNode(nodesJsonArray[i].toObject(), true); + } + + QJsonArray connectionJsonArray = jsonDocument["connections"].toArray(); + for (int i = 0; i < connectionJsonArray.size(); ++i) + { + QUuid in = QUuid(connectionJsonArray[i].toObject()["in_id"].toString()); + QUuid out = QUuid(connectionJsonArray[i].toObject()["out_id"].toString()); + + qDebug() << "Creating connection from " << in << " to " << out; + + pasteConnection(connectionJsonArray[i].toObject(), in, out ); + } + + QJsonArray groupsJsonArray = jsonDocument["groups"].toArray(); + for (int i = 0; i < groupsJsonArray.size(); ++i) + { + restoreGroup(groupsJsonArray[i].toObject()); + } +} + +void FlowScene::jsonToSceneMousePos(QJsonObject jsonDocument, QPointF mousePos) +{ + QJsonArray nodesJsonArray = jsonDocument["nodes"].toArray(); + + std::map addedIds; + + //Get Bounds of all the selected items + float minx = 10000000000; + float miny = 10000000000; + float maxx = -1000000000; + float maxy = -1000000000; + for (int i = 0; i < nodesJsonArray.size(); ++i) + { + QJsonObject nodeJsonObject = nodesJsonArray[i].toObject(); + QJsonObject positionJson = nodeJsonObject["position"].toObject(); + QPointF pos(positionJson["x"].toDouble(), positionJson["y"].toDouble()); + + if(pos.x() < minx) minx = pos.x(); + if(pos.y() < miny) miny = pos.y(); + if(pos.x() > maxx) maxx = pos.x(); + if(pos.y() > maxy) maxy = pos.y(); + } + + float centroidX = (maxx - minx) / 2.0 + minx; + float centroidY = (maxy - miny) / 2.0 + miny; + QPointF centroid(centroidX, centroidY); + + + + + + for (int i = 0; i < nodesJsonArray.size(); ++i) + { + QJsonObject nodeJson = nodesJsonArray[i].toObject(); + QUuid currentId = QUuid( nodeJson["id"].toString() ); + QUuid newId = pasteNode(nodesJsonArray[i].toObject(), centroid, mousePos); + + addedIds.insert(std::pair(currentId, newId)); + nodesJsonArray[i] = nodes()[newId]->save(); + } + jsonDocument["nodes"] = nodesJsonArray; + + QJsonArray connectionJsonArray = jsonDocument["connections"].toArray(); + for (int i = 0; i < connectionJsonArray.size(); ++i) + { + QJsonObject connectionJson = connectionJsonArray[i].toObject(); + + QUuid in = QUuid(connectionJson["in_id"].toString()); + QUuid newIn = addedIds[in]; + + QUuid out = QUuid(connectionJson["out_id"].toString()); + QUuid newOut = addedIds[out]; + + pasteConnection(connectionJson, newIn, newOut ); + + connectionJson["in_id"] = newIn.toString(); + connectionJson["out_id"] = newOut.toString(); + connectionJsonArray[i] = connectionJson; + } + jsonDocument["connections"] =connectionJsonArray; + + QJsonArray groupsJsonArray = jsonDocument["groups"].toArray(); + for (int i = 0; i < groupsJsonArray.size(); ++i) + { + pasteGroup(groupsJsonArray[i].toObject(), centroid, mousePos); + } + + + AddAction(UndoRedoAction( + [this, addedIds](void *ptr) + { + //Delete all the created nodes (and their connections) + for(auto &id: addedIds) + { + removeNodeWithID(id.second); + } + return 0; + }, + [this, jsonDocument](void *ptr) + { + jsonToScene(jsonDocument); //jsonDocument has now been updated with new IDs and new positions + return 0; + }, + "Created Node " + )); + +} + +void FlowScene::deleteJsonElements(const QJsonObject &jsonDocument) +{ + QJsonArray nodesJsonArray = jsonDocument["nodes"].toArray(); + for (int i = 0; i < nodesJsonArray.size(); ++i) + { + QJsonObject nodeJson = nodesJsonArray[i].toObject(); + QUuid id = QUuid( nodeJson["id"].toString() ); + removeNodeWithID(id); + } +} + + + //------------------------------------------------------------------------------ namespace QtNodes { diff --git a/src/FlowView.cpp b/src/FlowView.cpp index 35d9f35e9..7a875934d 100644 --- a/src/FlowView.cpp +++ b/src/FlowView.cpp @@ -152,7 +152,11 @@ FlowView::setScene(FlowScene *scene) _deleteSelectionAction = new QAction(QStringLiteral("Delete Selection"), this); _deleteSelectionAction->setShortcut(Qt::Key_Delete); _deleteSelectionAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); - connect(_deleteSelectionAction, &QAction::triggered, this, &FlowView::deleteSelectedNodes); + connect(_deleteSelectionAction, &QAction::triggered, this, [this]() { + if (_scene) { + _scene->deleteSelectedNodes(); + } + }); addAction(_deleteSelectionAction); _duplicateSelectionAction = new QAction(QStringLiteral("Duplicate Selection"), this); @@ -455,72 +459,16 @@ scaleDown() } -void -FlowView:: -deleteSelectedNodes() -{ - QJsonObject sceneJson = selectionToJson(true); - qDebug() << sceneJson; - std::vector nodeIds; - std::vector connectionsIds; - for (QGraphicsItem * item : _scene->selectedItems()) - { - if(item) - { - if (auto c = qgraphicsitem_cast(item)) - { - _scene->deleteConnection(c->connection()); - } - } - } - for (QGraphicsItem * item : _scene->selectedItems()) - { - if(item) - { - if (auto c = qgraphicsitem_cast(item)) - { - _scene->removeGroup(c->group()); - } - else if (auto c = qgraphicsitem_cast(item)) - { - _scene->removeNode(c->node()); - } - } - } - - - _scene->AddAction(UndoRedoAction( - //Undo action - [this, sceneJson](void *ptr) - { - jsonToScene(sceneJson); - return 0; - }, - //Redo action - [this, sceneJson](void *ptr) - { - deleteJsonElements(sceneJson); - return 0; - }, - //Name - "Deletes nodes" - )); -} -void FlowView::deleteJsonElements(const QJsonObject &jsonDocument) -{ - QJsonArray nodesJsonArray = jsonDocument["nodes"].toArray(); - for (int i = 0; i < nodesJsonArray.size(); ++i) - { - QJsonObject nodeJson = nodesJsonArray[i].toObject(); - QUuid id = QUuid( nodeJson["id"].toString() ); - _scene->removeNodeWithID(id); - } +void FlowView::jsonToSceneMousePos(QJsonObject jsonDocument) { + QPoint viewPointMouse = this->mapFromGlobal(QCursor::pos()); + QPointF posViewMouse = this->mapToScene(viewPointMouse); + _scene->jsonToSceneMousePos(jsonDocument, posViewMouse); } void FlowView::copySelectedNodes() { - QJsonObject sceneJson = selectionToJson(); + QJsonObject sceneJson = _scene->selectionToJson(); QJsonDocument document(sceneJson); std::string json = document.toJson().toStdString(); @@ -529,218 +477,10 @@ void FlowView::copySelectedNodes() { p_Clipboard->setText( QString::fromStdString(json)); } -void FlowView::jsonToScene(QJsonObject jsonDocument) -{ - QJsonArray nodesJsonArray = jsonDocument["nodes"].toArray(); - - - - for (int i = 0; i < nodesJsonArray.size(); ++i) - { - _scene->restoreNode(nodesJsonArray[i].toObject(), true); - } - - QJsonArray connectionJsonArray = jsonDocument["connections"].toArray(); - for (int i = 0; i < connectionJsonArray.size(); ++i) - { - QUuid in = QUuid(connectionJsonArray[i].toObject()["in_id"].toString()); - QUuid out = QUuid(connectionJsonArray[i].toObject()["out_id"].toString()); - - _scene->pasteConnection(connectionJsonArray[i].toObject(), in, out ); - } - - QJsonArray groupsJsonArray = jsonDocument["groups"].toArray(); - for (int i = 0; i < groupsJsonArray.size(); ++i) - { - _scene->restoreGroup(groupsJsonArray[i].toObject()); - } -} - - -void FlowView::jsonToSceneMousePos(QJsonObject jsonDocument) -{ - QJsonArray nodesJsonArray = jsonDocument["nodes"].toArray(); - - std::map addedIds; - - //Get Bounds of all the selected items - float minx = 10000000000; - float miny = 10000000000; - float maxx = -1000000000; - float maxy = -1000000000; - for (int i = 0; i < nodesJsonArray.size(); ++i) - { - QJsonObject nodeJsonObject = nodesJsonArray[i].toObject(); - QJsonObject positionJson = nodeJsonObject["position"].toObject(); - QPointF pos(positionJson["x"].toDouble(), positionJson["y"].toDouble()); - - if(pos.x() < minx) minx = pos.x(); - if(pos.y() < miny) miny = pos.y(); - if(pos.x() > maxx) maxx = pos.x(); - if(pos.y() > maxy) maxy = pos.y(); - } - - float centroidX = (maxx - minx) / 2.0 + minx; - float centroidY = (maxy - miny) / 2.0 + miny; - QPointF centroid(centroidX, centroidY); - - QPoint viewPointMouse = this->mapFromGlobal(QCursor::pos()); - QPointF posViewMouse = this->mapToScene(viewPointMouse); - - - - - for (int i = 0; i < nodesJsonArray.size(); ++i) - { - QJsonObject nodeJson = nodesJsonArray[i].toObject(); - QUuid currentId = QUuid( nodeJson["id"].toString() ); - QUuid newId = _scene->pasteNode(nodesJsonArray[i].toObject(), centroid, posViewMouse); - - addedIds.insert(std::pair(currentId, newId)); - nodesJsonArray[i] = _scene->nodes()[newId]->save(); - } - jsonDocument["nodes"] = nodesJsonArray; - - QJsonArray connectionJsonArray = jsonDocument["connections"].toArray(); - for (int i = 0; i < connectionJsonArray.size(); ++i) - { - QJsonObject connectionJson = connectionJsonArray[i].toObject(); - - QUuid in = QUuid(connectionJson["in_id"].toString()); - QUuid newIn = addedIds[in]; - - QUuid out = QUuid(connectionJson["out_id"].toString()); - QUuid newOut = addedIds[out]; - - _scene->pasteConnection(connectionJson, newIn, newOut ); - - connectionJson["in_id"] = newIn.toString(); - connectionJson["out_id"] = newOut.toString(); - connectionJsonArray[i] = connectionJson; - } - jsonDocument["connections"] =connectionJsonArray; - - QJsonArray groupsJsonArray = jsonDocument["groups"].toArray(); - for (int i = 0; i < groupsJsonArray.size(); ++i) - { - _scene->pasteGroup(groupsJsonArray[i].toObject(), centroid, posViewMouse); - } - - - _scene->AddAction(UndoRedoAction( - [this, addedIds](void *ptr) - { - //Delete all the created nodes (and their connections) - for(auto &id: addedIds) - { - _scene->removeNodeWithID(id.second); - } - return 0; - }, - [this, jsonDocument](void *ptr) - { - jsonToScene(jsonDocument); //jsonDocument has now been updated with new IDs and new positions - return 0; - }, - "Created Node " - )); - -} - -QJsonObject FlowView::selectionToJson(bool includePartialConnections) -{ - QJsonObject sceneJson; - QJsonArray nodesJsonArray; - QJsonArray connectionJsonArray; - std::set addedNodeIds; - std::set addedConnectionIds; - - for (QGraphicsItem * item : _scene->selectedItems()) - { - if (auto n = qgraphicsitem_cast(item)) - { - Node& node = n->node(); - nodesJsonArray.append(node.save()); - addedNodeIds.insert(node.id()); - - if(includePartialConnections) //find all connections of that node, even if the other node is not selected - { - std::vector allConnections = node.nodeState().allConnections(); - for(int i=0; iid()) != addedConnectionIds.end()) - continue; //Already added this connection - - QJsonObject connectionJson = allConnections[i]->save(); - if (!connectionJson.isEmpty()) - { - connectionJsonArray.append(connectionJson); - addedConnectionIds.insert(allConnections[i]->id()); - } - } - } - } - } - - for (QGraphicsItem * item : _scene->selectedItems()) - { - if (auto c = qgraphicsitem_cast(item)) { - Connection& connection = c->connection(); - - if(addedConnectionIds.find(connection.id()) == addedConnectionIds.end()) { - QJsonObject connectionJson = connection.save(); - if (!connectionJson.isEmpty()) - connectionJsonArray.append(connectionJson); - addedConnectionIds.insert(connection.id()); - } - } - } - - QJsonArray groupJsonArray; - for (QGraphicsItem * item : _scene->selectedItems()) - { - if (auto c = qgraphicsitem_cast(item)) { - Group& group = c->group(); - - //If collapsed - if(group.groupGraphicsObject().isCollapsed()) - { - //Add all child items to nodes array - for(int i=0; i(group.groupGraphicsObject().childItems()[i]); - if(ngo!=nullptr) - { - Node& node = ngo->node(); - nodesJsonArray.append(node.save()); - } - } - - //Add all connections to nodes array - for(int i=0; isave(); - if (!connectionJson.isEmpty()) - connectionJsonArray.append(connectionJson); - } - } - } - QJsonObject groupJson = group.save(); - if (!groupJson.isEmpty()) - groupJsonArray.append(groupJson); - } - } - sceneJson["nodes"] = nodesJsonArray; - sceneJson["connections"] = connectionJsonArray; - sceneJson["groups"] = groupJsonArray; - return sceneJson; -} void FlowView::pasteSelectedNodes() { QClipboard *p_Clipboard = QApplication::clipboard(); @@ -756,7 +496,7 @@ void FlowView::pasteSelectedNodes() { void FlowView::duplicateSelectedNode() { - QJsonObject selectionJson = selectionToJson(); + QJsonObject selectionJson = _scene->selectionToJson(); jsonToSceneMousePos(selectionJson); } diff --git a/src/Node.cpp b/src/Node.cpp index 04e6b6ab6..5bd395e15 100644 --- a/src/Node.cpp +++ b/src/Node.cpp @@ -44,6 +44,13 @@ Node(std::unique_ptr && dataModel) } +void +Node:: +updateEntries() +{ + _nodeState.updateEntries(); +} + void Node:: setInputSelected(int inx, bool selected) @@ -92,6 +99,8 @@ void Node:: updateView() { + nodeGraphicsObject().setGeometryChanged(); + nodeGeometry().recalculateSize(); nodeGeometry().recalculateInOut(); nodeState().updateEntries(); diff --git a/src/NodeGraphicsObject.cpp b/src/NodeGraphicsObject.cpp index 91703704c..68ab07acb 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -335,7 +335,16 @@ mouseMoveEvent(QGraphicsSceneMouseEvent * event) oldSize += QSize(diff.x(), diff.y()); - w->setFixedSize(oldSize); + int minHeight = w->minimumHeight(); + + if(oldSize.height() < w->minimumHeight()) + oldSize.setHeight(w->minimumHeight()); + + if(oldSize.width() < w->minimumWidth()) + oldSize.setWidth(w->minimumWidth()); + + + w->setMaximumSize(oldSize); _proxyWidget->setMinimumSize(oldSize); _proxyWidget->setMaximumSize(oldSize);