improved a number of pointer tool functions and fixed a number of bugs

This commit is contained in:
itsmattkc
2020-01-02 01:30:43 +11:00
parent f65e1e52b5
commit 62e94ca2cb
8 changed files with 216 additions and 56 deletions
+2
View File
@@ -143,6 +143,8 @@ void TrackList::RemoveTrack()
GetParentGraph()->TakeNode(track);
delete track;
track_input_->RemoveLast();
}
void TrackList::TrackConnected(NodeEdgePtr edge)
+21
View File
@@ -134,3 +134,24 @@ NodeRemoveWithExclusiveDeps::NodeRemoveWithExclusiveDeps(NodeGraph *graph, Node
remove_command_ = new NodeRemoveCommand(graph, node_and_its_deps, this);
}
NodeCopyInputsCommand::NodeCopyInputsCommand(Node *src, Node *dest, bool include_connections, QUndoCommand *parent) :
QUndoCommand(parent),
src_(src),
dest_(dest),
include_connections_(include_connections)
{
}
NodeCopyInputsCommand::NodeCopyInputsCommand(Node *src, Node *dest, QUndoCommand *parent) :
QUndoCommand(parent),
src_(src),
dest_(dest),
include_connections_(true)
{
}
void NodeCopyInputsCommand::redo()
{
Node::CopyInputs(src_, dest_, include_connections_);
}
+22
View File
@@ -88,4 +88,26 @@ private:
NodeRemoveCommand* remove_command_;
};
class NodeCopyInputsCommand : public QUndoCommand {
public:
NodeCopyInputsCommand(Node* src,
Node* dest,
bool include_connections = true,
QUndoCommand* parent = nullptr);
NodeCopyInputsCommand(Node* src,
Node* dest,
QUndoCommand* parent = nullptr);
virtual void redo() override;
private:
Node* src_;
Node* dest_;
bool include_connections_;
};
#endif // NODEVIEWUNDO_H
+10 -50
View File
@@ -389,60 +389,20 @@ void TimelineWidget::SplitAtPlayhead()
void TimelineWidget::DeleteSelectedInternal(const QList<Block *> blocks, bool remove_from_graph, QUndoCommand *command)
{
foreach (Block* b, blocks) {
// All this function does is replace blocks with gaps, nothing to do if the block is already a gap
if (b->type() == Block::kGap) {
continue;
}
bool previous_is_gap = (b->previous() && b->previous()->type() == Block::kGap);
bool next_is_gap = (b->next() && b->next()->type() == Block::kGap);
TrackOutput* original_track = TrackOutput::TrackFromBlock(b);
if (!b->next()) {
// If the block has no next, presumably it's the last block and doesn't actually need a gap
new TrackRippleRemoveBlockCommand(original_track,
b,
command);
} else if (!previous_is_gap && !next_is_gap) {
// Make new gap and replace old Block with it for now
GapBlock* gap = new GapBlock();
gap->set_length_and_media_out(b->length());
// Make new gap and replace old Block with it for now
GapBlock* gap = new GapBlock();
gap->set_length_and_media_out(b->length());
new NodeAddCommand(static_cast<NodeGraph*>(b->parent()),
gap,
command);
new NodeAddCommand(static_cast<NodeGraph*>(b->parent()),
gap,
command);
new TrackReplaceBlockCommand(original_track,
b,
gap,
command);
} else {
// Remove the block from the track
new TrackRippleRemoveBlockCommand(original_track,
b,
command);
if (previous_is_gap && next_is_gap) {
// Clip is surrounded by gaps, merge both together
// Remove one of the gaps
new TrackRippleRemoveBlockCommand(original_track,
b->next(),
command);
// Resize the other to match both
new BlockResizeCommand(b->previous(),
b->previous()->length() + b->length() + b->next()->length(),
command);
} else {
// Resize the surrounding block to take its place
Block* gap_to_resize = previous_is_gap ? b->previous() : b->next();
new BlockResizeCommand(gap_to_resize,
gap_to_resize->length() + b->length(),
command);
}
}
new TrackReplaceBlockCommand(original_track,
b,
gap,
command);
if (remove_from_graph) {
new NodeRemoveWithExclusiveDeps(static_cast<NodeGraph*>(b->parent()), b, command);
+42 -3
View File
@@ -162,6 +162,9 @@ void TimelineWidget::PointerTool::MouseReleaseInternal(TimelineViewMouseEvent *e
QUndoCommand* command = new QUndoCommand();
QList<Block*> blocks_to_temp_remove;
QList<TrackReference> tracks_affected;
bool duplicate_clips = (event->GetModifiers() & Qt::AltModifier);
// Since all the ghosts will be leaving their old position in some way, we replace all of them with gaps here so the
// entire timeline isn't disrupted in the process
@@ -175,9 +178,17 @@ void TimelineWidget::PointerTool::MouseReleaseInternal(TimelineViewMouseEvent *e
Block* b = Node::ValueToPtr<Block>(ghost->data(TimelineViewGhostItem::kAttachedBlock));
blocks_to_temp_remove.append(b);
// If we're duplicating (user is holding ALT), no need to remove the original clip. However if the ghost was
// trimmed, it can't be duplicated.
if (!duplicate_clips || ghost->mode() != Timeline::kMove) {
blocks_to_temp_remove.append(b);
}
tracks_affected.append(ghost->Track());
tracks_affected.append(ghost->GetAdjustedTrack());
}
// If there are any blocks to remove, remove them
parent()->DeleteSelectedInternal(blocks_to_temp_remove, false, command);
// Now we place the clips back in the timeline where the user moved them. It's legal for them to overwrite parts or
@@ -190,6 +201,8 @@ void TimelineWidget::PointerTool::MouseReleaseInternal(TimelineViewMouseEvent *e
continue;
}
const TrackReference& track_ref = ghost->GetAdjustedTrack();
Block* b = Node::ValueToPtr<Block>(ghost->data(TimelineViewGhostItem::kAttachedBlock));
// Normal blocks work in conjunction with the gap made above
@@ -202,9 +215,19 @@ void TimelineWidget::PointerTool::MouseReleaseInternal(TimelineViewMouseEvent *e
} else {
new BlockResizeCommand(b, ghost->AdjustedLength(), command);
}
}
} else if (duplicate_clips && ghost->mode() == Timeline::kMove) {
// Duplicate rather than move
Node* copy = b->copy();
const TrackReference& track_ref = ghost->GetAdjustedTrack();
new NodeAddCommand(static_cast<NodeGraph*>(b->parent()),
copy,
command);
new NodeCopyInputsCommand(b, copy, true, command);
// Place the copy instead of the original block
b = static_cast<Block*>(copy);
}
new TrackPlaceBlockCommand(parent()->timeline_node_->track_list(track_ref.type()),
track_ref.index(),
@@ -213,6 +236,14 @@ void TimelineWidget::PointerTool::MouseReleaseInternal(TimelineViewMouseEvent *e
command);
}
if (command->childCount() > 0) {
foreach (const TrackReference& t, tracks_affected) {
new TrackCleanGapsCommand(parent()->timeline_node_->track_list(t.type()),
t.index(),
command);
}
}
Core::instance()->undo_stack()->pushIfHasChildren(command);
}
@@ -360,6 +391,7 @@ void TimelineWidget::PointerTool::InitiateGhosts(TimelineViewBlockItem* clicked_
// For each selected item, create a "ghost", a visual representation of the action before it gets performed
foreach (TimelineViewBlockItem* clip_item, clips) {
// Determine correct mode for ghost
//
// Movement is indiscriminate, all the ghosts can be set to do this, however trimming is limited to one block
@@ -367,6 +399,12 @@ void TimelineWidget::PointerTool::InitiateGhosts(TimelineViewBlockItem* clicked_
bool include_this_clip = true;
if (clip_item->block()->type() == Block::kGap
&& trim_mode != Timeline::kTrimIn
&& trim_mode != Timeline::kTrimOut) {
continue;
}
if (clip_item != clicked_item
&& (trim_mode == Timeline::kTrimIn || trim_mode == Timeline::kTrimOut)) {
include_this_clip = multitrim_enabled ? IsClipTrimmable(clip_item, clips, trim_mode) : false;
@@ -376,6 +414,7 @@ void TimelineWidget::PointerTool::InitiateGhosts(TimelineViewBlockItem* clicked_
Block* block = clip_item->block();
Timeline::MovementMode block_mode = trim_mode;
// If we don't allow gap trimming, we automatically switch to the next/previous block to trim
if (block->type() == Block::kGap && !allow_gap_trimming) {
if (trim_mode == Timeline::kTrimIn) {
// Trim the previous clip's out point instead
+89 -3
View File
@@ -123,13 +123,13 @@ void BlockSetMediaOutCommand::undo()
TrackRippleRemoveBlockCommand::TrackRippleRemoveBlockCommand(TrackOutput *track, Block *block, QUndoCommand *parent) :
QUndoCommand(parent),
track_(track),
block_(block),
before_(block->previous())
block_(block)
{
}
void TrackRippleRemoveBlockCommand::redo()
{
before_ = block_->previous();
track_->RippleRemoveBlock(block_);
}
@@ -217,7 +217,7 @@ void TrackRippleRemoveAreaCommand::redo()
static_cast<NodeGraph*>(track_->parent())->AddNode(copy);
Node::CopyInputs(splice_, copy);
copy->set_length_and_media_in(splice_->length() - (out_ - splice_->in()));
copy->set_length_and_media_in(splice_original_length_ - (out_ - splice_->in()));
track_->InsertBlockAfter(copy, splice_);
@@ -296,6 +296,7 @@ void TrackRippleRemoveAreaCommand::undo()
track_->InsertBlockBefore(remove_block, trim_in_);
}
}
removed_blocks_.clear();
// If we picked up a block to trim the in point of
if (trim_in_old_length_ != trim_in_new_length_) {
@@ -552,3 +553,88 @@ BlockSplitPreservingLinksCommand::BlockSplitPreservingLinksCommand(const QVector
}
}
}
TrackCleanGapsCommand::TrackCleanGapsCommand(TrackList *track_list, int index, QUndoCommand *parent) :
QUndoCommand(parent),
track_list_(track_list),
track_index_(index)
{
}
void TrackCleanGapsCommand::redo()
{
GapBlock* on_gap = nullptr;
QList<GapBlock*> consecutive_gaps;
TrackOutput* track = track_list_->TrackAt(track_index_);
foreach (Block* b, track->Blocks()) {
if (b) {
if (b->type() == Block::kGap) {
if (on_gap) {
consecutive_gaps.append(static_cast<GapBlock*>(b));
} else {
on_gap = static_cast<GapBlock*>(b);
}
} else if (on_gap) {
merged_gaps_.append({on_gap, on_gap->length(), consecutive_gaps});
// Remove each gap and add to the length of the merged
// We can block the IC signal because merging gaps won't actually change anything
track->BlockInvalidateCache();
rational new_gap_length = on_gap->length();
foreach (GapBlock* gap, consecutive_gaps) {
track->RippleRemoveBlock(gap);
new_gap_length += gap->length();
}
on_gap->set_length(new_gap_length);
track->UnblockInvalidateCache();
// Reset state
on_gap = nullptr;
consecutive_gaps.clear();
}
}
}
if (on_gap) {
// If we're here, we found at least one or several
removed_end_gaps_.append(on_gap);
removed_end_gaps_.append(consecutive_gaps);
foreach (GapBlock* gap, removed_end_gaps_) {
track->RippleRemoveBlock(gap);
}
}
}
void TrackCleanGapsCommand::undo()
{
TrackOutput* track = track_list_->TrackAt(track_index_);
// Restored removed end gaps
foreach (GapBlock* gap, removed_end_gaps_) {
track->AppendBlock(gap);
}
removed_end_gaps_.clear();
track->BlockInvalidateCache();
for (int i=merged_gaps_.size()-1;i>=0;i--) {
const MergedGap& merge_info = merged_gaps_.at(i);
merge_info.merged->set_length(merge_info.original_length);
GapBlock* last_gap_added = merge_info.merged;
foreach (GapBlock* gap, merge_info.removed) {
track->InsertBlockAfter(gap, last_gap_added);
last_gap_added = gap;
}
}
track->UnblockInvalidateCache();
merged_gaps_.clear();
}
+26
View File
@@ -255,4 +255,30 @@ private:
Block* replace_;
};
class TrackCleanGapsCommand : public QUndoCommand {
public:
TrackCleanGapsCommand(TrackList* track_list, int index, QUndoCommand* parent = nullptr);
virtual void redo() override;
virtual void undo() override;
private:
struct MergedGap {
GapBlock* merged;
rational original_length;
QList<GapBlock*> removed;
};
TrackList* track_list_;
int track_index_;
QObject memory_manager_;
QList<MergedGap> merged_gaps_;
QList<GapBlock*> removed_end_gaps_;
};
#endif // TIMELINEUNDOABLE_H
@@ -180,6 +180,10 @@ void TimelineView::drawBackground(QPainter *painter, const QRectF &rect)
int line_y = 0;
foreach (TrackOutput* track, connected_track_list_->Tracks()) {
if (!track) {
continue;
}
line_y += track->GetTrackHeight();
// One px gap between tracks