From f18461e69b76be1ab8661f13d76caaaeefe5d666 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Mon, 15 Aug 2022 11:06:34 -0700 Subject: [PATCH] timeline: allow pasting nodes into or over clips --- app/widget/nodeparamview/nodeparamview.cpp | 30 ++++++++++++------ app/widget/nodeparamview/nodeparamview.h | 4 +++ app/widget/timelinewidget/timelinewidget.cpp | 33 ++++++++++++++++++-- app/widget/timelinewidget/timelinewidget.h | 3 ++ 4 files changed, 58 insertions(+), 12 deletions(-) diff --git a/app/widget/nodeparamview/nodeparamview.cpp b/app/widget/nodeparamview/nodeparamview.cpp index fee75f8ba..3177da6a5 100644 --- a/app/widget/nodeparamview/nodeparamview.cpp +++ b/app/widget/nodeparamview/nodeparamview.cpp @@ -29,7 +29,6 @@ #include "common/functiontimer.h" #include "common/timecodefunctions.h" #include "node/output/viewer/viewer.h" -#include "node/project/serializer/serializer.h" #include "widget/nodeparamview/nodeparamviewundo.h" #include "widget/nodeview/nodeviewundo.h" #include "widget/timeruler/timeruler.h" @@ -611,26 +610,24 @@ bool NodeParamView::Paste() } } + return Paste(this, std::bind(&NodeParamView::GenerateExistingPasteMap, this, std::placeholders::_1)); +} + +bool NodeParamView::Paste(QWidget *parent, std::function(const ProjectSerializer::Result &)> get_existing_map_function) +{ ProjectSerializer::Result res = ProjectSerializer::Paste(QStringLiteral("nodes")); if (res.GetLoadedNodes().isEmpty()) { return false; } // Determine if any nodes of this type are already in the editor - QVector ignore_nodes; - QMap existing_nodes; - for (Node *n : res.GetLoadedNodes()) { - if (Node *existing = GetNodeWithIDAndIgnoreList(n->id(), ignore_nodes)) { - existing_nodes.insert(existing, n); - ignore_nodes.append(existing); - } - } + QHash existing_nodes = get_existing_map_function(res); QVector nodes_to_paste_as_new = res.GetLoadedNodes(); MultiUndoCommand *command = new MultiUndoCommand(); if (!existing_nodes.empty()) { - QMessageBox b(this); + QMessageBox b(parent); b.setWindowTitle(tr("Paste Nodes")); QStringList node_names; @@ -870,6 +867,19 @@ void NodeParamView::ToggleSelect(NodeParamViewItem *item) } } +QHash NodeParamView::GenerateExistingPasteMap(const ProjectSerializer::Result &r) +{ + QVector ignore_nodes; + QHash existing_nodes; + for (Node *n : r.GetLoadedNodes()) { + if (Node *existing = GetNodeWithIDAndIgnoreList(n->id(), ignore_nodes)) { + existing_nodes.insert(existing, n); + ignore_nodes.append(existing); + } + } + return existing_nodes; +} + void NodeParamView::UpdateGlobalScrollBar() { if (keyframe_view_) { diff --git a/app/widget/nodeparamview/nodeparamview.h b/app/widget/nodeparamview/nodeparamview.h index 479685a21..1b78055a9 100644 --- a/app/widget/nodeparamview/nodeparamview.h +++ b/app/widget/nodeparamview/nodeparamview.h @@ -25,6 +25,7 @@ #include #include "node/node.h" +#include "node/project/serializer/serializer.h" #include "nodeparamviewcontext.h" #include "nodeparamviewdockarea.h" #include "nodeparamviewitem.h" @@ -75,6 +76,7 @@ public: virtual bool CopySelected(bool cut) override; virtual bool Paste() override; + static bool Paste(QWidget *parent, std::function(const ProjectSerializer::Result &)> get_existing_map_function); public slots: void SetContexts(const QVector &contexts); @@ -134,6 +136,8 @@ private: void ToggleSelect(NodeParamViewItem *item); + QHash GenerateExistingPasteMap(const ProjectSerializer::Result &r); + KeyframeView* keyframe_view_; QVector context_items_; diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index 49dda39bb..18dc32441 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -54,6 +54,7 @@ #include "undo/timelineundoworkarea.h" #include "widget/menu/menu.h" #include "widget/menu/menushared.h" +#include "widget/nodeparamview/nodeparamview.h" #include "widget/nodeview/nodeviewundo.h" #include "widget/timeruler/timeruler.h" @@ -640,13 +641,23 @@ bool TimelineWidget::CopySelected(bool cut) bool TimelineWidget::Paste() { + // TimeRuler gets first chance (markers, etc.) if (super::Paste()) { return true; - } if (!GetConnectedNode()) { + } + + // Ensure we have a connected node + if (!GetConnectedNode()) { return false; } - return PasteInternal(false); + // Attempt regular clip pasting + if (PasteInternal(false)) { + return true; + } + + // Give last chance to NodeParamView + return NodeParamView::Paste(this, std::bind(&TimelineWidget::GenerateExistingPasteMap, this, std::placeholders::_1)); } void TimelineWidget::PasteInsert() @@ -1707,6 +1718,24 @@ TimelineAndTrackView *TimelineWidget::AddTimelineAndTrackView(Qt::Alignment alig return v; } +QHash TimelineWidget::GenerateExistingPasteMap(const ProjectSerializer::Result &r) +{ + QHash m; + + for (Node *n : r.GetLoadedNodes()) { + for (Block *b : qAsConst(this->selected_blocks_)) { + for (auto it=b->GetContextPositions().cbegin(); it!=b->GetContextPositions().cend(); it++) { + if (it.key()->id() == n->id() && !m.contains(it.key())) { + m.insert(it.key(), n); + break; + } + } + } + } + + return m; +} + QByteArray TimelineWidget::SaveSplitterState() const { return view_splitter_->saveState(); diff --git a/app/widget/timelinewidget/timelinewidget.h b/app/widget/timelinewidget/timelinewidget.h index 5123e6006..494f19fba 100644 --- a/app/widget/timelinewidget/timelinewidget.h +++ b/app/widget/timelinewidget/timelinewidget.h @@ -28,6 +28,7 @@ #include "core.h" #include "node/block/transition/transition.h" #include "node/output/viewer/viewer.h" +#include "node/project/serializer/serializer.h" #include "timeline/timelinecommon.h" #include "timelineandtrackview.h" #include "widget/slider/rationalslider.h" @@ -311,6 +312,8 @@ private: TimelineAndTrackView *AddTimelineAndTrackView(Qt::Alignment alignment); + QHash GenerateExistingPasteMap(const ProjectSerializer::Result &r); + QPoint drag_origin_; QRubberBand rubberband_;