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 = new QtNodes::BasicGraphicsScene(*m_model, this);
|
||||||
m_scene->setItemIndexMethod(QGraphicsScene::BspTreeIndex);
|
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(
|
QtNodes::ConnectionStyle::setConnectionStyle(
|
||||||
R"({"ConnectionStyle": {
|
R"({"ConnectionStyle": {
|
||||||
"ConstructionColor": "#b4b4c8",
|
"ConstructionColor": "#b4b4c8",
|
||||||
|
|
@ -644,6 +651,10 @@ GraphEditorWidget::~GraphEditorWidget() {
|
||||||
if (m_client) {
|
if (m_client) {
|
||||||
m_client->SetChangeCallback(nullptr);
|
m_client->SetChangeCallback(nullptr);
|
||||||
}
|
}
|
||||||
|
m_meterTimer->stop();
|
||||||
|
m_refreshTimer->stop();
|
||||||
|
m_changeTimer->stop();
|
||||||
|
m_saveTimer->stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
int GraphEditorWidget::nodeCount() const {
|
int GraphEditorWidget::nodeCount() const {
|
||||||
|
|
|
||||||
|
|
@ -208,8 +208,8 @@ QVariant WarpGraphModel::nodeData(QtNodes::NodeId nodeId,
|
||||||
}
|
}
|
||||||
case QtNodes::NodeRole::Widget: {
|
case QtNodes::NodeRole::Widget: {
|
||||||
auto wIt = m_volumeWidgets.find(nodeId);
|
auto wIt = m_volumeWidgets.find(nodeId);
|
||||||
if (wIt != m_volumeWidgets.end())
|
if (wIt != m_volumeWidgets.end() && wIt->second)
|
||||||
return QVariant::fromValue(wIt->second);
|
return QVariant::fromValue(wIt->second.data());
|
||||||
return QVariant::fromValue(static_cast<QWidget *>(nullptr));
|
return QVariant::fromValue(static_cast<QWidget *>(nullptr));
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
@ -329,11 +329,7 @@ bool WarpGraphModel::deleteNode(QtNodes::NodeId const nodeId) {
|
||||||
m_sizes.erase(nodeId);
|
m_sizes.erase(nodeId);
|
||||||
m_volumeStates.erase(nodeId);
|
m_volumeStates.erase(nodeId);
|
||||||
m_styleCache.erase(nodeId);
|
m_styleCache.erase(nodeId);
|
||||||
auto vwIt = m_volumeWidgets.find(nodeId);
|
m_volumeWidgets.erase(nodeId);
|
||||||
if (vwIt != m_volumeWidgets.end()) {
|
|
||||||
delete vwIt->second;
|
|
||||||
m_volumeWidgets.erase(vwIt);
|
|
||||||
}
|
|
||||||
Q_EMIT nodeDeleted(nodeId);
|
Q_EMIT nodeDeleted(nodeId);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -356,9 +352,12 @@ void WarpGraphModel::refreshFromClient() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Q_EMIT beginBatchUpdate();
|
||||||
m_refreshing = true;
|
m_refreshing = true;
|
||||||
auto nodesResult = m_client->ListNodes();
|
auto nodesResult = m_client->ListNodes();
|
||||||
if (!nodesResult.ok()) {
|
if (!nodesResult.ok()) {
|
||||||
|
m_refreshing = false;
|
||||||
|
Q_EMIT endBatchUpdate();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -497,7 +496,8 @@ void WarpGraphModel::refreshFromClient() {
|
||||||
if (savedIt != m_savedPositions.end()) {
|
if (savedIt != m_savedPositions.end()) {
|
||||||
m_positions.emplace(qtId, savedIt->second);
|
m_positions.emplace(qtId, savedIt->second);
|
||||||
} else {
|
} 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));
|
m_positions.emplace(qtId, findNonOverlappingPosition(candidate, nodeIt->second));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -680,8 +680,8 @@ void WarpGraphModel::refreshFromClient() {
|
||||||
cached.mute = mute;
|
cached.mute = mute;
|
||||||
|
|
||||||
auto wIt = m_volumeWidgets.find(qtId);
|
auto wIt = m_volumeWidgets.find(qtId);
|
||||||
if (wIt != m_volumeWidgets.end()) {
|
if (wIt != m_volumeWidgets.end() && wIt->second) {
|
||||||
auto *vw = static_cast<NodeVolumeWidget *>(wIt->second);
|
auto *vw = static_cast<NodeVolumeWidget *>(wIt->second.data());
|
||||||
if (!vw->isSliderDown()) {
|
if (!vw->isSliderDown()) {
|
||||||
vw->setVolume(sliderVal);
|
vw->setVolume(sliderVal);
|
||||||
vw->setMuted(mute);
|
vw->setMuted(mute);
|
||||||
|
|
@ -692,6 +692,7 @@ void WarpGraphModel::refreshFromClient() {
|
||||||
}
|
}
|
||||||
|
|
||||||
m_refreshing = false;
|
m_refreshing = false;
|
||||||
|
Q_EMIT endBatchUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
const WarpNodeData *
|
const WarpNodeData *
|
||||||
|
|
@ -763,8 +764,61 @@ QPointF WarpGraphModel::nextPosition(const WarpNodeData &data) {
|
||||||
return pos;
|
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,
|
QPointF WarpGraphModel::findNonOverlappingPosition(QPointF candidate,
|
||||||
const WarpNodeData &data) const {
|
const WarpNodeData &data) const {
|
||||||
QSizeF newSize(estimateNodeSize(data));
|
QSizeF newSize(estimateNodeSize(data));
|
||||||
constexpr int kMaxAttempts = 50;
|
constexpr int kMaxAttempts = 50;
|
||||||
|
|
||||||
|
|
@ -801,6 +855,22 @@ bool WarpGraphModel::isGhost(QtNodes::NodeId nodeId) const {
|
||||||
return m_ghostNodes.find(nodeId) != m_ghostNodes.end();
|
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 {
|
uint32_t WarpGraphModel::findPwNodeIdByName(const std::string &name) const {
|
||||||
for (const auto &[qtId, data] : m_nodes) {
|
for (const auto &[qtId, data] : m_nodes) {
|
||||||
if (data.info.name == name) {
|
if (data.info.name == name) {
|
||||||
|
|
@ -855,8 +925,8 @@ void WarpGraphModel::setNodeVolumeState(QtNodes::NodeId nodeId,
|
||||||
}
|
}
|
||||||
|
|
||||||
auto wIt = m_volumeWidgets.find(nodeId);
|
auto wIt = m_volumeWidgets.find(nodeId);
|
||||||
if (wIt != m_volumeWidgets.end()) {
|
if (wIt != m_volumeWidgets.end() && wIt->second) {
|
||||||
auto *w = qobject_cast<NodeVolumeWidget *>(wIt->second);
|
auto *w = qobject_cast<NodeVolumeWidget *>(wIt->second.data());
|
||||||
if (w) {
|
if (w) {
|
||||||
w->setVolume(volumeToSlider(state.volume));
|
w->setVolume(volumeToSlider(state.volume));
|
||||||
w->setMuted(state.mute);
|
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) {
|
auto layoutColumn = [&](Column &col, double xOffset) {
|
||||||
double y = 0.0;
|
double y = 0.0;
|
||||||
for (QtNodes::NodeId id : col.ids) {
|
for (QtNodes::NodeId id : col.ids) {
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,10 @@
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
#include <QPointF>
|
#include <QPointF>
|
||||||
#include <QSize>
|
#include <QSize>
|
||||||
|
#include <QPointer>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
|
|
@ -77,6 +79,9 @@ public:
|
||||||
const WarpNodeData *warpNodeData(QtNodes::NodeId nodeId) const;
|
const WarpNodeData *warpNodeData(QtNodes::NodeId nodeId) const;
|
||||||
QtNodes::NodeId qtNodeIdForPw(uint32_t pwNodeId) const;
|
QtNodes::NodeId qtNodeIdForPw(uint32_t pwNodeId) const;
|
||||||
bool isGhost(QtNodes::NodeId nodeId) 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);
|
void setPendingPosition(const std::string &nodeName, QPointF pos);
|
||||||
static WarpNodeType classifyNode(const warppipe::NodeInfo &info);
|
static WarpNodeType classifyNode(const warppipe::NodeInfo &info);
|
||||||
|
|
||||||
|
|
@ -91,6 +96,8 @@ public:
|
||||||
NodeVolumeState nodeVolumeState(QtNodes::NodeId nodeId) const;
|
NodeVolumeState nodeVolumeState(QtNodes::NodeId nodeId) const;
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
|
void beginBatchUpdate();
|
||||||
|
void endBatchUpdate();
|
||||||
void nodeVolumeChanged(QtNodes::NodeId nodeId, NodeVolumeState previous,
|
void nodeVolumeChanged(QtNodes::NodeId nodeId, NodeVolumeState previous,
|
||||||
NodeVolumeState current);
|
NodeVolumeState current);
|
||||||
|
|
||||||
|
|
@ -120,6 +127,8 @@ private:
|
||||||
static QVariant styleForNode(WarpNodeType type, bool ghost);
|
static QVariant styleForNode(WarpNodeType type, bool ghost);
|
||||||
QPointF nextPosition(const WarpNodeData &data);
|
QPointF nextPosition(const WarpNodeData &data);
|
||||||
QPointF findNonOverlappingPosition(QPointF candidate, const WarpNodeData &data) const;
|
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);
|
static QSize estimateNodeSize(const WarpNodeData &data);
|
||||||
|
|
||||||
warppipe::Client *m_client = nullptr;
|
warppipe::Client *m_client = nullptr;
|
||||||
|
|
@ -158,6 +167,6 @@ private:
|
||||||
ViewState m_savedViewState{};
|
ViewState m_savedViewState{};
|
||||||
|
|
||||||
std::unordered_map<QtNodes::NodeId, NodeVolumeState> m_volumeStates;
|
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;
|
mutable std::unordered_map<QtNodes::NodeId, QVariant> m_styleCache;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -728,7 +728,7 @@ TEST_CASE("ghost connections preserved when node becomes ghost") {
|
||||||
model.refreshFromClient();
|
model.refreshFromClient();
|
||||||
|
|
||||||
REQUIRE(model.isGhost(appQt));
|
REQUIRE(model.isGhost(appQt));
|
||||||
REQUIRE(model.connectionExists(
|
REQUIRE(model.ghostConnectionExists(
|
||||||
QtNodes::ConnectionId{appQt, 0, sinkQt, 0}));
|
QtNodes::ConnectionId{appQt, 0, sinkQt, 0}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -782,7 +782,7 @@ TEST_CASE("ghost connections survive save/load round-trip") {
|
||||||
REQUIRE(appQt2 != 0);
|
REQUIRE(appQt2 != 0);
|
||||||
REQUIRE(model2.isGhost(appQt2));
|
REQUIRE(model2.isGhost(appQt2));
|
||||||
|
|
||||||
auto conns = model2.allConnectionIds(appQt2);
|
auto conns = model2.allGhostConnectionIds(appQt2);
|
||||||
REQUIRE(conns.size() == 1);
|
REQUIRE(conns.size() == 1);
|
||||||
auto conn = *conns.begin();
|
auto conn = *conns.begin();
|
||||||
REQUIRE(conn.outNodeId == appQt2);
|
REQUIRE(conn.outNodeId == appQt2);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue