handle cache properly in ripple delete

This commit is contained in:
itsmattkc
2022-01-28 13:49:49 -08:00
parent 04ae3acbda
commit cce379ad22
8 changed files with 168 additions and 31 deletions
+9 -6
View File
@@ -57,21 +57,24 @@ void PlaybackCache::Shift(rational from, rational to)
// An region between `from` and `to` will be inserted or spliced out
TimeRangeList ranges_to_shift = validated_.Intersects(TimeRange(from, RATIONAL_MAX));
// Remove everything from the minimum point
TimeRange remove_range = TimeRange(qMin(from, to), RATIONAL_MAX);
Invalidate(remove_range, false);
// Remove all ranges starting at to
validated_.remove(TimeRange(qMin(from, to), RATIONAL_MAX));
// Shift invalidated ranges
// (`diff` is POSITIVE when moving forward -> and NEGATIVE when moving backward <-)
// Restore ranges shifted
rational diff = to - from;
foreach (const TimeRange& r, ranges_to_shift) {
Validate(r + diff, false);
validated_.insert(r + diff);
}
// Tell derivatives that a shift has occurred
ShiftEvent(from, to);
// Emit signals
emit Shifted(from, to);
if (diff > 0) {
//emit Invalidated(TimeRange(from, to));
}
}
void PlaybackCache::Validate(const TimeRange &r, bool signal)
+9 -8
View File
@@ -462,7 +462,8 @@ void TimelineWidget::SplitAtPlayhead()
void TimelineWidget::ReplaceBlocksWithGaps(const QVector<Block *> &blocks,
bool remove_from_graph,
MultiUndoCommand *command,
bool handle_transitions)
bool handle_transitions,
bool handle_invalidations)
{
foreach (Block* b, blocks) {
if (dynamic_cast<GapBlock*>(b)) {
@@ -473,7 +474,7 @@ void TimelineWidget::ReplaceBlocksWithGaps(const QVector<Block *> &blocks,
Track* original_track = b->track();
command->add_child(new TrackReplaceBlockWithGapCommand(original_track, b, handle_transitions));
command->add_child(new TrackReplaceBlockWithGapCommand(original_track, b, handle_transitions, handle_invalidations));
if (remove_from_graph) {
command->add_child(new NodeRemoveWithExclusiveDependenciesAndDisconnect(b));
@@ -508,6 +509,9 @@ void TimelineWidget::DeleteSelected(bool ripple)
MultiUndoCommand* command = new MultiUndoCommand();
// Remove all selections
command->add_child(new SetSelectionsCommand(this, TimelineWidgetSelections(), GetSelections()));
// For transitions, remove them but extend their attached blocks to fill their place
foreach (TransitionBlock* transition, transitions_to_delete) {
TransitionRemoveCommand *trc = new TransitionRemoveCommand(transition, true);
@@ -519,10 +523,7 @@ void TimelineWidget::DeleteSelected(bool ripple)
}
// Replace clips with gaps (effectively deleting them)
ReplaceBlocksWithGaps(clips_to_delete, true, command);
// Remove all selections
command->add_child(new SetSelectionsCommand(this, TimelineWidgetSelections(), GetSelections(), false));
ReplaceBlocksWithGaps(clips_to_delete, true, command, false, !ripple);
// Insert ripple command now that it's all cleaned up gaps
TimelineRippleDeleteGapsAtRegionsCommand *ripple_command = nullptr;
@@ -1207,7 +1208,7 @@ void TimelineWidget::NudgeInternal(rational amount)
// Nudge selections
TimelineWidgetSelections new_sel = GetSelections();
new_sel.ShiftTime(amount);
command->add_child(new TimelineWidget::SetSelectionsCommand(this, new_sel, GetSelections(), true));
command->add_child(new TimelineWidget::SetSelectionsCommand(this, new_sel, GetSelections()));
Core::instance()->undo_stack()->push(command);
}
@@ -1259,7 +1260,7 @@ void TimelineWidget::MoveToPlayheadInternal(bool out)
it.value().shift(track_adj);
}
}
command->add_child(new SetSelectionsCommand(this, new_sel, GetSelections(), true));
command->add_child(new SetSelectionsCommand(this, new_sel, GetSelections()));
Core::instance()->undo_stack()->push(command);
}
+2 -2
View File
@@ -122,7 +122,7 @@ public:
void RestoreSplitterState(const QByteArray& state);
static void ReplaceBlocksWithGaps(const QVector<Block *> &blocks, bool remove_from_graph, MultiUndoCommand *command, bool handle_transitions = true);
static void ReplaceBlocksWithGaps(const QVector<Block *> &blocks, bool remove_from_graph, MultiUndoCommand *command, bool handle_transitions = true, bool handle_invalidations = true);
/**
* @brief Retrieve the QGraphicsItem at a particular scene position
@@ -230,7 +230,7 @@ public:
class SetSelectionsCommand : public UndoCommand {
public:
SetSelectionsCommand(TimelineWidget* timeline, const TimelineWidgetSelections& now, const TimelineWidgetSelections& old, bool process_block_changes) :
SetSelectionsCommand(TimelineWidget* timeline, const TimelineWidgetSelections& now, const TimelineWidgetSelections& old, bool process_block_changes = true) :
timeline_(timeline),
old_(old),
now_(now),
+3 -3
View File
@@ -572,7 +572,7 @@ void PointerTool::FinishDrag(TimelineViewMouseEvent *event)
} else {
new_sel.TrimOut(reference_ghost->GetOutAdjustment());
}
command->add_child(new TimelineWidget::SetSelectionsCommand(parent(), new_sel, parent()->GetSelections(), true));
command->add_child(new TimelineWidget::SetSelectionsCommand(parent(), new_sel, parent()->GetSelections()));
}
}
@@ -621,7 +621,7 @@ void PointerTool::FinishDrag(TimelineViewMouseEvent *event)
TimelineWidgetSelections new_sel = parent()->GetSelections();
new_sel.ShiftTime(blocks_moving.first().ghost->GetInAdjustment());
new_sel.ShiftTracks(drag_track_type_, blocks_moving.first().ghost->GetTrackAdjustment());
command->add_child(new TimelineWidget::SetSelectionsCommand(parent(), new_sel, parent()->GetSelections(), true));
command->add_child(new TimelineWidget::SetSelectionsCommand(parent(), new_sel, parent()->GetSelections()));
}
if (!blocks_sliding.isEmpty()) {
@@ -682,7 +682,7 @@ void PointerTool::FinishDrag(TimelineViewMouseEvent *event)
// Adjust selections
TimelineWidgetSelections new_sel = parent()->GetSelections();
new_sel.ShiftTime(movement);
command->add_child(new TimelineWidget::SetSelectionsCommand(parent(), new_sel, parent()->GetSelections(), true));
command->add_child(new TimelineWidget::SetSelectionsCommand(parent(), new_sel, parent()->GetSelections()));
}
}
@@ -490,7 +490,9 @@ void TrackReplaceBlockWithGapCommand::redo()
track_->EndOperation();
track_->Node::InvalidateCache(invalidate_range, Track::kBlockInput);
if (handle_invalidations_) {
track_->Node::InvalidateCache(invalidate_range, Track::kBlockInput);
}
} else {
// Block is at the end of the track, simply remove it
@@ -547,7 +549,9 @@ void TrackReplaceBlockWithGapCommand::undo()
track_->EndOperation();
track_->Node::InvalidateCache(TimeRange(block_->in(), block_->out()), Track::kBlockInput);
if (handle_invalidations_) {
track_->Node::InvalidateCache(TimeRange(block_->in(), block_->out()), Track::kBlockInput);
}
} else {
// Our gap and existing gap were both null, our block must have been at the end and thus
@@ -233,13 +233,14 @@ private:
class TrackReplaceBlockWithGapCommand : public UndoCommand {
public:
TrackReplaceBlockWithGapCommand(Track* track, Block* block, bool handle_transitions = true) :
TrackReplaceBlockWithGapCommand(Track* track, Block* block, bool handle_transitions = true, bool handle_invalidations = true) :
track_(track),
block_(block),
existing_gap_(nullptr),
existing_merged_gap_(nullptr),
our_gap_(nullptr),
handle_transitions_(handle_transitions)
handle_transitions_(handle_transitions),
handle_invalidations_(handle_invalidations)
{
}
@@ -265,6 +266,7 @@ private:
GapBlock* our_gap_;
bool handle_transitions_;
bool handle_invalidations_;
QObject memory_manager_;
@@ -360,6 +362,62 @@ private:
};
class NodeBeginOperationCommand : public UndoCommand
{
public:
NodeBeginOperationCommand(Node *node) :
node_(node)
{}
virtual Project* GetRelevantProject() const override
{
return node_->project();
}
protected:
virtual void redo() override
{
node_->BeginOperation();
}
virtual void undo() override
{
node_->EndOperation();
}
private:
Node *node_;
};
class NodeEndOperationCommand : public UndoCommand
{
public:
NodeEndOperationCommand(Node *node) :
node_(node)
{}
virtual Project* GetRelevantProject() const override
{
return node_->project();
}
protected:
virtual void redo() override
{
node_->EndOperation();
}
virtual void undo() override
{
node_->BeginOperation();
}
private:
Node *node_;
};
}
#endif // TIMELINEUNDOGENERAL_H
@@ -488,26 +488,36 @@ void TimelineRippleDeleteGapsAtRegionsCommand::prepare()
}
max_gaps = qMax(max_gaps, gaps_on_track.size());
} else {
qWarning() << "Failed to find corresponding gap to region";
}
}
// For each gap on each track, find a corresponding gap on every other track (which may include
// a requested gap) to ripple in order to keep everything synchronized
QHash<GapBlock*, rational> gap_lengths;
for (int gap_index=0; gap_index<max_gaps; gap_index++) {
rational earliest_point = RATIONAL_MAX;
rational ripple_length = RATIONAL_MAX;
rational latest_point = RATIONAL_MIN;
foreach (const QVector<RemovalRequest> &gaps_on_track, requested_gaps) {
if (gap_index < gaps_on_track.size()) {
const RemovalRequest &gap = gaps_on_track.at(gap_index);
earliest_point = qMin(earliest_point, gap.range.in());
ripple_length = qMin(ripple_length, gap.range.length());
latest_point = qMax(latest_point, gap.range.out());
}
}
// Determine which gaps will be involved in this operation
QVector<GapBlock*> gaps;
bool all_tracks_unlocked = true;
foreach (Track* track, timeline_->GetTracks()) {
if (track->IsLocked()) {
all_tracks_unlocked = false;
continue;
}
@@ -548,7 +558,12 @@ void TimelineRippleDeleteGapsAtRegionsCommand::prepare()
if (gap) {
gaps.append(gap);
ripple_length = qMin(ripple_length, gap->length());
if (!gap_lengths.contains(gap)) {
gap_lengths.insert(gap, gap->length());
}
ripple_length = qMin(ripple_length, gap_lengths.value(gap));
}
if (ripple_length == 0) {
@@ -558,11 +573,24 @@ void TimelineRippleDeleteGapsAtRegionsCommand::prepare()
if (ripple_length > 0) {
foreach (GapBlock *gap, gaps) {
if (gap->length() == ripple_length) {
if (all_tracks_unlocked) {
commands_.append(new NodeBeginOperationCommand(gap->track()));
}
if (gap_lengths.value(gap) == ripple_length) {
commands_.append(new TrackRippleRemoveBlockCommand(gap->track(), gap));
} else {
commands_.append(new BlockResizeCommand(gap, gap->length() - ripple_length));
gap_lengths[gap] -= ripple_length;
commands_.append(new BlockResizeCommand(gap, gap_lengths.value(gap)));
}
if (all_tracks_unlocked) {
commands_.append(new NodeEndOperationCommand(gap->track()));
}
}
if (all_tracks_unlocked) {
commands_.append(new TimelineShiftCacheCommand(timeline_, latest_point, latest_point - ripple_length));
}
}
}
@@ -582,4 +610,14 @@ void TimelineRippleDeleteGapsAtRegionsCommand::undo()
}
}
void TimelineShiftCacheCommand::redo()
{
timeline_->ShiftCache(from_, to_);
}
void TimelineShiftCacheCommand::undo()
{
timeline_->ShiftCache(to_, from_);
}
}
@@ -38,7 +38,8 @@ namespace olive {
* By default, nothing takes this area meaning all subsequent clips are pushed backward, however you can specify
* a block to insert at the `in` point. No checking is done to ensure `insert` is the same length as `in` to `out`.
*/
class TrackRippleRemoveAreaCommand : public UndoCommand {
class TrackRippleRemoveAreaCommand : public UndoCommand
{
public:
TrackRippleRemoveAreaCommand(Track* track, const TimeRange& range);
@@ -98,7 +99,8 @@ private:
};
class TrackListRippleRemoveAreaCommand : public UndoCommand {
class TrackListRippleRemoveAreaCommand : public UndoCommand
{
public:
TrackListRippleRemoveAreaCommand(TrackList* list, rational in, rational out) :
list_(list),
@@ -134,7 +136,8 @@ private:
};
class TimelineRippleRemoveAreaCommand : public MultiUndoCommand {
class TimelineRippleRemoveAreaCommand : public MultiUndoCommand
{
public:
TimelineRippleRemoveAreaCommand(Sequence* timeline, rational in, rational out);
@@ -148,7 +151,8 @@ private:
};
class TrackListRippleToolCommand : public UndoCommand {
class TrackListRippleToolCommand : public UndoCommand
{
public:
struct RippleInfo {
Block* block;
@@ -200,7 +204,8 @@ private:
};
class TimelineRippleDeleteGapsAtRegionsCommand : public UndoCommand {
class TimelineRippleDeleteGapsAtRegionsCommand : public UndoCommand
{
public:
TimelineRippleDeleteGapsAtRegionsCommand(Sequence* vo, const QVector<QPair<Track*, TimeRange> >& regions) :
timeline_(vo),
@@ -243,6 +248,34 @@ private:
};
class TimelineShiftCacheCommand : public UndoCommand
{
public:
TimelineShiftCacheCommand(Sequence* timeline, const rational &from, const rational &to) :
timeline_(timeline),
from_(from),
to_(to)
{}
virtual Project* GetRelevantProject() const override
{
return timeline_->project();
}
protected:
virtual void redo() override;
virtual void undo() override;
private:
Sequence* timeline_;
rational from_;
rational to_;
};
}
#endif // TIMELINEUNDORIPPLE_H