GUI Milestone 5

This commit is contained in:
Joey Yakimowich-Payne 2026-01-30 06:30:20 -07:00
commit 79cced017e
6 changed files with 228 additions and 20 deletions

View file

@ -1,6 +1,11 @@
#include "WarpGraphModel.h"
#include <QColor>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QVariant>
@ -430,7 +435,12 @@ void WarpGraphModel::refreshFromClient() {
m_positions.emplace(qtId, pendingIt->second);
m_pendingPositions.erase(pendingIt);
} else {
m_positions.emplace(qtId, nextPosition(nodeIt->second));
auto savedIt = m_savedPositions.find(nodeInfo.name);
if (savedIt != m_savedPositions.end()) {
m_positions.emplace(qtId, savedIt->second);
} else {
m_positions.emplace(qtId, nextPosition(nodeIt->second));
}
}
Q_EMIT nodeCreated(qtId);
@ -625,6 +635,116 @@ WarpGraphModel::classifyNode(const warppipe::NodeInfo &info) {
return WarpNodeType::kUnknown;
}
void WarpGraphModel::saveLayout(const QString &path) const {
QJsonArray nodesArray;
for (const auto &[qtId, data] : m_nodes) {
auto posIt = m_positions.find(qtId);
if (posIt == m_positions.end()) {
continue;
}
QJsonObject nodeObj;
nodeObj["name"] = QString::fromStdString(data.info.name);
nodeObj["x"] = posIt->second.x();
nodeObj["y"] = posIt->second.y();
nodesArray.append(nodeObj);
}
QJsonObject root;
root["version"] = 1;
root["nodes"] = nodesArray;
QFileInfo fi(path);
QDir dir = fi.absoluteDir();
if (!dir.exists()) {
dir.mkpath(".");
}
QFile file(path);
if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
file.write(QJsonDocument(root).toJson(QJsonDocument::Compact));
}
}
bool WarpGraphModel::loadLayout(const QString &path) {
QFile file(path);
if (!file.open(QIODevice::ReadOnly)) {
return false;
}
QJsonDocument doc = QJsonDocument::fromJson(file.readAll());
if (!doc.isObject()) {
return false;
}
QJsonObject root = doc.object();
if (root["version"].toInt() != 1) {
return false;
}
m_savedPositions.clear();
QJsonArray nodesArray = root["nodes"].toArray();
for (const auto &val : nodesArray) {
QJsonObject obj = val.toObject();
std::string name = obj["name"].toString().toStdString();
double x = obj["x"].toDouble();
double y = obj["y"].toDouble();
m_savedPositions[name] = QPointF(x, y);
}
return !m_savedPositions.empty();
}
void WarpGraphModel::autoArrange() {
struct Column {
std::vector<QtNodes::NodeId> ids;
double maxWidth = 0.0;
};
Column sources;
Column apps;
Column sinks;
for (const auto &[qtId, data] : m_nodes) {
WarpNodeType type = classifyNode(data.info);
QSize sz = estimateNodeSize(data);
double w = sz.width();
switch (type) {
case WarpNodeType::kHardwareSource:
case WarpNodeType::kVirtualSource:
sources.ids.push_back(qtId);
sources.maxWidth = std::max(sources.maxWidth, w);
break;
case WarpNodeType::kApplication:
apps.ids.push_back(qtId);
apps.maxWidth = std::max(apps.maxWidth, w);
break;
default:
sinks.ids.push_back(qtId);
sinks.maxWidth = std::max(sinks.maxWidth, w);
break;
}
}
auto layoutColumn = [&](Column &col, double xOffset) {
double y = 0.0;
for (QtNodes::NodeId id : col.ids) {
auto it = m_nodes.find(id);
if (it == m_nodes.end()) continue;
QSize sz = estimateNodeSize(it->second);
m_positions[id] = QPointF(xOffset, y);
Q_EMIT nodePositionUpdated(id);
y += sz.height() + kVerticalGap;
}
};
double x = 0.0;
layoutColumn(sources, x);
x += sources.maxWidth + kHorizontalGap * 3;
layoutColumn(apps, x);
x += apps.maxWidth + kHorizontalGap * 3;
layoutColumn(sinks, x);
}
QVariant WarpGraphModel::styleForNode(WarpNodeType type, bool ghost) {
QtNodes::NodeStyle style = QtNodes::StyleCollection::nodeStyle();