timeline: implement add default transition

Fixes #1939
This commit is contained in:
itsmattkc
2022-06-28 12:09:10 -07:00
parent 7785c0d66a
commit ac207e8fe7
7 changed files with 213 additions and 3 deletions
+4
View File
@@ -104,6 +104,10 @@ void Config::SetDefaults()
SetEntryInternal(QStringLiteral("ReassocLinToNonLin"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("PreviewNonFloatDontAskAgain"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("DefaultVideoTransition"), NodeValue::kText, QStringLiteral("org.olivevideoeditor.Olive.crossdissolve"));
SetEntryInternal(QStringLiteral("DefaultAudioTransition"), NodeValue::kText, QStringLiteral("org.olivevideoeditor.Olive.crossdissolve"));
SetEntryInternal(QStringLiteral("DefaultTransitionLength"), NodeValue::kRational, QVariant::fromValue(rational(1)));
SetEntryInternal(QStringLiteral("AutoCacheDelay"), NodeValue::kInt, 1000);
SetEntryInternal(QStringLiteral("CatColor0"), NodeValue::kInt, ColorCoding::kRed);
+5
View File
@@ -86,6 +86,11 @@ public:
virtual void MoveOutToPlayhead() override;
void AddDefaultTransitionsToSelected()
{
timeline_widget()->AddDefaultTransitionsToSelected();
}
void ShowSpeedDurationDialogForSelectedClips()
{
timeline_widget()->ShowSpeedDurationDialogForSelectedClips();
+1 -1
View File
@@ -291,7 +291,7 @@ void MenuShared::NestTriggered()
void MenuShared::DefaultTransitionTriggered()
{
qDebug() << "FIXME: Stub";
PanelManager::instance()->MostRecentlyFocused<TimelinePanel>()->AddDefaultTransitionsToSelected();
}
void MenuShared::TimecodeDisplayTriggered()
@@ -568,6 +568,22 @@ void TimelineWidget::ToggleLinksOnSelected()
Core::instance()->undo_stack()->push(new NodeLinkManyCommand(blocks, link));
}
void TimelineWidget::AddDefaultTransitionsToSelected()
{
QVector<ClipBlock*> blocks;
foreach (Block* item, GetSelectedBlocks()) {
// Only clips can be linked
if (ClipBlock *clip = dynamic_cast<ClipBlock*>(item)) {
blocks.append(clip);
}
}
if (!blocks.isEmpty()) {
Core::instance()->undo_stack()->push(new TimelineAddDefaultTransitionCommand(blocks, timebase()));
}
}
bool TimelineWidget::CopySelected(bool cut)
{
if (super::CopySelected(cut)) {
@@ -79,6 +79,8 @@ public:
void ToggleLinksOnSelected();
void AddDefaultTransitionsToSelected();
virtual bool CopySelected(bool cut) override;
virtual bool Paste() override;
@@ -22,9 +22,11 @@
#include "node/block/clip/clip.h"
#include "node/block/transition/transition.h"
#include "node/factory.h"
#include "node/math/math/math.h"
#include "node/math/merge/merge.h"
#include "timelineundocommon.h"
#include "widget/timelinewidget/undo/timelineundotrack.h"
namespace olive {
@@ -287,8 +289,8 @@ void TrackListInsertGaps::prepare()
QVector<Block*> blocks_to_append_gap_to;
QVector<Track*> tracks_to_append_gap_to;
foreach (Track* track, working_tracks_) {
foreach (Block* b, track->Blocks()) {
for (Track* track : qAsConst(working_tracks_)) {
for (Block* b : track->Blocks()) {
if (dynamic_cast<GapBlock*>(b) && b->in() <= point_ && b->out() >= point_) {
// Found a gap at the location
gaps_to_extend_.append(b);
@@ -542,4 +544,130 @@ void TimelineRemoveTrackCommand::undo()
remove_command_->undo_now();
}
void TimelineAddDefaultTransitionCommand::prepare()
{
for (auto it=clips_.cbegin(); it!=clips_.cend(); it++) {
ClipBlock *c = *it;
// Handle in transition
if (clips_.contains(static_cast<ClipBlock*>(c->previous()))) {
// Do nothing, assume this will be handled by a dual transition from that clip
} else if (dynamic_cast<GapBlock*>(c->previous()) || !c->previous()) {
// Create in transition
AddTransition(c, kIn);
}
// Handle out transition
if (clips_.contains(static_cast<ClipBlock*>(c->next()))) {
AddTransition(c, kOutDual);
} else if (dynamic_cast<GapBlock*>(c->next()) || !c->next()) {
// Create out transition
AddTransition(c, kOut);
}
}
}
void TimelineAddDefaultTransitionCommand::AddTransition(ClipBlock *c, CreateTransitionMode mode)
{
if (Track *t = c->track()) {
Node *p = nullptr;
if (t->type() == Track::kVideo) {
p = NodeFactory::CreateFromID(OLIVE_CONFIG("DefaultVideoTransition").toString());
} else if (t->type() == Track::kAudio) {
p = NodeFactory::CreateFromID(OLIVE_CONFIG("DefaultAudioTransition").toString());
}
rational transition_length = OLIVE_CONFIG("DefaultTransitionLength").value<rational>();
// Resize original clip
switch (mode) {
case kIn:
ValidateTransitionLength(c, transition_length);
if (transition_length > 0) {
AdjustClipLength(c, transition_length, false);
}
break;
case kOut:
ValidateTransitionLength(c, transition_length);
if (transition_length > 0) {
AdjustClipLength(c, transition_length, true);
}
break;
case kOutDual:
{
rational half_length = transition_length / 2;
ValidateTransitionLength(static_cast<ClipBlock*>(c->next()), half_length);
ValidateTransitionLength(c, half_length);
transition_length = half_length * 2;
if (transition_length > 0) {
AdjustClipLength(static_cast<ClipBlock*>(c->next()), half_length, false);
AdjustClipLength(c, half_length, true);
}
break;
}
}
if (transition_length > 0) {
if (TransitionBlock *transition = dynamic_cast<TransitionBlock*>(p)) {
transition->set_length_and_media_out(transition_length);
// Add transition
commands_.append(new NodeAddCommand(c->parent(), transition));
// Insert block
Block *insert_after;
switch (mode) {
case kIn:
insert_after = c->previous();
break;
case kOut:
case kOutDual:
insert_after = c;
break;
}
commands_.append(new TrackInsertBlockAfterCommand(c->track(), transition, insert_after));
// Connect
switch (mode) {
case kIn:
commands_.append(new NodeEdgeAddCommand(c, NodeInput(transition, TransitionBlock::kInBlockInput)));
break;
case kOutDual:
commands_.append(new NodeEdgeAddCommand(c->next(), NodeInput(transition, TransitionBlock::kInBlockInput)));
/* fall through */
case kOut:
commands_.append(new NodeEdgeAddCommand(c, NodeInput(transition, TransitionBlock::kOutBlockInput)));
break;
}
}
}
}
}
void TimelineAddDefaultTransitionCommand::AdjustClipLength(ClipBlock *c, const rational &transition_length, bool out)
{
rational cur_len = lengths_.value(c, c->length());
rational new_len = cur_len - transition_length;
if (out) {
commands_.append(new BlockResizeCommand(c, new_len));
} else {
commands_.append(new BlockResizeWithMediaInCommand(c, new_len));
}
lengths_.insert(c, new_len);
}
void TimelineAddDefaultTransitionCommand::ValidateTransitionLength(ClipBlock *c, rational &transition_length)
{
rational cur_len = lengths_.value(c, c->length());
rational half_cur_len = cur_len/2;
if (transition_length >= half_cur_len) {
transition_length = half_cur_len - timebase_;
}
}
}
@@ -358,6 +358,61 @@ private:
};
class TimelineAddDefaultTransitionCommand : public UndoCommand
{
public:
TimelineAddDefaultTransitionCommand(const QVector<ClipBlock*> &clips, const rational &timebase) :
clips_(clips),
timebase_(timebase)
{}
virtual ~TimelineAddDefaultTransitionCommand() override
{
qDeleteAll(commands_);
}
virtual Project* GetRelevantProject() const override
{
return clips_.empty() ? nullptr : clips_.first()->project();
}
protected:
virtual void prepare() override;
virtual void redo() override
{
for (auto it=commands_.cbegin(); it!=commands_.cend(); it++) {
(*it)->redo_now();
}
}
virtual void undo() override
{
for (auto it=commands_.crbegin(); it!=commands_.crend(); it++) {
(*it)->undo_now();
}
}
private:
enum CreateTransitionMode {
kIn,
kOut,
kOutDual
};
void AddTransition(ClipBlock *c, CreateTransitionMode mode);
void AdjustClipLength(ClipBlock *c, const rational &transition_length, bool out);
void ValidateTransitionLength(ClipBlock *c, rational &transition_length);
QVector<ClipBlock*> clips_;
rational timebase_;
QVector<UndoCommand*> commands_;
QHash<ClipBlock*, rational> lengths_;
};
}
#endif // TIMELINEUNDOGENERAL_H