#include "GraphEditorWidget.h" #include "WarpGraphModel.h" #include #include #include #include #include #include #include GraphEditorWidget::GraphEditorWidget(warppipe::Client *client, QWidget *parent) : QWidget(parent), m_client(client) { m_model = new WarpGraphModel(client, this); m_scene = new QtNodes::BasicGraphicsScene(*m_model, this); m_view = new QtNodes::GraphicsView(m_scene); auto *layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(m_view); m_view->setContextMenuPolicy(Qt::CustomContextMenu); connect(m_view, &QWidget::customContextMenuRequested, this, &GraphEditorWidget::onContextMenuRequested); m_model->refreshFromClient(); m_refreshTimer = new QTimer(this); connect(m_refreshTimer, &QTimer::timeout, this, &GraphEditorWidget::onRefreshTimer); m_refreshTimer->start(500); } void GraphEditorWidget::onRefreshTimer() { m_model->refreshFromClient(); } void GraphEditorWidget::onContextMenuRequested(const QPoint &pos) { QPointF scenePos = m_view->mapToScene(pos); uint32_t hitPwNodeId = 0; for (auto nodeId : m_model->allNodeIds()) { const WarpNodeData *data = m_model->warpNodeData(nodeId); if (!data) { continue; } QPointF nodePos = m_model->nodeData(nodeId, QtNodes::NodeRole::Position).toPointF(); QSize nodeSize = m_model->nodeData(nodeId, QtNodes::NodeRole::Size).toSize(); QRectF nodeRect(nodePos, QSizeF(nodeSize)); if (nodeRect.contains(scenePos)) { hitPwNodeId = data->info.id.value; break; } } QPoint screenPos = m_view->mapToGlobal(pos); if (hitPwNodeId != 0) { showNodeContextMenu(screenPos, hitPwNodeId); } else { showCanvasContextMenu(screenPos, scenePos); } } void GraphEditorWidget::showCanvasContextMenu(const QPoint &screenPos, const QPointF &scenePos) { QMenu menu; QAction *createSink = menu.addAction(QStringLiteral("Create Virtual Sink")); QAction *createSource = menu.addAction(QStringLiteral("Create Virtual Source")); QAction *chosen = menu.exec(screenPos); if (chosen == createSink) { createVirtualNode(true, scenePos); } else if (chosen == createSource) { createVirtualNode(false, scenePos); } } void GraphEditorWidget::showNodeContextMenu(const QPoint &screenPos, uint32_t pwNodeId) { QtNodes::NodeId qtId = m_model->qtNodeIdForPw(pwNodeId); const WarpNodeData *data = m_model->warpNodeData(qtId); if (!data) { return; } WarpNodeType type = WarpGraphModel::classifyNode(data->info); bool isVirtual = type == WarpNodeType::kVirtualSink || type == WarpNodeType::kVirtualSource; if (!isVirtual) { return; } QMenu menu; QAction *deleteAction = menu.addAction(QStringLiteral("Delete Node")); QAction *chosen = menu.exec(screenPos); if (chosen == deleteAction && m_client) { m_client->RemoveNode(warppipe::NodeId{pwNodeId}); m_model->refreshFromClient(); } } void GraphEditorWidget::createVirtualNode(bool isSink, const QPointF &scenePos) { QString label = isSink ? QStringLiteral("Create Virtual Sink") : QStringLiteral("Create Virtual Source"); bool ok = false; QString name = QInputDialog::getText(this, label, QStringLiteral("Node name:"), QLineEdit::Normal, QString(), &ok); if (!ok || name.trimmed().isEmpty()) { return; } std::string nodeName = name.trimmed().toStdString(); m_model->setPendingPosition(nodeName, scenePos); warppipe::Status status; if (isSink) { auto result = m_client->CreateVirtualSink(nodeName); status = result.status; } else { auto result = m_client->CreateVirtualSource(nodeName); status = result.status; } if (!status.ok()) { QMessageBox::warning(this, QStringLiteral("Error"), QString::fromStdString(status.message)); return; } m_model->refreshFromClient(); }