See More

#include "NodeGraphicsObject.hpp" #include #include #include #include #include "ConnectionGraphicsObject.hpp" #include "ConnectionState.hpp" #include "NodeGraphicsScene.hpp" #include "NodePainter.hpp" #include "Node.hpp" #include "NodeDataModel.hpp" #include "NodeConnectionInteraction.hpp" #include "StyleCollection.hpp" using QtNodes::NodeGraphicsObject; using QtNodes::Node; using QtNodes::NodeGraphicsScene; NodeGraphicsObject:: NodeGraphicsObject(NodeGraphicsScene & scene, NodeId nodeId) : _scene(scene) , _nodeId(nodeId) , _locked(false) , _proxyWidget(nullptr) { _scene.addItem(this); setFlag(QGraphicsItem::ItemDoesntPropagateOpacityToChildren, true); setFlag(QGraphicsItem::ItemIsFocusable, true); setFlag(QGraphicsItem::ItemIsMovable, true); setFlag(QGraphicsItem::ItemIsSelectable, true); setFlag(QGraphicsItem::ItemSendsScenePositionChanges, true); setCacheMode(QGraphicsItem::DeviceCoordinateCache); auto const & nodeStyle = nodeId.nodeDataModel()->nodeStyle(); { auto effect = new QGraphicsDropShadowEffect; effect->setOffset(4, 4); effect->setBlurRadius(20); effect->setColor(nodeStyle.ShadowColor); setGraphicsEffect(effect); } setOpacity(nodeStyle.Opacity); setAcceptHoverEvents(true); setZValue(0); embedQWidget(); // connect to the move signals to emit the move signals in NodeGraphicsScene //auto onMoveSlot = [this] { _scene.nodeMoved(_nodeId, pos()); }; //connect(this, &QGraphicsObject::xChanged, this, onMoveSlot); //connect(this, &QGraphicsObject::yChanged, this, onMoveSlot); } NodeGraphicsObject:: ~NodeGraphicsObject() { _scene.removeItem(this); } //void //NodeGraphicsObject:: //embedQWidget() //{ //NodeGeometry & geom = _nodeId.nodeGeometry(); //if (auto w = _nodeId.nodeDataModel()->embeddedWidget()) //{ //_proxyWidget = new QGraphicsProxyWidget(this); //_proxyWidget->setWidget(w); //_proxyWidget->setPreferredWidth(5); //geom.recalculateSize(); //if (w->sizePolicy().verticalPolicy() & QSizePolicy::ExpandFlag) //{ //// If the widget wants to use as much vertical space as possible, set it to have the geom's equivalentWidgetHeight. //_proxyWidget->setMinimumHeight(geom.equivalentWidgetHeight()); //} //_proxyWidget->setPos(geom.widgetPosition()); //update(); //_proxyWidget->setOpacity(1.0); //_proxyWidget->setFlag(QGraphicsItem::ItemIgnoresParentOpacity); //} //} QRectF NodeGraphicsObject:: boundingRect() const { return _nodeId.nodeGeometry().boundingRect(); } void NodeGraphicsObject:: setGeometryChanged() { prepareGeometryChange(); } void NodeGraphicsObject:: moveConnections() const { NodeState const & nodeState = _nodeId.nodeState(); for (PortType portType: {PortType::In, PortType::Out}) { auto const & connectionEntries = nodeState.getEntries(portType); for (auto const & connections : connectionEntries) { for (auto & con : connections) con.second->getConnectionGraphicsObject().move(); } } } void NodeGraphicsObject:: lock(bool locked) { _locked = locked; setFlag(QGraphicsItem::ItemIsMovable, !locked); setFlag(QGraphicsItem::ItemIsFocusable, !locked); setFlag(QGraphicsItem::ItemIsSelectable, !locked); } void NodeGraphicsObject:: paint(QPainter * painter, QStyleOptionGraphicsItem const * option, QWidget *) { painter->setClipRect(option->exposedRect); NodePainter::paint(painter, _nodeId, _scene); } QVariant NodeGraphicsObject:: itemChange(GraphicsItemChange change, const QVariant & value) { if (change == ItemPositionChange && scene()) { moveConnections(); } return QGraphicsItem::itemChange(change, value); } void NodeGraphicsObject:: mousePressEvent(QGraphicsSceneMouseEvent * event) { if (_locked) return; // deselect all other items after this one is selected if (!isSelected() && !(event->modifiers() & Qt::ControlModifier)) { _scene.clearSelection(); } for (PortType portToCheck: {PortType::In, PortType::Out}) { NodeGeometry const & nodeGeometry = _nodeId.nodeGeometry(); // TODO do not pass sceneTransform int const portIndex = nodeGeometry.checkHitScenePoint(portToCheck, event->scenePos(), sceneTransform()); if (portIndex != INVALID) { NodeState const & nodeState = _nodeId.nodeState(); std::unordered_map connections = nodeState.connections(portToCheck, portIndex); // start dragging existing connection if (!connections.empty() && portToCheck == PortType::In) { auto con = connections.begin()->second; NodeConnectionInteraction interaction(_nodeId, *con, _scene); interaction.disconnect(portToCheck); } else // initialize new Connection { if (portToCheck == PortType::Out) { auto const outPolicy = _node.nodeDataModel()->portOutConnectionPolicy(portIndex); if (!connections.empty() && outPolicy == NodeDataModel::ConnectionPolicy::One) { _scene.deleteConnection(*connections.begin()->second); } } // todo add to NodeGraphicsScene auto connection = _scene.createConnection(portToCheck, _node, portIndex); _node.nodeState().setConnection(portToCheck, portIndex, *connection); connection->getConnectionGraphicsObject().grabMouse(); } } } auto pos = event->pos(); auto & geom = _node.nodeGeometry(); auto & state = _node.nodeState(); if (_node.nodeDataModel()->resizable() && geom.resizeRect().contains(QPoint(pos.x(), pos.y()))) { state.setResizing(true); } } void NodeGraphicsObject:: mouseMoveEvent(QGraphicsSceneMouseEvent * event) { auto & geom = _node.nodeGeometry(); auto & state = _node.nodeState(); if (state.resizing()) { auto diff = event->pos() - event->lastPos(); if (auto w = _node.nodeDataModel()->embeddedWidget()) { prepareGeometryChange(); auto oldSize = w->size(); oldSize += QSize(diff.x(), diff.y()); w->setFixedSize(oldSize); _proxyWidget->setMinimumSize(oldSize); _proxyWidget->setMaximumSize(oldSize); _proxyWidget->setPos(geom.widgetPosition()); geom.recalculateSize(); update(); moveConnections(); event->accept(); } } else { QGraphicsObject::mouseMoveEvent(event); if (event->lastPos() != event->pos()) moveConnections(); event->ignore(); } QRectF r = scene()->sceneRect(); r = r.united(mapToScene(boundingRect()).boundingRect()); scene()->setSceneRect(r); } void NodeGraphicsObject:: mouseReleaseEvent(QGraphicsSceneMouseEvent * event) { auto & state = _node.nodeState(); state.setResizing(false); QGraphicsObject::mouseReleaseEvent(event); // position connections precisely after fast node move moveConnections(); } void NodeGraphicsObject:: hoverEnterEvent(QGraphicsSceneHoverEvent * event) { // bring all the colliding nodes to background QList overlapItems = collidingItems(); for (QGraphicsItem * item : overlapItems) { if (item->zValue() > 0.0) { item->setZValue(0.0); } } // bring this node forward setZValue(1.0); _node.nodeGeometry().setHovered(true); update(); _scene.nodeHovered(node(), event->screenPos()); event->accept(); } void NodeGraphicsObject:: hoverLeaveEvent(QGraphicsSceneHoverEvent * event) { _node.nodeGeometry().setHovered(false); update(); _scene.nodeHoverLeft(node()); event->accept(); } void NodeGraphicsObject:: hoverMoveEvent(QGraphicsSceneHoverEvent * event) { auto pos = event->pos(); auto & geom = _node.nodeGeometry(); if (_node.nodeDataModel()->resizable() && geom.resizeRect().contains(QPoint(pos.x(), pos.y()))) { setCursor(QCursor(Qt::SizeFDiagCursor)); } else { setCursor(QCursor()); } event->accept(); } void NodeGraphicsObject:: mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event) { QGraphicsItem::mouseDoubleClickEvent(event); _scene.nodeDoubleClicked(node()); } void NodeGraphicsObject:: contextMenuEvent(QGraphicsSceneContextMenuEvent * event) { _scene.nodeContextMenu(node(), mapToScene(event->pos())); }