diff --git a/examples/calculator/DivisionModel.hpp b/examples/calculator/DivisionModel.hpp index ea5e6b09a..1b7865c7c 100644 --- a/examples/calculator/DivisionModel.hpp +++ b/examples/calculator/DivisionModel.hpp @@ -59,23 +59,23 @@ class DivisionModel : public MathOperationDataModel if (n2 && (n2->number() == 0.0)) { state._state = QtNodes::NodeValidationState::State::Error; state._stateMessage = QStringLiteral("Division by zero error"); - setValidatonState(state); + setValidationState(state); _result.reset(); } else if ( n2 && (n2->number() < 1e-5)) { state._state = QtNodes::NodeValidationState::State::Warning; state._stateMessage = QStringLiteral("Very small divident. Result might overflow"); - setValidatonState(state); + setValidationState(state); if (n1) { _result = std::make_shared(n1->number() / n2->number()); } else { _result.reset(); } } else if (n1 && n2) { - setValidatonState(state); + setValidationState(state); _result = std::make_shared(n1->number() / n2->number()); } else { QtNodes::NodeValidationState state; - setValidatonState(state); + setValidationState(state); _result.reset(); } diff --git a/examples/calculator/LongProcessingRandomNumber.hpp b/examples/calculator/LongProcessingRandomNumber.hpp new file mode 100644 index 000000000..8be2882ca --- /dev/null +++ b/examples/calculator/LongProcessingRandomNumber.hpp @@ -0,0 +1,82 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "MathOperationDataModel.hpp" +#include "DecimalData.hpp" + +/// The model generates a random value in a long processing schema, +/// as it should demonstrate the usage of the NodeProcessingStatus. +/// The random number is generate in the [n1, n2] interval. +class RandomNumberModel : public MathOperationDataModel +{ +public: + RandomNumberModel() { + this->setNodeProcessingStatus(QtNodes::NodeProcessingStatus::Empty); + + QObject::connect(this, &NodeDelegateModel::computingStarted, this, [this]() { + if (_number1.lock() && _number2.lock()) { + this->setNodeProcessingStatus( + QtNodes::NodeProcessingStatus::Processing); + } + + emit requestNodeUpdate(); + }); + QObject::connect(this, &NodeDelegateModel::computingFinished, this, [this]() { + this->setNodeProcessingStatus( + QtNodes::NodeProcessingStatus::Updated); + + emit requestNodeUpdate(); + }); + } + virtual ~RandomNumberModel() {} + +public: + QString caption() const override { return QStringLiteral("Random Number"); } + + QString name() const override { return QStringLiteral("Random Number"); } + +private: + void compute() override + { + Q_EMIT computingStarted(); + PortIndex const outPortIndex = 0; + + auto n1 = _number1.lock(); + auto n2 = _number2.lock(); + + QTimer *timer = new QTimer(this); + timer->start(1000); + int secondsRemaining = 3; + connect(timer, &QTimer::timeout, this, [=]() mutable { + if (--secondsRemaining <= 0) { + timer->stop(); + if (n1 && n2) { + double a = n1->number(); + double b = n2->number(); + + if (a > b) { + setNodeProcessingStatus(QtNodes::NodeProcessingStatus::Failed); + + emit requestNodeUpdate(); + return; + } + + double upper = std::nextafter(b, std::numeric_limits::max()); + double randomValue = QRandomGenerator::global()->generateDouble() * (upper - a) + a; + + _result = std::make_shared(randomValue); + Q_EMIT computingFinished(); + } else { + _result.reset(); + } + + Q_EMIT dataUpdated(outPortIndex); + } + }); + } +}; diff --git a/examples/calculator/NumberDisplayDataModel.cpp b/examples/calculator/NumberDisplayDataModel.cpp index 5086050c8..6f58f5854 100644 --- a/examples/calculator/NumberDisplayDataModel.cpp +++ b/examples/calculator/NumberDisplayDataModel.cpp @@ -4,7 +4,9 @@ NumberDisplayDataModel::NumberDisplayDataModel() : _label{nullptr} -{} +{ + this->setNodeProcessingStatus(QtNodes::NodeProcessingStatus::NoStatus); +} unsigned int NumberDisplayDataModel::nPorts(PortType portType) const { diff --git a/examples/calculator/NumberSourceDataModel.cpp b/examples/calculator/NumberSourceDataModel.cpp index f6a7ca55d..f2564886a 100644 --- a/examples/calculator/NumberSourceDataModel.cpp +++ b/examples/calculator/NumberSourceDataModel.cpp @@ -9,7 +9,9 @@ NumberSourceDataModel::NumberSourceDataModel() : _lineEdit{nullptr} , _number(std::make_shared(0.0)) -{} +{ + this->setNodeProcessingStatus(QtNodes::NodeProcessingStatus::NoStatus); +} QJsonObject NumberSourceDataModel::save() const { diff --git a/examples/calculator/headless_main.cpp b/examples/calculator/headless_main.cpp index e4dafa022..89103a4f2 100644 --- a/examples/calculator/headless_main.cpp +++ b/examples/calculator/headless_main.cpp @@ -1,5 +1,6 @@ #include "AdditionModel.hpp" #include "DivisionModel.hpp" +#include "LongProcessingRandomNumber.hpp" #include "MultiplicationModel.hpp" #include "NumberDisplayDataModel.hpp" #include "NumberSourceDataModel.hpp" @@ -27,6 +28,8 @@ static std::shared_ptr registerDataModels() ret->registerModel("Operators"); + ret->registerModel("Operators"); + return ret; } diff --git a/examples/calculator/main.cpp b/examples/calculator/main.cpp index 980c0f23a..4d2504f02 100644 --- a/examples/calculator/main.cpp +++ b/examples/calculator/main.cpp @@ -14,6 +14,7 @@ #include "AdditionModel.hpp" #include "DivisionModel.hpp" +#include "LongProcessingRandomNumber.hpp" #include "MultiplicationModel.hpp" #include "NumberDisplayDataModel.hpp" #include "NumberSourceDataModel.hpp" @@ -40,6 +41,8 @@ static std::shared_ptr registerDataModels() ret->registerModel("Operators"); + ret->registerModel("Operators"); + return ret; } diff --git a/include/QtNodes/internal/DefaultNodePainter.hpp b/include/QtNodes/internal/DefaultNodePainter.hpp index 589800ddd..953faa065 100644 --- a/include/QtNodes/internal/DefaultNodePainter.hpp +++ b/include/QtNodes/internal/DefaultNodePainter.hpp @@ -32,6 +32,8 @@ class NODE_EDITOR_PUBLIC DefaultNodePainter : public AbstractNodePainter void drawResizeRect(QPainter *painter, NodeGraphicsObject &ngo) const; + void drawProcessingIndicator(QPainter *painter, NodeGraphicsObject &ngo) const; + void drawValidationIcon(QPainter *painter, NodeGraphicsObject &ngo) const; private: diff --git a/include/QtNodes/internal/Definitions.hpp b/include/QtNodes/internal/Definitions.hpp index efd9bbd3e..8c01475f9 100644 --- a/include/QtNodes/internal/Definitions.hpp +++ b/include/QtNodes/internal/Definitions.hpp @@ -33,6 +33,7 @@ enum class NodeRole { OutPortCount = 9, ///< `unsigned int` Widget = 10, ///< Optional `QWidget*` or `nullptr` ValidationState = 11, ///< Enum NodeValidationState of the node + ProcessingStatus = 12 ///< Enum NodeProcessingStatus of the node }; Q_ENUM_NS(NodeRole) diff --git a/include/QtNodes/internal/NodeDelegateModel.hpp b/include/QtNodes/internal/NodeDelegateModel.hpp index b47596f3a..4c7001f9e 100644 --- a/include/QtNodes/internal/NodeDelegateModel.hpp +++ b/include/QtNodes/internal/NodeDelegateModel.hpp @@ -9,6 +9,7 @@ #include "Export.hpp" #include "NodeData.hpp" #include "NodeStyle.hpp" +#include #include "Serializable.hpp" namespace QtNodes { @@ -19,9 +20,9 @@ namespace QtNodes { struct NodeValidationState { enum class State : int { - Valid = 0, ///< All required inputs are present and correct. - Warning = 1, ///< Some inputs are missing or questionable, processing may be unreliable. - Error = 2, ///< Inputs or settings are invalid, preventing successful computation. + Valid = 0, ///< All required inputs are present and correct. + Warning = 1, ///< Some inputs are missing or questionable, processing may be unreliable. + Error = 2, ///< Inputs or settings are invalid, preventing successful computation. }; bool isValid() { return _state == State::Valid; }; QString const message() { return _stateMessage; } @@ -31,6 +32,19 @@ struct NodeValidationState QString _stateMessage{""}; }; +/** + * Describes the node status, depending on its current situation + */ +enum class NodeProcessingStatus : int { + NoStatus = 0, /// + Updated = 1, /// + Processing = 2, /// + Pending = 3, /// + Empty = 4, /// + Failed = 5, /// + Partial = 6, /// +}; + class StyleCollection; /** @@ -39,7 +53,9 @@ class StyleCollection; * AbstractGraphModel. * This class is the same what has been called NodeDataModel before v3. */ -class NODE_EDITOR_PUBLIC NodeDelegateModel : public QObject, public Serializable +class NODE_EDITOR_PUBLIC NodeDelegateModel + : public QObject + , public Serializable { Q_OBJECT @@ -48,15 +64,15 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel : public QObject, public Serializable virtual ~NodeDelegateModel() = default; + /// It is possible to hide caption in GUI + virtual bool captionVisible() const { return true; } + /// Name makes this model unique virtual QString name() const = 0; /// Caption is used in GUI virtual QString caption() const = 0; - /// It is possible to hide caption in GUI - virtual bool captionVisible() const { return true; } - /// Port caption is used in GUI to label individual ports virtual QString portCaption(PortType, PortIndex) const { return QString(); } @@ -66,12 +82,16 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel : public QObject, public Serializable /// Validation State will default to Valid, but you can manipulate it by overriding in an inherited class virtual NodeValidationState validationState() const { return _nodeValidationState; } -public: + /// Returns the curent processing status + virtual NodeProcessingStatus processingStatus() const { return _processingStatus; } + QJsonObject save() const override; void load(QJsonObject const &) override; - void setValidatonState(const NodeValidationState &validationState); + void setValidationState(const NodeValidationState &validationState); + + void setNodeProcessingStatus(NodeProcessingStatus status); virtual unsigned int nPorts(PortType portType) const = 0; @@ -80,8 +100,13 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel : public QObject, public Serializable virtual ConnectionPolicy portConnectionPolicy(PortType, PortIndex) const; NodeStyle const &nodeStyle() const; + void setNodeStyle(NodeStyle const &style); + /// Convenience helper to change the node background color. + void setBackgroundColor(QColor const &color); + +public: virtual void setInData(std::shared_ptr nodeData, PortIndex const portIndex) = 0; virtual std::shared_ptr outData(PortIndex const port) = 0; @@ -114,10 +139,20 @@ public Q_SLOTS: void dataInvalidated(PortIndex const index); void computingStarted(); + void computingFinished(); void embeddedWidgetSizeUpdated(); + /// Request an update of the node's UI. + /** + * Emit this signal whenever some internal state change requires + * the node to be repainted. The containing graph model will + * propagate the update to the scene. + */ + void requestNodeUpdate(); + + /// Call this function before deleting the data associated with ports. /** * @brief Call this function before deleting the data associated with ports. * The function notifies the Graph Model and makes it remove and recompute the @@ -144,8 +179,11 @@ public Q_SLOTS: NodeStyle _nodeStyle; NodeValidationState _nodeValidationState; + + NodeProcessingStatus _processingStatus; }; } // namespace QtNodes Q_DECLARE_METATYPE(QtNodes::NodeValidationState) +Q_DECLARE_METATYPE(QtNodes::NodeProcessingStatus) diff --git a/include/QtNodes/internal/NodeGraphicsObject.hpp b/include/QtNodes/internal/NodeGraphicsObject.hpp index eab83c768..69eb2ba6b 100644 --- a/include/QtNodes/internal/NodeGraphicsObject.hpp +++ b/include/QtNodes/internal/NodeGraphicsObject.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -52,6 +53,14 @@ class NodeGraphicsObject : public QGraphicsObject void updateQWidgetEmbedPos(); + void updateStatusIconSize() const; + + const QIcon processingStatusIcon() const; + + QRect statusIconRect() const; + + QSize statusIconSize() const; + protected: void paint(QPainter *painter, QStyleOptionGraphicsItem const *option, @@ -81,5 +90,16 @@ class NodeGraphicsObject : public QGraphicsObject // either nullptr or owned by parent QGraphicsItem QGraphicsProxyWidget *_proxyWidget; + + mutable bool _statusIconActive; + + mutable QSize _statusIconSize; + + const QIcon _statusUpdated{"://status_icons/updated.svg"}; + const QIcon _statusProcessing{"://status_icons/processing.svg"}; + const QIcon _statusPending{"://status_icons/pending.svg"}; + const QIcon _statusInvalid{"://status_icons/failed.svg"}; + const QIcon _statusEmpty{"://status_icons/empty.svg"}; + const QIcon _statusPartial{"://status_icons/partial.svg"}; }; } // namespace QtNodes diff --git a/include/QtNodes/internal/NodeStyle.hpp b/include/QtNodes/internal/NodeStyle.hpp index 9cb4a250c..48dde7b0b 100644 --- a/include/QtNodes/internal/NodeStyle.hpp +++ b/include/QtNodes/internal/NodeStyle.hpp @@ -26,6 +26,12 @@ class NODE_EDITOR_PUBLIC NodeStyle : public Style QJsonObject toJson() const override; + /// Set uniform background color for the node. + void setBackgroundColor(QColor const &color); + + /// Current uniform background color. + QColor backgroundColor() const; + public: QColor NormalBoundaryColor; QColor SelectedBoundaryColor; diff --git a/include/QtNodes/internal/UndoCommands.hpp b/include/QtNodes/internal/UndoCommands.hpp index 7aed4d60b..870478618 100644 --- a/include/QtNodes/internal/UndoCommands.hpp +++ b/include/QtNodes/internal/UndoCommands.hpp @@ -3,9 +3,9 @@ #include "Definitions.hpp" #include "Export.hpp" +#include #include #include -#include #include diff --git a/resources/resources.qrc b/resources/resources.qrc index 08aec37e6..f9da26fcd 100644 --- a/resources/resources.qrc +++ b/resources/resources.qrc @@ -2,5 +2,11 @@ DefaultStyle.json info-tooltip.svg + status_icons/empty.svg + status_icons/failed.svg + status_icons/partial.svg + status_icons/pending.svg + status_icons/processing.svg + status_icons/updated.svg diff --git a/resources/status_icons/empty.svg b/resources/status_icons/empty.svg new file mode 100644 index 000000000..e38cd34ae --- /dev/null +++ b/resources/status_icons/empty.svg @@ -0,0 +1,22 @@ + + + + + + + + + diff --git a/resources/status_icons/failed.svg b/resources/status_icons/failed.svg new file mode 100644 index 000000000..0b94f245c --- /dev/null +++ b/resources/status_icons/failed.svg @@ -0,0 +1,78 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/resources/status_icons/partial.svg b/resources/status_icons/partial.svg new file mode 100644 index 000000000..ec991dc5d --- /dev/null +++ b/resources/status_icons/partial.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/resources/status_icons/pending.svg b/resources/status_icons/pending.svg new file mode 100644 index 000000000..c850c9132 --- /dev/null +++ b/resources/status_icons/pending.svg @@ -0,0 +1,84 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/resources/status_icons/processing.svg b/resources/status_icons/processing.svg new file mode 100644 index 000000000..74b82bc3a --- /dev/null +++ b/resources/status_icons/processing.svg @@ -0,0 +1,80 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/status_icons/updated.svg b/resources/status_icons/updated.svg new file mode 100644 index 000000000..c2485802e --- /dev/null +++ b/resources/status_icons/updated.svg @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/src/DataFlowGraphModel.cpp b/src/DataFlowGraphModel.cpp index 396437241..fa5655cde 100644 --- a/src/DataFlowGraphModel.cpp +++ b/src/DataFlowGraphModel.cpp @@ -96,6 +96,10 @@ NodeId DataFlowGraphModel::addNode(QString const nodeType) this, &DataFlowGraphModel::portsInserted); + connect(model.get(), &NodeDelegateModel::requestNodeUpdate, this, [newId, this]() { + Q_EMIT nodeUpdated(newId); + }); + _models[newId] = std::move(model); Q_EMIT nodeCreated(newId); @@ -294,6 +298,11 @@ QVariant DataFlowGraphModel::nodeData(NodeId nodeId, NodeRole role) const auto validationState = model->validationState(); result = QVariant::fromValue(validationState); } break; + + case NodeRole::ProcessingStatus: { + auto processingStatus = model->processingStatus(); + result = QVariant::fromValue(processingStatus); + } break; } return result; @@ -358,7 +367,17 @@ bool DataFlowGraphModel::setNodeData(NodeId nodeId, NodeRole role, QVariant valu if (value.canConvert()) { auto state = value.value(); if (auto node = delegateModel(nodeId); node != nullptr) { - node->setValidatonState(state); + node->setValidationState(state); + } + } + Q_EMIT nodeUpdated(nodeId); + } break; + + case NodeRole::ProcessingStatus: { + if (value.canConvert()) { + auto status = value.value(); + if (auto node = delegateModel(nodeId); node != nullptr) { + node->setNodeProcessingStatus(status); } } Q_EMIT nodeUpdated(nodeId); @@ -567,6 +586,9 @@ void DataFlowGraphModel::loadNode(QJsonObject const &nodeJson) &NodeDelegateModel::portsInserted, this, &DataFlowGraphModel::portsInserted); + connect(model.get(), &NodeDelegateModel::requestNodeUpdate, this, [restoredNodeId, this]() { + Q_EMIT nodeUpdated(restoredNodeId); + }); _models[restoredNodeId] = std::move(model); diff --git a/src/DefaultHorizontalNodeGeometry.cpp b/src/DefaultHorizontalNodeGeometry.cpp index 8f3e07aad..a2102c641 100644 --- a/src/DefaultHorizontalNodeGeometry.cpp +++ b/src/DefaultHorizontalNodeGeometry.cpp @@ -1,7 +1,7 @@ #include "DefaultHorizontalNodeGeometry.hpp" - #include "AbstractGraphModel.hpp" #include "NodeData.hpp" +#include "NodeDelegateModel.hpp" #include #include @@ -55,6 +55,12 @@ void DefaultHorizontalNodeGeometry::recomputeSize(NodeId const nodeId) const height += _portSpasing; // space above caption height += _portSpasing; // space below caption + QVariant var = _graphModel.nodeData(nodeId, NodeRole::ProcessingStatus); + auto processingStatusValue = var.value(); + + if (processingStatusValue != QtNodes::NodeProcessingStatus::NoStatus) + height += 20; + unsigned int inPortWidth = maxPortsTextAdvance(nodeId, PortType::In); unsigned int outPortWidth = maxPortsTextAdvance(nodeId, PortType::Out); diff --git a/src/DefaultNodePainter.cpp b/src/DefaultNodePainter.cpp index 1bd2bc5f5..259eb670e 100644 --- a/src/DefaultNodePainter.cpp +++ b/src/DefaultNodePainter.cpp @@ -5,6 +5,7 @@ #include "BasicGraphicsScene.hpp" #include "ConnectionGraphicsObject.hpp" #include "ConnectionIdUtils.hpp" +#include "DataFlowGraphModel.hpp" #include "NodeDelegateModel.hpp" #include "NodeGraphicsObject.hpp" #include "NodeState.hpp" @@ -14,7 +15,6 @@ #include - namespace QtNodes { void DefaultNodePainter::paint(QPainter *painter, NodeGraphicsObject &ngo) const @@ -33,6 +33,8 @@ void DefaultNodePainter::paint(QPainter *painter, NodeGraphicsObject &ngo) const drawEntryLabels(painter, ngo); + drawProcessingIndicator(painter, ngo); + drawResizeRect(painter, ngo); drawValidationIcon(painter, ngo); @@ -82,7 +84,6 @@ void DefaultNodePainter::drawNodeRect(QPainter *painter, NodeGraphicsObject &ngo QPen p(color, penWidth); painter->setPen(p); - QLinearGradient gradient(QPointF(0.0, 0.0), QPointF(2.0, size.height())); gradient.setColorAt(0.0, nodeStyle.GradientColor0); gradient.setColorAt(0.10, nodeStyle.GradientColor1); @@ -113,7 +114,6 @@ void DefaultNodePainter::drawConnectionPoints(QPainter *painter, NodeGraphicsObj auto reducedDiameter = diameter * 0.6; for (PortType portType : {PortType::Out, PortType::In}) { - auto portCountRole = (portType == PortType::Out) ? NodeRole::OutPortCount : NodeRole::InPortCount; size_t const n = model.nodeData(nodeId, portCountRole).toUInt(); @@ -293,6 +293,32 @@ void DefaultNodePainter::drawResizeRect(QPainter *painter, NodeGraphicsObject &n } } +void DefaultNodePainter::drawProcessingIndicator(QPainter *painter, NodeGraphicsObject &ngo) const +{ + AbstractGraphModel &model = ngo.graphModel(); + NodeId const nodeId = ngo.nodeId(); + + auto *dfModel = dynamic_cast(&model); + if (!dfModel) + return; + + auto *delegate = dfModel->delegateModel(nodeId); + if (!delegate) + return; + + AbstractNodeGeometry &geometry = ngo.nodeScene()->nodeGeometry(); + + ngo.updateStatusIconSize(); + QSize size = geometry.size(nodeId); + + QIcon icon = ngo.processingStatusIcon(); + QSize iconSize(16, 16); + QPixmap pixmap = icon.pixmap(iconSize); + + QRect r(size.width() - 28.0, size.height() - 28.0, 20.0, 20.0); + painter->drawPixmap(r, pixmap); +} + void DefaultNodePainter::drawValidationIcon(QPainter *painter, NodeGraphicsObject &ngo) const { AbstractGraphModel &model = ngo.graphModel(); @@ -329,13 +355,11 @@ void DefaultNodePainter::drawValidationIcon(QPainter *painter, NodeGraphicsObjec painter->setBrush(color); painter->drawEllipse(center, iconSize.width() / 2.0 + 2.0, iconSize.height() / 2.0 + 2.0); - QPainter imgPainter(&pixmap); imgPainter.setCompositionMode(QPainter::CompositionMode_SourceIn); imgPainter.fillRect(pixmap.rect(), nodeStyle.FontColor); imgPainter.end(); - painter->drawPixmap(center.toPoint() - QPoint(iconSize.width() / 2, iconSize.height() / 2), pixmap); diff --git a/src/NodeDelegateModel.cpp b/src/NodeDelegateModel.cpp index 34cf2cb22..c5d267602 100644 --- a/src/NodeDelegateModel.cpp +++ b/src/NodeDelegateModel.cpp @@ -24,7 +24,7 @@ void NodeDelegateModel::load(QJsonObject const &) // } -void NodeDelegateModel::setValidatonState(const NodeValidationState &validationState) +void NodeDelegateModel::setValidationState(const NodeValidationState &validationState) { _nodeValidationState = validationState; } @@ -56,4 +56,14 @@ void NodeDelegateModel::setNodeStyle(NodeStyle const &style) _nodeStyle = style; } +void NodeDelegateModel::setNodeProcessingStatus(NodeProcessingStatus status) +{ + _processingStatus = status; +} + +void NodeDelegateModel::setBackgroundColor(QColor const &color) +{ + _nodeStyle.setBackgroundColor(color); +} + } // namespace QtNodes diff --git a/src/NodeGraphicsObject.cpp b/src/NodeGraphicsObject.cpp index 4db8ed903..815ad4747 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -16,7 +16,6 @@ #include - namespace QtNodes { NodeGraphicsObject::NodeGraphicsObject(BasicGraphicsScene &scene, NodeId nodeId) @@ -65,6 +64,15 @@ NodeGraphicsObject::NodeGraphicsObject(BasicGraphicsScene &scene, NodeId nodeId) if (_nodeId == nodeId) setLockedState(); }); + + QVariant var = _graphModel.nodeData(_nodeId, NodeRole::ProcessingStatus); + + auto processingStatusValue = var.value(); + + _statusIconActive = processingStatusValue != QtNodes::NodeProcessingStatus::NoStatus; + + _statusIconSize.setWidth(_statusIconActive ? 32 : 0); + _statusIconSize.setHeight(_statusIconActive ? 32 : 0); } AbstractGraphModel &NodeGraphicsObject::graphModel() const @@ -382,4 +390,63 @@ void NodeGraphicsObject::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) Q_EMIT nodeScene()->nodeContextMenu(_nodeId, mapToScene(event->pos())); } +void NodeGraphicsObject::updateStatusIconSize() const +{ + QVariant var = _graphModel.nodeData(_nodeId, NodeRole::ProcessingStatus); + + auto processingStatusValue = var.value(); + + bool oldStatus = _statusIconActive; + _statusIconActive = processingStatusValue != QtNodes::NodeProcessingStatus::NoStatus; + + if (oldStatus != _statusIconActive) { + _statusIconSize.setWidth(_statusIconActive ? 32 : 0); + _statusIconSize.setHeight(_statusIconActive ? 32 : 0); + } +} + +QRect NodeGraphicsObject::statusIconRect() const +{ + QVariant var = _graphModel.nodeData(_nodeId, NodeRole::ProcessingStatus); + + // auto spacing = static_cast(_spacing); + auto iconPos = + // = portScenePosition(std::max(var.value().nPorts(PortType::Out), + // var.value().nPorts(PortType::In)), + // PortType::Out) + // .toPoint() + // + + QPoint{-statusIconSize().width() / 2, 0}; + + return QRect{iconPos, statusIconSize()}; +} + +const QIcon NodeGraphicsObject::processingStatusIcon() const +{ + QVariant var = _graphModel.nodeData(_nodeId, NodeRole::ProcessingStatus); + + switch (var.value()) { + case QtNodes::NodeProcessingStatus::NoStatus: + return QIcon(); + case QtNodes::NodeProcessingStatus::Updated: + return _statusUpdated; + case QtNodes::NodeProcessingStatus::Processing: + return _statusProcessing; + case QtNodes::NodeProcessingStatus::Pending: + return _statusPending; + case QtNodes::NodeProcessingStatus::Empty: + return _statusEmpty; + case QtNodes::NodeProcessingStatus::Failed: + return _statusInvalid; + case QtNodes::NodeProcessingStatus::Partial: + return _statusPartial; + } + return _statusInvalid; +} + +QSize NodeGraphicsObject::statusIconSize() const +{ + return _statusIconSize; +} + } // namespace QtNodes diff --git a/src/NodeStyle.cpp b/src/NodeStyle.cpp index b3079b332..cb7b17090 100644 --- a/src/NodeStyle.cpp +++ b/src/NodeStyle.cpp @@ -157,3 +157,16 @@ QJsonObject NodeStyle::toJson() const return root; } + +void NodeStyle::setBackgroundColor(QColor const &color) +{ + GradientColor0 = color; + GradientColor1 = color; + GradientColor2 = color; + GradientColor3 = color; +} + +QColor NodeStyle::backgroundColor() const +{ + return GradientColor0; +}