Stuff
This commit is contained in:
parent
9ac56d0d0b
commit
750868c63f
4 changed files with 119 additions and 16 deletions
|
|
@ -182,6 +182,13 @@ GraphEditorWidget::GraphEditorWidget(warppipe::Client *client,
|
|||
m_scene = new QtNodes::BasicGraphicsScene(*m_model, this);
|
||||
m_scene->setItemIndexMethod(QGraphicsScene::BspTreeIndex);
|
||||
|
||||
connect(m_model, &WarpGraphModel::beginBatchUpdate, this, [this]() {
|
||||
m_scene->setItemIndexMethod(QGraphicsScene::NoIndex);
|
||||
});
|
||||
connect(m_model, &WarpGraphModel::endBatchUpdate, this, [this]() {
|
||||
m_scene->setItemIndexMethod(QGraphicsScene::BspTreeIndex);
|
||||
});
|
||||
|
||||
QtNodes::ConnectionStyle::setConnectionStyle(
|
||||
R"({"ConnectionStyle": {
|
||||
"ConstructionColor": "#b4b4c8",
|
||||
|
|
@ -644,6 +651,10 @@ GraphEditorWidget::~GraphEditorWidget() {
|
|||
if (m_client) {
|
||||
m_client->SetChangeCallback(nullptr);
|
||||
}
|
||||
m_meterTimer->stop();
|
||||
m_refreshTimer->stop();
|
||||
m_changeTimer->stop();
|
||||
m_saveTimer->stop();
|
||||
}
|
||||
|
||||
int GraphEditorWidget::nodeCount() const {
|
||||
|
|
|
|||
|
|
@ -208,8 +208,8 @@ QVariant WarpGraphModel::nodeData(QtNodes::NodeId nodeId,
|
|||
}
|
||||
case QtNodes::NodeRole::Widget: {
|
||||
auto wIt = m_volumeWidgets.find(nodeId);
|
||||
if (wIt != m_volumeWidgets.end())
|
||||
return QVariant::fromValue(wIt->second);
|
||||
if (wIt != m_volumeWidgets.end() && wIt->second)
|
||||
return QVariant::fromValue(wIt->second.data());
|
||||
return QVariant::fromValue(static_cast<QWidget *>(nullptr));
|
||||
}
|
||||
default:
|
||||
|
|
@ -329,11 +329,7 @@ bool WarpGraphModel::deleteNode(QtNodes::NodeId const nodeId) {
|
|||
m_sizes.erase(nodeId);
|
||||
m_volumeStates.erase(nodeId);
|
||||
m_styleCache.erase(nodeId);
|
||||
auto vwIt = m_volumeWidgets.find(nodeId);
|
||||
if (vwIt != m_volumeWidgets.end()) {
|
||||
delete vwIt->second;
|
||||
m_volumeWidgets.erase(vwIt);
|
||||
}
|
||||
m_volumeWidgets.erase(nodeId);
|
||||
Q_EMIT nodeDeleted(nodeId);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -356,9 +352,12 @@ void WarpGraphModel::refreshFromClient() {
|
|||
return;
|
||||
}
|
||||
|
||||
Q_EMIT beginBatchUpdate();
|
||||
m_refreshing = true;
|
||||
auto nodesResult = m_client->ListNodes();
|
||||
if (!nodesResult.ok()) {
|
||||
m_refreshing = false;
|
||||
Q_EMIT endBatchUpdate();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -497,7 +496,8 @@ void WarpGraphModel::refreshFromClient() {
|
|||
if (savedIt != m_savedPositions.end()) {
|
||||
m_positions.emplace(qtId, savedIt->second);
|
||||
} else {
|
||||
QPointF candidate = nextPosition(nodeIt->second);
|
||||
auto groupPos = findAppGroupPosition(nodeIt->second);
|
||||
QPointF candidate = groupPos.value_or(nextPosition(nodeIt->second));
|
||||
m_positions.emplace(qtId, findNonOverlappingPosition(candidate, nodeIt->second));
|
||||
}
|
||||
}
|
||||
|
|
@ -680,8 +680,8 @@ void WarpGraphModel::refreshFromClient() {
|
|||
cached.mute = mute;
|
||||
|
||||
auto wIt = m_volumeWidgets.find(qtId);
|
||||
if (wIt != m_volumeWidgets.end()) {
|
||||
auto *vw = static_cast<NodeVolumeWidget *>(wIt->second);
|
||||
if (wIt != m_volumeWidgets.end() && wIt->second) {
|
||||
auto *vw = static_cast<NodeVolumeWidget *>(wIt->second.data());
|
||||
if (!vw->isSliderDown()) {
|
||||
vw->setVolume(sliderVal);
|
||||
vw->setMuted(mute);
|
||||
|
|
@ -692,6 +692,7 @@ void WarpGraphModel::refreshFromClient() {
|
|||
}
|
||||
|
||||
m_refreshing = false;
|
||||
Q_EMIT endBatchUpdate();
|
||||
}
|
||||
|
||||
const WarpNodeData *
|
||||
|
|
@ -763,8 +764,61 @@ QPointF WarpGraphModel::nextPosition(const WarpNodeData &data) {
|
|||
return pos;
|
||||
}
|
||||
|
||||
std::string WarpGraphModel::appGroupKey(const warppipe::NodeInfo &info) {
|
||||
if (!info.application_name.empty())
|
||||
return info.application_name;
|
||||
if (!info.process_binary.empty())
|
||||
return info.process_binary;
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<QPointF> WarpGraphModel::findAppGroupPosition(const WarpNodeData &data) const {
|
||||
WarpNodeType type = classifyNode(data.info);
|
||||
if (type != WarpNodeType::kApplication)
|
||||
return std::nullopt;
|
||||
|
||||
std::string key = appGroupKey(data.info);
|
||||
if (key.empty())
|
||||
return std::nullopt;
|
||||
|
||||
double lowestBottom = -1.0;
|
||||
QPointF siblingPos;
|
||||
bool found = false;
|
||||
|
||||
for (const auto &[existingId, existingData] : m_nodes) {
|
||||
if (classifyNode(existingData.info) != WarpNodeType::kApplication)
|
||||
continue;
|
||||
if (appGroupKey(existingData.info) != key)
|
||||
continue;
|
||||
|
||||
auto posIt = m_positions.find(existingId);
|
||||
if (posIt == m_positions.end())
|
||||
continue;
|
||||
|
||||
QSizeF existingSize;
|
||||
auto sizeIt = m_sizes.find(existingId);
|
||||
if (sizeIt != m_sizes.end()) {
|
||||
existingSize = QSizeF(sizeIt->second);
|
||||
} else {
|
||||
existingSize = QSizeF(estimateNodeSize(existingData));
|
||||
}
|
||||
|
||||
double bottom = posIt->second.y() + existingSize.height();
|
||||
if (bottom > lowestBottom) {
|
||||
lowestBottom = bottom;
|
||||
siblingPos = posIt->second;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return std::nullopt;
|
||||
|
||||
return QPointF(siblingPos.x(), lowestBottom + kVerticalGap);
|
||||
}
|
||||
|
||||
QPointF WarpGraphModel::findNonOverlappingPosition(QPointF candidate,
|
||||
const WarpNodeData &data) const {
|
||||
const WarpNodeData &data) const {
|
||||
QSizeF newSize(estimateNodeSize(data));
|
||||
constexpr int kMaxAttempts = 50;
|
||||
|
||||
|
|
@ -801,6 +855,22 @@ bool WarpGraphModel::isGhost(QtNodes::NodeId nodeId) const {
|
|||
return m_ghostNodes.find(nodeId) != m_ghostNodes.end();
|
||||
}
|
||||
|
||||
bool WarpGraphModel::ghostConnectionExists(
|
||||
QtNodes::ConnectionId connectionId) const {
|
||||
return m_ghostConnections.find(connectionId) != m_ghostConnections.end();
|
||||
}
|
||||
|
||||
std::unordered_set<QtNodes::ConnectionId>
|
||||
WarpGraphModel::allGhostConnectionIds(QtNodes::NodeId nodeId) const {
|
||||
std::unordered_set<QtNodes::ConnectionId> result;
|
||||
for (const auto &conn : m_ghostConnections) {
|
||||
if (conn.outNodeId == nodeId || conn.inNodeId == nodeId) {
|
||||
result.insert(conn);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t WarpGraphModel::findPwNodeIdByName(const std::string &name) const {
|
||||
for (const auto &[qtId, data] : m_nodes) {
|
||||
if (data.info.name == name) {
|
||||
|
|
@ -855,8 +925,8 @@ void WarpGraphModel::setNodeVolumeState(QtNodes::NodeId nodeId,
|
|||
}
|
||||
|
||||
auto wIt = m_volumeWidgets.find(nodeId);
|
||||
if (wIt != m_volumeWidgets.end()) {
|
||||
auto *w = qobject_cast<NodeVolumeWidget *>(wIt->second);
|
||||
if (wIt != m_volumeWidgets.end() && wIt->second) {
|
||||
auto *w = qobject_cast<NodeVolumeWidget *>(wIt->second.data());
|
||||
if (w) {
|
||||
w->setVolume(volumeToSlider(state.volume));
|
||||
w->setMuted(state.mute);
|
||||
|
|
@ -1175,6 +1245,19 @@ void WarpGraphModel::autoArrange() {
|
|||
}
|
||||
}
|
||||
|
||||
std::sort(apps.ids.begin(), apps.ids.end(),
|
||||
[this](QtNodes::NodeId a, QtNodes::NodeId b) {
|
||||
auto itA = m_nodes.find(a);
|
||||
auto itB = m_nodes.find(b);
|
||||
if (itA == m_nodes.end() || itB == m_nodes.end())
|
||||
return a < b;
|
||||
std::string keyA = appGroupKey(itA->second.info);
|
||||
std::string keyB = appGroupKey(itB->second.info);
|
||||
if (keyA != keyB)
|
||||
return keyA < keyB;
|
||||
return a < b;
|
||||
});
|
||||
|
||||
auto layoutColumn = [&](Column &col, double xOffset) {
|
||||
double y = 0.0;
|
||||
for (QtNodes::NodeId id : col.ids) {
|
||||
|
|
|
|||
|
|
@ -8,8 +8,10 @@
|
|||
#include <QHash>
|
||||
#include <QPointF>
|
||||
#include <QSize>
|
||||
#include <QPointer>
|
||||
#include <QString>
|
||||
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
|
|
@ -77,6 +79,9 @@ public:
|
|||
const WarpNodeData *warpNodeData(QtNodes::NodeId nodeId) const;
|
||||
QtNodes::NodeId qtNodeIdForPw(uint32_t pwNodeId) const;
|
||||
bool isGhost(QtNodes::NodeId nodeId) const;
|
||||
bool ghostConnectionExists(QtNodes::ConnectionId connectionId) const;
|
||||
std::unordered_set<QtNodes::ConnectionId> allGhostConnectionIds(
|
||||
QtNodes::NodeId nodeId) const;
|
||||
void setPendingPosition(const std::string &nodeName, QPointF pos);
|
||||
static WarpNodeType classifyNode(const warppipe::NodeInfo &info);
|
||||
|
||||
|
|
@ -91,6 +96,8 @@ public:
|
|||
NodeVolumeState nodeVolumeState(QtNodes::NodeId nodeId) const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void beginBatchUpdate();
|
||||
void endBatchUpdate();
|
||||
void nodeVolumeChanged(QtNodes::NodeId nodeId, NodeVolumeState previous,
|
||||
NodeVolumeState current);
|
||||
|
||||
|
|
@ -120,6 +127,8 @@ private:
|
|||
static QVariant styleForNode(WarpNodeType type, bool ghost);
|
||||
QPointF nextPosition(const WarpNodeData &data);
|
||||
QPointF findNonOverlappingPosition(QPointF candidate, const WarpNodeData &data) const;
|
||||
std::optional<QPointF> findAppGroupPosition(const WarpNodeData &data) const;
|
||||
static std::string appGroupKey(const warppipe::NodeInfo &info);
|
||||
static QSize estimateNodeSize(const WarpNodeData &data);
|
||||
|
||||
warppipe::Client *m_client = nullptr;
|
||||
|
|
@ -158,6 +167,6 @@ private:
|
|||
ViewState m_savedViewState{};
|
||||
|
||||
std::unordered_map<QtNodes::NodeId, NodeVolumeState> m_volumeStates;
|
||||
std::unordered_map<QtNodes::NodeId, QWidget *> m_volumeWidgets;
|
||||
std::unordered_map<QtNodes::NodeId, QPointer<QWidget>> m_volumeWidgets;
|
||||
mutable std::unordered_map<QtNodes::NodeId, QVariant> m_styleCache;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -728,7 +728,7 @@ TEST_CASE("ghost connections preserved when node becomes ghost") {
|
|||
model.refreshFromClient();
|
||||
|
||||
REQUIRE(model.isGhost(appQt));
|
||||
REQUIRE(model.connectionExists(
|
||||
REQUIRE(model.ghostConnectionExists(
|
||||
QtNodes::ConnectionId{appQt, 0, sinkQt, 0}));
|
||||
}
|
||||
|
||||
|
|
@ -782,7 +782,7 @@ TEST_CASE("ghost connections survive save/load round-trip") {
|
|||
REQUIRE(appQt2 != 0);
|
||||
REQUIRE(model2.isGhost(appQt2));
|
||||
|
||||
auto conns = model2.allConnectionIds(appQt2);
|
||||
auto conns = model2.allGhostConnectionIds(appQt2);
|
||||
REQUIRE(conns.size() == 1);
|
||||
auto conn = *conns.begin();
|
||||
REQUIRE(conn.outNodeId == appQt2);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue