Skip to content

Commit 8eedf85

Browse files
committed
Implement undo redo commands
1 parent 97a9b62 commit 8eedf85

16 files changed

+506
-156
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ set(CPP_SOURCE_FILES
9999
src/NodeState.cpp
100100
src/NodeStyle.cpp
101101
src/StyleCollection.cpp
102+
src/UndoCommands.cpp
102103
src/locateNode.cpp
103104
)
104105

examples/graph/CustomGraphModel.cpp

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,9 @@ nodeData(NodeId nodeId, NodeRole role) const
203203
}
204204
break;
205205

206+
case NodeRole::InternalData:
207+
break;
208+
206209
case NodeRole::NumberOfInPorts:
207210
result = 1u;
208211
break;
@@ -259,6 +262,9 @@ setNodeData(NodeId nodeId,
259262
case NodeRole::Style:
260263
break;
261264

265+
case NodeRole::InternalData:
266+
break;
267+
262268
case NodeRole::NumberOfInPorts:
263269
break;
264270

@@ -347,9 +353,8 @@ deleteConnection(ConnectionId const connectionId)
347353

348354
PortType opposite = oppositePort(portType);
349355

350-
auto oppositePair =
351-
std::make_pair(getNodeId(opposite, connectionId),
352-
getPortIndex(opposite, connectionId));
356+
auto oppositePair = std::make_pair(getNodeId(opposite, connectionId),
357+
getPortIndex(opposite, connectionId));
353358
it->second.erase(oppositePair);
354359

355360
if (it->second.empty())
@@ -398,13 +403,14 @@ saveNode(NodeId const nodeId) const
398403

399404
nodeJson["id"] = static_cast<qint64>(nodeId);
400405

401-
QPointF const pos =
402-
nodeData(nodeId, NodeRole::Position).value<QPointF>();
406+
{
407+
QPointF const pos = nodeData(nodeId, NodeRole::Position).value<QPointF>();
403408

404-
QJsonObject posJson;
405-
posJson["x"] = pos.x();
406-
posJson["y"] = pos.y();
407-
nodeJson["position"] = posJson;
409+
QJsonObject posJson;
410+
posJson["x"] = pos.x();
411+
posJson["y"] = pos.y();
412+
nodeJson["position"] = posJson;
413+
}
408414

409415
return nodeJson;
410416
}
@@ -414,19 +420,25 @@ void
414420
CustomGraphModel::
415421
loadNode(QJsonObject const & nodeJson)
416422
{
417-
NodeId nodeId = static_cast<NodeId>(nodeJson["id"].toInt());
423+
NodeId restoredNodeId = static_cast<NodeId>(nodeJson["id"].toInt());
418424

419-
_nextNodeId = std::max(_nextNodeId, nodeId);
425+
// Next NodeId must be larger that any id existing in the graph
426+
_nextNodeId = std::max(restoredNodeId + 1, _nextNodeId);
420427

421-
NodeId newId = addNode();
428+
// Create new node.
429+
_nodeIds.insert(restoredNodeId);
422430

423-
QJsonObject posJson = nodeJson["position"].toObject();
424-
QPointF const pos(posJson["x"].toInt(),
425-
posJson["y"].toInt());
431+
Q_EMIT nodeCreated(restoredNodeId);
426432

427-
setNodeData(newId,
428-
NodeRole::Position,
429-
pos);
433+
{
434+
QJsonObject posJson = nodeJson["position"].toObject();
435+
QPointF const pos(posJson["x"].toDouble(),
436+
posJson["y"].toDouble());
437+
438+
setNodeData(restoredNodeId,
439+
NodeRole::Position,
440+
pos);
441+
}
430442
}
431443

432444

@@ -449,10 +461,10 @@ void
449461
CustomGraphModel::
450462
loadConnection(QJsonObject const & connJson)
451463
{
452-
ConnectionId connId{connJson["outNodeId"].toInt(),
453-
connJson["outPortIndex"].toInt(),
454-
connJson["intNodeId"].toInt(),
455-
connJson["inPortIndex"].toInt()};
464+
ConnectionId connId{static_cast<NodeId>(connJson["outNodeId"].toInt()),
465+
static_cast<PortIndex>(connJson["outPortIndex"].toInt()),
466+
static_cast<NodeId>(connJson["intNodeId"].toInt()),
467+
static_cast<PortIndex>(connJson["inPortIndex"].toInt())};
456468

457469
addConnection(connId);
458470
}

examples/graph/CustomGraphModel.hpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,17 @@ class CustomGraphModel : public QtNodes::AbstractGraphModel
2525
{
2626
Q_OBJECT
2727
public:
28-
2928
struct NodeGeometryData
3029
{
3130
QSize size;
3231
QPointF pos;
3332
};
3433

35-
3634
public:
37-
3835
CustomGraphModel();
3936

4037
~CustomGraphModel() override;
4138

42-
4339
std::unordered_set<NodeId>
4440
allNodeIds() const override;
4541

@@ -109,7 +105,6 @@ class CustomGraphModel : public QtNodes::AbstractGraphModel
109105
loadConnection(QJsonObject const & connJson) override;
110106

111107
private:
112-
113108
std::unordered_set<NodeId> _nodeIds;
114109

115110
std::unordered_map<std::tuple<NodeId, PortType, PortIndex>,
@@ -122,5 +117,4 @@ class CustomGraphModel : public QtNodes::AbstractGraphModel
122117

123118

124119
unsigned int _nextNodeId;
125-
126120
};

include/QtNodes/internal/AbstractGraphModel.hpp

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include <QtCore/QObject>
99
#include <QtCore/QVariant>
10+
#include <QtCore/QJsonObject>
1011

1112
#include "Definitions.hpp"
1213
#include "ConnectionIdHash.hpp"
@@ -166,15 +167,33 @@ class NODE_EDITOR_PUBLIC AbstractGraphModel : public QObject
166167
*/
167168
virtual
168169
QJsonObject
169-
saveNode(NodeId const) const = 0;
170+
saveNode(NodeId const) const { return {}; }
170171

171172
/**
172-
* Reimplement the function if you want to store/restore the node's
173-
* inner state during undo/redo node deletion operations.
173+
* Reimplement the function if you want to support:
174+
*
175+
* - graph save/restore operations,
176+
* - undo/redo operations after deleting the node.
177+
*
178+
* QJsonObject must contain following fields:
179+
*
180+
*
181+
* ```
182+
* {
183+
* id : 5,
184+
* position : { x : 100, y : 200 },
185+
* internal-data {
186+
* "your model specific data here"
187+
* }
188+
* }
189+
* ```
190+
*
191+
* The function must do almost exacly the same thing as the normal addNode().
192+
* The main difference is in a model-specific `inner-data` processing.
174193
*/
175194
virtual
176195
void
177-
loadNode(QJsonObject const & nodeJson) = 0;
196+
loadNode(QJsonObject const &) {}
178197

179198
virtual
180199
QJsonObject

include/QtNodes/internal/BasicGraphicsScene.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616

1717
#include "QUuidStdHash.hpp"
1818

19+
20+
class QUndoStack;
21+
1922
namespace QtNodes
2023
{
2124

@@ -47,6 +50,8 @@ class NODE_EDITOR_PUBLIC BasicGraphicsScene : public QGraphicsScene
4750
AbstractGraphModel &
4851
graphModel();
4952

53+
QUndoStack& undoStack();
54+
5055
public:
5156

5257
/// Creates a "draft" instance of ConnectionGraphicsObject.
@@ -207,6 +212,8 @@ private Q_SLOTS:
207212

208213

209214
std::unique_ptr<ConnectionGraphicsObject> _draftConnection;
215+
216+
QUndoStack* _undoStack;
210217
};
211218

212219

include/QtNodes/internal/DataFlowGraphModel.hpp

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -88,17 +88,17 @@ class NODE_EDITOR_PUBLIC DataFlowGraphModel : public AbstractGraphModel
8888
bool
8989
deleteNode(NodeId const nodeId) override;
9090

91+
QJsonObject
92+
saveNode(NodeId const) const override;
93+
9194
QJsonDocument
9295
save() const;
9396

9497
void
95-
load(QJsonDocument const &json);
96-
97-
QJsonObject
98-
saveNode(NodeId const) const override;
98+
loadNode(QJsonObject const & nodeJson) override;
9999

100100
void
101-
loadNode(QJsonObject const & nodeJson) override;
101+
load(QJsonDocument const &json);
102102

103103
QJsonObject
104104
saveConnection(ConnectionId const & connId) const override;
@@ -107,19 +107,19 @@ class NODE_EDITOR_PUBLIC DataFlowGraphModel : public AbstractGraphModel
107107
loadConnection(QJsonObject const & connJson) override;
108108

109109
private:
110-
111110
NodeId
112111
newNodeId() { return _nextNodeId++; }
113112

114113
/**
115114
* The function could be used when we restore nodes from some file
116-
* and the NodeId values are already known.
115+
* and the NodeId values are already known. In this case we must
116+
* update internal counter for unique "next" node id in order not to
117+
* repeat the values when incrementing.
117118
*/
118-
NodeId
119-
newNodeId(NodeId const restoredNodeId)
119+
void
120+
setNextNodeId(NodeId const restoredNodeId)
120121
{
121-
_nextNodeId = std::max(_nextNodeId, restoredNodeId);
122-
return restoredNodeId;
122+
_nextNodeId = std::max(_nextNodeId, restoredNodeId + 1);
123123
}
124124

125125
private Q_SLOTS:

include/QtNodes/internal/DataFlowGraphicsScene.hpp

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,6 @@ public Q_SLOTS:
4747
PortIndex const portIndex);
4848

4949

50-
//std::shared_ptr<Connection> restoreConnection(QJsonObject const & connectionJson);
51-
52-
//Node & restoreNode(QJsonObject const & nodeJson)
53-
54-
55-
//void save() const;
56-
57-
//void load();
58-
5950
QJsonDocument saveToJsonDocument() const;
6051

6152
void loadFromJsonDocument(QJsonDocument const& json);

include/QtNodes/internal/Definitions.hpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,15 @@ Q_NAMESPACE_EXPORT(NODE_EDITOR_PUBLIC)
2121
enum class NodeRole
2222
{
2323
Type = 0, ///< Type of the current node, usually a string.
24-
Position = 1, ///< `QPointF` positon of the nod on the scene.
24+
Position = 1, ///< `QPointF` positon of the node on the scene.
2525
Size = 2, ///< `QSize` for resizable nodes.
2626
CaptionVisible = 3, ///< `bool` for caption visibility.
2727
Caption = 4, ///< `QString` for node caption.
28-
Style = 5, ///< Custom NodeStyle.
29-
NumberOfInPorts = 6, ///< `unsigned int`
30-
NumberOfOutPorts = 7, ///< `unsigned int`
31-
Widget = 8, ///< Optional `QWidget*` or `nullptr`
28+
Style = 5, ///< Custom NodeStyle as QJsonDocument
29+
InternalData = 6, ///< Node-stecific user data as QJsonObject
30+
NumberOfInPorts = 7, ///< `unsigned int`
31+
NumberOfOutPorts = 9, ///< `unsigned int`
32+
Widget = 10, ///< Optional `QWidget*` or `nullptr`
3233
};
3334
Q_ENUM_NS(NodeRole)
3435

src/BasicGraphicsScene.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
#include <unordered_set>
77
#include <utility>
88

9+
#include <QtGui/QUndoStack>
10+
911
#include <QtWidgets/QGraphicsSceneMoveEvent>
1012
#include <QtWidgets/QFileDialog>
1113

@@ -18,8 +20,6 @@
1820
#include <QtCore/QJsonObject>
1921
#include <QtCore/QtGlobal>
2022

21-
#include <QtCore/QDebug>
22-
2323
#include "ConnectionGraphicsObject.hpp"
2424
#include "ConnectionIdUtils.hpp"
2525
#include "GraphicsView.hpp"
@@ -34,6 +34,7 @@ BasicGraphicsScene(AbstractGraphModel &graphModel,
3434
QObject * parent)
3535
: QGraphicsScene(parent)
3636
, _graphModel(graphModel)
37+
, _undoStack(new QUndoStack(this))
3738
{
3839
setItemIndexMethod(QGraphicsScene::NoIndex);
3940

@@ -88,6 +89,14 @@ graphModel()
8889
}
8990

9091

92+
QUndoStack &
93+
BasicGraphicsScene::
94+
undoStack()
95+
{
96+
return *_undoStack;
97+
}
98+
99+
91100
std::unique_ptr<ConnectionGraphicsObject> const &
92101
BasicGraphicsScene::
93102
makeDraftConnection(ConnectionId const incompleteConnectionId)

0 commit comments

Comments
 (0)