1 | // Copyright (C) 2022 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
3 | |
4 | #include "nodeview.h" |
5 | #include "effectmanager.h" |
6 | |
7 | NodeView::NodeView(QQuickItem *parent) |
8 | : QQuickItem(parent) |
9 | { |
10 | setAcceptedMouseButtons(Qt::AllButtons); |
11 | setAcceptHoverEvents(true); |
12 | m_nodesModel = new NodesModel(); |
13 | m_arrowsModel = new ArrowsModel(); |
14 | |
15 | // Create main and output nodes |
16 | // These always exist |
17 | NodesModel::Node n1{.type: 0, .nodeId: 0, .x: 220, .y: 50, .width: 80, .height: 80, .name: "Main" }; |
18 | NodesModel::Node n2{.type: 1, .nodeId: 1, .x: 220, .y: 400, .width: 80, .height: 80, .name: "Output" }; |
19 | initializeNodeSize(node&: n1); |
20 | initializeNodeSize(node&: n2); |
21 | n1.nextNodeId = 1; |
22 | m_nodesModel->m_nodesList << n1; |
23 | m_nodesModel->m_nodesList << n2; |
24 | |
25 | // Connect source & output by default |
26 | ArrowsModel::Arrow a1{.startX: 0, .startY: 0, .endX: 0, .endY: 0, .startNodeId: 0, .endNodeId: 1}; |
27 | m_arrowsModel->m_arrowsList << a1; |
28 | |
29 | updateArrowsPositions(); |
30 | m_activeArrow.startNodeId = -1; |
31 | m_activeArrow.endNodeId = -1; |
32 | |
33 | connect(sender: m_nodesModel, signal: &QAbstractItemModel::modelReset, slot: [this]() { |
34 | updateCodeSelectorModel(); |
35 | }); |
36 | } |
37 | |
38 | void NodeView::mouseMoveEvent(QMouseEvent *event) |
39 | { |
40 | // Nodes not moved with right button |
41 | if (event->buttons() & Qt::RightButton) |
42 | return; |
43 | |
44 | m_nodesModel->beginResetModel(); |
45 | QPointF movement = event->position() - m_pressPosition; |
46 | for (auto &node : m_nodesModel->m_nodesList) { |
47 | if (node.selected) { |
48 | float newX = node.startX + movement.x(); |
49 | float newY = node.startY + movement.y(); |
50 | // Don't allow dragging outside the visible nodeview area. |
51 | newX = std::max(a: newX, b: float(-node.width / 2.0f)); |
52 | newX = std::min(a: newX, b: float(width() - node.width / 2.0f)); |
53 | newY = std::max(a: newY, b: float(-node.height / 2.0f)); |
54 | newY = std::min(a: newY, b: float(height() - node.height / 2.0f)); |
55 | node.x = newX; |
56 | node.y = newY; |
57 | } |
58 | } |
59 | m_nodesModel->endResetModel(); |
60 | |
61 | updateArrowsPositions(); |
62 | } |
63 | |
64 | void NodeView::mousePressEvent(QMouseEvent *event) |
65 | { |
66 | m_mousePressed = true; |
67 | |
68 | setFocus(true); |
69 | |
70 | QPointF p = event->position(); |
71 | m_pressPosition = p; |
72 | bool shiftPressed = (event->modifiers() & Qt::ShiftModifier); |
73 | |
74 | m_nodesModel->beginResetModel(); |
75 | m_arrowsModel->beginResetModel(); |
76 | |
77 | // Reset the nodes starting positions |
78 | for (auto &node : m_nodesModel->m_nodesList) { |
79 | if (node.selected) { |
80 | node.startX = node.x; |
81 | node.startY = node.y; |
82 | } |
83 | } |
84 | |
85 | bool connectorPressed = false; |
86 | if (!m_activeArrowEnabled) { |
87 | m_activeArrow.startNodeId = -1; |
88 | m_activeArrow.endNodeId = -1; |
89 | } |
90 | // Check node connectors first |
91 | for (auto &node : m_nodesModel->m_nodesList) { |
92 | float size = 20; |
93 | // Start is bottom side and end top side of the node. |
94 | QPointF startConnectorCenter = { node.x + node.width / 2, node.y + node.height}; |
95 | QPointF endConnectorCenter = { node.x + node.width / 2, node.y }; |
96 | QRectF startConnector(startConnectorCenter.x() - size / 2, startConnectorCenter.y() - size / 2, size, size); |
97 | QRectF endConnector(endConnectorCenter.x() - size / 2, endConnectorCenter.y() - size / 2, size, size); |
98 | if (startConnector.contains(p)) { |
99 | m_activeArrowStartPoint = startConnectorCenter; |
100 | emit activeArrowStartPointChanged(); |
101 | m_activeArrowEndPoint = startConnectorCenter; |
102 | emit activeArrowEndPointChanged(); |
103 | connectorPressed = true; |
104 | m_activeArrow.startNodeId = node.nodeId; |
105 | // Remove possible already existing arrow |
106 | for (auto &arrow : m_arrowsModel->m_arrowsList) { |
107 | if (arrow.startNodeId == node.nodeId) { |
108 | // Update nextNode |
109 | node.nextNodeId = -1; |
110 | m_arrowsModel->m_arrowsList.removeAll(t: arrow); |
111 | } |
112 | } |
113 | } else if (endConnector.contains(p)) { |
114 | m_activeArrowStartPoint = endConnectorCenter; |
115 | emit activeArrowStartPointChanged(); |
116 | m_activeArrowEndPoint = endConnectorCenter; |
117 | emit activeArrowEndPointChanged(); |
118 | connectorPressed = true; |
119 | m_activeArrow.endNodeId = node.nodeId; |
120 | // Remove possible already existing arrow |
121 | for (auto &arrow : m_arrowsModel->m_arrowsList) { |
122 | if (arrow.endNodeId == node.nodeId) { |
123 | // Update nextNode |
124 | if (auto n = m_nodesModel->getNodeWithId(id: arrow.startNodeId)) |
125 | n->nextNodeId = -1; |
126 | m_arrowsModel->m_arrowsList.removeAll(t: arrow); |
127 | } |
128 | } |
129 | } |
130 | // Don't allow connecting same node out & in together |
131 | if (m_activeArrow.startNodeId != -1 && m_activeArrow.startNodeId == m_activeArrow.endNodeId) { |
132 | m_activeArrow.startNodeId = -1; |
133 | m_activeArrow.endNodeId = -1; |
134 | } |
135 | } |
136 | |
137 | // True when pressing a node that was already selected |
138 | bool selectedNodeUnderMouse = false; |
139 | NodesModel::Node newlySelectedNode; |
140 | if (!connectorPressed) { |
141 | for (auto &node : m_nodesModel->m_nodesList) { |
142 | node.startX = node.x; |
143 | node.startY = node.y; |
144 | QRectF nodeArea(node.x, node.y, node.width, node.height); |
145 | if (nodeArea.contains(p)) { |
146 | if (node.selected) |
147 | selectedNodeUnderMouse = true; |
148 | if (shiftPressed) |
149 | node.selected = !node.selected; |
150 | else |
151 | node.selected = true; |
152 | if (node.selected) { |
153 | newlySelectedNode = node; |
154 | break; |
155 | } |
156 | } |
157 | } |
158 | } |
159 | |
160 | // Deselected previous nodes |
161 | if (!shiftPressed && !selectedNodeUnderMouse) { |
162 | for (auto &node : m_nodesModel->m_nodesList) { |
163 | if (newlySelectedNode != node) |
164 | node.selected = false; |
165 | } |
166 | } |
167 | |
168 | // Check if both arrow ends are connected |
169 | if (m_activeArrow.startNodeId >= 0 && m_activeArrow.endNodeId >= 0) { |
170 | m_arrowsModel->m_arrowsList.append(t: m_activeArrow); |
171 | connectorPressed = false; |
172 | updateArrowsPositions(); |
173 | // Update nextNode |
174 | auto n1 = m_nodesModel->getNodeWithId(id: m_activeArrow.startNodeId); |
175 | if (n1) |
176 | n1->nextNodeId = m_activeArrow.endNodeId; |
177 | } |
178 | |
179 | if (m_activeArrowEnabled != connectorPressed) { |
180 | m_activeArrowEnabled = connectorPressed; |
181 | emit activeArrowEnabledChanged(); |
182 | } |
183 | |
184 | // Check first selected node |
185 | NodesModel::Node *selectedNode = nullptr; |
186 | for (auto &node : m_nodesModel->m_nodesList) { |
187 | if (node.selected) { |
188 | selectedNode = &node; |
189 | break; |
190 | } |
191 | } |
192 | setSelectedNode(selectedNode); |
193 | |
194 | m_nodesModel->endResetModel(); |
195 | m_arrowsModel->endResetModel(); |
196 | |
197 | // Right button opens the context menu |
198 | if (m_effectNodeSelected && event->button() == Qt::RightButton) |
199 | Q_EMIT openNodeContextMenu(); |
200 | |
201 | // Check if active (connected) nodes have changed |
202 | updateActiveNodesList(); |
203 | } |
204 | |
205 | void NodeView::mouseReleaseEvent(QMouseEvent *event) |
206 | { |
207 | Q_UNUSED(event) |
208 | m_mousePressed = false; |
209 | } |
210 | |
211 | void NodeView::mouseDoubleClickEvent(QMouseEvent *event) |
212 | { |
213 | Q_UNUSED(event) |
214 | // When double clicking custom nodes or main node |
215 | if (m_selectedNodeId != -1 && m_selectedNodeId != 1) |
216 | Q_EMIT doubleClickNode(); |
217 | } |
218 | |
219 | void NodeView::hoverMoveEvent(QHoverEvent *event) |
220 | { |
221 | // If start/end is connected, other side follows mouse |
222 | if (m_activeArrow.startNodeId >= 0) { |
223 | m_activeArrowEndPoint = event->position(); |
224 | emit activeArrowEndPointChanged(); |
225 | } else if (m_activeArrow.endNodeId >= 0) { |
226 | m_activeArrowStartPoint = event->position(); |
227 | emit activeArrowStartPointChanged(); |
228 | } |
229 | |
230 | // Pass event forward, to e.g. keep SplitView mouse cursor |
231 | // changes functioning correctly. |
232 | event->setAccepted(false); |
233 | } |
234 | |
235 | void NodeView::keyPressEvent(QKeyEvent *event) |
236 | { |
237 | Q_UNUSED(event) |
238 | } |
239 | |
240 | void NodeView::keyReleaseEvent(QKeyEvent *event) |
241 | { |
242 | Q_UNUSED(event) |
243 | } |
244 | |
245 | // Selects the node with \a nodeId and deselects other nodes. |
246 | void NodeView::selectSingleNode(int nodeId) |
247 | { |
248 | m_nodesModel->beginResetModel(); |
249 | // Check first selected node |
250 | NodesModel::Node *selectedNode = nullptr; |
251 | for (auto &node : m_nodesModel->m_nodesList) { |
252 | if (node.nodeId == nodeId) { |
253 | node.selected = true; |
254 | selectedNode = &node; |
255 | } else { |
256 | node.selected = false; |
257 | } |
258 | } |
259 | setSelectedNode(selectedNode); |
260 | m_nodesModel->endResetModel(); |
261 | } |
262 | |
263 | // Selects the Main node and deselects other nodes. |
264 | void NodeView::selectMainNode() |
265 | { |
266 | m_nodesModel->beginResetModel(); |
267 | NodesModel::Node *selectedNode = nullptr; |
268 | for (auto &node : m_nodesModel->m_nodesList) { |
269 | if (node.type == 0) { |
270 | node.selected = true; |
271 | selectedNode = &node; |
272 | } else { |
273 | node.selected = false; |
274 | } |
275 | } |
276 | setSelectedNode(selectedNode); |
277 | m_nodesModel->endResetModel(); |
278 | } |
279 | |
280 | void NodeView::setSelectedNode(NodesModel::Node *selectedNode) |
281 | { |
282 | if (m_nodesModel->m_selectedNode == selectedNode) |
283 | return; |
284 | |
285 | m_nodesModel->setSelectedNode(selectedNode); |
286 | bool effectNodeSelected = (selectedNode != nullptr && selectedNode->type == 2); |
287 | if (effectNodeSelected != m_effectNodeSelected) { |
288 | m_effectNodeSelected = effectNodeSelected; |
289 | Q_EMIT effectNodeSelectedChanged(); |
290 | } |
291 | |
292 | bool mainNodeSelected = (selectedNode != nullptr && selectedNode->type == 0); |
293 | if (mainNodeSelected != m_mainNodeSelected) { |
294 | m_mainNodeSelected = mainNodeSelected; |
295 | Q_EMIT mainNodeSelectedChanged(); |
296 | } |
297 | |
298 | m_selectedNodeId = selectedNode ? selectedNode->nodeId : -1; |
299 | Q_EMIT selectedNodeIdChanged(); |
300 | |
301 | updateCodeSelectorIndex(); |
302 | |
303 | Q_EMIT selectedNodeFragmentCodeChanged(); |
304 | Q_EMIT selectedNodeVertexCodeChanged(); |
305 | Q_EMIT selectedNodeQmlCodeChanged(); |
306 | Q_EMIT selectedNodeNameChanged(); |
307 | Q_EMIT selectedNodeDescriptionChanged(); |
308 | } |
309 | |
310 | NodesModel *NodeView::nodesModel() const |
311 | { |
312 | return m_nodesModel; |
313 | } |
314 | |
315 | ArrowsModel *NodeView::arrowsModel() const |
316 | { |
317 | return m_arrowsModel; |
318 | } |
319 | |
320 | QPointF NodeView::activeArrowStartPoint() const |
321 | { |
322 | return m_activeArrowStartPoint; |
323 | } |
324 | |
325 | QPointF NodeView::activeArrowEndPoint() const |
326 | { |
327 | return m_activeArrowEndPoint; |
328 | } |
329 | |
330 | bool NodeView::activeArrowEnabled() const |
331 | { |
332 | return m_activeArrowEnabled; |
333 | } |
334 | |
335 | void NodeView::updateArrowsPositions() |
336 | { |
337 | m_arrowsModel->beginResetModel(); |
338 | for (auto &arrow : m_arrowsModel->m_arrowsList) { |
339 | auto node = m_nodesModel->getNodeWithId(id: arrow.startNodeId); |
340 | auto node2 = m_nodesModel->getNodeWithId(id: arrow.endNodeId); |
341 | if (node && node2) { |
342 | arrow.startX = node->x + node->width / 2; |
343 | arrow.startY = node->y + node->height; |
344 | arrow.endX = node2->x + node2->width / 2; |
345 | arrow.endY = node2->y; |
346 | } |
347 | } |
348 | m_arrowsModel->endResetModel(); |
349 | } |
350 | |
351 | void NodeView::updateActiveNodesList() |
352 | { |
353 | QList<NodesModel::Node *> nodes; |
354 | QList<int> nodesIds; |
355 | auto node = m_nodesModel->getNodeWithId(id: 0); |
356 | if (!node) |
357 | return; |
358 | nodes << node; |
359 | int nodeId = node ? node->nextNodeId : -1; |
360 | while (nodeId > 0) { |
361 | auto n = m_nodesModel->getNodeWithId(id: nodeId); |
362 | if (n) { |
363 | nodes << n; |
364 | if (!n->disabled) |
365 | nodesIds << n->nodeId; |
366 | nodeId = n->nextNodeId; |
367 | } else { |
368 | break; |
369 | } |
370 | } |
371 | m_activeNodesIds = nodesIds; |
372 | |
373 | // Update info of graph being complete (source -> output) |
374 | bool nodeGraphComplete = false; |
375 | if (!nodes.isEmpty() && nodes.last()->type == 1) |
376 | nodeGraphComplete = true; |
377 | |
378 | if (nodeGraphComplete != m_nodeGraphComplete) { |
379 | m_nodeGraphComplete = nodeGraphComplete; |
380 | Q_EMIT nodeGraphCompleteChanged(); |
381 | } |
382 | |
383 | if (m_activeNodesList != nodes) { |
384 | m_activeNodesList = nodes; |
385 | // Don't emit until fully initialized to avoid shader errors |
386 | if (m_initialized) |
387 | Q_EMIT activeNodesListChanged(); |
388 | } |
389 | } |
390 | |
391 | void NodeView::deleteSelectedNodes() |
392 | { |
393 | QList<int> nodes; |
394 | for (auto &node : m_nodesModel->m_nodesList) { |
395 | if (node.selected && node.type == 2) |
396 | nodes << node.nodeId; |
397 | } |
398 | m_effectManager->deleteEffectNodes(nodeIds: nodes); |
399 | } |
400 | |
401 | QString NodeView::selectedNodeName() const { |
402 | if (auto selectedNode = m_nodesModel->m_selectedNode) |
403 | return selectedNode->name; |
404 | return QString(); |
405 | } |
406 | |
407 | void NodeView::setSelectedNodeName(const QString &name) { |
408 | if (auto selectedNode = m_nodesModel->m_selectedNode) { |
409 | if (selectedNode->name == name) |
410 | return; |
411 | |
412 | // Make sure the name is unique |
413 | QString newName = getUniqueNodeName(origName: name); |
414 | m_nodesModel->beginResetModel(); |
415 | selectedNode->name = newName; |
416 | m_nodesModel->endResetModel(); |
417 | Q_EMIT selectedNodeNameChanged(); |
418 | } |
419 | } |
420 | |
421 | QString NodeView::selectedNodeDescription() const { |
422 | if (auto selectedNode = m_nodesModel->m_selectedNode) |
423 | return selectedNode->description; |
424 | return QString(); |
425 | } |
426 | |
427 | void NodeView::setSelectedNodeDescription(const QString &description) { |
428 | if (auto selectedNode = m_nodesModel->m_selectedNode) { |
429 | if (selectedNode->description == description) |
430 | return; |
431 | |
432 | m_nodesModel->beginResetModel(); |
433 | selectedNode->description = description; |
434 | m_nodesModel->endResetModel(); |
435 | Q_EMIT selectedNodeDescriptionChanged(); |
436 | } |
437 | } |
438 | |
439 | QString NodeView::selectedNodeFragmentCode() const |
440 | { |
441 | if (auto selectedNode = m_nodesModel->m_selectedNode) |
442 | return selectedNode->fragmentCode; |
443 | return QString(); |
444 | } |
445 | |
446 | void NodeView::setSelectedNodeFragmentCode(const QString &newSelectedNodeFragmentCode) |
447 | { |
448 | if (auto selectedNode = m_nodesModel->m_selectedNode) { |
449 | if (selectedNode->fragmentCode == newSelectedNodeFragmentCode) |
450 | return; |
451 | selectedNode->fragmentCode = newSelectedNodeFragmentCode; |
452 | Q_EMIT selectedNodeFragmentCodeChanged(); |
453 | } |
454 | } |
455 | |
456 | QString NodeView::selectedNodeVertexCode() const |
457 | { |
458 | if (auto selectedNode = m_nodesModel->m_selectedNode) |
459 | return selectedNode->vertexCode; |
460 | return QString(); |
461 | } |
462 | |
463 | void NodeView::setSelectedNodeVertexCode(const QString &newSelectedNodeVertexCode) |
464 | { |
465 | if (auto selectedNode = m_nodesModel->m_selectedNode) { |
466 | if (selectedNode->vertexCode == newSelectedNodeVertexCode) |
467 | return; |
468 | selectedNode->vertexCode = newSelectedNodeVertexCode; |
469 | Q_EMIT selectedNodeVertexCodeChanged(); |
470 | } |
471 | } |
472 | |
473 | QString NodeView::selectedNodeQmlCode() const |
474 | { |
475 | if (auto selectedNode = m_nodesModel->m_selectedNode) |
476 | return selectedNode->qmlCode; |
477 | return QString(); |
478 | } |
479 | |
480 | void NodeView::setSelectedNodeQmlCode(const QString &newSelectedNodeQmlCode) |
481 | { |
482 | if (auto selectedNode = m_nodesModel->m_selectedNode) { |
483 | if (selectedNode->qmlCode == newSelectedNodeQmlCode) |
484 | return; |
485 | selectedNode->qmlCode = newSelectedNodeQmlCode; |
486 | Q_EMIT selectedNodeQmlCodeChanged(); |
487 | } |
488 | } |
489 | |
490 | bool NodeView::nodeGraphComplete() const |
491 | { |
492 | return m_nodeGraphComplete; |
493 | } |
494 | |
495 | bool NodeView::effectNodeSelected() const |
496 | { |
497 | return m_effectNodeSelected; |
498 | } |
499 | |
500 | bool NodeView::mainNodeSelected() const |
501 | { |
502 | return m_mainNodeSelected; |
503 | } |
504 | |
505 | int NodeView::selectedNodeId() const |
506 | { |
507 | return m_selectedNodeId; |
508 | } |
509 | |
510 | // Layout the nodes currently in active list |
511 | void NodeView::layoutNodes(bool distribute) |
512 | { |
513 | if (m_activeNodesList.size() < 2) |
514 | return; |
515 | |
516 | // Make sure that also non-connected nodes |
517 | // are inside the visible nodeview area. |
518 | if (!distribute) { |
519 | for (auto &node : m_nodesModel->m_nodesList) { |
520 | float newX = node.x; |
521 | float newY = node.y; |
522 | newX = std::max(a: newX, b: float(-node.width / 2.0f)); |
523 | newX = std::min(a: newX, b: float(width() - node.width / 2.0f)); |
524 | newY = std::max(a: newY, b: float(-node.height / 2.0f)); |
525 | newY = std::min(a: newY, b: float(height() - node.height / 2.0f)); |
526 | node.x = newX; |
527 | node.y = newY; |
528 | } |
529 | } |
530 | |
531 | const int nodeCount = m_activeNodesList.size(); |
532 | const float areaHeight = height(); |
533 | const float sideMargin = areaHeight * 0.05f; |
534 | const float marginY = areaHeight * 0.02f; |
535 | const float areaWCenter = width() / 2.0f; |
536 | float origFullHeight = m_activeNodesList.last()->y - m_activeNodesList.first()->y; |
537 | origFullHeight = std::max(a: origFullHeight, b: 100.0f); |
538 | const float firstY = sideMargin; |
539 | const float lastY = areaHeight - firstY - m_activeNodesList.last()->height; |
540 | const float newFullHeight = lastY - firstY; |
541 | const float yScaling = newFullHeight / origFullHeight; |
542 | int i = 0; |
543 | m_nodesModel->beginResetModel(); |
544 | for (const auto &n : m_activeNodesList) { |
545 | if (!distribute) |
546 | n->x = areaWCenter - n->width / 2.0f; |
547 | |
548 | if (i == 0) { |
549 | n->y = firstY; |
550 | } else if (i == nodeCount - 1) { |
551 | n->y = lastY; |
552 | } else { |
553 | if (distribute) { |
554 | n->y = sideMargin + (float(i) / (nodeCount - 1)) * (areaHeight - sideMargin * 2.0f - n->height); |
555 | } else { |
556 | n->y = yScaling * n->y; |
557 | // Make sure that middle nodes Y are between some limits |
558 | if (auto previousNode = m_activeNodesList.at(i: i - 1)) |
559 | n->y = std::max(a: n->y, b: previousNode->y + previousNode->height + marginY); |
560 | n->y = std::max(a: n->y, b: firstY + marginY + m_activeNodesList.first()->height); |
561 | n->y = std::min(a: n->y, b: lastY - marginY - n->height); |
562 | } |
563 | } |
564 | i++; |
565 | } |
566 | |
567 | m_nodesModel->endResetModel(); |
568 | updateArrowsPositions(); |
569 | } |
570 | |
571 | QStringList NodeView::codeSelectorModel() const |
572 | { |
573 | return m_codeSelectorModel; |
574 | } |
575 | |
576 | int NodeView::codeSelectorIndex() const |
577 | { |
578 | return m_codeSelectorIndex; |
579 | } |
580 | |
581 | int NodeView::getNodeIdWithName(const QString &name) |
582 | { |
583 | for (const auto &node : m_nodesModel->m_nodesList) { |
584 | if (node.name == name) |
585 | return node.nodeId; |
586 | } |
587 | return -1; |
588 | } |
589 | |
590 | // Update the code selector (combobox) model |
591 | void NodeView::updateCodeSelectorModel() |
592 | { |
593 | QStringList codeSelectorModel; |
594 | for (const auto &node : m_nodesModel->m_nodesList) { |
595 | // Hide "Output" node as it currently doesn't contain code |
596 | if (node.type == NodesModel::DestinationNode) |
597 | continue; |
598 | codeSelectorModel << node.name; |
599 | } |
600 | if (codeSelectorModel != m_codeSelectorModel) { |
601 | m_codeSelectorModel = codeSelectorModel; |
602 | Q_EMIT codeSelectorModelChanged(); |
603 | } |
604 | } |
605 | |
606 | // Update the index of code selector (combobox) to match the currectly selected node |
607 | void NodeView::updateCodeSelectorIndex() { |
608 | int index = -1; |
609 | int i = 0; |
610 | for (const auto &node : m_nodesModel->m_nodesList) { |
611 | if (node.nodeId == m_selectedNodeId && node.type != NodesModel::DestinationNode) { |
612 | index = i; |
613 | break; |
614 | } |
615 | if (node.type != NodesModel::DestinationNode) |
616 | i++; |
617 | } |
618 | index = std::min(a: index, b: int(m_codeSelectorModel.size() - 1)); |
619 | if (index != m_codeSelectorIndex) { |
620 | m_codeSelectorIndex = index; |
621 | Q_EMIT codeSelectorIndexChanged(); |
622 | } |
623 | } |
624 | |
625 | void NodeView::toggleNodeDisabled() |
626 | { |
627 | if (auto node = m_nodesModel->m_selectedNode) { |
628 | m_nodesModel->beginResetModel(); |
629 | node->disabled = !node->disabled; |
630 | m_nodesModel->endResetModel(); |
631 | } |
632 | updateActiveNodesList(); |
633 | } |
634 | |
635 | |
636 | // This will return node id, which is not already in use |
637 | int NodeView::getUniqueNodeId() |
638 | { |
639 | if (!m_nodesModel) |
640 | return 0; |
641 | |
642 | int id = 0; |
643 | // Get biggest id + 1 |
644 | for (const auto &node : m_nodesModel->m_nodesList) |
645 | id = std::max(a: id, b: node.nodeId); |
646 | id++; |
647 | |
648 | return id; |
649 | } |
650 | |
651 | void NodeView::initializeNode(NodesModel::Node &node) |
652 | { |
653 | node.type = 2; |
654 | node.x = 20; |
655 | node.y = 60; |
656 | node.nodeId = getUniqueNodeId(); |
657 | initializeNodeSize(node); |
658 | } |
659 | |
660 | void NodeView::initializeNodeSize(NodesModel::Node &node) |
661 | { |
662 | if (node.type == NodesModel::CustomNode) { |
663 | node.width = 150; |
664 | node.height = 50; |
665 | } else { |
666 | node.width = 80; |
667 | node.height = 80; |
668 | } |
669 | } |
670 | |
671 | // Returns unique node name, so name appended with a number. |
672 | // "Custom" -> "Custom2" -> "Custom3" etc. |
673 | QString NodeView::getUniqueNodeName(const QString &origName, int counter) |
674 | { |
675 | if (!m_nodesModel) |
676 | return origName; |
677 | |
678 | // Initially try name as is, then first counter '2'. |
679 | QString counterString = (counter == 0) ? "" : QString::number(counter + 1); |
680 | QString name = origName + counterString; |
681 | counter++; |
682 | // Bail out just in case |
683 | if (counter > 99) |
684 | return name; |
685 | for (const auto &node : m_nodesModel->m_nodesList) { |
686 | if (node.name == name) { |
687 | name = getUniqueNodeName(origName, counter); |
688 | } |
689 | } |
690 | return name; |
691 | } |
692 | |