#include "DefaultNodePainter.hpp" #include #include #include "AbstractGraphModel.hpp" #include "AbstractNodeGeometry.hpp" #include "BasicGraphicsScene.hpp" #include "ConnectionGraphicsObject.hpp" #include "ConnectionIdUtils.hpp" #include "NodeGraphicsObject.hpp" #include "NodeState.hpp" #include "StyleCollection.hpp" namespace QtNodes { void DefaultNodePainter::paint(QPainter *painter, NodeGraphicsObject &ngo) const { // TODO? //AbstractNodeGeometry & geometry = ngo.nodeScene()->nodeGeometry(); //geometry.recomputeSizeIfFontChanged(painter->font()); drawNodeRect(painter, ngo); drawConnectionPoints(painter, ngo); drawFilledConnectionPoints(painter, ngo); drawNodeCaption(painter, ngo); drawEntryLabels(painter, ngo); drawResizeRect(painter, ngo); } void DefaultNodePainter::drawNodeRect(QPainter *painter, NodeGraphicsObject &ngo) const { AbstractGraphModel &model = ngo.graphModel(); NodeId const nodeId = ngo.nodeId(); AbstractNodeGeometry &geometry = ngo.nodeScene()->nodeGeometry(); QSize size = geometry.size(nodeId); QJsonDocument json = QJsonDocument::fromVariant(model.nodeData(nodeId, NodeRole::Style)); 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); // } QPen pen = painter->pen(); pen.setBrush(color); pen.setWidth(2); painter->setPen(pen); painter->setBrush(nodeStyle.GradientColor0); QRectF boundary(0, 0, size.width(), size.height()); painter->drawRect(boundary); } void DefaultNodePainter::drawConnectionPoints(QPainter *painter, NodeGraphicsObject &ngo) const { AbstractGraphModel &model = ngo.graphModel(); NodeId const nodeId = ngo.nodeId(); AbstractNodeGeometry &geometry = ngo.nodeScene()->nodeGeometry(); QJsonDocument json = QJsonDocument::fromVariant(model.nodeData(nodeId, NodeRole::Style)); NodeStyle nodeStyle(json.object()); auto const &connectionStyle = StyleCollection::connectionStyle(); float diameter = nodeStyle.ConnectionPointDiameter; auto reducedDiameter = diameter * 0.6; for (PortType portType : {PortType::Out, PortType::In}) { size_t const n = model .nodeData(nodeId, (portType == PortType::Out) ? NodeRole::OutPortCount : NodeRole::InPortCount) .toUInt(); for (PortIndex portIndex = 0; portIndex < n; ++portIndex) { QPointF p = geometry.portPosition(nodeId, portType, portIndex); auto const &dataType = model.portData(nodeId, portType, portIndex, PortRole::DataType) .value(); double r = 1.0; NodeState const &state = ngo.nodeState(); if (auto const *cgo = state.connectionForReaction()) { PortType requiredPort = cgo->connectionState().requiredPort(); if (requiredPort == portType) { ConnectionId possibleConnectionId = makeCompleteConnectionId(cgo->connectionId(), nodeId, portIndex); bool const possible = model.connectionPossible(possibleConnectionId); auto cp = cgo->sceneTransform().map(cgo->endPoint(requiredPort)); cp = ngo.sceneTransform().inverted().map(cp); auto diff = cp - p; double dist = std::sqrt(QPointF::dotProduct(diff, diff)); if (possible) { double const thres = 40.0; r = (dist < thres) ? (2.0 - dist / thres) : 1.0; } else { double const thres = 80.0; r = (dist < thres) ? (dist / thres) : 1.0; } } } if (connectionStyle.useDataDefinedColors()) { painter->setBrush(connectionStyle.normalColor(dataType.id)); } else { painter->setBrush(nodeStyle.ConnectionPointColor); } painter->drawEllipse(p, reducedDiameter * r, reducedDiameter * r); } } if (ngo.nodeState().connectionForReaction()) { ngo.nodeState().resetConnectionForReaction(); } } void DefaultNodePainter::drawFilledConnectionPoints(QPainter *painter, NodeGraphicsObject &ngo) const { AbstractGraphModel &model = ngo.graphModel(); NodeId const nodeId = ngo.nodeId(); AbstractNodeGeometry &geometry = ngo.nodeScene()->nodeGeometry(); QJsonDocument json = QJsonDocument::fromVariant(model.nodeData(nodeId, NodeRole::Style)); NodeStyle nodeStyle(json.object()); auto diameter = nodeStyle.ConnectionPointDiameter; for (PortType portType : {PortType::Out, PortType::In}) { size_t const n = model .nodeData(nodeId, (portType == PortType::Out) ? NodeRole::OutPortCount : NodeRole::InPortCount) .toUInt(); for (PortIndex portIndex = 0; portIndex < n; ++portIndex) { QPointF p = geometry.portPosition(nodeId, portType, portIndex); auto const &connected = model.connections(nodeId, portType, portIndex); if (!connected.empty()) { auto const &dataType = model .portData(nodeId, portType, portIndex, PortRole::DataType) .value(); auto const &connectionStyle = StyleCollection::connectionStyle(); if (connectionStyle.useDataDefinedColors()) { QColor const c = connectionStyle.normalColor(dataType.id); painter->setPen(c); painter->setBrush(c); } else { painter->setPen(nodeStyle.FilledConnectionPointColor); painter->setBrush(nodeStyle.FilledConnectionPointColor); } painter->drawEllipse(p, diameter * 0.4, diameter * 0.4); } } } } void DefaultNodePainter::drawNodeCaption(QPainter *painter, NodeGraphicsObject &ngo) const { AbstractGraphModel &model = ngo.graphModel(); NodeId const nodeId = ngo.nodeId(); AbstractNodeGeometry &geometry = ngo.nodeScene()->nodeGeometry(); if (!model.nodeData(nodeId, NodeRole::CaptionVisible).toBool()) return; QString const name = model.nodeData(nodeId, NodeRole::Caption).toString(); QFont f({ "Arial", 10 }); f.setBold(true); QPointF position = geometry.captionPosition(nodeId); QJsonDocument json = QJsonDocument::fromVariant(model.nodeData(nodeId, NodeRole::Style)); NodeStyle nodeStyle(json.object()); painter->setBrush(QBrush("#f7aa1b")); QFontMetrics metrics(f); auto fontRect = metrics.boundingRect(name); QSize sizeH = geometry.size(nodeId); QRectF titleRect; int w = sizeH.width(); // titleRect.setX(2); // titleRect.setY(2); titleRect.setWidth(w); titleRect.setHeight(fontRect.height() + position.ry()); QPen pen = painter->pen(); pen.setWidth(0); painter->setPen(pen); painter->drawRect(titleRect); painter->setFont(f); painter->setPen(Qt::black); painter->drawText(position, name); f.setBold(false); painter->setFont(f); } void DefaultNodePainter::drawEntryLabels(QPainter *painter, NodeGraphicsObject &ngo) const { AbstractGraphModel &model = ngo.graphModel(); NodeId const nodeId = ngo.nodeId(); AbstractNodeGeometry &geometry = ngo.nodeScene()->nodeGeometry(); QJsonDocument json = QJsonDocument::fromVariant(model.nodeData(nodeId, NodeRole::Style)); NodeStyle nodeStyle(json.object()); for (PortType portType : {PortType::Out, PortType::In}) { unsigned int n = model.nodeData(nodeId, (portType == PortType::Out) ? NodeRole::OutPortCount : NodeRole::InPortCount); for (PortIndex portIndex = 0; portIndex < n; ++portIndex) { auto const &connected = model.connections(nodeId, portType, portIndex); QPointF p = geometry.portTextPosition(nodeId, portType, portIndex); if (connected.empty()) painter->setPen(nodeStyle.FontColorFaded); else painter->setPen(nodeStyle.FontColor); QString s; if (model.portData(nodeId, portType, portIndex, PortRole::CaptionVisible)) { s = model.portData(nodeId, portType, portIndex, PortRole::Caption); } else { auto portData = model.portData(nodeId, portType, portIndex, PortRole::DataType); s = portData.value().name; } painter->drawText(p, s); } } } void DefaultNodePainter::drawResizeRect(QPainter *painter, NodeGraphicsObject &ngo) const { AbstractGraphModel &model = ngo.graphModel(); NodeId const nodeId = ngo.nodeId(); AbstractNodeGeometry &geometry = ngo.nodeScene()->nodeGeometry(); if (model.nodeFlags(nodeId) & NodeFlag::Resizable) { painter->setBrush(Qt::gray); painter->drawEllipse(geometry.resizeHandleRect(nodeId)); } } } // namespace QtNodes