-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathGraphEditor.h
More file actions
206 lines (163 loc) · 6.68 KB
/
GraphEditor.h
File metadata and controls
206 lines (163 loc) · 6.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
#pragma once
#include "GraphViewer.h"
namespace GraphEditor {
/* Listener interface. */
class Listener {
public:
virtual ~Listener() = default;
virtual void needsRepaint();
virtual void isDirty();
virtual void entitySelected(Entity* entity);
virtual void entityHovered(Entity* entity);
virtual void entityCreated(Entity* entity);
};
/* Base type for all editors. You should not use this type directly; it's for
* internal purposes only.
*/
class EditorBase {
public:
virtual ~EditorBase() = default;
/* Handles these mouse events to support dragging, adding new lines,
* etc. Forward these messages to have the editor handle dragging,
* creation of new items, etc.
*/
void mouseDoubleClicked(double x, double y);
void mouseMoved(double x, double y);
void mousePressed(double x, double y);
void mouseDragged(double x, double y);
void mouseReleased(double x, double y);
/* Callback registration. */
void addListener(std::shared_ptr<Listener> listener);
/* Draws the underlying viewer. Use this rather than manually
* asking the viewer to draw so that we can properly highlight
* everything.
*
* You may specify node and edge styles here if you would like.
* If you do, your specified node and edge styles will override
* any styling done for selected/hovered nodes and edges.
*/
void draw(GCanvas* canvas,
const std::unordered_map<Node*, NodeStyle>& nodeStyles = {},
const std::unordered_map<Edge*, EdgeStyle>& edgeStyles = {});
/* Sets which entity is active, if any. */
void setActive(GraphEditor::Entity* active);
private:
EditorBase(std::shared_ptr<ViewerBase>);
std::shared_ptr<ViewerBase> mViewer;
std::vector<std::shared_ptr<Listener>> mListeners;
/* Active/hovered items. */
Node* activeNode = nullptr;
Node* hoverNode = nullptr;
Edge* activeEdge = nullptr;
Edge* hoverEdge = nullptr;
/* For dragging things. */
GPoint lastState;
/* For dragging edges. */
GPoint dragEdge0, dragEdge1;
Node* edgeStart = nullptr;
/* What kind of drag are we doing? */
enum class DragType {
NONE,
NODE,
EDGE
};
DragType dragType = DragType::NONE;
void drawGraph(GCanvas* canvas,
const std::unordered_map<Node*, NodeStyle>& nodeStyles,
const std::unordered_map<Edge*, EdgeStyle>& edgeStyles);
void drawDraggedEdge(GCanvas* canvas);
/* Handle a drag in one of two ways. */
void dragState(GPoint pt);
void dragTransition(GPoint pt);
/* Handle the mouse release when adding a new transition. */
void finishCreatingEdge(GPoint pt);
/* Changes the hover node/edge. */
void setHover(GraphEditor::Entity* hover);
void setActiveNode(GraphEditor::Node* state);
void setActiveEdge(GraphEditor::Edge* transition);
void setHoverNode(GraphEditor::Node* state);
void setHoverEdge(GraphEditor::Edge* transition);
Node* selectedNode();
Edge* selectedEdge();
void dirty();
void requestRepaint();
/* Firewalled off from the base. We promise the types will work. */
virtual Node* newNode(const GPoint& pt) = 0;
virtual Edge* newEdge(Node* from, Node* to) = 0;
virtual Edge* edgeBetween(Node* from, Node* to) = 0;
template <typename Viewer> friend class Editor;
};
/* Interface to a Viewer that's specifically designed for interactively editing
* the underlying graph structure. It's parameterized on the type of the
* underlying viewer
*/
template <typename Viewer = GraphEditor::Viewer<>> class Editor: public EditorBase {
public:
using NodeType = typename Viewer::NodeType;
using EdgeType = typename Viewer::EdgeType;
/* Builds an editor that hooks into the specified Viewer. */
Editor(std::shared_ptr<Viewer> viewer);
/* Which node or edge, if any, is selected? */
NodeType* selectedNode();
EdgeType* selectedEdge();
/* Deletes the given node/edge. */
void deleteNode(NodeType* node);
void deleteEdge(EdgeType* edge);
/* Retrieve the underlying editor. */
std::shared_ptr<Viewer> viewer();
protected:
virtual Node* newNode(const GPoint& pt) override;
virtual Edge* newEdge(Node* from, Node* to) override;
virtual Edge* edgeBetween(Node* from, Node* to) override;
private:
std::shared_ptr<Viewer> mViewer;
};
template <typename Viewer>
Editor<Viewer>::Editor(std::shared_ptr<Viewer> viewer) : EditorBase(viewer), mViewer(viewer) {
// Handled above
}
template <typename Viewer>
std::shared_ptr<Viewer> Editor<Viewer>::viewer() {
return mViewer;
}
template <typename Viewer>
void Editor<Viewer>::deleteNode(NodeType* node) {
/* Remove the state. */
mViewer->removeNode(node);
/* If this was the active node, deselect it. */
if (node == activeNode) setActive(nullptr);
if (node == hoverNode) setHover(nullptr);
/* Deselect the active transition; it may no longer be valid! */
if (activeEdge) setActive(nullptr);
if (hoverEdge) setHover(nullptr);
dirty();
}
template <typename Viewer>
void Editor<Viewer>::deleteEdge(EdgeType* edge) {
/* Remove from the list of transitions. */
mViewer->removeEdge(edge);
if (activeEdge == edge) setActive(nullptr);
if (hoverEdge == edge) setHover(nullptr);
dirty();
}
template <typename Viewer>
Node* Editor<Viewer>::newNode(const GPoint& pt) {
return mViewer->newNode(pt);
}
template <typename Viewer>
Edge* Editor<Viewer>::newEdge(Node* src, Node* dst) {
return mViewer->newEdge(static_cast<NodeType*>(src), static_cast<NodeType*>(dst));
}
template <typename Viewer>
Edge* Editor<Viewer>::edgeBetween(Node* src, Node* dst) {
return mViewer->edgeBetween(static_cast<NodeType*>(src), static_cast<NodeType*>(dst));
}
template <typename Viewer>
typename Editor<Viewer>::NodeType* Editor<Viewer>::selectedNode() {
return static_cast<NodeType*>(EditorBase::selectedNode());
}
template <typename Viewer>
typename Editor<Viewer>::EdgeType* Editor<Viewer>::selectedEdge() {
return static_cast<EdgeType*>(EditorBase::selectedEdge());
}
}