diff --git a/examples/calculator/LongProcessingRandomNumber.hpp b/examples/calculator/LongProcessingRandomNumber.hpp index d64f4ba7..aa840560 100644 --- a/examples/calculator/LongProcessingRandomNumber.hpp +++ b/examples/calculator/LongProcessingRandomNumber.hpp @@ -16,6 +16,8 @@ class RandomNumberModel : public MathOperationDataModel { public: RandomNumberModel() { + setFrozenMenu(true); + this->setNodeProcessingStatus(QtNodes::NodeProcessingStatus::Empty); @@ -44,6 +46,9 @@ class RandomNumberModel : public MathOperationDataModel private: void compute() override { + if (frozen()) + return; + Q_EMIT computingStarted(); PortIndex const outPortIndex = 0; diff --git a/examples/calculator/MathOperationDataModel.cpp b/examples/calculator/MathOperationDataModel.cpp index f6a3a26d..5cf03e26 100644 --- a/examples/calculator/MathOperationDataModel.cpp +++ b/examples/calculator/MathOperationDataModel.cpp @@ -1,5 +1,4 @@ #include "MathOperationDataModel.hpp" - #include "DecimalData.hpp" unsigned int MathOperationDataModel::nPorts(PortType portType) const @@ -21,7 +20,9 @@ NodeDataType MathOperationDataModel::dataType(PortType, PortIndex) const std::shared_ptr MathOperationDataModel::outData(PortIndex) { - return std::static_pointer_cast(_result); + auto output = std::static_pointer_cast(_result); + + return output; } void MathOperationDataModel::setInData(std::shared_ptr data, PortIndex portIndex) diff --git a/examples/calculator/MathOperationDataModel.hpp b/examples/calculator/MathOperationDataModel.hpp index 6d279642..8d56f263 100644 --- a/examples/calculator/MathOperationDataModel.hpp +++ b/examples/calculator/MathOperationDataModel.hpp @@ -1,7 +1,6 @@ #pragma once #include - #include #include #include diff --git a/include/QtNodes/internal/BasicGraphicsScene.hpp b/include/QtNodes/internal/BasicGraphicsScene.hpp index 568835ff..b16f7747 100644 --- a/include/QtNodes/internal/BasicGraphicsScene.hpp +++ b/include/QtNodes/internal/BasicGraphicsScene.hpp @@ -5,7 +5,6 @@ #include "ConnectionIdHash.hpp" #include "Definitions.hpp" #include "Export.hpp" - #include "QUuidStdHash.hpp" #include @@ -17,7 +16,6 @@ #include #include - class QUndoStack; namespace QtNodes { @@ -29,7 +27,7 @@ class ConnectionGraphicsObject; class NodeGraphicsObject; class NodeStyle; -/// An instance of QGraphicsScene, holds connections and nodes. +/// An instance of QGraphicsScene , holds connections and nodes. class NODE_EDITOR_PUBLIC BasicGraphicsScene : public QGraphicsScene { Q_OBJECT @@ -112,6 +110,10 @@ class NODE_EDITOR_PUBLIC BasicGraphicsScene : public QGraphicsScene */ virtual QMenu *createSceneMenu(QPointF const scenePos); + QMenu *createFreezeMenu(); + + void freezeModelAndConnections(bool isFreeze); + Q_SIGNALS: void modified(BasicGraphicsScene *); void nodeMoved(NodeId const nodeId, QPointF const &newLocation); diff --git a/include/QtNodes/internal/ConnectionState.hpp b/include/QtNodes/internal/ConnectionState.hpp index ddd31cdb..d1373826 100644 --- a/include/QtNodes/internal/ConnectionState.hpp +++ b/include/QtNodes/internal/ConnectionState.hpp @@ -25,6 +25,7 @@ class NODE_EDITOR_PUBLIC ConnectionState ConnectionState(ConnectionGraphicsObject &cgo) : _cgo(cgo) , _hovered(false) + , _frozen(false) {} ConnectionState(ConnectionState const &) = delete; @@ -42,6 +43,9 @@ class NODE_EDITOR_PUBLIC ConnectionState bool hovered() const; void setHovered(bool hovered); + bool frozen() const { return _frozen; } + void setFrozen(bool frozen) { _frozen = frozen; } + public: /// Caches NodeId for further interaction. void setLastHoveredNode(NodeId const nodeId); @@ -56,5 +60,7 @@ class NODE_EDITOR_PUBLIC ConnectionState bool _hovered; NodeId _lastHoveredNode{InvalidNodeId}; + + bool _frozen; }; } // namespace QtNodes diff --git a/include/QtNodes/internal/DataFlowGraphicsScene.hpp b/include/QtNodes/internal/DataFlowGraphicsScene.hpp index e9f89cac..30bcc949 100644 --- a/include/QtNodes/internal/DataFlowGraphicsScene.hpp +++ b/include/QtNodes/internal/DataFlowGraphicsScene.hpp @@ -1,8 +1,10 @@ #pragma once #include "BasicGraphicsScene.hpp" +#include "ConnectionGraphicsObject.hpp" #include "DataFlowGraphModel.hpp" #include "Export.hpp" +#include "NodeConnectionInteraction.hpp" namespace QtNodes { @@ -22,6 +24,7 @@ class NODE_EDITOR_PUBLIC DataFlowGraphicsScene : public BasicGraphicsScene public: std::vector selectedNodes() const; QMenu *createSceneMenu(QPointF const scenePos) override; + void updateConnectionGraphics(const std::unordered_set &connections, bool state); public Q_SLOTS: bool save() const; diff --git a/include/QtNodes/internal/NodeDelegateModel.hpp b/include/QtNodes/internal/NodeDelegateModel.hpp index 04cc78a1..c202423d 100644 --- a/include/QtNodes/internal/NodeDelegateModel.hpp +++ b/include/QtNodes/internal/NodeDelegateModel.hpp @@ -12,6 +12,7 @@ #include "NodeData.hpp" #include "NodeStyle.hpp" #include "Serializable.hpp" +#include namespace QtNodes { @@ -132,6 +133,14 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel virtual bool resizable() const { return false; } + bool frozen() const { return _frozen; } + + void setFrozenState(bool state) { _frozen = state; } + + bool frozenMenu() const { return _frozenMenu; } + + void setFrozenMenu(bool state) { _frozenMenu = state; } + public Q_SLOTS: virtual void inputConnectionCreated(ConnectionId const &) {} virtual void inputConnectionDeleted(ConnectionId const &) {} @@ -185,6 +194,10 @@ public Q_SLOTS: private: NodeStyle _nodeStyle; + bool _frozen{false}; + + bool _frozenMenu{false}; + NodeValidationState _nodeValidationState; NodeProcessingStatus _processingStatus{NodeProcessingStatus::NoStatus}; diff --git a/src/BasicGraphicsScene.cpp b/src/BasicGraphicsScene.cpp index 84bf9a31..fe142ea7 100644 --- a/src/BasicGraphicsScene.cpp +++ b/src/BasicGraphicsScene.cpp @@ -3,14 +3,20 @@ #include "AbstractNodeGeometry.hpp" #include "ConnectionGraphicsObject.hpp" #include "ConnectionIdUtils.hpp" +#include "DataFlowGraphModel.hpp" #include "DefaultConnectionPainter.hpp" #include "DefaultHorizontalNodeGeometry.hpp" #include "DefaultNodePainter.hpp" #include "DefaultVerticalNodeGeometry.hpp" +#include "NodeDelegateModel.hpp" #include "NodeGraphicsObject.hpp" #include +#include +#include +#include +#include #include #include @@ -203,6 +209,68 @@ QMenu *BasicGraphicsScene::createSceneMenu(QPointF const scenePos) return nullptr; } +QMenu *BasicGraphicsScene::createFreezeMenu() +{ + QMenu *menu = new QMenu(); + + auto *txtBox = new QLineEdit(menu); + txtBox->setPlaceholderText(QStringLiteral("Filter")); + txtBox->setClearButtonEnabled(true); + + auto *txtBoxAction = new QWidgetAction(menu); + txtBoxAction->setDefaultWidget(txtBox); + menu->addAction(txtBoxAction); + + QTreeWidget *treeView = new QTreeWidget(menu); + treeView->header()->close(); + + treeView->setMaximumHeight(100); + treeView->setMaximumWidth(150); + + auto *treeViewAction = new QWidgetAction(menu); + treeViewAction->setDefaultWidget(treeView); + menu->addAction(treeViewAction); + + auto freezeItem = new QTreeWidgetItem(treeView); + freezeItem->setText(0, "Freeze"); + + auto unfreezeItem = new QTreeWidgetItem(treeView); + unfreezeItem->setText(0, "Unfreeze"); + + treeView->expandAll(); + + connect(treeView, &QTreeWidget::itemClicked, [this, menu](QTreeWidgetItem *item, int) { + if (item->text(0) == "Freeze") { + freezeModelAndConnections(true); + + menu->close(); + return; + } + if (item->text(0) == "Unfreeze") { + freezeModelAndConnections(false); + + menu->close(); + return; + } + }); + + // Filtro + connect(txtBox, &QLineEdit::textChanged, [treeView](const QString &text) { + QTreeWidgetItemIterator it(treeView); + while (*it) { + auto modelName = (*it)->text(0); + const bool match = (modelName.contains(text, Qt::CaseInsensitive)); + (*it)->setHidden(!match); + ++it; + } + }); + + txtBox->setFocus(); + menu->setAttribute(Qt::WA_DeleteOnClose); + + return menu; +} + void BasicGraphicsScene::traverseGraphAndPopulateGraphicsObjects() { auto allNodeIds = _graphModel.allNodeIds(); @@ -327,4 +395,31 @@ void BasicGraphicsScene::onModelReset() traverseGraphAndPopulateGraphicsObjects(); } +void BasicGraphicsScene::freezeModelAndConnections(bool isFreeze) +{ + for (QGraphicsItem *item : selectedItems()) { + if (auto n = qgraphicsitem_cast(item)) { + int portCount = graphModel().nodeData(n->nodeId(), NodeRole::OutPortCount).toInt(); + for (int i = 0; i < portCount; i++) { + auto graphConnections = graphModel().connections(n->nodeId(), + QtNodes::PortType::Out, + QtNodes::PortIndex(i)); + + for (auto const &c : graphConnections) { + if (auto *cgo = connectionGraphicsObject(c)) { + cgo->connectionState().setFrozen(isFreeze); + cgo->update(); + } + } + } + + if (auto *dfModel = dynamic_cast(&graphModel())) { + if (auto *delegate = dfModel->delegateModel(n->nodeId())) { + delegate->setFrozenState(isFreeze); + } + } + } + } +} + } // namespace QtNodes diff --git a/src/DataFlowGraphModel.cpp b/src/DataFlowGraphModel.cpp index fa5655cd..275d7639 100644 --- a/src/DataFlowGraphModel.cpp +++ b/src/DataFlowGraphModel.cpp @@ -444,6 +444,9 @@ bool DataFlowGraphModel::setPortData( switch (role) { case PortRole::Data: if (portType == PortType::In) { + if (model->frozen()) + return false; + model->setInData(value.value>(), portIndex); // Triggers repainting on the scene. diff --git a/src/DataFlowGraphicsScene.cpp b/src/DataFlowGraphicsScene.cpp index 32970608..b232164a 100644 --- a/src/DataFlowGraphicsScene.cpp +++ b/src/DataFlowGraphicsScene.cpp @@ -26,7 +26,6 @@ #include #include - namespace QtNodes { DataFlowGraphicsScene::DataFlowGraphicsScene(DataFlowGraphModel &graphModel, QObject *parent) @@ -191,4 +190,15 @@ bool DataFlowGraphicsScene::load() return true; } +void DataFlowGraphicsScene::updateConnectionGraphics( + const std::unordered_set &connections, bool state) +{ + for (auto const &c : connections) { + if (auto *cgo = connectionGraphicsObject(c)) { + cgo->connectionState().setFrozen(state); + cgo->update(); + } + } +} + } // namespace QtNodes diff --git a/src/DefaultConnectionPainter.cpp b/src/DefaultConnectionPainter.cpp index a7079ebd..d9c11333 100644 --- a/src/DefaultConnectionPainter.cpp +++ b/src/DefaultConnectionPainter.cpp @@ -9,7 +9,6 @@ #include - namespace QtNodes { QPainterPath DefaultConnectionPainter::cubicPath(ConnectionGraphicsObject const &connection) const @@ -27,11 +26,12 @@ QPainterPath DefaultConnectionPainter::cubicPath(ConnectionGraphicsObject const return cubic; } -void DefaultConnectionPainter::drawSketchLine(QPainter *painter, ConnectionGraphicsObject const &cgo) const +void DefaultConnectionPainter::drawSketchLine(QPainter *painter, + ConnectionGraphicsObject const &cgo) const { ConnectionState const &state = cgo.connectionState(); - if (state.requiresPort()) { + if (state.requiresPort() || state.frozen()) { auto const &connectionStyle = QtNodes::StyleCollection::connectionStyle(); QPen pen; @@ -49,7 +49,8 @@ void DefaultConnectionPainter::drawSketchLine(QPainter *painter, ConnectionGraph } } -void DefaultConnectionPainter::drawHoveredOrSelected(QPainter *painter, ConnectionGraphicsObject const &cgo) const +void DefaultConnectionPainter::drawHoveredOrSelected(QPainter *painter, + ConnectionGraphicsObject const &cgo) const { bool const hovered = cgo.connectionState().hovered(); bool const selected = cgo.isSelected(); @@ -74,11 +75,12 @@ void DefaultConnectionPainter::drawHoveredOrSelected(QPainter *painter, Connecti } } -void DefaultConnectionPainter::drawNormalLine(QPainter *painter, ConnectionGraphicsObject const &cgo) const +void DefaultConnectionPainter::drawNormalLine(QPainter *painter, + ConnectionGraphicsObject const &cgo) const { ConnectionState const &state = cgo.connectionState(); - if (state.requiresPort()) + if (state.requiresPort() || state.frozen()) return; // colors @@ -132,6 +134,7 @@ void DefaultConnectionPainter::drawNormalLine(QPainter *painter, ConnectionGraph painter->setBrush(Qt::NoBrush); QColor cOut = normalColorOut; + if (selected) cOut = cOut.darker(200); p.setColor(cOut); @@ -200,7 +203,8 @@ void DefaultConnectionPainter::paint(QPainter *painter, ConnectionGraphicsObject painter->drawEllipse(cgo.in(), pointRadius, pointRadius); } -QPainterPath DefaultConnectionPainter::getPainterStroke(ConnectionGraphicsObject const &connection) const +QPainterPath DefaultConnectionPainter::getPainterStroke( + ConnectionGraphicsObject const &connection) const { auto cubic = cubicPath(connection); diff --git a/src/GraphicsView.cpp b/src/GraphicsView.cpp index c587f081..42de101e 100644 --- a/src/GraphicsView.cpp +++ b/src/GraphicsView.cpp @@ -2,6 +2,8 @@ #include "BasicGraphicsScene.hpp" #include "ConnectionGraphicsObject.hpp" +#include "DataFlowGraphModel.hpp" +#include "NodeDelegateModel.hpp" #include "NodeGraphicsObject.hpp" #include "StyleCollection.hpp" #include "UndoCommands.hpp" @@ -23,7 +25,10 @@ #include using QtNodes::BasicGraphicsScene; +using QtNodes::DataFlowGraphModel; using QtNodes::GraphicsView; +using QtNodes::NodeDelegateModel; +using QtNodes::NodeGraphicsObject; GraphicsView::GraphicsView(QWidget *parent) : QGraphicsView(parent) @@ -75,8 +80,7 @@ QAction *GraphicsView::deleteSelectionAction() const void GraphicsView::setScene(BasicGraphicsScene *scene) { QGraphicsView::setScene(scene); - if (!scene) - { + if (!scene) { // Clear actions. delete _clearSelectionAction; delete _deleteSelectionAction; @@ -181,20 +185,30 @@ void GraphicsView::centerScene() void GraphicsView::contextMenuEvent(QContextMenuEvent *event) { - if (itemAt(event->pos())) { - QGraphicsView::contextMenuEvent(event); - return; - } + QGraphicsView::contextMenuEvent(event); + QMenu *menu = nullptr; - if (!nodeScene()) return; + bool isFrozenMenu; - auto const scenePos = mapToScene(event->pos()); + if (auto *dfModel = dynamic_cast(&nodeScene()->graphModel())) { + if (auto n = qgraphicsitem_cast(itemAt(event->pos()))) { + if (auto *delegate = dfModel->delegateModel(n->nodeId())) { + isFrozenMenu = delegate->frozenMenu(); + } + } + } - QMenu *menu = nodeScene()->createSceneMenu(scenePos); + if (itemAt(event->pos()) && isFrozenMenu) { + menu = nodeScene()->createFreezeMenu(); + } else if (!itemAt(event->pos())) { + menu = nodeScene()->createSceneMenu(mapToScene(event->pos())); + } if (menu) { menu->exec(event->globalPos()); } + + return; } void GraphicsView::wheelEvent(QWheelEvent *event) @@ -291,14 +305,16 @@ void GraphicsView::setupScale(double scale) void GraphicsView::onDeleteSelectedObjects() { - if (!nodeScene()) return; + if (!nodeScene()) + return; nodeScene()->undoStack().push(new DeleteCommand(nodeScene())); } void GraphicsView::onDuplicateSelectedObjects() { - if (!nodeScene()) return; + if (!nodeScene()) + return; QPointF const pastePosition = scenePastePosition(); @@ -308,14 +324,16 @@ void GraphicsView::onDuplicateSelectedObjects() void GraphicsView::onCopySelectedObjects() { - if (!nodeScene()) return; + if (!nodeScene()) + return; nodeScene()->undoStack().push(new CopyCommand(nodeScene())); } void GraphicsView::onPasteObjects() { - if (!nodeScene()) return; + if (!nodeScene()) + return; QPointF const pastePosition = scenePastePosition(); nodeScene()->undoStack().push(new PasteCommand(nodeScene(), pastePosition)); @@ -360,7 +378,8 @@ void GraphicsView::mouseMoveEvent(QMouseEvent *event) { QGraphicsView::mouseMoveEvent(event); - if (!scene()) return; + if (!scene()) + return; if (scene()->mouseGrabberItem() == nullptr && event->buttons() == Qt::LeftButton) { // Make sure shift is not being pressed