From b947b2224a41bee855935986a6442dfa218cc350 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 27 Apr 2020 16:08:48 +1000 Subject: [PATCH] nodeview: support multiple flow directions --- app/widget/keyframeview/keyframeviewbase.cpp | 2 +- app/widget/menu/menu.cpp | 11 + app/widget/menu/menu.h | 4 + app/widget/menu/menushared.cpp | 9 +- app/widget/menu/menushared.h | 2 +- app/widget/nodeview/CMakeLists.txt | 1 + app/widget/nodeview/nodeview.cpp | 65 +++++- app/widget/nodeview/nodeview.h | 12 ++ app/widget/nodeview/nodeviewcommon.h | 50 +++++ app/widget/nodeview/nodeviewedge.cpp | 24 ++- app/widget/nodeview/nodeviewedge.h | 8 + app/widget/nodeview/nodeviewitem.cpp | 75 +++++-- app/widget/nodeview/nodeviewitem.h | 16 ++ app/widget/nodeview/nodeviewscene.cpp | 203 +++++++------------ app/widget/nodeview/nodeviewscene.h | 30 ++- app/window/mainwindow/mainmenu.cpp | 2 +- 16 files changed, 338 insertions(+), 176 deletions(-) create mode 100644 app/widget/nodeview/nodeviewcommon.h diff --git a/app/widget/keyframeview/keyframeviewbase.cpp b/app/widget/keyframeview/keyframeviewbase.cpp index 2baac4e4c..8c830f615 100644 --- a/app/widget/keyframeview/keyframeviewbase.cpp +++ b/app/widget/keyframeview/keyframeviewbase.cpp @@ -391,7 +391,7 @@ void KeyframeViewBase::ShowContextMenu() { Menu m; - MenuShared::instance()->AddItemsForEditMenu(&m); + MenuShared::instance()->AddItemsForEditMenu(&m, false); QAction* linear_key_action = nullptr; QAction* bezier_key_action = nullptr; diff --git a/app/widget/menu/menu.cpp b/app/widget/menu/menu.cpp index 0ffc65e95..5515c65da 100644 --- a/app/widget/menu/menu.cpp +++ b/app/widget/menu/menu.cpp @@ -50,6 +50,17 @@ Menu::Menu(const QString &s, QWidget *parent) : Init(); } +QAction *Menu::AddActionWithData(const QString &text, const QVariant &data, const QVariant &compare) +{ + QAction* a = addAction(text); + + a->setData(data); + a->setCheckable(true); + a->setChecked(data == compare); + + return a; +} + QAction* Menu::InsertAlphabetically(const QString &s) { QAction* action = new QAction(s, this); diff --git a/app/widget/menu/menu.h b/app/widget/menu/menu.h index 4151cfd79..edafaf85d 100644 --- a/app/widget/menu/menu.h +++ b/app/widget/menu/menu.h @@ -131,6 +131,10 @@ public: return a; } + QAction* AddActionWithData(const QString& text, + const QVariant& data, + const QVariant& compare); + QAction *InsertAlphabetically(const QString& s); void InsertAlphabetically(QAction* entry); void InsertAlphabetically(Menu* menu); diff --git a/app/widget/menu/menushared.cpp b/app/widget/menu/menushared.cpp index 96015d2b8..2d68e3a3d 100644 --- a/app/widget/menu/menushared.cpp +++ b/app/widget/menu/menushared.cpp @@ -79,7 +79,7 @@ void MenuShared::AddItemsForNewMenu(Menu *m) m->addAction(new_folder_item_); } -void MenuShared::AddItemsForEditMenu(Menu *m) +void MenuShared::AddItemsForEditMenu(Menu *m, bool for_clips) { m->addAction(edit_cut_item_); m->addAction(edit_copy_item_); @@ -87,8 +87,11 @@ void MenuShared::AddItemsForEditMenu(Menu *m) m->addAction(edit_paste_insert_item_); m->addAction(edit_duplicate_item_); m->addAction(edit_delete_item_); - m->addAction(edit_ripple_delete_item_); - m->addAction(edit_split_item_); + + if (for_clips) { + m->addAction(edit_ripple_delete_item_); + m->addAction(edit_split_item_); + } } void MenuShared::AddItemsForInOutMenu(Menu *m) diff --git a/app/widget/menu/menushared.h b/app/widget/menu/menushared.h index 81848b3db..684616610 100644 --- a/app/widget/menu/menushared.h +++ b/app/widget/menu/menushared.h @@ -39,7 +39,7 @@ public: void Retranslate(); void AddItemsForNewMenu(Menu* m); - void AddItemsForEditMenu(Menu* m); + void AddItemsForEditMenu(Menu* m, bool for_clips); void AddItemsForInOutMenu(Menu* m); void AddItemsForClipEditMenu(Menu* m); diff --git a/app/widget/nodeview/CMakeLists.txt b/app/widget/nodeview/CMakeLists.txt index 9682e3972..c36c19882 100644 --- a/app/widget/nodeview/CMakeLists.txt +++ b/app/widget/nodeview/CMakeLists.txt @@ -18,6 +18,7 @@ set(OLIVE_SOURCES ${OLIVE_SOURCES} widget/nodeview/nodeview.h widget/nodeview/nodeview.cpp + widget/nodeview/nodeviewcommon.h widget/nodeview/nodeviewedge.h widget/nodeview/nodeviewedge.cpp widget/nodeview/nodeviewitem.h diff --git a/app/widget/nodeview/nodeview.cpp b/app/widget/nodeview/nodeview.cpp index 5483d50cf..4f57c3d2d 100644 --- a/app/widget/nodeview/nodeview.cpp +++ b/app/widget/nodeview/nodeview.cpp @@ -25,6 +25,7 @@ #include "core.h" #include "nodeviewundo.h" #include "node/factory.h" +#include "widget/menu/menushared.h" OLIVE_NAMESPACE_ENTER @@ -37,13 +38,14 @@ NodeView::NodeView(QWidget *parent) : setScene(&scene_); setDragMode(RubberBandDrag); setContextMenuPolicy(Qt::CustomContextMenu); + setMouseTracking(true); + setRenderHint(QPainter::Antialiasing); connect(&scene_, &QGraphicsScene::changed, this, &NodeView::ItemsChanged); connect(&scene_, &QGraphicsScene::selectionChanged, this, &NodeView::SceneSelectionChangedSlot); connect(this, &NodeView::customContextMenuRequested, this, &NodeView::ShowContextMenu); - setMouseTracking(true); - setRenderHint(QPainter::Antialiasing); + SetFlowDirection(NodeViewCommon::kTopToBottom); } NodeView::~NodeView() @@ -321,10 +323,42 @@ void NodeView::ShowContextMenu(const QPoint &pos) Menu m; - Menu* add_menu = NodeFactory::CreateMenu(&m); - add_menu->setTitle(tr("Add")); - connect(add_menu, &Menu::triggered, this, &NodeView::CreateNodeSlot); - m.addMenu(add_menu); + MenuShared::instance()->AddItemsForEditMenu(&m, false); + + m.addSeparator(); + + if (itemAt(pos)) { + QAction* autopos = m.addAction(tr("Auto-Position")); + connect(autopos, &QAction::triggered, this, &NodeView::AutoPositionDescendents); + } else { + Menu* direction_menu = new Menu(tr("Direction"), &m); + m.addMenu(direction_menu); + + direction_menu->AddActionWithData(tr("Top to Bottom"), + NodeViewCommon::kTopToBottom, + scene_.GetFlowDirection()); + + direction_menu->AddActionWithData(tr("Bottom to Top"), + NodeViewCommon::kBottomToTop, + scene_.GetFlowDirection()); + + direction_menu->AddActionWithData(tr("Left to Right"), + NodeViewCommon::kLeftToRight, + scene_.GetFlowDirection()); + + direction_menu->AddActionWithData(tr("Right to Left"), + NodeViewCommon::kRightToLeft, + scene_.GetFlowDirection()); + + connect(direction_menu, &Menu::triggered, this, &NodeView::ContextMenuSetDirection); + + m.addSeparator(); + + Menu* add_menu = NodeFactory::CreateMenu(&m); + add_menu->setTitle(tr("Add")); + connect(add_menu, &Menu::triggered, this, &NodeView::CreateNodeSlot); + m.addMenu(add_menu); + } m.exec(mapToGlobal(pos)); } @@ -341,6 +375,20 @@ void NodeView::CreateNodeSlot(QAction *action) } } +void NodeView::ContextMenuSetDirection(QAction *action) +{ + SetFlowDirection(static_cast(action->data().toInt())); +} + +void NodeView::AutoPositionDescendents() +{ + QList selected = scene_.GetSelectedNodes(); + + foreach (Node* n, selected) { + scene_.ReorganizeFrom(n); + } +} + void NodeView::PlaceNode(NodeViewItem *n, const QPointF &pos) { QRectF destination_rect = n->rect(); @@ -487,4 +535,9 @@ void NodeView::DetachItemFromCursor() AttachItemToCursor(nullptr); } +void NodeView::SetFlowDirection(NodeViewCommon::FlowDirection dir) +{ + scene_.SetFlowDirection(dir); +} + OLIVE_NAMESPACE_EXIT diff --git a/app/widget/nodeview/nodeview.h b/app/widget/nodeview/nodeview.h index 05d72464e..0bfcb37bb 100644 --- a/app/widget/nodeview/nodeview.h +++ b/app/widget/nodeview/nodeview.h @@ -85,6 +85,8 @@ private: void DetachItemFromCursor(); + void SetFlowDirection(NodeViewCommon::FlowDirection dir); + NodeGraph* graph_; NodeViewItem* attached_item_; @@ -117,6 +119,16 @@ private slots: */ void CreateNodeSlot(QAction* action); + /** + * @brief Receiver for setting the direction from the context menu + */ + void ContextMenuSetDirection(QAction* action); + + /** + * @brief Receiver for auto-position descendents menu action + */ + void AutoPositionDescendents(); + }; OLIVE_NAMESPACE_EXIT diff --git a/app/widget/nodeview/nodeviewcommon.h b/app/widget/nodeview/nodeviewcommon.h new file mode 100644 index 000000000..c54ffe89f --- /dev/null +++ b/app/widget/nodeview/nodeviewcommon.h @@ -0,0 +1,50 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef NODEVIEWCOMMON_H +#define NODEVIEWCOMMON_H + +#include + +#include "common/define.h" + +OLIVE_NAMESPACE_ENTER + +class NodeViewCommon { +public: + enum FlowDirection { + kTopToBottom, + kBottomToTop, + kLeftToRight, + kRightToLeft + }; + + static Qt::Orientation GetFlowOrientation(FlowDirection dir) { + if (dir == kTopToBottom || dir == kBottomToTop) { + return Qt::Vertical; + } else { + return Qt::Horizontal; + } + } +}; + +OLIVE_NAMESPACE_EXIT + +#endif // NODEVIEWCOMMON_H diff --git a/app/widget/nodeview/nodeviewedge.cpp b/app/widget/nodeview/nodeviewedge.cpp index 333385e22..5ca752d29 100644 --- a/app/widget/nodeview/nodeviewedge.cpp +++ b/app/widget/nodeview/nodeviewedge.cpp @@ -35,7 +35,8 @@ NodeViewEdge::NodeViewEdge(QGraphicsItem *parent) : QGraphicsPathItem(parent), edge_(nullptr), color_group_(QPalette::Active), - color_role_(QPalette::Text) + color_role_(QPalette::Text), + flow_dir_(NodeViewCommon::kLeftToRight) { // Ensures this UI object is drawn behind other objects setZValue(-1); @@ -75,7 +76,8 @@ void NodeViewEdge::Adjust() } // Draw a line between the two - SetPoints(output->GetParamPoint(edge_->output()), input->GetParamPoint(edge_->input())); + SetPoints(output->GetParamPoint(edge_->output()), + input->GetParamPoint(edge_->input())); } void NodeViewEdge::SetConnected(bool c) @@ -103,12 +105,26 @@ void NodeViewEdge::SetHighlighted(bool e) void NodeViewEdge::SetPoints(const QPointF &start, const QPointF &end) { QPainterPath path; - double half_x = lerp(start.x(), end.x(), 0.5); path.moveTo(start); - path.cubicTo(QPointF(half_x, start.y()), QPointF(half_x, end.y()), end); + + if (NodeViewCommon::GetFlowOrientation(flow_dir_) == Qt::Horizontal) { + double half_x = lerp(start.x(), end.x(), 0.5); + path.cubicTo(QPointF(half_x, start.y()), QPointF(half_x, end.y()), end); + } else { + double half_y = lerp(start.y(), end.y(), 0.5); + path.cubicTo(QPointF(start.x(), half_y), QPointF(end.x(), half_y), end); + } + setPath(path); } +void NodeViewEdge::SetFlowDirection(NodeViewCommon::FlowDirection dir) +{ + flow_dir_ = dir; + + Adjust(); +} + void NodeViewEdge::UpdatePen() { setPen(QPen(qApp->palette().color(color_group_, color_role_), edge_width_)); diff --git a/app/widget/nodeview/nodeviewedge.h b/app/widget/nodeview/nodeviewedge.h index 1f9b66876..b180333a4 100644 --- a/app/widget/nodeview/nodeviewedge.h +++ b/app/widget/nodeview/nodeviewedge.h @@ -25,6 +25,7 @@ #include #include "node/edge.h" +#include "nodeviewcommon.h" OLIVE_NAMESPACE_ENTER @@ -83,6 +84,11 @@ public: */ void SetPoints(const QPointF& start, const QPointF& end); + /** + * @brief Sets the direction nodes are flowing + */ + void SetFlowDirection(NodeViewCommon::FlowDirection dir); + private: void UpdatePen(); @@ -94,6 +100,8 @@ private: QPalette::ColorRole color_role_; + NodeViewCommon::FlowDirection flow_dir_; + }; OLIVE_NAMESPACE_EXIT diff --git a/app/widget/nodeview/nodeviewitem.cpp b/app/widget/nodeview/nodeviewitem.cpp index fe42d818d..7afe12687 100644 --- a/app/widget/nodeview/nodeviewitem.cpp +++ b/app/widget/nodeview/nodeviewitem.cpp @@ -46,7 +46,8 @@ NodeViewItem::NodeViewItem(QGraphicsItem *parent) : expanded_(false), standard_click_(false), highlighted_index_(-1), - node_edge_change_command_(nullptr) + node_edge_change_command_(nullptr), + flow_dir_(NodeViewCommon::kLeftToRight) { // Set flags for this widget setFlag(QGraphicsItem::ItemIsMovable); @@ -57,26 +58,36 @@ NodeViewItem::NodeViewItem(QGraphicsItem *parent) : // We use font metrics to set all the UI measurements for DPI-awareness // - QFont default_font; - QFontMetrics font_metrics(default_font); - // Set border width - node_border_width_ = font_metrics.height() / 12; + node_border_width_ = DefaultItemBorder(); - // Set text and icon padding - int node_text_padding = font_metrics.height() / 4; - - // Not particularly great way of using text scaling to set the width (DPI-awareness, etc.) - int widget_width = QFontMetricsWidth(font_metrics, "HHHHHHHHHHHHHH"); - - // Use the current default font height to size this widget - // Set default "collapsed" size - int widget_height = font_metrics.height() + node_text_padding * 2; + int widget_width = DefaultItemWidth(); + int widget_height = DefaultItemHeight(); title_bar_rect_ = QRectF(-widget_width/2, -widget_height/2, widget_width, widget_height); setRect(title_bar_rect_); } +int NodeViewItem::DefaultTextPadding() +{ + return QFontMetrics(QFont()).height() / 4; +} + +int NodeViewItem::DefaultItemHeight() +{ + return QFontMetrics(QFont()).height() + DefaultTextPadding() * 2; +} + +int NodeViewItem::DefaultItemWidth() +{ + return QFontMetricsWidth(QFontMetrics(QFont()), "HHHHHHHHHHHHHH");; +} + +int NodeViewItem::DefaultItemBorder() +{ + return QFontMetrics(QFont()).height() / 12; +} + void NodeViewItem::SetNode(Node *n) { node_ = n; @@ -203,6 +214,7 @@ void NodeViewItem::mousePressEvent(QGraphicsSceneMouseEvent *event) // Create draggable object dragging_edge_ = new NodeViewEdge(); + dragging_edge_->SetFlowDirection(flow_dir_); // Set up a QUndoCommand to make this action undoable node_edge_change_command_ = new QUndoCommand(); @@ -330,7 +342,9 @@ void NodeViewItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) if (!cached_drop_item_->GetNode()->OutputsTo(node_)) { drag_dest_param_ = comp_param; highlight_their_index = i; - end_point = cached_drop_item_->mapToScene(cached_drop_item_->GetInputPoint(i)); + + QPointF end_point_local = cached_drop_item_->GetInputPoint(i); + end_point = cached_drop_item_->mapToScene(end_point_local); } break; @@ -439,7 +453,19 @@ QRectF NodeViewItem::GetInputRect(int index) const QPointF NodeViewItem::GetParamPoint(NodeParam *param) const { if (param->type() == NodeParam::kOutput) { - return pos() + QPointF(rect().right(), rect().center().y()); + + switch (flow_dir_) { + case NodeViewCommon::kLeftToRight: + default: + return pos() + QPointF(rect().right(), rect().center().y()); + case NodeViewCommon::kRightToLeft: + return pos() + QPointF(rect().left(), rect().center().y()); + case NodeViewCommon::kTopToBottom: + return pos() + QPointF(rect().center().x(), rect().bottom()); + case NodeViewCommon::kBottomToTop: + return pos() + QPointF(rect().center().x(), rect().top()); + } + } else { NodeInput* input = static_cast(param); @@ -452,11 +478,26 @@ QPointF NodeViewItem::GetParamPoint(NodeParam *param) const } } +void NodeViewItem::SetFlowDirection(NodeViewCommon::FlowDirection dir) +{ + flow_dir_ = dir; +} + QPointF NodeViewItem::GetInputPoint(int index) const { QRectF input_rect = GetInputRect(index); - return QPointF(input_rect.left(), input_rect.center().y()); + switch (flow_dir_) { + case NodeViewCommon::kLeftToRight: + default: + return QPointF(input_rect.left(), input_rect.center().y()); + case NodeViewCommon::kRightToLeft: + return QPointF(input_rect.right(), input_rect.center().y()); + case NodeViewCommon::kTopToBottom: + return QPointF(input_rect.center().x(), input_rect.top()); + case NodeViewCommon::kBottomToTop: + return QPointF(input_rect.center().x(), input_rect.bottom()); + } } OLIVE_NAMESPACE_EXIT diff --git a/app/widget/nodeview/nodeviewitem.h b/app/widget/nodeview/nodeviewitem.h index 1ed17f844..28bc9a932 100644 --- a/app/widget/nodeview/nodeviewitem.h +++ b/app/widget/nodeview/nodeviewitem.h @@ -28,6 +28,7 @@ #include #include "node/node.h" +#include "nodeviewcommon.h" #include "nodeviewedge.h" #include "nodeviewitemwidgetproxy.h" @@ -71,6 +72,19 @@ public: */ QPointF GetParamPoint(NodeParam* param) const; + /** + * @brief Sets the direction nodes are flowing + */ + void SetFlowDirection(NodeViewCommon::FlowDirection dir); + + static int DefaultTextPadding(); + + static int DefaultItemHeight(); + + static int DefaultItemWidth(); + + static int DefaultItemBorder(); + protected: virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; @@ -153,6 +167,8 @@ private: */ QUndoCommand* node_edge_change_command_; + NodeViewCommon::FlowDirection flow_dir_; + }; OLIVE_NAMESPACE_EXIT diff --git a/app/widget/nodeview/nodeviewscene.cpp b/app/widget/nodeview/nodeviewscene.cpp index 7de9ce7b4..a1604d0c1 100644 --- a/app/widget/nodeview/nodeviewscene.cpp +++ b/app/widget/nodeview/nodeviewscene.cpp @@ -20,14 +20,35 @@ #include "nodeviewscene.h" +#include "nodeviewedge.h" +#include "nodeviewitem.h" + OLIVE_NAMESPACE_ENTER NodeViewScene::NodeViewScene(QObject *parent) : QGraphicsScene(parent), - graph_(nullptr) + graph_(nullptr), + direction_(NodeViewCommon::kLeftToRight) { - connect(&reorganize_timer_, &QTimer::timeout, &reorganize_timer_, &QTimer::stop); - connect(&reorganize_timer_, &QTimer::timeout, this, &NodeViewScene::Reorganize); +} + +void NodeViewScene::SetFlowDirection(NodeViewCommon::FlowDirection direction) +{ + direction_ = direction; + + { + QHash::const_iterator i; + for (i=item_map_.constBegin(); i!=item_map_.constEnd(); i++) { + i.value()->SetFlowDirection(direction_); + } + } + + { + QHash::const_iterator i; + for (i=edge_map_.constBegin(); i!=edge_map_.constEnd(); i++) { + i.value()->SetFlowDirection(direction_); + } + } } void NodeViewScene::clear() @@ -35,7 +56,7 @@ void NodeViewScene::clear() // Deselect everything (prevents signals that a selection has changed after deleting an object) DeselectAll(); - // HACK: QGraphicsScene contains some sort of internal hashing of the selected items which doesn't update unless + // HACK: QGraphicsScene contains some sort of internal caching of the selected items which doesn't update unless // we call a function like this. That means even though we deselect all items above, QGraphicsScene will // continue to incorrectly signal selectionChanged() when items that were selected (but are now not) get // deleted. Calling this function appears to update the internal cache and prevent this. @@ -134,6 +155,7 @@ void NodeViewScene::AddNode(Node* node) NodeViewItem* item = new NodeViewItem(); item->SetNode(node); + item->SetFlowDirection(direction_); addItem(item); item_map_.insert(node, item); @@ -151,8 +173,6 @@ void NodeViewScene::AddNode(Node* node) } } } - - QueueReorganize(); } void NodeViewScene::RemoveNode(Node *node) @@ -165,11 +185,10 @@ void NodeViewScene::AddEdge(NodeEdgePtr edge) NodeViewEdge* edge_ui = new NodeViewEdge(); edge_ui->SetEdge(edge); + edge_ui->SetFlowDirection(direction_); addItem(edge_ui); edge_map_.insert(edge.get(), edge_ui); - - QueueReorganize(); } void NodeViewScene::RemoveEdge(NodeEdgePtr edge) @@ -177,145 +196,77 @@ void NodeViewScene::RemoveEdge(NodeEdgePtr edge) delete edge_map_.take(edge.get()); } -void NodeViewScene::QueueReorganize() +Qt::Orientation NodeViewScene::GetFlowOrientation() const { - // Avoids the fairly complex Reorganize() function every single time a connection or node is added - - reorganize_timer_.stop(); - reorganize_timer_.start(20); + return NodeViewCommon::GetFlowOrientation(direction_); } -QList NodeViewScene::GetNodeDirectDescendants(Node* n, const QList connected_nodes, QList& processed_nodes) +NodeViewCommon::FlowDirection NodeViewScene::GetFlowDirection() const { - 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; + return direction_; } -int NodeViewScene::FindWeightsInternal(Node *node, QHash &weights, QList& weighted_nodes) +void NodeViewScene::ReorganizeFrom(Node* n) { - QList connected_nodes = node->GetImmediateDependencies(); + QList immediates = n->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) { + if (immediates.isEmpty()) { + // Nothing to do return; } - Node* n = src_item->GetNode(); + NodeViewItem* parent_item = NodeToUIObject(n); - QList connected_nodes = n->GetImmediateDependencies(); + int item_sz, item_padding, layer_diff, total_top; - if (connected_nodes.isEmpty()) { - return; + if (GetFlowOrientation() == Qt::Vertical) { + item_sz = NodeViewItem::DefaultItemWidth(); + item_padding = item_sz / 2; + layer_diff = NodeViewItem::DefaultItemHeight() * 2; + total_top = parent_item->x(); + } else { + item_sz = NodeViewItem::DefaultItemHeight(); + item_padding = item_sz; + layer_diff = NodeViewItem::DefaultItemWidth() * 3 / 2; + total_top = parent_item->y(); } - QList direct_descendants = GetNodeDirectDescendants(n, connected_nodes, positioned_nodes); + int item_sz_with_padding = item_sz + item_padding; - int descendant_weight = 0; - foreach (Node* dep, direct_descendants) { - descendant_weight += weights.value(dep); + int total_sz = item_sz_with_padding * immediates.size() - item_padding; + + total_top -= total_sz / 2; + total_top += item_sz / 2; + + int item_layer_pos; + + switch (direction_) { + case NodeViewCommon::kTopToBottom: + item_layer_pos = parent_item->pos().y() - layer_diff; + break; + case NodeViewCommon::kLeftToRight: + item_layer_pos = parent_item->pos().x() - layer_diff; + break; + case NodeViewCommon::kBottomToTop: + item_layer_pos = parent_item->pos().y() + layer_diff; + break; + case NodeViewCommon::kRightToLeft: + item_layer_pos = parent_item->pos().x() + layer_diff; + break; } - 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; + for (int i=0;isetPos(total_top + item_sz_with_padding * i, + item_layer_pos); + } else { + item->setPos(item_layer_pos, + total_top + item_sz_with_padding * i); } - double item_y = item_top; - - // Multiply the index by the item height (with 1.5 for padding) - item_y += weight_index * src_item->rect().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); + ReorganizeFrom(immediates.at(i)); } } diff --git a/app/widget/nodeview/nodeviewscene.h b/app/widget/nodeview/nodeviewscene.h index 436045700..48d4adba2 100644 --- a/app/widget/nodeview/nodeviewscene.h +++ b/app/widget/nodeview/nodeviewscene.h @@ -25,8 +25,8 @@ #include #include "node/graph.h" -#include "widget/nodeview/nodeviewedge.h" -#include "widget/nodeview/nodeviewitem.h" +#include "nodeviewedge.h" +#include "nodeviewitem.h" OLIVE_NAMESPACE_ENTER @@ -69,6 +69,16 @@ public: const QHash& item_map() const; const QHash& edge_map() const; + Qt::Orientation GetFlowOrientation() const; + + NodeViewCommon::FlowDirection GetFlowDirection() const; + void SetFlowDirection(NodeViewCommon::FlowDirection direction); + + /** + * @brief Automatically reposition the nodes based on their connections + */ + void ReorganizeFrom(Node* n); + public slots: /** * @brief Slot when a Node is added to a graph (SetGraph() connects this) @@ -103,27 +113,13 @@ public slots: 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(); + NodeViewCommon::FlowDirection direction_; }; diff --git a/app/window/mainwindow/mainmenu.cpp b/app/window/mainwindow/mainmenu.cpp index e35b83531..6af40d4a1 100644 --- a/app/window/mainwindow/mainmenu.cpp +++ b/app/window/mainwindow/mainmenu.cpp @@ -83,7 +83,7 @@ MainMenu::MainMenu(MainWindow *parent) : edit_menu_->addAction(edit_redo_item_); edit_menu_->addSeparator(); - MenuShared::instance()->AddItemsForEditMenu(edit_menu_); + MenuShared::instance()->AddItemsForEditMenu(edit_menu_, true); edit_menu_->addSeparator(); edit_select_all_item_ = edit_menu_->AddItem("selectall", this, &MainMenu::SelectAllTriggered, "Ctrl+A"); edit_deselect_all_item_ = edit_menu_->AddItem("deselectall", this, &MainMenu::DeselectAllTriggered, "Ctrl+Shift+A");