From 7d4804a7f8cfd0b1ad76bb3b0463c172ddc0e1ff Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Fri, 30 Jan 2026 16:25:07 -0700 Subject: [PATCH] Edit rules --- gui/GraphEditorWidget.cpp | 37 ++++++++++++++++++++++++++++++++++--- gui/GraphEditorWidget.h | 4 +++- gui/WarpGraphModel.cpp | 37 ++++++++++++++++++++++++++++++++++++- gui/WarpGraphModel.h | 1 + 4 files changed, 74 insertions(+), 5 deletions(-) diff --git a/gui/GraphEditorWidget.cpp b/gui/GraphEditorWidget.cpp index e05eef2..bb9061b 100644 --- a/gui/GraphEditorWidget.cpp +++ b/gui/GraphEditorWidget.cpp @@ -1448,6 +1448,10 @@ void GraphEditorWidget::rebuildRulesList() { " border-radius: 4px; padding: 6px 12px; }" "QPushButton:hover { background: #3a3a44; }" "QPushButton:pressed { background: #44444e; }"); + const QString editBtnStyle = QStringLiteral( + "QPushButton { background: transparent; color: #5070a0; border: none;" + " font-size: 14px; font-weight: bold; padding: 2px 6px; }" + "QPushButton:hover { color: #70a0e0; }"); const QString delBtnStyle = QStringLiteral( "QPushButton { background: transparent; color: #a05050; border: none;" " font-size: 14px; font-weight: bold; padding: 2px 6px; }" @@ -1500,10 +1504,23 @@ void GraphEditorWidget::rebuildRulesList() { cardLayout->addLayout(infoLayout, 1); + auto *editBtn = new QPushButton(QString(QChar(0x270E))); + editBtn->setFixedSize(24, 24); + editBtn->setStyleSheet(editBtnStyle); + warppipe::RuleId ruleId = rule.id; + std::string ruleApp = rule.match.application_name; + std::string ruleBin = rule.match.process_binary; + std::string ruleRole = rule.match.media_role; + std::string ruleTarget = rule.target_node; + connect(editBtn, &QPushButton::clicked, this, + [this, ruleApp, ruleBin, ruleRole, ruleTarget, ruleId]() { + showAddRuleDialog(ruleApp, ruleBin, ruleRole, ruleTarget, ruleId); + }); + cardLayout->addWidget(editBtn); + auto *delBtn = new QPushButton(QString(QChar(0x2715))); delBtn->setFixedSize(24, 24); delBtn->setStyleSheet(delBtnStyle); - warppipe::RuleId ruleId = rule.id; connect(delBtn, &QPushButton::clicked, this, [this, ruleId]() { m_client->RemoveRouteRule(ruleId); rebuildRulesList(); @@ -1525,12 +1542,17 @@ void GraphEditorWidget::rebuildRulesList() { void GraphEditorWidget::showAddRuleDialog(const std::string &prefillApp, const std::string &prefillBin, - const std::string &prefillRole) { + const std::string &prefillRole, + const std::string &prefillTarget, + warppipe::RuleId editRuleId) { if (!m_client) return; + bool editing = editRuleId.value != 0; + QDialog dlg(this); - dlg.setWindowTitle(QStringLiteral("Add Routing Rule")); + dlg.setWindowTitle(editing ? QStringLiteral("Edit Routing Rule") + : QStringLiteral("Add Routing Rule")); dlg.setStyleSheet(QStringLiteral( "QDialog { background: #1e1e22; }" "QLabel { color: #ecf0f6; }" @@ -1575,6 +1597,11 @@ void GraphEditorWidget::showAddRuleDialog(const std::string &prefillApp, } } } + if (!prefillTarget.empty()) { + int idx = targetCombo->findData(QString::fromStdString(prefillTarget)); + if (idx >= 0) + targetCombo->setCurrentIndex(idx); + } form->addRow(QStringLiteral("Target Node:"), targetCombo); auto *buttons = new QDialogButtonBox( @@ -1606,6 +1633,10 @@ void GraphEditorWidget::showAddRuleDialog(const std::string &prefillApp, return; } + if (editing) { + m_client->RemoveRouteRule(editRuleId); + } + warppipe::RouteRule rule; rule.match.application_name = appName; rule.match.process_binary = procBin; diff --git a/gui/GraphEditorWidget.h b/gui/GraphEditorWidget.h index deec287..a0bd9fd 100644 --- a/gui/GraphEditorWidget.h +++ b/gui/GraphEditorWidget.h @@ -76,7 +76,9 @@ private: void rebuildRulesList(); void showAddRuleDialog(const std::string &prefillApp = {}, const std::string &prefillBin = {}, - const std::string &prefillRole = {}); + const std::string &prefillRole = {}, + const std::string &prefillTarget = {}, + warppipe::RuleId editRuleId = {}); struct PendingPasteLink { std::string outNodeName; diff --git a/gui/WarpGraphModel.cpp b/gui/WarpGraphModel.cpp index a62a63a..2dcb566 100644 --- a/gui/WarpGraphModel.cpp +++ b/gui/WarpGraphModel.cpp @@ -466,7 +466,8 @@ void WarpGraphModel::refreshFromClient() { if (savedIt != m_savedPositions.end()) { m_positions.emplace(qtId, savedIt->second); } else { - m_positions.emplace(qtId, nextPosition(nodeIt->second)); + QPointF candidate = nextPosition(nodeIt->second); + m_positions.emplace(qtId, findNonOverlappingPosition(candidate, nodeIt->second)); } } @@ -698,6 +699,40 @@ QPointF WarpGraphModel::nextPosition(const WarpNodeData &data) { return pos; } +QPointF WarpGraphModel::findNonOverlappingPosition(QPointF candidate, + const WarpNodeData &data) const { + QSizeF newSize(estimateNodeSize(data)); + constexpr int kMaxAttempts = 50; + + for (int attempt = 0; attempt < kMaxAttempts; ++attempt) { + QRectF newRect(candidate, newSize); + bool overlaps = false; + for (const auto &[existingId, existingPos] : m_positions) { + auto nodeIt = m_nodes.find(existingId); + if (nodeIt == m_nodes.end()) + continue; + QSizeF existingSize; + auto sizeIt = m_sizes.find(existingId); + if (sizeIt != m_sizes.end()) { + existingSize = QSizeF(sizeIt->second); + } else { + existingSize = QSizeF(estimateNodeSize(nodeIt->second)); + } + QRectF existingRect(existingPos, existingSize); + QRectF padded = existingRect.adjusted(-kHorizontalGap / 2, -kVerticalGap / 2, + kHorizontalGap / 2, kVerticalGap / 2); + if (newRect.intersects(padded)) { + candidate.setY(existingRect.bottom() + kVerticalGap); + overlaps = true; + break; + } + } + if (!overlaps) + break; + } + return candidate; +} + bool WarpGraphModel::isGhost(QtNodes::NodeId nodeId) const { return m_ghostNodes.find(nodeId) != m_ghostNodes.end(); } diff --git a/gui/WarpGraphModel.h b/gui/WarpGraphModel.h index e667eb5..373b62c 100644 --- a/gui/WarpGraphModel.h +++ b/gui/WarpGraphModel.h @@ -102,6 +102,7 @@ private: static QString captionForNode(const warppipe::NodeInfo &info); static QVariant styleForNode(WarpNodeType type, bool ghost); QPointF nextPosition(const WarpNodeData &data); + QPointF findNonOverlappingPosition(QPointF candidate, const WarpNodeData &data) const; static QSize estimateNodeSize(const WarpNodeData &data); warppipe::Client *m_client = nullptr;