From 6af24c70941a72dd28da7f5e76cc3015942d7be9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Taiguara=20Tupinamb=C3=A1s?= Date: Mon, 23 Jun 2025 14:27:15 -0300 Subject: [PATCH 01/26] improves NodeValidationState struct --- .../QtNodes/internal/NodeDelegateModel.hpp | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/include/QtNodes/internal/NodeDelegateModel.hpp b/include/QtNodes/internal/NodeDelegateModel.hpp index a7ae23bd..f44b5745 100644 --- a/include/QtNodes/internal/NodeDelegateModel.hpp +++ b/include/QtNodes/internal/NodeDelegateModel.hpp @@ -6,12 +6,27 @@ #include "NodeStyle.hpp" #include "Serializable.hpp" -#include - #include - +#include namespace QtNodes { +/** + * Describes whether a node configuration is usable and defines a description message + */ +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. + }; + bool isValid() { return _state == State::Valid; }; + QString const message() { return _stateMessage; } + State state() { return _state; } + + State _state{State::Valid}; + QString _stateMessage{""}; +}; class StyleCollection; @@ -21,7 +36,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 From 4038ee045118af8e23e6d2ad5cff298cbbc8838d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Taiguara=20Tupinamb=C3=A1s?= Date: Mon, 23 Jun 2025 16:15:01 -0300 Subject: [PATCH 02/26] qt6 for linux-gcc --- .github/workflows/cmake_build.yml | 4 ++-- .gitignore | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cmake_build.yml b/.github/workflows/cmake_build.yml index b513864c..766fc2ef 100644 --- a/.github/workflows/cmake_build.yml +++ b/.github/workflows/cmake_build.yml @@ -30,9 +30,9 @@ jobs: - toolchain: linux-gcc os: ubuntu-22.04 compiler: gcc - qt_version: "5.15.2" + qt_version: "6.3.0" modules: "" - use_qt6: "OFF" + use_qt6: "ON" - toolchain: macos-clang os: macos-latest diff --git a/.gitignore b/.gitignore index fc8ef420..552a9b2f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,6 @@ CMakeLists.txt.user build*/ .vscode/ +qt-build + tags From 996dbaa306af964783646beffcaf8d6ff5e7bab8 Mon Sep 17 00:00:00 2001 From: Gabrielnmds Date: Sat, 12 Jul 2025 15:40:22 -0300 Subject: [PATCH 03/26] adds node nickname functionality --- .../QtNodes/internal/AbstractNodeGeometry.hpp | 9 ++++ .../DefaultHorizontalNodeGeometry.hpp | 5 ++ .../QtNodes/internal/DefaultNodePainter.hpp | 2 + .../internal/DefaultVerticalNodeGeometry.hpp | 4 ++ include/QtNodes/internal/Definitions.hpp | 25 ++++++---- .../QtNodes/internal/NodeDelegateModel.hpp | 10 ++++ src/DataFlowGraphModel.cpp | 49 +++++++++++-------- src/DefaultHorizontalNodeGeometry.cpp | 22 +++++++++ src/DefaultNodePainter.cpp | 33 ++++++++++++- src/DefaultVerticalNodeGeometry.cpp | 10 ++++ 10 files changed, 136 insertions(+), 33 deletions(-) diff --git a/include/QtNodes/internal/AbstractNodeGeometry.hpp b/include/QtNodes/internal/AbstractNodeGeometry.hpp index acc60def..5a4719d1 100644 --- a/include/QtNodes/internal/AbstractNodeGeometry.hpp +++ b/include/QtNodes/internal/AbstractNodeGeometry.hpp @@ -60,6 +60,15 @@ class NODE_EDITOR_PUBLIC AbstractNodeGeometry /// Caption rect is needed for estimating the total node size. virtual QRectF captionRect(NodeId const nodeId) const = 0; + /** + * Defines where to start drawing the label. The point corresponds to a font + * baseline. + */ + virtual QPointF labelPosition(NodeId const nodeId) const = 0; + + /// Caption rect is needed for estimating the total node size. + virtual QRectF labelRect(NodeId const nodeId) const = 0; + /// Position for an embedded widget. Return any value if you don't embed. virtual QPointF widgetPosition(NodeId const nodeId) const = 0; diff --git a/include/QtNodes/internal/DefaultHorizontalNodeGeometry.hpp b/include/QtNodes/internal/DefaultHorizontalNodeGeometry.hpp index 33367e10..be985e12 100644 --- a/include/QtNodes/internal/DefaultHorizontalNodeGeometry.hpp +++ b/include/QtNodes/internal/DefaultHorizontalNodeGeometry.hpp @@ -28,10 +28,15 @@ class NODE_EDITOR_PUBLIC DefaultHorizontalNodeGeometry : public AbstractNodeGeom QPointF portTextPosition(NodeId const nodeId, PortType const portType, PortIndex const PortIndex) const override; + QPointF captionPosition(NodeId const nodeId) const override; QRectF captionRect(NodeId const nodeId) const override; + QPointF labelPosition(const NodeId nodeId) const override; + + QRectF labelRect(NodeId const nodeId) const override; + QPointF widgetPosition(NodeId const nodeId) const override; QRect resizeHandleRect(NodeId const nodeId) const override; diff --git a/include/QtNodes/internal/DefaultNodePainter.hpp b/include/QtNodes/internal/DefaultNodePainter.hpp index 484969f9..339759fa 100644 --- a/include/QtNodes/internal/DefaultNodePainter.hpp +++ b/include/QtNodes/internal/DefaultNodePainter.hpp @@ -27,6 +27,8 @@ class NODE_EDITOR_PUBLIC DefaultNodePainter : public AbstractNodePainter void drawNodeCaption(QPainter *painter, NodeGraphicsObject &ngo) const; + void drawNodeLabel(QPainter *painter, NodeGraphicsObject &ngo) const; + void drawEntryLabels(QPainter *painter, NodeGraphicsObject &ngo) const; void drawResizeRect(QPainter *painter, NodeGraphicsObject &ngo) const; diff --git a/include/QtNodes/internal/DefaultVerticalNodeGeometry.hpp b/include/QtNodes/internal/DefaultVerticalNodeGeometry.hpp index ce4dd9f1..f035f8bc 100644 --- a/include/QtNodes/internal/DefaultVerticalNodeGeometry.hpp +++ b/include/QtNodes/internal/DefaultVerticalNodeGeometry.hpp @@ -33,6 +33,10 @@ class NODE_EDITOR_PUBLIC DefaultVerticalNodeGeometry : public AbstractNodeGeomet QRectF captionRect(NodeId const nodeId) const override; + QPointF labelPosition(const NodeId nodeId) const override; + + QRectF labelRect(NodeId const nodeId) const override; + QPointF widgetPosition(NodeId const nodeId) const override; QRect resizeHandleRect(NodeId const nodeId) const override; diff --git a/include/QtNodes/internal/Definitions.hpp b/include/QtNodes/internal/Definitions.hpp index 863fa40b..41e63444 100644 --- a/include/QtNodes/internal/Definitions.hpp +++ b/include/QtNodes/internal/Definitions.hpp @@ -21,18 +21,23 @@ Q_NAMESPACE_EXPORT(NODE_EDITOR_PUBLIC) /** * Constants used for fetching QVariant data from GraphModel. */ + enum class NodeRole { - Type = 0, ///< Type of the current node, usually a string. - Position = 1, ///< `QPointF` positon of the node on the scene. - Size = 2, ///< `QSize` for resizable nodes. - CaptionVisible = 3, ///< `bool` for caption visibility. - Caption = 4, ///< `QString` for node caption. - Style = 5, ///< Custom NodeStyle as QJsonDocument - InternalData = 6, ///< Node-stecific user data as QJsonObject - InPortCount = 7, ///< `unsigned int` - OutPortCount = 9, ///< `unsigned int` - Widget = 10, ///< Optional `QWidget*` or `nullptr` + Type = 0, ///< Type of the current node, usually a string. + Position = 1, ///< `QPointF` positon of the node on the scene. + Size = 2, ///< `QSize` for resizable nodes. + CaptionVisible = 3, ///< `bool` for caption visibility. + Caption = 4, ///< `QString` for node caption. + Style = 5, ///< Custom NodeStyle as QJsonDocument + InternalData = 6, ///< Node-stecific user data as QJsonObject + InPortCount = 7, ///< `unsigned int` + OutPortCount = 9, ///< `unsigned int` + Widget = 10, ///< Optional `QWidget*` or `nullptr` + ValidationState = 11, ///< Enum NodeValidationState of the node + LabelVisible = 12, ///< `bool` for label visibility. + Label = 13, ///< `QString` for node label. }; + Q_ENUM_NS(NodeRole) /** diff --git a/include/QtNodes/internal/NodeDelegateModel.hpp b/include/QtNodes/internal/NodeDelegateModel.hpp index f44b5745..dbcbeea5 100644 --- a/include/QtNodes/internal/NodeDelegateModel.hpp +++ b/include/QtNodes/internal/NodeDelegateModel.hpp @@ -62,6 +62,16 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel /// It is possible to hide port caption in GUI virtual bool portCaptionVisible(PortType, PortIndex) const { return false; } + /// Nicknames can be assigned to nodes and shown in GUI + virtual QString label() const { return QString("Teste"); } + + /// It is possible to hide the nickname in GUI + virtual bool labelVisible() const { return true; } + + /// Validation State will default to Valid, but you can manipulate it by overriding in an inherited class + virtual NodeValidationState validationState() const { return _nodeValidationState; } + +public: QJsonObject save() const override; void load(QJsonObject const &) override; diff --git a/src/DataFlowGraphModel.cpp b/src/DataFlowGraphModel.cpp index f7892a02..7fd9ecef 100644 --- a/src/DataFlowGraphModel.cpp +++ b/src/DataFlowGraphModel.cpp @@ -8,7 +8,6 @@ #include #include - namespace QtNodes { DataFlowGraphModel::DataFlowGraphModel(std::shared_ptr registry) @@ -117,12 +116,10 @@ bool DataFlowGraphModel::connectionPossible(ConnectionId const connectionId) con // Check port bounds, i.e. that we do not connect non-existing port numbers auto checkPortBounds = [&](PortType const portType) { NodeId const nodeId = getNodeId(portType, connectionId); - auto portCountRole = (portType == PortType::Out) ? - NodeRole::OutPortCount : - NodeRole::InPortCount; + auto portCountRole = (portType == PortType::Out) ? NodeRole::OutPortCount + : NodeRole::InPortCount; - std::size_t const portCount = - nodeData(nodeId, portCountRole).toUInt(); + std::size_t const portCount = nodeData(nodeId, portCountRole).toUInt(); return getPortIndex(portType, connectionId) < portCount; }; @@ -146,12 +143,9 @@ bool DataFlowGraphModel::connectionPossible(ConnectionId const connectionId) con return connected.empty() || (policy == ConnectionPolicy::Many); }; - bool const basicChecks = - getDataType(PortType::Out).id == getDataType(PortType::In).id - && portVacant(PortType::Out) - && portVacant(PortType::In) - && checkPortBounds(PortType::Out) - && checkPortBounds(PortType::In); + bool const basicChecks = getDataType(PortType::Out).id == getDataType(PortType::In).id + && portVacant(PortType::Out) && portVacant(PortType::In) + && checkPortBounds(PortType::Out) && checkPortBounds(PortType::In); // In data-flow mode (this class) it's important to forbid graph loops. // We perform depth-first graph traversal starting from the "Input" port of @@ -161,17 +155,16 @@ bool DataFlowGraphModel::connectionPossible(ConnectionId const connectionId) con std::stack filo; filo.push(connectionId.inNodeId); - while (!filo.empty()) - { - auto id = filo.top(); filo.pop(); + while (!filo.empty()) { + auto id = filo.top(); + filo.pop(); if (id == connectionId.outNodeId) { // LOOP! - return true; + return true; } // Add out-connections to continue interations - std::size_t const nOutPorts = - nodeData(id, NodeRole::OutPortCount).toUInt(); + std::size_t const nOutPorts = nodeData(id, NodeRole::OutPortCount).toUInt(); for (PortIndex index = 0; index < nOutPorts; ++index) { auto const &outConnectionIds = connections(id, PortType::Out, index); @@ -188,7 +181,6 @@ bool DataFlowGraphModel::connectionPossible(ConnectionId const connectionId) con return basicChecks && (loopsEnabled() || !hasLoops()); } - void DataFlowGraphModel::addConnection(ConnectionId const connectionId) { _connectivity.insert(connectionId); @@ -297,6 +289,19 @@ QVariant DataFlowGraphModel::nodeData(NodeId nodeId, NodeRole role) const auto w = model->embeddedWidget(); result = QVariant::fromValue(w); } break; + + case NodeRole::ValidationState: { + auto validationState = model->validationState(); + result = QVariant::fromValue(validationState); + } break; + + case NodeRole::LabelVisible: + result = model->labelVisible(); + break; + + case NodeRole::Label: + result = model->label(); + break; } return result; @@ -538,7 +543,8 @@ void DataFlowGraphModel::loadNode(QJsonObject const &nodeJson) connect(model.get(), &NodeDelegateModel::portsAboutToBeDeleted, this, - [restoredNodeId, this](PortType const portType, PortIndex const first, PortIndex const last) { + [restoredNodeId, + this](PortType const portType, PortIndex const first, PortIndex const last) { portsAboutToBeDeleted(restoredNodeId, portType, first, last); }); @@ -550,7 +556,8 @@ void DataFlowGraphModel::loadNode(QJsonObject const &nodeJson) connect(model.get(), &NodeDelegateModel::portsAboutToBeInserted, this, - [restoredNodeId, this](PortType const portType, PortIndex const first, PortIndex const last) { + [restoredNodeId, + this](PortType const portType, PortIndex const first, PortIndex const last) { portsAboutToBeInserted(restoredNodeId, portType, first, last); }); diff --git a/src/DefaultHorizontalNodeGeometry.cpp b/src/DefaultHorizontalNodeGeometry.cpp index 8f3e07aa..be11c367 100644 --- a/src/DefaultHorizontalNodeGeometry.cpp +++ b/src/DefaultHorizontalNodeGeometry.cpp @@ -156,6 +156,28 @@ QPointF DefaultHorizontalNodeGeometry::captionPosition(NodeId const nodeId) cons 0.5 * _portSpasing + captionRect(nodeId).height()); } +QRectF DefaultHorizontalNodeGeometry::labelRect(NodeId const nodeId) const +{ + if (!_graphModel.nodeData(nodeId, NodeRole::LabelVisible)) + return QRect(); + + QString nickname = _graphModel.nodeData(nodeId, NodeRole::Label); + + QRectF nickRect = _boldFontMetrics.boundingRect(nickname); + + nickRect.setWidth(nickRect.width() * 0.5); + nickRect.setHeight(nickRect.height() * 0.5); + + return nickRect; +} + +QPointF DefaultHorizontalNodeGeometry::labelPosition(NodeId const nodeId) const +{ + QSize size = _graphModel.nodeData(nodeId, NodeRole::Size); + return QPointF(0.4 * (size.width() - labelRect(nodeId).width()), + 0.5 * _portSpasing + labelRect(nodeId).height()); +} + QPointF DefaultHorizontalNodeGeometry::widgetPosition(NodeId const nodeId) const { QSize size = _graphModel.nodeData(nodeId, NodeRole::Size); diff --git a/src/DefaultNodePainter.cpp b/src/DefaultNodePainter.cpp index 30bc309d..f6618581 100644 --- a/src/DefaultNodePainter.cpp +++ b/src/DefaultNodePainter.cpp @@ -13,7 +13,6 @@ #include - namespace QtNodes { void DefaultNodePainter::paint(QPainter *painter, NodeGraphicsObject &ngo) const @@ -33,6 +32,10 @@ void DefaultNodePainter::paint(QPainter *painter, NodeGraphicsObject &ngo) const drawEntryLabels(painter, ngo); drawResizeRect(painter, ngo); + + drawValidationIcon(painter, ngo); + + drawNodeLabel(painter, ngo); } void DefaultNodePainter::drawNodeRect(QPainter *painter, NodeGraphicsObject &ngo) const @@ -90,7 +93,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(); @@ -217,6 +219,33 @@ void DefaultNodePainter::drawNodeCaption(QPainter *painter, NodeGraphicsObject & painter->setFont(f); } +void DefaultNodePainter::drawNodeLabel(QPainter *painter, NodeGraphicsObject &ngo) const +{ + AbstractGraphModel &model = ngo.graphModel(); + NodeId const nodeId = ngo.nodeId(); + AbstractNodeGeometry &geometry = ngo.nodeScene()->nodeGeometry(); + + if (!model.nodeData(nodeId, NodeRole::LabelVisible).toBool()) + return; + + QString const nickname = model.nodeData(nodeId, NodeRole::Label).toString(); + + QFont f = painter->font(); + f.setBold(true); + + QPointF position = geometry.labelPosition(nodeId); + + QJsonDocument json = QJsonDocument::fromVariant(model.nodeData(nodeId, NodeRole::Style)); + NodeStyle nodeStyle(json.object()); + + painter->setFont(f); + painter->setPen(nodeStyle.FontColor); + painter->drawText(position, nickname); + + f.setBold(false); + painter->setFont(f); +} + void DefaultNodePainter::drawEntryLabels(QPainter *painter, NodeGraphicsObject &ngo) const { AbstractGraphModel &model = ngo.graphModel(); diff --git a/src/DefaultVerticalNodeGeometry.cpp b/src/DefaultVerticalNodeGeometry.cpp index f20617f2..172f4e62 100644 --- a/src/DefaultVerticalNodeGeometry.cpp +++ b/src/DefaultVerticalNodeGeometry.cpp @@ -183,6 +183,16 @@ QPointF DefaultVerticalNodeGeometry::captionPosition(NodeId const nodeId) const return QPointF(0.5 * (size.width() - rect.width()), step + rect.height()); } +QPointF DefaultVerticalNodeGeometry::labelPosition(const NodeId nodeId) const +{ + return QPointF(); +} + +QRectF DefaultVerticalNodeGeometry::labelRect(NodeId const nodeId) const +{ + return QRectF(); +} + QPointF DefaultVerticalNodeGeometry::widgetPosition(NodeId const nodeId) const { QSize size = _graphModel.nodeData(nodeId, NodeRole::Size); From 0ed2133226d739a362bc557d65be41d6d2d66a0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Taiguara=20Tupinamb=C3=A1s?= Date: Tue, 22 Jul 2025 15:50:44 -0300 Subject: [PATCH 04/26] Adjust label layout --- src/DefaultHorizontalNodeGeometry.cpp | 29 +++++++++++++++++++--- src/DefaultVerticalNodeGeometry.cpp | 35 ++++++++++++++++++++++++--- 2 files changed, 58 insertions(+), 6 deletions(-) diff --git a/src/DefaultHorizontalNodeGeometry.cpp b/src/DefaultHorizontalNodeGeometry.cpp index be11c367..20817328 100644 --- a/src/DefaultHorizontalNodeGeometry.cpp +++ b/src/DefaultHorizontalNodeGeometry.cpp @@ -49,8 +49,13 @@ void DefaultHorizontalNodeGeometry::recomputeSize(NodeId const nodeId) const } QRectF const capRect = captionRect(nodeId); + QRectF const lblRect = labelRect(nodeId); height += capRect.height(); + if (!lblRect.isNull()) { + height += lblRect.height(); + height += _portSpasing / 2; + } height += _portSpasing; // space above caption height += _portSpasing; // space below caption @@ -64,7 +69,11 @@ void DefaultHorizontalNodeGeometry::recomputeSize(NodeId const nodeId) const width += w->width(); } - width = std::max(width, static_cast(capRect.width()) + 2 * _portSpasing); + unsigned int textWidth = static_cast(capRect.width()); + if (!lblRect.isNull()) + textWidth = std::max(textWidth, static_cast(lblRect.width())); + + width = std::max(width, textWidth + 2 * _portSpasing); QSize size(width, height); @@ -82,6 +91,12 @@ QPointF DefaultHorizontalNodeGeometry::portPosition(NodeId const nodeId, double totalHeight = 0.0; totalHeight += captionRect(nodeId).height(); + + if (_graphModel.nodeData(nodeId, NodeRole::LabelVisible)) { + totalHeight += labelRect(nodeId).height(); + totalHeight += _portSpasing / 2.0; + } + totalHeight += _portSpasing; totalHeight += step * portIndex; @@ -174,8 +189,14 @@ QRectF DefaultHorizontalNodeGeometry::labelRect(NodeId const nodeId) const QPointF DefaultHorizontalNodeGeometry::labelPosition(NodeId const nodeId) const { QSize size = _graphModel.nodeData(nodeId, NodeRole::Size); - return QPointF(0.4 * (size.width() - labelRect(nodeId).width()), - 0.5 * _portSpasing + labelRect(nodeId).height()); + + QRectF cap = captionRect(nodeId); + QRectF lbl = labelRect(nodeId); + + double y = 0.5 * _portSpasing + cap.height(); + y += _portSpasing / 2.0 + lbl.height(); + + return QPointF(0.5 * (size.width() - lbl.width()), y); } QPointF DefaultHorizontalNodeGeometry::widgetPosition(NodeId const nodeId) const @@ -183,6 +204,8 @@ QPointF DefaultHorizontalNodeGeometry::widgetPosition(NodeId const nodeId) const QSize size = _graphModel.nodeData(nodeId, NodeRole::Size); unsigned int captionHeight = captionRect(nodeId).height(); + if (_graphModel.nodeData(nodeId, NodeRole::LabelVisible)) + captionHeight += labelRect(nodeId).height() + _portSpasing / 2; if (auto w = _graphModel.nodeData(nodeId, NodeRole::Widget)) { // If the widget wants to use as much vertical space as possible, diff --git a/src/DefaultVerticalNodeGeometry.cpp b/src/DefaultVerticalNodeGeometry.cpp index 172f4e62..2d76a758 100644 --- a/src/DefaultVerticalNodeGeometry.cpp +++ b/src/DefaultVerticalNodeGeometry.cpp @@ -49,8 +49,13 @@ void DefaultVerticalNodeGeometry::recomputeSize(NodeId const nodeId) const } QRectF const capRect = captionRect(nodeId); + QRectF const lblRect = labelRect(nodeId); height += capRect.height(); + if (!lblRect.isNull()) { + height += lblRect.height(); + height += _portSpasing / 2; + } height += _portSpasing; height += _portSpasing; @@ -80,7 +85,11 @@ void DefaultVerticalNodeGeometry::recomputeSize(NodeId const nodeId) const width = std::max(width, static_cast(w->width())); } - width = std::max(width, static_cast(capRect.width())); + unsigned int textWidth = static_cast(capRect.width()); + if (!lblRect.isNull()) + textWidth = std::max(textWidth, static_cast(lblRect.width())); + + width = std::max(width, textWidth); width += _portSpasing; width += _portSpasing; @@ -185,12 +194,30 @@ QPointF DefaultVerticalNodeGeometry::captionPosition(NodeId const nodeId) const QPointF DefaultVerticalNodeGeometry::labelPosition(const NodeId nodeId) const { - return QPointF(); + QSize size = _graphModel.nodeData(nodeId, NodeRole::Size); + + QRectF rect = labelRect(nodeId); + + unsigned int step = portCaptionsHeight(nodeId, PortType::In); + step += _portSpasing; + step += captionRect(nodeId).height(); + step += _portSpasing / 2; + + return QPointF(0.5 * (size.width() - rect.width()), step + rect.height()); } QRectF DefaultVerticalNodeGeometry::labelRect(NodeId const nodeId) const { - return QRectF(); + if (!_graphModel.nodeData(nodeId, NodeRole::LabelVisible)) + return QRectF(); + + QString nickname = _graphModel.nodeData(nodeId, NodeRole::Label); + + QRectF rect = _boldFontMetrics.boundingRect(nickname); + rect.setWidth(rect.width() * 0.5); + rect.setHeight(rect.height() * 0.5); + + return rect; } QPointF DefaultVerticalNodeGeometry::widgetPosition(NodeId const nodeId) const @@ -198,6 +225,8 @@ QPointF DefaultVerticalNodeGeometry::widgetPosition(NodeId const nodeId) const QSize size = _graphModel.nodeData(nodeId, NodeRole::Size); unsigned int captionHeight = captionRect(nodeId).height(); + if (_graphModel.nodeData(nodeId, NodeRole::LabelVisible)) + captionHeight += labelRect(nodeId).height() + _portSpasing / 2; if (auto w = _graphModel.nodeData(nodeId, NodeRole::Widget)) { // If the widget wants to use as much vertical space as possible, From 44ad376067321028b10157344e793ca3303bae3a Mon Sep 17 00:00:00 2001 From: g-abilio Date: Wed, 23 Jul 2025 16:10:28 -0300 Subject: [PATCH 05/26] improve caption and nickname dynamic --- src/DefaultHorizontalNodeGeometry.cpp | 22 +++++++++++++++++----- src/DefaultNodePainter.cpp | 4 +++- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/DefaultHorizontalNodeGeometry.cpp b/src/DefaultHorizontalNodeGeometry.cpp index 20817328..682a7e67 100644 --- a/src/DefaultHorizontalNodeGeometry.cpp +++ b/src/DefaultHorizontalNodeGeometry.cpp @@ -167,8 +167,14 @@ QRectF DefaultHorizontalNodeGeometry::captionRect(NodeId const nodeId) const QPointF DefaultHorizontalNodeGeometry::captionPosition(NodeId const nodeId) const { QSize size = _graphModel.nodeData(nodeId, NodeRole::Size); - return QPointF(0.5 * (size.width() - captionRect(nodeId).width()), - 0.5 * _portSpasing + captionRect(nodeId).height()); + + QRectF cap = captionRect(nodeId); + QRectF lbl = labelRect(nodeId); + + double y = 0.5 * _portSpasing + cap.height(); + y += _portSpasing / 2.0 + lbl.height(); + + return QPointF(0.5 * (size.width() - captionRect(nodeId).width()), y); } QRectF DefaultHorizontalNodeGeometry::labelRect(NodeId const nodeId) const @@ -188,15 +194,21 @@ QRectF DefaultHorizontalNodeGeometry::labelRect(NodeId const nodeId) const QPointF DefaultHorizontalNodeGeometry::labelPosition(NodeId const nodeId) const { - QSize size = _graphModel.nodeData(nodeId, NodeRole::Size); - QRectF cap = captionRect(nodeId); QRectF lbl = labelRect(nodeId); double y = 0.5 * _portSpasing + cap.height(); y += _portSpasing / 2.0 + lbl.height(); - return QPointF(0.5 * (size.width() - lbl.width()), y); + if (!_graphModel.nodeData(nodeId, NodeRole::CaptionVisible)) { + return QPointF(captionPosition(nodeId).x() + + 0.5 * (captionRect(nodeId).width() - 2 * labelRect(nodeId).width()), + y); + } + + return QPointF(captionPosition(nodeId).x() + + 0.5 * (captionRect(nodeId).width() - 2 * labelRect(nodeId).width()), + 0.5 * _portSpasing + captionRect(nodeId).height()); } QPointF DefaultHorizontalNodeGeometry::widgetPosition(NodeId const nodeId) const diff --git a/src/DefaultNodePainter.cpp b/src/DefaultNodePainter.cpp index f6618581..2bd785d9 100644 --- a/src/DefaultNodePainter.cpp +++ b/src/DefaultNodePainter.cpp @@ -204,7 +204,8 @@ void DefaultNodePainter::drawNodeCaption(QPainter *painter, NodeGraphicsObject & QString const name = model.nodeData(nodeId, NodeRole::Caption).toString(); QFont f = painter->font(); - f.setBold(true); + f.setBold(!model.nodeData(nodeId, NodeRole::LabelVisible).toBool()); + f.setItalic(model.nodeData(nodeId, NodeRole::LabelVisible).toBool()); QPointF position = geometry.captionPosition(nodeId); @@ -216,6 +217,7 @@ void DefaultNodePainter::drawNodeCaption(QPainter *painter, NodeGraphicsObject & painter->drawText(position, name); f.setBold(false); + f.setItalic(false); painter->setFont(f); } From 90c8fa64302907ed7dde01617218a5476cee994d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Taiguara=20Tupinamb=C3=A1s?= Date: Fri, 25 Jul 2025 13:18:42 -0300 Subject: [PATCH 06/26] add editable nickname label --- .../QtNodes/internal/DataFlowGraphModel.hpp | 5 ++ include/QtNodes/internal/GraphicsView.hpp | 6 ++ src/DataFlowGraphModel.cpp | 39 +++++++++++- src/GraphicsView.cpp | 63 +++++++++++++++++++ 4 files changed, 111 insertions(+), 2 deletions(-) diff --git a/include/QtNodes/internal/DataFlowGraphModel.hpp b/include/QtNodes/internal/DataFlowGraphModel.hpp index ff93c6eb..fcde1482 100644 --- a/include/QtNodes/internal/DataFlowGraphModel.hpp +++ b/include/QtNodes/internal/DataFlowGraphModel.hpp @@ -11,6 +11,8 @@ #include #include +#include +#include namespace QtNodes { @@ -137,6 +139,9 @@ private Q_SLOTS: std::unordered_set _connectivity; mutable std::unordered_map _nodeGeometryData; + + std::unordered_map _labels; + std::unordered_map _labelsVisible; }; } // namespace QtNodes diff --git a/include/QtNodes/internal/GraphicsView.hpp b/include/QtNodes/internal/GraphicsView.hpp index 52068129..c8b0e2ed 100644 --- a/include/QtNodes/internal/GraphicsView.hpp +++ b/include/QtNodes/internal/GraphicsView.hpp @@ -3,6 +3,9 @@ #include #include "Export.hpp" +#include "Definitions.hpp" + +class QLineEdit; namespace QtNodes { @@ -93,5 +96,8 @@ public Q_SLOTS: QPointF _clickPos; ScaleRange _scaleRange; + + QLineEdit *_labelEdit = nullptr; + NodeId _editingNodeId = InvalidNodeId; }; } // namespace QtNodes diff --git a/src/DataFlowGraphModel.cpp b/src/DataFlowGraphModel.cpp index 7fd9ecef..842dac0d 100644 --- a/src/DataFlowGraphModel.cpp +++ b/src/DataFlowGraphModel.cpp @@ -98,6 +98,9 @@ NodeId DataFlowGraphModel::addNode(QString const nodeType) _models[newId] = std::move(model); + _labels[newId] = _models[newId]->label(); + _labelsVisible[newId] = _models[newId]->labelVisible(); + Q_EMIT nodeCreated(newId); return newId; @@ -296,11 +299,11 @@ QVariant DataFlowGraphModel::nodeData(NodeId nodeId, NodeRole role) const } break; case NodeRole::LabelVisible: - result = model->labelVisible(); + result = _labelsVisible[nodeId]; break; case NodeRole::Label: - result = model->label(); + result = _labels[nodeId]; break; } @@ -361,6 +364,28 @@ bool DataFlowGraphModel::setNodeData(NodeId nodeId, NodeRole role, QVariant valu case NodeRole::Widget: break; + + case NodeRole::ValidationState: { + if (value.canConvert()) { + auto state = value.value(); + if (auto node = delegateModel(nodeId); node != nullptr) { + node->setValidatonState(state); + } + } + Q_EMIT nodeUpdated(nodeId); + } break; + + case NodeRole::LabelVisible: + _labelsVisible[nodeId] = value.toBool(); + Q_EMIT nodeUpdated(nodeId); + result = true; + break; + + case NodeRole::Label: + _labels[nodeId] = value.toString(); + Q_EMIT nodeUpdated(nodeId); + result = true; + break; } return result; @@ -468,6 +493,8 @@ bool DataFlowGraphModel::deleteNode(NodeId const nodeId) } _nodeGeometryData.erase(nodeId); + _labels.erase(nodeId); + _labelsVisible.erase(nodeId); _models.erase(nodeId); Q_EMIT nodeDeleted(nodeId); @@ -483,6 +510,9 @@ QJsonObject DataFlowGraphModel::saveNode(NodeId const nodeId) const nodeJson["internal-data"] = _models.at(nodeId)->save(); + nodeJson["label"] = _labels.at(nodeId); + nodeJson["labelVisible"] = _labelsVisible.at(nodeId); + { QPointF const pos = nodeData(nodeId, NodeRole::Position).value(); @@ -575,6 +605,11 @@ void DataFlowGraphModel::loadNode(QJsonObject const &nodeJson) setNodeData(restoredNodeId, NodeRole::Position, pos); + _labels[restoredNodeId] = nodeJson["label"].toString(model->label()); + _labelsVisible[restoredNodeId] = nodeJson.contains("labelVisible") + ? nodeJson["labelVisible"].toBool() + : model->labelVisible(); + _models[restoredNodeId]->load(internalDataJson); } else { throw std::logic_error(std::string("No registered model with name ") diff --git a/src/GraphicsView.cpp b/src/GraphicsView.cpp index cbd87cc8..33d6b2f2 100644 --- a/src/GraphicsView.cpp +++ b/src/GraphicsView.cpp @@ -5,6 +5,9 @@ #include "NodeGraphicsObject.hpp" #include "StyleCollection.hpp" #include "UndoCommands.hpp" +#include "Definitions.hpp" + +#include #include @@ -300,6 +303,66 @@ void GraphicsView::onPasteObjects() void GraphicsView::keyPressEvent(QKeyEvent *event) { switch (event->key()) { + case Qt::Key_F2: { + BasicGraphicsScene *sc = nodeScene(); + if (sc) { + QList items = sc->selectedItems(); + NodeGraphicsObject *ngo = nullptr; + for (QGraphicsItem *it : items) { + ngo = qgraphicsitem_cast(it); + if (ngo) + break; + } + if (ngo) { + if (!_labelEdit) { + _labelEdit = new QLineEdit(this); + connect(_labelEdit, &QLineEdit::editingFinished, [this]() { + if (_editingNodeId != InvalidNodeId) { + nodeScene()->graphModel().setNodeData( + _editingNodeId, + NodeRole::LabelVisible, + true); + nodeScene()->graphModel().setNodeData( + _editingNodeId, + NodeRole::Label, + _labelEdit->text()); + } + _labelEdit->hide(); + _editingNodeId = InvalidNodeId; + }); + } + + _editingNodeId = ngo->nodeId(); + + // Ensure label is visible for geometry calculations + sc->graphModel().setNodeData(_editingNodeId, + NodeRole::LabelVisible, + true); + + AbstractNodeGeometry &geom = sc->nodeGeometry(); + QPointF labelPos = geom.labelPosition(_editingNodeId); + QPointF scenePos = ngo->mapToScene(labelPos); + QSize sz = _labelEdit->sizeHint(); + QPoint viewPos = mapFromScene(scenePos).toPoint(); + _labelEdit->move(viewPos.x() - sz.width() / 2, + viewPos.y() - sz.height() / 2); + bool visible = sc->graphModel() + .nodeData(_editingNodeId, NodeRole::LabelVisible) + .toBool(); + QString current = sc->graphModel() + .nodeData(_editingNodeId, NodeRole::Label) + .toString(); + if (!visible && current.isEmpty()) + _labelEdit->clear(); + else + _labelEdit->setText(current); + _labelEdit->resize(sz); + _labelEdit->show(); + _labelEdit->setFocus(); + return; + } + } + } break; case Qt::Key_Shift: setDragMode(QGraphicsView::RubberBandDrag); break; From a009f677bd42083f5537a47009dfd15a6ed9cd08 Mon Sep 17 00:00:00 2001 From: g-abilio Date: Fri, 25 Jul 2025 20:00:54 -0300 Subject: [PATCH 07/26] add correct caption dynamic, as well as labelEdit max size --- .../QtNodes/internal/NodeDelegateModel.hpp | 2 +- src/DataFlowGraphModel.cpp | 4 +- src/DefaultNodePainter.cpp | 5 ++- src/GraphicsView.cpp | 43 +++++++++---------- 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/include/QtNodes/internal/NodeDelegateModel.hpp b/include/QtNodes/internal/NodeDelegateModel.hpp index dbcbeea5..ac67315b 100644 --- a/include/QtNodes/internal/NodeDelegateModel.hpp +++ b/include/QtNodes/internal/NodeDelegateModel.hpp @@ -63,7 +63,7 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel virtual bool portCaptionVisible(PortType, PortIndex) const { return false; } /// Nicknames can be assigned to nodes and shown in GUI - virtual QString label() const { return QString("Teste"); } + virtual QString label() const { return QString(); } /// It is possible to hide the nickname in GUI virtual bool labelVisible() const { return true; } diff --git a/src/DataFlowGraphModel.cpp b/src/DataFlowGraphModel.cpp index 842dac0d..386fc479 100644 --- a/src/DataFlowGraphModel.cpp +++ b/src/DataFlowGraphModel.cpp @@ -299,11 +299,11 @@ QVariant DataFlowGraphModel::nodeData(NodeId nodeId, NodeRole role) const } break; case NodeRole::LabelVisible: - result = _labelsVisible[nodeId]; + result = _labelsVisible.at(nodeId); break; case NodeRole::Label: - result = _labels[nodeId]; + result = _labels.at(nodeId); break; } diff --git a/src/DefaultNodePainter.cpp b/src/DefaultNodePainter.cpp index 2bd785d9..b271419c 100644 --- a/src/DefaultNodePainter.cpp +++ b/src/DefaultNodePainter.cpp @@ -201,11 +201,12 @@ void DefaultNodePainter::drawNodeCaption(QPainter *painter, NodeGraphicsObject & if (!model.nodeData(nodeId, NodeRole::CaptionVisible).toBool()) return; + QString const nickname = model.nodeData(nodeId, NodeRole::Label).toString(); QString const name = model.nodeData(nodeId, NodeRole::Caption).toString(); QFont f = painter->font(); - f.setBold(!model.nodeData(nodeId, NodeRole::LabelVisible).toBool()); - f.setItalic(model.nodeData(nodeId, NodeRole::LabelVisible).toBool()); + f.setBold(nickname.isEmpty()); + f.setItalic(!nickname.isEmpty()); QPointF position = geometry.captionPosition(nodeId); diff --git a/src/GraphicsView.cpp b/src/GraphicsView.cpp index 33d6b2f2..fbb1484d 100644 --- a/src/GraphicsView.cpp +++ b/src/GraphicsView.cpp @@ -2,10 +2,10 @@ #include "BasicGraphicsScene.hpp" #include "ConnectionGraphicsObject.hpp" +#include "Definitions.hpp" #include "NodeGraphicsObject.hpp" #include "StyleCollection.hpp" #include "UndoCommands.hpp" -#include "Definitions.hpp" #include @@ -305,28 +305,32 @@ void GraphicsView::keyPressEvent(QKeyEvent *event) switch (event->key()) { case Qt::Key_F2: { BasicGraphicsScene *sc = nodeScene(); + if (sc) { QList items = sc->selectedItems(); NodeGraphicsObject *ngo = nullptr; for (QGraphicsItem *it : items) { ngo = qgraphicsitem_cast(it); + if (ngo) break; } + if (ngo) { if (!_labelEdit) { _labelEdit = new QLineEdit(this); + _labelEdit->setMaxLength(32); + connect(_labelEdit, &QLineEdit::editingFinished, [this]() { if (_editingNodeId != InvalidNodeId) { - nodeScene()->graphModel().setNodeData( - _editingNodeId, - NodeRole::LabelVisible, - true); - nodeScene()->graphModel().setNodeData( - _editingNodeId, - NodeRole::Label, - _labelEdit->text()); + nodeScene()->graphModel().setNodeData(_editingNodeId, + NodeRole::LabelVisible, + true); + nodeScene()->graphModel().setNodeData(_editingNodeId, + NodeRole::Label, + _labelEdit->text()); } + _labelEdit->hide(); _editingNodeId = InvalidNodeId; }); @@ -334,24 +338,19 @@ void GraphicsView::keyPressEvent(QKeyEvent *event) _editingNodeId = ngo->nodeId(); - // Ensure label is visible for geometry calculations - sc->graphModel().setNodeData(_editingNodeId, - NodeRole::LabelVisible, - true); + sc->graphModel().setNodeData(_editingNodeId, NodeRole::LabelVisible, true); AbstractNodeGeometry &geom = sc->nodeGeometry(); QPointF labelPos = geom.labelPosition(_editingNodeId); QPointF scenePos = ngo->mapToScene(labelPos); QSize sz = _labelEdit->sizeHint(); - QPoint viewPos = mapFromScene(scenePos).toPoint(); - _labelEdit->move(viewPos.x() - sz.width() / 2, - viewPos.y() - sz.height() / 2); - bool visible = sc->graphModel() - .nodeData(_editingNodeId, NodeRole::LabelVisible) - .toBool(); - QString current = sc->graphModel() - .nodeData(_editingNodeId, NodeRole::Label) - .toString(); + QPoint viewPos = mapFromScene(scenePos); + _labelEdit->move(viewPos.x() - sz.width() / 2, viewPos.y() - sz.height() / 2); + bool visible + = sc->graphModel().nodeData(_editingNodeId, NodeRole::LabelVisible).toBool(); + QString current + = sc->graphModel().nodeData(_editingNodeId, NodeRole::Label).toString(); + if (!visible && current.isEmpty()) _labelEdit->clear(); else From 37833236502100383864392d047a5b04315a0578 Mon Sep 17 00:00:00 2001 From: g-abilio Date: Mon, 4 Aug 2025 17:18:19 -0300 Subject: [PATCH 08/26] fix alignment issues between nickname and node caption --- .../QtNodes/internal/AbstractNodeGeometry.hpp | 8 +++---- .../DefaultHorizontalNodeGeometry.hpp | 2 ++ .../internal/DefaultVerticalNodeGeometry.hpp | 2 ++ src/DefaultNodePainter.cpp | 22 +++++++++++++++++-- 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/include/QtNodes/internal/AbstractNodeGeometry.hpp b/include/QtNodes/internal/AbstractNodeGeometry.hpp index 5a4719d1..9a134191 100644 --- a/include/QtNodes/internal/AbstractNodeGeometry.hpp +++ b/include/QtNodes/internal/AbstractNodeGeometry.hpp @@ -36,8 +36,7 @@ class NODE_EDITOR_PUBLIC AbstractNodeGeometry /// Port position in node's coordinate system. virtual QPointF portPosition(NodeId const nodeId, PortType const portType, - PortIndex const index) const - = 0; + PortIndex const index) const = 0; /// A convenience function using the `portPosition` and a given transformation. virtual QPointF portScenePosition(NodeId const nodeId, @@ -48,8 +47,7 @@ class NODE_EDITOR_PUBLIC AbstractNodeGeometry /// Defines where to draw port label. The point corresponds to a font baseline. virtual QPointF portTextPosition(NodeId const nodeId, PortType const portType, - PortIndex const portIndex) const - = 0; + PortIndex const portIndex) const = 0; /** * Defines where to start drawing the caption. The point corresponds to a font @@ -78,6 +76,8 @@ class NODE_EDITOR_PUBLIC AbstractNodeGeometry virtual QRect resizeHandleRect(NodeId const nodeId) const = 0; + virtual int getPortSpasing() = 0; + protected: AbstractGraphModel &_graphModel; }; diff --git a/include/QtNodes/internal/DefaultHorizontalNodeGeometry.hpp b/include/QtNodes/internal/DefaultHorizontalNodeGeometry.hpp index be985e12..723f8a20 100644 --- a/include/QtNodes/internal/DefaultHorizontalNodeGeometry.hpp +++ b/include/QtNodes/internal/DefaultHorizontalNodeGeometry.hpp @@ -41,6 +41,8 @@ class NODE_EDITOR_PUBLIC DefaultHorizontalNodeGeometry : public AbstractNodeGeom QRect resizeHandleRect(NodeId const nodeId) const override; + int getPortSpasing() override { return _portSpasing; } + private: QRectF portTextRect(NodeId const nodeId, PortType const portType, diff --git a/include/QtNodes/internal/DefaultVerticalNodeGeometry.hpp b/include/QtNodes/internal/DefaultVerticalNodeGeometry.hpp index f035f8bc..3fb3b816 100644 --- a/include/QtNodes/internal/DefaultVerticalNodeGeometry.hpp +++ b/include/QtNodes/internal/DefaultVerticalNodeGeometry.hpp @@ -41,6 +41,8 @@ class NODE_EDITOR_PUBLIC DefaultVerticalNodeGeometry : public AbstractNodeGeomet QRect resizeHandleRect(NodeId const nodeId) const override; + int getPortSpasing() override { return _portSpasing; } + private: QRectF portTextRect(NodeId const nodeId, PortType const portType, diff --git a/src/DefaultNodePainter.cpp b/src/DefaultNodePainter.cpp index b271419c..f5885308 100644 --- a/src/DefaultNodePainter.cpp +++ b/src/DefaultNodePainter.cpp @@ -208,7 +208,14 @@ void DefaultNodePainter::drawNodeCaption(QPainter *painter, NodeGraphicsObject & f.setBold(nickname.isEmpty()); f.setItalic(!nickname.isEmpty()); - QPointF position = geometry.captionPosition(nodeId); + QFontMetricsF metrics(f); + + QRectF bounding = metrics.boundingRect(name); + QRectF capRect = geometry.captionRect(nodeId); + QPointF capPos = geometry.captionPosition(nodeId); + double centerX = capPos.x() + capRect.width() / 2.0; + + QPointF position(centerX - bounding.width() / 2.0, capPos.y()); QJsonDocument json = QJsonDocument::fromVariant(model.nodeData(nodeId, NodeRole::Style)); NodeStyle nodeStyle(json.object()); @@ -235,8 +242,19 @@ void DefaultNodePainter::drawNodeLabel(QPainter *painter, NodeGraphicsObject &ng QFont f = painter->font(); f.setBold(true); + f.setItalic(false); + + QFontMetricsF metrics(f); + + QRectF bounding = metrics.boundingRect(nickname); + QRectF capRect = geometry.captionRect(nodeId); + QPointF capPos = geometry.captionPosition(nodeId); + double centerX = capPos.x() + capRect.width() / 2.0; + + double textHeight = metrics.height(); + double y = capPos.y() - textHeight - 2.0; - QPointF position = geometry.labelPosition(nodeId); + QPointF position(centerX - bounding.width() / 2.0, y); QJsonDocument json = QJsonDocument::fromVariant(model.nodeData(nodeId, NodeRole::Style)); NodeStyle nodeStyle(json.object()); From 1ac664a2ef0808caef6ca2b732efbb81933e19f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Taiguara=20Tupinamb=C3=A1s?= Date: Thu, 11 Sep 2025 14:00:37 -0300 Subject: [PATCH 09/26] solves conflict --- .../QtNodes/internal/NodeDelegateModel.hpp | 8 +- resources/info-tooltip.svg | 4 + src/DataFlowGraphModel.cpp | 8 +- src/DefaultNodePainter.cpp | 83 +++++++++++++++++-- src/NodeGraphicsObject.cpp | 5 +- 5 files changed, 90 insertions(+), 18 deletions(-) create mode 100644 resources/info-tooltip.svg diff --git a/include/QtNodes/internal/NodeDelegateModel.hpp b/include/QtNodes/internal/NodeDelegateModel.hpp index ac67315b..f28714a6 100644 --- a/include/QtNodes/internal/NodeDelegateModel.hpp +++ b/include/QtNodes/internal/NodeDelegateModel.hpp @@ -62,19 +62,21 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel /// It is possible to hide port caption in GUI virtual bool portCaptionVisible(PortType, PortIndex) const { return false; } + /// Validation State will default to Valid, but you can manipulate it by overriding in an inherited class + virtual NodeValidationState validationState() const { return _nodeValidationState; } + /// Nicknames can be assigned to nodes and shown in GUI virtual QString label() const { return QString(); } /// It is possible to hide the nickname in GUI virtual bool labelVisible() const { return true; } - /// Validation State will default to Valid, but you can manipulate it by overriding in an inherited class - virtual NodeValidationState validationState() const { return _nodeValidationState; } - public: QJsonObject save() const override; void load(QJsonObject const &) override; + void setValidatonState(const NodeValidationState &validationState); + virtual unsigned int nPorts(PortType portType) const = 0; virtual NodeDataType dataType(PortType portType, PortIndex portIndex) const = 0; diff --git a/resources/info-tooltip.svg b/resources/info-tooltip.svg new file mode 100644 index 00000000..bab5f333 --- /dev/null +++ b/resources/info-tooltip.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/DataFlowGraphModel.cpp b/src/DataFlowGraphModel.cpp index 386fc479..5f8a8ed2 100644 --- a/src/DataFlowGraphModel.cpp +++ b/src/DataFlowGraphModel.cpp @@ -375,17 +375,17 @@ bool DataFlowGraphModel::setNodeData(NodeId nodeId, NodeRole role, QVariant valu Q_EMIT nodeUpdated(nodeId); } break; - case NodeRole::LabelVisible: + case NodeRole::LabelVisible: { _labelsVisible[nodeId] = value.toBool(); Q_EMIT nodeUpdated(nodeId); result = true; - break; + } break; - case NodeRole::Label: + case NodeRole::Label: { _labels[nodeId] = value.toString(); Q_EMIT nodeUpdated(nodeId); result = true; - break; + } break; } return result; diff --git a/src/DefaultNodePainter.cpp b/src/DefaultNodePainter.cpp index f5885308..b6465e7b 100644 --- a/src/DefaultNodePainter.cpp +++ b/src/DefaultNodePainter.cpp @@ -52,18 +52,37 @@ void DefaultNodePainter::drawNodeRect(QPainter *painter, NodeGraphicsObject &ngo NodeStyle nodeStyle(json.object()); - auto color = ngo.isSelected() ? nodeStyle.SelectedBoundaryColor : nodeStyle.NormalBoundaryColor; - - if (ngo.nodeState().hovered()) { - QPen p(color, nodeStyle.HoveredPenWidth); - painter->setPen(p); - } else { - QPen p(color, nodeStyle.PenWidth); - painter->setPen(p); + QVariant var = model.nodeData(nodeId, NodeRole::ValidationState); + + QColor color = ngo.isSelected() ? nodeStyle.SelectedBoundaryColor + : nodeStyle.NormalBoundaryColor; + + auto validationState = NodeValidationState::State::Valid; + if (var.canConvert()) { + auto state = var.value(); + validationState = state._state; + switch (validationState) { + case NodeValidationState::State::Error: + color = nodeStyle.ErrorColor; + break; + case NodeValidationState::State::Warning: + color = nodeStyle.WarningColor; + break; + default: + break; + } } - QLinearGradient gradient(QPointF(0.0, 0.0), QPointF(2.0, size.height())); + float penWidth = ngo.nodeState().hovered() ? nodeStyle.HoveredPenWidth : nodeStyle.PenWidth; + if (validationState != NodeValidationState::State::Valid) { + float factor = (validationState == NodeValidationState::State::Error) ? 3.0f : 2.0f; + penWidth *= factor; + } + + 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); gradient.setColorAt(0.90, nodeStyle.GradientColor2); @@ -320,4 +339,50 @@ void DefaultNodePainter::drawResizeRect(QPainter *painter, NodeGraphicsObject &n } } +void DefaultNodePainter::drawValidationIcon(QPainter *painter, NodeGraphicsObject &ngo) const +{ + AbstractGraphModel &model = ngo.graphModel(); + NodeId const nodeId = ngo.nodeId(); + AbstractNodeGeometry &geometry = ngo.nodeScene()->nodeGeometry(); + + QVariant var = model.nodeData(nodeId, NodeRole::ValidationState); + if (!var.canConvert()) + return; + + auto state = var.value(); + if (state._state == NodeValidationState::State::Valid) + return; + + QJsonDocument json = QJsonDocument::fromVariant(model.nodeData(nodeId, NodeRole::Style)); + NodeStyle nodeStyle(json.object()); + + QSize size = geometry.size(nodeId); + + QIcon icon(":/info-tooltip.svg"); + QSize iconSize(16, 16); + QPixmap pixmap = icon.pixmap(iconSize); + + QColor color = (state._state == NodeValidationState::State::Error) ? nodeStyle.ErrorColor + : nodeStyle.WarningColor; + + QPointF center(size.width(), 0.0); + center += QPointF(iconSize.width() / 2.0, -iconSize.height() / 2.0); + + painter->save(); + + // Draw a colored circle behind the icon to highlight validation issues + painter->setPen(Qt::NoPen); + 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); + + painter->restore(); +} } // namespace QtNodes diff --git a/src/NodeGraphicsObject.cpp b/src/NodeGraphicsObject.cpp index d3ca03b4..8959f4cf 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -177,8 +177,9 @@ QVariant NodeGraphicsObject::itemChange(GraphicsItemChange change, const QVarian void NodeGraphicsObject::mousePressEvent(QGraphicsSceneMouseEvent *event) { - //if (_nodeState.locked()) - //return; + if (graphModel().nodeFlags(_nodeId) & NodeFlag::Locked) { + return; + } AbstractNodeGeometry &geometry = nodeScene()->nodeGeometry(); From 88d9fd487036d9c9c59fa87126cc5c712a40b84a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Taiguara=20Tupinamb=C3=A1s?= Date: Thu, 11 Sep 2025 14:06:25 -0300 Subject: [PATCH 10/26] revert workflows change --- .github/workflows/cmake_build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cmake_build.yml b/.github/workflows/cmake_build.yml index 766fc2ef..b513864c 100644 --- a/.github/workflows/cmake_build.yml +++ b/.github/workflows/cmake_build.yml @@ -30,9 +30,9 @@ jobs: - toolchain: linux-gcc os: ubuntu-22.04 compiler: gcc - qt_version: "6.3.0" + qt_version: "5.15.2" modules: "" - use_qt6: "ON" + use_qt6: "OFF" - toolchain: macos-clang os: macos-latest From b1714b2fd970df00c737237f00f052fa680d9d80 Mon Sep 17 00:00:00 2001 From: g-abilio Date: Fri, 12 Sep 2025 14:46:24 -0300 Subject: [PATCH 11/26] fix segfault detected in dataflowgraphmodel tests --- src/DataFlowGraphModel.cpp | 69 ++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 37 deletions(-) diff --git a/src/DataFlowGraphModel.cpp b/src/DataFlowGraphModel.cpp index 5f8a8ed2..3f53904a 100644 --- a/src/DataFlowGraphModel.cpp +++ b/src/DataFlowGraphModel.cpp @@ -63,50 +63,44 @@ NodeId DataFlowGraphModel::addNode(QString const nodeType) { std::unique_ptr model = _registry->create(nodeType); - if (model) { - NodeId newId = newNodeId(); + if (!model) + return InvalidNodeId; - connect(model.get(), - &NodeDelegateModel::dataUpdated, - [newId, this](PortIndex const portIndex) { - onOutPortDataUpdated(newId, portIndex); - }); + NodeId newId = newNodeId(); - connect(model.get(), - &NodeDelegateModel::portsAboutToBeDeleted, - this, - [newId, this](PortType const portType, PortIndex const first, PortIndex const last) { - portsAboutToBeDeleted(newId, portType, first, last); - }); + connect(model.get(), &NodeDelegateModel::dataUpdated, [newId, this](PortIndex const portIndex) { + onOutPortDataUpdated(newId, portIndex); + }); - connect(model.get(), - &NodeDelegateModel::portsDeleted, - this, - &DataFlowGraphModel::portsDeleted); + connect(model.get(), + &NodeDelegateModel::portsAboutToBeDeleted, + this, + [newId, this](PortType const portType, PortIndex const first, PortIndex const last) { + portsAboutToBeDeleted(newId, portType, first, last); + }); - connect(model.get(), - &NodeDelegateModel::portsAboutToBeInserted, - this, - [newId, this](PortType const portType, PortIndex const first, PortIndex const last) { - portsAboutToBeInserted(newId, portType, first, last); - }); + connect(model.get(), &NodeDelegateModel::portsDeleted, this, &DataFlowGraphModel::portsDeleted); - connect(model.get(), - &NodeDelegateModel::portsInserted, - this, - &DataFlowGraphModel::portsInserted); + connect(model.get(), + &NodeDelegateModel::portsAboutToBeInserted, + this, + [newId, this](PortType const portType, PortIndex const first, PortIndex const last) { + portsAboutToBeInserted(newId, portType, first, last); + }); - _models[newId] = std::move(model); + connect(model.get(), + &NodeDelegateModel::portsInserted, + this, + &DataFlowGraphModel::portsInserted); - _labels[newId] = _models[newId]->label(); - _labelsVisible[newId] = _models[newId]->labelVisible(); + _models[newId] = std::move(model); - Q_EMIT nodeCreated(newId); + _labels[newId] = _models[newId]->label(); + _labelsVisible[newId] = _models[newId]->labelVisible(); - return newId; - } + Q_EMIT nodeCreated(newId); - return InvalidNodeId; + return newId; } bool DataFlowGraphModel::connectionPossible(ConnectionId const connectionId) const @@ -605,12 +599,13 @@ void DataFlowGraphModel::loadNode(QJsonObject const &nodeJson) setNodeData(restoredNodeId, NodeRole::Position, pos); - _labels[restoredNodeId] = nodeJson["label"].toString(model->label()); + auto *restoredModel = _models[restoredNodeId].get(); + _labels[restoredNodeId] = nodeJson["label"].toString(restoredModel->label()); _labelsVisible[restoredNodeId] = nodeJson.contains("labelVisible") ? nodeJson["labelVisible"].toBool() - : model->labelVisible(); + : restoredModel->labelVisible(); - _models[restoredNodeId]->load(internalDataJson); + restoredModel->load(internalDataJson); } else { throw std::logic_error(std::string("No registered model with name ") + delegateModelName.toLocal8Bit().data()); From 206cf0922ccd993b2d8da7a560348c19eeb95385 Mon Sep 17 00:00:00 2001 From: g-abilio Date: Thu, 18 Sep 2025 11:33:31 -0300 Subject: [PATCH 12/26] produces optional nickname structure and adds example --- .../calculator/LongProcessingRandomNumber.hpp | 64 +++++++++++++++++++ examples/calculator/headless_main.cpp | 3 + examples/calculator/main.cpp | 3 + include/QtNodes/internal/Definitions.hpp | 1 + include/QtNodes/internal/GraphicsView.hpp | 2 +- .../QtNodes/internal/NodeDelegateModel.hpp | 3 + src/DataFlowGraphModel.cpp | 7 ++ src/GraphicsView.cpp | 10 ++- 8 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 examples/calculator/LongProcessingRandomNumber.hpp diff --git a/examples/calculator/LongProcessingRandomNumber.hpp b/examples/calculator/LongProcessingRandomNumber.hpp new file mode 100644 index 00000000..365ca9cb --- /dev/null +++ b/examples/calculator/LongProcessingRandomNumber.hpp @@ -0,0 +1,64 @@ +#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: + virtual ~RandomNumberModel() {} + +public: + QString caption() const override { return QStringLiteral("Random Number"); } + + QString name() const override { return QStringLiteral("Random Number"); } + + bool labelEditable() const override { return true; } + +private: + void compute() override + { + 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(); + + double minVal = std::min(a, b); + double maxVal = std::max(a, b); + + std::random_device rd; + std::mt19937 gen(rd()); + + std::uniform_real_distribution dist(minVal, maxVal); + + double randomValue = dist(gen); + + _result = std::make_shared(randomValue); + } else { + _result.reset(); + } + + Q_EMIT dataUpdated(outPortIndex); + } + }); + } +}; diff --git a/examples/calculator/headless_main.cpp b/examples/calculator/headless_main.cpp index e4dafa02..89103a4f 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 980c0f23..4d2504f0 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/Definitions.hpp b/include/QtNodes/internal/Definitions.hpp index 41e63444..a72f4e6e 100644 --- a/include/QtNodes/internal/Definitions.hpp +++ b/include/QtNodes/internal/Definitions.hpp @@ -36,6 +36,7 @@ Q_NAMESPACE_EXPORT(NODE_EDITOR_PUBLIC) ValidationState = 11, ///< Enum NodeValidationState of the node LabelVisible = 12, ///< `bool` for label visibility. Label = 13, ///< `QString` for node label. + LabelEditable = 14, ///< `bool` to indicate label editing support. }; Q_ENUM_NS(NodeRole) diff --git a/include/QtNodes/internal/GraphicsView.hpp b/include/QtNodes/internal/GraphicsView.hpp index c8b0e2ed..1fbe5a89 100644 --- a/include/QtNodes/internal/GraphicsView.hpp +++ b/include/QtNodes/internal/GraphicsView.hpp @@ -2,8 +2,8 @@ #include -#include "Export.hpp" #include "Definitions.hpp" +#include "Export.hpp" class QLineEdit; diff --git a/include/QtNodes/internal/NodeDelegateModel.hpp b/include/QtNodes/internal/NodeDelegateModel.hpp index f28714a6..7f4aa08b 100644 --- a/include/QtNodes/internal/NodeDelegateModel.hpp +++ b/include/QtNodes/internal/NodeDelegateModel.hpp @@ -71,6 +71,9 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel /// It is possible to hide the nickname in GUI virtual bool labelVisible() const { return true; } + /// Controls whether the label can be edited or not + virtual bool labelEditable() const { return false; } + public: QJsonObject save() const override; void load(QJsonObject const &) override; diff --git a/src/DataFlowGraphModel.cpp b/src/DataFlowGraphModel.cpp index 3f53904a..7edd3ff1 100644 --- a/src/DataFlowGraphModel.cpp +++ b/src/DataFlowGraphModel.cpp @@ -299,6 +299,10 @@ QVariant DataFlowGraphModel::nodeData(NodeId nodeId, NodeRole role) const case NodeRole::Label: result = _labels.at(nodeId); break; + + case NodeRole::LabelEditable: + result = model->labelEditable(); + break; } return result; @@ -380,6 +384,9 @@ bool DataFlowGraphModel::setNodeData(NodeId nodeId, NodeRole role, QVariant valu Q_EMIT nodeUpdated(nodeId); result = true; } break; + + case NodeRole::LabelEditable: + break; } return result; diff --git a/src/GraphicsView.cpp b/src/GraphicsView.cpp index fbb1484d..cd19581e 100644 --- a/src/GraphicsView.cpp +++ b/src/GraphicsView.cpp @@ -317,6 +317,12 @@ void GraphicsView::keyPressEvent(QKeyEvent *event) } if (ngo) { + bool const labelEditable + = sc->graphModel().nodeData(ngo->nodeId(), NodeRole::LabelEditable).toBool(); + + if (!labelEditable) + break; + if (!_labelEdit) { _labelEdit = new QLineEdit(this); _labelEdit->setMaxLength(32); @@ -361,7 +367,9 @@ void GraphicsView::keyPressEvent(QKeyEvent *event) return; } } - } break; + } + + break; case Qt::Key_Shift: setDragMode(QGraphicsView::RubberBandDrag); break; From 540046dbe90b6e97630b2ae80f40c5e87e309d25 Mon Sep 17 00:00:00 2001 From: g-abilio Date: Thu, 18 Sep 2025 11:37:14 -0300 Subject: [PATCH 13/26] fix typo in spacing method and attribute --- .../QtNodes/internal/AbstractNodeGeometry.hpp | 2 +- .../DefaultHorizontalNodeGeometry.hpp | 4 +- .../internal/DefaultVerticalNodeGeometry.hpp | 4 +- src/DefaultHorizontalNodeGeometry.cpp | 46 +++++++++---------- src/DefaultVerticalNodeGeometry.cpp | 42 ++++++++--------- 5 files changed, 49 insertions(+), 49 deletions(-) diff --git a/include/QtNodes/internal/AbstractNodeGeometry.hpp b/include/QtNodes/internal/AbstractNodeGeometry.hpp index 9a134191..c3be9630 100644 --- a/include/QtNodes/internal/AbstractNodeGeometry.hpp +++ b/include/QtNodes/internal/AbstractNodeGeometry.hpp @@ -76,7 +76,7 @@ class NODE_EDITOR_PUBLIC AbstractNodeGeometry virtual QRect resizeHandleRect(NodeId const nodeId) const = 0; - virtual int getPortSpasing() = 0; + virtual int getPortSpacing() = 0; protected: AbstractGraphModel &_graphModel; diff --git a/include/QtNodes/internal/DefaultHorizontalNodeGeometry.hpp b/include/QtNodes/internal/DefaultHorizontalNodeGeometry.hpp index 723f8a20..b2655528 100644 --- a/include/QtNodes/internal/DefaultHorizontalNodeGeometry.hpp +++ b/include/QtNodes/internal/DefaultHorizontalNodeGeometry.hpp @@ -41,7 +41,7 @@ class NODE_EDITOR_PUBLIC DefaultHorizontalNodeGeometry : public AbstractNodeGeom QRect resizeHandleRect(NodeId const nodeId) const override; - int getPortSpasing() override { return _portSpasing; } + int getPortSpacing() override { return _portSpacing; } private: QRectF portTextRect(NodeId const nodeId, @@ -59,7 +59,7 @@ class NODE_EDITOR_PUBLIC DefaultHorizontalNodeGeometry : public AbstractNodeGeom // constness of the Node. mutable unsigned int _portSize; - unsigned int _portSpasing; + unsigned int _portSpacing; mutable QFontMetrics _fontMetrics; mutable QFontMetrics _boldFontMetrics; }; diff --git a/include/QtNodes/internal/DefaultVerticalNodeGeometry.hpp b/include/QtNodes/internal/DefaultVerticalNodeGeometry.hpp index 3fb3b816..d2063656 100644 --- a/include/QtNodes/internal/DefaultVerticalNodeGeometry.hpp +++ b/include/QtNodes/internal/DefaultVerticalNodeGeometry.hpp @@ -41,7 +41,7 @@ class NODE_EDITOR_PUBLIC DefaultVerticalNodeGeometry : public AbstractNodeGeomet QRect resizeHandleRect(NodeId const nodeId) const override; - int getPortSpasing() override { return _portSpasing; } + int getPortSpacing() override { return _portSpacing; } private: QRectF portTextRect(NodeId const nodeId, @@ -60,7 +60,7 @@ class NODE_EDITOR_PUBLIC DefaultVerticalNodeGeometry : public AbstractNodeGeomet // constness of the Node. mutable unsigned int _portSize; - unsigned int _portSpasing; + unsigned int _portSpacing; mutable QFontMetrics _fontMetrics; mutable QFontMetrics _boldFontMetrics; }; diff --git a/src/DefaultHorizontalNodeGeometry.cpp b/src/DefaultHorizontalNodeGeometry.cpp index 682a7e67..cb5e4207 100644 --- a/src/DefaultHorizontalNodeGeometry.cpp +++ b/src/DefaultHorizontalNodeGeometry.cpp @@ -12,7 +12,7 @@ namespace QtNodes { DefaultHorizontalNodeGeometry::DefaultHorizontalNodeGeometry(AbstractGraphModel &graphModel) : AbstractNodeGeometry(graphModel) , _portSize(20) - , _portSpasing(10) + , _portSpacing(10) , _fontMetrics(QFont()) , _boldFontMetrics(QFont()) { @@ -27,7 +27,7 @@ QRectF DefaultHorizontalNodeGeometry::boundingRect(NodeId const nodeId) const { QSize s = size(nodeId); - qreal marginSize = 2.0 * _portSpasing; + qreal marginSize = 2.0 * _portSpacing; QMargins margins(marginSize, marginSize, marginSize, marginSize); QRectF r(QPointF(0, 0), s); @@ -54,16 +54,16 @@ void DefaultHorizontalNodeGeometry::recomputeSize(NodeId const nodeId) const height += capRect.height(); if (!lblRect.isNull()) { height += lblRect.height(); - height += _portSpasing / 2; + height += _portSpacing / 2; } - height += _portSpasing; // space above caption - height += _portSpasing; // space below caption + height += _portSpacing; // space above caption + height += _portSpacing; // space below caption unsigned int inPortWidth = maxPortsTextAdvance(nodeId, PortType::In); unsigned int outPortWidth = maxPortsTextAdvance(nodeId, PortType::Out); - unsigned int width = inPortWidth + outPortWidth + 4 * _portSpasing; + unsigned int width = inPortWidth + outPortWidth + 4 * _portSpacing; if (auto w = _graphModel.nodeData(nodeId, NodeRole::Widget)) { width += w->width(); @@ -73,7 +73,7 @@ void DefaultHorizontalNodeGeometry::recomputeSize(NodeId const nodeId) const if (!lblRect.isNull()) textWidth = std::max(textWidth, static_cast(lblRect.width())); - width = std::max(width, textWidth + 2 * _portSpasing); + width = std::max(width, textWidth + 2 * _portSpacing); QSize size(width, height); @@ -84,7 +84,7 @@ QPointF DefaultHorizontalNodeGeometry::portPosition(NodeId const nodeId, PortType const portType, PortIndex const portIndex) const { - unsigned int const step = _portSize + _portSpasing; + unsigned int const step = _portSize + _portSpacing; QPointF result; @@ -94,10 +94,10 @@ QPointF DefaultHorizontalNodeGeometry::portPosition(NodeId const nodeId, if (_graphModel.nodeData(nodeId, NodeRole::LabelVisible)) { totalHeight += labelRect(nodeId).height(); - totalHeight += _portSpasing / 2.0; + totalHeight += _portSpacing / 2.0; } - totalHeight += _portSpasing; + totalHeight += _portSpacing; totalHeight += step * portIndex; totalHeight += step / 2.0; @@ -140,11 +140,11 @@ QPointF DefaultHorizontalNodeGeometry::portTextPosition(NodeId const nodeId, switch (portType) { case PortType::In: - p.setX(_portSpasing); + p.setX(_portSpacing); break; case PortType::Out: - p.setX(size.width() - _portSpasing - rect.width()); + p.setX(size.width() - _portSpacing - rect.width()); break; default: @@ -171,8 +171,8 @@ QPointF DefaultHorizontalNodeGeometry::captionPosition(NodeId const nodeId) cons QRectF cap = captionRect(nodeId); QRectF lbl = labelRect(nodeId); - double y = 0.5 * _portSpasing + cap.height(); - y += _portSpasing / 2.0 + lbl.height(); + double y = 0.5 * _portSpacing + cap.height(); + y += _portSpacing / 2.0 + lbl.height(); return QPointF(0.5 * (size.width() - captionRect(nodeId).width()), y); } @@ -197,8 +197,8 @@ QPointF DefaultHorizontalNodeGeometry::labelPosition(NodeId const nodeId) const QRectF cap = captionRect(nodeId); QRectF lbl = labelRect(nodeId); - double y = 0.5 * _portSpasing + cap.height(); - y += _portSpasing / 2.0 + lbl.height(); + double y = 0.5 * _portSpacing + cap.height(); + y += _portSpacing / 2.0 + lbl.height(); if (!_graphModel.nodeData(nodeId, NodeRole::CaptionVisible)) { return QPointF(captionPosition(nodeId).x() @@ -208,7 +208,7 @@ QPointF DefaultHorizontalNodeGeometry::labelPosition(NodeId const nodeId) const return QPointF(captionPosition(nodeId).x() + 0.5 * (captionRect(nodeId).width() - 2 * labelRect(nodeId).width()), - 0.5 * _portSpasing + captionRect(nodeId).height()); + 0.5 * _portSpacing + captionRect(nodeId).height()); } QPointF DefaultHorizontalNodeGeometry::widgetPosition(NodeId const nodeId) const @@ -217,16 +217,16 @@ QPointF DefaultHorizontalNodeGeometry::widgetPosition(NodeId const nodeId) const unsigned int captionHeight = captionRect(nodeId).height(); if (_graphModel.nodeData(nodeId, NodeRole::LabelVisible)) - captionHeight += labelRect(nodeId).height() + _portSpasing / 2; + captionHeight += labelRect(nodeId).height() + _portSpacing / 2; if (auto w = _graphModel.nodeData(nodeId, NodeRole::Widget)) { // If the widget wants to use as much vertical space as possible, // place it immediately after the caption. if (w->sizePolicy().verticalPolicy() & QSizePolicy::ExpandFlag) { - return QPointF(2.0 * _portSpasing + maxPortsTextAdvance(nodeId, PortType::In), - _portSpasing + captionHeight); + return QPointF(2.0 * _portSpacing + maxPortsTextAdvance(nodeId, PortType::In), + _portSpacing + captionHeight); } else { - return QPointF(2.0 * _portSpasing + maxPortsTextAdvance(nodeId, PortType::In), + return QPointF(2.0 * _portSpacing + maxPortsTextAdvance(nodeId, PortType::In), (captionHeight + size.height() - w->height()) / 2.0); } } @@ -239,7 +239,7 @@ QRect DefaultHorizontalNodeGeometry::resizeHandleRect(NodeId const nodeId) const unsigned int rectSize = 7; - return QRect(size.width() - _portSpasing, size.height() - _portSpasing, rectSize, rectSize); + return QRect(size.width() - _portSpacing, size.height() - _portSpacing, rectSize, rectSize); } QRectF DefaultHorizontalNodeGeometry::portTextRect(NodeId const nodeId, @@ -265,7 +265,7 @@ unsigned int DefaultHorizontalNodeGeometry::maxVerticalPortsExtent(NodeId const PortCount nOutPorts = _graphModel.nodeData(nodeId, NodeRole::OutPortCount); unsigned int maxNumOfEntries = std::max(nInPorts, nOutPorts); - unsigned int step = _portSize + _portSpasing; + unsigned int step = _portSize + _portSpacing; return step * maxNumOfEntries; } diff --git a/src/DefaultVerticalNodeGeometry.cpp b/src/DefaultVerticalNodeGeometry.cpp index 2d76a758..589f5cdc 100644 --- a/src/DefaultVerticalNodeGeometry.cpp +++ b/src/DefaultVerticalNodeGeometry.cpp @@ -12,7 +12,7 @@ namespace QtNodes { DefaultVerticalNodeGeometry::DefaultVerticalNodeGeometry(AbstractGraphModel &graphModel) : AbstractNodeGeometry(graphModel) , _portSize(20) - , _portSpasing(10) + , _portSpacing(10) , _fontMetrics(QFont()) , _boldFontMetrics(QFont()) { @@ -27,7 +27,7 @@ QRectF DefaultVerticalNodeGeometry::boundingRect(NodeId const nodeId) const { QSize s = size(nodeId); - qreal marginSize = 2.0 * _portSpasing; + qreal marginSize = 2.0 * _portSpacing; QMargins margins(marginSize, marginSize, marginSize, marginSize); QRectF r(QPointF(0, 0), s); @@ -42,7 +42,7 @@ QSize DefaultVerticalNodeGeometry::size(NodeId const nodeId) const void DefaultVerticalNodeGeometry::recomputeSize(NodeId const nodeId) const { - unsigned int height = _portSpasing; // maxHorizontalPortsExtent(nodeId); + unsigned int height = _portSpacing; // maxHorizontalPortsExtent(nodeId); if (auto w = _graphModel.nodeData(nodeId, NodeRole::Widget)) { height = std::max(height, static_cast(w->height())); @@ -54,11 +54,11 @@ void DefaultVerticalNodeGeometry::recomputeSize(NodeId const nodeId) const height += capRect.height(); if (!lblRect.isNull()) { height += lblRect.height(); - height += _portSpasing / 2; + height += _portSpacing / 2; } - height += _portSpasing; - height += _portSpasing; + height += _portSpacing; + height += _portSpacing; PortCount nInPorts = _graphModel.nodeData(nodeId, NodeRole::InPortCount); PortCount nOutPorts = _graphModel.nodeData(nodeId, NodeRole::OutPortCount); @@ -72,11 +72,11 @@ void DefaultVerticalNodeGeometry::recomputeSize(NodeId const nodeId) const unsigned int outPortWidth = maxPortsTextAdvance(nodeId, PortType::Out); unsigned int totalInPortsWidth = nInPorts > 0 - ? inPortWidth * nInPorts + _portSpasing * (nInPorts - 1) + ? inPortWidth * nInPorts + _portSpacing * (nInPorts - 1) : 0; unsigned int totalOutPortsWidth = nOutPorts > 0 ? outPortWidth * nOutPorts - + _portSpasing * (nOutPorts - 1) + + _portSpacing * (nOutPorts - 1) : 0; unsigned int width = std::max(totalInPortsWidth, totalOutPortsWidth); @@ -91,8 +91,8 @@ void DefaultVerticalNodeGeometry::recomputeSize(NodeId const nodeId) const width = std::max(width, textWidth); - width += _portSpasing; - width += _portSpasing; + width += _portSpacing; + width += _portSpacing; QSize size(width, height); @@ -109,7 +109,7 @@ QPointF DefaultVerticalNodeGeometry::portPosition(NodeId const nodeId, switch (portType) { case PortType::In: { - unsigned int inPortWidth = maxPortsTextAdvance(nodeId, PortType::In) + _portSpasing; + unsigned int inPortWidth = maxPortsTextAdvance(nodeId, PortType::In) + _portSpacing; PortCount nInPorts = _graphModel.nodeData(nodeId, NodeRole::InPortCount); @@ -123,7 +123,7 @@ QPointF DefaultVerticalNodeGeometry::portPosition(NodeId const nodeId, } case PortType::Out: { - unsigned int outPortWidth = maxPortsTextAdvance(nodeId, PortType::Out) + _portSpasing; + unsigned int outPortWidth = maxPortsTextAdvance(nodeId, PortType::Out) + _portSpacing; PortCount nOutPorts = _graphModel.nodeData(nodeId, NodeRole::OutPortCount); double x = (size.width() - (nOutPorts - 1) * outPortWidth) / 2.0 + portIndex * outPortWidth; @@ -185,7 +185,7 @@ QPointF DefaultVerticalNodeGeometry::captionPosition(NodeId const nodeId) const QSize size = _graphModel.nodeData(nodeId, NodeRole::Size); unsigned int step = portCaptionsHeight(nodeId, PortType::In); - step += _portSpasing; + step += _portSpacing; auto rect = captionRect(nodeId); @@ -199,9 +199,9 @@ QPointF DefaultVerticalNodeGeometry::labelPosition(const NodeId nodeId) const QRectF rect = labelRect(nodeId); unsigned int step = portCaptionsHeight(nodeId, PortType::In); - step += _portSpasing; + step += _portSpacing; step += captionRect(nodeId).height(); - step += _portSpasing / 2; + step += _portSpacing / 2; return QPointF(0.5 * (size.width() - rect.width()), step + rect.height()); } @@ -226,15 +226,15 @@ QPointF DefaultVerticalNodeGeometry::widgetPosition(NodeId const nodeId) const unsigned int captionHeight = captionRect(nodeId).height(); if (_graphModel.nodeData(nodeId, NodeRole::LabelVisible)) - captionHeight += labelRect(nodeId).height() + _portSpasing / 2; + captionHeight += labelRect(nodeId).height() + _portSpacing / 2; if (auto w = _graphModel.nodeData(nodeId, NodeRole::Widget)) { // If the widget wants to use as much vertical space as possible, // place it immediately after the caption. if (w->sizePolicy().verticalPolicy() & QSizePolicy::ExpandFlag) { - return QPointF(_portSpasing + maxPortsTextAdvance(nodeId, PortType::In), captionHeight); + return QPointF(_portSpacing + maxPortsTextAdvance(nodeId, PortType::In), captionHeight); } else { - return QPointF(_portSpasing + maxPortsTextAdvance(nodeId, PortType::In), + return QPointF(_portSpacing + maxPortsTextAdvance(nodeId, PortType::In), (captionHeight + size.height() - w->height()) / 2.0); } } @@ -273,7 +273,7 @@ unsigned int DefaultVerticalNodeGeometry::maxHorizontalPortsExtent(NodeId const PortCount nOutPorts = _graphModel.nodeData(nodeId, NodeRole::OutPortCount); unsigned int maxNumOfEntries = std::max(nInPorts, nOutPorts); - unsigned int step = _portSize + _portSpasing; + unsigned int step = _portSize + _portSpacing; return step * maxNumOfEntries; } @@ -323,7 +323,7 @@ unsigned int DefaultVerticalNodeGeometry::portCaptionsHeight(NodeId const nodeId PortCount nInPorts = _graphModel.nodeData(nodeId, NodeRole::InPortCount); for (PortIndex i = 0; i < nInPorts; ++i) { if (_graphModel.portData(nodeId, PortType::In, i, PortRole::CaptionVisible)) { - h += _portSpasing; + h += _portSpacing; break; } } @@ -334,7 +334,7 @@ unsigned int DefaultVerticalNodeGeometry::portCaptionsHeight(NodeId const nodeId PortCount nOutPorts = _graphModel.nodeData(nodeId, NodeRole::OutPortCount); for (PortIndex i = 0; i < nOutPorts; ++i) { if (_graphModel.portData(nodeId, PortType::Out, i, PortRole::CaptionVisible)) { - h += _portSpasing; + h += _portSpacing; break; } } From 921e243552fd9918972c2756ac54ebf2ca0626b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Taiguara=20Tupinamb=C3=A1s?= Date: Tue, 11 Nov 2025 14:25:41 -0300 Subject: [PATCH 14/26] uniformizes icon files attributes --- resources/status_icons/empty.svg | 12 ++++++++ resources/status_icons/failed.svg | 14 +++++++++ resources/status_icons/partial.svg | 42 +++++++++++++++++++++++++++ resources/status_icons/pending.svg | 22 ++++++++++++++ resources/status_icons/processing.svg | 20 +++++++++++++ resources/status_icons/updated.svg | 14 +++++++++ 6 files changed, 124 insertions(+) create mode 100644 resources/status_icons/empty.svg create mode 100644 resources/status_icons/failed.svg create mode 100644 resources/status_icons/partial.svg create mode 100644 resources/status_icons/pending.svg create mode 100644 resources/status_icons/processing.svg create mode 100644 resources/status_icons/updated.svg diff --git a/resources/status_icons/empty.svg b/resources/status_icons/empty.svg new file mode 100644 index 00000000..21d5e820 --- /dev/null +++ b/resources/status_icons/empty.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/status_icons/failed.svg b/resources/status_icons/failed.svg new file mode 100644 index 00000000..90f53d66 --- /dev/null +++ b/resources/status_icons/failed.svg @@ -0,0 +1,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/resources/status_icons/partial.svg b/resources/status_icons/partial.svg new file mode 100644 index 00000000..78522eca --- /dev/null +++ b/resources/status_icons/partial.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/status_icons/pending.svg b/resources/status_icons/pending.svg new file mode 100644 index 00000000..7e74e3fb --- /dev/null +++ b/resources/status_icons/pending.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/status_icons/processing.svg b/resources/status_icons/processing.svg new file mode 100644 index 00000000..03ac710a --- /dev/null +++ b/resources/status_icons/processing.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/status_icons/updated.svg b/resources/status_icons/updated.svg new file mode 100644 index 00000000..5c1075e2 --- /dev/null +++ b/resources/status_icons/updated.svg @@ -0,0 +1,14 @@ + + + + + + + + + + \ No newline at end of file From 974d1157e9ceed29a43ef5263f9f88fe8469e579 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Taiguara=20Tupinamb=C3=A1s?= Date: Tue, 11 Nov 2025 14:32:00 -0300 Subject: [PATCH 15/26] removes commented code --- src/NodeGraphicsObject.cpp | 63 +++++++++++++++++++++++++++++++++----- 1 file changed, 56 insertions(+), 7 deletions(-) diff --git a/src/NodeGraphicsObject.cpp b/src/NodeGraphicsObject.cpp index 8959f4cf..d562ebb1 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -15,7 +15,6 @@ #include - namespace QtNodes { NodeGraphicsObject::NodeGraphicsObject(BasicGraphicsScene &scene, NodeId nodeId) @@ -37,8 +36,7 @@ NodeGraphicsObject::NodeGraphicsObject(BasicGraphicsScene &scene, NodeId nodeId) NodeStyle nodeStyle(nodeStyleJson); - if(nodeStyle.ShadowEnabled) - { + if (nodeStyle.ShadowEnabled) { auto effect = new QGraphicsDropShadowEffect; effect->setOffset(4, 4); effect->setBlurRadius(20); @@ -79,10 +77,10 @@ BasicGraphicsScene *NodeGraphicsObject::nodeScene() const void NodeGraphicsObject::updateQWidgetEmbedPos() { - if (_proxyWidget) { - AbstractNodeGeometry &geometry = nodeScene()->nodeGeometry(); - _proxyWidget->setPos(geometry.widgetPosition(_nodeId)); - } + if (_proxyWidget) { + AbstractNodeGeometry &geometry = nodeScene()->nodeGeometry(); + _proxyWidget->setPos(geometry.widgetPosition(_nodeId)); + } } void NodeGraphicsObject::embedQWidget() @@ -372,4 +370,55 @@ 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 iconPos = 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 From f171a553ad65d294e4e09d6180b2d8b98931cc27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Taiguara=20Tupinamb=C3=A1s?= Date: Thu, 13 Nov 2025 13:19:30 -0300 Subject: [PATCH 16/26] improves processing status icon resolution --- src/DefaultNodePainter.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/DefaultNodePainter.cpp b/src/DefaultNodePainter.cpp index b6465e7b..3a75c19f 100644 --- a/src/DefaultNodePainter.cpp +++ b/src/DefaultNodePainter.cpp @@ -339,6 +339,31 @@ 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(); + QPixmap pixmap = icon.pixmap(64); + + 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(); From 6a5530b1571f78d58b0bb6bf3ccaa259e1b6df0c Mon Sep 17 00:00:00 2001 From: g-abilio Date: Thu, 13 Nov 2025 13:26:25 -0300 Subject: [PATCH 17/26] solves situations where icons should not appear --- include/QtNodes/internal/NodeDelegateModel.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/QtNodes/internal/NodeDelegateModel.hpp b/include/QtNodes/internal/NodeDelegateModel.hpp index 7f4aa08b..360bda41 100644 --- a/include/QtNodes/internal/NodeDelegateModel.hpp +++ b/include/QtNodes/internal/NodeDelegateModel.hpp @@ -149,6 +149,10 @@ public Q_SLOTS: private: NodeStyle _nodeStyle; + + NodeValidationState _nodeValidationState; + + NodeProcessingStatus _processingStatus{NodeProcessingStatus::NoStatus}; }; } // namespace QtNodes From 46d97172ed106982b7fdc3dcb28ff302bdb7191e Mon Sep 17 00:00:00 2001 From: g-abilio Date: Thu, 13 Nov 2025 14:31:45 -0300 Subject: [PATCH 18/26] adds docstring to each nodeprocessingstatus --- include/QtNodes/internal/NodeDelegateModel.hpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/include/QtNodes/internal/NodeDelegateModel.hpp b/include/QtNodes/internal/NodeDelegateModel.hpp index 360bda41..bde9671b 100644 --- a/include/QtNodes/internal/NodeDelegateModel.hpp +++ b/include/QtNodes/internal/NodeDelegateModel.hpp @@ -28,6 +28,19 @@ struct NodeValidationState QString _stateMessage{""}; }; +/** +* Describes the node status, depending on its current situation +*/ +enum class NodeProcessingStatus : int { + NoStatus = 0, ///< No processing status is shown in the Node UI. + Updated = 1, ///< Node is up to date; its outputs reflect the current inputs and parameters. + Processing = 2, ///< Node is currently running a computation. + Pending = 3, ///< Node is out of date and waiting to be recomputed (e.g. manual/queued run). + Empty = 4, ///< Node has no valid input data; nothing to compute. + Failed = 5, ///< The last computation ended with an error. + Partial = 6, ///< Computation finished incompletely; only partial results are available. +}; + class StyleCollection; /** From 13d6c5399b34e52404c7a7784db4469e83b701e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Taiguara=20Tupinamb=C3=A1s?= Date: Thu, 13 Nov 2025 14:47:00 -0300 Subject: [PATCH 19/26] adds possibility to change the node processing status icon style --- .../resizable_images/ImageLoaderModel.cpp | 8 ++++++ .../QtNodes/internal/NodeDelegateModel.hpp | 28 +++++++++++++++++++ src/DefaultNodePainter.cpp | 13 ++++++++- src/NodeDelegateModel.cpp | 15 ++++++++++ 4 files changed, 63 insertions(+), 1 deletion(-) diff --git a/examples/resizable_images/ImageLoaderModel.cpp b/examples/resizable_images/ImageLoaderModel.cpp index 8cb600a9..3a506f1c 100644 --- a/examples/resizable_images/ImageLoaderModel.cpp +++ b/examples/resizable_images/ImageLoaderModel.cpp @@ -20,6 +20,14 @@ ImageLoaderModel::ImageLoaderModel() _label->setMaximumSize(500, 300); _label->installEventFilter(this); + + QtNodes::ProcessingIconStyle style; + style._margin = 4.0; + style._pos = QtNodes::ProcessingIconPos::BottomLeft; + style._size = 10.0; + + setProcessingIconStyle(style); + setNodeProcessingStatus(QtNodes::NodeProcessingStatus::Updated); } unsigned int ImageLoaderModel::nPorts(PortType portType) const diff --git a/include/QtNodes/internal/NodeDelegateModel.hpp b/include/QtNodes/internal/NodeDelegateModel.hpp index bde9671b..04ca49de 100644 --- a/include/QtNodes/internal/NodeDelegateModel.hpp +++ b/include/QtNodes/internal/NodeDelegateModel.hpp @@ -41,6 +41,24 @@ enum class NodeProcessingStatus : int { Partial = 6, ///< Computation finished incompletely; only partial results are available. }; +/** + * Describes the position of the processing icon on the node ui + */ +enum class ProcessingIconPos { + BottomLeft = 0, /// icon on the bottom left position + BottomRight = 1, /// icon on the bottom right position +}; + +/** + * Defines the processing icon style; + */ +struct ProcessingIconStyle +{ + ProcessingIconPos _pos{ProcessingIconPos::BottomRight}; + double _size{20.0}; + double _margin{8.0}; +}; + class StyleCollection; /** @@ -102,6 +120,14 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel NodeStyle const &nodeStyle() const; void setNodeStyle(NodeStyle const &style); +<<<<<<< HEAD +======= + void setProcessingIconStyle(ProcessingIconStyle new_style); + + ProcessingIconStyle processingIconStyle() const; + +public: +>>>>>>> 582cc45 (adds possibility to change the node processing status icon style) virtual void setInData(std::shared_ptr nodeData, PortIndex const portIndex) = 0; virtual std::shared_ptr outData(PortIndex const port) = 0; @@ -166,6 +192,8 @@ public Q_SLOTS: NodeValidationState _nodeValidationState; NodeProcessingStatus _processingStatus{NodeProcessingStatus::NoStatus}; + + ProcessingIconStyle _processingIconStyle{}; }; } // namespace QtNodes diff --git a/src/DefaultNodePainter.cpp b/src/DefaultNodePainter.cpp index 3a75c19f..6660b209 100644 --- a/src/DefaultNodePainter.cpp +++ b/src/DefaultNodePainter.cpp @@ -360,7 +360,18 @@ void DefaultNodePainter::drawProcessingIndicator(QPainter *painter, NodeGraphics QIcon icon = ngo.processingStatusIcon(); QPixmap pixmap = icon.pixmap(64); - QRect r(size.width() - 28.0, size.height() - 28.0, 20.0, 20.0); + ProcessingIconStyle iconStyle = delegate->processingIconStyle(); + + qreal iconSize = iconStyle._size; + qreal margin = iconStyle._margin; + + qreal x = margin; + + if (iconStyle._pos == ProcessingIconPos::BottomRight) { + x = size.width() - iconSize - margin; + } + + QRect r(x, size.height() - iconSize - margin, iconSize, iconSize); painter->drawPixmap(r, pixmap); } diff --git a/src/NodeDelegateModel.cpp b/src/NodeDelegateModel.cpp index 94e47ad6..2707e8a0 100644 --- a/src/NodeDelegateModel.cpp +++ b/src/NodeDelegateModel.cpp @@ -51,4 +51,19 @@ void NodeDelegateModel::setNodeStyle(NodeStyle const &style) _nodeStyle = style; } +void NodeDelegateModel::setProcessingIconStyle(ProcessingIconStyle placement) +{ + _processingIconStyle = placement; +} + +ProcessingIconStyle NodeDelegateModel::processingIconStyle() const +{ + return _processingIconStyle; +} + +void NodeDelegateModel::setNodeProcessingStatus(NodeProcessingStatus status) +{ + _processingStatus = status; +} + } // namespace QtNodes From 82e4a879d87415fd3b82883cdfb033e9e096aeba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Taiguara=20Tupinamb=C3=A1s?= Date: Thu, 13 Nov 2025 16:39:25 -0300 Subject: [PATCH 20/26] moves all status logic to NodeStyle --- .../calculator/LongProcessingRandomNumber.hpp | 20 +++++++ .../resizable_images/ImageLoaderModel.cpp | 8 --- .../QtNodes/internal/NodeDelegateModel.hpp | 29 ++--------- include/QtNodes/internal/NodeStyle.hpp | 29 +++++++++++ src/DefaultNodePainter.cpp | 7 ++- src/NodeDelegateModel.cpp | 52 +++++++++++++++++-- src/NodeGraphicsObject.cpp | 2 + src/NodeStyle.cpp | 9 ++-- 8 files changed, 111 insertions(+), 45 deletions(-) diff --git a/examples/calculator/LongProcessingRandomNumber.hpp b/examples/calculator/LongProcessingRandomNumber.hpp index 365ca9cb..ec762baa 100644 --- a/examples/calculator/LongProcessingRandomNumber.hpp +++ b/examples/calculator/LongProcessingRandomNumber.hpp @@ -15,6 +15,26 @@ 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: diff --git a/examples/resizable_images/ImageLoaderModel.cpp b/examples/resizable_images/ImageLoaderModel.cpp index 3a506f1c..8cb600a9 100644 --- a/examples/resizable_images/ImageLoaderModel.cpp +++ b/examples/resizable_images/ImageLoaderModel.cpp @@ -20,14 +20,6 @@ ImageLoaderModel::ImageLoaderModel() _label->setMaximumSize(500, 300); _label->installEventFilter(this); - - QtNodes::ProcessingIconStyle style; - style._margin = 4.0; - style._pos = QtNodes::ProcessingIconPos::BottomLeft; - style._size = 10.0; - - setProcessingIconStyle(style); - setNodeProcessingStatus(QtNodes::NodeProcessingStatus::Updated); } unsigned int ImageLoaderModel::nPorts(PortType portType) const diff --git a/include/QtNodes/internal/NodeDelegateModel.hpp b/include/QtNodes/internal/NodeDelegateModel.hpp index 04ca49de..04bbdc17 100644 --- a/include/QtNodes/internal/NodeDelegateModel.hpp +++ b/include/QtNodes/internal/NodeDelegateModel.hpp @@ -41,24 +41,6 @@ enum class NodeProcessingStatus : int { Partial = 6, ///< Computation finished incompletely; only partial results are available. }; -/** - * Describes the position of the processing icon on the node ui - */ -enum class ProcessingIconPos { - BottomLeft = 0, /// icon on the bottom left position - BottomRight = 1, /// icon on the bottom right position -}; - -/** - * Defines the processing icon style; - */ -struct ProcessingIconStyle -{ - ProcessingIconPos _pos{ProcessingIconPos::BottomRight}; - double _size{20.0}; - double _margin{8.0}; -}; - class StyleCollection; /** @@ -120,14 +102,15 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel NodeStyle const &nodeStyle() const; void setNodeStyle(NodeStyle const &style); -<<<<<<< HEAD -======= void setProcessingIconStyle(ProcessingIconStyle new_style); - ProcessingIconStyle processingIconStyle() const; + QPixmap processingStatusIcon() const; + + void setStatusIcon(NodeProcessingStatus status, const QPixmap &pixmap); + + void setStatusIconStyle(ProcessingIconStyle const &style); public: ->>>>>>> 582cc45 (adds possibility to change the node processing status icon style) virtual void setInData(std::shared_ptr nodeData, PortIndex const portIndex) = 0; virtual std::shared_ptr outData(PortIndex const port) = 0; @@ -192,8 +175,6 @@ public Q_SLOTS: NodeValidationState _nodeValidationState; NodeProcessingStatus _processingStatus{NodeProcessingStatus::NoStatus}; - - ProcessingIconStyle _processingIconStyle{}; }; } // namespace QtNodes diff --git a/include/QtNodes/internal/NodeStyle.hpp b/include/QtNodes/internal/NodeStyle.hpp index 85abc561..dcb24486 100644 --- a/include/QtNodes/internal/NodeStyle.hpp +++ b/include/QtNodes/internal/NodeStyle.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include "Export.hpp" @@ -7,6 +8,25 @@ namespace QtNodes { +/** + * Describes the position of the processing icon on the node ui + */ +enum class ProcessingIconPos { + BottomLeft = 0, /// icon on the bottom left position + BottomRight = 1, /// icon on the bottom right position +}; + +/** + * Defines the processing icon style; + */ +struct ProcessingIconStyle +{ + ProcessingIconPos _pos{ProcessingIconPos::BottomRight}; + double _size{20.0}; + double _margin{8.0}; + int _resolution{64}; +}; + class NODE_EDITOR_PUBLIC NodeStyle : public Style { public: @@ -50,5 +70,14 @@ class NODE_EDITOR_PUBLIC NodeStyle : public Style float ConnectionPointDiameter; float Opacity; + + QIcon statusUpdated{QStringLiteral("://status_icons/updated.svg")}; + QIcon statusProcessing{QStringLiteral("://status_icons/processing.svg")}; + QIcon statusPending{QStringLiteral("://status_icons/pending.svg")}; + QIcon statusInvalid{QStringLiteral("://status_icons/failed.svg")}; + QIcon statusEmpty{QStringLiteral("://status_icons/empty.svg")}; + QIcon statusPartial{QStringLiteral("://status_icons/partial.svg")}; + + ProcessingIconStyle processingIconStyle{}; }; } // namespace QtNodes diff --git a/src/DefaultNodePainter.cpp b/src/DefaultNodePainter.cpp index 6660b209..057d1458 100644 --- a/src/DefaultNodePainter.cpp +++ b/src/DefaultNodePainter.cpp @@ -354,13 +354,12 @@ void DefaultNodePainter::drawProcessingIndicator(QPainter *painter, NodeGraphics AbstractNodeGeometry &geometry = ngo.nodeScene()->nodeGeometry(); - ngo.updateStatusIconSize(); QSize size = geometry.size(nodeId); - QIcon icon = ngo.processingStatusIcon(); - QPixmap pixmap = icon.pixmap(64); + QPixmap pixmap = delegate->processingStatusIcon(); + NodeStyle nodeStyle = delegate->nodeStyle(); - ProcessingIconStyle iconStyle = delegate->processingIconStyle(); + ProcessingIconStyle iconStyle = nodeStyle.processingIconStyle; qreal iconSize = iconStyle._size; qreal margin = iconStyle._margin; diff --git a/src/NodeDelegateModel.cpp b/src/NodeDelegateModel.cpp index 2707e8a0..eb7e468f 100644 --- a/src/NodeDelegateModel.cpp +++ b/src/NodeDelegateModel.cpp @@ -51,14 +51,58 @@ void NodeDelegateModel::setNodeStyle(NodeStyle const &style) _nodeStyle = style; } -void NodeDelegateModel::setProcessingIconStyle(ProcessingIconStyle placement) +QPixmap NodeDelegateModel::processingStatusIcon() const { - _processingIconStyle = placement; + int resolution = _nodeStyle.processingIconStyle._resolution; + switch (_processingStatus) { + case NodeProcessingStatus::NoStatus: + return {}; + case NodeProcessingStatus::Updated: + return _nodeStyle.statusUpdated.pixmap(resolution); + case NodeProcessingStatus::Processing: + return _nodeStyle.statusProcessing.pixmap(resolution); + case NodeProcessingStatus::Pending: + return _nodeStyle.statusPending.pixmap(resolution); + case NodeProcessingStatus::Empty: + return _nodeStyle.statusEmpty.pixmap(resolution); + case NodeProcessingStatus::Failed: + return _nodeStyle.statusInvalid.pixmap(resolution); + case NodeProcessingStatus::Partial: + return _nodeStyle.statusPartial.pixmap(resolution); + } + + return {}; +} + +void NodeDelegateModel::setStatusIcon(NodeProcessingStatus status, const QPixmap &pixmap) +{ + switch (status) { + case NodeProcessingStatus::NoStatus: + break; + case NodeProcessingStatus::Updated: + _nodeStyle.statusUpdated = QIcon(pixmap); + break; + case NodeProcessingStatus::Processing: + _nodeStyle.statusProcessing = QIcon(pixmap); + break; + case NodeProcessingStatus::Pending: + _nodeStyle.statusPending = QIcon(pixmap); + break; + case NodeProcessingStatus::Empty: + _nodeStyle.statusEmpty = QIcon(pixmap); + break; + case NodeProcessingStatus::Failed: + _nodeStyle.statusInvalid = QIcon(pixmap); + break; + case NodeProcessingStatus::Partial: + _nodeStyle.statusPartial = QIcon(pixmap); + break; + } } -ProcessingIconStyle NodeDelegateModel::processingIconStyle() const +void NodeDelegateModel::setStatusIconStyle(const ProcessingIconStyle &style) { - return _processingIconStyle; + _nodeStyle.processingIconStyle = style; } void NodeDelegateModel::setNodeProcessingStatus(NodeProcessingStatus status) diff --git a/src/NodeGraphicsObject.cpp b/src/NodeGraphicsObject.cpp index d562ebb1..5d463880 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -63,6 +63,8 @@ NodeGraphicsObject::NodeGraphicsObject(BasicGraphicsScene &scene, NodeId nodeId) if (_nodeId == nodeId) setLockedState(); }); + + QVariant var = _graphModel.nodeData(_nodeId, NodeRole::ProcessingStatus); } AbstractGraphModel &NodeGraphicsObject::graphModel() const diff --git a/src/NodeStyle.cpp b/src/NodeStyle.cpp index b3079b33..2c232fb4 100644 --- a/src/NodeStyle.cpp +++ b/src/NodeStyle.cpp @@ -8,7 +8,6 @@ #include - using QtNodes::NodeStyle; inline void initResources() @@ -89,14 +88,14 @@ void NodeStyle::setNodeStyle(QString jsonText) #define NODE_STYLE_READ_BOOL(values, variable) \ { \ - auto valueRef = values[#variable]; \ - NODE_STYLE_CHECK_UNDEFINED_VALUE(valueRef, variable) \ - variable = valueRef.toBool(); \ + auto valueRef = values[#variable]; \ + NODE_STYLE_CHECK_UNDEFINED_VALUE(valueRef, variable) \ + variable = valueRef.toBool(); \ } #define NODE_STYLE_WRITE_BOOL(values, variable) \ { \ - values[#variable] = variable; \ + values[#variable] = variable; \ } void NodeStyle::loadJson(QJsonObject const &json) From 43e1d51526084a62085ed75899c4a918202fbd24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Taiguara=20Tupinamb=C3=A1s?= Date: Thu, 13 Nov 2025 16:46:48 -0300 Subject: [PATCH 21/26] removes unnecessary code --- src/DefaultHorizontalNodeGeometry.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/DefaultHorizontalNodeGeometry.cpp b/src/DefaultHorizontalNodeGeometry.cpp index cb5e4207..87caf695 100644 --- a/src/DefaultHorizontalNodeGeometry.cpp +++ b/src/DefaultHorizontalNodeGeometry.cpp @@ -57,8 +57,14 @@ void DefaultHorizontalNodeGeometry::recomputeSize(NodeId const nodeId) const height += _portSpacing / 2; } - height += _portSpacing; // space above caption - height += _portSpacing; // space below caption + height += _portSpasing; // space above caption + height += _portSpasing; // space below caption + + QVariant var = _graphModel.nodeData(nodeId, NodeRole::ProcessingStatus); + auto processingStatusValue = var.value(); + + if (processingStatusValue != 0) + height += 20; unsigned int inPortWidth = maxPortsTextAdvance(nodeId, PortType::In); unsigned int outPortWidth = maxPortsTextAdvance(nodeId, PortType::Out); From 156433c27126602a5aad72c7ef57cecb233b3c1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Taiguara=20Tupinamb=C3=A1s?= Date: Thu, 13 Nov 2025 17:05:14 -0300 Subject: [PATCH 22/26] adds declaration of QPixmap --- include/QtNodes/internal/NodeDelegateModel.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/QtNodes/internal/NodeDelegateModel.hpp b/include/QtNodes/internal/NodeDelegateModel.hpp index 04bbdc17..687bf557 100644 --- a/include/QtNodes/internal/NodeDelegateModel.hpp +++ b/include/QtNodes/internal/NodeDelegateModel.hpp @@ -1,5 +1,8 @@ #pragma once +#include +#include + #include "Definitions.hpp" #include "Export.hpp" #include "NodeData.hpp" From c0457af9de53adbd88dc9dd3bcf2d2cce7e10020 Mon Sep 17 00:00:00 2001 From: g-abilio Date: Wed, 10 Dec 2025 15:14:06 -0300 Subject: [PATCH 23/26] solve conflicts --- examples/calculator/DivisionModel.hpp | 22 +++-- .../calculator/LongProcessingRandomNumber.hpp | 22 +++-- .../calculator/NumberDisplayDataModel.cpp | 4 +- examples/calculator/NumberSourceDataModel.cpp | 4 +- examples/styles/models.hpp | 2 + .../QtNodes/internal/DefaultNodePainter.hpp | 7 ++ include/QtNodes/internal/Definitions.hpp | 29 +++--- .../QtNodes/internal/NodeDelegateModel.hpp | 31 +++++-- .../QtNodes/internal/NodeGraphicsObject.hpp | 20 +++++ include/QtNodes/internal/NodeStyle.hpp | 6 ++ include/QtNodes/internal/UndoCommands.hpp | 2 +- resources/resources.qrc | 10 +++ resources/status_icons/empty.svg | 27 +++++- resources/status_icons/failed.svg | 83 ++++++++++++++++- resources/status_icons/partial.svg | 31 ++++++- resources/status_icons/pending.svg | 89 ++++++++++++++++++- resources/status_icons/processing.svg | 85 +++++++++++++++++- resources/status_icons/updated.svg | 20 ++++- src/DataFlowGraphModel.cpp | 24 ++++- src/DefaultHorizontalNodeGeometry.cpp | 8 +- src/DefaultNodePainter.cpp | 4 + src/NodeDelegateModel.cpp | 10 +++ src/NodeGraphicsObject.cpp | 2 - src/NodeStyle.cpp | 13 +++ 24 files changed, 505 insertions(+), 50 deletions(-) diff --git a/examples/calculator/DivisionModel.hpp b/examples/calculator/DivisionModel.hpp index 9018450c..1b7865c7 100644 --- a/examples/calculator/DivisionModel.hpp +++ b/examples/calculator/DivisionModel.hpp @@ -55,17 +55,27 @@ class DivisionModel : public MathOperationDataModel auto n1 = _number1.lock(); auto n2 = _number2.lock(); + QtNodes::NodeValidationState state; if (n2 && (n2->number() == 0.0)) { - //modelValidationState = NodeValidationState::Error; - //modelValidationError = QStringLiteral("Division by zero error"); + state._state = QtNodes::NodeValidationState::State::Error; + state._stateMessage = QStringLiteral("Division by zero error"); + 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"); + setValidationState(state); + if (n1) { + _result = std::make_shared(n1->number() / n2->number()); + } else { + _result.reset(); + } } else if (n1 && n2) { - //modelValidationState = NodeValidationState::Valid; - //modelValidationError = QString(); + setValidationState(state); _result = std::make_shared(n1->number() / n2->number()); } else { - //modelValidationState = NodeValidationState::Warning; - //modelValidationError = QStringLiteral("Missing or incorrect inputs"); + QtNodes::NodeValidationState state; + setValidationState(state); _result.reset(); } diff --git a/examples/calculator/LongProcessingRandomNumber.hpp b/examples/calculator/LongProcessingRandomNumber.hpp index ec762baa..8be2882c 100644 --- a/examples/calculator/LongProcessingRandomNumber.hpp +++ b/examples/calculator/LongProcessingRandomNumber.hpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include "MathOperationDataModel.hpp" #include "DecimalData.hpp" @@ -18,7 +18,6 @@ class RandomNumberModel : public MathOperationDataModel RandomNumberModel() { this->setNodeProcessingStatus(QtNodes::NodeProcessingStatus::Empty); - QObject::connect(this, &NodeDelegateModel::computingStarted, this, [this]() { if (_number1.lock() && _number2.lock()) { this->setNodeProcessingStatus( @@ -34,7 +33,6 @@ class RandomNumberModel : public MathOperationDataModel emit requestNodeUpdate(); }); } - virtual ~RandomNumberModel() {} public: @@ -42,11 +40,10 @@ class RandomNumberModel : public MathOperationDataModel QString name() const override { return QStringLiteral("Random Number"); } - bool labelEditable() const override { return true; } - private: void compute() override { + Q_EMIT computingStarted(); PortIndex const outPortIndex = 0; auto n1 = _number1.lock(); @@ -62,17 +59,18 @@ class RandomNumberModel : public MathOperationDataModel double a = n1->number(); double b = n2->number(); - double minVal = std::min(a, b); - double maxVal = std::max(a, b); - - std::random_device rd; - std::mt19937 gen(rd()); + if (a > b) { + setNodeProcessingStatus(QtNodes::NodeProcessingStatus::Failed); - std::uniform_real_distribution dist(minVal, maxVal); + emit requestNodeUpdate(); + return; + } - double randomValue = dist(gen); + 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(); } diff --git a/examples/calculator/NumberDisplayDataModel.cpp b/examples/calculator/NumberDisplayDataModel.cpp index 5086050c..6f58f585 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 f6a7ca55..f2564886 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/styles/models.hpp b/examples/styles/models.hpp index 1352e4bf..199f80be 100644 --- a/examples/styles/models.hpp +++ b/examples/styles/models.hpp @@ -36,6 +36,8 @@ class MyDataModel : public NodeDelegateModel QString name() const override { return QString("MyDataModel"); } + bool labelEditable() const override { return true; } + public: QJsonObject save() const override { diff --git a/include/QtNodes/internal/DefaultNodePainter.hpp b/include/QtNodes/internal/DefaultNodePainter.hpp index 339759fa..2468510f 100644 --- a/include/QtNodes/internal/DefaultNodePainter.hpp +++ b/include/QtNodes/internal/DefaultNodePainter.hpp @@ -32,5 +32,12 @@ class NODE_EDITOR_PUBLIC DefaultNodePainter : public AbstractNodePainter void drawEntryLabels(QPainter *painter, NodeGraphicsObject &ngo) const; void drawResizeRect(QPainter *painter, NodeGraphicsObject &ngo) const; + + void drawProcessingIndicator(QPainter *painter, NodeGraphicsObject &ngo) const; + + void drawValidationIcon(QPainter *painter, NodeGraphicsObject &ngo) const; + +private: + QIcon _toolTipIcon{"://info-tooltip.svg"}; }; } // namespace QtNodes diff --git a/include/QtNodes/internal/Definitions.hpp b/include/QtNodes/internal/Definitions.hpp index a72f4e6e..e9996d52 100644 --- a/include/QtNodes/internal/Definitions.hpp +++ b/include/QtNodes/internal/Definitions.hpp @@ -23,20 +23,21 @@ Q_NAMESPACE_EXPORT(NODE_EDITOR_PUBLIC) */ enum class NodeRole { - Type = 0, ///< Type of the current node, usually a string. - Position = 1, ///< `QPointF` positon of the node on the scene. - Size = 2, ///< `QSize` for resizable nodes. - CaptionVisible = 3, ///< `bool` for caption visibility. - Caption = 4, ///< `QString` for node caption. - Style = 5, ///< Custom NodeStyle as QJsonDocument - InternalData = 6, ///< Node-stecific user data as QJsonObject - InPortCount = 7, ///< `unsigned int` - OutPortCount = 9, ///< `unsigned int` - Widget = 10, ///< Optional `QWidget*` or `nullptr` - ValidationState = 11, ///< Enum NodeValidationState of the node - LabelVisible = 12, ///< `bool` for label visibility. - Label = 13, ///< `QString` for node label. - LabelEditable = 14, ///< `bool` to indicate label editing support. + Type = 0, ///< Type of the current node, usually a string. + Position = 1, ///< `QPointF` positon of the node on the scene. + Size = 2, ///< `QSize` for resizable nodes. + CaptionVisible = 3, ///< `bool` for caption visibility. + Caption = 4, ///< `QString` for node caption. + Style = 5, ///< Custom NodeStyle as QJsonDocument + InternalData = 6, ///< Node-stecific user data as QJsonObject + InPortCount = 7, ///< `unsigned int` + OutPortCount = 9, ///< `unsigned int` + Widget = 10, ///< Optional `QWidget*` or `nullptr` + ValidationState = 11, ///< Enum NodeValidationState of the node + LabelVisible = 12, ///< `bool` for label visibility. + ProcessingStatus = 13, ///< Enum NodeProcessingStatus of the node + Label = 14, ///< `QString` for node label. + LabelEditable = 15, ///< `bool` to indicate label editing support. }; Q_ENUM_NS(NodeRole) diff --git a/include/QtNodes/internal/NodeDelegateModel.hpp b/include/QtNodes/internal/NodeDelegateModel.hpp index 687bf557..7e70c69c 100644 --- a/include/QtNodes/internal/NodeDelegateModel.hpp +++ b/include/QtNodes/internal/NodeDelegateModel.hpp @@ -8,6 +8,7 @@ #include "NodeData.hpp" #include "NodeStyle.hpp" #include "Serializable.hpp" +#include #include #include @@ -63,15 +64,15 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel 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(); } @@ -90,11 +91,15 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel /// Controls whether the label can be edited or not virtual bool labelEditable() const { return false; } -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; @@ -103,6 +108,7 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel virtual ConnectionPolicy portConnectionPolicy(PortType, PortIndex) const; NodeStyle const &nodeStyle() const; + void setNodeStyle(NodeStyle const &style); void setProcessingIconStyle(ProcessingIconStyle new_style); @@ -112,6 +118,8 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel void setStatusIcon(NodeProcessingStatus status, const QPixmap &pixmap); void setStatusIconStyle(ProcessingIconStyle 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; @@ -146,10 +154,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 @@ -181,3 +199,6 @@ public Q_SLOTS: }; } // 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 eab83c76..69eb2ba6 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 dcb24486..9bfa5c72 100644 --- a/include/QtNodes/internal/NodeStyle.hpp +++ b/include/QtNodes/internal/NodeStyle.hpp @@ -46,6 +46,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 7aed4d60..87047861 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 a0b5ef8b..b3c851c8 100644 --- a/resources/resources.qrc +++ b/resources/resources.qrc @@ -1,5 +1,15 @@ DefaultStyle.json +<<<<<<< HEAD +======= + 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 +>>>>>>> 5d30a5d (solve conflicts) diff --git a/resources/status_icons/empty.svg b/resources/status_icons/empty.svg index 21d5e820..07da93e8 100644 --- a/resources/status_icons/empty.svg +++ b/resources/status_icons/empty.svg @@ -1,3 +1,4 @@ +<<<<<<< HEAD @@ -9,4 +10,28 @@ - \ No newline at end of file + +======= + + + + + + + + + +>>>>>>> 5d30a5d (solve conflicts) diff --git a/resources/status_icons/failed.svg b/resources/status_icons/failed.svg index 90f53d66..e21f8f0b 100644 --- a/resources/status_icons/failed.svg +++ b/resources/status_icons/failed.svg @@ -1,3 +1,4 @@ +<<<<<<< HEAD @@ -11,4 +12,84 @@ - \ No newline at end of file + +======= + + + + + + + + + + image/svg+xml + + + + + + + + + + + + +>>>>>>> 5d30a5d (solve conflicts) diff --git a/resources/status_icons/partial.svg b/resources/status_icons/partial.svg index 78522eca..4f3a2f51 100644 --- a/resources/status_icons/partial.svg +++ b/resources/status_icons/partial.svg @@ -1,3 +1,4 @@ +<<<<<<< HEAD @@ -39,4 +40,32 @@ - \ No newline at end of file + +======= + + + + + + + + + + + + + + + + + + + + +>>>>>>> 5d30a5d (solve conflicts) diff --git a/resources/status_icons/pending.svg b/resources/status_icons/pending.svg index 7e74e3fb..b1bcd857 100644 --- a/resources/status_icons/pending.svg +++ b/resources/status_icons/pending.svg @@ -1,3 +1,4 @@ +<<<<<<< HEAD @@ -19,4 +20,90 @@ - \ No newline at end of file + +======= + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + +>>>>>>> 5d30a5d (solve conflicts) diff --git a/resources/status_icons/processing.svg b/resources/status_icons/processing.svg index 03ac710a..12ba7792 100644 --- a/resources/status_icons/processing.svg +++ b/resources/status_icons/processing.svg @@ -1,3 +1,4 @@ +<<<<<<< HEAD @@ -17,4 +18,86 @@ - \ No newline at end of file + +======= + + + + + + + + + + image/svg+xml + + + + + + + + + + + +>>>>>>> 5d30a5d (solve conflicts) diff --git a/resources/status_icons/updated.svg b/resources/status_icons/updated.svg index 5c1075e2..0209c3dc 100644 --- a/resources/status_icons/updated.svg +++ b/resources/status_icons/updated.svg @@ -1,3 +1,4 @@ +<<<<<<< HEAD @@ -11,4 +12,21 @@ - \ No newline at end of file + +======= + + + + + + + + + + +>>>>>>> 5d30a5d (solve conflicts) diff --git a/src/DataFlowGraphModel.cpp b/src/DataFlowGraphModel.cpp index 7edd3ff1..bdbdbdfa 100644 --- a/src/DataFlowGraphModel.cpp +++ b/src/DataFlowGraphModel.cpp @@ -93,6 +93,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); _labels[newId] = _models[newId]->label(); @@ -303,6 +307,11 @@ QVariant DataFlowGraphModel::nodeData(NodeId nodeId, NodeRole role) const case NodeRole::LabelEditable: result = model->labelEditable(); break; + + case NodeRole::ProcessingStatus: { + auto processingStatus = model->processingStatus(); + result = QVariant::fromValue(processingStatus); + } break; } return result; @@ -367,7 +376,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); @@ -596,6 +615,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 87caf695..689aaf73 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 @@ -66,6 +66,12 @@ void DefaultHorizontalNodeGeometry::recomputeSize(NodeId const nodeId) const if (processingStatusValue != 0) height += 20; + 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 057d1458..40757418 100644 --- a/src/DefaultNodePainter.cpp +++ b/src/DefaultNodePainter.cpp @@ -5,6 +5,8 @@ #include "BasicGraphicsScene.hpp" #include "ConnectionGraphicsObject.hpp" #include "ConnectionIdUtils.hpp" +#include "DataFlowGraphModel.hpp" +#include "NodeDelegateModel.hpp" #include "NodeGraphicsObject.hpp" #include "NodeState.hpp" #include "StyleCollection.hpp" @@ -31,6 +33,8 @@ void DefaultNodePainter::paint(QPainter *painter, NodeGraphicsObject &ngo) const drawEntryLabels(painter, ngo); + drawProcessingIndicator(painter, ngo); + drawResizeRect(painter, ngo); drawValidationIcon(painter, ngo); diff --git a/src/NodeDelegateModel.cpp b/src/NodeDelegateModel.cpp index eb7e468f..fc8f3e8d 100644 --- a/src/NodeDelegateModel.cpp +++ b/src/NodeDelegateModel.cpp @@ -24,6 +24,11 @@ void NodeDelegateModel::load(QJsonObject const &) // } +void NodeDelegateModel::setValidationState(const NodeValidationState &validationState) +{ + _nodeValidationState = validationState; +} + ConnectionPolicy NodeDelegateModel::portConnectionPolicy(PortType portType, PortIndex) const { auto result = ConnectionPolicy::One; @@ -110,4 +115,9 @@ 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 5d463880..0d3f7d52 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -389,8 +389,6 @@ void NodeGraphicsObject::updateStatusIconSize() const QRect NodeGraphicsObject::statusIconRect() const { - QVariant var = _graphModel.nodeData(_nodeId, NodeRole::ProcessingStatus); - auto iconPos = QPoint{-statusIconSize().width() / 2, 0}; return QRect{iconPos, statusIconSize()}; diff --git a/src/NodeStyle.cpp b/src/NodeStyle.cpp index 2c232fb4..4b469ea1 100644 --- a/src/NodeStyle.cpp +++ b/src/NodeStyle.cpp @@ -156,3 +156,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; +} From e2051bd096861354f1181bd204120d521e482802 Mon Sep 17 00:00:00 2001 From: g-abilio Date: Wed, 10 Dec 2025 15:18:52 -0300 Subject: [PATCH 24/26] pull new node_processing_status and solve conflicts --- .../calculator/LongProcessingRandomNumber.hpp | 1 + .../calculator/NumberDisplayDataModel.cpp | 4 +- examples/calculator/NumberSourceDataModel.cpp | 4 +- .../QtNodes/internal/NodeDelegateModel.hpp | 6 ++ .../QtNodes/internal/NodeGraphicsObject.hpp | 19 ---- resources/status_icons/empty.svg | 7 ++ resources/status_icons/failed.svg | 8 ++ resources/status_icons/partial.svg | 7 ++ resources/status_icons/pending.svg | 84 +++++------------ resources/status_icons/processing.svg | 94 ++++--------------- resources/status_icons/updated.svg | 7 ++ src/DefaultHorizontalNodeGeometry.cpp | 5 +- 12 files changed, 80 insertions(+), 166 deletions(-) diff --git a/examples/calculator/LongProcessingRandomNumber.hpp b/examples/calculator/LongProcessingRandomNumber.hpp index 8be2882c..d64f4ba7 100644 --- a/examples/calculator/LongProcessingRandomNumber.hpp +++ b/examples/calculator/LongProcessingRandomNumber.hpp @@ -18,6 +18,7 @@ class RandomNumberModel : public MathOperationDataModel RandomNumberModel() { this->setNodeProcessingStatus(QtNodes::NodeProcessingStatus::Empty); + QObject::connect(this, &NodeDelegateModel::computingStarted, this, [this]() { if (_number1.lock() && _number2.lock()) { this->setNodeProcessingStatus( diff --git a/examples/calculator/NumberDisplayDataModel.cpp b/examples/calculator/NumberDisplayDataModel.cpp index 6f58f585..5086050c 100644 --- a/examples/calculator/NumberDisplayDataModel.cpp +++ b/examples/calculator/NumberDisplayDataModel.cpp @@ -4,9 +4,7 @@ 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 f2564886..f6a7ca55 100644 --- a/examples/calculator/NumberSourceDataModel.cpp +++ b/examples/calculator/NumberSourceDataModel.cpp @@ -9,9 +9,7 @@ NumberSourceDataModel::NumberSourceDataModel() : _lineEdit{nullptr} , _number(std::make_shared(0.0)) -{ - this->setNodeProcessingStatus(QtNodes::NodeProcessingStatus::NoStatus); -} +{} QJsonObject NumberSourceDataModel::save() const { diff --git a/include/QtNodes/internal/NodeDelegateModel.hpp b/include/QtNodes/internal/NodeDelegateModel.hpp index 7e70c69c..6365fb7d 100644 --- a/include/QtNodes/internal/NodeDelegateModel.hpp +++ b/include/QtNodes/internal/NodeDelegateModel.hpp @@ -121,6 +121,12 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel /// Convenience helper to change the node background color. void setBackgroundColor(QColor const &color); + QPixmap processingStatusIcon() const; + + void setStatusIcon(NodeProcessingStatus status, const QPixmap &pixmap); + + void setStatusIconStyle(ProcessingIconStyle const &style); + public: virtual void setInData(std::shared_ptr nodeData, PortIndex const portIndex) = 0; diff --git a/include/QtNodes/internal/NodeGraphicsObject.hpp b/include/QtNodes/internal/NodeGraphicsObject.hpp index 69eb2ba6..b3c01b90 100644 --- a/include/QtNodes/internal/NodeGraphicsObject.hpp +++ b/include/QtNodes/internal/NodeGraphicsObject.hpp @@ -53,14 +53,6 @@ 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, @@ -90,16 +82,5 @@ 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/resources/status_icons/empty.svg b/resources/status_icons/empty.svg index 07da93e8..a089016c 100644 --- a/resources/status_icons/empty.svg +++ b/resources/status_icons/empty.svg @@ -1,4 +1,7 @@ <<<<<<< HEAD +<<<<<<< HEAD +======= +>>>>>>> a973ef0 (pull new node_processing_status and solve conflicts) @@ -10,6 +13,7 @@ +<<<<<<< HEAD ======= @@ -35,3 +39,6 @@ >>>>>>> 5d30a5d (solve conflicts) +======= + +>>>>>>> a973ef0 (pull new node_processing_status and solve conflicts) diff --git a/resources/status_icons/failed.svg b/resources/status_icons/failed.svg index e21f8f0b..aee4f83a 100644 --- a/resources/status_icons/failed.svg +++ b/resources/status_icons/failed.svg @@ -1,4 +1,7 @@ <<<<<<< HEAD +<<<<<<< HEAD +======= +>>>>>>> a973ef0 (pull new node_processing_status and solve conflicts) @@ -11,6 +14,7 @@ +<<<<<<< HEAD ======= @@ -93,3 +97,7 @@ >>>>>>> 5d30a5d (solve conflicts) +======= + + +>>>>>>> a973ef0 (pull new node_processing_status and solve conflicts) diff --git a/resources/status_icons/partial.svg b/resources/status_icons/partial.svg index 4f3a2f51..7355ae87 100644 --- a/resources/status_icons/partial.svg +++ b/resources/status_icons/partial.svg @@ -1,4 +1,7 @@ <<<<<<< HEAD +<<<<<<< HEAD +======= +>>>>>>> a973ef0 (pull new node_processing_status and solve conflicts) @@ -40,6 +43,7 @@ +<<<<<<< HEAD ======= @@ -69,3 +73,6 @@ >>>>>>> 5d30a5d (solve conflicts) +======= + +>>>>>>> a973ef0 (pull new node_processing_status and solve conflicts) diff --git a/resources/status_icons/pending.svg b/resources/status_icons/pending.svg index b1bcd857..f077b6a9 100644 --- a/resources/status_icons/pending.svg +++ b/resources/status_icons/pending.svg @@ -1,4 +1,7 @@ <<<<<<< HEAD +<<<<<<< HEAD +======= +>>>>>>> a973ef0 (pull new node_processing_status and solve conflicts) @@ -7,6 +10,7 @@ .st0 { fill: #f1c21b; } +<<<<<<< HEAD .st1 { fill: #fff; @@ -24,71 +28,22 @@ ======= +======= +>>>>>>> a973ef0 (pull new node_processing_status and solve conflicts) - - - - - - - image/svg+xml - - - - - - - + .st1 { + fill: #fff; + } + + + + + + + + +<<<<<<< HEAD >>>>>>> 5d30a5d (solve conflicts) +======= + +>>>>>>> a973ef0 (pull new node_processing_status and solve conflicts) diff --git a/resources/status_icons/processing.svg b/resources/status_icons/processing.svg index 12ba7792..555374ab 100644 --- a/resources/status_icons/processing.svg +++ b/resources/status_icons/processing.svg @@ -1,4 +1,7 @@ <<<<<<< HEAD +<<<<<<< HEAD +======= +>>>>>>> a973ef0 (pull new node_processing_status and solve conflicts) @@ -7,6 +10,7 @@ .st0 { fill: #233ea0; } +<<<<<<< HEAD .st1 { fill: #fffffe; @@ -22,82 +26,22 @@ ======= +======= +>>>>>>> a973ef0 (pull new node_processing_status and solve conflicts) - - - - - - - image/svg+xml - - - - - - - - - + .st1 { + fill: #fffffe; + } + + + + + + +<<<<<<< HEAD >>>>>>> 5d30a5d (solve conflicts) +======= + +>>>>>>> a973ef0 (pull new node_processing_status and solve conflicts) diff --git a/resources/status_icons/updated.svg b/resources/status_icons/updated.svg index 0209c3dc..08ef992f 100644 --- a/resources/status_icons/updated.svg +++ b/resources/status_icons/updated.svg @@ -1,4 +1,7 @@ <<<<<<< HEAD +<<<<<<< HEAD +======= +>>>>>>> a973ef0 (pull new node_processing_status and solve conflicts) @@ -12,6 +15,7 @@ +<<<<<<< HEAD ======= @@ -30,3 +34,6 @@ >>>>>>> 5d30a5d (solve conflicts) +======= + +>>>>>>> a973ef0 (pull new node_processing_status and solve conflicts) diff --git a/src/DefaultHorizontalNodeGeometry.cpp b/src/DefaultHorizontalNodeGeometry.cpp index 689aaf73..01674494 100644 --- a/src/DefaultHorizontalNodeGeometry.cpp +++ b/src/DefaultHorizontalNodeGeometry.cpp @@ -1,7 +1,6 @@ #include "DefaultHorizontalNodeGeometry.hpp" #include "AbstractGraphModel.hpp" #include "NodeData.hpp" -#include "NodeDelegateModel.hpp" #include #include @@ -67,9 +66,9 @@ void DefaultHorizontalNodeGeometry::recomputeSize(NodeId const nodeId) const height += 20; QVariant var = _graphModel.nodeData(nodeId, NodeRole::ProcessingStatus); - auto processingStatusValue = var.value(); + auto processingStatusValue = var.value(); - if (processingStatusValue != QtNodes::NodeProcessingStatus::NoStatus) + if (processingStatusValue != 0) height += 20; unsigned int inPortWidth = maxPortsTextAdvance(nodeId, PortType::In); From fc93e4de25a12f3048c6fa91ee52af28729ffdf5 Mon Sep 17 00:00:00 2001 From: g-abilio Date: Mon, 15 Dec 2025 15:43:26 -0300 Subject: [PATCH 25/26] solve minor errors --- .../QtNodes/internal/DefaultNodePainter.hpp | 4 +- .../QtNodes/internal/NodeDelegateModel.hpp | 6 --- resources/resources.qrc | 3 -- src/DefaultHorizontalNodeGeometry.cpp | 9 +--- src/NodeGraphicsObject.cpp | 50 ------------------- 5 files changed, 4 insertions(+), 68 deletions(-) diff --git a/include/QtNodes/internal/DefaultNodePainter.hpp b/include/QtNodes/internal/DefaultNodePainter.hpp index 2468510f..86f23642 100644 --- a/include/QtNodes/internal/DefaultNodePainter.hpp +++ b/include/QtNodes/internal/DefaultNodePainter.hpp @@ -1,9 +1,9 @@ #pragma once -#include - #include "AbstractNodePainter.hpp" #include "Definitions.hpp" +#include +#include namespace QtNodes { diff --git a/include/QtNodes/internal/NodeDelegateModel.hpp b/include/QtNodes/internal/NodeDelegateModel.hpp index 6365fb7d..7e70c69c 100644 --- a/include/QtNodes/internal/NodeDelegateModel.hpp +++ b/include/QtNodes/internal/NodeDelegateModel.hpp @@ -121,12 +121,6 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel /// Convenience helper to change the node background color. void setBackgroundColor(QColor const &color); - QPixmap processingStatusIcon() const; - - void setStatusIcon(NodeProcessingStatus status, const QPixmap &pixmap); - - void setStatusIconStyle(ProcessingIconStyle const &style); - public: virtual void setInData(std::shared_ptr nodeData, PortIndex const portIndex) = 0; diff --git a/resources/resources.qrc b/resources/resources.qrc index b3c851c8..f4073145 100644 --- a/resources/resources.qrc +++ b/resources/resources.qrc @@ -1,8 +1,6 @@ DefaultStyle.json -<<<<<<< HEAD -======= info-tooltip.svg status_icons/empty.svg status_icons/failed.svg @@ -10,6 +8,5 @@ status_icons/pending.svg status_icons/processing.svg status_icons/updated.svg ->>>>>>> 5d30a5d (solve conflicts) diff --git a/src/DefaultHorizontalNodeGeometry.cpp b/src/DefaultHorizontalNodeGeometry.cpp index 01674494..de7223de 100644 --- a/src/DefaultHorizontalNodeGeometry.cpp +++ b/src/DefaultHorizontalNodeGeometry.cpp @@ -56,16 +56,11 @@ void DefaultHorizontalNodeGeometry::recomputeSize(NodeId const nodeId) const height += _portSpacing / 2; } - height += _portSpasing; // space above caption - height += _portSpasing; // space below caption + height += _portSpacing; // space above caption + height += _portSpacing; // space below caption QVariant var = _graphModel.nodeData(nodeId, NodeRole::ProcessingStatus); - auto processingStatusValue = var.value(); - if (processingStatusValue != 0) - height += 20; - - QVariant var = _graphModel.nodeData(nodeId, NodeRole::ProcessingStatus); auto processingStatusValue = var.value(); if (processingStatusValue != 0) diff --git a/src/NodeGraphicsObject.cpp b/src/NodeGraphicsObject.cpp index 0d3f7d52..a0deea28 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -371,54 +371,4 @@ 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 -{ - auto iconPos = 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 From db79277df82792563671bfdb3e518a864d0a66df Mon Sep 17 00:00:00 2001 From: g-abilio Date: Mon, 15 Dec 2025 16:09:44 -0300 Subject: [PATCH 26/26] remove unnecessary changes --- .gitignore | 2 - .../calculator/LongProcessingRandomNumber.hpp | 83 ------------ examples/calculator/headless_main.cpp | 3 - examples/calculator/main.cpp | 3 - .../QtNodes/internal/DefaultNodePainter.hpp | 8 -- .../QtNodes/internal/NodeDelegateModel.hpp | 57 --------- .../QtNodes/internal/NodeGraphicsObject.hpp | 1 - include/QtNodes/internal/NodeStyle.hpp | 34 ----- resources/info-tooltip.svg | 4 - resources/resources.qrc | 11 +- src/DataFlowGraphModel.cpp | 70 ++++++----- src/DefaultHorizontalNodeGeometry.cpp | 5 - src/DefaultNodePainter.cpp | 119 ++---------------- src/NodeDelegateModel.cpp | 69 ---------- src/NodeGraphicsObject.cpp | 6 - 15 files changed, 47 insertions(+), 428 deletions(-) delete mode 100644 examples/calculator/LongProcessingRandomNumber.hpp delete mode 100644 resources/info-tooltip.svg diff --git a/.gitignore b/.gitignore index 552a9b2f..fc8ef420 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,4 @@ CMakeLists.txt.user build*/ .vscode/ -qt-build - tags diff --git a/examples/calculator/LongProcessingRandomNumber.hpp b/examples/calculator/LongProcessingRandomNumber.hpp deleted file mode 100644 index d64f4ba7..00000000 --- a/examples/calculator/LongProcessingRandomNumber.hpp +++ /dev/null @@ -1,83 +0,0 @@ -#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/headless_main.cpp b/examples/calculator/headless_main.cpp index 89103a4f..e4dafa02 100644 --- a/examples/calculator/headless_main.cpp +++ b/examples/calculator/headless_main.cpp @@ -1,6 +1,5 @@ #include "AdditionModel.hpp" #include "DivisionModel.hpp" -#include "LongProcessingRandomNumber.hpp" #include "MultiplicationModel.hpp" #include "NumberDisplayDataModel.hpp" #include "NumberSourceDataModel.hpp" @@ -28,8 +27,6 @@ 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 4d2504f0..980c0f23 100644 --- a/examples/calculator/main.cpp +++ b/examples/calculator/main.cpp @@ -14,7 +14,6 @@ #include "AdditionModel.hpp" #include "DivisionModel.hpp" -#include "LongProcessingRandomNumber.hpp" #include "MultiplicationModel.hpp" #include "NumberDisplayDataModel.hpp" #include "NumberSourceDataModel.hpp" @@ -41,8 +40,6 @@ 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 86f23642..041d2401 100644 --- a/include/QtNodes/internal/DefaultNodePainter.hpp +++ b/include/QtNodes/internal/DefaultNodePainter.hpp @@ -2,7 +2,6 @@ #include "AbstractNodePainter.hpp" #include "Definitions.hpp" -#include #include namespace QtNodes { @@ -32,12 +31,5 @@ class NODE_EDITOR_PUBLIC DefaultNodePainter : public AbstractNodePainter void drawEntryLabels(QPainter *painter, NodeGraphicsObject &ngo) const; void drawResizeRect(QPainter *painter, NodeGraphicsObject &ngo) const; - - void drawProcessingIndicator(QPainter *painter, NodeGraphicsObject &ngo) const; - - void drawValidationIcon(QPainter *painter, NodeGraphicsObject &ngo) const; - -private: - QIcon _toolTipIcon{"://info-tooltip.svg"}; }; } // namespace QtNodes diff --git a/include/QtNodes/internal/NodeDelegateModel.hpp b/include/QtNodes/internal/NodeDelegateModel.hpp index 7e70c69c..29fdc959 100644 --- a/include/QtNodes/internal/NodeDelegateModel.hpp +++ b/include/QtNodes/internal/NodeDelegateModel.hpp @@ -14,36 +14,6 @@ #include namespace QtNodes { -/** - * Describes whether a node configuration is usable and defines a description message - */ -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. - }; - bool isValid() { return _state == State::Valid; }; - QString const message() { return _stateMessage; } - State state() { return _state; } - - State _state{State::Valid}; - QString _stateMessage{""}; -}; - -/** -* Describes the node status, depending on its current situation -*/ -enum class NodeProcessingStatus : int { - NoStatus = 0, ///< No processing status is shown in the Node UI. - Updated = 1, ///< Node is up to date; its outputs reflect the current inputs and parameters. - Processing = 2, ///< Node is currently running a computation. - Pending = 3, ///< Node is out of date and waiting to be recomputed (e.g. manual/queued run). - Empty = 4, ///< Node has no valid input data; nothing to compute. - Failed = 5, ///< The last computation ended with an error. - Partial = 6, ///< Computation finished incompletely; only partial results are available. -}; class StyleCollection; @@ -79,9 +49,6 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel /// It is possible to hide port caption in GUI virtual bool portCaptionVisible(PortType, PortIndex) const { return false; } - /// Validation State will default to Valid, but you can manipulate it by overriding in an inherited class - virtual NodeValidationState validationState() const { return _nodeValidationState; } - /// Nicknames can be assigned to nodes and shown in GUI virtual QString label() const { return QString(); } @@ -91,16 +58,9 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel /// Controls whether the label can be edited or not virtual bool labelEditable() const { return false; } - /// Returns the curent processing status - virtual NodeProcessingStatus processingStatus() const { return _processingStatus; } - QJsonObject save() const override; void load(QJsonObject const &) override; - void setValidationState(const NodeValidationState &validationState); - - void setNodeProcessingStatus(NodeProcessingStatus status); - virtual unsigned int nPorts(PortType portType) const = 0; virtual NodeDataType dataType(PortType portType, PortIndex portIndex) const = 0; @@ -111,16 +71,6 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel void setNodeStyle(NodeStyle const &style); - void setProcessingIconStyle(ProcessingIconStyle new_style); - - QPixmap processingStatusIcon() const; - - void setStatusIcon(NodeProcessingStatus status, const QPixmap &pixmap); - - void setStatusIconStyle(ProcessingIconStyle 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; @@ -192,13 +142,6 @@ public Q_SLOTS: private: NodeStyle _nodeStyle; - - NodeValidationState _nodeValidationState; - - NodeProcessingStatus _processingStatus{NodeProcessingStatus::NoStatus}; }; } // 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 b3c01b90..eab83c76 100644 --- a/include/QtNodes/internal/NodeGraphicsObject.hpp +++ b/include/QtNodes/internal/NodeGraphicsObject.hpp @@ -1,6 +1,5 @@ #pragma once -#include #include #include diff --git a/include/QtNodes/internal/NodeStyle.hpp b/include/QtNodes/internal/NodeStyle.hpp index 9bfa5c72..8b31f3e3 100644 --- a/include/QtNodes/internal/NodeStyle.hpp +++ b/include/QtNodes/internal/NodeStyle.hpp @@ -8,25 +8,6 @@ namespace QtNodes { -/** - * Describes the position of the processing icon on the node ui - */ -enum class ProcessingIconPos { - BottomLeft = 0, /// icon on the bottom left position - BottomRight = 1, /// icon on the bottom right position -}; - -/** - * Defines the processing icon style; - */ -struct ProcessingIconStyle -{ - ProcessingIconPos _pos{ProcessingIconPos::BottomRight}; - double _size{20.0}; - double _margin{8.0}; - int _resolution{64}; -}; - class NODE_EDITOR_PUBLIC NodeStyle : public Style { public: @@ -46,12 +27,6 @@ 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; @@ -76,14 +51,5 @@ class NODE_EDITOR_PUBLIC NodeStyle : public Style float ConnectionPointDiameter; float Opacity; - - QIcon statusUpdated{QStringLiteral("://status_icons/updated.svg")}; - QIcon statusProcessing{QStringLiteral("://status_icons/processing.svg")}; - QIcon statusPending{QStringLiteral("://status_icons/pending.svg")}; - QIcon statusInvalid{QStringLiteral("://status_icons/failed.svg")}; - QIcon statusEmpty{QStringLiteral("://status_icons/empty.svg")}; - QIcon statusPartial{QStringLiteral("://status_icons/partial.svg")}; - - ProcessingIconStyle processingIconStyle{}; }; } // namespace QtNodes diff --git a/resources/info-tooltip.svg b/resources/info-tooltip.svg deleted file mode 100644 index bab5f333..00000000 --- a/resources/info-tooltip.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/resources/resources.qrc b/resources/resources.qrc index f4073145..20a0dc58 100644 --- a/resources/resources.qrc +++ b/resources/resources.qrc @@ -1,12 +1,5 @@ - - + + 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/src/DataFlowGraphModel.cpp b/src/DataFlowGraphModel.cpp index bdbdbdfa..f3f55a8a 100644 --- a/src/DataFlowGraphModel.cpp +++ b/src/DataFlowGraphModel.cpp @@ -63,48 +63,54 @@ NodeId DataFlowGraphModel::addNode(QString const nodeType) { std::unique_ptr model = _registry->create(nodeType); - if (!model) - return InvalidNodeId; + if (model) { + NodeId newId = newNodeId(); - NodeId newId = newNodeId(); + connect(model.get(), + &NodeDelegateModel::dataUpdated, + [newId, this](PortIndex const portIndex) { + onOutPortDataUpdated(newId, portIndex); + }); - connect(model.get(), &NodeDelegateModel::dataUpdated, [newId, this](PortIndex const portIndex) { - onOutPortDataUpdated(newId, portIndex); - }); + connect(model.get(), + &NodeDelegateModel::portsAboutToBeDeleted, + this, + [newId, this](PortType const portType, PortIndex const first, PortIndex const last) { + portsAboutToBeDeleted(newId, portType, first, last); + }); - connect(model.get(), - &NodeDelegateModel::portsAboutToBeDeleted, - this, - [newId, this](PortType const portType, PortIndex const first, PortIndex const last) { - portsAboutToBeDeleted(newId, portType, first, last); - }); + connect(model.get(), + &NodeDelegateModel::portsDeleted, + this, + &DataFlowGraphModel::portsDeleted); - connect(model.get(), &NodeDelegateModel::portsDeleted, this, &DataFlowGraphModel::portsDeleted); + connect(model.get(), + &NodeDelegateModel::portsAboutToBeInserted, + this, + [newId, this](PortType const portType, PortIndex const first, PortIndex const last) { + portsAboutToBeInserted(newId, portType, first, last); + }); - connect(model.get(), - &NodeDelegateModel::portsAboutToBeInserted, - this, - [newId, this](PortType const portType, PortIndex const first, PortIndex const last) { - portsAboutToBeInserted(newId, portType, first, last); - }); + connect(model.get(), + &NodeDelegateModel::portsInserted, + this, + &DataFlowGraphModel::portsInserted); - connect(model.get(), - &NodeDelegateModel::portsInserted, - this, - &DataFlowGraphModel::portsInserted); + connect(model.get(), &NodeDelegateModel::requestNodeUpdate, this, [newId, this]() { + Q_EMIT nodeUpdated(newId); + }); - connect(model.get(), &NodeDelegateModel::requestNodeUpdate, this, [newId, this]() { - Q_EMIT nodeUpdated(newId); - }); + _models[newId] = std::move(model); - _models[newId] = std::move(model); + _labels[newId] = _models[newId]->label(); + _labelsVisible[newId] = _models[newId]->labelVisible(); - _labels[newId] = _models[newId]->label(); - _labelsVisible[newId] = _models[newId]->labelVisible(); + Q_EMIT nodeCreated(newId); - Q_EMIT nodeCreated(newId); + return newId; + } - return newId; + return InvalidNodeId; } bool DataFlowGraphModel::connectionPossible(ConnectionId const connectionId) const @@ -287,7 +293,7 @@ QVariant DataFlowGraphModel::nodeData(NodeId nodeId, NodeRole role) const break; case NodeRole::Widget: { - auto w = model->embeddedWidget(); + auto *w = model->embeddedWidget(); result = QVariant::fromValue(w); } break; diff --git a/src/DefaultHorizontalNodeGeometry.cpp b/src/DefaultHorizontalNodeGeometry.cpp index de7223de..341cc5a7 100644 --- a/src/DefaultHorizontalNodeGeometry.cpp +++ b/src/DefaultHorizontalNodeGeometry.cpp @@ -61,11 +61,6 @@ void DefaultHorizontalNodeGeometry::recomputeSize(NodeId const nodeId) const QVariant var = _graphModel.nodeData(nodeId, NodeRole::ProcessingStatus); - auto processingStatusValue = var.value(); - - if (processingStatusValue != 0) - 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 40757418..c88f4b4e 100644 --- a/src/DefaultNodePainter.cpp +++ b/src/DefaultNodePainter.cpp @@ -33,12 +33,8 @@ void DefaultNodePainter::paint(QPainter *painter, NodeGraphicsObject &ngo) const drawEntryLabels(painter, ngo); - drawProcessingIndicator(painter, ngo); - drawResizeRect(painter, ngo); - drawValidationIcon(painter, ngo); - drawNodeLabel(painter, ngo); } @@ -56,36 +52,16 @@ void DefaultNodePainter::drawNodeRect(QPainter *painter, NodeGraphicsObject &ngo NodeStyle nodeStyle(json.object()); - QVariant var = model.nodeData(nodeId, NodeRole::ValidationState); - - QColor color = ngo.isSelected() ? nodeStyle.SelectedBoundaryColor - : nodeStyle.NormalBoundaryColor; - - auto validationState = NodeValidationState::State::Valid; - if (var.canConvert()) { - auto state = var.value(); - validationState = state._state; - switch (validationState) { - case NodeValidationState::State::Error: - color = nodeStyle.ErrorColor; - break; - case NodeValidationState::State::Warning: - color = nodeStyle.WarningColor; - break; - default: - break; - } - } + auto color = ngo.isSelected() ? nodeStyle.SelectedBoundaryColor : nodeStyle.NormalBoundaryColor; - float penWidth = ngo.nodeState().hovered() ? nodeStyle.HoveredPenWidth : nodeStyle.PenWidth; - if (validationState != NodeValidationState::State::Valid) { - float factor = (validationState == NodeValidationState::State::Error) ? 3.0f : 2.0f; - penWidth *= factor; + if (ngo.nodeState().hovered()) { + QPen p(color, nodeStyle.HoveredPenWidth); + painter->setPen(p); + } else { + QPen p(color, nodeStyle.PenWidth); + painter->setPen(p); } - 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); @@ -343,85 +319,4 @@ 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(); - - QSize size = geometry.size(nodeId); - - QPixmap pixmap = delegate->processingStatusIcon(); - NodeStyle nodeStyle = delegate->nodeStyle(); - - ProcessingIconStyle iconStyle = nodeStyle.processingIconStyle; - - qreal iconSize = iconStyle._size; - qreal margin = iconStyle._margin; - - qreal x = margin; - - if (iconStyle._pos == ProcessingIconPos::BottomRight) { - x = size.width() - iconSize - margin; - } - - QRect r(x, size.height() - iconSize - margin, iconSize, iconSize); - painter->drawPixmap(r, pixmap); -} - -void DefaultNodePainter::drawValidationIcon(QPainter *painter, NodeGraphicsObject &ngo) const -{ - AbstractGraphModel &model = ngo.graphModel(); - NodeId const nodeId = ngo.nodeId(); - AbstractNodeGeometry &geometry = ngo.nodeScene()->nodeGeometry(); - - QVariant var = model.nodeData(nodeId, NodeRole::ValidationState); - if (!var.canConvert()) - return; - - auto state = var.value(); - if (state._state == NodeValidationState::State::Valid) - return; - - QJsonDocument json = QJsonDocument::fromVariant(model.nodeData(nodeId, NodeRole::Style)); - NodeStyle nodeStyle(json.object()); - - QSize size = geometry.size(nodeId); - - QIcon icon(":/info-tooltip.svg"); - QSize iconSize(16, 16); - QPixmap pixmap = icon.pixmap(iconSize); - - QColor color = (state._state == NodeValidationState::State::Error) ? nodeStyle.ErrorColor - : nodeStyle.WarningColor; - - QPointF center(size.width(), 0.0); - center += QPointF(iconSize.width() / 2.0, -iconSize.height() / 2.0); - - painter->save(); - - // Draw a colored circle behind the icon to highlight validation issues - painter->setPen(Qt::NoPen); - 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); - - painter->restore(); -} } // namespace QtNodes diff --git a/src/NodeDelegateModel.cpp b/src/NodeDelegateModel.cpp index fc8f3e8d..94e47ad6 100644 --- a/src/NodeDelegateModel.cpp +++ b/src/NodeDelegateModel.cpp @@ -24,11 +24,6 @@ void NodeDelegateModel::load(QJsonObject const &) // } -void NodeDelegateModel::setValidationState(const NodeValidationState &validationState) -{ - _nodeValidationState = validationState; -} - ConnectionPolicy NodeDelegateModel::portConnectionPolicy(PortType portType, PortIndex) const { auto result = ConnectionPolicy::One; @@ -56,68 +51,4 @@ void NodeDelegateModel::setNodeStyle(NodeStyle const &style) _nodeStyle = style; } -QPixmap NodeDelegateModel::processingStatusIcon() const -{ - int resolution = _nodeStyle.processingIconStyle._resolution; - switch (_processingStatus) { - case NodeProcessingStatus::NoStatus: - return {}; - case NodeProcessingStatus::Updated: - return _nodeStyle.statusUpdated.pixmap(resolution); - case NodeProcessingStatus::Processing: - return _nodeStyle.statusProcessing.pixmap(resolution); - case NodeProcessingStatus::Pending: - return _nodeStyle.statusPending.pixmap(resolution); - case NodeProcessingStatus::Empty: - return _nodeStyle.statusEmpty.pixmap(resolution); - case NodeProcessingStatus::Failed: - return _nodeStyle.statusInvalid.pixmap(resolution); - case NodeProcessingStatus::Partial: - return _nodeStyle.statusPartial.pixmap(resolution); - } - - return {}; -} - -void NodeDelegateModel::setStatusIcon(NodeProcessingStatus status, const QPixmap &pixmap) -{ - switch (status) { - case NodeProcessingStatus::NoStatus: - break; - case NodeProcessingStatus::Updated: - _nodeStyle.statusUpdated = QIcon(pixmap); - break; - case NodeProcessingStatus::Processing: - _nodeStyle.statusProcessing = QIcon(pixmap); - break; - case NodeProcessingStatus::Pending: - _nodeStyle.statusPending = QIcon(pixmap); - break; - case NodeProcessingStatus::Empty: - _nodeStyle.statusEmpty = QIcon(pixmap); - break; - case NodeProcessingStatus::Failed: - _nodeStyle.statusInvalid = QIcon(pixmap); - break; - case NodeProcessingStatus::Partial: - _nodeStyle.statusPartial = QIcon(pixmap); - break; - } -} - -void NodeDelegateModel::setStatusIconStyle(const ProcessingIconStyle &style) -{ - _nodeStyle.processingIconStyle = 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 a0deea28..e6c0e312 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -63,8 +63,6 @@ NodeGraphicsObject::NodeGraphicsObject(BasicGraphicsScene &scene, NodeId nodeId) if (_nodeId == nodeId) setLockedState(); }); - - QVariant var = _graphModel.nodeData(_nodeId, NodeRole::ProcessingStatus); } AbstractGraphModel &NodeGraphicsObject::graphModel() const @@ -177,10 +175,6 @@ QVariant NodeGraphicsObject::itemChange(GraphicsItemChange change, const QVarian void NodeGraphicsObject::mousePressEvent(QGraphicsSceneMouseEvent *event) { - if (graphModel().nodeFlags(_nodeId) & NodeFlag::Locked) { - return; - } - AbstractNodeGeometry &geometry = nodeScene()->nodeGeometry(); for (PortType portToCheck : {PortType::In, PortType::Out}) {