Moving clip with mixes should now work correctly

parent ee8fb1c2
......@@ -568,7 +568,7 @@ bool TimelineModel::requestFakeClipMove(int clipId, int trackId, int position, b
return false;
}
bool TimelineModel::requestClipMove(int clipId, int trackId, int position, bool moveMirrorTracks, bool updateView, bool invalidateTimeline, bool finalMove, Fun &undo, Fun &redo, bool groupMove)
bool TimelineModel::requestClipMove(int clipId, int trackId, int position, bool moveMirrorTracks, bool updateView, bool invalidateTimeline, bool finalMove, Fun &undo, Fun &redo, bool groupMove, QMap <int, int> moving_clips)
{
Q_UNUSED(moveMirrorTracks)
// qDebug() << "// FINAL MOVE: " << invalidateTimeline << ", UPDATE VIEW: " << updateView<<", FINAL: "<<finalMove;
......@@ -609,91 +609,111 @@ bool TimelineModel::requestClipMove(int clipId, int trackId, int position, bool
return true;
};
}
Fun move_mix = []() { return true; };
Fun restore_mix = []() { return true; };
Fun update_mix = []() { return true; };
Fun move_mix_end = []() { return true; };
Fun restore_mix_end = []() { return true; };
Fun update_mix_end = []() { return true; };
if (isTrack(old_trackId) && getTrackById_const(old_trackId)->hasMix(clipId)) {
Fun sync_mix = []() { return true; };
Fun update_playlist = []() { return true; };
Fun update_playlist_undo = []() { return true; };
Fun simple_move_mix = []() { return true; };
Fun simple_restore_mix = []() { return true; };
int previous_track = moving_clips.value(clipId, -1);
if (old_trackId == -1 && isTrack(previous_track) && getTrackById_const(previous_track)->hasMix(clipId)) {
// Clip is moved to another track
std::pair<MixInfo, MixInfo> mixData = getTrackById_const(previous_track)->getMixInfo(clipId);
bool mixGroupMove = false;
if (m_groups->isInGroup(clipId)) {
int parentGroup = m_groups->getRootId(clipId);
if (parentGroup > -1) {
std::unordered_set<int> sub = m_groups->getLeaves(parentGroup);
if (sub.count(mixData.first.firstClipId) > 0 && sub.count(mixData.first.secondClipId) > 0) {
mixGroupMove = true;
}
}
}
if (mixGroupMove) {
// We are moving a group on another track, delete and re-add
bool isAudio = getTrackById_const(previous_track)->isAudioTrack();
simple_move_mix = [this, previous_track, trackId, clipId, finalMove, mixData, isAudio]() {
getTrackById_const(previous_track)->syncronizeMixes(finalMove);
bool result = getTrackById_const(trackId)->createMix(mixData.first, isAudio);
return result;
};
simple_restore_mix = [this, previous_track, trackId, finalMove, mixData, isAudio]() {
bool result = true;
qDebug()<<"===================\n\nTRACKMOVE UNDO DELETE MIX!!!\n\n=======================";
if (finalMove) {
result = getTrackById_const(previous_track)->createMix(mixData.first, isAudio);
getTrackById_const(trackId)->syncronizeMixes(finalMove);
//getTrackById_const(previous_track)->syncronizeMixes(finalMove);
}
return result;
};
}
} else
if (!groupMove && isTrack(old_trackId) && getTrackById_const(old_trackId)->hasMix(clipId)) {
// Clip has a mix
std::pair<MixInfo, MixInfo> mixData = getTrackById_const(old_trackId)->getMixInfo(clipId);
if (mixData.first.firstClipId > -1) {
// We have a mix at clip start
update_mix = [this, mixData]() {
QModelIndex ix = makeClipIndexFromID(mixData.first.secondClipId);
emit dataChanged(ix, ix, {TimelineModel::StartRole,TimelineModel::MixRole});
bool mixGroupMove = false;
if (m_groups->isInGroup(clipId)) {
int parentGroup = m_groups->getRootId(clipId);
if (parentGroup > -1) {
std::unordered_set<int> sub = m_groups->getLeaves(parentGroup);
if (sub.count(mixData.first.firstClipId) > 0 && sub.count(mixData.first.secondClipId) > 0) {
mixGroupMove = true;
}
}
}
sync_mix = [this, old_trackId, finalMove]() {
getTrackById_const(old_trackId)->syncronizeMixes(finalMove);
return true;
};
if (old_trackId != trackId || position >= mixData.first.firstClipInOut.second) {
// Clip moved to another track, or outside of mix duration, delete mix
int subPlaylist = m_allClips[clipId]->getSubPlaylistIndex();
move_mix = [this, old_trackId, clipId, finalMove, subPlaylist]() {
bool result = getTrackById_const(old_trackId)->deleteMix(clipId, finalMove);
if (finalMove) {
//TODO: check if there is another mix on this clip
if (old_trackId == trackId) {
// We are moving a group on same track
if (finalMove && position >= mixData.first.firstClipInOut.second) {
int subPlaylist = m_allClips[clipId]->getSubPlaylistIndex();
update_playlist = [this, clipId, subPlaylist]() {
m_allClips[clipId]->setSubPlaylistIndex(subPlaylist == 0 ? 1 : 0);
}
return result;
};
restore_mix = [this, old_trackId, mixData, finalMove, subPlaylist]() {
if (finalMove) {
m_allClips[mixData.first.secondClipId]->setSubPlaylistIndex(subPlaylist);
}
bool result = getTrackById_const(old_trackId)->createMix({mixData.first.firstClipId,mixData.first.secondClipId}, {mixData.first.secondClipInOut.first, mixData.first.firstClipInOut.second - mixData.first.secondClipInOut.first});
return result;
};
} else if (old_trackId == trackId) {
// Clip moved on same track, resize mix
move_mix = [this, old_trackId, clipId, position]() {
return getTrackById_const(old_trackId)->resizeMixStart(clipId, position);
};
restore_mix = [this, old_trackId, clipId, mixData]() {
// Resize mix to oringinal start
return getTrackById_const(old_trackId)->resizeMixStart(clipId, mixData.first.secondClipInOut.first);
};
return true;
};
bool isAudio = getTrackById_const(old_trackId)->isAudioTrack();
update_playlist_undo = [this, clipId, subPlaylist, old_trackId, mixData, isAudio]() {
m_allClips[clipId]->setSubPlaylistIndex(subPlaylist);
bool result = getTrackById_const(old_trackId)->createMix(mixData.first, isAudio);
return result;
};
}
}
}
if (mixData.second.firstClipId > -1) {
// We have a mix at clip end
update_mix_end = [this, mixData]() {
QModelIndex ix = makeClipIndexFromID(mixData.second.secondClipId);
emit dataChanged(ix, ix, {TimelineModel::StartRole,TimelineModel::MixRole});
int clipDuration = mixData.second.firstClipInOut.second - mixData.second.firstClipInOut.first;
sync_mix = [this, old_trackId, finalMove]() {
getTrackById_const(old_trackId)->syncronizeMixes(finalMove);
return true;
};
int clipDuration = mixData.second.firstClipInOut.second - mixData.second.firstClipInOut.first;
if (old_trackId != trackId || (position + clipDuration <= mixData.second.secondClipInOut.first)) {
// Clip moved to another track, or outside of mix duration, delete mix
int subPlaylist = m_allClips[mixData.second.secondClipId]->getSubPlaylistIndex();
move_mix_end = [this, old_trackId, mixData, finalMove, subPlaylist]() {
bool result = getTrackById_const(old_trackId)->deleteMix(mixData.second.secondClipId, finalMove);
if (finalMove) {
//TODO: check if there is another mix on this clip
if (old_trackId == trackId) {
if (finalMove && (position + clipDuration <= mixData.second.secondClipInOut.first)) {
int subPlaylist = m_allClips[mixData.second.secondClipId]->getSubPlaylistIndex();
update_playlist = [this, mixData, subPlaylist, trackId]() {
m_allClips[mixData.second.secondClipId]->setSubPlaylistIndex(subPlaylist == 0 ? 1 : 0);
}
return result;
};
restore_mix_end = [this, old_trackId, mixData, finalMove, subPlaylist]() {
if (finalMove) {
bool result = getTrackById_const(trackId)->switchPlaylist(mixData.second.secondClipId, mixData.second.secondClipInOut.first, subPlaylist == 0 ? 1 : 0);
return result;
};
bool isAudio = getTrackById_const(trackId)->isAudioTrack();
update_playlist_undo = [this, mixData, subPlaylist, trackId, isAudio]() {
m_allClips[mixData.second.secondClipId]->setSubPlaylistIndex(subPlaylist);
}
bool result = getTrackById_const(old_trackId)->createMix({mixData.second.firstClipId,mixData.second.secondClipId}, {mixData.second.secondClipInOut.first, mixData.second.firstClipInOut.second - mixData.second.secondClipInOut.first});
return result;
};
} else if (old_trackId == trackId) {
// Clip moved on same track, resize mix
move_mix_end = [this, old_trackId, mixData, position, clipDuration]() {
return getTrackById_const(old_trackId)->resizeMixEnd(mixData.second.secondClipId, position + clipDuration);
};
restore_mix_end = [this, old_trackId, mixData]() {
// Resize mix to oringinal start
return getTrackById_const(old_trackId)->resizeMixEnd(mixData.second.secondClipId, mixData.second.firstClipInOut.second);
};
bool result = getTrackById_const(trackId)->switchPlaylist(mixData.second.secondClipId, mixData.second.secondClipInOut.first, subPlaylist);
result = result && getTrackById_const(trackId)->createMix(mixData.second, isAudio);
return result;
};
}
}
}
}
move_mix_end();
PUSH_LAMBDA(update_mix, local_undo);
PUSH_LAMBDA(update_mix_end, local_undo);
PUSH_LAMBDA(simple_restore_mix, local_undo);
if (finalMove) {
PUSH_LAMBDA(sync_mix, local_undo);
}
if (old_trackId != -1) {
if (notifyViewOnly) {
PUSH_LAMBDA(update_model, local_undo);
......@@ -705,9 +725,8 @@ bool TimelineModel::requestClipMove(int clipId, int trackId, int position, bool
return false;
}
}
move_mix();
UPDATE_UNDO_REDO(move_mix, restore_mix, local_undo, local_redo);
UPDATE_UNDO_REDO(move_mix_end, restore_mix_end, local_undo, local_redo);
update_playlist();
UPDATE_UNDO_REDO(update_playlist, update_playlist_undo, local_undo, local_redo);
ok = getTrackById(trackId)->requestClipInsertion(clipId, position, updateView, finalMove, local_undo, local_redo, groupMove);
if (!ok) {
qDebug() << "-------------\n\nINSERTION FAILED, REVERTING\n\n-------------------";
......@@ -715,12 +734,15 @@ bool TimelineModel::requestClipMove(int clipId, int trackId, int position, bool
Q_ASSERT(undone);
return false;
}
sync_mix();
update_model();
update_mix();
simple_move_mix();
PUSH_LAMBDA(simple_move_mix, local_redo);
if (finalMove) {
PUSH_LAMBDA(sync_mix, local_redo);
}
if (notifyViewOnly) {
PUSH_LAMBDA(update_model, local_redo);
PUSH_LAMBDA(update_mix, local_redo);
PUSH_LAMBDA(update_mix_end, local_redo);
}
UPDATE_UNDO_REDO(local_redo, local_undo, undo, redo);
return true;
......@@ -1852,9 +1874,11 @@ bool TimelineModel::requestGroupMove(int itemId, int groupId, int delta_track, i
Fun local_undo = []() { return true; };
Fun local_redo = []() { return true; };
std::vector< std::pair<int, int> > sorted_clips;
std::vector<int> sorted_clips_ids;
std::vector< std::pair<int, std::pair<int, int> > > sorted_compositions;
int lowerTrack = -1;
int upperTrack = -1;
QVector <int> tracksWithMix;
// Separate clips from compositions to sort and check source tracks
for (int affectedItemId : all_items) {
......@@ -1870,6 +1894,12 @@ bool TimelineModel::requestGroupMove(int itemId, int groupId, int delta_track, i
}
if (isClip(affectedItemId)) {
sorted_clips.push_back({affectedItemId, m_allClips[affectedItemId]->getPosition()});
sorted_clips_ids.push_back(affectedItemId);
int current_track_id = getClipTrackId(affectedItemId);
if (!tracksWithMix.contains(current_track_id) && getTrackById_const(current_track_id)->hasMix(affectedItemId)) {
// There is a mix, prepare for update
tracksWithMix << current_track_id;
}
} else {
sorted_compositions.push_back({affectedItemId, {m_allCompositions[affectedItemId]->getPosition(), getTrackMltIndex(m_allCompositions[affectedItemId]->getCurrentTrackId())}});
}
......@@ -1900,7 +1930,6 @@ bool TimelineModel::requestGroupMove(int itemId, int groupId, int delta_track, i
};
// Check if there is a track move
bool updatePositionOnly = false;
// Second step, reinsert clips at correct positions
int audio_delta, video_delta;
audio_delta = video_delta = delta_track;
......@@ -1955,7 +1984,6 @@ bool TimelineModel::requestGroupMove(int itemId, int groupId, int delta_track, i
if (delta_track == 0 && updateView) {
updateView = false;
allowViewRefresh = false;
updatePositionOnly = true;
update_model = [sorted_clips, sorted_compositions, finalMove, this]() {
QModelIndex modelIndex;
QVector<int> roles{StartRole};
......@@ -1975,6 +2003,7 @@ bool TimelineModel::requestGroupMove(int itemId, int groupId, int delta_track, i
}
std::unordered_map<int, int> old_track_ids, old_position, old_forced_track;
QMap<int, int> oldTrackIds;
// First, remove clips
if (delta_track != 0) {
// We delete our clips only if changing track
......@@ -1983,6 +2012,8 @@ bool TimelineModel::requestGroupMove(int itemId, int groupId, int delta_track, i
old_track_ids[item.first] = old_trackId;
if (old_trackId != -1) {
bool updateThisView = allowViewRefresh;
// Keep track of old track for mixes
oldTrackIds.insert(item.first, old_trackId);
ok = ok && getTrackById(old_trackId)->requestClipDeletion(item.first, updateThisView, finalMove, local_undo, local_redo, true, false);
old_position[item.first] = item.second;
if (!ok) {
......@@ -2007,6 +2038,13 @@ bool TimelineModel::requestGroupMove(int itemId, int groupId, int delta_track, i
audio_delta = -delta_track;
}
}
Fun sync_mix = [this, tracksWithMix, finalMove]() {
for (int tid : tracksWithMix) {
getTrackById_const(tid)->syncronizeMixes(finalMove);
}
return true;
};
// We need to insert depending on the move direction to avoid confusing the view
// std::reverse(std::begin(sorted_clips), std::end(sorted_clips));
bool updateThisView = allowViewRefresh;
......@@ -2054,6 +2092,7 @@ bool TimelineModel::requestGroupMove(int itemId, int groupId, int delta_track, i
}
}
}
PUSH_LAMBDA(sync_mix, local_undo);
for (const std::pair<int, int> &item : sorted_clips) {
int current_track_id = getClipTrackId(item.first);
if (!allowedTracks.isEmpty() && !allowedTracks.contains(current_track_id)) {
......@@ -2067,6 +2106,9 @@ bool TimelineModel::requestGroupMove(int itemId, int groupId, int delta_track, i
break;
}
}
sync_mix();
PUSH_LAMBDA(sync_mix, local_redo);
if (ok) {
for (const std::pair<int, std::pair<int, int>> &item : sorted_compositions) {
int current_track_id = getItemTrackId(item.first);
......@@ -2088,6 +2130,7 @@ bool TimelineModel::requestGroupMove(int itemId, int groupId, int delta_track, i
}
} else {
// Track changed
PUSH_LAMBDA(sync_mix, local_undo);
for (const std::pair<int, int> &item : sorted_clips) {
int current_track_id = old_track_ids[item.first];
int current_track_position = getTrackPosition(current_track_id);
......@@ -2101,7 +2144,7 @@ bool TimelineModel::requestGroupMove(int itemId, int groupId, int delta_track, i
std::advance(it, target_track_position);
int target_track = (*it)->getId();
int target_position = old_position[item.first] + delta_pos;
ok = ok && requestClipMove(item.first, target_track, target_position, moveMirrorTracks, updateThisView, finalMove, finalMove, local_undo, local_redo, true);
ok = ok && requestClipMove(item.first, target_track, target_position, moveMirrorTracks, updateThisView, finalMove, finalMove, local_undo, local_redo, true, oldTrackIds);
} else {
ok = false;
}
......@@ -2111,6 +2154,8 @@ bool TimelineModel::requestGroupMove(int itemId, int groupId, int delta_track, i
return false;
}
}
sync_mix();
PUSH_LAMBDA(sync_mix, local_redo);
for (const std::pair<int, std::pair<int, int> > &item : sorted_compositions) {
int current_track_id = old_track_ids[item.first];
int current_track_position = getTrackPosition(current_track_id);
......
......@@ -368,7 +368,7 @@ public:
/* Same function, but accumulates undo and redo, and doesn't check
for group*/
bool requestClipMove(int clipId, int trackId, int position, bool moveMirrorTracks, bool updateView, bool invalidateTimeline, bool finalMove, Fun &undo, Fun &redo, bool groupMove = false);
bool requestClipMove(int clipId, int trackId, int position, bool moveMirrorTracks, bool updateView, bool invalidateTimeline, bool finalMove, Fun &undo, Fun &redo, bool groupMove = false, QMap <int, int> moving_clips = QMap <int, int>());
bool requestCompositionMove(int transid, int trackId, int compositionTrack, int position, bool updateView, bool finalMove, Fun &undo, Fun &redo);
/* When timeline edit mode is insert or overwrite, we fake the move (as it will overlap existing clips, and only process the real move on drop */
......
......@@ -128,6 +128,20 @@ int TrackModel::getClipsCount()
return count;
}
bool TrackModel::switchPlaylist(int clipId, int position, int playlist)
{
QWriteLocker locker(&m_lock);
int source_playlist = playlist == 1 ? 0 : 1;
int target_clip = m_playlists[source_playlist].get_clip_index_at(position);
std::unique_ptr<Mlt::Producer> prod(m_playlists[source_playlist].replace_with_blank(target_clip));
if (auto ptr = m_parent.lock()) {
std::shared_ptr<ClipModel> clip = ptr->getClipPtr(clipId);
int index = m_playlists[playlist].insert_at(position, *clip, 1);
return index != -1;
}
return false;
}
Fun TrackModel::requestClipInsertion_lambda(int clipId, int position, bool updateView, bool finalMove, bool groupMove)
{
QWriteLocker locker(&m_lock);
......@@ -1538,14 +1552,16 @@ std::pair<MixInfo, MixInfo> TrackModel::getMixInfo(int clipId) const
return result;
}
bool TrackModel::deleteMix(int clipId, bool final)
bool TrackModel::deleteMix(int clipId, bool final, bool notify)
{
Q_ASSERT(m_sameCompositions.count(clipId) > 0);
if (auto ptr = m_parent.lock()) {
std::shared_ptr<ClipModel> movedClip(ptr->getClipPtr(clipId));
movedClip->setMixDuration(final ? 0 : 1);
QModelIndex ix = ptr->makeClipIndexFromID(clipId);
emit ptr->dataChanged(ix, ix, {TimelineModel::StartRole,TimelineModel::MixRole});
if (notify) {
std::shared_ptr<ClipModel> movedClip(ptr->getClipPtr(clipId));
movedClip->setMixDuration(final ? 0 : 1);
QModelIndex ix = ptr->makeClipIndexFromID(clipId);
emit ptr->dataChanged(ix, ix, {TimelineModel::StartRole,TimelineModel::MixRole});
}
if (final) {
Mlt::Transition &transition = *m_sameCompositions[clipId].get();
QScopedPointer<Mlt::Field> field(m_track->field());
......@@ -1563,6 +1579,35 @@ bool TrackModel::deleteMix(int clipId, bool final)
return false;
}
bool TrackModel::createMix(MixInfo info, bool isAudio)
{
if (m_sameCompositions.count(info.secondClipId) > 0) {
return false;
}
if (auto ptr = m_parent.lock()) {
// Insert mix transition
std::shared_ptr<ClipModel> movedClip(ptr->getClipPtr(info.secondClipId));
int in = movedClip->getPosition();
//int out = in + info.firstClipInOut.second - info.secondClipInOut.first;
int out = in + movedClip->getMixDuration();
movedClip->setMixDuration(out - in);
if (isAudio) {
std::shared_ptr<Mlt::Transition> t(new Mlt::Transition(*ptr->getProfile(), "mix"));
t->set_in_and_out(in, out);
m_track->plant_transition(*t.get(), 0, 1);
m_sameCompositions[info.secondClipId] = t;
} else {
std::shared_ptr<Mlt::Transition> t(new Mlt::Transition(*ptr->getProfile(), "luma"));
t->set_in_and_out(in, out);
m_track->plant_transition(*t.get(), 0, 1);
m_sameCompositions[info.secondClipId] = t;
}
m_mixList.insert(info.firstClipId, info.secondClipId);
return true;
}
return false;
}
bool TrackModel::createMix(std::pair<int, int> clipIds, std::pair<int, int> mixData)
{
if (m_sameCompositions.count(clipIds.second) > 0) {
......@@ -1571,6 +1616,8 @@ bool TrackModel::createMix(std::pair<int, int> clipIds, std::pair<int, int> mixD
if (auto ptr = m_parent.lock()) {
std::shared_ptr<ClipModel> movedClip(ptr->getClipPtr(clipIds.second));
movedClip->setMixDuration(mixData.second);
QModelIndex ix = ptr->makeClipIndexFromID(clipIds.second);
emit ptr->dataChanged(ix, ix, {TimelineModel::MixRole});
// Insert mix transition
if (isAudioTrack()) {
std::shared_ptr<Mlt::Transition> t(new Mlt::Transition(*ptr->getProfile(), "mix"));
......@@ -1589,38 +1636,58 @@ bool TrackModel::createMix(std::pair<int, int> clipIds, std::pair<int, int> mixD
return false;
}
bool TrackModel::resizeMixStart(int clipId, int position)
void TrackModel::syncronizeMixes(bool finalMove)
{
Q_ASSERT(m_sameCompositions.count(clipId) > 0);
if (auto ptr = m_parent.lock()) {
Mlt::Transition &transition = *m_sameCompositions[clipId].get();
std::shared_ptr<ClipModel> movedClip(ptr->getClipPtr(clipId));
int in = position;
int out = transition.get_out();
transition.set_in_and_out(in, out);
int updatedDuration = out - in;
movedClip->setMixDuration(qMax(1, updatedDuration));
return true;
QList<int>toDelete;
for( const auto& n : m_sameCompositions ) {
std::cout << "Key:[" << n.first << "] Value:[" << n.second << "]\n";
int secondClipId = n.first;
int firstClip = m_mixList.key(secondClipId, -1);
Q_ASSERT(firstClip > -1);
if (m_allClips.find(firstClip) == m_allClips.end() || m_allClips.find(secondClipId) == m_allClips.end()) {
// One of the clip was removed, delete the mix
qDebug()<<"=== CLIPS: "<<firstClip<<" / "<<secondClipId<<" ARE MISSING!!!!";
Mlt::Transition &transition = *m_sameCompositions[secondClipId].get();
QScopedPointer<Mlt::Field> field(m_track->field());
field->lock();
field->disconnect_service(transition);
field->unlock();
toDelete << secondClipId;
m_mixList.remove(firstClip);
continue;
}
// Asjust mix in/out
int mixIn = m_allClips[secondClipId]->getPosition();
int mixOut = m_allClips[firstClip]->getPosition() + m_allClips[firstClip]->getPlaytime();
if (mixOut <= mixIn) {
if (finalMove) {
// Delete mix
mixOut = mixIn;
} else {
mixOut = mixIn + 1;
}
}
Mlt::Transition &transition = *m_sameCompositions[secondClipId].get();
if (mixIn == mixOut) {
QScopedPointer<Mlt::Field> field(m_track->field());
field->lock();
field->disconnect_service(transition);
field->unlock();
toDelete << secondClipId;
m_mixList.remove(firstClip);
} else {
qDebug()<<"=== ADJUSTING MIX FOR CID: "<<secondClipId<<" TO: "<<mixIn<<" - "<<mixOut;
transition.set_in_and_out(mixIn, mixOut);
}
if (auto ptr = m_parent.lock()) {
ptr->getClipPtr(secondClipId)->setMixDuration(mixOut - mixIn);
QModelIndex ix = ptr->makeClipIndexFromID(secondClipId);
emit ptr->dataChanged(ix, ix, {TimelineModel::MixRole});
}
}
return false;
}
bool TrackModel::resizeMixEnd(int clipId, int position)
{
Q_ASSERT(m_sameCompositions.count(clipId) > 0);
if (auto ptr = m_parent.lock()) {
Mlt::Transition &transition = *m_sameCompositions[clipId].get();
std::shared_ptr<ClipModel> refClip(ptr->getClipPtr(clipId));
int in = transition.get_in();
int out = position;
transition.set_in_and_out(in, out);
int updatedDuration = out - in;
refClip->setMixDuration(qMax(1, updatedDuration));
QModelIndex ix = ptr->makeClipIndexFromID(clipId);
emit ptr->dataChanged(ix, ix, {TimelineModel::MixRole});
return true;
for (int i : toDelete) {
m_sameCompositions.erase(i);
}
return false;
}
int TrackModel::mixCount() const
......
......@@ -124,13 +124,14 @@ public:
/** @brief Get clip ids and in/out position for mixes in this clip */
std::pair<MixInfo, MixInfo> getMixInfo(int cid) const;
/** @brief Delete a mix composition */
bool deleteMix(int clipId, bool final);
/** @brief Create a mix composition */
bool deleteMix(int clipId, bool final, bool notify = true);
/** @brief Create a mix composition using clip ids */
bool createMix(std::pair<int, int> clipIds, std::pair<int, int> mixData);
/** @brief Resize a mix composition start pos */
bool resizeMixStart(int clipId, int offset);
/** @brief Resize a mix composition end pos */
bool resizeMixEnd(int clipId, int position);
/** @brief Create a mix composition using mix info */
bool createMix(MixInfo info, bool isAudio);
void syncronizeMixes(bool finalMove);
/** @brief Switch a clip from one playlist to the other */
bool switchPlaylist(int clipId, int position, int playlist);
protected:
/* @brief This will lock the track: it will no longer allow insertion/deletion/resize of items
......
......@@ -338,7 +338,13 @@ Rectangle {
anchors.bottom: parent.bottom
width: clipRoot.mixDuration * timeScale
color: 'red'
opacity: 0.5
//opacity: 0.5
Text {
text: clipRoot.mixDuration
anchors {
bottom: parent.bottom
}
}
}
Repeater {
......
......@@ -47,7 +47,7 @@ Item{
id: trackModel
delegate: Item {
property var itemModel : model
z: model.clipType == ProducerType.Composition ? 5 : 0
z: model.clipType == ProducerType.Composition ? 5 : model.mixDuration > 0 ? 2 : 0
Loader {
id: loader
Binding {
......
......@@ -123,19 +123,6 @@ TEST_CASE("Simple Mix", "[SameTrackMix]")
state0();
}
SECTION("Create and delete mix on AV clips")
{
state0();
REQUIRE(timeline->mixClip(cid2));
state1();
undoStack->undo();
state0();
undoStack->redo();
state1();
undoStack->undo();
state0();
}
SECTION("Create mix on color clips and move main (right side) clip")
{
state0();
......@@ -163,7 +150,7 @@ TEST_CASE("Simple Mix", "[SameTrackMix]")
state0();
}
SECTION("Create mix on color clip and left side clip")
SECTION("Create mix on color clip and move left side clip")
{
state0();
REQUIRE(timeline->mixClip(cid4));
......@@ -188,6 +175,44 @@ TEST_CASE("Simple Mix", "[SameTrackMix]")
state0();
}
SECTION("Create mix on color clips and group move")
{
state0();
REQUIRE(timeline->mixClip(cid4));
state2();
// Move clip inside mix zone, should resize the mix