Fix perf issues and some visual bugs
This commit is contained in:
parent
00e997a204
commit
d389161f4a
5 changed files with 300 additions and 11 deletions
|
|
@ -4,6 +4,7 @@
|
|||
#include "SquareConnectionPainter.h"
|
||||
#include "VolumeWidgets.h"
|
||||
#include "WarpGraphModel.h"
|
||||
#include "ZoomGraphicsView.h"
|
||||
|
||||
#include <QtNodes/BasicGraphicsScene>
|
||||
#include <QtNodes/ConnectionStyle>
|
||||
|
|
@ -178,6 +179,7 @@ GraphEditorWidget::GraphEditorWidget(warppipe::Client *client,
|
|||
bool hasLayout = m_model->loadLayout(m_layoutPath);
|
||||
|
||||
m_scene = new QtNodes::BasicGraphicsScene(*m_model, this);
|
||||
m_scene->setItemIndexMethod(QGraphicsScene::BspTreeIndex);
|
||||
|
||||
QtNodes::ConnectionStyle::setConnectionStyle(
|
||||
R"({"ConnectionStyle": {
|
||||
|
|
@ -192,10 +194,12 @@ GraphEditorWidget::GraphEditorWidget(warppipe::Client *client,
|
|||
"UseDataDefinedColors": false
|
||||
}})");
|
||||
|
||||
m_view = new QtNodes::GraphicsView(m_scene);
|
||||
m_view = new ZoomGraphicsView(m_scene);
|
||||
m_view->setFocusPolicy(Qt::StrongFocus);
|
||||
m_view->viewport()->setFocusPolicy(Qt::StrongFocus);
|
||||
m_view->viewport()->installEventFilter(this);
|
||||
connect(m_view, &ZoomGraphicsView::scaleChanged, m_view,
|
||||
[this]() { m_view->updateProxyCacheMode(); });
|
||||
|
||||
m_presetDir =
|
||||
QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation) +
|
||||
|
|
@ -233,6 +237,108 @@ GraphEditorWidget::GraphEditorWidget(warppipe::Client *client,
|
|||
|
||||
presetsLayout->addWidget(savePresetBtn);
|
||||
presetsLayout->addWidget(loadPresetBtn);
|
||||
|
||||
presetsLayout->addSpacing(16);
|
||||
|
||||
auto *zoomSensLabel = new QLabel(QStringLiteral("ZOOM SENSITIVITY"));
|
||||
zoomSensLabel->setStyleSheet(QStringLiteral(
|
||||
"QLabel { color: #a0a8b6; font-size: 11px; font-weight: bold;"
|
||||
" background: transparent; }"));
|
||||
presetsLayout->addWidget(zoomSensLabel);
|
||||
|
||||
m_zoomSensSlider = new QSlider(Qt::Horizontal);
|
||||
m_zoomSensSlider->setRange(5, 50);
|
||||
m_zoomSensSlider->setValue(20);
|
||||
m_zoomSensSlider->setStyleSheet(QStringLiteral(
|
||||
"QSlider::groove:horizontal {"
|
||||
" background: #1a1a1e; border-radius: 3px; height: 6px; }"
|
||||
"QSlider::handle:horizontal {"
|
||||
" background: #ecf0f6; border-radius: 5px;"
|
||||
" width: 10px; margin: -4px 0; }"
|
||||
"QSlider::sub-page:horizontal {"
|
||||
" background: #4caf50; border-radius: 3px; }"));
|
||||
|
||||
m_zoomSensValue = new QLabel(QStringLiteral("1.20x"));
|
||||
m_zoomSensValue->setStyleSheet(QStringLiteral(
|
||||
"QLabel { color: #ecf0f6; font-size: 11px; background: transparent; }"));
|
||||
m_zoomSensValue->setAlignment(Qt::AlignCenter);
|
||||
|
||||
connect(m_zoomSensSlider, &QSlider::valueChanged, this,
|
||||
[this](int value) {
|
||||
double sensitivity = 1.0 + value / 100.0;
|
||||
m_view->setZoomSensitivity(sensitivity);
|
||||
m_zoomSensValue->setText(
|
||||
QString::number(sensitivity, 'f', 2) + QStringLiteral("x"));
|
||||
scheduleSaveLayout();
|
||||
});
|
||||
|
||||
presetsLayout->addWidget(m_zoomSensSlider);
|
||||
presetsLayout->addWidget(m_zoomSensValue);
|
||||
|
||||
auto sliderStyle = m_zoomSensSlider->styleSheet();
|
||||
auto valueLabelStyle = m_zoomSensValue->styleSheet();
|
||||
|
||||
presetsLayout->addSpacing(12);
|
||||
|
||||
auto *zoomMinLabel = new QLabel(QStringLiteral("MIN ZOOM"));
|
||||
zoomMinLabel->setStyleSheet(zoomSensLabel->styleSheet());
|
||||
presetsLayout->addWidget(zoomMinLabel);
|
||||
|
||||
m_zoomMinSlider = new QSlider(Qt::Horizontal);
|
||||
m_zoomMinSlider->setRange(5, 90);
|
||||
m_zoomMinSlider->setValue(30);
|
||||
m_zoomMinSlider->setStyleSheet(sliderStyle);
|
||||
|
||||
m_zoomMinValue = new QLabel(QStringLiteral("0.30x"));
|
||||
m_zoomMinValue->setStyleSheet(valueLabelStyle);
|
||||
m_zoomMinValue->setAlignment(Qt::AlignCenter);
|
||||
|
||||
connect(m_zoomMinSlider, &QSlider::valueChanged, this,
|
||||
[this](int value) {
|
||||
double minZoom = value / 100.0;
|
||||
if (m_zoomMaxSlider->value() <= value) {
|
||||
m_zoomMaxSlider->setValue(value + 5);
|
||||
}
|
||||
m_view->setScaleRange(minZoom,
|
||||
m_zoomMaxSlider->value() / 100.0);
|
||||
m_zoomMinValue->setText(
|
||||
QString::number(minZoom, 'f', 2) + QStringLiteral("x"));
|
||||
scheduleSaveLayout();
|
||||
});
|
||||
|
||||
presetsLayout->addWidget(m_zoomMinSlider);
|
||||
presetsLayout->addWidget(m_zoomMinValue);
|
||||
|
||||
presetsLayout->addSpacing(12);
|
||||
|
||||
auto *zoomMaxLabel = new QLabel(QStringLiteral("MAX ZOOM"));
|
||||
zoomMaxLabel->setStyleSheet(zoomSensLabel->styleSheet());
|
||||
presetsLayout->addWidget(zoomMaxLabel);
|
||||
|
||||
m_zoomMaxSlider = new QSlider(Qt::Horizontal);
|
||||
m_zoomMaxSlider->setRange(10, 500);
|
||||
m_zoomMaxSlider->setValue(200);
|
||||
m_zoomMaxSlider->setStyleSheet(sliderStyle);
|
||||
|
||||
m_zoomMaxValue = new QLabel(QStringLiteral("2.00x"));
|
||||
m_zoomMaxValue->setStyleSheet(valueLabelStyle);
|
||||
m_zoomMaxValue->setAlignment(Qt::AlignCenter);
|
||||
|
||||
connect(m_zoomMaxSlider, &QSlider::valueChanged, this,
|
||||
[this](int value) {
|
||||
double maxZoom = value / 100.0;
|
||||
if (m_zoomMinSlider->value() >= value) {
|
||||
m_zoomMinSlider->setValue(value - 5);
|
||||
}
|
||||
m_view->setScaleRange(m_zoomMinSlider->value() / 100.0,
|
||||
maxZoom);
|
||||
m_zoomMaxValue->setText(
|
||||
QString::number(maxZoom, 'f', 2) + QStringLiteral("x"));
|
||||
scheduleSaveLayout();
|
||||
});
|
||||
|
||||
presetsLayout->addWidget(m_zoomMaxSlider);
|
||||
presetsLayout->addWidget(m_zoomMaxValue);
|
||||
presetsLayout->addStretch();
|
||||
|
||||
auto *metersTab = new QWidget();
|
||||
|
|
@ -504,9 +610,7 @@ void GraphEditorWidget::onRefreshTimer() {
|
|||
}
|
||||
|
||||
void GraphEditorWidget::scheduleSaveLayout() {
|
||||
if (!m_saveTimer->isActive()) {
|
||||
m_saveTimer->start();
|
||||
}
|
||||
}
|
||||
|
||||
GraphEditorWidget::~GraphEditorWidget() {
|
||||
|
|
@ -1130,6 +1234,9 @@ void GraphEditorWidget::saveLayoutWithViewState() {
|
|||
vs.splitterGraph = sizes.value(0, 1200);
|
||||
vs.splitterSidebar = sizes.value(1, 320);
|
||||
vs.connectionStyle = static_cast<int>(m_connectionStyle);
|
||||
vs.zoomSensitivity = m_view->zoomSensitivity();
|
||||
vs.zoomMin = m_zoomMinSlider->value() / 100.0;
|
||||
vs.zoomMax = m_zoomMaxSlider->value() / 100.0;
|
||||
vs.valid = true;
|
||||
m_model->saveLayout(m_layoutPath, vs);
|
||||
}
|
||||
|
|
@ -1145,6 +1252,17 @@ void GraphEditorWidget::restoreViewState() {
|
|||
if (vs.connectionStyle == static_cast<int>(ConnectionStyleType::kSquare)) {
|
||||
setConnectionStyle(ConnectionStyleType::kSquare);
|
||||
}
|
||||
if (vs.zoomSensitivity > 0.0) {
|
||||
m_view->setZoomSensitivity(vs.zoomSensitivity);
|
||||
int sliderVal = static_cast<int>((vs.zoomSensitivity - 1.0) * 100.0);
|
||||
m_zoomSensSlider->setValue(sliderVal);
|
||||
}
|
||||
if (vs.zoomMin > 0.0) {
|
||||
m_zoomMinSlider->setValue(static_cast<int>(vs.zoomMin * 100.0));
|
||||
}
|
||||
if (vs.zoomMax > 0.0) {
|
||||
m_zoomMaxSlider->setValue(static_cast<int>(vs.zoomMax * 100.0));
|
||||
}
|
||||
} else {
|
||||
m_view->zoomFitAll();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,9 +14,10 @@
|
|||
namespace QtNodes {
|
||||
using NodeId = unsigned int;
|
||||
class BasicGraphicsScene;
|
||||
class GraphicsView;
|
||||
} // namespace QtNodes
|
||||
|
||||
class ZoomGraphicsView;
|
||||
|
||||
class AudioLevelMeter;
|
||||
class WarpGraphModel;
|
||||
class NodeVolumeWidget;
|
||||
|
|
@ -24,6 +25,7 @@ class QLabel;
|
|||
class QScrollArea;
|
||||
class QSplitter;
|
||||
class QTabWidget;
|
||||
class QSlider;
|
||||
class QTimer;
|
||||
class DeleteVirtualNodeCommand;
|
||||
|
||||
|
|
@ -96,7 +98,7 @@ private:
|
|||
warppipe::Client *m_client = nullptr;
|
||||
WarpGraphModel *m_model = nullptr;
|
||||
QtNodes::BasicGraphicsScene *m_scene = nullptr;
|
||||
QtNodes::GraphicsView *m_view = nullptr;
|
||||
ZoomGraphicsView *m_view = nullptr;
|
||||
QSplitter *m_splitter = nullptr;
|
||||
QTabWidget *m_sidebar = nullptr;
|
||||
QTimer *m_refreshTimer = nullptr;
|
||||
|
|
@ -129,4 +131,11 @@ private:
|
|||
QScrollArea *m_rulesScroll = nullptr;
|
||||
|
||||
ConnectionStyleType m_connectionStyle = ConnectionStyleType::kBezier;
|
||||
|
||||
QSlider *m_zoomSensSlider = nullptr;
|
||||
QLabel *m_zoomSensValue = nullptr;
|
||||
QSlider *m_zoomMinSlider = nullptr;
|
||||
QLabel *m_zoomMinValue = nullptr;
|
||||
QSlider *m_zoomMaxSlider = nullptr;
|
||||
QLabel *m_zoomMaxValue = nullptr;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ WarpGraphModel::WarpGraphModel(warppipe::Client *client, QObject *parent)
|
|||
if (parent) {
|
||||
setParent(parent);
|
||||
}
|
||||
connect(this, &WarpGraphModel::nodeUpdated, this,
|
||||
[this](QtNodes::NodeId nodeId) { m_styleCache.erase(nodeId); });
|
||||
}
|
||||
|
||||
QtNodes::NodeId WarpGraphModel::newNodeId() { return m_nextNodeId++; }
|
||||
|
|
@ -195,9 +197,14 @@ QVariant WarpGraphModel::nodeData(QtNodes::NodeId nodeId,
|
|||
case QtNodes::NodeRole::Type:
|
||||
return QString("PipeWire");
|
||||
case QtNodes::NodeRole::Style: {
|
||||
auto cacheIt = m_styleCache.find(nodeId);
|
||||
if (cacheIt != m_styleCache.end())
|
||||
return cacheIt->second;
|
||||
bool ghost = m_ghostNodes.find(nodeId) != m_ghostNodes.end();
|
||||
WarpNodeType type = classifyNode(data.info);
|
||||
return styleForNode(type, ghost);
|
||||
QVariant result = styleForNode(type, ghost);
|
||||
m_styleCache[nodeId] = result;
|
||||
return result;
|
||||
}
|
||||
case QtNodes::NodeRole::Widget: {
|
||||
auto wIt = m_volumeWidgets.find(nodeId);
|
||||
|
|
@ -321,6 +328,7 @@ bool WarpGraphModel::deleteNode(QtNodes::NodeId const nodeId) {
|
|||
m_positions.erase(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;
|
||||
|
|
@ -593,7 +601,8 @@ void WarpGraphModel::refreshFromClient() {
|
|||
|
||||
if (outIsGhost || inIsGhost) {
|
||||
m_ghostConnections.insert(connId);
|
||||
} else {
|
||||
}
|
||||
{
|
||||
auto connIt = m_connections.find(connId);
|
||||
if (connIt != m_connections.end()) {
|
||||
m_connections.erase(connIt);
|
||||
|
|
@ -643,10 +652,8 @@ void WarpGraphModel::refreshFromClient() {
|
|||
}
|
||||
|
||||
QtNodes::ConnectionId connId{outQtId, outIdx, inQtId, inIdx};
|
||||
if (m_connections.find(connId) == m_connections.end()) {
|
||||
m_connections.insert(connId);
|
||||
if (m_ghostConnections.find(connId) == m_ghostConnections.end()) {
|
||||
m_ghostConnections.insert(connId);
|
||||
Q_EMIT connectionCreated(connId);
|
||||
}
|
||||
it = m_pendingGhostConnections.erase(it);
|
||||
}
|
||||
|
|
@ -969,6 +976,12 @@ void WarpGraphModel::saveLayout(const QString &path,
|
|||
viewObj["splitter_sidebar"] = viewState.splitterSidebar;
|
||||
}
|
||||
viewObj["connection_style"] = viewState.connectionStyle;
|
||||
if (viewState.zoomSensitivity > 0.0)
|
||||
viewObj["zoom_sensitivity"] = viewState.zoomSensitivity;
|
||||
if (viewState.zoomMin > 0.0)
|
||||
viewObj["zoom_min"] = viewState.zoomMin;
|
||||
if (viewState.zoomMax > 0.0)
|
||||
viewObj["zoom_max"] = viewState.zoomMax;
|
||||
root["view"] = viewObj;
|
||||
}
|
||||
|
||||
|
|
@ -1029,6 +1042,9 @@ bool WarpGraphModel::loadLayout(const QString &path) {
|
|||
m_savedViewState.splitterGraph = viewObj["splitter_graph"].toInt(0);
|
||||
m_savedViewState.splitterSidebar = viewObj["splitter_sidebar"].toInt(0);
|
||||
m_savedViewState.connectionStyle = viewObj["connection_style"].toInt(0);
|
||||
m_savedViewState.zoomSensitivity = viewObj["zoom_sensitivity"].toDouble(0.0);
|
||||
m_savedViewState.zoomMin = viewObj["zoom_min"].toDouble(0.0);
|
||||
m_savedViewState.zoomMax = viewObj["zoom_max"].toDouble(0.0);
|
||||
m_savedViewState.valid = true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -99,6 +99,9 @@ public:
|
|||
double scale;
|
||||
double centerX;
|
||||
double centerY;
|
||||
double zoomSensitivity;
|
||||
double zoomMin;
|
||||
double zoomMax;
|
||||
int splitterGraph;
|
||||
int splitterSidebar;
|
||||
int connectionStyle;
|
||||
|
|
@ -156,4 +159,5 @@ private:
|
|||
|
||||
std::unordered_map<QtNodes::NodeId, NodeVolumeState> m_volumeStates;
|
||||
std::unordered_map<QtNodes::NodeId, QWidget *> m_volumeWidgets;
|
||||
mutable std::unordered_map<QtNodes::NodeId, QVariant> m_styleCache;
|
||||
};
|
||||
|
|
|
|||
142
gui/ZoomGraphicsView.h
Normal file
142
gui/ZoomGraphicsView.h
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
#pragma once
|
||||
|
||||
#include <QtNodes/GraphicsView>
|
||||
#include <QtNodes/StyleCollection>
|
||||
|
||||
#include <QGraphicsProxyWidget>
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QScrollBar>
|
||||
#include <QWheelEvent>
|
||||
|
||||
#include <QtNodes/internal/ConnectionGraphicsObject.hpp>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
class ZoomGraphicsView : public QtNodes::GraphicsView {
|
||||
public:
|
||||
explicit ZoomGraphicsView(QtNodes::BasicGraphicsScene *scene,
|
||||
QWidget *parent = nullptr)
|
||||
: QtNodes::GraphicsView(scene, parent) {
|
||||
setViewportUpdateMode(QGraphicsView::SmartViewportUpdate);
|
||||
setCacheMode(QGraphicsView::CacheNone);
|
||||
}
|
||||
|
||||
void setZoomSensitivity(double sensitivity) {
|
||||
m_sensitivity = sensitivity;
|
||||
}
|
||||
double zoomSensitivity() const { return m_sensitivity; }
|
||||
|
||||
void updateProxyCacheMode() {
|
||||
if (!scene())
|
||||
return;
|
||||
|
||||
const double zoom = transform().m11();
|
||||
const bool highZoom = zoom > 1.4;
|
||||
|
||||
if (highZoom == m_proxiesCached)
|
||||
return;
|
||||
m_proxiesCached = highZoom;
|
||||
|
||||
auto cacheMode = highZoom ? QGraphicsItem::DeviceCoordinateCache
|
||||
: QGraphicsItem::NoCache;
|
||||
for (QGraphicsItem *item : scene()->items()) {
|
||||
if (item->type() == QGraphicsProxyWidget::Type ||
|
||||
item->type() == QtNodes::ConnectionGraphicsObject::Type)
|
||||
item->setCacheMode(cacheMode);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void wheelEvent(QWheelEvent *event) override {
|
||||
const double dy = event->angleDelta().y();
|
||||
if (dy == 0.0) {
|
||||
event->ignore();
|
||||
return;
|
||||
}
|
||||
|
||||
static constexpr double kTickUnits = 120.0;
|
||||
const double exponent = dy / kTickUnits;
|
||||
const double factor = std::pow(m_sensitivity, exponent);
|
||||
const double proposed = transform().m11() * factor;
|
||||
setupScale(proposed);
|
||||
}
|
||||
|
||||
void mousePressEvent(QMouseEvent *event) override {
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
m_panActive = true;
|
||||
m_panStart = event->pos();
|
||||
}
|
||||
QGraphicsView::mousePressEvent(event);
|
||||
}
|
||||
|
||||
void mouseMoveEvent(QMouseEvent *event) override {
|
||||
if (m_panActive && (event->buttons() & Qt::LeftButton) &&
|
||||
!(event->modifiers() & Qt::ShiftModifier) &&
|
||||
!scene()->mouseGrabberItem()) {
|
||||
QPoint delta = event->pos() - m_panStart;
|
||||
m_panStart = event->pos();
|
||||
horizontalScrollBar()->setValue(horizontalScrollBar()->value() - delta.x());
|
||||
verticalScrollBar()->setValue(verticalScrollBar()->value() - delta.y());
|
||||
return;
|
||||
}
|
||||
QGraphicsView::mouseMoveEvent(event);
|
||||
}
|
||||
|
||||
void mouseReleaseEvent(QMouseEvent *event) override {
|
||||
if (event->button() == Qt::LeftButton)
|
||||
m_panActive = false;
|
||||
QGraphicsView::mouseReleaseEvent(event);
|
||||
}
|
||||
|
||||
void drawBackground(QPainter *painter, const QRectF &r) override {
|
||||
QGraphicsView::drawBackground(painter, r);
|
||||
|
||||
const double zoom = transform().m11();
|
||||
auto const &style =
|
||||
QtNodes::StyleCollection::flowViewStyle();
|
||||
|
||||
static constexpr double kBaseFine = 15.0;
|
||||
static constexpr double kBaseCoarse = 150.0;
|
||||
static constexpr double kMinPixelSpacing = 10.0;
|
||||
|
||||
double fineStep = kBaseFine;
|
||||
while (fineStep * zoom < kMinPixelSpacing)
|
||||
fineStep *= 2.0;
|
||||
|
||||
double coarseStep = kBaseCoarse;
|
||||
while (coarseStep * zoom < kMinPixelSpacing * 5.0)
|
||||
coarseStep *= 2.0;
|
||||
|
||||
auto drawGrid = [&](double gridStep) {
|
||||
QRect windowRect = rect();
|
||||
QPointF tl = mapToScene(windowRect.topLeft());
|
||||
QPointF br = mapToScene(windowRect.bottomRight());
|
||||
|
||||
double left = std::floor(tl.x() / gridStep - 0.5);
|
||||
double right = std::floor(br.x() / gridStep + 1.0);
|
||||
double bottom = std::floor(tl.y() / gridStep - 0.5);
|
||||
double top = std::floor(br.y() / gridStep + 1.0);
|
||||
|
||||
for (int xi = int(left); xi <= int(right); ++xi)
|
||||
painter->drawLine(QLineF(xi * gridStep, bottom * gridStep,
|
||||
xi * gridStep, top * gridStep));
|
||||
|
||||
for (int yi = int(bottom); yi <= int(top); ++yi)
|
||||
painter->drawLine(QLineF(left * gridStep, yi * gridStep,
|
||||
right * gridStep, yi * gridStep));
|
||||
};
|
||||
|
||||
painter->setPen(QPen(style.FineGridColor, 1.0));
|
||||
drawGrid(fineStep);
|
||||
|
||||
painter->setPen(QPen(style.CoarseGridColor, 1.0));
|
||||
drawGrid(coarseStep);
|
||||
}
|
||||
|
||||
private:
|
||||
double m_sensitivity = 1.2;
|
||||
bool m_proxiesCached = false;
|
||||
bool m_panActive = false;
|
||||
QPoint m_panStart;
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue