diff --git a/examples/dynamic_ports/DynamicPortsModel.cpp b/examples/dynamic_ports/DynamicPortsModel.cpp index 1d8d537e1..2af46b857 100644 --- a/examples/dynamic_ports/DynamicPortsModel.cpp +++ b/examples/dynamic_ports/DynamicPortsModel.cpp @@ -143,6 +143,10 @@ QVariant DynamicPortsModel::nodeData(NodeId nodeId, NodeRole role) const result = _nodePortCounts[nodeId].out; break; + case NodeRole::WidgetEmbeddable: + result = true; + break; + case NodeRole::Widget: { result = QVariant::fromValue(widget(nodeId)); break; @@ -194,6 +198,9 @@ bool DynamicPortsModel::setNodeData(NodeId nodeId, NodeRole role, QVariant value widget(nodeId)->populateButtons(PortType::Out, value.toUInt()); break; + case NodeRole::WidgetEmbeddable: + break; + case NodeRole::Widget: break; } diff --git a/examples/resizable_images/ImageLoaderModel.hpp b/examples/resizable_images/ImageLoaderModel.hpp index 394cf28a9..96785bcd0 100644 --- a/examples/resizable_images/ImageLoaderModel.hpp +++ b/examples/resizable_images/ImageLoaderModel.hpp @@ -18,7 +18,7 @@ using QtNodes::PortType; /// The model dictates the number of inputs and outputs for the Node. /// In this example it has no logic. -class ImageLoaderModel : public NodeDelegateModel +class ImageLoaderModel final : public NodeDelegateModel { Q_OBJECT diff --git a/examples/resizable_images/ImageShowModel.cpp b/examples/resizable_images/ImageShowModel.cpp index 1d62f9f3f..5e6e7844c 100644 --- a/examples/resizable_images/ImageShowModel.cpp +++ b/examples/resizable_images/ImageShowModel.cpp @@ -24,6 +24,11 @@ ImageShowModel::ImageShowModel() _label->installEventFilter(this); } +ImageShowModel::~ImageShowModel() +{ + delete _label; +} + unsigned int ImageShowModel::nPorts(PortType portType) const { unsigned int result = 1; diff --git a/examples/resizable_images/ImageShowModel.hpp b/examples/resizable_images/ImageShowModel.hpp index 7e4c8a06c..2874d301e 100644 --- a/examples/resizable_images/ImageShowModel.hpp +++ b/examples/resizable_images/ImageShowModel.hpp @@ -16,14 +16,14 @@ using QtNodes::PortType; /// The model dictates the number of inputs and outputs for the Node. /// In this example it has no logic. -class ImageShowModel : public NodeDelegateModel +class ImageShowModel final : public NodeDelegateModel { Q_OBJECT public: ImageShowModel(); - ~ImageShowModel() = default; + ~ImageShowModel() override; public: QString caption() const override { return QString("Image Display"); } @@ -41,6 +41,8 @@ class ImageShowModel : public NodeDelegateModel void setInData(std::shared_ptr nodeData, PortIndex const port) override; + bool widgetEmbeddable() const override { return false; } + QWidget *embeddedWidget() override { return _label; } bool resizable() const override { return true; } diff --git a/examples/resizable_images/main.cpp b/examples/resizable_images/main.cpp index 2e0e91e5f..9283ba674 100644 --- a/examples/resizable_images/main.cpp +++ b/examples/resizable_images/main.cpp @@ -38,6 +38,26 @@ int main(int argc, char *argv[]) GraphicsView view(&scene); + QObject::connect(&scene, + &DataFlowGraphicsScene::nodeDoubleClicked, + &dataFlowGraphModel, + [&dataFlowGraphModel](QtNodes::NodeId nodeId) { + QString name = dataFlowGraphModel + .nodeData(nodeId, QtNodes::NodeRole::Caption) + .value(); + + bool isEmbedded = dataFlowGraphModel + .nodeData(nodeId, QtNodes::NodeRole::WidgetEmbeddable) + .value(); + auto w = dataFlowGraphModel.nodeData(nodeId, QtNodes::NodeRole::Widget) + .value(); + + if (!isEmbedded && w) { + w->setWindowTitle(name + "_" + QString::number(nodeId)); + w->show(); + } + }); + view.setWindowTitle("Data Flow: Resizable Images"); view.resize(800, 600); // Center window. diff --git a/examples/simple_graph_model/SimpleGraphModel.cpp b/examples/simple_graph_model/SimpleGraphModel.cpp index 7c04440f0..249dfb0f5 100644 --- a/examples/simple_graph_model/SimpleGraphModel.cpp +++ b/examples/simple_graph_model/SimpleGraphModel.cpp @@ -121,6 +121,10 @@ QVariant SimpleGraphModel::nodeData(NodeId nodeId, NodeRole role) const result = 1u; break; + case NodeRole::WidgetEmbeddable: + result = true; + break; + case NodeRole::Widget: result = QVariant(); break; @@ -167,6 +171,9 @@ bool SimpleGraphModel::setNodeData(NodeId nodeId, NodeRole role, QVariant value) case NodeRole::OutPortCount: break; + case NodeRole::WidgetEmbeddable: + break; + case NodeRole::Widget: break; } diff --git a/examples/vertical_layout/SimpleGraphModel.cpp b/examples/vertical_layout/SimpleGraphModel.cpp index 5b5e3b8b1..1b68effc9 100644 --- a/examples/vertical_layout/SimpleGraphModel.cpp +++ b/examples/vertical_layout/SimpleGraphModel.cpp @@ -121,6 +121,10 @@ QVariant SimpleGraphModel::nodeData(NodeId nodeId, NodeRole role) const result = 3u; break; + case NodeRole::WidgetEmbeddable: + result = true; + break; + case NodeRole::Widget: result = QVariant(); break; @@ -167,6 +171,9 @@ bool SimpleGraphModel::setNodeData(NodeId nodeId, NodeRole role, QVariant value) case NodeRole::OutPortCount: break; + case NodeRole::WidgetEmbeddable: + break; + case NodeRole::Widget: break; } diff --git a/include/QtNodes/internal/Definitions.hpp b/include/QtNodes/internal/Definitions.hpp index 863fa40b4..53e165aaf 100644 --- a/include/QtNodes/internal/Definitions.hpp +++ b/include/QtNodes/internal/Definitions.hpp @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include "Export.hpp" @@ -22,16 +22,17 @@ 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, ///< `QPointF` positon of the node on the scene. + Size, ///< `QSize` for resizable nodes. + CaptionVisible, ///< `bool` for caption visibility. + Caption, ///< `QString` for node caption. + Style, ///< Custom NodeStyle as QJsonDocument + InternalData, ///< Node-stecific user data as QJsonObject + InPortCount, ///< `unsigned int` + OutPortCount, ///< `unsigned int` + WidgetEmbeddable, ///< `bool` for widget embeddability + Widget, ///< Optional `QWidget*` or `nullptr` }; Q_ENUM_NS(NodeRole) diff --git a/include/QtNodes/internal/NodeDelegateModel.hpp b/include/QtNodes/internal/NodeDelegateModel.hpp index 6301164db..a387350f2 100644 --- a/include/QtNodes/internal/NodeDelegateModel.hpp +++ b/include/QtNodes/internal/NodeDelegateModel.hpp @@ -20,7 +20,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 @@ -78,6 +80,8 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel : public QObject, public Serializable */ virtual QWidget *embeddedWidget() = 0; + virtual bool widgetEmbeddable() const { return true; } + virtual bool resizable() const { return false; } public Q_SLOTS: diff --git a/src/DataFlowGraphModel.cpp b/src/DataFlowGraphModel.cpp index a5ba8734f..2790257c5 100644 --- a/src/DataFlowGraphModel.cpp +++ b/src/DataFlowGraphModel.cpp @@ -203,7 +203,7 @@ QVariant DataFlowGraphModel::nodeData(NodeId nodeId, NodeRole role) const break; case NodeRole::CaptionVisible: - result = model->captionVisible(); + result = model->widgetEmbeddable() ? model->captionVisible() : true; break; case NodeRole::Caption: @@ -232,10 +232,17 @@ QVariant DataFlowGraphModel::nodeData(NodeId nodeId, NodeRole role) const result = model->nPorts(PortType::Out); break; + case NodeRole::WidgetEmbeddable: + result = model->widgetEmbeddable(); + break; + case NodeRole::Widget: { auto w = model->embeddedWidget(); result = QVariant::fromValue(w); } break; + + default: + break; } return result; @@ -245,7 +252,7 @@ NodeFlags DataFlowGraphModel::nodeFlags(NodeId nodeId) const { auto it = _models.find(nodeId); - if (it != _models.end() && it->second->resizable()) + if (it != _models.end() && it->second->widgetEmbeddable() && it->second->resizable()) return NodeFlag::Resizable; return NodeFlag::NoFlags; @@ -293,8 +300,14 @@ bool DataFlowGraphModel::setNodeData(NodeId nodeId, NodeRole role, QVariant valu case NodeRole::OutPortCount: break; + case NodeRole::WidgetEmbeddable: + break; + case NodeRole::Widget: break; + + default: + break; } return result; @@ -333,7 +346,9 @@ QVariant DataFlowGraphModel::portData(NodeId nodeId, case PortRole::Caption: result = model->portCaption(portType, portIndex); + break; + default: break; } diff --git a/src/DefaultHorizontalNodeGeometry.cpp b/src/DefaultHorizontalNodeGeometry.cpp index 466c5ef34..2f1fba7d9 100644 --- a/src/DefaultHorizontalNodeGeometry.cpp +++ b/src/DefaultHorizontalNodeGeometry.cpp @@ -32,7 +32,10 @@ void DefaultHorizontalNodeGeometry::recomputeSize(NodeId const nodeId) const { unsigned int height = maxVerticalPortsExtent(nodeId); - if (auto w = _graphModel.nodeData(nodeId, NodeRole::Widget)) { + bool isEmbedded = _graphModel.nodeData(nodeId, NodeRole::WidgetEmbeddable).value(); + auto w = _graphModel.nodeData(nodeId, NodeRole::Widget); + + if (isEmbedded && w) { height = std::max(height, static_cast(w->height())); } @@ -48,7 +51,7 @@ void DefaultHorizontalNodeGeometry::recomputeSize(NodeId const nodeId) const unsigned int width = inPortWidth + outPortWidth + 4 * _portSpasing; - if (auto w = _graphModel.nodeData(nodeId, NodeRole::Widget)) { + if (isEmbedded && w) { width += w->width(); } @@ -150,7 +153,10 @@ QPointF DefaultHorizontalNodeGeometry::widgetPosition(NodeId const nodeId) const unsigned int captionHeight = captionRect(nodeId).height(); - if (auto w = _graphModel.nodeData(nodeId, NodeRole::Widget)) { + bool isEmbedded = _graphModel.nodeData(nodeId, NodeRole::WidgetEmbeddable).value(); + auto w = _graphModel.nodeData(nodeId, NodeRole::Widget); + + if (isEmbedded && w) { // If the widget wants to use as much vertical space as possible, // place it immediately after the caption. if (w->sizePolicy().verticalPolicy() & QSizePolicy::ExpandFlag) { diff --git a/src/DefaultVerticalNodeGeometry.cpp b/src/DefaultVerticalNodeGeometry.cpp index 0254028ed..74b52292a 100644 --- a/src/DefaultVerticalNodeGeometry.cpp +++ b/src/DefaultVerticalNodeGeometry.cpp @@ -32,7 +32,10 @@ void DefaultVerticalNodeGeometry::recomputeSize(NodeId const nodeId) const { unsigned int height = _portSpasing; // maxHorizontalPortsExtent(nodeId); - if (auto w = _graphModel.nodeData(nodeId, NodeRole::Widget)) { + bool isEmbedded = _graphModel.nodeData(nodeId, NodeRole::WidgetEmbeddable).value(); + auto w = _graphModel.nodeData(nodeId, NodeRole::Widget); + + if (isEmbedded && w) { height = std::max(height, static_cast(w->height())); } @@ -64,7 +67,7 @@ void DefaultVerticalNodeGeometry::recomputeSize(NodeId const nodeId) const unsigned int width = std::max(totalInPortsWidth, totalOutPortsWidth); - if (auto w = _graphModel.nodeData(nodeId, NodeRole::Widget)) { + if (isEmbedded && w) { width = std::max(width, static_cast(w->width())); } @@ -177,7 +180,10 @@ QPointF DefaultVerticalNodeGeometry::widgetPosition(NodeId const nodeId) const unsigned int captionHeight = captionRect(nodeId).height(); - if (auto w = _graphModel.nodeData(nodeId, NodeRole::Widget)) { + bool isEmbedded = _graphModel.nodeData(nodeId, NodeRole::WidgetEmbeddable).value(); + auto w = _graphModel.nodeData(nodeId, NodeRole::Widget); + + if (isEmbedded && w) { // If the widget wants to use as much vertical space as possible, // place it immediately after the caption. if (w->sizePolicy().verticalPolicy() & QSizePolicy::ExpandFlag) { diff --git a/src/NodeGraphicsObject.cpp b/src/NodeGraphicsObject.cpp index aa727cdaa..e4aa4ff99 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -81,6 +81,9 @@ void NodeGraphicsObject::embedQWidget() AbstractNodeGeometry &geometry = nodeScene()->nodeGeometry(); geometry.recomputeSize(_nodeId); + if (!_graphModel.nodeData(_nodeId, NodeRole::WidgetEmbeddable).value()) + return; + if (auto w = _graphModel.nodeData(_nodeId, NodeRole::Widget).value()) { _proxyWidget = new QGraphicsProxyWidget(this); @@ -245,7 +248,8 @@ void NodeGraphicsObject::mouseMoveEvent(QGraphicsSceneMouseEvent *event) setSelected(true); } - if (_nodeState.resizing()) { + if (_nodeState.resizing() + && _graphModel.nodeData(_nodeId, NodeRole::WidgetEmbeddable).value()) { auto diff = event->pos() - event->lastPos(); if (auto w = _graphModel.nodeData(_nodeId, NodeRole::Widget)) {