From 27433e0fd1fc12b9730d3cb4ce6944ae7f517b6e Mon Sep 17 00:00:00 2001 From: ReubenJCarter Date: Wed, 8 Nov 2017 17:34:24 +0000 Subject: [PATCH 01/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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/33] 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);