nodeview: update positions correctly

This commit is contained in:
itsmattkc
2021-05-24 10:12:53 +10:00
parent 74a1507c6f
commit f9309da4b3
6 changed files with 149 additions and 83 deletions
+111 -81
View File
@@ -20,6 +20,7 @@
#include "nodeview.h" #include "nodeview.h"
#include <cfloat>
#include <QInputDialog> #include <QInputDialog>
#include <QMouseEvent> #include <QMouseEvent>
#include <QScrollBar> #include <QScrollBar>
@@ -56,6 +57,7 @@ NodeView::NodeView(QWidget *parent) :
setViewportUpdateMode(FullViewportUpdate); setViewportUpdateMode(FullViewportUpdate);
connect(this, &NodeView::customContextMenuRequested, this, &NodeView::ShowContextMenu); connect(this, &NodeView::customContextMenuRequested, this, &NodeView::ShowContextMenu);
connect(&scene_, &NodeViewScene::NodePositionChanged, this, &NodeView::NodePositionChanged);
ConnectSelectionChangedSignal(); ConnectSelectionChangedSignal();
@@ -70,106 +72,120 @@ NodeView::~NodeView()
void NodeView::SetGraph(NodeGraph *graph, const QVector<void*> &nodes) void NodeView::SetGraph(NodeGraph *graph, const QVector<void*> &nodes)
{ {
// Handle potentially changing graph bool graph_changed = graph_ != graph;
if (graph_ != graph) { bool context_changed = filter_nodes_ != nodes;
if (graph_) {
disconnect(graph_, &NodeGraph::NodeAdded, this, &NodeView::AddNode);
disconnect(graph_, &NodeGraph::NodeRemoved, this, &NodeView::RemoveNode);
disconnect(graph_, &NodeGraph::InputConnected, this, &NodeView::AddEdge);
disconnect(graph_, &NodeGraph::InputDisconnected, this, &NodeView::RemoveEdge);
disconnect(graph_, &NodeGraph::NodePositionAdded, this, &NodeView::AddNodePosition);
disconnect(graph_, &NodeGraph::NodePositionRemoved, this, &NodeView::RemoveNodePosition);
if (filter_mode_ == kFilterShowAll) { if (graph_changed || context_changed) {
// Switching graphs, close all nodes // Clear nodes if necessary
DeselectAll(); bool refresh_required = (graph_changed && filter_mode_ == kFilterShowAll)
scene_.clear(); || (context_changed && filter_mode_ == kFilterShowSelective);
} bool nodes_visible = (graph && filter_mode_ == kFilterShowAll)
} || (!nodes.isEmpty() && filter_mode_ == kFilterShowSelective);
graph_ = graph; if (refresh_required) {
if (graph_) {
connect(graph_, &NodeGraph::NodeAdded, this, &NodeView::AddNode);
connect(graph_, &NodeGraph::NodeRemoved, this, &NodeView::RemoveNode);
connect(graph_, &NodeGraph::InputConnected, this, &NodeView::AddEdge);
connect(graph_, &NodeGraph::InputDisconnected, this, &NodeView::RemoveEdge);
connect(graph_, &NodeGraph::NodePositionAdded, this, &NodeView::AddNodePosition);
connect(graph_, &NodeGraph::NodePositionRemoved, this, &NodeView::RemoveNodePosition);
if (filter_mode_ == kFilterShowAll) {
foreach (Node* n, graph_->nodes()) {
scene_.AddNode(n);
}
foreach (Node* n, graph_->nodes()) {
for (auto it=n->input_connections().cbegin(); it!=n->input_connections().cend(); it++) {
scene_.AddEdge(it->second, it->first);
}
}
}
}
}
// Handle changing nodes
if (filter_nodes_ != nodes) {
filter_nodes_ = nodes;
if (filter_mode_ == kFilterShowSelective) {
DeselectAll(); DeselectAll();
positions_.clear();
scene_.clear(); scene_.clear();
}
// Handle graph change
if (graph_changed) {
if (graph_) {
// Disconnect from current graph
disconnect(graph_, &NodeGraph::NodeAdded, this, &NodeView::AddNode);
disconnect(graph_, &NodeGraph::NodeRemoved, this, &NodeView::RemoveNode);
disconnect(graph_, &NodeGraph::InputConnected, this, &NodeView::AddEdge);
disconnect(graph_, &NodeGraph::InputDisconnected, this, &NodeView::RemoveEdge);
disconnect(graph_, &NodeGraph::NodePositionAdded, this, &NodeView::AddNodePosition);
disconnect(graph_, &NodeGraph::NodePositionRemoved, this, &NodeView::RemoveNodePosition);
}
graph_ = graph;
if (graph_) {
// Connect to new graph
connect(graph_, &NodeGraph::NodeAdded, this, &NodeView::AddNode);
connect(graph_, &NodeGraph::NodeRemoved, this, &NodeView::RemoveNode);
connect(graph_, &NodeGraph::InputConnected, this, &NodeView::AddEdge);
connect(graph_, &NodeGraph::InputDisconnected, this, &NodeView::RemoveEdge);
connect(graph_, &NodeGraph::NodePositionAdded, this, &NodeView::AddNodePosition);
connect(graph_, &NodeGraph::NodePositionRemoved, this, &NodeView::RemoveNodePosition);
}
}
if (context_changed) {
filter_nodes_ = nodes;
}
if (refresh_required && nodes_visible) {
QMap<NodeViewItem*, QVector<QPointF> > averaged_positions; QMap<NodeViewItem*, QVector<QPointF> > averaged_positions;
QPointF origin(0, 0); QPointF origin(0, 0);
foreach (void *n, filter_nodes_) { if (filter_mode_ == kFilterShowAll) {
const NodeGraph::PositionMap &map = graph_->GetNodesForRelative(n); // FIXME: Implement
} else {
// Reserve an arbitrary number to reduce the amount of reallocations
foreach (void *n, filter_nodes_) {
const NodeGraph::PositionMap &map = graph_->GetNodesForRelative(n);
qreal top = 0, bottom = 0; qreal top = 0, bottom = 0;
for (auto it=map.cbegin(); it!=map.cend(); it++) { for (auto it=map.cbegin(); it!=map.cend(); it++) {
NodeViewItem *item = scene_.item_map().value(it.key()); // Determine position
NodeViewItem *item = scene_.item_map().value(it.key());
if (item) { if (item) {
QVector<QPointF> &averages = averaged_positions[item]; QVector<QPointF> &averages = averaged_positions[item];
if (averages.isEmpty()) { if (averages.isEmpty()) {
averages.append(item->GetNodePosition()); averages.append(item->GetNodePosition());
}
averages.append(origin + it.value());
} else {
item = scene_.AddNode(it.key());
} }
averages.append(origin + it.value());
} else { const QPointF &pos = it.value();
item = scene_.AddNode(it.key()); top = qMin(top, pos.y());
bottom = qMax(bottom, pos.y());
item->SetNodePosition(origin + pos);
} }
const QPointF &pos = it.value(); origin.setY(origin.y() + 1 + (bottom - top));
top = qMin(top, pos.y());
bottom = qMax(bottom, pos.y());
item->SetNodePosition(origin + pos);
} }
origin.setY(origin.y() + 1 + (bottom - top)); for (auto it=averaged_positions.cbegin(); it!=averaged_positions.cend(); it++) {
} const QVector<QPointF> &positions = it.value();
double x = DBL_MAX;
double y = 0.0;
for (auto it=scene_.item_map().cbegin(); it!=scene_.item_map().cend(); it++) { // Min the X value and average the Y values
Node *node = it.key(); foreach (const QPointF &pos, positions) {
for (auto jt=node->input_connections().cbegin(); jt!=node->input_connections().cend(); jt++) { x = qMin(x, pos.x());
const NodeOutput &output = jt->second; y += pos.y();
if (scene_.item_map().contains(output.node())) {
// Create edge since both input and output exist
scene_.AddEdge(output, jt->first);
} }
}
}
for (auto it=averaged_positions.cbegin(); it!=averaged_positions.cend(); it++) { y /= positions.size();
const QVector<QPointF> &positions = it.value();
QPointF p; it.key()->SetNodePosition(QPointF(x, y));
foreach (const QPointF &pos, positions) { }
p += pos;
for (auto it=scene_.item_map().cbegin(); it!=scene_.item_map().cend(); it++) {
// Add edge objects
Node *node = it.key();
for (auto jt=node->input_connections().cbegin(); jt!=node->input_connections().cend(); jt++) {
const NodeOutput &output = jt->second;
if (scene_.item_map().contains(output.node())) {
// Create edge since both input and output exist
scene_.AddEdge(output, jt->first);
}
}
// Store view position
positions_.insert(it.value(), {it.key(), it.value()->GetNodePosition()});
} }
p /= positions.size();
it.key()->SetNodePosition(p);
} }
} }
} }
@@ -983,7 +999,7 @@ void NodeView::RemoveEdge(const NodeOutput &output, const NodeInput &input)
void NodeView::AddNodePosition(Node *node, void *relative, const QPointF &pos) void NodeView::AddNodePosition(Node *node, void *relative, const QPointF &pos)
{ {
if (filter_mode_ == kFilterShowSelective) { /*if (filter_mode_ == kFilterShowSelective) {
if (filter_nodes_.contains(relative)) { if (filter_nodes_.contains(relative)) {
NodeViewItem *item = scene_.item_map().value(node); NodeViewItem *item = scene_.item_map().value(node);
@@ -1003,7 +1019,7 @@ void NodeView::AddNodePosition(Node *node, void *relative, const QPointF &pos)
item->SetNodePosition(pos); item->SetNodePosition(pos);
} }
} }*/
} }
void NodeView::RemoveNodePosition(Node *node, void *relative) void NodeView::RemoveNodePosition(Node *node, void *relative)
@@ -1016,6 +1032,20 @@ void NodeView::RemoveNodePosition(Node *node, void *relative)
} }
} }
void NodeView::NodePositionChanged(NodeViewItem *item, const QPointF &pos)
{
Position &original_pos = positions_[item];
Node *node = original_pos.node;
QPointF diff = pos - original_pos.original_item_pos;
original_pos.original_item_pos = pos;
foreach (void *context, filter_nodes_) {
QPointF p = graph_->GetNodePosition(node, context);
p += diff;
graph_->SetNodePosition(node, context, p);
}
}
void NodeView::AttachNodesToCursor(const QVector<Node *> &nodes) void NodeView::AttachNodesToCursor(const QVector<Node *> &nodes)
{ {
QVector<NodeViewItem*> items(nodes.size()); QVector<NodeViewItem*> items(nodes.size());
+9
View File
@@ -155,6 +155,13 @@ private:
kFilterShowSelective kFilterShowSelective
}; };
struct Position {
Node *node;
QPointF original_item_pos;
};
QMap<NodeViewItem *, Position> positions_;
FilterMode filter_mode_; FilterMode filter_mode_;
QVector<void*> filter_nodes_; QVector<void*> filter_nodes_;
@@ -209,6 +216,8 @@ private slots:
void AddNodePosition(Node *node, void *relative, const QPointF &pos); void AddNodePosition(Node *node, void *relative, const QPointF &pos);
void RemoveNodePosition(Node *node, void *relative); void RemoveNodePosition(Node *node, void *relative);
void NodePositionChanged(NodeViewItem *item, const QPointF &pos);
}; };
} }
+10 -1
View File
@@ -45,7 +45,8 @@ NodeViewItem::NodeViewItem(QGraphicsItem *parent) :
expanded_(false), expanded_(false),
hide_titlebar_(false), hide_titlebar_(false),
highlighted_index_(-1), highlighted_index_(-1),
flow_dir_(NodeViewCommon::kLeftToRight) flow_dir_(NodeViewCommon::kLeftToRight),
dont_signal_(false)
{ {
// Set flags for this widget // Set flags for this widget
setFlag(QGraphicsItem::ItemIsMovable); setFlag(QGraphicsItem::ItemIsMovable);
@@ -336,6 +337,10 @@ QVariant NodeViewItem::itemChange(QGraphicsItem::GraphicsItemChange change, cons
{ {
if (change == ItemPositionHasChanged && node_) { if (change == ItemPositionHasChanged && node_) {
ReadjustAllEdges(); ReadjustAllEdges();
if (!dont_signal_) {
emit NodePositionChanged(GetNodePosition());
}
} }
return QGraphicsItem::itemChange(change, value); return QGraphicsItem::itemChange(change, value);
@@ -479,6 +484,8 @@ QPointF NodeViewItem::GetInputPointInternal(int index, const QPointF& source_pos
void NodeViewItem::UpdateNodePosition() void NodeViewItem::UpdateNodePosition()
{ {
dont_signal_ = true;
const QPointF &pos = cached_node_pos_; const QPointF &pos = cached_node_pos_;
switch (flow_dir_) { switch (flow_dir_) {
@@ -499,6 +506,8 @@ void NodeViewItem::UpdateNodePosition()
-pos.x() * DefaultItemVerticalPadding()); -pos.x() * DefaultItemVerticalPadding());
break; break;
} }
dont_signal_ = false;
} }
} }
+7 -1
View File
@@ -40,8 +40,9 @@ class NodeViewEdge;
* *
* To retrieve the NodeViewItem for a certain Node, use NodeView::NodeToUIObject(). * To retrieve the NodeViewItem for a certain Node, use NodeView::NodeToUIObject().
*/ */
class NodeViewItem : public QGraphicsRectItem class NodeViewItem : public QObject, public QGraphicsRectItem
{ {
Q_OBJECT
public: public:
NodeViewItem(QGraphicsItem* parent = nullptr); NodeViewItem(QGraphicsItem* parent = nullptr);
@@ -111,6 +112,9 @@ public:
void SetHighlightedIndex(int index); void SetHighlightedIndex(int index);
signals:
void NodePositionChanged(const QPointF &pos);
protected: protected:
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
@@ -174,6 +178,8 @@ private:
QPointF cached_node_pos_; QPointF cached_node_pos_;
bool dont_signal_;
}; };
} }
+7
View File
@@ -159,6 +159,7 @@ NodeViewItem* NodeViewScene::AddNode(Node* node)
connect(node, &Node::LabelChanged, this, &NodeViewScene::NodeAppearanceChanged); connect(node, &Node::LabelChanged, this, &NodeViewScene::NodeAppearanceChanged);
connect(node, &Node::ColorChanged, this, &NodeViewScene::NodeAppearanceChanged); connect(node, &Node::ColorChanged, this, &NodeViewScene::NodeAppearanceChanged);
connect(item, &NodeViewItem::NodePositionChanged, this, &NodeViewScene::NodeItemPositionChanged);
return item; return item;
} }
@@ -278,4 +279,10 @@ void NodeViewScene::NodeAppearanceChanged()
item_map_.value(static_cast<Node*>(sender()))->update(); item_map_.value(static_cast<Node*>(sender()))->update();
} }
void NodeViewScene::NodeItemPositionChanged(const QPointF &pos)
{
NodeViewItem *item = static_cast<NodeViewItem *>(sender());
emit NodePositionChanged(item, pos);
}
} }
+5
View File
@@ -106,6 +106,9 @@ public slots:
*/ */
void SetEdgesAreCurved(bool curved); void SetEdgesAreCurved(bool curved);
signals:
void NodePositionChanged(NodeViewItem *node, const QPointF &pos);
private: private:
static int DetermineWeight(Node* n); static int DetermineWeight(Node* n);
@@ -127,6 +130,8 @@ private slots:
*/ */
void NodeAppearanceChanged(); void NodeAppearanceChanged();
void NodeItemPositionChanged(const QPointF &pos);
}; };
} }