diff --git a/app/widget/nodeview/CMakeLists.txt b/app/widget/nodeview/CMakeLists.txt index 9f28bd1b1..9682e3972 100644 --- a/app/widget/nodeview/CMakeLists.txt +++ b/app/widget/nodeview/CMakeLists.txt @@ -24,6 +24,8 @@ set(OLIVE_SOURCES widget/nodeview/nodeviewitem.cpp widget/nodeview/nodeviewitemwidgetproxy.h widget/nodeview/nodeviewitemwidgetproxy.cpp + widget/nodeview/nodeviewscene.h + widget/nodeview/nodeviewscene.cpp widget/nodeview/nodeviewundo.h widget/nodeview/nodeviewundo.cpp PARENT_SCOPE diff --git a/app/widget/nodeview/nodeview.cpp b/app/widget/nodeview/nodeview.cpp index ff8d121ac..f39b6a66d 100644 --- a/app/widget/nodeview/nodeview.cpp +++ b/app/widget/nodeview/nodeview.cpp @@ -39,8 +39,6 @@ NodeView::NodeView(QWidget *parent) : connect(&scene_, &QGraphicsScene::changed, this, &NodeView::ItemsChanged); connect(&scene_, &QGraphicsScene::selectionChanged, this, &NodeView::SceneSelectionChangedSlot); connect(this, &NodeView::customContextMenuRequested, this, &NodeView::ShowContextMenu); - connect(&reorganize_timer_, &QTimer::timeout, &reorganize_timer_, &QTimer::stop); - connect(&reorganize_timer_, &QTimer::timeout, this, &NodeView::Reorganize); setMouseTracking(true); } @@ -58,10 +56,10 @@ void NodeView::SetGraph(NodeGraph *graph) } if (graph_ != nullptr) { - disconnect(graph_, &NodeGraph::NodeAdded, this, &NodeView::AddNode); - disconnect(graph_, &NodeGraph::NodeRemoved, this, &NodeView::RemoveNode); - disconnect(graph_, &NodeGraph::EdgeAdded, this, &NodeView::AddEdge); - disconnect(graph_, &NodeGraph::EdgeRemoved, this, &NodeView::RemoveEdge); + disconnect(graph_, &NodeGraph::NodeAdded, &scene_, &NodeViewScene::AddNode); + disconnect(graph_, &NodeGraph::NodeRemoved, &scene_, &NodeViewScene::RemoveNode); + disconnect(graph_, &NodeGraph::EdgeAdded, &scene_, &NodeViewScene::AddEdge); + disconnect(graph_, &NodeGraph::EdgeRemoved, &scene_, &NodeViewScene::RemoveEdge); } // Clear the scene of all UI objects @@ -69,66 +67,23 @@ void NodeView::SetGraph(NodeGraph *graph) // Set reference to the graph graph_ = graph; + scene_.SetGraph(graph_); // If the graph is valid, add UI objects for each of its Nodes if (graph_ != nullptr) { - connect(graph_, &NodeGraph::NodeAdded, this, &NodeView::AddNode); - connect(graph_, &NodeGraph::NodeRemoved, this, &NodeView::RemoveNode); - connect(graph_, &NodeGraph::EdgeAdded, this, &NodeView::AddEdge); - connect(graph_, &NodeGraph::EdgeRemoved, this, &NodeView::RemoveEdge); + connect(graph_, &NodeGraph::NodeAdded, &scene_, &NodeViewScene::AddNode); + connect(graph_, &NodeGraph::NodeRemoved, &scene_, &NodeViewScene::RemoveNode); + connect(graph_, &NodeGraph::EdgeAdded, &scene_, &NodeViewScene::AddEdge); + connect(graph_, &NodeGraph::EdgeRemoved, &scene_, &NodeViewScene::RemoveEdge); QList graph_nodes = graph_->nodes(); foreach (Node* node, graph_nodes) { - AddNode(node); + scene_.AddNode(node); } } } -NodeViewItem *NodeView::NodeToUIObject(QGraphicsScene *scene, Node *n) -{ - QList graphics_items = scene->items(); - - for (int i=0;i(graphics_items.at(i)); - - if (item != nullptr) { - if (item->node() == n) { - return item; - } - } - } - - return nullptr; -} - -NodeViewEdge *NodeView::EdgeToUIObject(QGraphicsScene *scene, NodeEdgePtr n) -{ - QList graphics_items = scene->items(); - - for (int i=0;i(graphics_items.at(i)); - - if (edge != nullptr) { - if (edge->edge() == n) { - return edge; - } - } - } - - return nullptr; -} - -NodeViewItem *NodeView::NodeToUIObject(Node *n) -{ - return NodeToUIObject(&scene_, n); -} - -NodeViewEdge *NodeView::EdgeToUIObject(NodeEdgePtr n) -{ - return EdgeToUIObject(&scene_, n); -} - void NodeView::DeleteSelected() { if (!graph_) { @@ -176,7 +131,7 @@ void NodeView::Select(const QList &nodes) DeselectAll(); foreach (Node* n, nodes) { - NodeViewItem* item = NodeToUIObject(n); + NodeViewItem* item = scene_.NodeToUIObject(n); if (item) { item->setSelected(true); @@ -191,7 +146,7 @@ void NodeView::SelectWithDependencies(const QList &nodes) QList nodes_with_deps; foreach (Node* n, nodes) { - NodeViewItem* item = NodeToUIObject(n); + NodeViewItem* item = scene_.NodeToUIObject(n); if (item) { item->setSelected(true); @@ -200,7 +155,7 @@ void NodeView::SelectWithDependencies(const QList &nodes) QList deps = n->GetDependencies(); foreach (Node* d, deps) { - item = NodeToUIObject(d); + item = scene_.NodeToUIObject(d); if (item) { item->setSelected(true); @@ -209,56 +164,6 @@ void NodeView::SelectWithDependencies(const QList &nodes) } } -void NodeView::AddNode(Node* node) -{ - NodeViewItem* item = new NodeViewItem(); - - item->SetNode(node); - - scene_.addItem(item); - - // Add a NodeViewEdge for each connection - foreach (NodeParam* param, node->parameters()) { - - // We only bother working with outputs since eventually this will cover all inputs too - // (covering both would lead to duplicates since every edge connects to one input and one output) - if (param->type() == NodeParam::kOutput) { - const QVector& edges = param->edges(); - - foreach(NodeEdgePtr edge, edges) { - AddEdge(edge); - } - } - } - - QueueReorganize(); -} - -void NodeView::RemoveNode(Node *node) -{ - NodeViewItem* item = NodeToUIObject(scene(), node); - - delete item; -} - -void NodeView::AddEdge(NodeEdgePtr edge) -{ - NodeViewEdge* edge_ui = new NodeViewEdge(); - - edge_ui->SetEdge(edge); - - scene_.addItem(edge_ui); - - QueueReorganize(); -} - -void NodeView::RemoveEdge(NodeEdgePtr edge) -{ - NodeViewEdge* edge_ui = EdgeToUIObject(scene(), edge); - - delete edge_ui; -} - void NodeView::ItemsChanged() { QList items = scene_.items(); @@ -416,7 +321,7 @@ void NodeView::CreateNodeSlot(QAction *action) if (new_node) { Core::instance()->undo_stack()->push(new NodeAddCommand(graph_, new_node)); - NodeViewItem* item = NodeToUIObject(new_node); + NodeViewItem* item = scene_.NodeToUIObject(new_node); AttachItemToCursor(item); } } @@ -555,115 +460,6 @@ void NodeView::PlaceNode(NodeViewItem *n, const QPointF &pos) } } -QList NodeView::GetNodeDirectDescendants(Node* n, const QList connected_nodes, QList& processed_nodes) -{ - QList direct_descendants = connected_nodes; - - processed_nodes.append(n); - - // Remove any nodes that aren't necessarily attached directly - for (int i=0;ioutput()->edges().size();j++) { - Node* this_output_connection = connected->output()->edges().at(j)->input()->parentNode(); - if (!processed_nodes.contains(this_output_connection)) { - direct_descendants.removeAt(i); - i--; - break; - } - } - } - - return direct_descendants; -} - -int NodeView::FindWeightsInternal(Node *node, QHash &weights, QList& weighted_nodes) -{ - QList connected_nodes = node->GetImmediateDependencies(); - - int weight = 0; - - if (!connected_nodes.isEmpty()) { - QList direct_descendants = GetNodeDirectDescendants(node, connected_nodes, weighted_nodes); - - foreach (Node* dep, direct_descendants) { - weight += FindWeightsInternal(dep, weights, weighted_nodes); - } - } - - weight = qMax(weight, 1); - - weights.insert(node, weight); - - return weight; -} - -void NodeView::ReorganizeInternal(NodeViewItem* src_item, QHash& weights, QList& positioned_nodes) -{ - Node* n = src_item->node(); - - QList connected_nodes = n->GetImmediateDependencies(); - - if (connected_nodes.isEmpty()) { - return; - } - - QList direct_descendants = GetNodeDirectDescendants(n, connected_nodes, positioned_nodes); - - int descendant_weight = 0; - foreach (Node* dep, direct_descendants) { - descendant_weight += weights.value(dep); - } - - qreal center_y = src_item->y(); - qreal total_height = descendant_weight * src_item->rect().height() + (direct_descendants.size()-1) * src_item->rect().height()/2; - double item_top = center_y - (total_height/2) + src_item->rect().height()/2; - - // Set each node's position - int weight_index = 0; - for (int i=0;irect().height() * 1.5; - - QPointF item_pos(src_item->pos().x() - item->rect().width() * 3 / 2, - item_y); - - item->setPos(item_pos); - - weight_index += weights.value(connected); - } - - // Recursively work on each node - foreach (Node* connected, connected_nodes) { - NodeViewItem* item = NodeToUIObject(connected); - - if (!item) { - continue; - } - - ReorganizeInternal(item, weights, positioned_nodes); - } -} - -void NodeView::QueueReorganize() -{ - // Avoids the fairly complex Reorganize() function every single time a connection or node is added - - reorganize_timer_.stop(); - reorganize_timer_.start(20); -} - void NodeView::AttachItemToCursor(NodeViewItem *item) { attached_item_ = item; @@ -675,32 +471,3 @@ void NodeView::DetachItemFromCursor() { AttachItemToCursor(nullptr); } - -void NodeView::Reorganize() -{ - if (!graph_) { - return; - } - - QList end_nodes; - - // Calculate the nodes that don't output to anything, they'll be our anchors - foreach (Node* node, graph_->nodes()) { - if (!node->HasConnectedOutputs()) { - end_nodes.append(node); - } - } - - QList processed_nodes; - - QHash node_weights; - foreach (Node* end_node, end_nodes) { - FindWeightsInternal(end_node, node_weights, processed_nodes); - } - - processed_nodes.clear(); - - foreach (Node* end_node, end_nodes) { - ReorganizeInternal(NodeToUIObject(end_node), node_weights, processed_nodes); - } -} diff --git a/app/widget/nodeview/nodeview.h b/app/widget/nodeview/nodeview.h index 6f4fc3ab2..a79e1fc73 100644 --- a/app/widget/nodeview/nodeview.h +++ b/app/widget/nodeview/nodeview.h @@ -25,8 +25,7 @@ #include #include "node/graph.h" -#include "widget/nodeview/nodeviewedge.h" -#include "widget/nodeview/nodeviewitem.h" +#include "nodeviewscene.h" /** * @brief A widget for viewing and editing node graphs @@ -47,36 +46,6 @@ public: */ void SetGraph(NodeGraph* graph); - /** - * @brief Retrieve the graphical widget corresponding to a specific Node - * - * In situations where you know what Node you're working with but need the UI object (e.g. for positioning), this - * static function will retrieve the NodeViewItem (Node UI representation) connected to this Node in a certain - * QGraphicsScene. This can be called from any other UI object, since it'll have a reference to the QGraphicsScene - * through QGraphicsItem::scene(). - * - * If the scene does not contain a widget for this node (usually meaning the node's graph is not the active graph - * in this view/scene), this function returns nullptr. - */ - static NodeViewItem* NodeToUIObject(QGraphicsScene* scene, Node* n); - - /** - * @brief Retrieve the graphical widget corresponding to a specific NodeEdge - * - * Same as NodeToUIObject() but returns a NodeViewEdge corresponding to a NodeEdgePtr instead. - */ - static NodeViewEdge* EdgeToUIObject(QGraphicsScene* scene, NodeEdgePtr n); - - /** - * @brief Overloaded NodeToUIObject(QGraphicsScene* scene, Node* n) if you have direct access to a NodeView instance - */ - NodeViewItem* NodeToUIObject(Node* n); - - /** - * @brief Overloaded EdgeToUIObject(QGraphicsScene* scene, NodeEdgePtr n) if you have direct access to a NodeView instance - */ - NodeViewEdge* EdgeToUIObject(NodeEdgePtr n); - /** * @brief Delete selected nodes from graph (user-friendly/undoable) */ @@ -102,16 +71,8 @@ protected: virtual void mouseMoveEvent(QMouseEvent *event) override; private: - QList GetNodeDirectDescendants(Node* n, const QList connected_nodes, QList& processed_nodes); - void PlaceNode(NodeViewItem* n, const QPointF& pos); - int FindWeightsInternal(Node* node, QHash& weights, QList& weighted_nodes); - - void ReorganizeInternal(NodeViewItem *src_item, QHash& weights, QList &positioned_nodes); - - void QueueReorganize(); - void AttachItemToCursor(NodeViewItem* item); void DetachItemFromCursor(); @@ -123,43 +84,9 @@ private: NodeViewEdge* drop_edge_; NodeInput* drop_compatible_input_; - QGraphicsScene scene_; - - QTimer reorganize_timer_; + NodeViewScene scene_; private slots: - /** - * @brief Slot when a Node is added to a graph (SetGraph() connects this) - * - * This should NEVER be called directly, only connected to a NodeGraph. To add a Node to the NodeGraph - * use NodeGraph::AddNode(). - */ - void AddNode(Node* node); - - /** - * @brief Slot when a Node is removed from a graph (SetGraph() connects this) - * - * This should NEVER be called directly, only connected to a NodeGraph. To remove a Node from the NodeGraph - * use NodeGraph::RemoveNode(). - */ - void RemoveNode(Node* node); - - /** - * @brief Slot when an edge is added to a graph (SetGraph() connects this) - * - * This should NEVER be called directly, only connected to a NodeGraph. To add an edge (i.e. connect two node - * parameters together), use NodeParam::ConnectEdge(). - */ - void AddEdge(NodeEdgePtr edge); - - /** - * @brief Slot when an edge is removed from a graph (SetGraph() connects this) - * - * This should NEVER be called directly, only connected to a NodeGraph. To remove an edge (i.e. disconnect two node - * parameters), use NodeParam::DisconnectEdge(). - */ - void RemoveEdge(NodeEdgePtr edge); - /** * @brief Internal function triggered when any change is signalled from the QGraphicsScene * @@ -182,11 +109,6 @@ private slots: */ void CreateNodeSlot(QAction* action); - /** - * @brief Automatically reposition the nodes based on their connections - */ - void Reorganize(); - }; #endif // NODEVIEW_H diff --git a/app/widget/nodeview/nodeviewedge.cpp b/app/widget/nodeview/nodeviewedge.cpp index 6f05a8ade..30e35a618 100644 --- a/app/widget/nodeview/nodeviewedge.cpp +++ b/app/widget/nodeview/nodeviewedge.cpp @@ -27,6 +27,7 @@ #include "common/clamp.h" #include "common/lerp.h" #include "nodeview.h" +#include "nodeviewscene.h" NodeViewEdge::NodeViewEdge(QGraphicsItem *parent) : QGraphicsPathItem(parent), @@ -77,13 +78,17 @@ qreal CalculateEdgeYPoint(NodeViewItem *item, NodeParam* param, NodeViewItem *op void NodeViewEdge::Adjust() { - if (edge_ == nullptr || scene() == nullptr) { + if (!edge_ || !scene()) { return; } // Get the UI objects of both nodes that this edge connects - NodeViewItem* output = NodeView::NodeToUIObject(scene(), edge_->output()->parentNode()); - NodeViewItem* input = NodeView::NodeToUIObject(scene(), edge_->input()->parentNode()); + NodeViewItem* output = static_cast(scene())->NodeToUIObject(edge_->output()->parentNode()); + NodeViewItem* input = static_cast(scene())->NodeToUIObject(edge_->input()->parentNode()); + + if (!output || !input) { + return; + } // Create initial values QPointF output_point = QPointF(output->pos().x() + output->rect().left() + output->rect().width(), 0); diff --git a/app/widget/nodeview/nodeviewitem.cpp b/app/widget/nodeview/nodeviewitem.cpp index 77dae6f7e..9a99cee2b 100644 --- a/app/widget/nodeview/nodeviewitem.cpp +++ b/app/widget/nodeview/nodeviewitem.cpp @@ -29,6 +29,7 @@ #include "common/qtutils.h" #include "core.h" #include "nodeview.h" +#include "nodeviewscene.h" #include "nodeviewundo.h" #include "ui/icons/icons.h" #include "window/mainwindow/mainwindow.h" @@ -326,12 +327,12 @@ void NodeViewItem::mousePressEvent(QGraphicsSceneMouseEvent *event) drag_src_param_ = edge->output(); // Get its Node UI object - drag_source_ = NodeView::NodeToUIObject(scene(), drag_src_param_->parentNode()); + drag_source_ = static_cast(scene())->NodeToUIObject(drag_src_param_->parentNode()); // Get the opposing parameter's rect center using the line's current coordinates // (we use the current coordinates because a complex formula is used for the line's coords if the opposing // node is collapsed, therefore it's easier to just retrieve it from line itself) - NodeViewEdge* existing_edge_ui = NodeView::EdgeToUIObject(scene(), edge); + NodeViewEdge* existing_edge_ui = static_cast(scene())->EdgeToUIObject(edge); QPainterPath existing_edge_line = existing_edge_ui->path(); QPointF edge_start = existing_edge_line.pointAtPercent(0); QPointF edge_end = existing_edge_line.pointAtPercent(1); diff --git a/app/widget/nodeview/nodeviewscene.cpp b/app/widget/nodeview/nodeviewscene.cpp new file mode 100644 index 000000000..712a8150b --- /dev/null +++ b/app/widget/nodeview/nodeviewscene.cpp @@ -0,0 +1,233 @@ +#include "nodeviewscene.h" + +NodeViewScene::NodeViewScene(QObject *parent) : + QGraphicsScene(parent), + graph_(nullptr) +{ + connect(&reorganize_timer_, &QTimer::timeout, &reorganize_timer_, &QTimer::stop); + connect(&reorganize_timer_, &QTimer::timeout, this, &NodeViewScene::Reorganize); +} + +void NodeViewScene::clear() +{ + { + QHash::const_iterator i; + for (i=item_map_.begin();i!=item_map_.end();i++) { + delete i.value(); + } + item_map_.clear(); + } + + { + QHash::const_iterator i; + for (i=edge_map_.begin();i!=edge_map_.end();i++) { + delete i.value(); + } + edge_map_.clear(); + } +} + +NodeViewItem *NodeViewScene::NodeToUIObject(Node *n) +{ + return item_map_.value(n); +} + +NodeViewEdge *NodeViewScene::EdgeToUIObject(NodeEdgePtr n) +{ + return edge_map_.value(n.get()); +} + +void NodeViewScene::SetGraph(NodeGraph *graph) +{ + graph_ = graph; +} + +void NodeViewScene::AddNode(Node* node) +{ + NodeViewItem* item = new NodeViewItem(); + + item->SetNode(node); + + addItem(item); + item_map_.insert(node, item); + + // Add a NodeViewEdge for each connection + foreach (NodeParam* param, node->parameters()) { + + // We only bother working with outputs since eventually this will cover all inputs too + // (covering both would lead to duplicates since every edge connects to one input and one output) + if (param->type() == NodeParam::kOutput) { + const QVector& edges = param->edges(); + + foreach(NodeEdgePtr edge, edges) { + AddEdge(edge); + } + } + } + + QueueReorganize(); +} + +void NodeViewScene::RemoveNode(Node *node) +{ + delete item_map_.take(node); +} + +void NodeViewScene::AddEdge(NodeEdgePtr edge) +{ + NodeViewEdge* edge_ui = new NodeViewEdge(); + + edge_ui->SetEdge(edge); + + addItem(edge_ui); + edge_map_.insert(edge.get(), edge_ui); + + QueueReorganize(); +} + +void NodeViewScene::RemoveEdge(NodeEdgePtr edge) +{ + delete edge_map_.take(edge.get()); +} + +void NodeViewScene::QueueReorganize() +{ + // Avoids the fairly complex Reorganize() function every single time a connection or node is added + + reorganize_timer_.stop(); + reorganize_timer_.start(20); +} + +QList NodeViewScene::GetNodeDirectDescendants(Node* n, const QList connected_nodes, QList& processed_nodes) +{ + QList direct_descendants = connected_nodes; + + processed_nodes.append(n); + + // Remove any nodes that aren't necessarily attached directly + for (int i=0;ioutput()->edges().size();j++) { + Node* this_output_connection = connected->output()->edges().at(j)->input()->parentNode(); + if (!processed_nodes.contains(this_output_connection)) { + direct_descendants.removeAt(i); + i--; + break; + } + } + } + + return direct_descendants; +} + +int NodeViewScene::FindWeightsInternal(Node *node, QHash &weights, QList& weighted_nodes) +{ + QList connected_nodes = node->GetImmediateDependencies(); + + int weight = 0; + + if (!connected_nodes.isEmpty()) { + QList direct_descendants = GetNodeDirectDescendants(node, connected_nodes, weighted_nodes); + + foreach (Node* dep, direct_descendants) { + weight += FindWeightsInternal(dep, weights, weighted_nodes); + } + } + + weight = qMax(weight, 1); + + weights.insert(node, weight); + + return weight; +} + +void NodeViewScene::ReorganizeInternal(NodeViewItem* src_item, QHash& weights, QList& positioned_nodes) +{ + if (!src_item) { + return; + } + + Node* n = src_item->node(); + + QList connected_nodes = n->GetImmediateDependencies(); + + if (connected_nodes.isEmpty()) { + return; + } + + QList direct_descendants = GetNodeDirectDescendants(n, connected_nodes, positioned_nodes); + + int descendant_weight = 0; + foreach (Node* dep, direct_descendants) { + descendant_weight += weights.value(dep); + } + + qreal center_y = src_item->y(); + qreal total_height = descendant_weight * src_item->rect().height() + (direct_descendants.size()-1) * src_item->rect().height()/2; + double item_top = center_y - (total_height/2) + src_item->rect().height()/2; + + // Set each node's position + int weight_index = 0; + for (int i=0;irect().height() * 1.5; + + QPointF item_pos(src_item->pos().x() - item->rect().width() * 3 / 2, + item_y); + + item->setPos(item_pos); + + weight_index += weights.value(connected); + } + + // Recursively work on each node + foreach (Node* connected, connected_nodes) { + NodeViewItem* item = NodeToUIObject(connected); + + if (!item) { + continue; + } + + ReorganizeInternal(item, weights, positioned_nodes); + } +} + +void NodeViewScene::Reorganize() +{ + if (!graph_) { + return; + } + + QList end_nodes; + + // Calculate the nodes that don't output to anything, they'll be our anchors + foreach (Node* node, graph_->nodes()) { + if (!node->HasConnectedOutputs()) { + end_nodes.append(node); + } + } + + QList processed_nodes; + + QHash node_weights; + foreach (Node* end_node, end_nodes) { + FindWeightsInternal(end_node, node_weights, processed_nodes); + } + + processed_nodes.clear(); + + foreach (Node* end_node, end_nodes) { + ReorganizeInternal(NodeToUIObject(end_node), node_weights, processed_nodes); + } +} diff --git a/app/widget/nodeview/nodeviewscene.h b/app/widget/nodeview/nodeviewscene.h new file mode 100644 index 000000000..782970f3e --- /dev/null +++ b/app/widget/nodeview/nodeviewscene.h @@ -0,0 +1,99 @@ +#ifndef NODEVIEWSCENE_H +#define NODEVIEWSCENE_H + +#include +#include + +#include "node/graph.h" +#include "widget/nodeview/nodeviewedge.h" +#include "widget/nodeview/nodeviewitem.h" + +class NodeViewScene : public QGraphicsScene +{ + Q_OBJECT +public: + NodeViewScene(QObject *parent = nullptr); + + void clear(); + + /** + * @brief Retrieve the graphical widget corresponding to a specific Node + * + * In situations where you know what Node you're working with but need the UI object (e.g. for positioning), this + * static function will retrieve the NodeViewItem (Node UI representation) connected to this Node in a certain + * QGraphicsScene. This can be called from any other UI object, since it'll have a reference to the QGraphicsScene + * through QGraphicsItem::scene(). + * + * If the scene does not contain a widget for this node (usually meaning the node's graph is not the active graph + * in this view/scene), this function returns nullptr. + */ + NodeViewItem* NodeToUIObject(Node* n); + + /** + * @brief Retrieve the graphical widget corresponding to a specific NodeEdge + * + * Same as NodeToUIObject() but returns a NodeViewEdge corresponding to a NodeEdgePtr instead. + */ + NodeViewEdge* EdgeToUIObject(NodeEdgePtr n); + + void SetGraph(NodeGraph* graph); + +public slots: + /** + * @brief Slot when a Node is added to a graph (SetGraph() connects this) + * + * This should NEVER be called directly, only connected to a NodeGraph. To add a Node to the NodeGraph + * use NodeGraph::AddNode(). + */ + void AddNode(Node* node); + + /** + * @brief Slot when a Node is removed from a graph (SetGraph() connects this) + * + * This should NEVER be called directly, only connected to a NodeGraph. To remove a Node from the NodeGraph + * use NodeGraph::RemoveNode(). + */ + void RemoveNode(Node* node); + + /** + * @brief Slot when an edge is added to a graph (SetGraph() connects this) + * + * This should NEVER be called directly, only connected to a NodeGraph. To add an edge (i.e. connect two node + * parameters together), use NodeParam::ConnectEdge(). + */ + void AddEdge(NodeEdgePtr edge); + + /** + * @brief Slot when an edge is removed from a graph (SetGraph() connects this) + * + * This should NEVER be called directly, only connected to a NodeGraph. To remove an edge (i.e. disconnect two node + * parameters), use NodeParam::DisconnectEdge(). + */ + void RemoveEdge(NodeEdgePtr edge); + +private: + void QueueReorganize(); + + QList GetNodeDirectDescendants(Node* n, const QList connected_nodes, QList& processed_nodes); + + int FindWeightsInternal(Node* node, QHash& weights, QList& weighted_nodes); + + void ReorganizeInternal(NodeViewItem *src_item, QHash& weights, QList &positioned_nodes); + + QHash item_map_; + + QHash edge_map_; + + QTimer reorganize_timer_; + + NodeGraph* graph_; + +private slots: + /** + * @brief Automatically reposition the nodes based on their connections + */ + void Reorganize(); + +}; + +#endif // NODEVIEWSCENE_H