From 27433e0fd1fc12b9730d3cb4ce6944ae7f517b6e Mon Sep 17 00:00:00 2001 From: ReubenJCarter Date: Wed, 8 Nov 2017 17:34:24 +0000 Subject: [PATCH 01/39] Added duplicate action to the flow view. Removed shadow effect from nodes as the performance was bad. Added OpenGL rendering for flow view. --- src/FlowView.cpp | 78 ++++++++++++++++++++++++++++++++++++-- src/FlowView.hpp | 3 ++ src/NodeGraphicsObject.cpp | 12 +++--- 3 files changed, 84 insertions(+), 9 deletions(-) diff --git a/src/FlowView.cpp b/src/FlowView.cpp index 5a3b6143..1a7a636c 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 1440fe15..a16d4f15 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 19bc46aa..185b5150 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -43,12 +43,12 @@ NodeGraphicsObject(FlowScene &scene, auto const &nodeStyle = StyleCollection::nodeStyle(); { - auto effect = new QGraphicsDropShadowEffect; - effect->setOffset(4, 4); - effect->setBlurRadius(20); - effect->setColor(nodeStyle.ShadowColor); - - setGraphicsEffect(effect); + //auto effect = new QGraphicsDropShadowEffect; + //effect->setOffset(4, 4); + //effect->setBlurRadius(0); + //effect->setColor(nodeStyle.ShadowColor); + //auto effect = new QGraphicsColorizeEffect ; + //setGraphicsEffect(effect); } setOpacity(nodeStyle.Opacity); From 4b90d7d0c2dfd79be212d756b9ffc5eff26e99f1 Mon Sep 17 00:00:00 2001 From: ReubenJCarter Date: Wed, 8 Nov 2017 18:04:23 +0000 Subject: [PATCH 02/39] Added duplicating connections, there is a bug to handle connections that have no endpoint on selection --- src/FlowView.cpp | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/FlowView.cpp b/src/FlowView.cpp index 1a7a636c..ca99ea3a 100644 --- a/src/FlowView.cpp +++ b/src/FlowView.cpp @@ -318,7 +318,41 @@ void FlowView::duplicateSelectedNode() } //create connections - + std::vector > createdConnections; + for (QGraphicsItem * item : _scene->selectedItems()) + { + if (auto c = qgraphicsitem_cast(item)) + { + //if(c->connection().connectionState().) + + Node* nodeIn = c->connection().getNode(PortType::In); + PortIndex portIndexIn = c->connection().getPortIndex(PortType::In); + Node* nodeOut = c->connection().getNode(PortType::Out); + PortIndex portIndexOut = c->connection().getPortIndex(PortType::Out); + + //find index of node in couterpartNode Array + int j = -1; + for(j = 0; j < couterpartNode.size(); j++) + { + if(couterpartNode[j] == nodeIn) + break; + } + + int k = -1; + for(k = 0; k < couterpartNode.size(); k++) + { + if(couterpartNode[k] == nodeOut) + break; + } + + if(j >=0 && k>=0) + { + auto& connection = _scene->createConnection(*createdNodes[j], portIndexIn, *createdNodes[k], portIndexOut); + createdConnections.push_back(connection); + } + } + } + //reset selection to nodes created _scene->clearSelection(); @@ -326,6 +360,10 @@ void FlowView::duplicateSelectedNode() { createdNodes[i]->nodeGraphicsObject().setSelected(true); } + for(int i = 0; i < createdConnections.size(); i++) + { + createdConnections[i]->getConnectionGraphicsObject().setSelected(true); + } } From c5080282772d2f0ca7962c22b97686ff32b9ba4d Mon Sep 17 00:00:00 2001 From: ReubenJCarter Date: Thu, 9 Nov 2017 09:44:14 +0000 Subject: [PATCH 03/39] added CTRL+D key sequence for duplicate --- src/FlowView.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/FlowView.cpp b/src/FlowView.cpp index ca99ea3a..e47995bd 100644 --- a/src/FlowView.cpp +++ b/src/FlowView.cpp @@ -63,7 +63,7 @@ FlowView(FlowScene *scene) _duplicateSelectionAction = new QAction(QStringLiteral("Delete Selection"), this); - _duplicateSelectionAction->setShortcut(Qt::Key_D); + _duplicateSelectionAction->setShortcut(QKeySequence(tr("Ctrl+D"))); connect(_duplicateSelectionAction, &QAction::triggered, this, &FlowView::duplicateSelectedNode); addAction(_duplicateSelectionAction); @@ -345,7 +345,7 @@ void FlowView::duplicateSelectedNode() break; } - if(j >=0 && k>=0) + if(j >=0 && k>=0 && j < couterpartNode.size() && k < couterpartNode.size()) { auto& connection = _scene->createConnection(*createdNodes[j], portIndexIn, *createdNodes[k], portIndexOut); createdConnections.push_back(connection); From facd31b1d6e2e414cd209cd4591c0e766c733980 Mon Sep 17 00:00:00 2001 From: ReubenJCarter Date: Thu, 9 Nov 2017 11:04:13 +0000 Subject: [PATCH 04/39] Fixed node delete pointer error --- src/FlowView.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/FlowView.cpp b/src/FlowView.cpp index e47995bd..8d90b74b 100644 --- a/src/FlowView.cpp +++ b/src/FlowView.cpp @@ -248,17 +248,25 @@ void FlowView:: deleteSelectedNodes() { + std::cout << "deleteSelectedNodes" << std::endl; // delete the nodes, this will delete many of the connections - for (QGraphicsItem * item : _scene->selectedItems()) + std::vector nodeItems = _scene->selectedNodes(); + for(int i = 0; i < nodeItems.size(); i++) { - if (auto n = qgraphicsitem_cast(item)) - _scene->removeNode(n->node()); + Node* item = nodeItems[i]; + if(item) + { + _scene->removeNode(*item); + } } for (QGraphicsItem * item : _scene->selectedItems()) { - if (auto c = qgraphicsitem_cast(item)) - _scene->deleteConnection(c->connection()); + if(item) + { + if (auto c = qgraphicsitem_cast(item)) + _scene->deleteConnection(c->connection()); + } } } From a92ba580e877f2ebd0519c5266130978cb05eb15 Mon Sep 17 00:00:00 2001 From: ReubenJCarter Date: Thu, 9 Nov 2017 14:34:35 +0000 Subject: [PATCH 05/39] Added very simple Undo Redo system --- src/FlowScene.cpp | 69 +++++++++++++++++++++++++++++++ src/FlowScene.hpp | 17 ++++++++ src/FlowView.cpp | 22 +++++++++- src/FlowView.hpp | 2 + src/NodeConnectionInteraction.cpp | 6 +++ 5 files changed, 114 insertions(+), 2 deletions(-) diff --git a/src/FlowScene.cpp b/src/FlowScene.cpp index a8d854c6..7eb70933 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 aa00de90..58108133 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 8d90b74b..574bde22 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 a16d4f15..aed92f1d 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 acab66ff..1fd5c116 100644 --- a/src/NodeConnectionInteraction.cpp +++ b/src/NodeConnectionInteraction.cpp @@ -114,11 +114,13 @@ tryConnect() const { _scene->createConnection(converterNode, 0, *outNode, outNodePortIndex); _scene->createConnection(*_node, portIndex, converterNode, 0); + _scene->UpdateHistory(); } else { _scene->createConnection(converterNode, 0, *_node, portIndex); _scene->createConnection(*outNode, outNodePortIndex, converterNode, 0); + _scene->UpdateHistory(); } //Delete the users connection, we already replaced it. @@ -150,6 +152,8 @@ tryConnect() const PortIndex outPortIndex = _connection->getPortIndex(PortType::Out); outNode->onDataUpdated(outPortIndex); } + + _scene->UpdateHistory(); return true; } @@ -180,6 +184,8 @@ disconnect(PortType portToDisconnect) const _connection->getConnectionGraphicsObject().grabMouse(); + _scene->UpdateHistory(); + return true; } From e42fe23222efef9e2af9089dd74895e15a783561 Mon Sep 17 00:00:00 2001 From: ReubenJCarter Date: Tue, 14 Nov 2017 18:05:17 +0000 Subject: [PATCH 06/39] Added action context WidgetWithChildrenShortcut. Added duplication of data model to nodes when duplicating --- src/FlowView.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/FlowView.cpp b/src/FlowView.cpp index 574bde22..31fde930 100644 --- a/src/FlowView.cpp +++ b/src/FlowView.cpp @@ -58,22 +58,26 @@ FlowView(FlowScene *scene) _deleteSelectionAction = new QAction(QStringLiteral("Delete Selection"), this); _deleteSelectionAction->setShortcut(Qt::Key_Delete); + _deleteSelectionAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); connect(_deleteSelectionAction, &QAction::triggered, this, &FlowView::deleteSelectedNodes); addAction(_deleteSelectionAction); _duplicateSelectionAction = new QAction(QStringLiteral("Duplicate Selection"), this); _duplicateSelectionAction->setShortcut(QKeySequence(tr("Ctrl+D"))); + _duplicateSelectionAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); connect(_duplicateSelectionAction, &QAction::triggered, this, &FlowView::duplicateSelectedNode); addAction(_duplicateSelectionAction); _undoAction = new QAction(QStringLiteral("Undo"), this); _undoAction->setShortcut(QKeySequence(tr("Ctrl+Z"))); + _undoAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); connect(_undoAction, &QAction::triggered, _scene, &FlowScene::Undo); addAction(_undoAction); _redoAction = new QAction(QStringLiteral("Redo"), this); _redoAction->setShortcut(QKeySequence(tr("Ctrl+Y"))); + _redoAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); connect(_redoAction, &QAction::triggered, _scene, &FlowScene::Redo); addAction(_redoAction); @@ -311,6 +315,7 @@ void FlowView::duplicateSelectedNode() //create nodes std::vector createdNodes; std::vector couterpartNode; + std::vector nodeData; for (QGraphicsItem * item : _scene->selectedItems()) { if (auto n = qgraphicsitem_cast(item)) @@ -322,6 +327,7 @@ void FlowView::duplicateSelectedNode() if (type) { auto& node = _scene->createNode(std::move(type)); + node.nodeDataModel()->restore(n->node().nodeDataModel()->save()); createdNodes.push_back(&node); couterpartNode.push_back(&(n->node())); From b5f7310c0a82dddf9b588001e410f58a0e387ec0 Mon Sep 17 00:00:00 2001 From: ReubenJCarter Date: Wed, 15 Nov 2017 12:07:33 +0000 Subject: [PATCH 07/39] added updating history for node movement --- src/FlowScene.cpp | 8 +++++++- src/FlowScene.hpp | 2 ++ src/NodeGraphicsObject.cpp | 4 ++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/FlowScene.cpp b/src/FlowScene.cpp index 7eb70933..195df72d 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 58108133..9f34ccda 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 185b5150..9097a3f2 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -312,6 +312,8 @@ mouseMoveEvent(QGraphicsSceneMouseEvent * event) moveConnections(); event->ignore(); + + } QRectF r = scene()->sceneRect(); @@ -331,6 +333,8 @@ mouseReleaseEvent(QGraphicsSceneMouseEvent* event) state.setResizing(false); QGraphicsObject::mouseReleaseEvent(event); + + _scene.nodeMoveFinished(_node, pos()); // position connections precisely after fast node move moveConnections(); From d649c82f8aacb4921f4a78cc3309e2b099224ba4 Mon Sep 17 00:00:00 2001 From: ReubenJCarter Date: Fri, 17 Nov 2017 10:27:59 +0000 Subject: [PATCH 08/39] Added Fix for Mingw "invalid initialization of non-const reference of type" --- src/FlowView.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/FlowView.cpp b/src/FlowView.cpp index 31fde930..ff960bd4 100644 --- a/src/FlowView.cpp +++ b/src/FlowView.cpp @@ -321,12 +321,12 @@ void FlowView::duplicateSelectedNode() if (auto n = qgraphicsitem_cast(item)) { QString modelName = n->node().nodeDataModel()->name(); - - auto& type = _scene->registry().create(modelName); + auto type = _scene->registry().create(modelName); + auto& typeRef = type; - if (type) + if (typeRef) { - auto& node = _scene->createNode(std::move(type)); + auto& node = _scene->createNode(std::move(typeRef)); node.nodeDataModel()->restore(n->node().nodeDataModel()->save()); createdNodes.push_back(&node); couterpartNode.push_back(&(n->node())); @@ -375,7 +375,8 @@ void FlowView::duplicateSelectedNode() if(j >=0 && k>=0 && j < couterpartNode.size() && k < couterpartNode.size()) { - auto& connection = _scene->createConnection(*createdNodes[j], portIndexIn, *createdNodes[k], portIndexOut); + auto connection = _scene->createConnection(*createdNodes[j], portIndexIn, *createdNodes[k], portIndexOut); + auto& connectionRef = connection; createdConnections.push_back(connection); } } From c0737644d2d3f9cf27732b3868e321023cb23338 Mon Sep 17 00:00:00 2001 From: ReubenJCarter Date: Wed, 7 Feb 2018 18:04:58 +0000 Subject: [PATCH 09/39] added setViewportUpdateMode(QGraphicsView::FullViewportUpdate); to flow view. --- src/FlowView.cpp | 13 ++++++++++++- src/FlowView.hpp | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/FlowView.cpp b/src/FlowView.cpp index ff960bd4..3a691ad9 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 aed92f1d..ebe77e42 100644 --- a/src/FlowView.hpp +++ b/src/FlowView.hpp @@ -13,7 +13,7 @@ class NODE_EDITOR_PUBLIC FlowView : public QGraphicsView { public: - + FlowView(FlowScene *scene); FlowView(const FlowView&) = delete; From 243c382ce82942023871002ceee853e0027081b8 Mon Sep 17 00:00:00 2001 From: ReubenJCarter Date: Mon, 19 Feb 2018 14:34:20 +0000 Subject: [PATCH 10/39] Turn on/off the background grid render based on scale in viewport for performance benefits --- src/FlowView.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/FlowView.cpp b/src/FlowView.cpp index 3a691ad9..1318005a 100644 --- a/src/FlowView.cpp +++ b/src/FlowView.cpp @@ -492,6 +492,9 @@ drawBackground(QPainter* painter, const QRectF& r) double bottom = std::floor(tl.y() / gridStep - 0.5); double top = std::floor (br.y() / gridStep + 1.0); + if(right - left > 100) + return; + // vertical lines for (int xi = int(left); xi <= int(right); ++xi) { From 7215528fe658713339076a11eed5009e6e79941a Mon Sep 17 00:00:00 2001 From: ReubenJCarter Date: Tue, 20 Feb 2018 18:02:02 +0000 Subject: [PATCH 11/39] Added the ability to set the tool tip for each node --- src/Node.cpp | 3 +++ src/NodeDataModel.cpp | 13 +++++++++++++ src/NodeDataModel.hpp | 9 +++++++++ src/NodeGraphicsObject.cpp | 4 ++++ 4 files changed, 29 insertions(+) diff --git a/src/Node.cpp b/src/Node.cpp index e72d0818..43d22ec5 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 9737b345..1f48c510 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 51714ad1..cedd6c5b 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 aeef3077..cdeffea6 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -66,6 +66,10 @@ NodeGraphicsObject(FlowScene &scene, connect(this, &QGraphicsObject::xChanged, this, onMoveSlot); connect(this, &QGraphicsObject::yChanged, this, onMoveSlot); + + connect(_node.nodeDataModel(), &NodeDataModel::setToolTipTextSignal, this, [this](QString &toolTipText ){ + setToolTip(toolTipText); + }); } From 477d90457d4e706243f4f4e741f0c080f2b11cb4 Mon Sep 17 00:00:00 2001 From: ReubenJCarter Date: Thu, 22 Feb 2018 15:56:54 +0000 Subject: [PATCH 12/39] Made the signal and slot compatible for the setToolTip signal in the NodeDataModel. --- src/NodeGraphicsObject.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NodeGraphicsObject.cpp b/src/NodeGraphicsObject.cpp index cdeffea6..e87ab500 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -67,7 +67,7 @@ NodeGraphicsObject(FlowScene &scene, connect(this, &QGraphicsObject::yChanged, this, onMoveSlot); - connect(_node.nodeDataModel(), &NodeDataModel::setToolTipTextSignal, this, [this](QString &toolTipText ){ + connect(_node.nodeDataModel(), &NodeDataModel::setToolTipTextSignal, this, [this](QString toolTipText ){ setToolTip(toolTipText); }); } From bc9c58faf484cf38f091d103b1f97f4709b904b9 Mon Sep 17 00:00:00 2001 From: jacquespillet Date: Thu, 17 Jan 2019 09:36:57 +0000 Subject: [PATCH 13/39] Added copy/paste between sheets --- .gitignore | 1 + include/nodes/internal/Connection.hpp | 3 +- include/nodes/internal/FlowScene.hpp | 6 ++ include/nodes/internal/FlowView.hpp | 6 ++ include/nodes/internal/Node.hpp | 11 ++++ src/Connection.cpp | 16 +++++ src/FlowScene.cpp | 47 +++++++++++++-- src/FlowView.cpp | 86 ++++++++++++++++++++++++++- src/Node.cpp | 35 +++++++++++ 9 files changed, 205 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index c9eb5941..6c8dd3dc 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 a563c14c..c0562d1b 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 9f34ccda..f6c7a1b4 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 138bdeae..fcf35ea5 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 793e8a85..b6aa05fb 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 194b6cac..93e18c1f 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 195df72d..609c4f79 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 36af9077..26977d47 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 43d22ec5..1984f66b 100644 --- a/src/Node.cpp +++ b/src/Node.cpp @@ -60,6 +60,22 @@ save() const return nodeJson; } +QJsonObject Node::copyWithNewID(QUuid newId) const +{ + QJsonObject nodeJson; + + nodeJson["id"] = newId.toString(); + nodeJson["model"] = _nodeDataModel->save(); + + QJsonObject obj; + obj["x"] = _nodeGraphicsObject->pos().x(); + obj["y"] = _nodeGraphicsObject->pos().y(); + nodeJson["position"] = obj; + + return nodeJson; +} + + void Node:: @@ -75,6 +91,20 @@ restore(QJsonObject const& json) _nodeDataModel->restore(json["model"].toObject()); } +void +Node:: +paste(QJsonObject const& json, QUuid ID) +{ + _id = ID; + + QJsonObject positionJson = json["position"].toObject(); + QPointF point(positionJson["x"].toDouble(), + positionJson["y"].toDouble()); + _nodeGraphicsObject->setPos(point); + + _nodeDataModel->restore(json["model"].toObject()); +} + QUuid Node:: @@ -83,6 +113,11 @@ id() const return _id; } +void Node::setId(QUuid id) { + this->_id = id; +} + + void Node:: From a0adb17e032b11c11c05154782813621b412fe83 Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Fri, 1 Nov 2019 09:58:24 +0000 Subject: [PATCH 14/39] Started groups, added anchors, fixed position of nodes when pasting --- CMakeLists.txt | 2 + include/nodes/internal/FlowScene.hpp | 17 +++- include/nodes/internal/FlowView.hpp | 8 ++ include/nodes/internal/Group.hpp | 54 +++++++++++ .../nodes/internal/GroupGraphicsObject.hpp | 28 ++++++ include/nodes/internal/Node.hpp | 1 + src/FlowScene.cpp | 46 ++++++++-- src/FlowView.cpp | 90 ++++++++++++++++++- src/Group.cpp | 0 src/GroupGraphicsObject.cpp | 0 10 files changed, 237 insertions(+), 9 deletions(-) create mode 100644 include/nodes/internal/Group.hpp create mode 100644 include/nodes/internal/GroupGraphicsObject.hpp create mode 100644 src/Group.cpp create mode 100644 src/GroupGraphicsObject.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 23328dbb..71390c58 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 f6c7a1b4..fe109ecb 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 fcf35ea5..9db7606a 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 00000000..d1fab312 --- /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 00000000..fb49f4f7 --- /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 b6aa05fb..78bdc501 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 609c4f79..8b4d005c 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 00000000..e69de29b diff --git a/src/GroupGraphicsObject.cpp b/src/GroupGraphicsObject.cpp new file mode 100644 index 00000000..e69de29b From 02c5c7d76c0172a0b8b1281d973930357a27cbc7 Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Wed, 6 Nov 2019 16:06:57 +0000 Subject: [PATCH 15/39] Added getter for history index, added resizing nodes --- include/nodes/internal/FlowScene.hpp | 2 ++ include/nodes/internal/NodeDataModel.hpp | 2 +- src/FlowScene.cpp | 5 +++++ src/NodeGeometry.cpp | 4 ++-- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/include/nodes/internal/FlowScene.hpp b/include/nodes/internal/FlowScene.hpp index fe109ecb..a115ac65 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 cedd6c5b..a48298b0 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 8b4d005c..ce074e77 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 8309abf9..d120e1c9 100644 --- a/src/NodeGeometry.cpp +++ b/src/NodeGeometry.cpp @@ -217,8 +217,8 @@ resizeRect() const { unsigned int rectSize = 7; - return QRect(_width - rectSize, - _height - rectSize, + return QRect(_width + rectSize, + _height + rectSize, rectSize, rectSize); } From ddbe4a805ec826631565d601528bd4bdb0b08041 Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Mon, 6 Jan 2020 13:57:42 +0000 Subject: [PATCH 16/39] Started groups --- include/nodes/internal/FlowScene.hpp | 10 +- include/nodes/internal/Group.hpp | 19 ++- .../nodes/internal/GroupGraphicsObject.hpp | 58 ++++++- include/nodes/internal/NodeDataModel.hpp | 5 +- src/FlowScene.cpp | 22 ++- src/FlowView.cpp | 28 +-- src/Group.cpp | 10 ++ src/GroupGraphicsObject.cpp | 159 ++++++++++++++++++ src/NodeGeometry.cpp | 3 +- 9 files changed, 283 insertions(+), 31 deletions(-) diff --git a/include/nodes/internal/FlowScene.hpp b/include/nodes/internal/FlowScene.hpp index a115ac65..c7764c63 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 d1fab312..5627ac61 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 fb49f4f7..cef9d093 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 a48298b0..16fc55ac 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 ce074e77..98b5012d 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 163eeb14..3ffff3f9 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 e69de29b..c5cb605f 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 e69de29b..3dc2737c 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 d120e1c9..0c8d5318 100644 --- a/src/NodeGeometry.cpp +++ b/src/NodeGeometry.cpp @@ -58,7 +58,8 @@ boundingRect() const { auto const &nodeStyle = StyleCollection::nodeStyle(); - double addon = 4 * nodeStyle.ConnectionPointDiameter; + double addon = nodeStyle.ConnectionPointDiameter * 1.6; + // double addon = 0; return QRectF(0 - addon, 0 - addon, From 4c2812ff6e5a425889c46ce206fbfed8e2f80e1f Mon Sep 17 00:00:00 2001 From: jacquespillet Date: Wed, 7 Apr 2021 10:30:59 +0100 Subject: [PATCH 17/39] continued groups --- include/nodes/internal/FlowScene.hpp | 18 +- include/nodes/internal/Group.hpp | 65 ++++++- .../nodes/internal/GroupGraphicsObject.hpp | 26 ++- src/FlowScene.cpp | 160 ++++++++++++++++-- src/FlowView.cpp | 39 +++-- src/Group.cpp | 21 ++- src/GroupGraphicsObject.cpp | 151 +++++++++++++++-- 7 files changed, 423 insertions(+), 57 deletions(-) diff --git a/include/nodes/internal/FlowScene.hpp b/include/nodes/internal/FlowScene.hpp index c7764c63..fa9e8119 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 5627ac61..286cf8ac 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 cef9d093..2777078b 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 98b5012d..0d23fba8 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 3ffff3f9..87d23628 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 c5cb605f..45f15b72 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 3dc2737c..e59c816b 100644 --- a/src/GroupGraphicsObject.cpp +++ b/src/GroupGraphicsObject.cpp @@ -12,7 +12,7 @@ #include "FlowScene.hpp" #include "NodePainter.hpp" -#include "Node.hpp" +#include "Group.hpp" #include "NodeDataModel.hpp" #include "NodeConnectionInteraction.hpp" @@ -20,11 +20,13 @@ using QtNodes::GroupGraphicsObject; using QtNodes::Node; +using QtNodes::Group; using QtNodes::FlowScene; GroupGraphicsObject:: -GroupGraphicsObject(FlowScene &scene) +GroupGraphicsObject(FlowScene &scene, Group& group) : _scene(scene) + , _group(group) { _scene.addItem(this); @@ -39,7 +41,38 @@ GroupGraphicsObject(FlowScene &scene) setAcceptHoverEvents(true); setAcceptTouchEvents(true); - setZValue(0); + setZValue(-2); + + r = g = b = 135; + + QGraphicsProxyWidget* _proxyWidget = new QGraphicsProxyWidget(this); + + QString name = group.GetName(); + QLineEdit* l = new QLineEdit(name); + l->setAlignment(Qt::AlignHCenter); + l->setStyleSheet("\ + QLineEdit {\ + border: 0px solid gray;\ + border-radius: 0px;\ + padding: 0 0px;\ + background: rgb(135, 135, 135);\ + selection-background-color: darkgray;\ + color: rgb(220, 220, 220);\ + font-size: 30px;\ + }\ + QLineEdit:hover {\ + background: rgb(90, 90, 90);\ + }\ + "); + + connect(l, &QLineEdit::textChanged, this, [&group](const QString &text) { + group.SetName("wuh"); + std::cout << "HERE" << std::endl; + }); + + + _proxyWidget->setWidget(l); + _proxyWidget->setPos(pos() + QPointF(sizeX/2 - _proxyWidget->size().width()/2, 0)); } @@ -50,11 +83,24 @@ GroupGraphicsObject:: } +Group& +GroupGraphicsObject:: +group() { + return _group; +} + +Group const& +GroupGraphicsObject:: +group() const { + return _group; +} + + QRectF GroupGraphicsObject:: boundingRect() const { - return QRectF(0, 0, 1000, 1000); + return QRectF(0, 0, sizeX, sizeY); } @@ -65,6 +111,22 @@ setGeometryChanged() prepareGeometryChange(); } +void +GroupGraphicsObject:: +moveConnections() const { + for(int i=0; i(childItems()[i]); + if (ngo) { + ngo->moveConnections(); + } + + GroupGraphicsObject* ggo = dynamic_cast(childItems()[i]); + if (ggo) { + ggo->moveConnections(); + } + } +} + void @@ -74,8 +136,8 @@ paint(QPainter * painter, QWidget* ) { painter->setClipRect(option->exposedRect); - QRect rect(0, 0, 1000, 1000); - auto color = QColor(255, 0, 0, 255); + QRect rect(0, 0, sizeX, sizeY); + auto color = QColor(r, g, b, 255); painter->drawRect(rect); painter->fillRect(rect, QBrush(color)); } @@ -93,6 +155,24 @@ void GroupGraphicsObject:: mousePressEvent(QGraphicsSceneMouseEvent * event) { + // deselect all other items after this one is selected + if (!isSelected() && + !(event->modifiers() & Qt::ControlModifier)) + { + _scene.clearSelection(); + } + + auto mousePos = event->pos(); + + if (abs(mousePos.x() - sizeX) < 20 && abs(mousePos.y() - sizeY) < 20) + { + isResizingXY=true; + } else if (abs(mousePos.x() - sizeX) < 20) + { + isResizingX=true; + } else if (abs(mousePos.y() - sizeY) < 20) { + isResizingY=true; + } } @@ -100,14 +180,34 @@ void GroupGraphicsObject:: mouseMoveEvent(QGraphicsSceneMouseEvent * event) { - // setPos(mapFromScene(event->scenePos())); + if(isResizingX) { + int diff = event->pos().x() - event->lastPos().x(); + prepareGeometryChange(); + sizeX += diff; + update(); + // ngo->moveConnections(); + event->accept(); + } + else if(isResizingY) { + int diff = event->pos().y() - event->lastPos().y(); + prepareGeometryChange(); + sizeY += diff; + update(); + // ngo->moveConnections(); + event->accept(); + } else if(isResizingXY) { + auto diff = event->pos() - event->lastPos(); + prepareGeometryChange(); + sizeX += diff.x(); + sizeY += diff.y(); + update(); + // ngo->moveConnections(); + event->accept(); + } else { + _scene.groupMoved(_group, pos()); QGraphicsObject::mouseMoveEvent(event); - // std::cout << scene()->scale() << std::endl; - // event->ignore(); - // //DO SNAPPING HERE - // QRectF r = scene()->sceneRect(); - // r = r.united(mapToScene(boundingRect()).boundingRect()); - // scene()->setSceneRect(r); + moveConnections(); + } } @@ -116,6 +216,11 @@ GroupGraphicsObject:: mouseReleaseEvent(QGraphicsSceneMouseEvent* event) { QGraphicsObject::mouseReleaseEvent(event); + _scene.groupMoveFinished(_group, pos()); + + isResizingX=false; + isResizingY=false; + isResizingXY=false; } @@ -123,6 +228,8 @@ void GroupGraphicsObject:: hoverEnterEvent(QGraphicsSceneHoverEvent * event) { + auto mousePos = event->pos(); + setCursor(QCursor()); update(); event->accept(); } @@ -132,6 +239,7 @@ void GroupGraphicsObject:: hoverLeaveEvent(QGraphicsSceneHoverEvent * event) { + // std::cout << "hverleave" << std::endl; update(); event->accept(); } @@ -141,6 +249,21 @@ void GroupGraphicsObject:: hoverMoveEvent(QGraphicsSceneHoverEvent * event) { + auto mousePos = event->pos(); + + if (abs(mousePos.x() - sizeX) < 20 && abs(mousePos.y() - sizeY) < 20) + { + setCursor(QCursor(Qt::SizeFDiagCursor)); + } else if (abs(mousePos.x() - sizeX) < 20) + { + setCursor(QCursor(Qt::SizeHorCursor)); + } else if (abs(mousePos.y() - sizeY) < 20) { + setCursor(QCursor(Qt::SizeVerCursor)); + } else + { + setCursor(QCursor()); + } + event->accept(); } @@ -149,6 +272,8 @@ void GroupGraphicsObject:: mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) { + _scene.groupDoubleClicked(group()); + // std::cout << "doubleclick" << std::endl; QGraphicsItem::mouseDoubleClickEvent(event); } From d341631a79655348cb04ca8525f52593dabd7fc1 Mon Sep 17 00:00:00 2001 From: jacquespillet Date: Mon, 19 Apr 2021 07:46:45 +0100 Subject: [PATCH 18/39] added deleted node --- src/FlowScene.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/FlowScene.cpp b/src/FlowScene.cpp index 0d23fba8..a84b2135 100644 --- a/src/FlowScene.cpp +++ b/src/FlowScene.cpp @@ -232,8 +232,12 @@ restoreNode(QJsonObject const& nodeJson) auto dataModel = registry().create(modelName); //This is where it looks for the node by name if (!dataModel) - throw std::logic_error(std::string("No registered model with name ") + - modelName.toLocal8Bit().data()); + { + dataModel = registry().create("DeletedNode"); + } + /*throw std::logic_error(std::string("No registered model with name ") + + modelName.toLocal8Bit().data()); +*/ auto node = std::make_shared(std::move(dataModel)); auto ngo = std::make_unique(*this, *node); node->setGraphicsObject(std::move(ngo)); From f6fb7b8f5325e0c36a03b28f99ec74238f6e7917 Mon Sep 17 00:00:00 2001 From: jacquespillet Date: Fri, 27 Aug 2021 09:08:52 +0100 Subject: [PATCH 19/39] Added node templates --- include/nodes/internal/DataModelRegistry.hpp | 22 +++- include/nodes/internal/FlowView.hpp | 8 +- src/DataModelRegistry.cpp | 8 ++ src/FlowView.cpp | 122 +++++++++++++------ 4 files changed, 120 insertions(+), 40 deletions(-) diff --git a/include/nodes/internal/DataModelRegistry.hpp b/include/nodes/internal/DataModelRegistry.hpp index 674bc67b..6adab92b 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 9db7606a..a92203e3 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 8a1426ac..028cfed7 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 87d23628..f2e383c5 100644 --- a/src/FlowView.cpp +++ b/src/FlowView.cpp @@ -232,6 +232,12 @@ contextMenuEvent(QContextMenuEvent *event) topLevelItems[cat] = item; } + //Add templates category + auto templatesCategory = new QTreeWidgetItem(treeView); + templatesCategory->setText(0, "Templates"); + templatesCategory->setData(0, Qt::UserRole, "Templates"); + topLevelItems["Templates"] = templatesCategory; + for (auto const &assoc : _scene->registry().registeredModelsCategoryAssociation()) { auto parent = topLevelItems[assoc.second]; @@ -239,9 +245,19 @@ contextMenuEvent(QContextMenuEvent *event) item->setText(0, assoc.first); item->setData(0, Qt::UserRole, assoc.first); } + + for (auto const &assoc : _scene->registry().RegisteredTemplates()) + { + QString name = assoc.first; + auto item = new QTreeWidgetItem(templatesCategory); + item->setText(0, name); + item->setData(0, Qt::UserRole, name); + } + treeView->expandAll(); + connect(treeView, &QTreeWidget::itemActivated, [&](QTreeWidgetItem *item, int) { QString modelName = item->data(0, Qt::UserRole).toString(); @@ -251,6 +267,26 @@ contextMenuEvent(QContextMenuEvent *event) return; } + QString parent = item->parent()->data(0, Qt::UserRole).toString(); + std::cout << parent.toStdString() << std::endl; + if(parent == "Templates") + { + DataModelRegistry::RegisteredTemplatesMap map = _scene->registry().RegisteredTemplates(); + QString fileName = map[modelName]; + + QFile file; + file.setFileName(fileName); + file.open(QIODevice::ReadOnly | QIODevice::Text); + QString val = file.readAll(); + file.close(); + qWarning() << val; + QJsonDocument d = QJsonDocument::fromJson(val.toUtf8()); + QJsonObject sett2 = d.object(); + jsonToScene(sett2); + + modelMenu.close(); + } + auto type = _scene->registry().create(modelName); if (type) @@ -375,38 +411,7 @@ deleteSelectedNodes() } void FlowView::copySelectedNodes() { - QJsonObject sceneJson; - QJsonArray nodesJsonArray; - std::vector addedIds; - - for (QGraphicsItem * item : _scene->selectedItems()) - { - if (auto n = qgraphicsitem_cast(item)) - { - Node& node = n->node(); - nodesJsonArray.append(node.save()); - addedIds.push_back(node.id()); - } - } - - QJsonArray connectionJsonArray; - for (QGraphicsItem * item : _scene->selectedItems()) - { - if (auto c = qgraphicsitem_cast(item)) { - Connection& connection = c->connection(); - - if( std::find(addedIds.begin(), addedIds.end(), connection.getNode(PortType::In)->id()) != addedIds.end() && - std::find(addedIds.begin(), addedIds.end(), connection.getNode(PortType::Out)->id()) != addedIds.end() ) { - QJsonObject connectionJson = connection.save(); - - if (!connectionJson.isEmpty()) - connectionJsonArray.append(connectionJson); - } - } - } - - sceneJson["nodes"] = nodesJsonArray; - sceneJson["connections"] = connectionJsonArray; + QJsonObject sceneJson = selectionToJson(); QJsonDocument document(sceneJson); std::string json = document.toJson().toStdString(); @@ -415,11 +420,8 @@ void FlowView::copySelectedNodes() { p_Clipboard->setText( QString::fromStdString(json)); } -void FlowView::pasteSelectedNodes() { - QClipboard *p_Clipboard = QApplication::clipboard(); - QByteArray text = p_Clipboard->text().toUtf8(); - - QJsonObject const jsonDocument = QJsonDocument::fromJson(text).object(); +void FlowView::jsonToScene(QJsonObject jsonDocument) +{ QJsonArray nodesJsonArray = jsonDocument["nodes"].toArray(); std::map addedIds; @@ -472,6 +474,52 @@ void FlowView::pasteSelectedNodes() { _scene->UpdateHistory(); } +QJsonObject FlowView::selectionToJson() +{ + QJsonObject sceneJson; + QJsonArray nodesJsonArray; + std::vector addedIds; + + for (QGraphicsItem * item : _scene->selectedItems()) + { + if (auto n = qgraphicsitem_cast(item)) + { + Node& node = n->node(); + nodesJsonArray.append(node.save()); + addedIds.push_back(node.id()); + } + } + + QJsonArray connectionJsonArray; + for (QGraphicsItem * item : _scene->selectedItems()) + { + if (auto c = qgraphicsitem_cast(item)) { + Connection& connection = c->connection(); + + if( std::find(addedIds.begin(), addedIds.end(), connection.getNode(PortType::In)->id()) != addedIds.end() && + std::find(addedIds.begin(), addedIds.end(), connection.getNode(PortType::Out)->id()) != addedIds.end() ) { + QJsonObject connectionJson = connection.save(); + + if (!connectionJson.isEmpty()) + connectionJsonArray.append(connectionJson); + } + } + } + + sceneJson["nodes"] = nodesJsonArray; + sceneJson["connections"] = connectionJsonArray; + + return sceneJson; +} + +void FlowView::pasteSelectedNodes() { + QClipboard *p_Clipboard = QApplication::clipboard(); + QByteArray text = p_Clipboard->text().toUtf8(); + + QJsonObject const jsonDocument = QJsonDocument::fromJson(text).object(); + jsonToScene(jsonDocument); +} + void FlowView::createGroup() { _scene->createGroup(); } From 0eb037a3245b7a93ac6fad499f0402ad07fe1cd4 Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Wed, 22 Sep 2021 11:06:18 +0100 Subject: [PATCH 20/39] Fixes on groups --- include/nodes/internal/FlowScene.hpp | 4 +- include/nodes/internal/FlowView.hpp | 3 - include/nodes/internal/Group.hpp | 15 +++- .../nodes/internal/GroupGraphicsObject.hpp | 12 +++- src/FlowScene.cpp | 29 +++++--- src/FlowView.cpp | 68 +++++++++++++------ src/GroupGraphicsObject.cpp | 29 ++++---- src/Node.cpp | 4 +- 8 files changed, 115 insertions(+), 49 deletions(-) diff --git a/include/nodes/internal/FlowScene.hpp b/include/nodes/internal/FlowScene.hpp index fa9e8119..cf2a983c 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 a92203e3..9320e304 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 286cf8ac..78fba58c 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 2777078b..90b46cbb 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 a84b2135..bac90197 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 f2e383c5..a718de11 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 e59c816b..2d981ebb 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 1984f66b..93d627df 100644 --- a/src/Node.cpp +++ b/src/Node.cpp @@ -53,8 +53,8 @@ save() const nodeJson["model"] = _nodeDataModel->save(); QJsonObject obj; - obj["x"] = _nodeGraphicsObject->pos().x(); - obj["y"] = _nodeGraphicsObject->pos().y(); + obj["x"] = _nodeGraphicsObject->scenePos().x(); + obj["y"] = _nodeGraphicsObject->scenePos().y(); nodeJson["position"] = obj; return nodeJson; From b91389bd30ce2ebb359c25627760da5794ccc5d6 Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Mon, 15 Nov 2021 11:55:05 +0000 Subject: [PATCH 21/39] Added snapping --- include/nodes/internal/FlowScene.hpp | 3 +++ src/NodeGraphicsObject.cpp | 26 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/include/nodes/internal/FlowScene.hpp b/include/nodes/internal/FlowScene.hpp index cf2a983c..ab304201 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 e87ab500..45ead5a1 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -182,14 +182,40 @@ paint(QPainter * painter, NodePainter::paint(painter, _node, _scene); } +int closestMultiple(int n, int x) +{ + int sign = (n < 0) ? -1 : 1; + int absN = std::abs(n); + + if(x>absN) + return x; + + absN = absN + x/2; + absN = absN - (absN%x); + return absN * sign; +} QVariant NodeGraphicsObject:: itemChange(GraphicsItemChange change, const QVariant &value) { + if (change == ItemPositionChange && scene()) { moveConnections(); + QPointF newPos = value.toPointF(); + if(_scene.snapping) + { + int xi = (int)round(newPos.x()); + int yi = (int)round(newPos.y()); + qreal xF = (qreal)closestMultiple(xi, 15 * _scene.gridSize) - 2; + qreal yF = (qreal)closestMultiple(yi, 15 * _scene.gridSize) - 2; + return QPointF(xF, yF); + } + else + { + return newPos; + } } return QGraphicsItem::itemChange(change, value); From 51804f16f42b604dace36bb5e9513196921912f8 Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Wed, 26 Jan 2022 14:38:22 +0000 Subject: [PATCH 22/39] Added Collapsable group --- include/nodes/internal/Connection.hpp | 17 + include/nodes/internal/FlowScene.hpp | 5 + include/nodes/internal/Group.hpp | 33 +- .../nodes/internal/GroupGraphicsObject.hpp | 52 ++- src/Connection.cpp | 63 +++ src/ConnectionGraphicsObject.cpp | 80 +++- src/FlowScene.cpp | 108 ++++- src/FlowView.cpp | 58 ++- src/Group.cpp | 64 +++ src/GroupGraphicsObject.cpp | 380 +++++++++++++++++- 10 files changed, 796 insertions(+), 64 deletions(-) diff --git a/include/nodes/internal/Connection.hpp b/include/nodes/internal/Connection.hpp index c0562d1b..216f6b39 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 ab304201..8ad26daa 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 78fba58c..cb372353 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 90b46cbb..cb5dc046 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 93e18c1f..72ab154a 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 a0b86358..a07d7de2 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 bac90197..15f777ad 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 a718de11..c926d0f0 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 45f15b72..5c50653d 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 2d981ebb..eccb0003 100644 --- a/src/GroupGraphicsObject.cpp +++ b/src/GroupGraphicsObject.cpp @@ -8,6 +8,7 @@ #include "ConnectionGraphicsObject.hpp" #include "ConnectionState.hpp" +#include "Connection.hpp" #include "FlowScene.hpp" #include "NodePainter.hpp" @@ -22,6 +23,8 @@ using QtNodes::GroupGraphicsObject; using QtNodes::Node; using QtNodes::Group; using QtNodes::FlowScene; +using QtNodes::Connection; +using QtNodes::PortType; GroupGraphicsObject:: GroupGraphicsObject(FlowScene &scene, Group& group) @@ -46,6 +49,7 @@ GroupGraphicsObject(FlowScene &scene, Group& group) r = g = b = 135; _proxyWidget = new QGraphicsProxyWidget(this); + _collapseButton = new QGraphicsProxyWidget(this); QString name = group.GetName(); nameLineEdit = new QLineEdit(name); @@ -70,7 +74,260 @@ GroupGraphicsObject(FlowScene &scene, Group& group) }); _proxyWidget->setWidget(nameLineEdit); - _proxyWidget->setPos(pos() + QPointF(sizeX/2 - _proxyWidget->size().width()/2, 0)); + _proxyWidget->setPos(QPointF(sizeX/2 - _proxyWidget->size().width()/2, 0)); + + collapseButtonWidget = new QPushButton("X"); + collapseButtonWidget->setCheckable(true); + _collapseButton->setWidget(collapseButtonWidget); + _collapseButton->setPos(QPointF(sizeX - _collapseButton->size().width(), 0)); + + connect(collapseButtonWidget, &QPushButton::clicked, this, [this](int state){ + Collapse(); + }); +} + +void +GroupGraphicsObject:: +Collapse() +{ + unusedConnections.clear(); + + ////1. Identify all the nodes that have external inputs + inOutLabels[(int)PortType::In].clear(); + inOutConnections[(int)PortType::In].clear(); + inOutNodes[(int)PortType::In].clear(); + inOutPorts[(int)PortType::In].clear(); + //for all the nodes in the group + for (int i = 0; i < childItems().size(); i++) + { + NodeGraphicsObject* nodeGO = qgraphicsitem_cast(childItems()[i]); + if (nodeGO != nullptr) + { + Node &node = nodeGO->node(); + int numEntries = node.nodeState().getEntries(PortType::In).size(); + + //For each input of the current node + for (int j = 0; j < numEntries; j++) + { + NodeState::ConnectionPtrSet connections = node.nodeState().connections(PortType::In, j); + //Get the connection + for (std::pair connectionPair : connections) + { + Connection *connection = connectionPair.second; + Node *sourceNode = connection->getNode(PortType::Out); + Node *destNode = connection->getNode(PortType::In); + NodeGraphicsObject &sourceNodeGO = sourceNode->nodeGraphicsObject(); + + //Check if the the input is inside the group + if (childItems().indexOf(&sourceNodeGO) == -1) { //if it's not inside + inOutLabels[(int)PortType::In].push_back( + node.nodeDataModel()->name() + "." + node.nodeDataModel()->portCaption(PortType::In, j) + ); + inOutConnections[(int)PortType::In].push_back(connection); + inOutNodes[(int)PortType::In].push_back(destNode); + inOutPorts[(int)PortType::In].push_back(j); + } + else { //it's inside + unusedConnections.push_back(connection); + } + } + } + } + } + + ////2. Identify all the nodes that have external outputs + inOutLabels[(int)PortType::Out].clear(); + inOutConnections[(int)PortType::Out].clear(); + inOutNodes[(int)PortType::Out].clear(); + inOutPorts[(int)PortType::Out].clear(); + //for all the nodes in the group + for (int i = 0; i < childItems().size(); i++) + { + NodeGraphicsObject* nodeGO = qgraphicsitem_cast(childItems()[i]); + if (nodeGO != nullptr) + { + Node &node = nodeGO->node(); + int numOutputs = node.nodeState().getEntries(PortType::Out).size(); + for (int j = 0; j < numOutputs; j++) + { + NodeState::ConnectionPtrSet connections = node.nodeState().connections(PortType::Out, j); + for (std::pair connectionPair : connections) + { + Connection *connection = connectionPair.second; + Node *destNode = connection->getNode(PortType::In); + NodeGraphicsObject &destNodeGO = destNode->nodeGraphicsObject(); + + if (childItems().indexOf(&destNodeGO) == -1) { + inOutLabels[(int)PortType::Out].push_back( + node.nodeDataModel()->name() + "." + node.nodeDataModel()->portCaption(PortType::Out, j) + ); + inOutConnections[(int)PortType::Out].push_back(connection); + inOutNodes[(int)PortType::Out].push_back(destNode); + inOutPorts[(int)PortType::Out].push_back(j); + } + else { + unusedConnections.push_back(connection); + } + } + } + } + } + + + if(!collapsed) + { + int numInOut = std::max(inOutLabels[(int)PortType::In].size(), inOutLabels[(int)PortType::Out].size()); + + savedSizeX = sizeX; + savedSizeY = sizeY; + + //Do resize + sizeX = 500; + sizeY = numInOut * spacing; + _proxyWidget->setPos(QPointF(sizeX/2 - _proxyWidget->size().width()/2, 0)); + _collapseButton->setPos(QPointF(sizeX - _collapseButton->size().width(), 0)); + + + //Sets the inside nodes invisible + for(int i=0; i(childItems()[i]); + if(ngo!=nullptr) + { + childItems()[i]->setVisible(false); + } + } + + //Sets the inside connections invisible + for(int i=0; igetConnectionGraphicsObject().setVisible(false); + } + + //Change the input connection positions + for(int i=0;igetConnectionGraphicsObject().sceneTransform().inverted().map(position); + inOutConnections[(int)PortType::In][i]->connectionGeometry().setEndPoint(PortType::In, connectionPos); + + + Group &thisGroup = group(); + inOutConnections[(int)PortType::In][i]->setGroup(&thisGroup, PortType::In, i); + } + + //Change the output connection positions + for(int i=0;igetConnectionGraphicsObject().sceneTransform().inverted().map(position); + inOutConnections[(int)PortType::Out][i]->connectionGeometry().setEndPoint(PortType::Out, connectionPos); + + Group &thisGroup = group(); + inOutConnections[(int)PortType::Out][i]->setGroup(&thisGroup, PortType::Out, i); + } + + collapsed=true; + } + else + { + sizeX = savedSizeX; + sizeY = savedSizeY; + + _proxyWidget->setPos(QPointF(sizeX/2 - _proxyWidget->size().width()/2, 0)); + _collapseButton->setPos(QPointF(sizeX - _collapseButton->size().width(), 0)); + + //Sets the inside nodes invisible + for(int i=0; i(childItems()[i]); + if(ngo!=nullptr) + { + childItems()[i]->setVisible(true); + } + } + + for(int i=0; igetConnectionGraphicsObject().setVisible(true); + } + + for(int i=0; isetGroup(nullptr, PortType::In, 0); + } + + for(int i=0; isetGroup(nullptr, PortType::Out, 0); + } + + collapsed=false; + unusedConnections.clear();; + inOutLabels[(int)PortType::In].clear(); + inOutLabels[(int)PortType::Out].clear(); + inOutConnections[(int)PortType::In].clear(); + inOutConnections[(int)PortType::Out].clear(); + moveConnections(); + } +} + +QPointF GroupGraphicsObject::portScenePosition(int i, PortType type) const +{ + + if(type == PortType::In) + { + return scenePos() +QPointF(inputSize, topPadding + i * spacing); + } + else + { + return scenePos() +QPointF(sizeX - inputSize, topPadding + i * spacing); + } +} + +QPointF GroupGraphicsObject::portPosition(int i, PortType type) const +{ + + if(type == PortType::In) + { + return QPointF(inputSize, topPadding + i * spacing); + } + else + { + return QPointF(sizeX - inputSize, topPadding + i * spacing); + } +} + +int GroupGraphicsObject::checkHitScenePoint(PortType portType, + QPointF const scenePoint, + QTransform sceneTransform) const +{ + auto const &nodeStyle = StyleCollection::nodeStyle(); + + PortIndex result = INVALID; + + if (portType == PortType::None) + return result; + + double const tolerance = 2.0 * nodeStyle.ConnectionPointDiameter; + + size_t const nItems = inOutLabels[(int)portType].size(); + + for (size_t i = 0; i < nItems; ++i) + { + auto pp = portScenePosition(i, portType); + + QPointF p = pp - scenePoint; + auto distance = std::sqrt(QPointF::dotProduct(p, p)); + + if (distance < tolerance) + { + result = PortIndex(i); + break; + } + } + + return result; } @@ -133,11 +390,50 @@ paint(QPainter * painter, QStyleOptionGraphicsItem const* option, QWidget* ) { - painter->setClipRect(option->exposedRect); - QRect rect(0, 0, sizeX, sizeY); - auto color = QColor(r, g, b, 255); - painter->drawRect(rect); - painter->fillRect(rect, QBrush(color)); + painter->setClipRect(option->exposedRect); + QRect rect(0, 0, sizeX, sizeY); + auto color = QColor(r, g, b, 255); + painter->drawRect(rect); + painter->fillRect(rect, QBrush(color)); + + QFontMetrics const & metrics = + painter->fontMetrics(); + + auto drawPoints = + [&](PortType portType) + { + size_t n = inOutLabels[(int)portType].size(); + for (size_t i = 0; i < n; ++i) + { + QPointF p = portPosition(i, portType); + + painter->setPen(QColor(255, 255, 255, 255)); + + QString s = inOutLabels[(int)portType][i]; + + auto rect = metrics.boundingRect(s); + + p.setY(p.y() + rect.height() / 4.0); + switch (portType) + { + case PortType::In: + p.setX(inputSize + 5.0); + break; + + case PortType::Out: + p.setX(sizeX - 5.0 - rect.width() - inputSize); + break; + + default: + break; + } + + painter->drawText(p, s); + } + }; + + drawPoints(PortType::Out); + drawPoints(PortType::In); } @@ -153,7 +449,6 @@ void GroupGraphicsObject:: mousePressEvent(QGraphicsSceneMouseEvent * event) { - std::cout << "OEFIJWOEFJW " << std::endl; if(QApplication::keyboardModifiers().testFlag(Qt::ShiftModifier)) { event->ignore(); @@ -178,6 +473,66 @@ mousePressEvent(QGraphicsSceneMouseEvent * event) } else if (abs(mousePos.y() - sizeY) < 20) { isResizingY=true; } + + auto clickPort = + [&](PortType portToCheck) + { + + // TODO do not pass sceneTransform + int portIndex = checkHitScenePoint(portToCheck, + event->scenePos(), + sceneTransform()); + if (portIndex != INVALID) + { + // start dragging existing connection + if (portToCheck == PortType::In) + { + auto con = inOutConnections[(int)PortType::In][portIndex]; + Node *currentNode = inOutConnections[(int)PortType::In][portIndex]->getNode(PortType::In); + Node ¤tNodeRef = *currentNode; + NodeConnectionInteraction interaction(currentNodeRef, *con, _scene); + interaction.disconnect(portToCheck); + } + else // initialize new Connection + { + //Get the node from which the connection is created + Node *currentNode = inOutConnections[(int)PortType::Out][portIndex]->getNode(PortType::Out); + int nodePortIndex = inOutConnections[(int)PortType::Out][portIndex]->getPortIndex(PortType::Out); + Node ¤tNodeRef = *currentNode; + + //Create the group connection + auto connection = _scene.createConnection(portToCheck, + currentNodeRef, + nodePortIndex); + + //Set connection position + QPointF position = portScenePosition(portIndex, PortType::Out); + QPointF connectionPos = connection->getConnectionGraphicsObject().sceneTransform().inverted().map(position); + connection->connectionGeometry().setEndPoint(PortType::In, connectionPos); + connection->connectionGeometry().setEndPoint(PortType::Out, connectionPos); + + //Set the group to the connection + Group &thisGroup = group(); + connection->setGroup(&thisGroup, PortType::Out, portIndex); + + + //Add to the connections + inOutConnections[(int)PortType::Out].push_back(connection.get()); + + //Set the connection to the node + currentNodeRef.nodeState().setConnection(portToCheck, + nodePortIndex, + *connection); + + + connection->getConnectionGraphicsObject().grabMouse(); + connection->getConnectionGraphicsObject().move(); + } + } + }; + + clickPort(PortType::In); + clickPort(PortType::Out); } @@ -190,6 +545,8 @@ mouseMoveEvent(QGraphicsSceneMouseEvent * event) prepareGeometryChange(); sizeX += diff; update(); + _proxyWidget->setPos(QPointF(sizeX/2 - _proxyWidget->size().width()/2, 0)); + _collapseButton->setPos(QPointF(sizeX - _collapseButton->size().width(), 0)); event->accept(); } else if(isResizingY) { @@ -197,7 +554,8 @@ mouseMoveEvent(QGraphicsSceneMouseEvent * event) prepareGeometryChange(); sizeY += diff; update(); - _proxyWidget->setPos(pos() + QPointF(sizeX/2 - _proxyWidget->size().width()/2, 0)); + _proxyWidget->setPos(QPointF(sizeX/2 - _proxyWidget->size().width()/2, 0)); + _collapseButton->setPos(QPointF(sizeX - _collapseButton->size().width(), 0)); event->accept(); } else if(isResizingXY) { auto diff = event->pos() - event->lastPos(); @@ -205,7 +563,8 @@ mouseMoveEvent(QGraphicsSceneMouseEvent * event) sizeX += diff.x(); sizeY += diff.y(); update(); - _proxyWidget->setPos(pos() + QPointF(sizeX/2 - _proxyWidget->size().width()/2, 0)); + _proxyWidget->setPos(QPointF(sizeX/2 - _proxyWidget->size().width()/2, 0)); + _collapseButton->setPos(QPointF(sizeX - _collapseButton->size().width(), 0)); event->accept(); } else { _scene.groupMoved(_group, pos()); @@ -243,7 +602,6 @@ void GroupGraphicsObject:: hoverLeaveEvent(QGraphicsSceneHoverEvent * event) { - // std::cout << "hverleave" << std::endl; update(); event->accept(); } @@ -277,7 +635,7 @@ GroupGraphicsObject:: mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) { _scene.groupDoubleClicked(group()); - // std::cout << "doubleclick" << std::endl; + QGraphicsItem::mouseDoubleClickEvent(event); } From d198287475b2c399a0802f0bf3e5179b07b9b5e5 Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Mon, 8 Aug 2022 10:14:15 +0100 Subject: [PATCH 23/39] Update view on geometry --- include/nodes/internal/FlowScene.hpp | 1 + include/nodes/internal/Node.hpp | 6 +++++ include/nodes/internal/NodeGeometry.hpp | 1 + include/nodes/internal/NodeGraphicsObject.hpp | 3 +++ include/nodes/internal/NodeState.hpp | 13 +++++++++- src/FlowScene.cpp | 15 ++++++++++-- src/FlowView.cpp | 14 +++++++---- src/Node.cpp | 24 +++++++++++++++++++ src/NodeGeometry.cpp | 8 +++++++ src/NodeGraphicsObject.cpp | 8 +++++++ src/NodeState.cpp | 20 +++++++++++++++- 11 files changed, 105 insertions(+), 8 deletions(-) diff --git a/include/nodes/internal/FlowScene.hpp b/include/nodes/internal/FlowScene.hpp index 8ad26daa..c1829b5c 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 78bdc501..de116962 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 06245902..5ee5f8c1 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 fe87c6c8..02bba6d2 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 fc1b175f..b1071ad1 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 15f777ad..e98cf9ca 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 c926d0f0..e7f52815 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 93d627df..c0bc6aa9 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 0c8d5318..6875c175 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 45ead5a1..35fbc738 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 670ea3cd..c4db5ebc 100644 --- a/src/NodeState.cpp +++ b/src/NodeState.cpp @@ -18,6 +18,7 @@ NodeState(std::unique_ptr const &model) , _reaction(NOT_REACTING) , _reactingPortType(PortType::None) , _resizing(false) + , _model(model) {} @@ -41,7 +42,7 @@ getEntries(PortType portType) else return _outConnections; } - + NodeState::ConnectionPtrSet NodeState:: @@ -76,6 +77,13 @@ eraseConnection(PortType portType, } +void +NodeState:: +eraseInputAtIndex(PortIndex portIndex) +{ + +} + NodeState::ReactToConnectionState NodeState:: reaction() const @@ -136,3 +144,13 @@ resizing() const { return _resizing; } + + + +void +NodeState:: +updateEntries() +{ + _inConnections.resize(_model->nPorts(PortType::In)); + _outConnections.resize(_model->nPorts(PortType::Out)); +} \ No newline at end of file From a0318ec01f073929242fd5f1590baded7cb80616 Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Thu, 22 Sep 2022 15:33:30 +0100 Subject: [PATCH 24/39] Loading optimizations --- include/nodes/internal/Node.hpp | 3 +++ src/FlowScene.cpp | 15 ++++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/include/nodes/internal/Node.hpp b/include/nodes/internal/Node.hpp index de116962..781f015e 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 e98cf9ca..8e5cbe61 100644 --- a/src/FlowScene.cpp +++ b/src/FlowScene.cpp @@ -149,6 +149,8 @@ restoreConnection(QJsonObject const &connectionJson) portIndexIn = std::min(numConnectionsIn - 1, portIndexIn); portIndexOut = std::min(numConnectionsOut - 1, portIndexOut); + nodeIn->currentInputConnections++; + return createConnection(*nodeIn, portIndexIn, *nodeOut, portIndexOut); } @@ -283,9 +285,7 @@ restoreNode(QJsonObject const& nodeJson) { dataModel = registry().create("DeletedNode"); } - /*throw std::logic_error(std::string("No registered model with name ") + - modelName.toLocal8Bit().data()); -*/ + auto node = std::make_shared(std::move(dataModel)); auto ngo = std::make_unique(*this, *node); node->setGraphicsObject(std::move(ngo)); @@ -799,6 +799,15 @@ loadFromMemory(const QByteArray& data) QJsonArray connectionJsonArray = jsonDocument["connections"].toArray(); + for (int i = 0; i < connectionJsonArray.size(); ++i) + { + QJsonObject jsonObject = connectionJsonArray[i].toObject(); + QUuid nodeInId = QUuid(jsonObject["in_id"].toString()); + auto nodeIn = _nodes[nodeInId].get(); + nodeIn->targetInputConnections++; + } + + for (int i = 0; i < connectionJsonArray.size(); ++i) { restoreConnection(connectionJsonArray[i].toObject()); From 54b5cf942b50a3957770db7543769de2fa289aeb Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Mon, 21 Nov 2022 09:37:35 +0000 Subject: [PATCH 25/39] Optimized loading --- include/nodes/internal/Node.hpp | 8 ++++++ src/FlowScene.cpp | 48 +++++++++++++++++++++++++++++-- src/Node.cpp | 11 +++++++ src/NodeConnectionInteraction.cpp | 3 +- 4 files changed, 66 insertions(+), 4 deletions(-) diff --git a/include/nodes/internal/Node.hpp b/include/nodes/internal/Node.hpp index de116962..0736800e 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 e98cf9ca..a0695fdc 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 c0bc6aa9..f829cf0a 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 1fd5c116..b6ada3ad 100644 --- a/src/NodeConnectionInteraction.cpp +++ b/src/NodeConnectionInteraction.cpp @@ -150,7 +150,8 @@ tryConnect() const if (outNode) { PortIndex outPortIndex = _connection->getPortIndex(PortType::Out); - outNode->onDataUpdated(outPortIndex); + // outNode->onDataUpdated(outPortIndex); + outNode->onDataUpdatedConnection(outPortIndex, _connection); } _scene->UpdateHistory(); From 410c8233832138c57c6ae0042f07c844a27f60ec Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Mon, 21 Nov 2022 09:38:00 +0000 Subject: [PATCH 26/39] started undoRedo --- include/nodes/internal/FlowScene.hpp | 13 +++++++++++++ src/FlowScene.cpp | 15 ++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/include/nodes/internal/FlowScene.hpp b/include/nodes/internal/FlowScene.hpp index c1829b5c..73078d57 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 a0695fdc..c8ef12b7 100644 --- a/src/FlowScene.cpp +++ b/src/FlowScene.cpp @@ -39,6 +39,8 @@ using QtNodes::NodeDataModel; using QtNodes::PortType; using QtNodes::PortIndex; + + FlowScene:: FlowScene(std::shared_ptr registry) : _registry(registry) @@ -51,7 +53,18 @@ FlowScene(std::shared_ptr registry) auto UpdateLamda = [this](Node& n, const QPointF& p) { resolveGroups(n); - UpdateHistory(); + actionsHistory.push(UndoRedoAction( + [](double v) + { + return 0; + }, + [](double v) + { + return 0; + } + )); + + // UpdateHistory(); }; connect(this, &FlowScene::nodeMoveFinished, this, UpdateLamda); From 09d79269d033d6733b7f3d2fb241046ac1efeb2d Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Tue, 22 Nov 2022 11:28:30 +0000 Subject: [PATCH 27/39] progress on undo/redo system --- include/nodes/internal/FlowScene.hpp | 23 ++- include/nodes/internal/FlowView.hpp | 6 +- .../nodes/internal/GroupGraphicsObject.hpp | 1 + include/nodes/internal/NodeGraphicsObject.hpp | 2 +- include/nodes/internal/NodeState.hpp | 3 + src/FlowScene.cpp | 191 +++++++++++++----- src/FlowView.cpp | 159 +++++++++++++-- src/GroupGraphicsObject.cpp | 3 +- src/NodeConnectionInteraction.cpp | 8 +- src/NodeGraphicsObject.cpp | 5 +- src/NodeState.cpp | 26 +++ 11 files changed, 334 insertions(+), 93 deletions(-) diff --git a/include/nodes/internal/FlowScene.hpp b/include/nodes/internal/FlowScene.hpp index 73078d57..5bf5dadc 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 9320e304..d14df633 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 cb5dc046..a28da495 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 02bba6d2..77d7ab8a 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 b1071ad1..f3f6052f 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 c8ef12b7..eb72220b 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 e7f52815..c843dc29 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 eccb0003..facd4d78 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 b6ada3ad..88460508 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 35fbc738..b07d878c 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 c4db5ebc..91d80f5f 100644 --- a/src/NodeState.cpp +++ b/src/NodeState.cpp @@ -54,6 +54,32 @@ connections(PortType portType, PortIndex portIndex) const } +std::vector +NodeState:: +allConnections() const +{ + std::vector res; + std::vector ins = getEntries(PortType::In); + for(int i=0; i outs = getEntries(PortType::Out); + for(int i=0; i Date: Tue, 6 Dec 2022 18:21:44 +0000 Subject: [PATCH 28/39] UndoRedo for paste, duplicate and create connection --- include/nodes/internal/Connection.hpp | 3 +- include/nodes/internal/FlowScene.hpp | 4 +- src/Connection.cpp | 10 ++- src/FlowScene.cpp | 15 +++- src/FlowView.cpp | 117 ++------------------------ src/Node.cpp | 13 +-- src/NodeConnectionInteraction.cpp | 26 +++++- 7 files changed, 64 insertions(+), 124 deletions(-) diff --git a/include/nodes/internal/Connection.hpp b/include/nodes/internal/Connection.hpp index 216f6b39..1fca7f87 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 5bf5dadc..6933b185 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 72ab154a..38a46059 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 eb72220b..28e0e3be 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 c843dc29..b3330130 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 f829cf0a..4d4df6df 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 88460508..9bc956ce 100644 --- a/src/NodeConnectionInteraction.cpp +++ b/src/NodeConnectionInteraction.cpp @@ -114,13 +114,11 @@ tryConnect() const { _scene->createConnection(converterNode, 0, *outNode, outNodePortIndex); _scene->createConnection(*_node, portIndex, converterNode, 0); - // _scene->UpdateHistory(); } else { _scene->createConnection(converterNode, 0, *_node, portIndex); _scene->createConnection(*outNode, outNodePortIndex, converterNode, 0); - // _scene->UpdateHistory(); } //Delete the users connection, we already replaced it. @@ -155,6 +153,30 @@ tryConnect() const } // _scene->UpdateHistory(); + QUuid connectionID = _connection->id(); + FlowScene *scene = _scene; + Node *nodeIn = _connection->getNode(PortType::In); + Node *nodeOut = _connection->getNode(PortType::Out); + PortIndex portIn = _connection->getPortIndex(PortType::In); + PortIndex portOut = _connection->getPortIndex(PortType::Out); + _scene->AddAction(UndoRedoAction( + [scene, connectionID](double v) + { + scene->deleteConnectionWithID(connectionID); + return 0; + }, + [scene, nodeIn, portIn, nodeOut, portOut, connectionID](double v) + { + QUuid id = connectionID; + scene->createConnection(*nodeIn, + portIn, + *nodeOut, + portOut, + &id); + return 0; + }, + "Created Connection " + )); return true; } From 54c1f5b5964aed074fdb2f8f58fd1d58280437b6 Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Fri, 9 Dec 2022 12:19:50 +0000 Subject: [PATCH 29/39] fixed undo redo --- include/nodes/internal/FlowScene.hpp | 15 +++---- src/FlowScene.cpp | 58 +++++++++++++++------------- src/FlowView.cpp | 51 ++++++++++++------------ src/NodeConnectionInteraction.cpp | 38 +++++++++++++++--- 4 files changed, 95 insertions(+), 67 deletions(-) diff --git a/include/nodes/internal/FlowScene.hpp b/include/nodes/internal/FlowScene.hpp index 6933b185..0729495a 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 28e0e3be..8351af4b 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 b3330130..edef2ae1 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 9bc956ce..c1aa7313 100644 --- a/src/NodeConnectionInteraction.cpp +++ b/src/NodeConnectionInteraction.cpp @@ -152,22 +152,23 @@ tryConnect() const outNode->onDataUpdatedConnection(outPortIndex, _connection); } - // _scene->UpdateHistory(); QUuid connectionID = _connection->id(); FlowScene *scene = _scene; - Node *nodeIn = _connection->getNode(PortType::In); - Node *nodeOut = _connection->getNode(PortType::Out); + QUuid nodeInID = _connection->getNode(PortType::In)->id(); + QUuid nodeOutID = _connection->getNode(PortType::Out)->id(); PortIndex portIn = _connection->getPortIndex(PortType::In); PortIndex portOut = _connection->getPortIndex(PortType::Out); _scene->AddAction(UndoRedoAction( - [scene, connectionID](double v) + [scene, connectionID](void *ptr) { scene->deleteConnectionWithID(connectionID); return 0; }, - [scene, nodeIn, portIn, nodeOut, portOut, connectionID](double v) + [scene, nodeInID, portIn, nodeOutID, portOut, connectionID](void *ptr) { QUuid id = connectionID; + Node *nodeIn = scene->nodes()[nodeInID].get(); + Node *nodeOut = scene->nodes()[nodeOutID].get(); scene->createConnection(*nodeIn, portIn, *nodeOut, @@ -189,6 +190,14 @@ bool NodeConnectionInteraction:: disconnect(PortType portToDisconnect) const { + + QUuid connectionID = _connection->id(); + FlowScene *scene = _scene; + Node *nodeIn = _connection->getNode(PortType::In); + Node *nodeOut = _connection->getNode(PortType::Out); + PortIndex portIn = _connection->getPortIndex(PortType::In); + PortIndex portOut = _connection->getPortIndex(PortType::Out); + PortIndex portIndex = _connection->getPortIndex(portToDisconnect); @@ -207,7 +216,24 @@ disconnect(PortType portToDisconnect) const _connection->getConnectionGraphicsObject().grabMouse(); - // _scene->UpdateHistory(); + _scene->AddAction(UndoRedoAction( + [scene, nodeIn, portIn, nodeOut, portOut, connectionID](void *ptr) + { + QUuid id = connectionID; + scene->createConnection(*nodeIn, + portIn, + *nodeOut, + portOut, + &id); + return 0; + }, + [scene, connectionID](void *ptr) + { + scene->deleteConnectionWithID(connectionID); + return 0; + }, + "Removed Connection " + )); return true; } From c0706451526b5f7e377e1164f3825bc4695a1ef0 Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Thu, 23 Nov 2023 16:44:14 +0000 Subject: [PATCH 30/39] added gotonode function --- include/nodes/internal/FlowView.hpp | 6 ++++++ src/FlowScene.cpp | 14 ++++++++++---- src/FlowView.cpp | 19 +++++++++++++++++++ src/NodeGraphicsObject.cpp | 2 +- 4 files changed, 36 insertions(+), 5 deletions(-) diff --git a/include/nodes/internal/FlowView.hpp b/include/nodes/internal/FlowView.hpp index d14df633..15986571 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 8351af4b..1329712a 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 edef2ae1..c390b690 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 b07d878c..6ce2e6c1 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -122,7 +122,7 @@ embedQWidget() _proxyWidget->setPos(geom.widgetPosition()); update(); - + _proxyWidget->setContentsMargins(0, 0, 0, 0); _proxyWidget->setOpacity(1.0); _proxyWidget->setFlag(QGraphicsItem::ItemIgnoresParentOpacity); } From f5a177f47c452de37586eff2419a2dfad38f2ee7 Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Mon, 10 Jun 2024 12:00:23 +0100 Subject: [PATCH 31/39] added go to node id --- include/nodes/internal/FlowView.hpp | 1 + src/FlowScene.cpp | 5 +++-- src/FlowView.cpp | 6 ++++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/include/nodes/internal/FlowView.hpp b/include/nodes/internal/FlowView.hpp index 15986571..904fba71 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 1329712a..e62ded46 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 c390b690..c54c92af 100644 --- a/src/FlowView.cpp +++ b/src/FlowView.cpp @@ -128,6 +128,12 @@ void FlowView::goToNode(NodeGraphicsObject *node) scale(scaleX, scaleY); } +void +FlowView::goToNodeID(QUuid ID) +{ + goToNode(&(_scene->nodes()[ID]->nodeGraphicsObject())); +} + void FlowView::setScene(FlowScene *scene) { From 7e892614cd33a63236a22575821718506b7d0c04 Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Wed, 25 Sep 2024 17:22:38 +0100 Subject: [PATCH 32/39] added input colour to node --- include/nodes/internal/FlowScene.hpp | 3 +++ include/nodes/internal/Node.hpp | 4 ++++ include/nodes/internal/NodeStyle.hpp | 1 + resources/DefaultStyle.json | 1 + src/Node.cpp | 8 ++++++++ src/NodeGraphicsObject.cpp | 2 ++ src/NodePainter.cpp | 12 ++++++++---- src/NodePainter.hpp | 3 ++- src/NodeStyle.cpp | 1 + 9 files changed, 30 insertions(+), 5 deletions(-) diff --git a/include/nodes/internal/FlowScene.hpp b/include/nodes/internal/FlowScene.hpp index 0729495a..9b59d50f 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 0736800e..8659f81c 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 5f051888..43f5d6b5 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 8375b4a1..90ba80d0 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 4d4df6df..0e94c053 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 6ce2e6c1..2aa92707 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 c2e55712..5147a07b 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 a226d79c..9ad1cd29 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 c62e0ac9..37efc0c2 100644 --- a/src/NodeStyle.cpp +++ b/src/NodeStyle.cpp @@ -123,6 +123,7 @@ loadJsonFromByteArray(QByteArray const &byteArray) NODE_STYLE_READ_COLOR(obj, ShadowColor); NODE_STYLE_READ_COLOR(obj, FontColor); NODE_STYLE_READ_COLOR(obj, FontColorFaded); + NODE_STYLE_READ_COLOR(obj, FontColorSelected); NODE_STYLE_READ_COLOR(obj, ConnectionPointColor); NODE_STYLE_READ_COLOR(obj, FilledConnectionPointColor); NODE_STYLE_READ_COLOR(obj, WarningColor); From 6f8c72d91ad23985706d840e8b937d64dc9a1bf7 Mon Sep 17 00:00:00 2001 From: Tom Date: Fri, 27 Sep 2024 16:47:19 +0100 Subject: [PATCH 33/39] fix crash --- src/NodeGraphicsObject.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/NodeGraphicsObject.cpp b/src/NodeGraphicsObject.cpp index 2aa92707..91703704 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -185,6 +185,10 @@ paint(QPainter * painter, QStyleOptionGraphicsItem const* option, QWidget* ) { + if(_node.inputSelected.size() != _node.nodeDataModel()->nPorts(PortType::In)) + { + _node.inputSelected.resize(_node.nodeDataModel()->nPorts(PortType::In)); + } painter->setClipRect(option->exposedRect); NodePainter::paint(painter, _node, _scene); From bdd4e90fd6a7636dc2f21e62d6458c81213a1834 Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Fri, 30 May 2025 10:20:48 +0100 Subject: [PATCH 34/39] crash fix --- CMakeLists.txt | 2 +- src/FlowScene.cpp | 1 + src/Node.cpp | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 71390c58..c9a018b1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.2) +cmake_minimum_required(VERSION 3.5) # version 3.4 is required as other do not work with C++14 and clang project(NodeEditor CXX) diff --git a/src/FlowScene.cpp b/src/FlowScene.cpp index e62ded46..f71f501e 100644 --- a/src/FlowScene.cpp +++ b/src/FlowScene.cpp @@ -391,6 +391,7 @@ QUuid FlowScene::pasteNode(QJsonObject &nodeJson, QPointF nodeGroupCentroid, QPo auto nodePtr = node.get(); _nodes[node->id()] = std::move(node); nodeCreated(*nodePtr); + nodePtr->updateView(); return newId; } diff --git a/src/Node.cpp b/src/Node.cpp index 0e94c053..04e6b6ab 100644 --- a/src/Node.cpp +++ b/src/Node.cpp @@ -138,6 +138,7 @@ paste(QJsonObject const& json, QUuid ID) _nodeGraphicsObject->setPos(point); _nodeDataModel->restore(json["model"].toObject()); + // this->updateView(); } From 73ef40bd6667cc4f9cb4a099a1429468850eb9f7 Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Tue, 3 Jun 2025 09:04:15 +0100 Subject: [PATCH 35/39] added node not found signal --- include/nodes/internal/FlowView.hpp | 2 ++ src/FlowView.cpp | 48 ++++++++++++++++------------- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/include/nodes/internal/FlowView.hpp b/include/nodes/internal/FlowView.hpp index 904fba71..302e4bf1 100644 --- a/include/nodes/internal/FlowView.hpp +++ b/include/nodes/internal/FlowView.hpp @@ -72,6 +72,8 @@ public slots: void addAnchor(int index); void goToAnchor(int index); +signals: + void nodeNotFound(const QString &str); protected: diff --git a/src/FlowView.cpp b/src/FlowView.cpp index c54c92af..2308b32d 100644 --- a/src/FlowView.cpp +++ b/src/FlowView.cpp @@ -306,27 +306,27 @@ contextMenuEvent(QContextMenuEvent *event) return; } - if (item->parent() != nullptr) - { - QString parent = item->parent()->data(0, Qt::UserRole).toString(); - if(parent == "Templates") - { - DataModelRegistry::RegisteredTemplatesMap map = _scene->registry().RegisteredTemplates(); - QString fileName = map[modelName]; - - QFile file; - file.setFileName(fileName); - file.open(QIODevice::ReadOnly | QIODevice::Text); - QString val = file.readAll(); - file.close(); - qWarning() << val; - QJsonDocument d = QJsonDocument::fromJson(val.toUtf8()); - QJsonObject sett2 = d.object(); - jsonToSceneMousePos(sett2); - - modelMenu.close(); - } - } + if (item->parent() != nullptr) + { + QString parent = item->parent()->data(0, Qt::UserRole).toString(); + if(parent == "Templates") + { + DataModelRegistry::RegisteredTemplatesMap map = _scene->registry().RegisteredTemplates(); + QString fileName = map[modelName]; + + QFile file; + file.setFileName(fileName); + file.open(QIODevice::ReadOnly | QIODevice::Text); + QString val = file.readAll(); + file.close(); + qWarning() << val; + QJsonDocument d = QJsonDocument::fromJson(val.toUtf8()); + QJsonObject sett2 = d.object(); + jsonToSceneMousePos(sett2); + + modelMenu.close(); + } + } auto type = _scene->registry().create(modelName); @@ -393,6 +393,12 @@ contextMenuEvent(QContextMenuEvent *event) } }); + connect(txtBox, &QLineEdit::returnPressed, [&]() { + QString text = txtBox->text(); + emit nodeNotFound(text); + modelMenu.close(); + }); + // make sure the text box gets focus so the user doesn't have to click on it txtBox->setFocus(); modelMenu.exec(event->globalPos()); From 42a5316c5cf804ae0d257eaba064b907f2b381ea Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Mon, 7 Jul 2025 08:58:34 +0100 Subject: [PATCH 36/39] added action signal --- include/nodes/internal/FlowScene.hpp | 6 ++++-- src/FlowScene.cpp | 12 ++++++------ src/FlowView.cpp | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/include/nodes/internal/FlowScene.hpp b/include/nodes/internal/FlowScene.hpp index 9b59d50f..4f243791 100644 --- a/include/nodes/internal/FlowScene.hpp +++ b/include/nodes/internal/FlowScene.hpp @@ -31,8 +31,8 @@ struct UndoRedoAction { std::function undoAction; std::function redoAction; - std::string name; - UndoRedoAction(std::function undoAction, std::function redoAction, std::string name) { + QString name; + UndoRedoAction(std::function undoAction, std::function redoAction, const QString &name) { this->undoAction = undoAction; this->redoAction = redoAction; this->name = name; @@ -189,6 +189,8 @@ class NODE_EDITOR_PUBLIC FlowScene void nodeHoverLeft(Node& n); void nodeContextMenu(Node& n, const QPointF& pos); + + void ActionAdded(const QString actionName); public: diff --git a/src/FlowScene.cpp b/src/FlowScene.cpp index f71f501e..55e2e523 100644 --- a/src/FlowScene.cpp +++ b/src/FlowScene.cpp @@ -68,7 +68,7 @@ FlowScene(std::shared_ptr registry) n->nodeGraphicsObject().setPos(newPosition); return 0; }, - "Moved Node " + n.nodeDataModel()->name().toStdString() + "Moved Node " + n.nodeDataModel()->name() ) ); }; @@ -90,7 +90,7 @@ FlowScene(std::shared_ptr registry) g.groupGraphicsObject().setPos(newPosition); return 0; }, - "Moved Group " + g.GetName().toStdString() + "Moved Group " + g.GetName() )); }; connect(this, &FlowScene::groupMoveFinished, this, GroupUpdateLamda); @@ -985,14 +985,14 @@ loadFromMemory(const QByteArray& data) void FlowScene::PrintActions() { - std::cout << "ACTIONS " << std::endl; + qDebug() << "ACTIONS "; for(int i=0; iname().toStdString() + "Created Node " + node.nodeDataModel()->name() )); } else From 21eee6e0a959015fb7bad56849ae0eecd6acbf28 Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Mon, 21 Jul 2025 14:07:45 +0100 Subject: [PATCH 37/39] Prevent duplicate connections in scene serialization Added logic to avoid saving duplicate connections when serializing the scene and when exporting selected nodes and connections. This ensures that each connection is only included once in the resulting JSON, improving data integrity and preventing redundant entries. --- src/FlowScene.cpp | 22 +++++++++++++++++++++- src/FlowView.cpp | 23 +++++++++++++++-------- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/FlowScene.cpp b/src/FlowScene.cpp index 55e2e523..32f25a33 100644 --- a/src/FlowScene.cpp +++ b/src/FlowScene.cpp @@ -2,6 +2,8 @@ #include #include +#include +#include #include #include @@ -856,6 +858,8 @@ saveToMemory() const sceneJson["nodes"] = nodesJsonArray; QJsonArray connectionJsonArray; + std::set> uniqueConnections; + for (auto const & pair : _connections) { auto const &connection = pair.second; @@ -863,7 +867,23 @@ saveToMemory() const QJsonObject connectionJson = connection->save(); if (!connectionJson.isEmpty()) - connectionJsonArray.append(connectionJson); + { + // Extract connection details for duplicate detection + QUuid nodeInId = QUuid(connectionJson["in_id"].toString()); + QUuid nodeOutId = QUuid(connectionJson["out_id"].toString()); + PortIndex portIndexIn = connectionJson["in_index"].toInt(); + PortIndex portIndexOut = connectionJson["out_index"].toInt(); + + // Create a unique identifier tuple + auto connectionKey = std::make_tuple(nodeInId, portIndexIn, nodeOutId, portIndexOut); + + // Only add if we haven't seen this connection before + if (uniqueConnections.find(connectionKey) == uniqueConnections.end()) + { + uniqueConnections.insert(connectionKey); + connectionJsonArray.append(connectionJson); + } + } } sceneJson["connections"] = connectionJsonArray; diff --git a/src/FlowView.cpp b/src/FlowView.cpp index 33f8335d..35d9f35e 100644 --- a/src/FlowView.cpp +++ b/src/FlowView.cpp @@ -460,7 +460,7 @@ FlowView:: deleteSelectedNodes() { QJsonObject sceneJson = selectionToJson(true); - + qDebug() << sceneJson; std::vector nodeIds; std::vector connectionsIds; for (QGraphicsItem * item : _scene->selectedItems()) @@ -652,7 +652,8 @@ QJsonObject FlowView::selectionToJson(bool includePartialConnections) QJsonObject sceneJson; QJsonArray nodesJsonArray; QJsonArray connectionJsonArray; - std::vector addedIds; + std::set addedNodeIds; + std::set addedConnectionIds; for (QGraphicsItem * item : _scene->selectedItems()) { @@ -660,35 +661,41 @@ QJsonObject FlowView::selectionToJson(bool includePartialConnections) { Node& node = n->node(); nodesJsonArray.append(node.save()); - addedIds.push_back(node.id()); + addedNodeIds.insert(node.id()); - if(includePartialConnections) + if(includePartialConnections) //find all connections of that node, even if the other node is not selected { std::vector allConnections = node.nodeState().allConnections(); for(int i=0; iid()) != addedConnectionIds.end()) + continue; //Already added this connection + QJsonObject connectionJson = allConnections[i]->save(); if (!connectionJson.isEmpty()) + { connectionJsonArray.append(connectionJson); + addedConnectionIds.insert(allConnections[i]->id()); + } } } } } for (QGraphicsItem * item : _scene->selectedItems()) - { + { if (auto c = qgraphicsitem_cast(item)) { Connection& connection = c->connection(); - if((std::find(addedIds.begin(), addedIds.end(), connection.getNode(PortType::In)->id()) != addedIds.end() && - std::find(addedIds.begin(), addedIds.end(), connection.getNode(PortType::Out)->id()) != addedIds.end()) || includePartialConnections) { + if(addedConnectionIds.find(connection.id()) == addedConnectionIds.end()) { QJsonObject connectionJson = connection.save(); - if (!connectionJson.isEmpty()) connectionJsonArray.append(connectionJson); + addedConnectionIds.insert(connection.id()); } } } + QJsonArray groupJsonArray; for (QGraphicsItem * item : _scene->selectedItems()) From f0fada85297e6ed40fee5728f5e1b187bb05246a Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Mon, 8 Sep 2025 10:22:35 +0100 Subject: [PATCH 38/39] fixed line return on error messages --- src/FlowScene.cpp | 3 +++ src/NodeGeometry.cpp | 43 +++++++++++++++++++++++++++++++++++++++++-- src/NodePainter.cpp | 14 +++++++++----- 3 files changed, 53 insertions(+), 7 deletions(-) diff --git a/src/FlowScene.cpp b/src/FlowScene.cpp index 32f25a33..2d1955e1 100644 --- a/src/FlowScene.cpp +++ b/src/FlowScene.cpp @@ -202,6 +202,9 @@ void FlowScene::pasteConnection(QJsonObject const &connectionJson, QUuid newIn, auto nodeIn = _nodes[newIn].get(); auto nodeOut = _nodes[newOut].get(); + if (!nodeIn || !nodeOut) + return; + createConnection(*nodeIn, portIndexIn, *nodeOut, portIndexOut); } diff --git a/src/NodeGeometry.cpp b/src/NodeGeometry.cpp index 6875c175..36258c4f 100644 --- a/src/NodeGeometry.cpp +++ b/src/NodeGeometry.cpp @@ -285,7 +285,25 @@ validationHeight() const { QString msg = _dataModel->validationMessage(); - return _boldFontMetrics.boundingRect(msg).height(); + if (msg.isEmpty()) + return 0; + + // Calculate height for multi-line text with word wrapping + // Use the node width minus padding for text wrapping + double padding = 8.0; // 4.0 padding on each side + int availableWidth = _width - static_cast(padding); + + // Ensure we have a minimum width for the calculation + if (availableWidth < 50) + availableWidth = 50; + + // Use boundingRect with width constraint to get the actual height needed for wrapped text + QRect boundingRect = _boldFontMetrics.boundingRect(0, 0, availableWidth, 0, + Qt::AlignCenter | Qt::TextWordWrap, + msg); + + // Add some padding for better visual appearance + return boundingRect.height() + static_cast(padding); } @@ -295,7 +313,28 @@ validationWidth() const { QString msg = _dataModel->validationMessage(); - return _boldFontMetrics.boundingRect(msg).width(); + if (msg.isEmpty()) + return 0; + + // Instead of using the full text width, return a reasonable maximum width + // that allows for text wrapping. This prevents the node from becoming too wide. + double padding = 8.0; // 4.0 padding on each side + int availableWidth = _width - static_cast(padding); + + // Ensure we have a minimum width for the calculation + if (availableWidth < 50) + availableWidth = 50; + QRect boundingRect = _boldFontMetrics.boundingRect(0, 0, availableWidth, 0, + Qt::AlignCenter | Qt::TextWordWrap, + msg); + int singleLineWidth = boundingRect.width(); + + // Set a maximum width - this can be adjusted based on your UI preferences + // int maxWidth = 300; // Adjust this value as needed + + // // Use the smaller of the two: either the natural text width or our maximum + // return std::min(singleLineWidth, maxWidth); + return singleLineWidth; } diff --git a/src/NodePainter.cpp b/src/NodePainter.cpp index 5147a07b..d6bd0725 100644 --- a/src/NodePainter.cpp +++ b/src/NodePainter.cpp @@ -420,13 +420,17 @@ drawValidationRect(QPainter * painter, QFontMetrics metrics(f); - auto rect = metrics.boundingRect(errorMsg); - - QPointF position((geom.width() - rect.width()) / 2.0, - geom.height() - (geom.validationHeight() - diam) / 2.0); + // Calculate the available area for text with some padding + double padding = 4.0; + QRectF textRect(padding, + geom.height() - geom.validationHeight() + padding, + geom.width() - 2 * padding, + geom.validationHeight() - 2 * padding); painter->setFont(f); painter->setPen(nodeStyle.FontColor); - painter->drawText(position, errorMsg); + + // Use drawText with rectangle and flags to enable word wrapping and line breaks + painter->drawText(textRect, Qt::AlignCenter | Qt::TextWordWrap, errorMsg); } } From 24c2a790f0f3a907c46652c809c70acdad0f868c Mon Sep 17 00:00:00 2001 From: jacque pillet Date: Thu, 18 Sep 2025 10:22:45 +0100 Subject: [PATCH 39/39] added placeholder nodes --- include/nodes/internal/FlowScene.hpp | 14 +- include/nodes/internal/FlowView.hpp | 7 +- include/nodes/internal/Node.hpp | 9 +- src/FlowScene.cpp | 303 ++++++++++++++++++++++++++- src/FlowView.cpp | 282 +------------------------ src/Node.cpp | 9 + src/NodeGraphicsObject.cpp | 11 +- 7 files changed, 348 insertions(+), 287 deletions(-) diff --git a/include/nodes/internal/FlowScene.hpp b/include/nodes/internal/FlowScene.hpp index 4f243791..4a3d1d37 100644 --- a/include/nodes/internal/FlowScene.hpp +++ b/include/nodes/internal/FlowScene.hpp @@ -103,6 +103,8 @@ class NODE_EDITOR_PUBLIC FlowScene DataModelRegistry®istry() const; + std::shared_ptrregistryShared() const; + void setRegistry(std::shared_ptr registry); void iterateOverNodes(std::function visitor); @@ -128,7 +130,7 @@ class NODE_EDITOR_PUBLIC FlowScene std::unordered_map > const &connections() const; - std::vector>selectedNodes() const; + std::vector selectedNodes() const; public: @@ -154,6 +156,16 @@ class NODE_EDITOR_PUBLIC FlowScene int GetHistoryIndex(); + void deleteSelectedNodes(); + + QJsonObject selectionToJson(bool includePartialConnections=false); + + void jsonToScene(QJsonObject object); + + void jsonToSceneMousePos(QJsonObject object, QPointF mousePos); + + void deleteJsonElements(const QJsonObject &object); + signals: void nodeCreated(Node &n); diff --git a/include/nodes/internal/FlowView.hpp b/include/nodes/internal/FlowView.hpp index 302e4bf1..dfde0dd2 100644 --- a/include/nodes/internal/FlowView.hpp +++ b/include/nodes/internal/FlowView.hpp @@ -28,12 +28,10 @@ class NODE_EDITOR_PUBLIC FlowView void setScene(FlowScene *scene); - QJsonObject selectionToJson(bool includePartialConnections=false); - void jsonToScene(QJsonObject object); void jsonToSceneMousePos(QJsonObject object); - void deleteJsonElements(const QJsonObject &object); - + void goToNode(NodeGraphicsObject *node); + void goToNodeID(QUuid ID); @@ -43,7 +41,6 @@ public slots: void scaleDown(); - void deleteSelectedNodes(); void duplicateSelectedNode(); diff --git a/include/nodes/internal/Node.hpp b/include/nodes/internal/Node.hpp index 8659f81c..c3a5000a 100644 --- a/include/nodes/internal/Node.hpp +++ b/include/nodes/internal/Node.hpp @@ -56,9 +56,6 @@ class NODE_EDITOR_PUBLIC Node paste(QJsonObject const &json, QUuid ID); - void - updateView(); - void eraseInputAtIndex(int portIndex); @@ -102,6 +99,12 @@ class NODE_EDITOR_PUBLIC Node NodeDataModel* nodeDataModel() const; + void + updateView(); + + void + updateEntries(); + void setInputSelected(int inx, bool selected); int targetInputConnections=0; diff --git a/src/FlowScene.cpp b/src/FlowScene.cpp index 32f25a33..00926f23 100644 --- a/src/FlowScene.cpp +++ b/src/FlowScene.cpp @@ -148,11 +148,12 @@ createConnection(Node& nodeIn, auto cgo = std::make_unique(*this, *connection); + // after this function connection points are set to node port + connection->setGraphicsObject(std::move(cgo)); + nodeIn.nodeState().setConnection(PortType::In, portIndexIn, *connection); nodeOut.nodeState().setConnection(PortType::Out, portIndexOut, *connection); - // after this function connection points are set to node port - connection->setGraphicsObject(std::move(cgo)); // trigger data propagation nodeOut.onDataUpdated(portIndexOut); @@ -487,6 +488,12 @@ registry() const return *_registry; } +std::shared_ptr +FlowScene::registryShared() const +{ + return _registry; +} + void FlowScene:: @@ -728,13 +735,13 @@ connections() const } -std::vector> +std::vector FlowScene:: selectedNodes() const { QList graphicsItems = selectedItems(); - std::vector> ret; + std::vector ret; ret.reserve(graphicsItems.size()); @@ -745,8 +752,7 @@ selectedNodes() const if (ngo != nullptr) { Node* n = &(ngo->node()); - std::shared_ptr ptr(n); - ret.push_back(ptr); + ret.push_back(n); } } @@ -1074,6 +1080,291 @@ int FlowScene::GetHistoryIndex() { } + +void +FlowScene:: +deleteSelectedNodes() +{ + QJsonObject sceneJson = selectionToJson(true); + qDebug() << sceneJson; + std::vector nodeIds; + std::vector connectionsIds; + for (QGraphicsItem * item : selectedItems()) + { + if(item) + { + if (auto c = qgraphicsitem_cast(item)) + { + deleteConnection(c->connection()); + } + } + } + for (QGraphicsItem * item : selectedItems()) + { + if(item) + { + if (auto c = qgraphicsitem_cast(item)) + { + removeGroup(c->group()); + } + else if (auto c = qgraphicsitem_cast(item)) + { + removeNode(c->node()); + } + } + } + + + AddAction(UndoRedoAction( + //Undo action + [this, sceneJson](void *ptr) + { + jsonToScene(sceneJson); + return 0; + }, + //Redo action + [this, sceneJson](void *ptr) + { + deleteJsonElements(sceneJson); + return 0; + }, + //Name + "Deletes nodes" + )); +} + +QJsonObject FlowScene::selectionToJson(bool includePartialConnections) +{ + QJsonObject sceneJson; + QJsonArray nodesJsonArray; + QJsonArray connectionJsonArray; + std::set addedNodeIds; + std::set addedConnectionIds; + + for (QGraphicsItem * item : selectedItems()) + { + if (auto n = qgraphicsitem_cast(item)) + { + Node& node = n->node(); + nodesJsonArray.append(node.save()); + addedNodeIds.insert(node.id()); + + if(includePartialConnections) //find all connections of that node, even if the other node is not selected + { + std::vector allConnections = node.nodeState().allConnections(); + for(int i=0; iid()) != addedConnectionIds.end()) + continue; //Already added this connection + + QJsonObject connectionJson = allConnections[i]->save(); + if (!connectionJson.isEmpty()) + { + connectionJsonArray.append(connectionJson); + addedConnectionIds.insert(allConnections[i]->id()); + } + } + } + } + } + + if(!includePartialConnections) + { + for (QGraphicsItem * item : selectedItems()) + { + if (auto c = qgraphicsitem_cast(item)) { + Connection& connection = c->connection(); + QUuid inNodeId = connection.getNode(PortType::In)->id(); + QUuid outNodeId = connection.getNode(PortType::Out)->id(); + if(addedNodeIds.find(inNodeId) == addedNodeIds.end()) continue; //The input node is not selected + if(addedNodeIds.find(outNodeId) == addedNodeIds.end()) continue; //The output node is not selected + + if(addedConnectionIds.find(connection.id()) == addedConnectionIds.end()) { + QJsonObject connectionJson = connection.save(); + if (!connectionJson.isEmpty()) + connectionJsonArray.append(connectionJson); + addedConnectionIds.insert(connection.id()); + } + } + } + } + + + QJsonArray groupJsonArray; + for (QGraphicsItem * item : selectedItems()) + { + if (auto c = qgraphicsitem_cast(item)) { + Group& group = c->group(); + + //If collapsed + if(group.groupGraphicsObject().isCollapsed()) + { + //Add all child items to nodes array + for(int i=0; i(group.groupGraphicsObject().childItems()[i]); + if(ngo!=nullptr) + { + Node& node = ngo->node(); + nodesJsonArray.append(node.save()); + } + } + + //Add all connections to nodes array + for(int i=0; isave(); + if (!connectionJson.isEmpty()) + connectionJsonArray.append(connectionJson); + } + } + } + + QJsonObject groupJson = group.save(); + if (!groupJson.isEmpty()) + groupJsonArray.append(groupJson); + } + } + + sceneJson["nodes"] = nodesJsonArray; + sceneJson["connections"] = connectionJsonArray; + sceneJson["groups"] = groupJsonArray; + + return sceneJson; +} + +void FlowScene::jsonToScene(QJsonObject jsonDocument) +{ + QJsonArray nodesJsonArray = jsonDocument["nodes"].toArray(); + + + + for (int i = 0; i < nodesJsonArray.size(); ++i) + { + restoreNode(nodesJsonArray[i].toObject(), true); + } + + QJsonArray connectionJsonArray = jsonDocument["connections"].toArray(); + for (int i = 0; i < connectionJsonArray.size(); ++i) + { + QUuid in = QUuid(connectionJsonArray[i].toObject()["in_id"].toString()); + QUuid out = QUuid(connectionJsonArray[i].toObject()["out_id"].toString()); + + qDebug() << "Creating connection from " << in << " to " << out; + + pasteConnection(connectionJsonArray[i].toObject(), in, out ); + } + + QJsonArray groupsJsonArray = jsonDocument["groups"].toArray(); + for (int i = 0; i < groupsJsonArray.size(); ++i) + { + restoreGroup(groupsJsonArray[i].toObject()); + } +} + +void FlowScene::jsonToSceneMousePos(QJsonObject jsonDocument, QPointF mousePos) +{ + QJsonArray nodesJsonArray = jsonDocument["nodes"].toArray(); + + std::map addedIds; + + //Get Bounds of all the selected items + float minx = 10000000000; + float miny = 10000000000; + float maxx = -1000000000; + float maxy = -1000000000; + for (int i = 0; i < nodesJsonArray.size(); ++i) + { + QJsonObject nodeJsonObject = nodesJsonArray[i].toObject(); + QJsonObject positionJson = nodeJsonObject["position"].toObject(); + QPointF pos(positionJson["x"].toDouble(), positionJson["y"].toDouble()); + + if(pos.x() < minx) minx = pos.x(); + if(pos.y() < miny) miny = pos.y(); + if(pos.x() > maxx) maxx = pos.x(); + if(pos.y() > maxy) maxy = pos.y(); + } + + float centroidX = (maxx - minx) / 2.0 + minx; + float centroidY = (maxy - miny) / 2.0 + miny; + QPointF centroid(centroidX, centroidY); + + + + + + for (int i = 0; i < nodesJsonArray.size(); ++i) + { + QJsonObject nodeJson = nodesJsonArray[i].toObject(); + QUuid currentId = QUuid( nodeJson["id"].toString() ); + QUuid newId = pasteNode(nodesJsonArray[i].toObject(), centroid, mousePos); + + addedIds.insert(std::pair(currentId, newId)); + nodesJsonArray[i] = nodes()[newId]->save(); + } + jsonDocument["nodes"] = nodesJsonArray; + + QJsonArray connectionJsonArray = jsonDocument["connections"].toArray(); + for (int i = 0; i < connectionJsonArray.size(); ++i) + { + QJsonObject connectionJson = connectionJsonArray[i].toObject(); + + QUuid in = QUuid(connectionJson["in_id"].toString()); + QUuid newIn = addedIds[in]; + + QUuid out = QUuid(connectionJson["out_id"].toString()); + QUuid newOut = addedIds[out]; + + pasteConnection(connectionJson, newIn, newOut ); + + connectionJson["in_id"] = newIn.toString(); + connectionJson["out_id"] = newOut.toString(); + connectionJsonArray[i] = connectionJson; + } + jsonDocument["connections"] =connectionJsonArray; + + QJsonArray groupsJsonArray = jsonDocument["groups"].toArray(); + for (int i = 0; i < groupsJsonArray.size(); ++i) + { + pasteGroup(groupsJsonArray[i].toObject(), centroid, mousePos); + } + + + AddAction(UndoRedoAction( + [this, addedIds](void *ptr) + { + //Delete all the created nodes (and their connections) + for(auto &id: addedIds) + { + removeNodeWithID(id.second); + } + return 0; + }, + [this, jsonDocument](void *ptr) + { + jsonToScene(jsonDocument); //jsonDocument has now been updated with new IDs and new positions + return 0; + }, + "Created Node " + )); + +} + +void FlowScene::deleteJsonElements(const QJsonObject &jsonDocument) +{ + QJsonArray nodesJsonArray = jsonDocument["nodes"].toArray(); + for (int i = 0; i < nodesJsonArray.size(); ++i) + { + QJsonObject nodeJson = nodesJsonArray[i].toObject(); + QUuid id = QUuid( nodeJson["id"].toString() ); + removeNodeWithID(id); + } +} + + + //------------------------------------------------------------------------------ namespace QtNodes { diff --git a/src/FlowView.cpp b/src/FlowView.cpp index 35d9f35e..7a875934 100644 --- a/src/FlowView.cpp +++ b/src/FlowView.cpp @@ -152,7 +152,11 @@ FlowView::setScene(FlowScene *scene) _deleteSelectionAction = new QAction(QStringLiteral("Delete Selection"), this); _deleteSelectionAction->setShortcut(Qt::Key_Delete); _deleteSelectionAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); - connect(_deleteSelectionAction, &QAction::triggered, this, &FlowView::deleteSelectedNodes); + connect(_deleteSelectionAction, &QAction::triggered, this, [this]() { + if (_scene) { + _scene->deleteSelectedNodes(); + } + }); addAction(_deleteSelectionAction); _duplicateSelectionAction = new QAction(QStringLiteral("Duplicate Selection"), this); @@ -455,72 +459,16 @@ scaleDown() } -void -FlowView:: -deleteSelectedNodes() -{ - QJsonObject sceneJson = selectionToJson(true); - qDebug() << sceneJson; - std::vector nodeIds; - std::vector connectionsIds; - for (QGraphicsItem * item : _scene->selectedItems()) - { - if(item) - { - if (auto c = qgraphicsitem_cast(item)) - { - _scene->deleteConnection(c->connection()); - } - } - } - for (QGraphicsItem * item : _scene->selectedItems()) - { - if(item) - { - if (auto c = qgraphicsitem_cast(item)) - { - _scene->removeGroup(c->group()); - } - else if (auto c = qgraphicsitem_cast(item)) - { - _scene->removeNode(c->node()); - } - } - } - - - _scene->AddAction(UndoRedoAction( - //Undo action - [this, sceneJson](void *ptr) - { - jsonToScene(sceneJson); - return 0; - }, - //Redo action - [this, sceneJson](void *ptr) - { - deleteJsonElements(sceneJson); - return 0; - }, - //Name - "Deletes nodes" - )); -} -void FlowView::deleteJsonElements(const QJsonObject &jsonDocument) -{ - QJsonArray nodesJsonArray = jsonDocument["nodes"].toArray(); - for (int i = 0; i < nodesJsonArray.size(); ++i) - { - QJsonObject nodeJson = nodesJsonArray[i].toObject(); - QUuid id = QUuid( nodeJson["id"].toString() ); - _scene->removeNodeWithID(id); - } +void FlowView::jsonToSceneMousePos(QJsonObject jsonDocument) { + QPoint viewPointMouse = this->mapFromGlobal(QCursor::pos()); + QPointF posViewMouse = this->mapToScene(viewPointMouse); + _scene->jsonToSceneMousePos(jsonDocument, posViewMouse); } void FlowView::copySelectedNodes() { - QJsonObject sceneJson = selectionToJson(); + QJsonObject sceneJson = _scene->selectionToJson(); QJsonDocument document(sceneJson); std::string json = document.toJson().toStdString(); @@ -529,218 +477,10 @@ void FlowView::copySelectedNodes() { p_Clipboard->setText( QString::fromStdString(json)); } -void FlowView::jsonToScene(QJsonObject jsonDocument) -{ - QJsonArray nodesJsonArray = jsonDocument["nodes"].toArray(); - - - - for (int i = 0; i < nodesJsonArray.size(); ++i) - { - _scene->restoreNode(nodesJsonArray[i].toObject(), true); - } - - QJsonArray connectionJsonArray = jsonDocument["connections"].toArray(); - for (int i = 0; i < connectionJsonArray.size(); ++i) - { - QUuid in = QUuid(connectionJsonArray[i].toObject()["in_id"].toString()); - QUuid out = QUuid(connectionJsonArray[i].toObject()["out_id"].toString()); - - _scene->pasteConnection(connectionJsonArray[i].toObject(), in, out ); - } - - QJsonArray groupsJsonArray = jsonDocument["groups"].toArray(); - for (int i = 0; i < groupsJsonArray.size(); ++i) - { - _scene->restoreGroup(groupsJsonArray[i].toObject()); - } -} - - -void FlowView::jsonToSceneMousePos(QJsonObject jsonDocument) -{ - QJsonArray nodesJsonArray = jsonDocument["nodes"].toArray(); - - std::map addedIds; - - //Get Bounds of all the selected items - float minx = 10000000000; - float miny = 10000000000; - float maxx = -1000000000; - float maxy = -1000000000; - for (int i = 0; i < nodesJsonArray.size(); ++i) - { - QJsonObject nodeJsonObject = nodesJsonArray[i].toObject(); - QJsonObject positionJson = nodeJsonObject["position"].toObject(); - QPointF pos(positionJson["x"].toDouble(), positionJson["y"].toDouble()); - - if(pos.x() < minx) minx = pos.x(); - if(pos.y() < miny) miny = pos.y(); - if(pos.x() > maxx) maxx = pos.x(); - if(pos.y() > maxy) maxy = pos.y(); - } - - float centroidX = (maxx - minx) / 2.0 + minx; - float centroidY = (maxy - miny) / 2.0 + miny; - QPointF centroid(centroidX, centroidY); - - QPoint viewPointMouse = this->mapFromGlobal(QCursor::pos()); - QPointF posViewMouse = this->mapToScene(viewPointMouse); - - - - - for (int i = 0; i < nodesJsonArray.size(); ++i) - { - QJsonObject nodeJson = nodesJsonArray[i].toObject(); - QUuid currentId = QUuid( nodeJson["id"].toString() ); - QUuid newId = _scene->pasteNode(nodesJsonArray[i].toObject(), centroid, posViewMouse); - - addedIds.insert(std::pair(currentId, newId)); - nodesJsonArray[i] = _scene->nodes()[newId]->save(); - } - jsonDocument["nodes"] = nodesJsonArray; - - QJsonArray connectionJsonArray = jsonDocument["connections"].toArray(); - for (int i = 0; i < connectionJsonArray.size(); ++i) - { - QJsonObject connectionJson = connectionJsonArray[i].toObject(); - - QUuid in = QUuid(connectionJson["in_id"].toString()); - QUuid newIn = addedIds[in]; - - QUuid out = QUuid(connectionJson["out_id"].toString()); - QUuid newOut = addedIds[out]; - - _scene->pasteConnection(connectionJson, newIn, newOut ); - - connectionJson["in_id"] = newIn.toString(); - connectionJson["out_id"] = newOut.toString(); - connectionJsonArray[i] = connectionJson; - } - jsonDocument["connections"] =connectionJsonArray; - - QJsonArray groupsJsonArray = jsonDocument["groups"].toArray(); - for (int i = 0; i < groupsJsonArray.size(); ++i) - { - _scene->pasteGroup(groupsJsonArray[i].toObject(), centroid, posViewMouse); - } - - - _scene->AddAction(UndoRedoAction( - [this, addedIds](void *ptr) - { - //Delete all the created nodes (and their connections) - for(auto &id: addedIds) - { - _scene->removeNodeWithID(id.second); - } - return 0; - }, - [this, jsonDocument](void *ptr) - { - jsonToScene(jsonDocument); //jsonDocument has now been updated with new IDs and new positions - return 0; - }, - "Created Node " - )); - -} - -QJsonObject FlowView::selectionToJson(bool includePartialConnections) -{ - QJsonObject sceneJson; - QJsonArray nodesJsonArray; - QJsonArray connectionJsonArray; - std::set addedNodeIds; - std::set addedConnectionIds; - - for (QGraphicsItem * item : _scene->selectedItems()) - { - if (auto n = qgraphicsitem_cast(item)) - { - Node& node = n->node(); - nodesJsonArray.append(node.save()); - addedNodeIds.insert(node.id()); - - if(includePartialConnections) //find all connections of that node, even if the other node is not selected - { - std::vector allConnections = node.nodeState().allConnections(); - for(int i=0; iid()) != addedConnectionIds.end()) - continue; //Already added this connection - - QJsonObject connectionJson = allConnections[i]->save(); - if (!connectionJson.isEmpty()) - { - connectionJsonArray.append(connectionJson); - addedConnectionIds.insert(allConnections[i]->id()); - } - } - } - } - } - - for (QGraphicsItem * item : _scene->selectedItems()) - { - if (auto c = qgraphicsitem_cast(item)) { - Connection& connection = c->connection(); - - if(addedConnectionIds.find(connection.id()) == addedConnectionIds.end()) { - QJsonObject connectionJson = connection.save(); - if (!connectionJson.isEmpty()) - connectionJsonArray.append(connectionJson); - addedConnectionIds.insert(connection.id()); - } - } - } - - QJsonArray groupJsonArray; - for (QGraphicsItem * item : _scene->selectedItems()) - { - if (auto c = qgraphicsitem_cast(item)) { - Group& group = c->group(); - - //If collapsed - if(group.groupGraphicsObject().isCollapsed()) - { - //Add all child items to nodes array - for(int i=0; i(group.groupGraphicsObject().childItems()[i]); - if(ngo!=nullptr) - { - Node& node = ngo->node(); - nodesJsonArray.append(node.save()); - } - } - - //Add all connections to nodes array - for(int i=0; isave(); - if (!connectionJson.isEmpty()) - connectionJsonArray.append(connectionJson); - } - } - } - QJsonObject groupJson = group.save(); - if (!groupJson.isEmpty()) - groupJsonArray.append(groupJson); - } - } - sceneJson["nodes"] = nodesJsonArray; - sceneJson["connections"] = connectionJsonArray; - sceneJson["groups"] = groupJsonArray; - return sceneJson; -} void FlowView::pasteSelectedNodes() { QClipboard *p_Clipboard = QApplication::clipboard(); @@ -756,7 +496,7 @@ void FlowView::pasteSelectedNodes() { void FlowView::duplicateSelectedNode() { - QJsonObject selectionJson = selectionToJson(); + QJsonObject selectionJson = _scene->selectionToJson(); jsonToSceneMousePos(selectionJson); } diff --git a/src/Node.cpp b/src/Node.cpp index 04e6b6ab..5bd395e1 100644 --- a/src/Node.cpp +++ b/src/Node.cpp @@ -44,6 +44,13 @@ Node(std::unique_ptr && dataModel) } +void +Node:: +updateEntries() +{ + _nodeState.updateEntries(); +} + void Node:: setInputSelected(int inx, bool selected) @@ -92,6 +99,8 @@ void Node:: updateView() { + nodeGraphicsObject().setGeometryChanged(); + nodeGeometry().recalculateSize(); nodeGeometry().recalculateInOut(); nodeState().updateEntries(); diff --git a/src/NodeGraphicsObject.cpp b/src/NodeGraphicsObject.cpp index 91703704..68ab07ac 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -335,7 +335,16 @@ mouseMoveEvent(QGraphicsSceneMouseEvent * event) oldSize += QSize(diff.x(), diff.y()); - w->setFixedSize(oldSize); + int minHeight = w->minimumHeight(); + + if(oldSize.height() < w->minimumHeight()) + oldSize.setHeight(w->minimumHeight()); + + if(oldSize.width() < w->minimumWidth()) + oldSize.setWidth(w->minimumWidth()); + + + w->setMaximumSize(oldSize); _proxyWidget->setMinimumSize(oldSize); _proxyWidget->setMaximumSize(oldSize);