diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 78178808..de6a4474 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -7,3 +7,5 @@ add_subdirectory(calculator) add_subdirectory(images) add_subdirectory(styles) + +add_subdirectory(state_machine) diff --git a/examples/calculator/AdditionModel.hpp b/examples/calculator/AdditionModel.hpp index 672c468b..e2a6dbab 100644 --- a/examples/calculator/AdditionModel.hpp +++ b/examples/calculator/AdditionModel.hpp @@ -55,3 +55,130 @@ class AdditionModel : public MathOperationDataModel Q_EMIT dataUpdated(outPortIndex); } }; + +class MultiAdditionModel : public NodeDataModel +{ +public: + QString + caption() const override + { return QStringLiteral("Multi Addition"); } + + QString + name() const override + { return QStringLiteral("Multi Addition"); } + + bool + portCaptionVisible(PortType portType, PortIndex portIndex) const override + { + Q_UNUSED(portType); Q_UNUSED(portIndex); + return true; + } + + QString + portCaption(PortType portType, PortIndex portIndex) const override + { + Q_UNUSED(portIndex); + + if(portType == PortType::In && _input.size() > 0) + { + return QString("Decimals (%1)").arg(_input.size()); + } + + return QStringLiteral("Decimal"); + } + + unsigned int + nPorts(PortType portType) const override + { + Q_UNUSED(portType); + return 1; + } + + NodeDataType + dataType(PortType portType, + PortIndex portIndex) const override + { + Q_UNUSED(portType); Q_UNUSED(portIndex); + return DecimalData().type(); + } + + std::shared_ptr + outData(PortIndex portIndex) override + { + Q_UNUSED(portIndex); + return _result; + } + + void + setInData(std::shared_ptr data, PortIndex portIndex) override + { Q_UNUSED(data); Q_UNUSED(portIndex); } + + void + setInData(std::vector > data, PortIndex portIndex) override + { + Q_UNUSED(portIndex); + + _input.clear(); + _input.reserve(data.size()); + for (auto& node : data) + { + std::shared_ptr decimalData = std::dynamic_pointer_cast(node); + _input.push_back(decimalData); + } + + if (_input.empty()) + { + modelValidationState = NodeValidationState::Warning; + } + else + { + modelValidationState = NodeValidationState::Valid; + } + + double result = 0.0f; + int counter = 0; + for (auto& node : _input) + { + if(std::shared_ptr locked = node.lock()) + { + result += locked->number(); + ++counter; + } + } + + if(counter > 0) + { + _result = std::make_shared(result); + } + else + { + _result.reset(); + } + + Q_EMIT dataUpdated(0); + } + + QWidget * + embeddedWidget() override + { return nullptr; } + + NodeValidationState + validationState() const override + { return modelValidationState; } + + QString + validationMessage() const override + { return modelValidationError; } + + ConnectionPolicy + portInConnectionPolicy(PortIndex) const override + { return ConnectionPolicy::Many; } + +private: + std::vector > _input; + + std::shared_ptr _result; + + NodeValidationState modelValidationState = NodeValidationState::Warning; + QString modelValidationError = QString("Missing or incorrect inputs"); +}; diff --git a/examples/calculator/main.cpp b/examples/calculator/main.cpp index 06d0481a..5b4a757a 100644 --- a/examples/calculator/main.cpp +++ b/examples/calculator/main.cpp @@ -36,6 +36,7 @@ registerDataModels() ret->registerModel("I/O//Displays"); ret->registerModel("Operators"); + ret->registerModel("Operators"); ret->registerModel("Operators"); @@ -94,8 +95,8 @@ main(int argc, char *argv[]) QWidget mainWidget; auto menuBar = new QMenuBar(); - auto saveAction = menuBar->addAction("Save.."); - auto loadAction = menuBar->addAction("Load.."); + auto saveAction = menuBar->addAction("Save"); + auto loadAction = menuBar->addAction("Load"); QVBoxLayout *l = new QVBoxLayout(&mainWidget); diff --git a/examples/state_machine/CMakeLists.txt b/examples/state_machine/CMakeLists.txt new file mode 100644 index 00000000..0b65e42a --- /dev/null +++ b/examples/state_machine/CMakeLists.txt @@ -0,0 +1,5 @@ +file(GLOB_RECURSE CPPS ./*.cpp ) + +add_executable(state_machine ${CPPS}) + +target_link_libraries(state_machine nodes) diff --git a/examples/state_machine/StateNodeModel.cpp b/examples/state_machine/StateNodeModel.cpp new file mode 100644 index 00000000..b1ed6f3b --- /dev/null +++ b/examples/state_machine/StateNodeModel.cpp @@ -0,0 +1,86 @@ +#include "StateNodeModel.hpp" + +QString +StateNodeModel:: +caption() const +{ + return QStringLiteral("State"); +} + + +QString +StateNodeModel:: +name() const +{ + return QStringLiteral("state_node"); +} + + +unsigned int +StateNodeModel:: +nPorts(PortType portType) const +{ + std::ignore = portType; + return 1; +} + + +NodeDataType +StateNodeModel:: +dataType(PortType portType, PortIndex portIndex) const +{ + std::ignore = portType; + std::ignore = portIndex; + return getTranstitionType(); +} + + +NodeDataType +StateNodeModel:: +getTranstitionType() +{ + return NodeDataType{"transition_port", ""}; +} + + +QtNodes::NodeDataModel::ConnectionPolicy +StateNodeModel:: +portOutConnectionPolicy(PortIndex) const +{ + return ConnectionPolicy::Many; +} + + +QtNodes::NodeDataModel::ConnectionPolicy +StateNodeModel:: +portInConnectionPolicy(PortIndex) const +{ + return ConnectionPolicy::Many; +} + + +void +StateNodeModel:: +setInData(std::shared_ptr nodeData, PortIndex port) +{ + std::ignore = nodeData; + std::ignore = port; +} + + +void +StateNodeModel:: +setInData(std::vector > nodeData, PortIndex port) +{ + std::ignore = nodeData; + std::ignore = port; +} + + +std::shared_ptr +StateNodeModel:: +outData(PortIndex port) +{ + std::ignore = port; + return nullptr; +} diff --git a/examples/state_machine/StateNodeModel.hpp b/examples/state_machine/StateNodeModel.hpp new file mode 100644 index 00000000..dc3a7acf --- /dev/null +++ b/examples/state_machine/StateNodeModel.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include + +using QtNodes::NodeData; +using QtNodes::PortIndex; +using QtNodes::PortType; +using QtNodes::NodeDataType; + +class StateNodeModel : public QtNodes::NodeDataModel +{ +public: + static NodeDataType getTranstitionType(); + + QString caption() const override; + QString name() const override; + + unsigned int nPorts(PortType portType) const override; + NodeDataType dataType(PortType portType, PortIndex portIndex) const override; + ConnectionPolicy portOutConnectionPolicy(PortIndex) const override; + ConnectionPolicy portInConnectionPolicy(PortIndex) const override; + + void setInData(std::shared_ptr nodeData, PortIndex port) override; + void setInData(std::vector > nodeData, PortIndex port) override; + std::shared_ptr outData(PortIndex port) override; + + QWidget* embeddedWidget() override { return nullptr; } +}; diff --git a/examples/state_machine/main.cpp b/examples/state_machine/main.cpp new file mode 100644 index 00000000..27fc09ac --- /dev/null +++ b/examples/state_machine/main.cpp @@ -0,0 +1,84 @@ +#include "StateNodeModel.hpp" + +#include +#include +#include + +#include +#include +#include + +using QtNodes::FlowScene; +using QtNodes::FlowView; +using QtNodes::DataModelRegistry; +using QtNodes::ConnectionStyle; + +std::shared_ptr +registerDataModels() +{ + std::shared_ptr registry(new DataModelRegistry()); + + registry->registerModel(); + + return registry; +} + + +static +void +setStyle() +{ + ConnectionStyle::setConnectionStyle( + R"( + { + "ConnectionStyle": { + "ConstructionColor": "gray", + "NormalColor": "black", + "SelectedColor": "gray", + "SelectedHaloColor": "deepskyblue", + "HoveredColor": "deepskyblue", + + "LineWidth": 3.0, + "ConstructionLineWidth": 2.0, + "PointDiameter": 10.0, + + "UseDataDefinedColors": true + } + } + )"); +} + + +int +main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + setStyle(); + + QWidget mainWidget; + + auto menuBar = new QMenuBar(); + auto saveAction = menuBar->addAction("Save.."); + auto loadAction = menuBar->addAction("Load.."); + + QVBoxLayout *l = new QVBoxLayout(&mainWidget); + + l->addWidget(menuBar); + auto scene = new FlowScene(registerDataModels(), &mainWidget); + l->addWidget(new FlowView(scene)); + l->setContentsMargins(0, 0, 0, 0); + l->setSpacing(0); + + QObject::connect(saveAction, &QAction::triggered, + scene, &FlowScene::save); + + QObject::connect(loadAction, &QAction::triggered, + scene, &FlowScene::load); + + mainWidget.setWindowTitle("Simplest state editor"); + mainWidget.resize(800, 600); + mainWidget.showNormal(); + + return app.exec(); +} diff --git a/include/nodes/internal/Connection.hpp b/include/nodes/internal/Connection.hpp index 5f79b38e..efee84cf 100644 --- a/include/nodes/internal/Connection.hpp +++ b/include/nodes/internal/Connection.hpp @@ -115,6 +115,8 @@ class NODE_EDITOR_PUBLIC Connection NodeDataType dataType(PortType portType) const; + TypeConverter getTypeConverter() const; + void setTypeConverter(TypeConverter converter); @@ -124,7 +126,7 @@ class NODE_EDITOR_PUBLIC Connection public: // data propagation void - propagateData(std::shared_ptr nodeData) const; + propagateData() const; void propagateEmptyData() const; diff --git a/include/nodes/internal/ConnectionGraphicsObject.hpp b/include/nodes/internal/ConnectionGraphicsObject.hpp index 7db498e4..db9ffe2b 100644 --- a/include/nodes/internal/ConnectionGraphicsObject.hpp +++ b/include/nodes/internal/ConnectionGraphicsObject.hpp @@ -3,6 +3,7 @@ #include #include +#include "Export.hpp" class QGraphicsSceneMouseEvent; @@ -15,7 +16,7 @@ class ConnectionGeometry; class Node; /// Graphic Object for connection. Adds itself to scene -class ConnectionGraphicsObject +class NODE_EDITOR_PUBLIC ConnectionGraphicsObject : public QGraphicsObject { Q_OBJECT diff --git a/include/nodes/internal/FlowScene.hpp b/include/nodes/internal/FlowScene.hpp index dc763d3f..063729d5 100644 --- a/include/nodes/internal/FlowScene.hpp +++ b/include/nodes/internal/FlowScene.hpp @@ -149,6 +149,7 @@ class NODE_EDITOR_PUBLIC FlowScene void nodeContextMenu(Node& n, const QPointF& pos); private: + using SharedConnection = std::shared_ptr; using UniqueNode = std::unique_ptr; diff --git a/include/nodes/internal/FlowView.hpp b/include/nodes/internal/FlowView.hpp index 54728148..b8778847 100644 --- a/include/nodes/internal/FlowView.hpp +++ b/include/nodes/internal/FlowView.hpp @@ -19,43 +19,59 @@ class NODE_EDITOR_PUBLIC FlowView FlowView(FlowScene *scene, QWidget *parent = Q_NULLPTR); FlowView(const FlowView&) = delete; - FlowView operator=(const FlowView&) = delete; + FlowView + operator=(const FlowView&) = delete; - QAction* clearSelectionAction() const; + QAction* + clearSelectionAction() const; - QAction* deleteSelectionAction() const; + QAction* + deleteSelectionAction() const; - void setScene(FlowScene *scene); + void + setScene(FlowScene *scene); public Q_SLOTS: - void scaleUp(); + virtual void + scaleUp(); - void scaleDown(); + virtual void + scaleDown(); - void deleteSelectedNodes(); + virtual void + deleteSelectedNodes(); protected: - void contextMenuEvent(QContextMenuEvent *event) override; + void + contextMenuEvent(QContextMenuEvent *event) override; - void wheelEvent(QWheelEvent *event) override; + void + wheelEvent(QWheelEvent *event) override; - void keyPressEvent(QKeyEvent *event) override; + void + keyPressEvent(QKeyEvent *event) override; - void keyReleaseEvent(QKeyEvent *event) override; + void + keyReleaseEvent(QKeyEvent *event) override; - void mousePressEvent(QMouseEvent *event) override; + void + mousePressEvent(QMouseEvent *event) override; - void mouseMoveEvent(QMouseEvent *event) override; + void + mouseMoveEvent(QMouseEvent *event) override; - void drawBackground(QPainter* painter, const QRectF& r) override; + void + drawBackground(QPainter* painter, const QRectF& r) override; - void showEvent(QShowEvent *event) override; + void + showEvent(QShowEvent *event) override; protected: - FlowScene * scene(); + FlowScene * + scene(); /** * MIME type used to copy/paste and drag n drop nodes. diff --git a/include/nodes/internal/Node.hpp b/include/nodes/internal/Node.hpp index cab7d828..5873bace 100644 --- a/include/nodes/internal/Node.hpp +++ b/include/nodes/internal/Node.hpp @@ -89,8 +89,7 @@ public Q_SLOTS: // data propagation /// Propagates incoming data to the underlying model. void - propagateData(std::shared_ptr nodeData, - PortIndex inPortIndex) const; + propagateData(PortIndex inPortIndex) const; /// Fetches data from model's OUT #index port /// and propagates it to the connection diff --git a/include/nodes/internal/NodeDataModel.hpp b/include/nodes/internal/NodeDataModel.hpp index b65b9ecd..95c4fe60 100644 --- a/include/nodes/internal/NodeDataModel.hpp +++ b/include/nodes/internal/NodeDataModel.hpp @@ -1,6 +1,5 @@ #pragma once - #include #include "PortType.hpp" @@ -67,19 +66,25 @@ class NODE_EDITOR_PUBLIC NodeDataModel public: virtual - unsigned int nPorts(PortType portType) const = 0; + unsigned int + nPorts(PortType portType) const = 0; virtual - NodeDataType dataType(PortType portType, PortIndex portIndex) const = 0; + NodeDataType + dataType(PortType portType, PortIndex portIndex) const = 0; public: enum class ConnectionPolicy { One, - Many, + Many }; + + ConnectionPolicy + portConnectionPolicy(PortType portType, PortIndex portIndex) const; + virtual ConnectionPolicy portOutConnectionPolicy(PortIndex) const @@ -87,6 +92,13 @@ class NODE_EDITOR_PUBLIC NodeDataModel return ConnectionPolicy::Many; } + virtual + ConnectionPolicy + portInConnectionPolicy(PortIndex) const + { + return ConnectionPolicy::One; + } + NodeStyle const& nodeStyle() const; @@ -101,6 +113,11 @@ class NODE_EDITOR_PUBLIC NodeDataModel setInData(std::shared_ptr nodeData, PortIndex port) = 0; + virtual + void + setInData(std::vector > nodeData, + PortIndex port); + virtual std::shared_ptr outData(PortIndex port) = 0; @@ -122,29 +139,22 @@ class NODE_EDITOR_PUBLIC NodeDataModel validationMessage() const { return QString(""); } virtual - NodePainterDelegate* painterDelegate() const { return nullptr; } + NodePainterDelegate* + painterDelegate() const { return nullptr; } public Q_SLOTS: virtual void - inputConnectionCreated(Connection const&) - { - } + inputConnectionCreated(Connection const&) {} virtual void - inputConnectionDeleted(Connection const&) - { - } + inputConnectionDeleted(Connection const&) {} virtual void - outputConnectionCreated(Connection const&) - { - } + outputConnectionCreated(Connection const&) {} virtual void - outputConnectionDeleted(Connection const&) - { - } + outputConnectionDeleted(Connection const&) {} Q_SIGNALS: @@ -160,7 +170,8 @@ public Q_SLOTS: void computingFinished(); - void embeddedWidgetSizeUpdated(); + void + embeddedWidgetSizeUpdated(); private: diff --git a/include/nodes/internal/NodeGraphicsObject.hpp b/include/nodes/internal/NodeGraphicsObject.hpp index fe87c6c8..89864633 100644 --- a/include/nodes/internal/NodeGraphicsObject.hpp +++ b/include/nodes/internal/NodeGraphicsObject.hpp @@ -7,6 +7,7 @@ #include "NodeGeometry.hpp" #include "NodeState.hpp" +#include "Export.hpp" class QGraphicsProxyWidget; @@ -18,7 +19,7 @@ class FlowItemEntry; /// Class reacts on GUI events, mouse clicks and /// forwards painting operation. -class NodeGraphicsObject : public QGraphicsObject +class NODE_EDITOR_PUBLIC NodeGraphicsObject : public QGraphicsObject { Q_OBJECT diff --git a/include/nodes/internal/NodeState.hpp b/include/nodes/internal/NodeState.hpp index 966bd46a..911f4bfb 100644 --- a/include/nodes/internal/NodeState.hpp +++ b/include/nodes/internal/NodeState.hpp @@ -70,7 +70,6 @@ class NODE_EDITOR_PUBLIC NodeState void setReaction(ReactToConnectionState reaction, PortType reactingPortType = PortType::None, - NodeDataType reactingDataType = NodeDataType()); diff --git a/src/Connection.cpp b/src/Connection.cpp index 1395d13f..2d92b5c6 100644 --- a/src/Connection.cpp +++ b/src/Connection.cpp @@ -415,6 +415,12 @@ dataType(PortType portType) const Q_UNREACHABLE(); } +TypeConverter +Connection:: +getTypeConverter() const +{ + return _converter; +} void Connection:: @@ -426,16 +432,11 @@ setTypeConverter(TypeConverter converter) void Connection:: -propagateData(std::shared_ptr nodeData) const +propagateData() const { if (_inNode) { - if (_converter) - { - nodeData = _converter(nodeData); - } - - _inNode->propagateData(nodeData, _inPortIndex); + _inNode->propagateData(_inPortIndex); } } @@ -444,7 +445,5 @@ void Connection:: propagateEmptyData() const { - std::shared_ptr emptyData; - - propagateData(emptyData); + propagateData(); } diff --git a/src/ConnectionGraphicsObject.cpp b/src/ConnectionGraphicsObject.cpp index 8fff2645..96a34538 100644 --- a/src/ConnectionGraphicsObject.cpp +++ b/src/ConnectionGraphicsObject.cpp @@ -100,7 +100,7 @@ void ConnectionGraphicsObject:: move() { - for(PortType portType: { PortType::In, PortType::Out } ) + for (PortType portType: { PortType::In, PortType::Out }) { if (auto node = _connection.getNode(portType)) { @@ -124,10 +124,12 @@ move() _connection.getConnectionGraphicsObject().update(); } } - } -void ConnectionGraphicsObject::lock(bool locked) + +void +ConnectionGraphicsObject:: +lock(bool locked) { setFlag(QGraphicsItem::ItemIsMovable, !locked); setFlag(QGraphicsItem::ItemIsFocusable, !locked); @@ -143,8 +145,7 @@ paint(QPainter* painter, { painter->setClipRect(option->exposedRect); - ConnectionPainter::paint(painter, - _connection); + ConnectionPainter::paint(painter, _connection); } @@ -153,7 +154,6 @@ ConnectionGraphicsObject:: mousePressEvent(QGraphicsSceneMouseEvent* event) { QGraphicsItem::mousePressEvent(event); - //event->ignore(); } @@ -168,13 +168,14 @@ mouseMoveEvent(QGraphicsSceneMouseEvent* event) _scene, view->transform()); - auto &state = _connection.connectionState(); + auto & connectionState = _connection.connectionState(); - state.interactWithNode(node); + connectionState.interactWithNode(node); if (node) { - node->reactToPossibleConnection(state.requiredPort(), - _connection.dataType(oppositePort(state.requiredPort())), + auto oppPort = oppositePort(connectionState.requiredPort()); + node->reactToPossibleConnection(connectionState.requiredPort(), + _connection.dataType(oppPort), event->scenePos()); } @@ -184,6 +185,7 @@ mouseMoveEvent(QGraphicsSceneMouseEvent* event) auto requiredPort = _connection.requiredPort(); + // This moves the loose end exactly to the mouse position if (requiredPort != PortType::None) { _connection.connectionGeometry().moveEndPoint(requiredPort, offset); diff --git a/src/FlowScene.cpp b/src/FlowScene.cpp index f90a0daf..93d2394e 100644 --- a/src/FlowScene.cpp +++ b/src/FlowScene.cpp @@ -51,6 +51,7 @@ FlowScene(std::shared_ptr registry, connect(this, &FlowScene::connectionDeleted, this, &FlowScene::sendConnectionDeletedToNodes); } + FlowScene:: FlowScene(QObject * parent) : FlowScene(std::make_shared(), @@ -85,12 +86,8 @@ createConnection(PortType connectedPort, // Note: this connection isn't truly created yet. It's only partially created. // Thus, don't send the connectionCreated(...) signal. - connect(connection.get(), - &Connection::connectionCompleted, - this, - [this](Connection const& c) { - connectionCreated(c); - }); + connect(connection.get(), &Connection::connectionCompleted, + this, [this](Connection const& c) { connectionCreated(c); }); return connection; } @@ -157,7 +154,7 @@ restoreConnection(QJsonObject const &connectionJson) NodeDataType outType { converterJson["out"].toObject()["id"].toString(), converterJson["out"].toObject()["name"].toString() }; - auto converter = + auto converter = registry().getTypeConverter(outType, inType); if (converter) @@ -184,7 +181,9 @@ FlowScene:: deleteConnection(Connection& connection) { auto it = _connections.find(connection.id()); - if (it != _connections.end()) { + + if (it != _connections.end()) + { connection.removeFromNodes(); _connections.erase(it); } @@ -242,7 +241,7 @@ removeNode(Node& node) // call signal nodeDeleted(node); - for(auto portType: {PortType::In,PortType::Out}) + for (auto portType: {PortType::In, PortType::Out}) { auto nodeState = node.nodeState(); auto const & nodeEntries = nodeState.getEntries(portType); diff --git a/src/Node.cpp b/src/Node.cpp index 8ad34c34..d62fa9f9 100644 --- a/src/Node.cpp +++ b/src/Node.cpp @@ -95,9 +95,9 @@ reactToPossibleConnection(PortType reactingPortType, { QTransform const t = _nodeGraphicsObject->sceneTransform(); - QPointF p = t.inverted().map(scenePoint); + QPointF nodePoint = t.inverted().map(scenePoint); - _nodeGeometry.setDraggingPosition(p); + _nodeGeometry.setDraggingPosition(nodePoint); _nodeGraphicsObject->update(); @@ -184,9 +184,29 @@ nodeDataModel() const void Node:: -propagateData(std::shared_ptr nodeData, - PortIndex inPortIndex) const +propagateData(PortIndex inPortIndex) const { + NodeState const& state = nodeState(); + NodeState::ConnectionPtrSet connections = state.connections(PortType::In, inPortIndex); + + std::vector> nodeData; + nodeData.reserve(connections.size()); + for (const auto& connection : connections) + { + if(Connection* c = connection.second) + { + Node* outNode = c->getNode(PortType::Out); + PortIndex outNodeIndex = c->getPortIndex(PortType::Out); + std::shared_ptr outData = outNode->nodeDataModel()->outData(outNodeIndex); + TypeConverter converter = c->getTypeConverter(); + if (converter != nullptr) + { + outData = converter(outData); + } + nodeData.push_back(outData); + } + } + _nodeDataModel->setInData(std::move(nodeData), inPortIndex); //Recalculate the nodes visuals. A data change can result in the node taking more space than before, so this forces a recalculate+repaint on the affected node @@ -201,33 +221,30 @@ void Node:: onDataUpdated(PortIndex index) { - auto nodeData = _nodeDataModel->outData(index); - - auto connections = - _nodeState.connections(PortType::Out, index); - + auto connections = _nodeState.connections(PortType::Out, index); for (auto const & c : connections) - c.second->propagateData(nodeData); + c.second->propagateData(); } void Node:: onNodeSizeUpdated() { - if( nodeDataModel()->embeddedWidget() ) - { - nodeDataModel()->embeddedWidget()->adjustSize(); - } - nodeGeometry().recalculateSize(); - for(PortType type: {PortType::In, PortType::Out}) + if (nodeDataModel()->embeddedWidget()) + { + nodeDataModel()->embeddedWidget()->adjustSize(); + } + nodeGeometry().recalculateSize(); + + for(PortType type: {PortType::In, PortType::Out}) + { + for(auto& conn_set : nodeState().getEntries(type)) { - for(auto& conn_set : nodeState().getEntries(type)) - { - for(auto& pair: conn_set) - { - Connection* conn = pair.second; - conn->getConnectionGraphicsObject().move(); - } - } + for(auto& pair: conn_set) + { + Connection* conn = pair.second; + conn->getConnectionGraphicsObject().move(); + } } + } } diff --git a/src/NodeConnectionInteraction.cpp b/src/NodeConnectionInteraction.cpp index 01d9fbf8..033bf4bf 100644 --- a/src/NodeConnectionInteraction.cpp +++ b/src/NodeConnectionInteraction.cpp @@ -30,21 +30,20 @@ bool NodeConnectionInteraction:: canConnect(PortIndex &portIndex, TypeConverter & converter) const { - // 1) Connection requires a port - - PortType requiredPort = connectionRequiredPort(); + // 1) Connection requires a port + PortType requiredPort = connectionRequiredPort(); - if (requiredPort == PortType::None) - { - return false; - } + if (requiredPort == PortType::None) + { + return false; + } - // 1.5) Forbid connecting the node to itself - Node* node = _connection->getNode(oppositePort(requiredPort)); + // 1.5) Forbid connecting the node to itself + Node* node = _connection->getNode(oppositePort(requiredPort)); - if (node == _node) - return false; + if (node == _node) + return false; // 1.6) Check for Cyclic Connection // Fix #198 @@ -117,7 +116,7 @@ canConnect(PortIndex &portIndex, TypeConverter & converter) const } else if (requiredPort == PortType::Out) { - converter = _scene->registry().getTypeConverter(candidateNodeDataType , connectionDataType); + converter = _scene->registry().getTypeConverter(candidateNodeDataType, connectionDataType); } return (converter != nullptr); @@ -188,7 +187,7 @@ disconnect(PortType portToDisconnect) const NodeState &state = _node->nodeState(); // clear pointer to Connection in the NodeState - state.getEntries(portToDisconnect)[portIndex].clear(); + state.getEntries(portToDisconnect)[portIndex].erase(_connection->id()); // 4) Propagate invalid data to IN node _connection->propagateEmptyData(); @@ -258,6 +257,7 @@ nodePortIndexUnderScenePoint(PortType portType, PortIndex portIndex = nodeGeom.checkHitScenePoint(portType, scenePoint, sceneTransform); + return portIndex; } @@ -266,12 +266,20 @@ bool NodeConnectionInteraction:: nodePortIsEmpty(PortType portType, PortIndex portIndex) const { + if (portType == PortType::None) + { + return false; + } + NodeState const & nodeState = _node->nodeState(); auto const & entries = nodeState.getEntries(portType); - if (entries[portIndex].empty()) return true; + if (entries[portIndex].empty()) + return true; + + if (portType == PortType::In) + return _node->nodeDataModel()->portInConnectionPolicy(portIndex) == NodeDataModel::ConnectionPolicy::Many; - const auto outPolicy = _node->nodeDataModel()->portOutConnectionPolicy(portIndex); - return ( portType == PortType::Out && outPolicy == NodeDataModel::ConnectionPolicy::Many); + return _node->nodeDataModel()->portOutConnectionPolicy(portIndex) == NodeDataModel::ConnectionPolicy::Many; } diff --git a/src/NodeConnectionInteraction.hpp b/src/NodeConnectionInteraction.hpp index 03e57a83..ddbd87cc 100644 --- a/src/NodeConnectionInteraction.hpp +++ b/src/NodeConnectionInteraction.hpp @@ -16,54 +16,60 @@ class NodeDataModel; class NodeConnectionInteraction { public: - NodeConnectionInteraction(Node& node, - Connection& connection, - FlowScene& scene); - - /// Can connect when following conditions are met: - /// 1) Connection 'requires' a port - /// 2) Connection's vacant end is above the node port - /// 3) Node port is vacant - /// 4) Connection type equals node port type, or there is a registered type conversion that can translate between the two - bool canConnect(PortIndex & portIndex, - TypeConverter & converter) const; - - /// 1) Check conditions from 'canConnect' - /// 1.5) If the connection is possible but a type conversion is needed, add a converter node to the scene, and connect it properly - /// 1.6) Check for cyclic connection. - /// 2) Assign node to required port in Connection - /// 3) Assign Connection to empty port in NodeState - /// 4) Adjust Connection geometry - /// 5) Poke model to initiate data transfer - bool tryConnect() const; - - - /// 1) Node and Connection should be already connected - /// 2) If so, clear Connection entry in the NodeState - /// 3) Propagate invalid data to IN node - /// 4) Set Connection end to 'requiring a port' - bool disconnect(PortType portToDisconnect) const; + NodeConnectionInteraction(Node& node, + Connection& connection, + FlowScene& scene); + + /// Can connect when following conditions are met: + /// 1) Connection 'requires' a port + /// 2) Connection's vacant end is above the node port + /// 3) Node port is vacant + /// 4) Connection type equals node port type, or there is a registered type conversion that can translate between the two + bool + canConnect(PortIndex & portIndex, + TypeConverter & converter) const; + + /// 1) Check conditions from 'canConnect' + /// 1.5) If the connection is possible but a type conversion is needed, add a converter node to the scene, and connect it properly + /// 1.6) Check for cyclic connection. + /// 2) Assign node to required port in Connection + /// 3) Assign Connection to empty port in NodeState + /// 4) Adjust Connection geometry + /// 5) Poke model to initiate data transfer + bool + tryConnect() const; + + /// 1) Node and Connection should be already connected + /// 2) If so, clear Connection entry in the NodeState + /// 3) Propagate invalid data to IN node + /// 4) Set Connection end to 'requiring a port' + bool + disconnect(PortType portToDisconnect) const; private: - PortType connectionRequiredPort() const; + PortType + connectionRequiredPort() const; - QPointF connectionEndScenePosition(PortType) const; + QPointF connectionEndScenePosition(PortType) const; - QPointF nodePortScenePosition(PortType portType, - PortIndex portIndex) const; + QPointF + nodePortScenePosition(PortType portType, + PortIndex portIndex) const; - PortIndex nodePortIndexUnderScenePoint(PortType portType, - QPointF const &p) const; + PortIndex + nodePortIndexUnderScenePoint(PortType portType, + QPointF const &p) const; - bool nodePortIsEmpty(PortType portType, PortIndex portIndex) const; + bool + nodePortIsEmpty(PortType portType, PortIndex portIndex) const; private: - Node* _node; + Node* _node; - Connection* _connection; + Connection* _connection; - FlowScene* _scene; + FlowScene* _scene; }; } diff --git a/src/NodeDataModel.cpp b/src/NodeDataModel.cpp index 9737b345..15dd2a45 100644 --- a/src/NodeDataModel.cpp +++ b/src/NodeDataModel.cpp @@ -4,6 +4,9 @@ using QtNodes::NodeDataModel; using QtNodes::NodeStyle; +using QtNodes::NodeData; +using QtNodes::PortIndex; +using QtNodes::PortType; NodeDataModel:: NodeDataModel() @@ -25,6 +28,30 @@ save() const } +NodeDataModel::ConnectionPolicy +NodeDataModel:: +portConnectionPolicy(PortType portType, PortIndex portIndex) const +{ + ConnectionPolicy result = ConnectionPolicy::One; + + switch (portType) + { + case PortType::In: + result = portInConnectionPolicy(portIndex); + break; + + case PortType::Out: + result = portOutConnectionPolicy(portIndex); + break; + + default: + break; + } + + return result; +} + + NodeStyle const& NodeDataModel:: nodeStyle() const @@ -39,3 +66,21 @@ setNodeStyle(NodeStyle const& style) { _nodeStyle = style; } + + +void +NodeDataModel:: +setInData(std::vector > nodeData, PortIndex port) +{ + if (portInConnectionPolicy(port) == QtNodes::NodeDataModel::ConnectionPolicy::One) + { + if (nodeData.empty()) + setInData(nullptr, port); + else + setInData(nodeData[0], port); + } + else + { + Q_ASSERT(false); + } +} diff --git a/src/NodeGraphicsObject.cpp b/src/NodeGraphicsObject.cpp index efa473a2..9d19f6fd 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -216,8 +216,8 @@ mousePressEvent(QGraphicsSceneMouseEvent * event) // TODO do not pass sceneTransform int const portIndex = nodeGeometry.checkHitScenePoint(portToCheck, - event->scenePos(), - sceneTransform()); + event->scenePos(), + sceneTransform()); if (portIndex != INVALID) { diff --git a/src/NodePainter.cpp b/src/NodePainter.cpp index c3bdf7ae..3c1670d3 100644 --- a/src/NodePainter.cpp +++ b/src/NodePainter.cpp @@ -118,7 +118,7 @@ drawConnectionPoints(QPainter* painter, float diameter = nodeStyle.ConnectionPointDiameter; auto reducedDiameter = diameter * 0.6; - for(PortType portType: {PortType::Out, PortType::In}) + for (PortType portType: {PortType::Out, PortType::In}) { size_t n = state.getEntries(portType).size(); @@ -129,15 +129,13 @@ drawConnectionPoints(QPainter* painter, auto const & dataType = model->dataType(portType, i); bool canConnect = (state.getEntries(portType)[i].empty() || - (portType == PortType::Out && - model->portOutConnectionPolicy(i) == NodeDataModel::ConnectionPolicy::Many) ); + model->portConnectionPolicy(portType, i) == NodeDataModel::ConnectionPolicy::Many); double r = 1.0; if (state.isReacting() && canConnect && portType == state.reactingPortType()) { - auto diff = geom.draggingPos() - p; double dist = std::sqrt(QPointF::dotProduct(diff, diff)); bool typeConvertable = false; @@ -157,15 +155,15 @@ drawConnectionPoints(QPainter* painter, { double const thres = 40.0; r = (dist < thres) ? - (2.0 - dist / thres ) : - 1.0; + (2.0 - dist / thres ) : + 1.0; } else { double const thres = 80.0; r = (dist < thres) ? - (dist / thres) : - 1.0; + (dist / thres) : + 1.0; } } @@ -182,7 +180,8 @@ drawConnectionPoints(QPainter* painter, reducedDiameter * r, reducedDiameter * r); } - }; + } + } @@ -198,7 +197,7 @@ drawFilledConnectionPoints(QPainter * painter, auto diameter = nodeStyle.ConnectionPointDiameter; - for(PortType portType: {PortType::Out, PortType::In}) + for (PortType portType: {PortType::Out, PortType::In}) { size_t n = state.getEntries(portType).size(); @@ -277,7 +276,7 @@ drawEntryLabels(QPainter * painter, QFontMetrics const & metrics = painter->fontMetrics(); - for(PortType portType: {PortType::Out, PortType::In}) + for (PortType portType: {PortType::Out, PortType::In}) { auto const &nodeStyle = model->nodeStyle(); @@ -311,16 +310,16 @@ drawEntryLabels(QPainter * painter, switch (portType) { - case PortType::In: - p.setX(5.0); - break; + case PortType::In: + p.setX(5.0); + break; - case PortType::Out: - p.setX(geom.width() - 5.0 - rect.width()); - break; + case PortType::Out: + p.setX(geom.width() - 5.0 - rect.width()); + break; - default: - break; + default: + break; } painter->drawText(p, s); diff --git a/src/NodeState.cpp b/src/NodeState.cpp index 226d5cfe..5136ddae 100644 --- a/src/NodeState.cpp +++ b/src/NodeState.cpp @@ -62,7 +62,7 @@ setConnection(PortType portType, auto &connections = getEntries(portType); connections.at(portIndex).insert(std::make_pair(connection.id(), - &connection)); + &connection)); }