diff --git a/examples/resizable_images/ImageShowModel.cpp b/examples/resizable_images/ImageShowModel.cpp index 1d62f9f3..5e6e7844 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 7e4c8a06..edf27280 100644 --- a/examples/resizable_images/ImageShowModel.hpp +++ b/examples/resizable_images/ImageShowModel.hpp @@ -23,7 +23,7 @@ class ImageShowModel : public NodeDelegateModel public: ImageShowModel(); - ~ImageShowModel() = default; + virtual ~ImageShowModel(); 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 2e0e91e5..3b194eb5 100644 --- a/examples/resizable_images/main.cpp +++ b/examples/resizable_images/main.cpp @@ -38,6 +38,25 @@ 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::Type) + .value(); + + bool isEmbeded = dataFlowGraphModel + .nodeData(nodeId, QtNodes::NodeRole::WidgetEmbeddable) + .value(); + auto w = dataFlowGraphModel.nodeData(nodeId, QtNodes::NodeRole::Widget) + .value(); + + if (!isEmbeded && w) { + w->setWindowTitle(name + "_" + QString::number(nodeId)); + w->show(); + } + }); + view.setWindowTitle("Data Flow: Resizable Images"); view.resize(800, 600); // Center window. diff --git a/include/QtNodes/internal/Definitions.hpp b/include/QtNodes/internal/Definitions.hpp index 863fa40b..53e165aa 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 6301164d..a387350f 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 a5ba8734..0e05854e 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,6 +232,10 @@ 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); @@ -245,7 +249,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; diff --git a/src/DefaultHorizontalNodeGeometry.cpp b/src/DefaultHorizontalNodeGeometry.cpp index 466c5ef3..f05cff06 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 isEmbeded = _graphModel.nodeData(nodeId, NodeRole::WidgetEmbeddable).value(); + auto w = _graphModel.nodeData(nodeId, NodeRole::Widget); + + if (isEmbeded && 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 (isEmbeded && 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 isEmbeded = _graphModel.nodeData(nodeId, NodeRole::WidgetEmbeddable).value(); + auto w = _graphModel.nodeData(nodeId, NodeRole::Widget); + + if (isEmbeded && 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 0254028e..f6aecf07 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 isEmbeded = _graphModel.nodeData(nodeId, NodeRole::WidgetEmbeddable).value(); + auto w = _graphModel.nodeData(nodeId, NodeRole::Widget); + + if (isEmbeded && 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 (isEmbeded && 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 isEmbeded = _graphModel.nodeData(nodeId, NodeRole::WidgetEmbeddable).value(); + auto w = _graphModel.nodeData(nodeId, NodeRole::Widget); + + if (isEmbeded && 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 aa727cda..e4aa4ff9 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)) {