diff --git a/app/widget/nodeview/CMakeLists.txt b/app/widget/nodeview/CMakeLists.txt index 10163c953..c36c19882 100644 --- a/app/widget/nodeview/CMakeLists.txt +++ b/app/widget/nodeview/CMakeLists.txt @@ -21,8 +21,6 @@ set(OLIVE_SOURCES widget/nodeview/nodeviewcommon.h widget/nodeview/nodeviewedge.h widget/nodeview/nodeviewedge.cpp - widget/nodeview/nodeviewfilter.h - widget/nodeview/nodeviewfilter.cpp widget/nodeview/nodeviewitem.h widget/nodeview/nodeviewitem.cpp widget/nodeview/nodeviewitemwidgetproxy.h diff --git a/app/widget/nodeview/nodeview.cpp b/app/widget/nodeview/nodeview.cpp index 958be003a..f1e9a3f42 100644 --- a/app/widget/nodeview/nodeview.cpp +++ b/app/widget/nodeview/nodeview.cpp @@ -26,7 +26,6 @@ #include "core.h" #include "nodeviewundo.h" #include "node/factory.h" -#include "nodeviewfilter.h" #include "widget/menu/menushared.h" #define super HandMovableView @@ -44,6 +43,7 @@ NodeView::NodeView(QWidget *parent) : setContextMenuPolicy(Qt::CustomContextMenu); setMouseTracking(true); setRenderHint(QPainter::Antialiasing); + setViewportUpdateMode(FullViewportUpdate); connect(&scene_, &QGraphicsScene::changed, this, &NodeView::ItemsChanged); connect(this, &NodeView::customContextMenuRequested, this, &NodeView::ShowContextMenu); @@ -51,6 +51,8 @@ NodeView::NodeView(QWidget *parent) : ConnectSelectionChangedSignal(); SetFlowDirection(NodeViewCommon::kTopToBottom); + + scene_.setSceneRect(-1000000, -1000000, 2000000, 2000000); } NodeView::~NodeView() @@ -66,10 +68,10 @@ void NodeView::SetGraph(NodeGraph *graph) } if (graph_ != nullptr) { - disconnect(graph_, &NodeGraph::NodeAdded, this, &NodeView::AddNode); + disconnect(graph_, &NodeGraph::NodeAdded, &scene_, &NodeViewScene::AddNode); disconnect(graph_, &NodeGraph::NodeRemoved, &scene_, &NodeViewScene::RemoveNode); - disconnect(graph_, &NodeGraph::EdgeAdded, this, &NodeView::AddEdge); - disconnect(graph_, &NodeGraph::EdgeRemoved, &scene_, &NodeViewScene::RemoveEdge); + disconnect(graph_, &NodeGraph::EdgeAdded, this, &NodeView::GraphEdgeAdded); + disconnect(graph_, &NodeGraph::EdgeRemoved, this, &NodeView::GraphEdgeRemoved); } // Clear the scene of all UI objects @@ -81,12 +83,16 @@ void NodeView::SetGraph(NodeGraph *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::NodeAdded, &scene_, &NodeViewScene::AddNode); connect(graph_, &NodeGraph::NodeRemoved, &scene_, &NodeViewScene::RemoveNode); - connect(graph_, &NodeGraph::EdgeAdded, this, &NodeView::AddEdge); - connect(graph_, &NodeGraph::EdgeRemoved, &scene_, &NodeViewScene::RemoveEdge); + connect(graph_, &NodeGraph::EdgeAdded, this, &NodeView::GraphEdgeAdded); + connect(graph_, &NodeGraph::EdgeRemoved, this, &NodeView::GraphEdgeRemoved); - AddNodes(graph_->nodes()); + foreach (Node* n, graph_->nodes()) { + scene_.AddNode(n); + } + + ValidateFilter(); } } @@ -103,7 +109,9 @@ void NodeView::DeleteSelected() foreach (NodeEdge* edge, selected_edges) { new NodeEdgeRemoveCommand(edge->output(), edge->input(), command); + qDebug() << "Deleting edge between" << edge->output()->parentNode() << "and" << edge->input()->parentNode(); } + } { @@ -184,6 +192,13 @@ void NodeView::SelectWithDependencies(QList nodes) void NodeView::SelectBlocks(const QList &blocks) { + // Remove temporary associations + foreach (Block* b, selected_blocks_) { + if (!blocks.contains(b)) { + temporary_association_map_.remove(b); + } + } + selected_blocks_ = blocks; if (filter_mode_ == kFilterShowSelectedBlocks) { @@ -199,6 +214,13 @@ void NodeView::SelectBlocks(const QList &blocks) } SelectWithDependencies(nodes); + + if (!blocks.isEmpty()) { + NodeViewItem* item = scene_.NodeToUIObject(blocks.first()); + if (item) { + centerOn(item); + } + } } void NodeView::CopySelected(bool cut) @@ -294,7 +316,7 @@ void NodeView::ItemsChanged() { QHash::const_iterator i; - for (i=scene_.edge_map().begin(); i!=scene_.edge_map().end(); i++) { + for (i=scene_.edge_map().constBegin(); i!=scene_.edge_map().constEnd(); i++) { i.value()->Adjust(); } } @@ -528,6 +550,10 @@ void NodeView::CreateNodeSlot(QAction *action) Node* new_node = NodeFactory::CreateFromMenuAction(action); if (new_node) { + // Associate this new node with these blocks (allows it to be visible with them even while it + // isn't connected to anything) + AssociateNodeWithSelectedBlocks(new_node); + Core::instance()->undo_stack()->push(new NodeAddCommand(graph_, new_node)); NodeViewItem* item = scene_.NodeToUIObject(new_node); @@ -573,12 +599,6 @@ void NodeView::ContextMenuLabelNode() } } -void NodeView::ContextMenuShowFiltersDialog() -{ - NodeViewFilterDialog fd(this); - fd.exec(); -} - void NodeView::ContextMenuFilterChanged(QAction *action) { FilterMode filter = static_cast(action->data().toInt()); @@ -807,21 +827,17 @@ void NodeView::UpdateBlockFilter() bool first = true; QRectF last_rect; - QList all_nodes; + QList currently_visible; foreach (Block* b, selected_blocks_) { + // Auto-position this node's dependencies scene_.ReorganizeFrom(b); + // Start calculating the bounding rect of this node's deps QPointF node_pos = b->GetPosition(); QRectF anchor(node_pos, node_pos); - all_nodes.append(b); - QList deps = b->GetDependencies(); - all_nodes.append(deps); - - // Show nodes that are block dependencies - scene_.NodeToUIObject(b)->setVisible(true); foreach (Node* d, deps) { QPointF dep_pos = d->GetPosition(); @@ -831,9 +847,13 @@ void NodeView::UpdateBlockFilter() anchor.setTop(qMin(anchor.top(), dep_pos.y())); anchor.setBottom(qMax(anchor.bottom(), dep_pos.y())); - scene_.NodeToUIObject(d)->setVisible(true); + NodeViewItem* item = scene_.NodeToUIObject(d); + if (item) { + item->setVisible(true); + } } + // Shift the bounding rect in relation to the other bounding rects if (first) { first = false; } else { @@ -847,37 +867,53 @@ void NodeView::UpdateBlockFilter() } } + // Now that we're done calculating the bounding rect, add this block... + deps.append(b); + + // ...then add its associations + deps.append(temporary_association_map_[b]); + QHash >::const_iterator i; + for (i=association_map_.begin(); i!=association_map_.end(); i++) { + if (i.value().contains(b)) { + deps.append(i.key()); + } + } + + // And make sure all nodes are shown + foreach (Node* n, deps) { + NodeViewItem* item = scene_.NodeToUIObject(n); + if (item) { + item->setVisible(true); + } + } + + // And lastly, add them all to our currently visible list + currently_visible.append(deps); + + // Cache this rect so we can calculate other rects last_rect = anchor; } // Show only edges between those dependencies foreach (NodeViewEdge* edge, scene_.edge_map()) { - edge->setVisible((all_nodes.contains(edge->edge()->input()->parentNode()) - && all_nodes.contains(edge->edge()->output()->parentNode()))); + edge->setVisible((currently_visible.contains(edge->edge()->input()->parentNode()) + && currently_visible.contains(edge->edge()->output()->parentNode()))); } } -void NodeView::AddNodes(const QList node) +void NodeView::AssociateNodeWithSelectedBlocks(Node *n) { - foreach (Node* n, node) { - scene_.AddNode(n); + association_map_.insert(n, selected_blocks_); + connect(n, &Node::destroyed, this, &NodeView::AssociatedNodeDestroyed, Qt::DirectConnection); +} + +void NodeView::DisassociateNode(Node *n, bool remove_from_map) +{ + if (remove_from_map) { + association_map_.remove(n); } - ValidateFilter(); -} - -void NodeView::AddNode(Node *node) -{ - scene_.AddNode(node); - - ValidateFilter(); -} - -void NodeView::AddEdge(NodeEdgePtr edge) -{ - scene_.AddEdge(edge); - - ValidateFilter(); + disconnect(n, &Node::destroyed, this, &NodeView::AssociatedNodeDestroyed); } void NodeView::ValidateFilter() @@ -894,4 +930,62 @@ void NodeView::ValidateFilter() } } +void NodeView::AssociatedNodeDestroyed() +{ + DisassociateNode(static_cast(sender()), true); +} + +void NodeView::GraphEdgeAdded(NodeEdgePtr edge) +{ + Node* input_node = edge->input()->parentNode(); + + if (input_node->OutputsTo(static_cast(graph_)->viewer_output(), true)) { + QHash >::const_iterator i = association_map_.begin(); + + while (i != association_map_.end()) { + if (input_node->InputsFrom(i.key(), true)) { + i = association_map_.erase(i); + } else { + i++; + } + } + } + + scene_.AddEdge(edge); + + ValidateFilter(); +} + +void NodeView::GraphEdgeRemoved(NodeEdgePtr edge) +{ + scene_.RemoveEdge(edge); + + Node* output_node = edge->output()->parentNode(); + + // Check if this disconnected node still connects to a selected block, in which case do nothing + foreach (Block* b, selected_blocks_) { + if (output_node->OutputsTo(b, true)) { + return; + } + } + + QList disconnected_nodes; + disconnected_nodes.append(output_node); + disconnected_nodes.append(output_node->GetDependencies()); + + if (output_node->OutputsTo(static_cast(graph_)->viewer_output(), true)) { + // Check if this disconnected node still has a path to the viewer somewhere else + foreach (Block* b, selected_blocks_) { + QList& temp_assocs = temporary_association_map_[b]; + + temp_assocs.append(disconnected_nodes); + } + } else { + // Otherwise, we must associate these nodes + foreach (Node* n, disconnected_nodes) { + AssociateNodeWithSelectedBlocks(n); + } + } +} + OLIVE_NAMESPACE_EXIT diff --git a/app/widget/nodeview/nodeview.h b/app/widget/nodeview/nodeview.h index 06fe381af..6869126ec 100644 --- a/app/widget/nodeview/nodeview.h +++ b/app/widget/nodeview/nodeview.h @@ -102,6 +102,9 @@ private: void UpdateBlockFilter(); + void AssociateNodeWithSelectedBlocks(Node* n); + void DisassociateNode(Node* n, bool remove_from_map); + NodeGraph* graph_; struct AttachedItem { @@ -118,6 +121,10 @@ private: QList selected_blocks_; + QHash > association_map_; + + QHash > temporary_association_map_; + enum FilterMode { kFilterShowAll, kFilterShowSelectedBlocks @@ -126,12 +133,14 @@ private: FilterMode filter_mode_; private slots: - void AddNodes(const QList node); - void AddNode(Node* node); - void AddEdge(NodeEdgePtr edge); - void ValidateFilter(); + void AssociatedNodeDestroyed(); + + void GraphEdgeAdded(NodeEdgePtr edge); + + void GraphEdgeRemoved(NodeEdgePtr edge); + /** * @brief Internal function triggered when any change is signalled from the QGraphicsScene * @@ -169,11 +178,6 @@ private slots: */ void ContextMenuLabelNode(); - /** - * @brief Receiver that shows the filters dialog - */ - void ContextMenuShowFiltersDialog(); - /** * @brief Receiver for the user changing the filter */ diff --git a/app/widget/nodeview/nodeviewfilter.cpp b/app/widget/nodeview/nodeviewfilter.cpp deleted file mode 100644 index fea2d42dc..000000000 --- a/app/widget/nodeview/nodeviewfilter.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/*** - - 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 . - -***/ - -#include "nodeviewfilter.h" - -#include -#include -#include -#include -#include -#include -#include - -#define FILTER_COLUMN_COUNT 7 - -OLIVE_NAMESPACE_ENTER - -NodeViewFilterDialog::NodeViewFilterDialog(QWidget *parent) : - QDialog(parent), - row_count_(0) -{ - setWindowTitle(tr("Configure Node Filters")); - - QVBoxLayout* layout = new QVBoxLayout(this); - - { - QGroupBox* filter_group = new QGroupBox(); - filter_group->setTitle(tr("Filters")); - - QVBoxLayout* filter_outer_layout = new QVBoxLayout(filter_group); - filter_layout_ = new QGridLayout(); - filter_layout_->setMargin(0); - filter_outer_layout->addLayout(filter_layout_); - filter_outer_layout->addStretch(); - - layout->addWidget(filter_group); - } - - plus_btn_ = new QPushButton(tr("+")); - plus_btn_->setFixedWidth(plus_btn_->sizeHint().height()); - connect(plus_btn_, &QPushButton::clicked, this, &NodeViewFilterDialog::AppendRow); - - AppendRow(); - - QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); - connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); - connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); - layout->addWidget(buttons); -} - -void NodeViewFilterDialog::AppendRow() -{ - QCheckBox* enabled_check = new QCheckBox(); - enabled_check->setChecked(true); - filter_layout_->addWidget(enabled_check, row_count_, 0); - - QComboBox* showhide_combo = new QComboBox(); - showhide_combo->addItem(tr("Show")); - showhide_combo->addItem(tr("Hide")); - filter_layout_->addWidget(showhide_combo, row_count_, 1); - - QComboBox* type_combo = new QComboBox(); - type_combo->addItem(tr("Any")); - type_combo->addItem(tr("Selected")); - filter_layout_->addWidget(type_combo, row_count_, 2); - - QComboBox* from_combo = new QComboBox(); - from_combo->addItem(tr("From Node")); - from_combo->addItem(tr("From Category")); - filter_layout_->addWidget(from_combo, row_count_, 3); - - QComboBox* from_entry_combo = new QComboBox(); - filter_layout_->addWidget(from_entry_combo, row_count_, 4); - - QComboBox* to_entry_combo = new QComboBox(); - filter_layout_->addWidget(to_entry_combo, row_count_, 5); - - QPushButton* minus_btn = new QPushButton(tr("-")); - minus_btn->setFixedWidth(minus_btn->sizeHint().height()); - connect(minus_btn, &QPushButton::clicked, this, &NodeViewFilterDialog::RemoveRowButtonClicked); - filter_layout_->addWidget(minus_btn, row_count_, 6); - - row_count_++; - - UpdateAddButtonPos(); -} - -void NodeViewFilterDialog::RemoveRowButtonClicked() -{ - for (int i=0;iitemAtPosition(i, FILTER_COLUMN_COUNT-1)->widget() == sender()) { - RemoveRow(i); - return; - } - } -} - -void NodeViewFilterDialog::RemoveRow(int row) -{ - for (int i=0;iitemAtPosition(row, i)->widget(); - } - - for (int j=row+1;jitemAtPosition(j, i)->widget(); - - filter_layout_->addWidget(w, j - 1, i); - } - } - - row_count_--; - - UpdateAddButtonPos(); -} - -void NodeViewFilterDialog::UpdateAddButtonPos() -{ - filter_layout_->addWidget(plus_btn_, row_count_, FILTER_COLUMN_COUNT - 1); -} - -OLIVE_NAMESPACE_EXIT diff --git a/app/widget/nodeview/nodeviewfilter.h b/app/widget/nodeview/nodeviewfilter.h deleted file mode 100644 index 38e28f4ad..000000000 --- a/app/widget/nodeview/nodeviewfilter.h +++ /dev/null @@ -1,56 +0,0 @@ -/*** - - 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 NODEVIEWFILTERDIALOG_H -#define NODEVIEWFILTERDIALOG_H - -#include -#include - -#include "node/node.h" - -OLIVE_NAMESPACE_ENTER - -class NodeViewFilterDialog : public QDialog -{ -public: - NodeViewFilterDialog(QWidget* parent = nullptr); - -private: - void RemoveRow(int row); - - void UpdateAddButtonPos(); - - int row_count_; - - QGridLayout* filter_layout_; - - QPushButton* plus_btn_; - -private slots: - void AppendRow(); - - void RemoveRowButtonClicked(); - -}; - -OLIVE_NAMESPACE_EXIT - -#endif // NODEVIEWFILTERDIALOG_H