Edit rules

This commit is contained in:
Joey Yakimowich-Payne 2026-01-30 16:25:07 -07:00
commit 7d4804a7f8
4 changed files with 74 additions and 5 deletions

View file

@ -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;

View file

@ -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;

View file

@ -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();
}

View file

@ -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;