Node editor
This commit is contained in:
parent
d389161f4a
commit
21cd3bd3f9
4 changed files with 479 additions and 18 deletions
|
|
@ -15,6 +15,7 @@
|
|||
#include <QtNodes/internal/UndoCommands.hpp>
|
||||
|
||||
#include <QAction>
|
||||
#include <QCheckBox>
|
||||
#include <QClipboard>
|
||||
#include <QContextMenuEvent>
|
||||
#include <QComboBox>
|
||||
|
|
@ -420,6 +421,25 @@ GraphEditorWidget::GraphEditorWidget(warppipe::Client *client,
|
|||
m_rulesScroll->setWidget(m_rulesContainer);
|
||||
m_sidebar->addTab(m_rulesScroll, QStringLiteral("RULES"));
|
||||
|
||||
m_nodeDetailsScroll = new QScrollArea();
|
||||
m_nodeDetailsScroll->setWidgetResizable(true);
|
||||
m_nodeDetailsScroll->setStyleSheet(m_mixerScroll->styleSheet());
|
||||
m_nodeDetailsContainer = new QWidget();
|
||||
m_nodeDetailsContainer->setStyleSheet(QStringLiteral("background: #1a1a1e;"));
|
||||
auto *nodeDetailsLayout = new QVBoxLayout(m_nodeDetailsContainer);
|
||||
nodeDetailsLayout->setContentsMargins(8, 8, 8, 8);
|
||||
nodeDetailsLayout->setSpacing(6);
|
||||
auto *noSelectionLabel = new QLabel(QStringLiteral("Select a node to view details"));
|
||||
noSelectionLabel->setStyleSheet(QStringLiteral(
|
||||
"color: #6a6a7a; font-style: italic; background: transparent;"));
|
||||
noSelectionLabel->setAlignment(Qt::AlignCenter);
|
||||
noSelectionLabel->setWordWrap(true);
|
||||
nodeDetailsLayout->addStretch();
|
||||
nodeDetailsLayout->addWidget(noSelectionLabel);
|
||||
nodeDetailsLayout->addStretch();
|
||||
m_nodeDetailsScroll->setWidget(m_nodeDetailsContainer);
|
||||
m_sidebar->addTab(m_nodeDetailsScroll, QStringLiteral("NODE"));
|
||||
|
||||
m_splitter = new QSplitter(Qt::Horizontal);
|
||||
m_splitter->addWidget(m_view);
|
||||
m_splitter->addWidget(m_sidebar);
|
||||
|
|
@ -549,8 +569,15 @@ GraphEditorWidget::GraphEditorWidget(warppipe::Client *client,
|
|||
rebuildMixerStrips();
|
||||
rebuildNodeMeters();
|
||||
rebuildRulesList();
|
||||
if (nodeId == m_selectedNodeId) {
|
||||
m_selectedNodeId = 0;
|
||||
clearNodeDetailsPanel();
|
||||
}
|
||||
});
|
||||
|
||||
connect(m_scene, &QGraphicsScene::selectionChanged, this,
|
||||
&GraphEditorWidget::onSelectionChanged);
|
||||
|
||||
m_saveTimer = new QTimer(this);
|
||||
m_saveTimer->setSingleShot(true);
|
||||
m_saveTimer->setInterval(1000);
|
||||
|
|
@ -884,34 +911,113 @@ void GraphEditorWidget::showNodeContextMenu(const QPoint &screenPos,
|
|||
|
||||
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()) {
|
||||
if (isSink) {
|
||||
bool ok = false;
|
||||
QString name = QInputDialog::getText(
|
||||
this, QStringLiteral("Create Virtual Sink"),
|
||||
QStringLiteral("Node name:"), QLineEdit::Normal, QString(), &ok);
|
||||
if (!ok || name.trimmed().isEmpty())
|
||||
return;
|
||||
|
||||
std::string nodeName = name.trimmed().toStdString();
|
||||
m_model->setPendingPosition(nodeName, scenePos);
|
||||
auto result = m_client->CreateVirtualSink(nodeName);
|
||||
if (!result.status.ok()) {
|
||||
QMessageBox::warning(this, QStringLiteral("Error"),
|
||||
QString::fromStdString(result.status.message));
|
||||
return;
|
||||
}
|
||||
m_model->refreshFromClient();
|
||||
return;
|
||||
}
|
||||
|
||||
std::string nodeName = name.trimmed().toStdString();
|
||||
static const QString kDialogStyle = QStringLiteral(
|
||||
"QDialog { background: #1e1e22; }"
|
||||
"QLabel { color: #ecf0f6; }"
|
||||
"QLineEdit { background: #2a2a32; color: #ecf0f6; border: 1px solid #3a3a44;"
|
||||
" border-radius: 4px; padding: 4px 8px; }"
|
||||
"QCheckBox { color: #ecf0f6; }"
|
||||
"QCheckBox::indicator { width: 16px; height: 16px; }"
|
||||
"QComboBox { background: #2a2a32; color: #ecf0f6; border: 1px solid #3a3a44;"
|
||||
" border-radius: 4px; padding: 4px 8px; }"
|
||||
"QComboBox::drop-down { border: none; }"
|
||||
"QComboBox QAbstractItemView { background: #2a2a32; color: #ecf0f6;"
|
||||
" selection-background-color: #3a3a44; }");
|
||||
|
||||
static const QString kButtonStyle = QStringLiteral(
|
||||
"QPushButton { background: #2e2e36; color: #ecf0f6; border: 1px solid #3a3a44;"
|
||||
" border-radius: 4px; padding: 6px 16px; }"
|
||||
"QPushButton:hover { background: #3a3a44; }");
|
||||
|
||||
QDialog dlg(this);
|
||||
dlg.setWindowTitle(QStringLiteral("Create Virtual Source"));
|
||||
dlg.setStyleSheet(kDialogStyle);
|
||||
|
||||
auto *form = new QFormLayout(&dlg);
|
||||
form->setContentsMargins(16, 16, 16, 16);
|
||||
form->setSpacing(8);
|
||||
|
||||
auto *nameEdit = new QLineEdit();
|
||||
nameEdit->setPlaceholderText(QStringLiteral("e.g. Desktop Audio"));
|
||||
form->addRow(QStringLiteral("Name:"), nameEdit);
|
||||
|
||||
auto *loopbackCheck = new QCheckBox(QStringLiteral("Loopback from another node"));
|
||||
form->addRow(loopbackCheck);
|
||||
|
||||
auto *targetCombo = new QComboBox();
|
||||
targetCombo->setEnabled(false);
|
||||
auto nodesResult = m_client->ListNodes();
|
||||
if (nodesResult.ok()) {
|
||||
for (const auto &node : nodesResult.value) {
|
||||
if (node.media_class.find("Sink") != std::string::npos ||
|
||||
node.media_class.find("Source") != std::string::npos) {
|
||||
QString label = QString::fromStdString(
|
||||
node.description.empty() ? node.name : node.description);
|
||||
targetCombo->addItem(label, QString::fromStdString(node.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
auto *targetLabel = new QLabel(QStringLiteral("Target Node:"));
|
||||
targetLabel->setEnabled(false);
|
||||
form->addRow(targetLabel, targetCombo);
|
||||
|
||||
connect(loopbackCheck, &QCheckBox::toggled, this, [=](bool checked) {
|
||||
targetCombo->setEnabled(checked);
|
||||
targetLabel->setEnabled(checked);
|
||||
});
|
||||
|
||||
auto *buttons = new QDialogButtonBox(
|
||||
QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
buttons->setStyleSheet(kButtonStyle);
|
||||
connect(buttons, &QDialogButtonBox::accepted, &dlg, &QDialog::accept);
|
||||
connect(buttons, &QDialogButtonBox::rejected, &dlg, &QDialog::reject);
|
||||
form->addRow(buttons);
|
||||
|
||||
if (dlg.exec() != QDialog::Accepted)
|
||||
return;
|
||||
|
||||
QString name = nameEdit->text().trimmed();
|
||||
if (name.isEmpty()) {
|
||||
QMessageBox::warning(this, QStringLiteral("Error"),
|
||||
QStringLiteral("Name cannot be empty."));
|
||||
return;
|
||||
}
|
||||
|
||||
std::string nodeName = name.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;
|
||||
warppipe::VirtualNodeOptions opts;
|
||||
if (loopbackCheck->isChecked()) {
|
||||
opts.behavior = warppipe::VirtualBehavior::kLoopback;
|
||||
opts.target_node = targetCombo->currentData().toString().toStdString();
|
||||
}
|
||||
|
||||
if (!status.ok()) {
|
||||
auto result = m_client->CreateVirtualSource(nodeName, opts);
|
||||
if (!result.status.ok()) {
|
||||
QMessageBox::warning(this, QStringLiteral("Error"),
|
||||
QString::fromStdString(status.message));
|
||||
QString::fromStdString(result.status.message));
|
||||
return;
|
||||
}
|
||||
|
||||
m_model->refreshFromClient();
|
||||
}
|
||||
|
||||
|
|
@ -1814,3 +1920,321 @@ void GraphEditorWidget::showAddRuleDialog(const std::string &prefillApp,
|
|||
m_client->AddRouteRule(rule);
|
||||
rebuildRulesList();
|
||||
}
|
||||
|
||||
void GraphEditorWidget::onSelectionChanged() {
|
||||
auto items = m_scene->selectedItems();
|
||||
|
||||
QtNodes::NodeId selected = 0;
|
||||
for (QGraphicsItem *item : items) {
|
||||
if (auto *ngo =
|
||||
qgraphicsitem_cast<QtNodes::NodeGraphicsObject *>(item)) {
|
||||
selected = ngo->nodeId();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (selected == m_selectedNodeId)
|
||||
return;
|
||||
|
||||
if (selected != 0) {
|
||||
m_selectedNodeId = selected;
|
||||
updateNodeDetailsPanel(selected);
|
||||
m_sidebar->setCurrentWidget(m_nodeDetailsScroll);
|
||||
} else {
|
||||
m_selectedNodeId = 0;
|
||||
clearNodeDetailsPanel();
|
||||
}
|
||||
}
|
||||
|
||||
void GraphEditorWidget::clearNodeDetailsPanel() {
|
||||
delete m_nodeDetailsScroll->takeWidget();
|
||||
m_nodeDetailsContainer = new QWidget();
|
||||
m_nodeDetailsContainer->setStyleSheet(QStringLiteral("background: #1a1a1e;"));
|
||||
auto *layout = new QVBoxLayout(m_nodeDetailsContainer);
|
||||
layout->setContentsMargins(8, 8, 8, 8);
|
||||
layout->setSpacing(6);
|
||||
auto *label = new QLabel(QStringLiteral("Select a node to view details"));
|
||||
label->setStyleSheet(QStringLiteral(
|
||||
"color: #6a6a7a; font-style: italic; background: transparent;"));
|
||||
label->setAlignment(Qt::AlignCenter);
|
||||
label->setWordWrap(true);
|
||||
layout->addStretch();
|
||||
layout->addWidget(label);
|
||||
layout->addStretch();
|
||||
m_nodeDetailsScroll->setWidget(m_nodeDetailsContainer);
|
||||
}
|
||||
|
||||
static QLabel *makeDetailHeader(const QString &text) {
|
||||
auto *label = new QLabel(text);
|
||||
label->setStyleSheet(QStringLiteral(
|
||||
"QLabel { color: #a0a8b6; font-size: 11px; font-weight: bold;"
|
||||
" background: transparent; }"));
|
||||
return label;
|
||||
}
|
||||
|
||||
static QLabel *makeDetailValue(const QString &text) {
|
||||
auto *label = new QLabel(text);
|
||||
label->setStyleSheet(QStringLiteral(
|
||||
"QLabel { color: #ecf0f6; font-size: 12px; background: transparent; }"));
|
||||
label->setWordWrap(true);
|
||||
label->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||
return label;
|
||||
}
|
||||
|
||||
void GraphEditorWidget::updateNodeDetailsPanel(QtNodes::NodeId nodeId) {
|
||||
const WarpNodeData *data = m_model->warpNodeData(nodeId);
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
const auto &info = data->info;
|
||||
WarpNodeType type = WarpGraphModel::classifyNode(info);
|
||||
bool isVirtual = type == WarpNodeType::kVirtualSink ||
|
||||
type == WarpNodeType::kVirtualSource;
|
||||
|
||||
delete m_nodeDetailsScroll->takeWidget();
|
||||
m_nodeDetailsContainer = new QWidget();
|
||||
m_nodeDetailsContainer->setStyleSheet(QStringLiteral("background: #1a1a1e;"));
|
||||
auto *layout = new QVBoxLayout(m_nodeDetailsContainer);
|
||||
layout->setContentsMargins(8, 8, 8, 8);
|
||||
layout->setSpacing(4);
|
||||
|
||||
auto addField = [&](const QString &header, const QString &value) {
|
||||
if (value.isEmpty())
|
||||
return;
|
||||
layout->addSpacing(4);
|
||||
layout->addWidget(makeDetailHeader(header));
|
||||
layout->addWidget(makeDetailValue(value));
|
||||
};
|
||||
|
||||
QString typeLabel;
|
||||
switch (type) {
|
||||
case WarpNodeType::kHardwareSink: typeLabel = QStringLiteral("Hardware Sink"); break;
|
||||
case WarpNodeType::kHardwareSource: typeLabel = QStringLiteral("Hardware Source"); break;
|
||||
case WarpNodeType::kVirtualSink: typeLabel = QStringLiteral("Virtual Sink"); break;
|
||||
case WarpNodeType::kVirtualSource: typeLabel = QStringLiteral("Virtual Source"); break;
|
||||
case WarpNodeType::kApplication: typeLabel = QStringLiteral("Application"); break;
|
||||
case WarpNodeType::kVideoSource: typeLabel = QStringLiteral("Video Source"); break;
|
||||
case WarpNodeType::kVideoSink: typeLabel = QStringLiteral("Video Sink"); break;
|
||||
default: typeLabel = QStringLiteral("Unknown"); break;
|
||||
}
|
||||
|
||||
auto *titleLabel = new QLabel(QString::fromStdString(
|
||||
info.description.empty() ? info.name : info.description));
|
||||
titleLabel->setStyleSheet(QStringLiteral(
|
||||
"QLabel { color: #ecf0f6; font-size: 14px; font-weight: bold;"
|
||||
" background: transparent; }"));
|
||||
titleLabel->setWordWrap(true);
|
||||
layout->addWidget(titleLabel);
|
||||
layout->addSpacing(2);
|
||||
|
||||
auto *typeBadge = new QLabel(typeLabel);
|
||||
typeBadge->setStyleSheet(QStringLiteral(
|
||||
"QLabel { color: #1a1a1e; background: %1; font-size: 10px;"
|
||||
" font-weight: bold; border-radius: 3px; padding: 2px 6px; }")
|
||||
.arg(isVirtual ? QStringLiteral("#4caf50")
|
||||
: QStringLiteral("#6a7a8a")));
|
||||
typeBadge->setFixedHeight(18);
|
||||
typeBadge->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
|
||||
layout->addWidget(typeBadge);
|
||||
|
||||
static const QString kEditStyle = QStringLiteral(
|
||||
"QLineEdit { background: #2a2a32; color: #ecf0f6;"
|
||||
" border: 1px solid #3a3a44; border-radius: 4px; padding: 4px 8px; }");
|
||||
static const QString kComboStyle = QStringLiteral(
|
||||
"QComboBox { background: #2a2a32; color: #ecf0f6;"
|
||||
" border: 1px solid #3a3a44; border-radius: 4px; padding: 4px 8px; }"
|
||||
"QComboBox::drop-down { border: none; }"
|
||||
"QComboBox QAbstractItemView { background: #2a2a32; color: #ecf0f6;"
|
||||
" selection-background-color: #3a3a44; }");
|
||||
static const QString kCheckStyle = QStringLiteral(
|
||||
"QCheckBox { color: #ecf0f6; background: transparent; }"
|
||||
"QCheckBox::indicator { width: 16px; height: 16px; }");
|
||||
static const QString kBtnStyle = QStringLiteral(
|
||||
"QPushButton { background: #2e2e36; color: #ecf0f6;"
|
||||
" border: 1px solid #3a3a44; border-radius: 4px;"
|
||||
" padding: 6px 16px; font-weight: bold; }"
|
||||
"QPushButton:hover { background: #3a3a44; }");
|
||||
static const QString kDeleteBtnStyle = QStringLiteral(
|
||||
"QPushButton { background: #b03030; color: #ecf0f6;"
|
||||
" border: 1px solid #d04040; border-radius: 4px;"
|
||||
" padding: 6px 16px; font-weight: bold; }"
|
||||
"QPushButton:hover { background: #c04040; }");
|
||||
|
||||
if (isVirtual) {
|
||||
bool isSource = type == WarpNodeType::kVirtualSource;
|
||||
auto vnResult = m_client->GetVirtualNodeInfo(info.id);
|
||||
|
||||
layout->addSpacing(4);
|
||||
layout->addWidget(makeDetailHeader(QStringLiteral("NAME")));
|
||||
auto *nameEdit = new QLineEdit(QString::fromStdString(info.name));
|
||||
nameEdit->setStyleSheet(kEditStyle);
|
||||
layout->addWidget(nameEdit);
|
||||
|
||||
QCheckBox *loopbackCheck = nullptr;
|
||||
QComboBox *targetCombo = nullptr;
|
||||
|
||||
if (isSource) {
|
||||
layout->addSpacing(8);
|
||||
loopbackCheck = new QCheckBox(QStringLiteral("Loopback from another node"));
|
||||
loopbackCheck->setStyleSheet(kCheckStyle);
|
||||
layout->addWidget(loopbackCheck);
|
||||
|
||||
layout->addSpacing(4);
|
||||
layout->addWidget(makeDetailHeader(QStringLiteral("TARGET NODE")));
|
||||
targetCombo = new QComboBox();
|
||||
targetCombo->setStyleSheet(kComboStyle);
|
||||
targetCombo->setEnabled(false);
|
||||
auto nodesResult = m_client->ListNodes();
|
||||
if (nodesResult.ok()) {
|
||||
for (const auto &node : nodesResult.value) {
|
||||
if (node.id.value == info.id.value)
|
||||
continue;
|
||||
if (node.media_class.find("Sink") != std::string::npos ||
|
||||
node.media_class.find("Source") != std::string::npos) {
|
||||
QString label = QString::fromStdString(
|
||||
node.description.empty() ? node.name : node.description);
|
||||
targetCombo->addItem(label, QString::fromStdString(node.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
layout->addWidget(targetCombo);
|
||||
|
||||
if (vnResult.ok() && vnResult.value.loopback) {
|
||||
loopbackCheck->setChecked(true);
|
||||
int idx = targetCombo->findData(
|
||||
QString::fromStdString(vnResult.value.target_node));
|
||||
if (idx >= 0)
|
||||
targetCombo->setCurrentIndex(idx);
|
||||
}
|
||||
|
||||
connect(loopbackCheck, &QCheckBox::toggled, targetCombo,
|
||||
&QWidget::setEnabled);
|
||||
}
|
||||
|
||||
layout->addSpacing(12);
|
||||
auto *applyBtn = new QPushButton(QStringLiteral("Apply Changes"));
|
||||
applyBtn->setStyleSheet(kBtnStyle);
|
||||
connect(applyBtn, &QPushButton::clicked, this,
|
||||
[this, nodeId, nameEdit, loopbackCheck, targetCombo, isSource]() {
|
||||
const WarpNodeData *d = m_model->warpNodeData(nodeId);
|
||||
if (!d) return;
|
||||
|
||||
QString newName = nameEdit->text().trimmed();
|
||||
if (newName.isEmpty()) {
|
||||
QMessageBox::warning(this, QStringLiteral("Error"),
|
||||
QStringLiteral("Name cannot be empty."));
|
||||
return;
|
||||
}
|
||||
|
||||
QPointF pos = m_model->nodeData(nodeId,
|
||||
QtNodes::NodeRole::Position).toPointF();
|
||||
|
||||
auto removeStatus = m_client->RemoveNode(d->info.id);
|
||||
if (!removeStatus.ok()) {
|
||||
QMessageBox::warning(this, QStringLiteral("Error"),
|
||||
QString::fromStdString(removeStatus.message));
|
||||
return;
|
||||
}
|
||||
|
||||
std::string nodeName = newName.toStdString();
|
||||
m_model->setPendingPosition(nodeName, pos);
|
||||
|
||||
warppipe::Status status;
|
||||
if (isSource) {
|
||||
warppipe::VirtualNodeOptions opts;
|
||||
if (loopbackCheck && loopbackCheck->isChecked() && targetCombo) {
|
||||
opts.behavior = warppipe::VirtualBehavior::kLoopback;
|
||||
opts.target_node =
|
||||
targetCombo->currentData().toString().toStdString();
|
||||
}
|
||||
auto result = m_client->CreateVirtualSource(nodeName, opts);
|
||||
status = result.status;
|
||||
} else {
|
||||
auto result = m_client->CreateVirtualSink(nodeName);
|
||||
status = result.status;
|
||||
}
|
||||
|
||||
if (!status.ok()) {
|
||||
QMessageBox::warning(this, QStringLiteral("Error"),
|
||||
QString::fromStdString(status.message));
|
||||
m_model->refreshFromClient();
|
||||
return;
|
||||
}
|
||||
|
||||
m_selectedNodeId = 0;
|
||||
m_model->refreshFromClient();
|
||||
|
||||
for (auto nid : m_model->allNodeIds()) {
|
||||
const WarpNodeData *nd = m_model->warpNodeData(nid);
|
||||
if (nd && nd->info.name == nodeName) {
|
||||
m_selectedNodeId = nid;
|
||||
updateNodeDetailsPanel(nid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto *mw = qobject_cast<QMainWindow *>(window()))
|
||||
mw->statusBar()->showMessage(
|
||||
QStringLiteral("Virtual node updated"), 4000);
|
||||
});
|
||||
layout->addWidget(applyBtn);
|
||||
|
||||
layout->addSpacing(8);
|
||||
auto *deleteBtn = new QPushButton(QStringLiteral("Delete Node"));
|
||||
deleteBtn->setStyleSheet(kDeleteBtnStyle);
|
||||
connect(deleteBtn, &QPushButton::clicked, this, [this, nodeId]() {
|
||||
const WarpNodeData *d = m_model->warpNodeData(nodeId);
|
||||
if (!d) return;
|
||||
auto status = m_client->RemoveNode(d->info.id);
|
||||
if (status.ok()) {
|
||||
m_model->refreshFromClient();
|
||||
clearNodeDetailsPanel();
|
||||
m_selectedNodeId = 0;
|
||||
} else {
|
||||
QMessageBox::warning(this, QStringLiteral("Error"),
|
||||
QString::fromStdString(status.message));
|
||||
}
|
||||
});
|
||||
layout->addWidget(deleteBtn);
|
||||
} else {
|
||||
addField(QStringLiteral("NAME"), QString::fromStdString(info.name));
|
||||
if (!info.description.empty() && info.description != info.name)
|
||||
addField(QStringLiteral("DESCRIPTION"),
|
||||
QString::fromStdString(info.description));
|
||||
addField(QStringLiteral("MEDIA CLASS"),
|
||||
QString::fromStdString(info.media_class));
|
||||
if (!info.application_name.empty())
|
||||
addField(QStringLiteral("APPLICATION"),
|
||||
QString::fromStdString(info.application_name));
|
||||
if (!info.process_binary.empty())
|
||||
addField(QStringLiteral("PROCESS"),
|
||||
QString::fromStdString(info.process_binary));
|
||||
if (!info.media_role.empty())
|
||||
addField(QStringLiteral("MEDIA ROLE"),
|
||||
QString::fromStdString(info.media_role));
|
||||
}
|
||||
|
||||
addField(QStringLiteral("NODE ID"),
|
||||
QString::number(info.id.value));
|
||||
|
||||
if (!data->inputPorts.empty()) {
|
||||
layout->addSpacing(8);
|
||||
layout->addWidget(makeDetailHeader(QStringLiteral("INPUT PORTS")));
|
||||
for (const auto &port : data->inputPorts) {
|
||||
layout->addWidget(makeDetailValue(
|
||||
QStringLiteral(" ") + QString::fromStdString(port.name)));
|
||||
}
|
||||
}
|
||||
|
||||
if (!data->outputPorts.empty()) {
|
||||
layout->addSpacing(8);
|
||||
layout->addWidget(makeDetailHeader(QStringLiteral("OUTPUT PORTS")));
|
||||
for (const auto &port : data->outputPorts) {
|
||||
layout->addWidget(makeDetailValue(
|
||||
QStringLiteral(" ") + QString::fromStdString(port.name)));
|
||||
}
|
||||
}
|
||||
|
||||
layout->addStretch();
|
||||
m_nodeDetailsScroll->setWidget(m_nodeDetailsContainer);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue