implemented support for trimming transitions
This commit is contained in:
@@ -52,8 +52,8 @@ rational TransitionBlock::in_offset() const
|
||||
return length();
|
||||
}
|
||||
|
||||
// Assume both are connected, in which case the in offset will be <= length
|
||||
return length() / 2 + media_in();
|
||||
// Assume both are connected
|
||||
return length() + media_in();
|
||||
}
|
||||
|
||||
rational TransitionBlock::out_offset() const
|
||||
@@ -68,8 +68,8 @@ rational TransitionBlock::out_offset() const
|
||||
return length();
|
||||
}
|
||||
|
||||
// Assume both are connected, in which case the in offset will be <= length
|
||||
return length() / 2 - media_in();
|
||||
// Assume both are connected
|
||||
return -media_in();
|
||||
}
|
||||
|
||||
Block *TransitionBlock::connected_out_block() const
|
||||
|
||||
@@ -68,6 +68,11 @@ void NodeInputArray::SetSize(int size, bool lock)
|
||||
emit SizeChanged(size);
|
||||
}
|
||||
|
||||
bool NodeInputArray::ContainsSubParameter(NodeInput *input) const
|
||||
{
|
||||
return sub_params_.contains(input);
|
||||
}
|
||||
|
||||
int NodeInputArray::IndexOfSubParameter(NodeInput *input) const
|
||||
{
|
||||
return sub_params_.indexOf(input);
|
||||
|
||||
@@ -20,6 +20,7 @@ public:
|
||||
void RemoveAt(int index);
|
||||
void SetSize(int size, bool lock = true);
|
||||
|
||||
bool ContainsSubParameter(NodeInput* input) const;
|
||||
int IndexOfSubParameter(NodeInput* input) const;
|
||||
|
||||
NodeInput* First() const;
|
||||
|
||||
@@ -91,10 +91,9 @@ TrackOutput* TrackList::AddTrack()
|
||||
|
||||
track_input_->Append();
|
||||
|
||||
NodeInput* assoc_input = track_input_->At(track_input_->GetSize() - 1);
|
||||
|
||||
// Connect this track directly to this output
|
||||
NodeParam::ConnectEdge(track->output(), assoc_input);
|
||||
NodeParam::ConnectEdge(track->output(),
|
||||
track_input_->At(track_input_->GetSize() - 1));
|
||||
|
||||
// FIXME: Test code only
|
||||
if (track_input_->GetSize() > 1) {
|
||||
@@ -111,7 +110,7 @@ TrackOutput* TrackList::AddTrack()
|
||||
|
||||
if (last_track && last_track->output()->IsConnected()) {
|
||||
foreach (NodeEdgePtr edge, last_track->output()->edges()) {
|
||||
if (edge->input()->parentNode() != track_input_->parentNode()) {
|
||||
if (!track_input_->ContainsSubParameter(edge->input())) {
|
||||
Node* blend = NodeFactory::CreateFromID("org.olivevideoeditor.Olive.alphaoverblend");
|
||||
GetParentGraph()->AddNode(blend);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "core.h"
|
||||
#include "common/timecodefunctions.h"
|
||||
#include "dialog/speedduration/speedduration.h"
|
||||
#include "node/block/transition/transition.h"
|
||||
#include "tool/tool.h"
|
||||
#include "trackview/trackview.h"
|
||||
#include "widget/menu/menu.h"
|
||||
@@ -394,23 +395,46 @@ void TimelineWidget::SplitAtPlayhead()
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineWidget::DeleteSelectedInternal(const QList<Block *> blocks, bool remove_from_graph, QUndoCommand *command)
|
||||
void TimelineWidget::DeleteSelectedInternal(const QList<Block *> blocks, bool transition_aware, bool remove_from_graph, QUndoCommand *command)
|
||||
{
|
||||
foreach (Block* b, blocks) {
|
||||
TrackOutput* original_track = TrackOutput::TrackFromBlock(b);
|
||||
|
||||
// Make new gap and replace old Block with it for now
|
||||
GapBlock* gap = new GapBlock();
|
||||
gap->set_length_and_media_out(b->length());
|
||||
if (transition_aware && b->type() == Block::kTransition) {
|
||||
// Deleting transitions restores their in/out offsets to their attached blocks
|
||||
TransitionBlock* transition = static_cast<TransitionBlock*>(b);
|
||||
|
||||
new NodeAddCommand(static_cast<NodeGraph*>(b->parent()),
|
||||
gap,
|
||||
command);
|
||||
// Ripple remove transition
|
||||
new TrackRippleRemoveBlockCommand(original_track,
|
||||
transition,
|
||||
command);
|
||||
|
||||
new TrackReplaceBlockCommand(original_track,
|
||||
b,
|
||||
gap,
|
||||
command);
|
||||
// Resize attached blocks to make up length
|
||||
if (transition->connected_in_block()) {
|
||||
new BlockResizeWithMediaInCommand(transition->connected_in_block(),
|
||||
transition->connected_in_block()->length() + transition->in_offset(),
|
||||
command);
|
||||
}
|
||||
|
||||
if (transition->connected_out_block()) {
|
||||
new BlockResizeCommand(transition->connected_out_block(),
|
||||
transition->connected_out_block()->length() + transition->out_offset(),
|
||||
command);
|
||||
}
|
||||
} else {
|
||||
// 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 TrackReplaceBlockCommand(original_track,
|
||||
b,
|
||||
gap,
|
||||
command);
|
||||
}
|
||||
|
||||
if (remove_from_graph) {
|
||||
new NodeRemoveWithExclusiveDeps(static_cast<NodeGraph*>(b->parent()), b, command);
|
||||
@@ -442,7 +466,7 @@ void TimelineWidget::DeleteSelected()
|
||||
QUndoCommand* command = new QUndoCommand();
|
||||
|
||||
// Replace blocks with gaps (effectively deleting them)
|
||||
DeleteSelectedInternal(blocks_to_delete, true, command);
|
||||
DeleteSelectedInternal(blocks_to_delete, true, true, command);
|
||||
|
||||
// Clean each track
|
||||
foreach (const TrackReference& track, tracks_affected) {
|
||||
|
||||
@@ -331,7 +331,7 @@ private:
|
||||
bool dual_transition_;
|
||||
};
|
||||
|
||||
void DeleteSelectedInternal(const QList<Block*> blocks, bool remove_from_graph, QUndoCommand* command);
|
||||
void DeleteSelectedInternal(const QList<Block*> blocks, bool transition_aware, bool remove_from_graph, QUndoCommand* command);
|
||||
|
||||
void SetBlockLinksSelected(Block *block, bool selected);
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "config/config.h"
|
||||
#include "core.h"
|
||||
#include "node/block/gap/gap.h"
|
||||
#include "node/block/transition/transition.h"
|
||||
#include "widget/nodeview/nodeviewundo.h"
|
||||
|
||||
TimelineWidget::PointerTool::PointerTool(TimelineWidget *parent) :
|
||||
@@ -216,7 +217,7 @@ void TimelineWidget::PointerTool::MouseReleaseInternal(TimelineViewMouseEvent *e
|
||||
}
|
||||
|
||||
// If there are any blocks to remove, remove them
|
||||
parent()->DeleteSelectedInternal(blocks_to_temp_remove, false, command);
|
||||
parent()->DeleteSelectedInternal(blocks_to_temp_remove, false, 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
|
||||
// all of the gaps we inserted earlier
|
||||
@@ -459,8 +460,19 @@ void TimelineWidget::PointerTool::InitiateGhosts(TimelineViewBlockItem* clicked_
|
||||
block_mode = FlipTrimMode(trim_mode);
|
||||
}
|
||||
|
||||
if (block != nullptr) {
|
||||
if (block) {
|
||||
AddGhostFromBlock(block, clip_item->Track(), block_mode);
|
||||
|
||||
if (block->type() == Block::kTransition) {
|
||||
TransitionBlock* transition = static_cast<TransitionBlock*>(block);
|
||||
|
||||
// Create a rolling effect with the attached block
|
||||
if (transition->connected_in_block() && block_mode == Timeline::kTrimOut) {
|
||||
AddGhostFromBlock(transition->connected_in_block(), clip_item->Track(), Timeline::kTrimIn);
|
||||
} else if (transition->connected_out_block() && block_mode == Timeline::kTrimIn) {
|
||||
AddGhostFromBlock(transition->connected_out_block(), clip_item->Track(), Timeline::kTrimOut);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -531,6 +543,11 @@ bool TimelineWidget::PointerTool::IsClipTrimmable(TimelineViewBlockItem* clip,
|
||||
return true;
|
||||
}
|
||||
|
||||
rational GetEarliestPointForClip(Block* block)
|
||||
{
|
||||
return qMax(rational(0), block->in() - block->media_in());
|
||||
}
|
||||
|
||||
rational TimelineWidget::PointerTool::ValidateInTrimming(rational movement,
|
||||
const QVector<TimelineViewGhostItem *> ghosts,
|
||||
bool prevent_overwriting)
|
||||
@@ -542,8 +559,34 @@ rational TimelineWidget::PointerTool::ValidateInTrimming(rational movement,
|
||||
|
||||
Block* block = Node::ValueToPtr<Block>(ghost->data(TimelineViewGhostItem::kAttachedBlock));
|
||||
|
||||
// Determine the earliest in point this block could have
|
||||
rational earliest_in = qMax(rational(0), block->in() - block->media_in());
|
||||
rational earliest_in = RATIONAL_MIN;
|
||||
rational latest_in = ghost->Out();
|
||||
|
||||
if (block->type() == Block::kTransition) {
|
||||
// For transitions, validate with the attached block
|
||||
TransitionBlock* transition = static_cast<TransitionBlock*>(block);
|
||||
|
||||
if (transition->connected_in_block() && transition->connected_out_block()) {
|
||||
// Here, we try to get the latest earliest point for both the in and out blocks, we do in here and out will
|
||||
// be calculated later
|
||||
earliest_in = GetEarliestPointForClip(transition->connected_in_block());
|
||||
|
||||
// We set the block to the out block since that will be before the in block and will be the one we use to
|
||||
// prevent overwriting since we're trimming the in side of this transition
|
||||
block = transition->connected_out_block();
|
||||
|
||||
latest_in = transition->in() + transition->out_offset();
|
||||
} else {
|
||||
// Use whatever block is attached
|
||||
block = transition->connected_in_block() ? transition->connected_in_block() : transition->connected_out_block();
|
||||
}
|
||||
}
|
||||
|
||||
earliest_in = qMax(earliest_in, GetEarliestPointForClip(block));
|
||||
|
||||
if (!ghost->CanHaveZeroLength()) {
|
||||
latest_in -= parent()->timebase();
|
||||
}
|
||||
|
||||
if (prevent_overwriting) {
|
||||
// Look for a Block in the way
|
||||
@@ -557,13 +600,6 @@ rational TimelineWidget::PointerTool::ValidateInTrimming(rational movement,
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the latest point this block could have
|
||||
rational latest_in = ghost->Out();
|
||||
|
||||
if (!ghost->CanHaveZeroLength()) {
|
||||
latest_in -= parent()->timebase();
|
||||
}
|
||||
|
||||
// Clamp adjusted value between the earliest and latest values
|
||||
rational adjusted = ghost->In() + movement;
|
||||
rational clamped = clamp(adjusted, earliest_in, latest_in);
|
||||
@@ -596,6 +632,24 @@ rational TimelineWidget::PointerTool::ValidateOutTrimming(rational movement,
|
||||
|
||||
rational latest_out = RATIONAL_MAX;
|
||||
|
||||
if (block->type() == Block::kTransition) {
|
||||
// For transitions, validate with the attached block
|
||||
TransitionBlock* transition = static_cast<TransitionBlock*>(block);
|
||||
|
||||
if (transition->connected_in_block() && transition->connected_out_block()) {
|
||||
// We set the block to the out block since that will be before the in block and will be the one we use to
|
||||
// prevent overwriting since we're trimming the in side of this transition
|
||||
|
||||
// FIXME: At some point we may add some better logic to `latest_out` akin to the logic in ValidateInTrimming
|
||||
// which is why this hasn't yet been collapsed into the ternary below.
|
||||
block = transition->connected_in_block();
|
||||
|
||||
earliest_out = transition->out() - transition->in_offset();
|
||||
} else {
|
||||
block = transition->connected_in_block() ? transition->connected_in_block() : transition->connected_out_block();
|
||||
}
|
||||
}
|
||||
|
||||
if (prevent_overwriting) {
|
||||
// Determine if there's a block in the way
|
||||
Block* next = block->next();
|
||||
|
||||
@@ -97,6 +97,7 @@ void TimelineWidget::TransitionTool::MouseRelease(TimelineViewMouseEvent *event)
|
||||
new NodeAddCommand(static_cast<NodeGraph*>(parent()->timeline_node_->parent()),
|
||||
transition,
|
||||
command);
|
||||
|
||||
new TrackPlaceBlockCommand(parent()->timeline_node_->track_list(track.type()),
|
||||
track.index(),
|
||||
transition,
|
||||
@@ -104,8 +105,8 @@ void TimelineWidget::TransitionTool::MouseRelease(TimelineViewMouseEvent *event)
|
||||
command);
|
||||
|
||||
if (dual_transition_) {
|
||||
//transition->set_in_and_out_offset(ghost_->AdjustedLength()/2, ghost_->AdjustedLength()/2);
|
||||
transition->set_length_and_media_out(ghost_->AdjustedLength());
|
||||
transition->set_media_in(-ghost_->AdjustedLength()/2);
|
||||
|
||||
// Block mouse is hovering over
|
||||
Block* active_block = Node::ValueToPtr<Block>(ghost_->data(TimelineViewGhostItem::kAttachedBlock));
|
||||
|
||||
@@ -546,6 +546,7 @@ void TrackCleanGapsCommand::redo()
|
||||
rational new_gap_length = on_gap->length();
|
||||
foreach (GapBlock* gap, consecutive_gaps) {
|
||||
track->RippleRemoveBlock(gap);
|
||||
static_cast<NodeGraph*>(gap->parent())->TakeNode(gap, &memory_manager_);
|
||||
|
||||
new_gap_length += gap->length();
|
||||
}
|
||||
@@ -566,6 +567,7 @@ void TrackCleanGapsCommand::redo()
|
||||
|
||||
foreach (GapBlock* gap, removed_end_gaps_) {
|
||||
track->RippleRemoveBlock(gap);
|
||||
static_cast<NodeGraph*>(gap->parent())->TakeNode(gap, &memory_manager_);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -576,6 +578,7 @@ void TrackCleanGapsCommand::undo()
|
||||
|
||||
// Restored removed end gaps
|
||||
foreach (GapBlock* gap, removed_end_gaps_) {
|
||||
static_cast<NodeGraph*>(gap->parent())->AddNode(gap);
|
||||
track->AppendBlock(gap);
|
||||
}
|
||||
removed_end_gaps_.clear();
|
||||
@@ -590,6 +593,7 @@ void TrackCleanGapsCommand::undo()
|
||||
GapBlock* last_gap_added = merge_info.merged;
|
||||
|
||||
foreach (GapBlock* gap, merge_info.removed) {
|
||||
static_cast<NodeGraph*>(gap->parent())->AddNode(gap);
|
||||
track->InsertBlockAfter(gap, last_gap_added);
|
||||
last_gap_added = gap;
|
||||
}
|
||||
|
||||
@@ -146,17 +146,8 @@ void TimelineViewBlockItem::paint(QPainter *painter, const QStyleOptionGraphicsI
|
||||
|
||||
TransitionBlock* t = static_cast<TransitionBlock*>(block_);
|
||||
|
||||
if (t->connected_out_block()) {
|
||||
// Transition fades something out, we'll draw a line
|
||||
painter->drawLine(rect().topLeft(), rect().bottomRight());
|
||||
}
|
||||
|
||||
if (t->connected_in_block()) {
|
||||
// Transition fades something in, we'll draw a line
|
||||
painter->drawLine(rect().bottomLeft(), rect().topRight());
|
||||
}
|
||||
|
||||
if (t->connected_out_block() && t->connected_in_block()) {
|
||||
|
||||
// Draw line between out offset and in offset
|
||||
qreal crossover_line = rect().left();
|
||||
crossover_line += TimeToScene(t->out_offset());
|
||||
@@ -164,6 +155,24 @@ void TimelineViewBlockItem::paint(QPainter *painter, const QStyleOptionGraphicsI
|
||||
qRound(rect().top()),
|
||||
qRound(crossover_line),
|
||||
qRound(rect().bottom()));
|
||||
|
||||
// Draw lines to mid point
|
||||
QPointF mid_point(crossover_line, rect().center().y());
|
||||
painter->drawLine(rect().topLeft(), mid_point);
|
||||
painter->drawLine(rect().bottomLeft(), mid_point);
|
||||
painter->drawLine(rect().topRight(), mid_point);
|
||||
painter->drawLine(rect().bottomRight(), mid_point);
|
||||
|
||||
} else if (t->connected_out_block()) {
|
||||
|
||||
// Transition fades something out, we'll draw a line
|
||||
painter->drawLine(rect().topLeft(), rect().bottomRight());
|
||||
|
||||
} else if (t->connected_in_block()) {
|
||||
|
||||
// Transition fades something in, we'll draw a line
|
||||
painter->drawLine(rect().bottomLeft(), rect().topRight());
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user