keyframes: implemented copying/pasting

This commit is contained in:
itsmattkc
2022-05-10 15:42:09 -07:00
parent af9e54504c
commit 182e1599af
24 changed files with 567 additions and 160 deletions
+5
View File
@@ -41,6 +41,11 @@ public:
void SetKeyframeTrackColor(const NodeKeyframeTrackReference& ref, const QColor& color);
const QHash<NodeKeyframeTrackReference, KeyframeViewInputConnection*> &GetConnections() const
{
return track_connections_;
}
public slots:
void ZoomToFit();
+30
View File
@@ -129,6 +129,36 @@ void CurveWidget::DeleteSelected()
view_->DeleteSelected();
}
Node *CurveWidget::GetSelectedNodeWithID(const QString &id)
{
for (auto it=view_->GetConnections().cbegin(); it!=view_->GetConnections().cend(); it++) {
Node *n = it.key().input().node();
if (n->id() == id) {
return n;
}
}
return nullptr;
}
bool CurveWidget::CopySelected(bool cut)
{
if (super::CopySelected(cut)) {
return true;
}
return view_->CopySelected(cut);
}
bool CurveWidget::Paste()
{
if (super::Paste()) {
return true;
}
return view_->Paste(std::bind(&CurveWidget::GetSelectedNodeWithID, this, std::placeholders::_1));
}
void CurveWidget::SetNodes(const QVector<Node *> &nodes)
{
tree_view_->SetNodes(nodes);
+6
View File
@@ -55,6 +55,12 @@ public:
view_->DeselectAll();
}
Node *GetSelectedNodeWithID(const QString &id);
virtual bool CopySelected(bool cut) override;
virtual bool Paste() override;
public slots:
void SetNodes(const QVector<Node *> &nodes);
+63
View File
@@ -28,6 +28,7 @@
#include "dialog/keyframeproperties/keyframeproperties.h"
#include "keyframeviewundo.h"
#include "node/node.h"
#include "node/project/serializer/serializer.h"
#include "widget/menu/menu.h"
#include "widget/menu/menushared.h"
#include "widget/nodeparamview/nodeparamviewundo.h"
@@ -181,6 +182,68 @@ void KeyframeView::SelectionManagerDeselectEvent(void *obj)
emit SelectionChanged();
}
bool KeyframeView::CopySelected(bool cut)
{
if (!selection_manager_.GetSelectedObjects().empty()) {
ProjectSerializer::SaveData sdata;
sdata.SetOnlySerializeKeyframes(selection_manager_.GetSelectedObjects());
ProjectSerializer::Copy(sdata, QStringLiteral("keyframes"));
if (cut) {
DeleteSelected();
}
return true;
}
return false;
}
bool KeyframeView::Paste(std::function<Node *(const QString &)> find_node_function)
{
ProjectSerializer::Result res = ProjectSerializer::Paste(QStringLiteral("keyframes"));
if (res == ProjectSerializer::kSuccess) {
const ProjectSerializer::SerializedKeyframes &keys = res.GetLoadData().keyframes;
MultiUndoCommand *command = new MultiUndoCommand();
rational min = RATIONAL_MAX;
for (auto it=keys.cbegin(); it!=keys.cend(); it++) {
for (NodeKeyframe *key : it.value()) {
min = std::min(min, key->time());
}
}
min -= GetTime();
for (auto it=keys.cbegin(); it!=keys.cend(); it++) {
const QString &paste_id = it.key();
// Find a node with this ID
Node *node_with_id = find_node_function(paste_id);
if (node_with_id) {
for (NodeKeyframe *key : it.value()) {
key->set_time(key->time() - min);
if (NodeKeyframe *existing = node_with_id->GetKeyframeAtTimeOnTrack(key->input(), key->time(), key->track(), key->element())) {
command->add_child(new NodeParamRemoveKeyframeCommand(existing));
}
command->add_child(new NodeParamInsertKeyframeCommand(node_with_id, key));
}
} else {
qDeleteAll(it.value());
}
}
Core::instance()->undo_stack()->pushIfHasChildren(command);
return true;
}
return false;
}
void KeyframeView::mousePressEvent(QMouseEvent *event)
{
NodeKeyframe *key_under_cursor = selection_manager_.GetObjectAtPoint(event->pos());
+6
View File
@@ -21,6 +21,8 @@
#ifndef KEYFRAMEVIEWBASE_H
#define KEYFRAMEVIEWBASE_H
#include <functional>
#include "keyframeviewinputconnection.h"
#include "node/keyframe.h"
#include "widget/menu/menu.h"
@@ -77,6 +79,10 @@ public:
UpdateSceneRect();
}
bool CopySelected(bool cut);
bool Paste(std::function<Node *(const QString &)> find_node_function);
signals:
void Dragged(int current_x, int current_y);
@@ -60,6 +60,11 @@ public:
return brush_;
}
const NodeKeyframeTrackReference &GetReference() const
{
return input_;
}
void SetBrush(const QBrush &brush);
signals:
+83 -3
View File
@@ -28,6 +28,8 @@
#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"
@@ -519,6 +521,84 @@ void NodeParamView::SetSelectedNodes(const QVector<Node::ContextPair> &nodes, bo
}
}
Node *NodeParamView::GetNodeWithID(const QString &id)
{
for (NodeParamViewItem *item : selected_nodes_) {
if (item->GetNode()->id() == id) {
return item->GetNode();
}
}
for (NodeParamViewContext *ctx : context_items_) {
for (NodeParamViewItem *item : ctx->GetItems()) {
if (item->GetNode()->id() == id) {
return item->GetNode();
}
}
}
return nullptr;
}
bool NodeParamView::CopySelected(bool cut)
{
if (super::CopySelected(cut)) {
return true;
}
if (keyframe_view_ && keyframe_view_->hasFocus()) {
if (keyframe_view_->CopySelected(cut)) {
return true;
}
}
if (contexts_.empty()) {
return false;
}
ProjectSerializer::SaveData sdata(contexts_.first()->project());
ProjectSerializer::SerializedProperties properties;
QVector<Node*> nodes;
for (NodeParamViewItem *item : selected_nodes_) {
Node *n = item->GetNode();
if (!nodes.contains(n)) {
nodes.append(n);
Node::Position pos = item->GetContext()->GetNodePositionDataInContext(n);
properties[n][QStringLiteral("x")] = QString::number(pos.position.x());
properties[n][QStringLiteral("y")] = QString::number(pos.position.y());
properties[n][QStringLiteral("expanded")] = QString::number(pos.expanded);
}
}
sdata.SetOnlySerializeNodesAndResolveGroups(nodes);
sdata.SetProperties(properties);
ProjectSerializer::Copy(sdata, QStringLiteral("nodes"));
if (cut) {
DeleteSelected();
}
return false;
}
bool NodeParamView::Paste()
{
if (keyframe_view_) {
if (keyframe_view_->Paste(std::bind(&NodeParamView::GetNodeWithID, this, std::placeholders::_1))) {
return true;
}
}
// FIXME: Pasting nodes
return false;
}
void NodeParamView::UpdateItemTime(const rational &time)
{
foreach (NodeParamViewContext* item, context_items_) {
@@ -764,14 +844,14 @@ void NodeParamView::KeyframeViewDragged(int x, int y)
void NodeParamView::UpdateElementY()
{
foreach (NodeParamViewContext *ctx, context_items_) {
for (NodeParamViewContext *ctx : context_items_) {
for (auto it=ctx->GetItems().cbegin(); it!=ctx->GetItems().cend(); it++) {
NodeParamViewItem *item = *it;
Node *node = item->GetNode();
const KeyframeView::NodeConnections &connections = item->GetKeyframeConnections();
if (!connections.isEmpty()) {
foreach (const QString& input, node->inputs()) {
for (const QString& input : node->inputs()) {
if (!(node->GetInputFlags(input) & kInputFlagHidden)) {
int arr_sz = NodeGroup::ResolveInput(NodeInput(node, input)).GetArraySize();
@@ -787,7 +867,7 @@ void NodeParamView::UpdateElementY()
int use_index = i + 1;
if (use_index < input_con.size()) {
const KeyframeView::ElementConnections &ele_con = input_con.at(ic.element()+1);
foreach (KeyframeViewInputConnection *track, ele_con) {
for (KeyframeViewInputConnection *track : ele_con) {
track->SetKeyframeY(y);
}
}
+6
View File
@@ -64,11 +64,17 @@ public:
void SetSelectedNodes(const QVector<NodeParamViewItem *> &nodes, bool handle_focused_node = true, bool emit_signal = true);
void SetSelectedNodes(const QVector<Node::ContextPair> &nodes, bool emit_signal = true);
Node *GetNodeWithID(const QString &id);
const QVector<Node*> &GetContexts() const
{
return contexts_;
}
virtual bool CopySelected(bool cut) override;
virtual bool Paste() override;
public slots:
void SetContexts(const QVector<Node*> &contexts);
@@ -41,6 +41,8 @@ public:
update();
}
bool IsHighlighted() const { return highlighted_; }
bool IsExpanded() const;
static QString GetTitleBarTextFromNode(Node *n);
+18
View File
@@ -860,4 +860,22 @@ void TimeBasedWidget::HideSnaps()
}
}
bool TimeBasedWidget::CopySelected(bool cut)
{
if (ruler()->hasFocus() && ruler()->CopySelected(cut)) {
return true;
}
return false;
}
bool TimeBasedWidget::Paste()
{
if (ruler()->hasFocus() && ruler()->PasteMarkers()) {
return true;
}
return false;
}
}
+4
View File
@@ -73,6 +73,10 @@ public:
void ShowSnaps(const std::vector<rational> &times);
void HideSnaps();
virtual bool CopySelected(bool cut);
virtual bool Paste();
public slots:
void SetTime(const rational &time);
+67 -55
View File
@@ -568,18 +568,14 @@ void TimelineWidget::ToggleLinksOnSelected()
Core::instance()->undo_stack()->push(new NodeLinkManyCommand(blocks, link));
}
void TimelineWidget::CopySelected(bool cut)
bool TimelineWidget::CopySelected(bool cut)
{
if (!GetConnectedNode()) {
return;
if (super::CopySelected(cut)) {
return true;
}
if (ruler()->hasFocus() && ruler()->CopySelected(cut)) {
return;
}
if (selected_blocks_.isEmpty()) {
return;
if (!GetConnectedNode() || selected_blocks_.isEmpty()) {
return false;
}
QVector<Node*> selected_nodes;
@@ -619,58 +615,24 @@ void TimelineWidget::CopySelected(bool cut)
if (cut) {
DeleteSelected();
}
return true;
}
void TimelineWidget::Paste(bool insert)
bool TimelineWidget::Paste()
{
if (!GetConnectedNode()) {
return;
if (super::Paste()) {
return true;
} if (!GetConnectedNode()) {
return false;
}
if (ruler()->hasFocus() && ruler()->PasteMarkers(insert, GetTime())) {
return;
}
return PasteInternal(false);
}
ProjectSerializer::Result res = ProjectSerializer::Paste(QStringLiteral("timeline"));
if (res.GetLoadedNodes().isEmpty()) {
return;
}
MultiUndoCommand *command = new MultiUndoCommand();
foreach (Node *n, res.GetLoadedNodes()) {
command->add_child(new NodeAddCommand(GetConnectedNode()->project(), n));
}
rational paste_start = GetTime();
if (insert) {
rational paste_end = GetTime();
for (auto it=res.GetLoadData().properties.cbegin(); it!=res.GetLoadData().properties.cend(); it++) {
rational length = static_cast<Block*>(it.key())->length();
rational in = rational::fromString(it.value()[QStringLiteral("in")]);
paste_end = qMax(paste_end, paste_start + in + length);
}
if (paste_end != paste_start) {
InsertGapsAt(paste_start, paste_end - paste_start, command);
}
}
for (auto it=res.GetLoadData().properties.cbegin(); it!=res.GetLoadData().properties.cend(); it++) {
Block *block = static_cast<Block*>(it.key());
rational in = rational::fromString(it.value()[QStringLiteral("in")]);
Track::Reference track = Track::Reference::FromString(it.value()[QStringLiteral("track")]);
command->add_child(new TrackPlaceBlockCommand(sequence()->track_list(track.type()),
track.index(),
block,
paste_start + in));
}
Core::instance()->undo_stack()->pushIfHasChildren(command);
void TimelineWidget::PasteInsert()
{
PasteInternal(true);
}
void TimelineWidget::DeleteInToOut(bool ripple)
@@ -1629,6 +1591,56 @@ QVector<Block *> TimelineWidget::GetBlocksInGlobalRect(const QPoint &p1, const Q
return blocks_in_rect;
}
bool TimelineWidget::PasteInternal(bool insert)
{
if (!GetConnectedNode()) {
return false;
}
ProjectSerializer::Result res = ProjectSerializer::Paste(QStringLiteral("timeline"));
if (res.GetLoadedNodes().isEmpty()) {
return false;
}
MultiUndoCommand *command = new MultiUndoCommand();
foreach (Node *n, res.GetLoadedNodes()) {
command->add_child(new NodeAddCommand(GetConnectedNode()->project(), n));
}
rational paste_start = GetTime();
if (insert) {
rational paste_end = GetTime();
for (auto it=res.GetLoadData().properties.cbegin(); it!=res.GetLoadData().properties.cend(); it++) {
rational length = static_cast<Block*>(it.key())->length();
rational in = rational::fromString(it.value()[QStringLiteral("in")]);
paste_end = qMax(paste_end, paste_start + in + length);
}
if (paste_end != paste_start) {
InsertGapsAt(paste_start, paste_end - paste_start, command);
}
}
for (auto it=res.GetLoadData().properties.cbegin(); it!=res.GetLoadData().properties.cend(); it++) {
Block *block = static_cast<Block*>(it.key());
rational in = rational::fromString(it.value()[QStringLiteral("in")]);
Track::Reference track = Track::Reference::FromString(it.value()[QStringLiteral("track")]);
command->add_child(new TrackPlaceBlockCommand(sequence()->track_list(track.type()),
track.index(),
block,
paste_start + in));
}
Core::instance()->undo_stack()->pushIfHasChildren(command);
return true;
}
QByteArray TimelineWidget::SaveSplitterState() const
{
return view_splitter_->saveState();
+6 -2
View File
@@ -79,9 +79,11 @@ public:
void ToggleLinksOnSelected();
void CopySelected(bool cut);
virtual bool CopySelected(bool cut) override;
void Paste(bool insert);
virtual bool Paste() override;
void PasteInsert();
void DeleteInToOut(bool ripple);
@@ -294,6 +296,8 @@ private:
QVector<Block*> GetBlocksInGlobalRect(const QPoint &p1, const QPoint &p2);
bool PasteInternal(bool insert);
QPoint drag_origin_;
QRubberBand rubberband_;
+7 -18
View File
@@ -102,7 +102,7 @@ void SeekableWidget::DeleteSelected()
bool SeekableWidget::CopySelected(bool cut)
{
if (!selection_manager_.GetSelectedObjects().empty()) {
ProjectSerializer::SaveData sdata(Project::GetProjectFromObject(timeline_points_));
ProjectSerializer::SaveData sdata;
sdata.SetOnlySerializeMarkers(selection_manager_.GetSelectedObjects());
ProjectSerializer::Copy(sdata, QStringLiteral("markers"));
@@ -117,7 +117,7 @@ bool SeekableWidget::CopySelected(bool cut)
}
}
bool SeekableWidget::PasteMarkers(bool insert, rational insert_time)
bool SeekableWidget::PasteMarkers()
{
ProjectSerializer::Result res = ProjectSerializer::Paste(QStringLiteral("markers"));
if (res == ProjectSerializer::kSuccess) {
@@ -128,30 +128,19 @@ bool SeekableWidget::PasteMarkers(bool insert, rational insert_time)
// Normalize markers to start at playhead
rational min = RATIONAL_MAX;
for (auto it=markers.cbegin(); it!=markers.cend(); it++) {
min = qMin(min, (*it)->time());
min = std::min(min, (*it)->time());
}
min -= GetTime();
// Avoid duplicates
bool loop;
do {
loop = false;
for (auto it=markers.cbegin(); it!=markers.cend(); it++) {
rational proposed_time = (*it)->time() - min;
if (timeline_points_->markers()->GetMarkerAtTime(proposed_time)) {
min -= timebase();
loop = true;
break;
}
}
} while (loop);
for (auto it=markers.cbegin(); it!=markers.cend(); it++) {
TimelineMarker *m = *it;
m->set_time(m->time() - min);
if (TimelineMarker *existing = timeline_points_->markers()->GetMarkerAtTime(m->time())) {
command->add_child(new MarkerRemoveCommand(existing));
}
command->add_child(new MarkerAddCommand(timeline_points_->markers(), m));
}
+1 -1
View File
@@ -54,7 +54,7 @@ public:
bool CopySelected(bool cut);
bool PasteMarkers(bool insert, rational insert_time);
bool PasteMarkers();
void DeselectAllMarkers();