diff --git a/app/panel/node/node.cpp b/app/panel/node/node.cpp index 72a1c7c23..44aad38f6 100644 --- a/app/panel/node/node.cpp +++ b/app/panel/node/node.cpp @@ -29,7 +29,8 @@ NodePanel::NodePanel(QWidget *parent) : node_view_ = new NodeView(this); // Connect node view signals to this panel - connect(node_view_, SIGNAL(SelectionChanged(QList)), this, SIGNAL(SelectionChanged(QList))); + connect(node_view_, &NodeView::NodesSelected, this, &NodePanel::NodesSelected); + connect(node_view_, &NodeView::NodesDeselected, this, &NodePanel::NodesDeselected); // Set it as the main widget of this panel SetWidgetWithPadding(node_view_); @@ -38,64 +39,4 @@ NodePanel::NodePanel(QWidget *parent) : Retranslate(); } -void NodePanel::SetGraph(NodeGraph *graph) -{ - node_view_->SetGraph(graph); -} - -void NodePanel::SelectAll() -{ - node_view_->SelectAll(); -} - -void NodePanel::DeselectAll() -{ - node_view_->DeselectAll(); -} - -void NodePanel::DeleteSelected() -{ - node_view_->DeleteSelected(); -} - -void NodePanel::CutSelected() -{ - node_view_->CopySelected(true); -} - -void NodePanel::CopySelected() -{ - node_view_->CopySelected(false); -} - -void NodePanel::Paste() -{ - node_view_->Paste(); -} - -void NodePanel::Duplicate() -{ - node_view_->Duplicate(); -} - -void NodePanel::Select(const QList &nodes) -{ - node_view_->Select(nodes); -} - -void NodePanel::SelectWithDependencies(const QList &nodes) -{ - node_view_->SelectWithDependencies(nodes); -} - -void NodePanel::SelectBlocks(const QList &nodes) -{ - node_view_->SelectBlocks(nodes); -} - -void NodePanel::Retranslate() -{ - SetTitle(tr("Node Editor")); -} - OLIVE_NAMESPACE_EXIT diff --git a/app/panel/node/node.h b/app/panel/node/node.h index c2b4f370e..8580b7c1e 100644 --- a/app/panel/node/node.h +++ b/app/panel/node/node.h @@ -35,34 +35,77 @@ class NodePanel : public PanelWidget public: NodePanel(QWidget* parent); - void SetGraph(NodeGraph* graph); + void SetGraph(NodeGraph *graph) + { + node_view_->SetGraph(graph); + } - virtual void SelectAll() override; - virtual void DeselectAll() override; + virtual void SelectAll() override + { + node_view_->SelectAll(); + } - virtual void DeleteSelected() override; + virtual void DeselectAll() override + { + node_view_->DeselectAll(); + } - virtual void CutSelected() override; - virtual void CopySelected() override; + virtual void DeleteSelected() override + { + node_view_->DeleteSelected(); + } - virtual void Paste() override; + virtual void CutSelected() override + { + node_view_->CopySelected(true); + } - virtual void Duplicate() override; + virtual void CopySelected() override + { + node_view_->CopySelected(false); + } + + virtual void Paste() override + { + node_view_->Paste(); + } + + virtual void Duplicate() override + { + node_view_->Duplicate(); + } public slots: - void Select(const QList& nodes); - void SelectWithDependencies(const QList& nodes); + void Select(const QList& nodes) + { + node_view_->Select(nodes); + } - void SelectBlocks(const QList& nodes); + void SelectWithDependencies(const QList& nodes) + { + node_view_->SelectWithDependencies(nodes); + } + + void SelectBlocks(const QList& nodes) + { + node_view_->SelectBlocks(nodes); + } + + void DeselectBlocks(const QList& nodes) + { + node_view_->DeselectBlocks(nodes); + } signals: - /** - * @brief Wrapper for NodeView::SelectionChanged() - */ - void SelectionChanged(QList selected_nodes); + void NodesSelected(const QList& nodes); + + void NodesDeselected(const QList& nodes); private: - virtual void Retranslate() override; + virtual void Retranslate() override + { + SetTitle(tr("Node Editor")); + } NodeView* node_view_; diff --git a/app/panel/param/param.cpp b/app/panel/param/param.cpp index e6731e9b0..519250837 100644 --- a/app/panel/param/param.cpp +++ b/app/panel/param/param.cpp @@ -30,15 +30,22 @@ ParamPanel::ParamPanel(QWidget* parent) : NodeParamView* view = new NodeParamView(); connect(view, &NodeParamView::InputDoubleClicked, this, &ParamPanel::CreateCurvePanel); connect(view, &NodeParamView::RequestSelectNode, this, &ParamPanel::RequestSelectNode); - connect(view, &NodeParamView::FoundGizmos, this, &ParamPanel::FoundGizmos); + //connect(view, &NodeParamView::FoundGizmos, this, &ParamPanel::FoundGizmos); SetTimeBasedWidget(view); Retranslate(); } -void ParamPanel::SetNodes(QList nodes) +void ParamPanel::SelectNodes(const QList &nodes) { - static_cast(GetTimeBasedWidget())->SetNodes(nodes); + static_cast(GetTimeBasedWidget())->SelectNodes(nodes); + + Retranslate(); +} + +void ParamPanel::DeselectNodes(const QList &nodes) +{ + static_cast(GetTimeBasedWidget())->DeselectNodes(nodes); Retranslate(); } @@ -62,10 +69,10 @@ void ParamPanel::Retranslate() NodeParamView* view = static_cast(GetTimeBasedWidget()); - if (view->nodes().isEmpty()) { + if (view->GetItemMap().isEmpty()) { SetSubtitle(tr("(none)")); - } else if (view->nodes().size() == 1) { - SetSubtitle(view->nodes().first()->Name()); + } else if (view->GetItemMap().size() == 1) { + SetSubtitle(view->GetItemMap().firstKey()->Name()); } else { SetSubtitle(tr("(multiple)")); } diff --git a/app/panel/param/param.h b/app/panel/param/param.h index 12bc1715e..442c5e921 100644 --- a/app/panel/param/param.h +++ b/app/panel/param/param.h @@ -34,7 +34,8 @@ public: ParamPanel(QWidget* parent); public slots: - void SetNodes(QList nodes); + void SelectNodes(const QList& nodes); + void DeselectNodes(const QList& nodes); virtual void SetTimestamp(const int64_t& timestamp) override; diff --git a/app/panel/table/table.cpp b/app/panel/table/table.cpp index 7183e9c23..0b3c5260f 100644 --- a/app/panel/table/table.cpp +++ b/app/panel/table/table.cpp @@ -25,17 +25,11 @@ OLIVE_NAMESPACE_ENTER NodeTablePanel::NodeTablePanel(QWidget* parent) : TimeBasedPanel(QStringLiteral("NodeTablePanel"), parent) { - view_ = new NodeTableWidget(); - SetTimeBasedWidget(view_); + SetTimeBasedWidget(new NodeTableWidget()); Retranslate(); } -void NodeTablePanel::SetNodes(const QList &nodes) -{ - view_->SetNodes(nodes); -} - void NodeTablePanel::Retranslate() { SetTitle(tr("Table View")); diff --git a/app/panel/table/table.h b/app/panel/table/table.h index d08fe0de3..85c6ea888 100644 --- a/app/panel/table/table.h +++ b/app/panel/table/table.h @@ -33,12 +33,19 @@ public: NodeTablePanel(QWidget* parent); public slots: - void SetNodes(const QList& nodes); + void SelectNodes(const QList& nodes) + { + static_cast(GetTimeBasedWidget())->SelectNodes(nodes); + } + + void DeselectNodes(const QList& nodes) + { + static_cast(GetTimeBasedWidget())->DeselectNodes(nodes); + } private: virtual void Retranslate() override; - NodeTableWidget* view_; }; OLIVE_NAMESPACE_EXIT diff --git a/app/panel/timeline/timeline.cpp b/app/panel/timeline/timeline.cpp index 7d6cd4c8d..2a36987b3 100644 --- a/app/panel/timeline/timeline.cpp +++ b/app/panel/timeline/timeline.cpp @@ -33,7 +33,8 @@ TimelinePanel::TimelinePanel(QWidget *parent) : Retranslate(); - connect(tw, &TimelineWidget::SelectionChanged, this, &TimelinePanel::SelectionChanged); + connect(tw, &TimelineWidget::BlocksSelected, this, &TimelinePanel::BlocksSelected); + connect(tw, &TimelineWidget::BlocksDeselected, this, &TimelinePanel::BlocksDeselected); } void TimelinePanel::Clear() diff --git a/app/panel/timeline/timeline.h b/app/panel/timeline/timeline.h index cf09322cb..b05f7b8bc 100644 --- a/app/panel/timeline/timeline.h +++ b/app/panel/timeline/timeline.h @@ -91,7 +91,9 @@ protected: virtual void Retranslate() override; signals: - void SelectionChanged(const QList& selected_blocks); + void BlocksSelected(const QList& selected_blocks); + + void BlocksDeselected(const QList& deselected_blocks); }; diff --git a/app/widget/keyframeview/keyframeviewbase.cpp b/app/widget/keyframeview/keyframeviewbase.cpp index c97a85372..56ba3078d 100644 --- a/app/widget/keyframeview/keyframeviewbase.cpp +++ b/app/widget/keyframeview/keyframeviewbase.cpp @@ -74,6 +74,19 @@ void KeyframeViewBase::DeleteSelected() Core::instance()->undo_stack()->pushIfHasChildren(command); } +void KeyframeViewBase::RemoveKeyframesOfNode(Node *n) +{ + QList inputs = n->GetInputsIncludingArrays(); + + foreach (NodeInput* i, inputs) { + foreach (const NodeInput::KeyframeTrack& track, i->keyframe_tracks()) { + foreach (NodeKeyframePtr key, track) { + RemoveKeyframe(key); + } + } + } +} + void KeyframeViewBase::RemoveKeyframe(NodeKeyframePtr key) { KeyframeAboutToBeRemoved(key.get()); diff --git a/app/widget/keyframeview/keyframeviewbase.h b/app/widget/keyframeview/keyframeviewbase.h index 743ef996f..9cbe0b01f 100644 --- a/app/widget/keyframeview/keyframeviewbase.h +++ b/app/widget/keyframeview/keyframeviewbase.h @@ -40,6 +40,8 @@ public: void DeleteSelected(); + void RemoveKeyframesOfNode(Node* n); + public slots: void RemoveKeyframe(NodeKeyframePtr key); diff --git a/app/widget/nodeparamview/nodeparamview.cpp b/app/widget/nodeparamview/nodeparamview.cpp index 24f32e7d6..51b332fe6 100644 --- a/app/widget/nodeparamview/nodeparamview.cpp +++ b/app/widget/nodeparamview/nodeparamview.cpp @@ -122,52 +122,41 @@ NodeParamView::NodeParamView(QWidget *parent) : SetMaximumScale(TimelineViewBase::kMaximumScale); } -void NodeParamView::SetNodes(QList nodes) +void NodeParamView::SelectNodes(const QList &nodes) { - // If we already have item widgets, delete them all now - foreach (NodeParamViewItem* item, items_) { - emit ClosedNode(item->GetNode()); - emit FoundGizmos(nullptr); - item->deleteLater(); + foreach (Node* n, nodes) { + NodeParamViewItem* item = new NodeParamViewItem(n); + + // Insert the widget before the stretch + param_layout_->insertWidget(param_layout_->count() - 1, item); + + connect(item, &NodeParamViewItem::KeyframeAdded, keyframe_view_, &KeyframeView::AddKeyframe); + connect(item, &NodeParamViewItem::KeyframeRemoved, keyframe_view_, &KeyframeView::RemoveKeyframe); + connect(item, &NodeParamViewItem::RequestSetTime, this, &NodeParamView::ItemRequestedTimeChanged); + connect(item, &NodeParamViewItem::InputDoubleClicked, this, &NodeParamView::InputDoubleClicked); + connect(item, &NodeParamViewItem::RequestSelectNode, this, &NodeParamView::RequestSelectNode); + + items_.insert(n, item); } - items_.clear(); - // Reset keyframe view - keyframe_view_->Clear(); + UpdateItemTime(GetTimestamp()); - // Set the internal list to the one we've received - nodes_ = nodes; + // Re-arrange keyframes + QMetaObject::invokeMethod(this, "PlaceKeyframesOnView", Qt::QueuedConnection); +} - if (!nodes_.isEmpty()) { - // For each node, create a widget - bool found_gizmos = false; +void NodeParamView::DeselectNodes(const QList &nodes) +{ + // Remove item from map and delete the widget + foreach (Node* n, nodes) { + // Remove all keyframes from this node + keyframe_view_->RemoveKeyframesOfNode(n); - foreach (Node* node, nodes_) { - NodeParamViewItem* item = new NodeParamViewItem(node); - - // Insert the widget before the stretch - param_layout_->insertWidget(param_layout_->count() - 1, item); - - connect(item, &NodeParamViewItem::KeyframeAdded, keyframe_view_, &KeyframeView::AddKeyframe); - connect(item, &NodeParamViewItem::KeyframeRemoved, keyframe_view_, &KeyframeView::RemoveKeyframe); - connect(item, &NodeParamViewItem::RequestSetTime, this, &NodeParamView::ItemRequestedTimeChanged); - connect(item, &NodeParamViewItem::InputDoubleClicked, this, &NodeParamView::InputDoubleClicked); - connect(item, &NodeParamViewItem::RequestSelectNode, this, &NodeParamView::RequestSelectNode); - - items_.append(item); - - emit OpenedNode(node); - - if (!found_gizmos && node->HasGizmos()) { - emit FoundGizmos(node); - found_gizmos = true; - } - } - - UpdateItemTime(GetTimestamp()); - - QMetaObject::invokeMethod(this, "PlaceKeyframesOnView", Qt::QueuedConnection); + delete items_.take(n); } + + // Re-arrange keyframes + QMetaObject::invokeMethod(this, "PlaceKeyframesOnView", Qt::QueuedConnection); } void NodeParamView::resizeEvent(QResizeEvent *event) @@ -212,11 +201,6 @@ void NodeParamView::ConnectedNodeChanged(ViewerOutput *n) } } -const QList &NodeParamView::nodes() -{ - return nodes_; -} - Node *NodeParamView::GetTimeTarget() const { return keyframe_view_->GetTimeTarget(); diff --git a/app/widget/nodeparamview/nodeparamview.h b/app/widget/nodeparamview/nodeparamview.h index 9fe697d97..1e3889b0c 100644 --- a/app/widget/nodeparamview/nodeparamview.h +++ b/app/widget/nodeparamview/nodeparamview.h @@ -37,8 +37,13 @@ class NodeParamView : public TimeBasedWidget public: NodeParamView(QWidget* parent = nullptr); - void SetNodes(QList nodes); - const QList& nodes(); + void SelectNodes(const QList& nodes); + void DeselectNodes(const QList& nodes); + + const QMap& GetItemMap() const + { + return items_; + } Node* GetTimeTarget() const; @@ -49,12 +54,6 @@ signals: void RequestSelectNode(const QList& target); - void OpenedNode(Node* n); - - void ClosedNode(Node* n); - - void FoundGizmos(Node* n); - protected: virtual void resizeEvent(QResizeEvent *event) override; @@ -71,9 +70,7 @@ private: KeyframeView* keyframe_view_; - QList nodes_; - - QList items_; + QMap items_; QScrollBar* vertical_scrollbar_; diff --git a/app/widget/nodetableview/nodetableview.cpp b/app/widget/nodetableview/nodetableview.cpp index 9d6590cbc..01fc8c315 100644 --- a/app/widget/nodetableview/nodetableview.cpp +++ b/app/widget/nodetableview/nodetableview.cpp @@ -29,8 +29,7 @@ OLIVE_NAMESPACE_ENTER NodeTableView::NodeTableView(QWidget* parent) : - QTreeWidget(parent), - last_set_node_(nullptr) + QTreeWidget(parent) { setColumnCount(3); setHeaderLabels({tr("Type"), @@ -41,14 +40,135 @@ NodeTableView::NodeTableView(QWidget* parent) : tr("A/W")}); } +void NodeTableView::SelectNodes(const QList &nodes) +{ + foreach (Node* n, nodes) { + QTreeWidgetItem* top_item = new QTreeWidgetItem(); + top_item->setText(0, n->Name()); + top_item->setFirstColumnSpanned(true); + this->addTopLevelItem(top_item); + top_level_item_map_.insert(n, top_item); + } + + SetTime(last_time_); +} + +void NodeTableView::DeselectNodes(const QList &nodes) +{ + foreach (Node* n, nodes) { + delete top_level_item_map_.take(n); + } +} + +void NodeTableView::SetTime(const rational &time) +{ + last_time_ = time; + + NodeTableTraverser traverser; + + QMap::const_iterator i; + for (i=top_level_item_map_.constBegin(); i!=top_level_item_map_.constEnd(); i++) { + Node* node = i.key(); + QTreeWidgetItem* item = i.value(); + + // Generate a value database for this node at this time + NodeValueDatabase db = traverser.GenerateDatabase(node, TimeRange(time, time)); + + // Delete any children of this item that aren't in this database + for (int j=0; jchildCount(); j++) { + if (!db.contains(item->child(j)->data(0, Qt::UserRole).toString())) { + delete item->takeChild(j); + j--; + } + } + + // Update all inputs + NodeValueDatabase::const_iterator l; + + for (l=db.begin(); l!=db.end(); l++) { + const NodeValueTable& table = l.value(); + + NodeInput* input = node->GetInputWithID(l.key()); + if (!input) { + // Filters out table entries that aren't inputs (like "global") + continue; + } + + QTreeWidgetItem* input_item = nullptr; + + for (int j=0; jchildCount(); j++) { + QTreeWidgetItem* compare = item->child(j); + + if (compare->data(0, Qt::UserRole).toString() == input->id()) { + input_item = compare; + break; + } + } + + if (!input_item) { + input_item = new QTreeWidgetItem(); + input_item->setText(0, input->name()); + input_item->setData(0, Qt::UserRole, input->id()); + input_item->setFirstColumnSpanned(true); + item->addChild(input_item); + } + + // Create children if necessary + while (input_item->childCount() < table.Count()) { + input_item->addChild(new QTreeWidgetItem()); + } + + // Remove children if necessary + while (input_item->childCount() > table.Count()) { + delete input_item->takeChild(input_item->childCount() - 1); + } + + for (int j=0;jchild(j); + + // Set data type name + sub_item->setText(0, NodeParam::GetPrettyDataTypeName(value.type())); + + // Determine source + QString source_name; + if (value.source()) { + source_name = value.source()->Name(); + } else { + source_name = tr("(unknown)"); + } + sub_item->setText(1, source_name); + + switch (value.type()) { + case NodeParam::kTexture: + { + // NodeTableTraverser puts video params in here + VideoParams p = value.data().value(); + int channel_count = PixelFormat::ChannelCount(p.format()); + + for (int k=0;ksetItemWidget(sub_item, 2 + k, new QCheckBox()); + } + break; + } + default: + { + QVector split_values = input->split_normal_value_into_track_values(value.data()); + for (int k=0;ksetText(2 + k, NodeInput::ValueToString(value.type(), split_values.at(k))); + } + } + } + } + } + } +} + +/* void NodeTableView::SetNode(Node *n, const rational &time) { - if (last_set_node_ != n) { - // Clear everything if the node has changed - clear(); - } - last_set_node_ = n; - NodeTableTraverser traverser; NodeValueDatabase db = traverser.GenerateDatabase(n, TimeRange(time, time)); @@ -141,15 +261,6 @@ void NodeTableView::SetNode(Node *n, const rational &time) } } } - -void NodeTableView::SetMultipleNodeMessage() -{ - this->clear(); - - QTreeWidgetItem* item = new QTreeWidgetItem(); - item->setText(0, tr("Multiple nodes selected")); - item->setFirstColumnSpanned(true); - this->addTopLevelItem(item); -} +*/ OLIVE_NAMESPACE_EXIT diff --git a/app/widget/nodetableview/nodetableview.h b/app/widget/nodetableview/nodetableview.h index 1ce906c09..7aa6e3eeb 100644 --- a/app/widget/nodetableview/nodetableview.h +++ b/app/widget/nodetableview/nodetableview.h @@ -32,12 +32,16 @@ class NodeTableView : public QTreeWidget public: NodeTableView(QWidget* parent = nullptr); - void SetNode(Node* n, const rational& time); + void SelectNodes(const QList& nodes); - void SetMultipleNodeMessage(); + void DeselectNodes(const QList& nodes); + + void SetTime(const rational& time); private: - Node* last_set_node_; + QMap top_level_item_map_; + + rational last_time_; }; diff --git a/app/widget/nodetableview/nodetablewidget.cpp b/app/widget/nodetableview/nodetablewidget.cpp index 0c50794ab..f06678d33 100644 --- a/app/widget/nodetableview/nodetablewidget.cpp +++ b/app/widget/nodetableview/nodetablewidget.cpp @@ -25,8 +25,7 @@ OLIVE_NAMESPACE_ENTER NodeTableWidget::NodeTableWidget(QWidget* parent) : - TimeBasedWidget(parent), - node_(nullptr) + TimeBasedWidget(parent) { QVBoxLayout* layout = new QVBoxLayout(this); layout->setSpacing(0); @@ -36,37 +35,4 @@ NodeTableWidget::NodeTableWidget(QWidget* parent) : layout->addWidget(view_); } -void NodeTableWidget::SetNodes(const QList &nodes) -{ - node_ = nullptr; - - if (nodes.isEmpty()) { - view_->clear(); - } else if (nodes.size() == 1) { - node_ = nodes.first(); - - ViewerOutput* viewer = node_->FindOutputNode(); - if (viewer) { - qDebug() << "Found timebase"; - SetTimebase(viewer->video_params().time_base()); - } - - UpdateView(); - } else { - view_->SetMultipleNodeMessage(); - } -} - -void NodeTableWidget::TimeChangedEvent(const int64_t &) -{ - UpdateView(); -} - -void NodeTableWidget::UpdateView() -{ - if (node_) { - view_->SetNode(node_, GetTime()); - } -} - OLIVE_NAMESPACE_EXIT diff --git a/app/widget/nodetableview/nodetablewidget.h b/app/widget/nodetableview/nodetablewidget.h index bc05d768d..1eeecbfcb 100644 --- a/app/widget/nodetableview/nodetablewidget.h +++ b/app/widget/nodetableview/nodetablewidget.h @@ -31,18 +31,30 @@ class NodeTableWidget : public TimeBasedWidget public: NodeTableWidget(QWidget* parent = nullptr); - void SetNodes(const QList& nodes); + void SelectNodes(const QList& nodes) + { + view_->SelectNodes(nodes); + } + + void DeselectNodes(const QList& nodes) + { + view_->DeselectNodes(nodes); + } protected: - virtual void TimeChangedEvent(const int64_t& ts) override; + virtual void TimeChangedEvent(const int64_t& ts) override + { + UpdateView(); + } private: - void UpdateView(); + void UpdateView() + { + view_->SetTime(GetTime()); + } NodeTableView* view_; - Node* node_; - }; OLIVE_NAMESPACE_EXIT diff --git a/app/widget/nodeview/nodeview.cpp b/app/widget/nodeview/nodeview.cpp index ebd5d8c26..cba47d257 100644 --- a/app/widget/nodeview/nodeview.cpp +++ b/app/widget/nodeview/nodeview.cpp @@ -144,7 +144,8 @@ void NodeView::SelectAll() scene_.SelectAll(); - ReconnectSelectionChangedSignal(); + ConnectSelectionChangedSignal(); + SceneSelectionChangedSlot(); } void NodeView::DeselectAll() @@ -155,7 +156,8 @@ void NodeView::DeselectAll() scene_.DeselectAll(); - ReconnectSelectionChangedSignal(); + ConnectSelectionChangedSignal(); + SceneSelectionChangedSlot(); } void NodeView::Select(const QList &nodes) @@ -176,7 +178,8 @@ void NodeView::Select(const QList &nodes) item->setSelected(true); } - ReconnectSelectionChangedSignal(); + ConnectSelectionChangedSignal(); + SceneSelectionChangedSlot(); } void NodeView::SelectWithDependencies(QList nodes) @@ -195,10 +198,13 @@ void NodeView::SelectWithDependencies(QList nodes) void NodeView::SelectBlocks(const QList &blocks) { - if (selected_blocks_ == blocks) { - return; - } + selected_blocks_.append(blocks); + SelectBlocksInternal(); +} + +void NodeView::DeselectBlocks(const QList &blocks) +{ // Remove temporary associations foreach (Block* b, selected_blocks_) { if (!blocks.contains(b)) { @@ -206,35 +212,12 @@ void NodeView::SelectBlocks(const QList &blocks) } } - selected_blocks_ = blocks; - - // Block scene signals while our selection is changing a lot - scene_.blockSignals(true); - - if (filter_mode_ == kFilterShowSelectedBlocks) { - UpdateBlockFilter(); - } - - QList nodes; - nodes.reserve(blocks.size()); - + // Remove blocks from selected array foreach (Block* b, blocks) { - nodes.append(b); - nodes.append(b->GetDependencies()); + selected_blocks_.removeOne(b); } - SelectWithDependencies(nodes); - - // Stop blocking signals and send a change signal now that all of our processing is done - scene_.blockSignals(false); - SceneSelectionChangedSlot(); - - if (!blocks.isEmpty()) { - NodeViewItem* item = scene_.NodeToUIObject(blocks.first()); - if (item) { - centerOn(item); - } - } + SelectBlocksInternal(); } void NodeView::CopySelected(bool cut) @@ -478,7 +461,44 @@ void NodeView::wheelEvent(QWheelEvent *event) void NodeView::SceneSelectionChangedSlot() { - emit SelectionChanged(scene_.GetSelectedNodes()); + QList current_selection = scene_.GetSelectedNodes(); + + QList selected; + QList deselected; + + // Determine which nodes are newly selected + if (selected_nodes_.isEmpty()) { + // All nodes in the current selection have just been selected + selected = current_selection; + } else { + foreach (Node* n, current_selection) { + if (!selected_nodes_.contains(n)) { + selected.append(n); + } + } + } + + // Determine which nodes are newly deselected + if (current_selection.isEmpty()) { + // All nodes that were selected have been deselected + deselected = selected_nodes_; + } else { + foreach (Node* n, selected_nodes_) { + if (!current_selection.contains(n)) { + deselected.append(n); + } + } + } + + selected_nodes_ = current_selection; + + if (!selected.isEmpty()) { + emit NodesSelected(selected); + } + + if (!deselected.isEmpty()) { + emit NodesDeselected(deselected); + } } void NodeView::ShowContextMenu(const QPoint &pos) @@ -803,12 +823,6 @@ void NodeView::ConnectSelectionChangedSignal() connect(&scene_, &QGraphicsScene::selectionChanged, this, &NodeView::SceneSelectionChangedSlot); } -void NodeView::ReconnectSelectionChangedSignal() -{ - ConnectSelectionChangedSignal(); - SceneSelectionChangedSlot(); -} - void NodeView::DisconnectSelectionChangedSignal() { disconnect(&scene_, &QGraphicsScene::selectionChanged, this, &NodeView::SceneSelectionChangedSlot); @@ -913,6 +927,37 @@ void NodeView::DisassociateNode(Node *n, bool remove_from_map) disconnect(n, &Node::destroyed, this, &NodeView::AssociatedNodeDestroyed); } +void NodeView::SelectBlocksInternal() +{ + // Block scene signals while our selection is changing a lot + scene_.blockSignals(true); + + if (filter_mode_ == kFilterShowSelectedBlocks) { + UpdateBlockFilter(); + } + + QList nodes; + nodes.reserve(selected_blocks_.size()); + + foreach (Block* b, selected_blocks_) { + nodes.append(b); + nodes.append(b->GetDependencies()); + } + + SelectWithDependencies(nodes); + + // Stop blocking signals and send a change signal now that all of our processing is done + scene_.blockSignals(false); + SceneSelectionChangedSlot(); + + if (!selected_blocks_.isEmpty()) { + NodeViewItem* item = scene_.NodeToUIObject(selected_blocks_.first()); + if (item) { + centerOn(item); + } + } +} + void NodeView::ValidateFilter() { // Force auto-positioning diff --git a/app/widget/nodeview/nodeview.h b/app/widget/nodeview/nodeview.h index bca1d669a..d2c7d7a95 100644 --- a/app/widget/nodeview/nodeview.h +++ b/app/widget/nodeview/nodeview.h @@ -61,18 +61,19 @@ public: void Select(const QList& nodes); void SelectWithDependencies(QList nodes); - void SelectBlocks(const QList& blocks); - void CopySelected(bool cut); void Paste(); void Duplicate(); + void SelectBlocks(const QList& blocks); + + void DeselectBlocks(const QList& blocks); + signals: - /** - * @brief Signal emitted when the selected nodes have changed - */ - void SelectionChanged(QList selected_nodes); + void NodesSelected(const QList& nodes); + + void NodesDeselected(const QList& nodes); protected: virtual void keyPressEvent(QKeyEvent *event) override; @@ -83,6 +84,8 @@ protected: virtual void wheelEvent(QWheelEvent* event) override; + //virtual void scrollContentsBy(int dx, int dy) override; + private: void PlaceNode(NodeViewItem* n, const QPointF& pos); @@ -97,7 +100,6 @@ private: void MoveAttachedNodesToCursor(const QPoint &p); void ConnectSelectionChangedSignal(); - void ReconnectSelectionChangedSignal(); void DisconnectSelectionChangedSignal(); void UpdateBlockFilter(); @@ -105,6 +107,8 @@ private: void AssociateNodeWithSelectedBlocks(Node* n); void DisassociateNode(Node* n, bool remove_from_map); + void SelectBlocksInternal(); + NodeGraph* graph_; struct AttachedItem { @@ -119,6 +123,8 @@ private: NodeViewScene scene_; + QList selected_nodes_; + QList selected_blocks_; QHash > association_map_; diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index 3a89da8fc..fae2a6910 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -129,7 +129,6 @@ TimelineWidget::TimelineWidget(QWidget *parent) : connect(view, &TimelineView::DragMoved, this, &TimelineWidget::ViewDragMoved); connect(view, &TimelineView::DragLeft, this, &TimelineWidget::ViewDragLeft); connect(view, &TimelineView::DragDropped, this, &TimelineWidget::ViewDragDropped); - ConnectViewSelectionSignal(view); connect(tview->splitter(), &QSplitter::splitterMoved, this, &TimelineWidget::UpdateHorizontalSplitters); @@ -163,21 +162,19 @@ TimelineWidget::~TimelineWidget() void TimelineWidget::Clear() { - foreach (TimelineAndTrackView* tview, views_) { - DisconnectViewSelectionSignal(tview->view()); - } + QList deselected_blocks; QMap::const_iterator iterator; for (iterator=block_items_.begin(); iterator!=block_items_.end(); iterator++) { + if (iterator.value()->isSelected()) { + deselected_blocks.append(iterator.key()); + } + delete iterator.value(); } block_items_.clear(); - foreach (TimelineAndTrackView* tview, views_) { - ConnectViewSelectionSignal(tview->view()); - } - - emit SelectionChanged(QList()); + emit BlocksDeselected(deselected_blocks); SetTimebase(0); } @@ -379,24 +376,34 @@ rational TimelineWidget::GetToolTipTimebase() const void TimelineWidget::SelectAll() { - foreach (TimelineAndTrackView* view, views_) { - DisconnectViewSelectionSignal(view->view()); - view->view()->SelectAll(); - ConnectViewSelectionSignal(view->view()); + QList blocks_selected; + + QMap::const_iterator i; + + for (i=block_items_.constBegin(); i!=block_items_.end(); i++) { + if (!i.value()->isSelected()) { + i.value()->setSelected(true); + blocks_selected.append(i.key()); + } } - ViewSelectionChanged(); + emit BlocksSelected(blocks_selected); } void TimelineWidget::DeselectAll() { - foreach (TimelineAndTrackView* view, views_) { - DisconnectViewSelectionSignal(view->view()); - view->view()->DeselectAll(); - ConnectViewSelectionSignal(view->view()); + QList blocks_deselected; + + QMap::const_iterator i; + + for (i=block_items_.constBegin(); i!=block_items_.end(); i++) { + if (i.value()->isSelected()) { + i.value()->setSelected(false); + blocks_deselected.append(i.key()); + } } - emit SelectionChanged(QList()); + emit BlocksDeselected(blocks_deselected); } void TimelineWidget::RippleToIn() @@ -803,16 +810,6 @@ TrackOutput *TimelineWidget::GetTrackFromReference(const TrackReference &ref) return GetConnectedNode()->track_list(ref.type())->GetTrackAt(ref.index()); } -void TimelineWidget::ConnectViewSelectionSignal(TimelineView *view) -{ - connect(view, &TimelineView::SelectionChanged, this, &TimelineWidget::ViewSelectionChanged); -} - -void TimelineWidget::DisconnectViewSelectionSignal(TimelineView *view) -{ - disconnect(view, &TimelineView::SelectionChanged, this, &TimelineWidget::ViewSelectionChanged); -} - int TimelineWidget::GetTrackY(const TrackReference &ref) { return views_.at(ref.type())->view()->GetTrackY(ref.index()); @@ -983,22 +980,6 @@ void TimelineWidget::TrackIndexChanged() } } -void TimelineWidget::ViewSelectionChanged() -{ - if (rubberband_.isVisible()) { - return; - } - - QList selected_items = GetSelectedBlocks(); - QList selected_blocks; - - foreach (TimelineViewBlockItem* item, selected_items) { - selected_blocks.append(item->block()); - } - - emit SelectionChanged(selected_blocks); -} - void TimelineWidget::BlockRefreshed() { TimelineViewRect* rect = block_items_.value(static_cast(sender())); @@ -1393,6 +1374,14 @@ void TimelineWidget::StartRubberBandSelect(bool enable_selecting, bool select_li drag_origin_ = QCursor::pos(); rubberband_.show(); + // We don't touch any blocks that are already selected. If you want these to be deselected by + // default, call DeselectAll() befoer calling StartRubberBandSelect() + foreach (TimelineViewBlockItem* block, block_items_) { + if (block->isSelected()) { + rubberband_already_selected_.append(block); + } + } + MoveRubberBandSelect(enable_selecting, select_links); } @@ -1408,10 +1397,11 @@ void TimelineWidget::MoveRubberBandSelect(bool enable_selecting, bool select_lin QList new_selected_list; + // Determine all items in the rubberband foreach (TimelineAndTrackView* tview, views_) { - // Map global mouse coordinates to viewport TimelineView* view = tview->view(); + // Map global mouse coordinates to viewport QRect mapped_rect(view->viewport()->mapFromGlobal(drag_origin_), view->viewport()->mapFromGlobal(rubberband_now)); @@ -1421,13 +1411,25 @@ void TimelineWidget::MoveRubberBandSelect(bool enable_selecting, bool select_lin new_selected_list.append(rubberband_items); } + // Filter out any items that were already selected + if (!rubberband_already_selected_.isEmpty()) { + for (int i=0; isetSelected(false); } - foreach (QGraphicsItem* item, new_selected_list) { - TimelineViewBlockItem* block_item = dynamic_cast(item); - if (!block_item || block_item->block()->type() == Block::kGap) { + // Cache limit because we append to this array in this loop and don't need to process those + int lim = new_selected_list.size(); + for (int i=0;i(new_selected_list.at(i)); + if (block_item->block()->type() == Block::kGap) { continue; } @@ -1436,18 +1438,22 @@ void TimelineWidget::MoveRubberBandSelect(bool enable_selecting, bool select_lin continue; } + // Since new_selected_list is filtered by rubberband_already_selected_, this should certainly + // be deselected by now block_item->setSelected(true); if (select_links) { // Select the block's links Block* b = block_item->block(); - SetBlockLinksSelected(b, true); // Add its links to the list TimelineViewBlockItem* link_item; foreach (Block* link, b->linked_clips()) { if ((link_item = block_items_[link]) != nullptr) { - if (!new_selected_list.contains(link_item)) { + link_item->setSelected(true); + + if (!new_selected_list.contains(link_item) + && !rubberband_already_selected_.contains(link_item)) { new_selected_list.append(link_item); } } @@ -1458,13 +1464,19 @@ void TimelineWidget::MoveRubberBandSelect(bool enable_selecting, bool select_lin rubberband_now_selected_ = new_selected_list; } -void TimelineWidget::EndRubberBandSelect(bool enable_selecting, bool select_links) +void TimelineWidget::EndRubberBandSelect() { - MoveRubberBandSelect(enable_selecting, select_links); rubberband_.hide(); - rubberband_now_selected_.clear(); - ViewSelectionChanged(); + // Emit any blocks that were newly selected + QList selected_blocks; + foreach (QGraphicsItem* item, rubberband_now_selected_) { + selected_blocks.append(static_cast(item)->block()); + } + emit BlocksSelected(selected_blocks); + + rubberband_now_selected_.clear(); + rubberband_already_selected_.clear(); } struct SnapData { diff --git a/app/widget/timelinewidget/timelinewidget.h b/app/widget/timelinewidget/timelinewidget.h index 7b957c67c..cab78ee0f 100644 --- a/app/widget/timelinewidget/timelinewidget.h +++ b/app/widget/timelinewidget/timelinewidget.h @@ -104,7 +104,9 @@ public: void RestoreSplitterState(const QByteArray& state); signals: - void SelectionChanged(const QList& selected_blocks); + void BlocksSelected(const QList& selected_blocks); + + void BlocksDeselected(const QList& deselected_blocks); protected: virtual void resizeEvent(QResizeEvent *event) override; @@ -474,8 +476,9 @@ private: void StartRubberBandSelect(bool enable_selecting, bool select_links); void MoveRubberBandSelect(bool enable_selecting, bool select_links); - void EndRubberBandSelect(bool enable_selecting, bool select_links); + void EndRubberBandSelect(); QRubberBand rubberband_; + QList rubberband_already_selected_; QList rubberband_now_selected_; Tool* GetActiveTool(); @@ -496,10 +499,6 @@ private: TrackOutput* GetTrackFromReference(const TrackReference& ref); - void ConnectViewSelectionSignal(TimelineView* view); - - void DisconnectViewSelectionSignal(TimelineView* view); - QList views_; TimeSlider* timecode_label_; @@ -539,8 +538,6 @@ private slots: void RemoveTrack(TrackOutput* track); void TrackIndexChanged(); - void ViewSelectionChanged(); - /** * @brief Slot for when a Block node changes its parameters and the graphics need to update * diff --git a/app/widget/timelinewidget/tool/pointer.cpp b/app/widget/timelinewidget/tool/pointer.cpp index 85e1884f5..9889a30a3 100644 --- a/app/widget/timelinewidget/tool/pointer.cpp +++ b/app/widget/timelinewidget/tool/pointer.cpp @@ -78,16 +78,23 @@ void TimelineWidget::PointerTool::MousePress(TimelineViewMouseEvent *event) // If this item is already selected, no further selection needs to be made if (clicked_item_->isSelected()) { + // Collect item deselections + QList deselected_blocks; + // If shift is held, deselect it if (event->GetModifiers() & Qt::ShiftModifier) { clicked_item_->setSelected(false); + deselected_blocks.append(clicked_item_->block()); // If not holding alt, deselect all links as well if (!(event->GetModifiers() & Qt::AltModifier)) { parent()->SetBlockLinksSelected(clicked_item_->block(), false); + deselected_blocks.append(clicked_item_->block()->linked_clips().toList()); } } + emit parent()->BlocksDeselected(deselected_blocks); + return; } } @@ -98,18 +105,29 @@ void TimelineWidget::PointerTool::MousePress(TimelineViewMouseEvent *event) } if (selectable_item) { + + // Collect item selections + QList selected_blocks; + // Select this item clicked_item_->setSelected(true); + selected_blocks.append(clicked_item_->block()); // If not holding alt, select all links as well if (!(event->GetModifiers() & Qt::AltModifier)) { parent()->SetBlockLinksSelected(clicked_item_->block(), true); + selected_blocks.append(clicked_item_->block()->linked_clips().toList()); } + + emit parent()->BlocksSelected(selected_blocks); + } else if (event->GetButton() == Qt::LeftButton) { + // Start rubberband drag parent()->StartRubberBandSelect(true, !(event->GetModifiers() & Qt::AltModifier)); rubberband_selecting_ = true; + } } @@ -150,7 +168,7 @@ void TimelineWidget::PointerTool::MouseRelease(TimelineViewMouseEvent *event) { if (rubberband_selecting_) { // Finish rubberband select - parent()->EndRubberBandSelect(true, !(event->GetModifiers() & Qt::AltModifier)); + parent()->EndRubberBandSelect(); rubberband_selecting_ = false; return; } diff --git a/app/widget/timelinewidget/tool/zoom.cpp b/app/widget/timelinewidget/tool/zoom.cpp index 123b821c8..a961a88e7 100644 --- a/app/widget/timelinewidget/tool/zoom.cpp +++ b/app/widget/timelinewidget/tool/zoom.cpp @@ -51,7 +51,7 @@ void TimelineWidget::ZoomTool::MouseRelease(TimelineViewMouseEvent *event) // Zoom into the rubberband selection QRect screen_coords = parent()->rubberband_.geometry(); - parent()->EndRubberBandSelect(false, false); + parent()->EndRubberBandSelect(); TimelineView* reference_view = parent()->views_.first()->view(); QPointF scene_topleft = reference_view->mapToScene(reference_view->mapFrom(parent(), screen_coords.topLeft())); diff --git a/app/widget/timelinewidget/view/timelineview.cpp b/app/widget/timelinewidget/view/timelineview.cpp index b55ec6cba..3ff4cd08a 100644 --- a/app/widget/timelinewidget/view/timelineview.cpp +++ b/app/widget/timelinewidget/view/timelineview.cpp @@ -47,26 +47,6 @@ TimelineView::TimelineView(Qt::Alignment vertical_alignment, QWidget *parent) : setBackgroundRole(QPalette::Window); setContextMenuPolicy(Qt::CustomContextMenu); viewport()->setMouseTracking(true); - - connect(scene(), &QGraphicsScene::selectionChanged, this, &TimelineView::SelectionChanged); -} - -void TimelineView::SelectAll() -{ - QList all_items = items(); - - foreach (QGraphicsItem* i, all_items) { - i->setSelected(true); - } -} - -void TimelineView::DeselectAll() -{ - QList all_items = items(); - - foreach (QGraphicsItem* i, all_items) { - i->setSelected(false); - } } void TimelineView::mousePressEvent(QMouseEvent *event) diff --git a/app/widget/timelinewidget/view/timelineview.h b/app/widget/timelinewidget/view/timelineview.h index 4bacad401..e409b425d 100644 --- a/app/widget/timelinewidget/view/timelineview.h +++ b/app/widget/timelinewidget/view/timelineview.h @@ -49,10 +49,6 @@ public: TimelineView(Qt::Alignment vertical_alignment = Qt::AlignTop, QWidget* parent = nullptr); - void SelectAll(); - - void DeselectAll(); - int GetTrackY(int track_index) const; int GetTrackHeight(int track_index) const; @@ -74,8 +70,6 @@ signals: void DragLeft(QDragLeaveEvent* event); void DragDropped(TimelineViewMouseEvent* event); - void SelectionChanged(); - protected: virtual void mousePressEvent(QMouseEvent *event) override; virtual void mouseMoveEvent(QMouseEvent *event) override; diff --git a/app/window/mainwindow/mainwindow.cpp b/app/window/mainwindow/mainwindow.cpp index 4825c9b61..4c010e963 100644 --- a/app/window/mainwindow/mainwindow.cpp +++ b/app/window/mainwindow/mainwindow.cpp @@ -82,8 +82,10 @@ MainWindow::MainWindow(QWidget *parent) : audio_monitor_panel_ = PanelManager::instance()->CreatePanel(this); // Make connections to sequence viewer - connect(node_panel_, &NodePanel::SelectionChanged, param_panel_, &ParamPanel::SetNodes); - connect(node_panel_, &NodePanel::SelectionChanged, table_panel_, &NodeTablePanel::SetNodes); + connect(node_panel_, &NodePanel::NodesSelected, param_panel_, &ParamPanel::SelectNodes); + connect(node_panel_, &NodePanel::NodesDeselected, param_panel_, &ParamPanel::DeselectNodes); + connect(node_panel_, &NodePanel::NodesSelected, table_panel_, &NodeTablePanel::SelectNodes); + connect(node_panel_, &NodePanel::NodesDeselected, table_panel_, &NodeTablePanel::DeselectNodes); connect(param_panel_, &ParamPanel::RequestSelectNode, node_panel_, &NodePanel::Select); connect(sequence_viewer_panel_, &SequenceViewerPanel::TimeChanged, param_panel_, &ParamPanel::SetTimestamp); connect(sequence_viewer_panel_, &SequenceViewerPanel::TimeChanged, table_panel_, &NodeTablePanel::SetTimestamp); @@ -480,7 +482,8 @@ TimelinePanel* MainWindow::AppendTimelinePanel() connect(panel, &TimelinePanel::TimeChanged, param_panel_, &ParamPanel::SetTimestamp); connect(panel, &TimelinePanel::TimeChanged, table_panel_, &NodeTablePanel::SetTimestamp); connect(panel, &TimelinePanel::TimeChanged, sequence_viewer_panel_, &SequenceViewerPanel::SetTimestamp); - connect(panel, &TimelinePanel::SelectionChanged, node_panel_, &NodePanel::SelectBlocks); + connect(panel, &TimelinePanel::BlocksSelected, node_panel_, &NodePanel::SelectBlocks); + connect(panel, &TimelinePanel::BlocksDeselected, node_panel_, &NodePanel::DeselectBlocks); connect(param_panel_, &ParamPanel::TimeChanged, panel, &TimelinePanel::SetTimestamp); connect(sequence_viewer_panel_, &SequenceViewerPanel::TimeChanged, panel, &TimelinePanel::SetTimestamp);