Easy selection and deletion of clip mix

parent 7f3f3365
......@@ -47,6 +47,7 @@ ClipModel::ClipModel(const std::shared_ptr<TimelineModel> &parent, std::shared_p
, m_positionOffset(0)
, m_subPlaylistIndex(0)
, m_mixDuration(0)
, m_mixCutPos(0)
{
m_producer->set("kdenlive:id", binClipId.toUtf8().constData());
m_producer->set("_kdenlive_cid", m_id);
......@@ -651,6 +652,18 @@ void ClipModel::setPosition(int pos)
m_clipMarkerModel->updateSnapModelPos(pos);
}
void ClipModel::setMixDuration(int mix, int cutOffset)
{
if (mix == 0) {
// Deleting a mix
m_mixCutPos = 0;
} else {
// Creating a new mix
m_mixCutPos = cutOffset;
}
m_mixDuration = mix;
}
void ClipModel::setMixDuration(int mix)
{
m_mixDuration = mix;
......@@ -661,6 +674,11 @@ int ClipModel::getMixDuration() const
return m_mixDuration;
}
int ClipModel::getMixCutPosition() const
{
return m_mixCutPos;
}
void ClipModel::setInOut(int in, int out)
{
MoveableItem::setInOut(in, out);
......
......@@ -101,8 +101,10 @@ public:
void setFakeTrackId(int fid);
int getFakePosition() const;
void setFakePosition(int fid);
void setMixDuration(int mix, int offset);
void setMixDuration(int mix);
int getMixDuration() const;
int getMixCutPosition() const;
void setGrab(bool grab) override;
void setSelected(bool sel) override;
......@@ -253,6 +255,8 @@ protected:
// Duration of a same track mix.
int m_mixDuration;
// Position of the original cut, relative to mix right side
int m_mixCutPos;
};
#endif
......@@ -198,6 +198,7 @@ QHash<int, QByteArray> TimelineItemModel::roleNames() const
roles[FakePositionRole] = "fakePosition";
roles[StartRole] = "start";
roles[MixRole] = "mixDuration";
roles[MixCutRole] = "mixCut";
roles[DurationRole] = "duration";
roles[MaxDurationRole] = "maxDuration";
roles[MarkersRole] = "markers";
......@@ -336,6 +337,8 @@ QVariant TimelineItemModel::data(const QModelIndex &index, int role) const
return clip->fadeOut();
case MixRole:
return clip->getMixDuration();
case MixCutRole:
return clip->getMixCutPosition();
case ReloadThumbRole:
return clip->forceThumbReload;
case PositionOffsetRole:
......
......@@ -2642,6 +2642,20 @@ int TimelineModel::requestItemSpeedChange(int itemId, int size, bool right, int
return proposed_size > 0 ? proposed_size : size;
}
bool TimelineModel::removeMix(int cid)
{
Fun undo = []() { return true; };
Fun redo = []() { return true; };
int tid = getItemTrackId(cid);
MixInfo mixData = getTrackById_const(tid)->getMixInfo(cid).first;
bool res = getTrackById(tid)->requestRemoveMix({mixData.firstClipId,mixData.secondClipId}, undo, redo);
if (res) {
PUSH_UNDO(undo, redo, i18n("Remove mix"));
} else {
pCore->displayMessage(i18n("Removing mix failed"), InformationMessage, 500);
}
return res;
}
int TimelineModel::requestItemResize(int itemId, int size, bool right, bool logUndo, int snapDistance, bool allowSingleResize)
{
......@@ -2660,6 +2674,7 @@ int TimelineModel::requestItemResize(int itemId, int size, bool right, bool logU
Fun undo = []() { return true; };
Fun redo = []() { return true; };
Fun sync_mix = []() { return true; };
Fun adjust_mix = []() { return true; };
Fun sync_end_mix = []() { return true; };
Fun sync_end_mix_undo = []() { return true; };
PUSH_LAMBDA(sync_mix, undo);
......@@ -2673,24 +2688,32 @@ int TimelineModel::requestItemResize(int itemId, int size, bool right, bool logU
if (getTrackById_const(tid)->hasEndMix(itemId)) {
tracksWithMixes << tid;
std::pair<MixInfo, MixInfo> mixData = getTrackById_const(tid)->getMixInfo(itemId);
if (in + size <= mixData.second.secondClipInOut.first) {
if (in + size <= mixData.second.secondClipInOut.first + m_allClips[mixData.second.secondClipId]->getMixDuration() - m_allClips[mixData.second.secondClipId]->getMixCutPosition()) {
// Clip resized outside of mix zone, mix will be deleted
bool switchPlaylist = getTrackById_const(tid)->hasEndMix(mixData.second.secondClipId) == false && m_allClips[mixData.second.secondClipId]->getSubPlaylistIndex() == 1;
Fun sync_mix_undo = [this, tid, mixData]() {
getTrackById_const(tid)->createMix(mixData.second, getTrackById_const(tid)->isAudioTrack());
getTrackById_const(tid)->syncronizeMixes(true);
bool res = removeMix(mixData.second.secondClipId);
if (res) {
return m_allClips[itemId]->getPlaytime();
}
return -1;
} else {
// Mix was resized, update cut position
int currentMixDuration = m_allClips[mixData.second.secondClipId]->getMixDuration();
int currentMixCut = m_allClips[mixData.second.secondClipId]->getMixCutPosition();
adjust_mix = [this, tid, mixData, currentMixCut, itemId]() {
MixInfo secondMixData = getTrackById_const(tid)->getMixInfo(itemId).second;
int offset = mixData.second.firstClipInOut.second - secondMixData.firstClipInOut.second;
m_allClips[secondMixData.secondClipId]->setMixDuration(secondMixData.firstClipInOut.second - secondMixData.secondClipInOut.first, currentMixCut - offset);
QModelIndex ix = makeClipIndexFromID(secondMixData.secondClipId);
emit dataChanged(ix, ix, {TimelineModel::MixRole,TimelineModel::MixCutRole});
return true;
};
// Move second clip to first playlist again
if (switchPlaylist) {
sync_end_mix = [this, tid, mixData]() {
return getTrackById_const(tid)->switchPlaylist(mixData.second.secondClipId, mixData.second.secondClipInOut.first, 1, 0);
};
sync_end_mix_undo = [this, tid, mixData]() {
return getTrackById_const(tid)->switchPlaylist(mixData.second.secondClipId, m_allClips[mixData.second.secondClipId]->getPosition(), 0, 1);
};
}
PUSH_LAMBDA(sync_mix_undo, undo);
Fun adjust_mix_undo = [this, mixData, currentMixCut, currentMixDuration]() {
m_allClips[mixData.second.secondClipId]->setMixDuration(currentMixDuration, currentMixCut);
QModelIndex ix = makeClipIndexFromID(mixData.second.secondClipId);
emit dataChanged(ix, ix, {TimelineModel::MixRole,TimelineModel::MixCutRole});
return true;
};
PUSH_LAMBDA(adjust_mix_undo, undo);
}
}
} else if (getTrackById_const(tid)->hasStartMix(itemId)) {
......@@ -2839,7 +2862,9 @@ int TimelineModel::requestItemResize(int itemId, int size, bool right, bool logU
if (isClip(itemId)) {
sync_end_mix();
sync_mix();
adjust_mix();
PUSH_LAMBDA(sync_end_mix, redo);
PUSH_LAMBDA(adjust_mix, redo);
PUSH_LAMBDA(sync_mix, redo);
PUSH_LAMBDA(undo, sync_end_mix_undo);
PUSH_UNDO(sync_end_mix_undo, redo, i18n("Resize clip"))
......@@ -2867,19 +2892,21 @@ bool TimelineModel::requestItemResize(int itemId, int size, bool right, bool log
if (getTrackById_const(tid)->hasEndMix(itemId)) {
hasMix = true;
/*std::pair<MixInfo, MixInfo> mixData = getTrackById_const(tid)->getMixInfo(itemId);
int mixDuration = mixData.second.firstClipInOut.second - (mixData.second.secondClipInOut.second - size);
m_allClips[mixData.second.secondClipId]->setMixDuration(mixDuration);
int mixDuration = mixData.second.firstClipInOut.first + size - mixData.second.secondClipInOut.second;
qDebug() << "- - - -\nGOT RIGHT NEW MIX DURATION: "<<mixDuration<<"\nCURRENT CUT POS: "<<m_allClips[mixData.second.secondClipId]->getMixCutPosition()<<", CURRENT MIX DURATION: "<<m_allClips[mixData.second.secondClipId]->getMixDuration();
int mixCut = m_allClips[mixData.second.secondClipId]->getMixCutPosition() + m_allClips[mixData.second.secondClipId]->getMixDuration() - mixDuration;
m_allClips[mixData.second.secondClipId]->setMixDuration(mixDuration, mixCut);
QModelIndex ix = makeClipIndexFromID(mixData.second.secondClipId);
emit dataChanged(ix, ix, {TimelineModel::MixRole});*/
emit dataChanged(ix, ix, {TimelineModel::MixRole,TimelineModel::MixCutRole});*/
}
} else if (getTrackById_const(tid)->hasStartMix(itemId)) {
hasMix = true;
std::pair<MixInfo, MixInfo> mixData = getTrackById_const(tid)->getMixInfo(itemId);
// We have a mix at clip start
int mixDuration = mixData.first.firstClipInOut.second - (mixData.first.secondClipInOut.second - size);
m_allClips[itemId]->setMixDuration(qMax(1, mixDuration));
m_allClips[itemId]->setMixDuration(qMax(1, mixDuration), m_allClips[itemId]->getMixCutPosition());
QModelIndex ix = makeClipIndexFromID(itemId);
emit dataChanged(ix, ix, {TimelineModel::MixRole});
emit dataChanged(ix, ix, {TimelineModel::MixRole,TimelineModel::MixCutRole});
}
}
} else {
......@@ -4498,6 +4525,10 @@ bool TimelineModel::requestClearSelection(bool onDeletion)
{
QWriteLocker locker(&m_lock);
TRACE();
if (m_selectedMix > -1) {
m_selectedMix = -1;
emit selectedMixChanged();
}
if (m_currentSelection == -1) {
TRACE_RES(true);
return true;
......@@ -4536,6 +4567,14 @@ bool TimelineModel::requestClearSelection(bool onDeletion)
TRACE_RES(true);
return true;
}
void TimelineModel::requestMixSelection(int cid)
{
requestClearSelection();
m_selectedMix = cid;
emit selectedMixChanged();
}
void TimelineModel::requestClearSelection(bool onDeletion, Fun &undo, Fun &redo)
{
Fun operation = [this, onDeletion]() {
......
......@@ -117,7 +117,8 @@ public:
IsProxyRole, /// clip only
ServiceRole, /// clip only
StartRole, /// clip only
MixRole, /// clip only
MixRole, /// clip only, the duration of the mix
MixCutRole, /// The original cut position for the mix
BinIdRole, /// clip only
TrackIdRole,
FakeTrackIdRole,
......@@ -420,6 +421,7 @@ public:
/** @brief Plant a same track composition in track tid
*/
void plantMix(int tid, Mlt::Transition &t);
bool removeMix(int cid);
protected:
/* @brief Creates a new clip instance without inserting it.
......@@ -671,6 +673,11 @@ public:
Q_INVOKABLE bool requestClearSelection(bool onDeletion = false);
// same function with undo/redo accumulation
void requestClearSelection(bool onDeletion, Fun &undo, Fun &redo);
/** @brief Select a given mix in timeline
@param cid clip id
*/
Q_INVOKABLE void requestMixSelection(int cid);
/** @brief Add the given item to the selection
If @param clear is true, the selection is first cleared
......@@ -791,6 +798,8 @@ signals:
/* @brief Signal sent whenever the selection changes */
void selectionChanged();
/* @brief Signal sent whenever the selected mix changes */
void selectedMixChanged();
/* @brief Signal when a track is deleted so we make sure we don't store its id */
void checkTrackDeletion(int tid);
/* @brief Emitted when a clip is deleted to check if it was not used in timeline qml */
......@@ -834,6 +843,7 @@ protected:
// item, or, finally, the id of a group which is not of type selection. The last case happens when the selection exactly matches an existing group
// (in that case we cannot further group it because the selection would have only one child, which is prohibited by design)
int m_currentSelection = -1;
int m_selectedMix = -1;
// The index of the temporary overlay track in tractor, or -1 if not connected
int m_overlayTrackCount;
......
......@@ -628,7 +628,8 @@ Fun TrackModel::requestClipResize_lambda(int clipId, int in, int out, bool right
int blank = -1;
int other_blank_end = getBlankEnd(clip_position, (target_track + 1) % 2);
if (right) {
if (target_clip == m_playlists[target_track].count() - 1 && (hasMix || other_blank_end >= out)) { // clip is last, it can always be extended
if (target_clip == m_playlists[target_track].count() - 1 && (hasMix || other_blank_end >= out)) {
// clip is last, it can always be extended
return [this, target_clip, target_track, in, out, update_snaps, clipId]() {
if (isLocked()) return false;
// color, image and title clips can have unlimited resize
......@@ -652,7 +653,6 @@ Fun TrackModel::requestClipResize_lambda(int clipId, int in, int out, bool right
return err == 0;
};
}
blank = target_clip + 1;
} else {
if (target_clip == 0) {
......@@ -697,7 +697,6 @@ Fun TrackModel::requestClipResize_lambda(int clipId, int in, int out, bool right
return err == 0;
};
} else {
qDebug()<<"==== NOPE ABORT RESIZE";
}
}
return []() { return false; };
......@@ -1431,6 +1430,107 @@ bool TrackModel::isAvailable(int position, int duration, int playlist)
return m_playlists[playlist].is_blank(start_clip);
}
bool TrackModel::requestRemoveMix(std::pair<int, int> clipIds, Fun &undo, Fun &redo)
{
int mixDuration;
int mixCutPos;
int endPos;
int firstInPos;
int secondInPos;
int mixPosition;
int src_track = 0;
bool clipHasEndMix = false;
if (auto ptr = m_parent.lock()) {
// The clip that will be moved to playlist 1
std::shared_ptr<ClipModel> secondClip(ptr->getClipPtr(clipIds.second));
mixDuration = secondClip->getMixDuration();
mixCutPos = secondClip->getMixCutPosition();
mixPosition = secondClip->getPosition();
secondInPos = mixPosition + mixDuration - mixCutPos;
firstInPos = ptr->getClipPtr(clipIds.first)->getPosition();
endPos = mixPosition + secondClip->getPlaytime();
clipHasEndMix = hasEndMix(clipIds.second);
src_track = secondClip->getSubPlaylistIndex();
} else {
return false;
}
bool result = false;
Fun local_undo = []() { return true; };
Fun local_redo = []() { return true; };
if (auto ptr = m_parent.lock()) {
// Resize main clip
result = ptr->getClipPtr(clipIds.second)->requestResize(endPos - secondInPos, false, local_undo, local_redo, true, clipHasEndMix);
// Resize first part clip
result = result && ptr->getClipPtr(clipIds.first)->requestResize(secondInPos - firstInPos, true, local_undo, local_redo, true, true);
}
if (result) {
PUSH_LAMBDA(local_redo, redo);
Fun replay = [this, clipIds, secondInPos, clipHasEndMix, src_track]() {
if (src_track == 1 && !clipHasEndMix) {
// Revert clip to playlist 0 since it has no mix
switchPlaylist(clipIds.second, secondInPos, 1, 0);
}
// Delete transition
Mlt::Transition &transition = *m_sameCompositions[clipIds.second].get();
QScopedPointer<Mlt::Field> field(m_track->field());
field->lock();
field->disconnect_service(transition);
field->unlock();
m_sameCompositions.erase(clipIds.second);
m_mixList.remove(clipIds.first);
if (auto ptr = m_parent.lock()) {
std::shared_ptr<ClipModel> movedClip(ptr->getClipPtr(clipIds.second));
movedClip->setMixDuration(0);
/*QModelIndex ix = ptr->makeClipIndexFromID(clipIds.first);
emit ptr->dataChanged(ix, ix, {TimelineModel::DurationRole});*/
QModelIndex ix2 = ptr->makeClipIndexFromID(clipIds.second);
emit ptr->dataChanged(ix2, ix2, {TimelineModel::MixRole,TimelineModel::MixCutRole});
}
return true;
};
replay();
Fun reverse = [this, clipIds, mixDuration, mixPosition, mixCutPos, secondInPos, clipHasEndMix, src_track]() {
// First restore correct playlist
if (src_track == 1 && !clipHasEndMix) {
// Revert clip to playlist 0 since it has no mix
switchPlaylist(clipIds.second, secondInPos, 0, 1);
}
// Build mix
if (auto ptr = m_parent.lock()) {
std::shared_ptr<ClipModel> movedClip(ptr->getClipPtr(clipIds.second));
movedClip->setMixDuration(mixDuration, mixCutPos);
// Insert mix transition
if (isAudioTrack()) {
std::shared_ptr<Mlt::Transition> t(new Mlt::Transition(*ptr->getProfile(), "mix"));
t->set_in_and_out(mixPosition, mixPosition + mixDuration);
if (src_track == 0) {
t->set("reverse", 1);
}
m_track->plant_transition(*t.get(), 0, 1);
m_sameCompositions[clipIds.second] = t;
} else {
std::shared_ptr<Mlt::Transition> t(new Mlt::Transition(*ptr->getProfile(), "luma"));
t->set_in_and_out(mixPosition, mixPosition + mixDuration);
if (src_track == 0) {
t->set("reverse", 1);
}
m_track->plant_transition(*t.get(), 0, 1);
m_sameCompositions[clipIds.second] = t;
}
m_mixList.insert(clipIds.first, clipIds.second);
QModelIndex ix2 = ptr->makeClipIndexFromID(clipIds.second);
emit ptr->dataChanged(ix2, ix2, {TimelineModel::MixRole,TimelineModel::MixCutRole});
}
return true;
};
PUSH_LAMBDA(replay, redo);
PUSH_LAMBDA(reverse, undo);
PUSH_LAMBDA(local_undo, undo);
return true;
}
return false;
}
bool TrackModel::requestClipMix(std::pair<int, int> clipIds, int mixDuration, bool updateView, bool finalMove, Fun &undo, Fun &redo, bool groupMove)
{
QWriteLocker locker(&m_lock);
......@@ -1442,6 +1542,7 @@ bool TrackModel::requestClipMix(std::pair<int, int> clipIds, int mixDuration, bo
int firstClipDuration;
int source_track;
int mixPosition;
int secondClipCut = 0;
int dest_track = 1;
bool remixPlaylists = false;
bool clipHasEndMix = false;
......@@ -1473,6 +1574,7 @@ bool TrackModel::requestClipMix(std::pair<int, int> clipIds, int mixDuration, bo
}
}
mixDuration = qMin(mixDuration, maxPos - mixPosition);
secondClipCut = maxPos - secondClipPos;
} else {
// Error, timeline unavailable
return false;
......@@ -1498,7 +1600,6 @@ bool TrackModel::requestClipMix(std::pair<int, int> clipIds, int mixDuration, bo
}
rearrange_playlists = [this, rearrangedPlaylists]() {
bool result = true;
// First, remove all clips on playlist 0
QMapIterator<int, int> i(rearrangedPlaylists);
while (i.hasNext()) {
......@@ -1548,7 +1649,6 @@ bool TrackModel::requestClipMix(std::pair<int, int> clipIds, int mixDuration, bo
} else return false;
};
rearrange_playlists_undo = [this, rearrangedPlaylists]() {
bool result = true;
// First, remove all clips on playlist 1
QMapIterator<int, int> i(rearrangedPlaylists);
while (i.hasNext()) {
......@@ -1599,10 +1699,10 @@ bool TrackModel::requestClipMix(std::pair<int, int> clipIds, int mixDuration, bo
};
}
// Create mix compositing
Fun build_mix = [clipIds, mixPosition, mixDuration, dest_track, this]() {
Fun build_mix = [clipIds, mixPosition, mixDuration, dest_track, secondClipCut, this]() {
if (auto ptr = m_parent.lock()) {
std::shared_ptr<ClipModel> movedClip(ptr->getClipPtr(clipIds.second));
movedClip->setMixDuration(mixDuration);
movedClip->setMixDuration(mixDuration, secondClipCut);
// Insert mix transition
if (isAudioTrack()) {
std::shared_ptr<Mlt::Transition> t(new Mlt::Transition(*ptr->getProfile(), "mix"));
......@@ -1632,7 +1732,7 @@ bool TrackModel::requestClipMix(std::pair<int, int> clipIds, int mixDuration, bo
std::shared_ptr<ClipModel> movedClip(ptr->getClipPtr(clipIds.second));
movedClip->setMixDuration(0);
QModelIndex ix = ptr->makeClipIndexFromID(clipIds.second);
emit ptr->dataChanged(ix, ix, {TimelineModel::StartRole,TimelineModel::MixRole});
emit ptr->dataChanged(ix, ix, {TimelineModel::StartRole,TimelineModel::MixRole,TimelineModel::MixCutRole});
QScopedPointer<Mlt::Field> field(m_track->field());
field->lock();
field->disconnect_service(transition);
......@@ -1661,7 +1761,7 @@ bool TrackModel::requestClipMix(std::pair<int, int> clipIds, int mixDuration, bo
result = ptr->getClipPtr(clipIds.second)->requestResize(secondClipPos + secondClipDuration - mixPosition, false, local_undo, local_redo, clipHasEndMix);
result = result && ptr->getClipPtr(clipIds.first)->requestResize(mixPosition + mixDuration - firstClipPos, true, local_undo, local_redo, false);
QModelIndex ix = ptr->makeClipIndexFromID(clipIds.second);
emit ptr->dataChanged(ix, ix, {TimelineModel::StartRole,TimelineModel::MixRole});
emit ptr->dataChanged(ix, ix, {TimelineModel::StartRole,TimelineModel::MixRole,TimelineModel::MixCutRole});
}
}
return result;
......@@ -1747,7 +1847,7 @@ bool TrackModel::deleteMix(int clipId, bool final, bool 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});
emit ptr->dataChanged(ix, ix, {TimelineModel::StartRole,TimelineModel::MixRole,TimelineModel::MixCutRole});
}
if (final) {
Mlt::Transition &transition = *m_sameCompositions[clipId].get();
......@@ -1811,7 +1911,7 @@ bool TrackModel::createMix(std::pair<int, int> clipIds, std::pair<int, int> mixD
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});
emit ptr->dataChanged(ix, ix, {TimelineModel::MixRole,TimelineModel::MixCutRole});
bool reverse = movedClip->getSubPlaylistIndex() == 0;
// Insert mix transition
if (isAudioTrack()) {
......@@ -1882,7 +1982,7 @@ void TrackModel::syncronizeMixes(bool finalMove)
if (auto ptr = m_parent.lock()) {
ptr->getClipPtr(secondClipId)->setMixDuration(mixOut - mixIn);
QModelIndex ix = ptr->makeClipIndexFromID(secondClipId);
emit ptr->dataChanged(ix, ix, {TimelineModel::MixRole});
emit ptr->dataChanged(ix, ix, {TimelineModel::MixRole,TimelineModel::MixCutRole});
}
}
for (int i : toDelete) {
......
......@@ -119,6 +119,8 @@ public:
// TODO make protected
QVariant getProperty(const QString &name) const;
void setProperty(const QString &name, const QString &value);
/** @brief Remove a composition between 2 same track clips */
bool requestRemoveMix(std::pair<int, int> clipIds, Fun &undo, Fun &redo);
/** @brief Create a composition between 2 same track clips */
bool requestClipMix(std::pair<int, int> clipIds, int mixDuration, bool updateView, bool finalMove, Fun &undo, Fun &redo, bool groupMove);
/** @brief Get clip ids and in/out position for mixes in this clip */
......
......@@ -34,6 +34,7 @@ Rectangle {
property bool isProxy: false
property int modelStart
property int mixDuration: 0
property int mixCut: 0
property real scrollX: 0
property int inPoint: 0
property int outPoint: 0
......@@ -353,7 +354,29 @@ Rectangle {
anchors.bottom: parent.bottom
text: clipRoot.mixDuration
}
opacity: 0.7
opacity: mixArea.containsMouse || root.selectedMix == clipRoot.clipId ? 1 : 0.5
border.color: root.selectedMix == clipRoot.clipId ? root.selectionColor : "transparent"
border.width: 2
MouseArea {
// Mix click mouse area
id: mixArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onPressed: {
console.log('MIX CLICKED: ', clipRoot.clipId)
controller.requestMixSelection(clipRoot.clipId);
}
}
Rectangle {
id: mixCutPos
anchors.right: parent.right
anchors.rightMargin: clipRoot.mixCut * clipRoot.timeScale
anchors.top: parent.top
anchors.bottom: parent.bottom
width: 2
color: "#000"
}
MouseArea {
// Left resize handle
id: trimInMixArea
......@@ -376,6 +399,7 @@ Rectangle {
mixOut.opacity = 1
anchors.left = undefined
parent.anchors.right = undefined
mixCutPos.anchors.right = undefined
}
onReleased: {
controller.resizeStartMix(clipRoot.clipId, Math.round(Math.max(0, x) / clipRoot.timeScale))
......@@ -386,6 +410,7 @@ Rectangle {
anchors.left = parent.left
parent.anchors.right = mixContainer.right
mixOut.opacity = 0.5
mixCutPos.anchors.right = mixCutPos.parent.right
}
onPositionChanged: {
if (mouse.buttons === Qt.LeftButton) {
......@@ -1053,7 +1078,7 @@ Rectangle {
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
drag.target: fadeInMouseArea
drag.minimumX: - Math.ceil(width / 2)
drag.minimumX: - 2*root.baseUnit
drag.maximumX: container.width - width / 2
drag.axis: Drag.XAxis
drag.smoothed: false
......@@ -1074,13 +1099,22 @@ Rectangle {
onReleased: {
root.autoScrolling = timeline.autoScroll
fadeInTriangle.opacity = 0.4
timeline.adjustFade(clipRoot.clipId, 'fadein', clipRoot.fadeIn, startFadeIn)
if (startFadeIn == 0 && x < 0) {
timeline.mixClip(clipRoot.clipId)
} else {
timeline.adjustFade(clipRoot.clipId, 'fadein', clipRoot.fadeIn, startFadeIn)
}
bubbleHelp.hide()
anchors.left = container.left
}
onPositionChanged: {
if (mouse.buttons === Qt.LeftButton) {
var delta = Math.round((x + width / 2) / timeScale)
if (delta < root.baseUnit) {
fadeInControl.radius = 0
} else {
fadeInControl.radius = fadeInControl.width / 2
}
var duration = Math.max(0, delta)
duration = Math.min(duration, clipRoot.clipDuration - 1)
if (duration != clipRoot.fadeIn) {
......
......@@ -47,6 +47,7 @@ Item{
id: trackModel
delegate: Item {
property var itemModel : model
property bool clipItem: isClip(model.clipType)
z: model.clipType == ProducerType.Composition ? 5 : model.mixDuration > 0 ? model.start / 25 : 0
Loader {
id: loader
......@@ -60,19 +61,25 @@ Item{
target: loader.item
property: "fakeTid"
value: model.fakeTrackId
when: loader.status == Loader.Ready && loader.item && isClip(model.clipType)
when: loader.status == Loader.Ready && loader.item && clipItem
}
Binding {
target: loader.item
property: "fakePosition"
value: model.fakePosition
when: loader.status == Loader.Ready && loader.item && isClip(model.clipType)
when: loader.status == Loader.Ready && loader.item && clipItem
}
Binding {
target: loader.item
property: "mixDuration"
value: model.mixDuration
when: loader.status == Loader.Ready && loader.item && isClip(model.clipType)
when: loader.status == Loader.Ready && loader.item && clipItem
}
Binding {
target: loader.item
property: "mixCut"
value: model.mixCut
when: loader.status == Loader.Ready && loader.item && clipItem
}
Binding {
target