Commit d0ad13a2 authored by luyikei's avatar luyikei

Timeline: Implement linking clips

parent 06cc789c
......@@ -29,6 +29,8 @@ Rectangle {
property int begin
property int end
property string uuid
property string linkedClip // Uuid
property bool linked: false
property string type
property bool selected: false
......@@ -41,6 +43,7 @@ Rectangle {
position = clipInfo["position"];
begin = clipInfo["begin"];
end = clipInfo["end"];
linkedClip = clipInfo["linkedClip"];
}
onPositionChanged: {
......@@ -55,6 +58,30 @@ Rectangle {
clipInfo["end"] = end;
}
onLinkedClipChanged: {
clipInfo["linkedClip"] = linkedClip;
if ( linkedClip ) {
linked = true;
var linkedClipItem = findClipItem( linkedClip );
if ( linkedClipItem )
linkedClipItem.linked = true;
}
else
linked = false;
}
onLinkedChanged: {
selectLinkedClip();
if ( !linkedClip )
return;
if ( linked === true )
findClipItem( linkedClip ).linked = true;
else
findClipItem( linkedClip ).linked = false;
}
function setPixelPosition( pixels )
{
if ( pixels >= 0 )
......@@ -81,6 +108,11 @@ Rectangle {
workflow.resizeClip( uuid, begin, end, position )
}
function selectLinkedClip() {
if ( selected === true && linked === true && linkedClip )
findClipItem( linkedClip ).selected = true;
}
Component.onCompleted: {
selected = true;
newTrackId = trackId;
......@@ -205,7 +237,7 @@ Rectangle {
ClipContextMenu {
id: clipContextMenu
uuid: clip.uuid
clip: clip
}
onYChanged: {
......@@ -225,11 +257,11 @@ Rectangle {
var group = findGroup( uuid );
for ( var i = 0; i < ( group ? group.length : 0 ); ++i ) {
for ( var j = 0; j < allClips.length; ++j ) {
if ( group[i] === allClips[j].uuid )
allClips[j].selected = true;
}
var clipItem = findClipItem( group[i] );
if ( clipItem )
clipItem.selected = true;
}
selectLinkedClip();
}
else {
for ( i = 0; i < selectedClips.length; ++i )
......
......@@ -5,7 +5,7 @@ Menu {
id: clipContextMenu
title: "Edit"
property string uuid
property var clip
property bool isGrouped
MenuItem {
......@@ -15,8 +15,8 @@ Menu {
if ( selectedClips.length <= 1 )
return;
if ( isGrouped ) {
removeGroup( uuid );
if ( isGrouped === true ) {
removeGroup( clip.uuid );
}
else {
var l = [];
......@@ -28,7 +28,15 @@ Menu {
}
}
MenuItem {
text: clip.linked ? "Unlink" : "Link"
onTriggered: {
clip.linked = !clip.linked;
}
}
onAboutToShow: {
isGrouped = findGroup( uuid );
isGrouped = findGroup( clip.uuid );
}
}
......@@ -81,24 +81,111 @@ Item {
property int lastX: 0
property int deltaX: 0
onContainsDragChanged: {
if ( containsDrag === true )
lastX = drag.x;
function findNewPosition( newX, target, useMagneticMode ) {
var oldX = target.pixelPosition();
if ( useMagneticMode === true ) {
var leastDestance = 25;
// Check two times
for ( var k = 0; k < 2; ++k ) {
for ( var j = 0; j < markers.count; ++j ) {
var mx = ftop( markers.get( j ).position );
if ( Math.abs( newX - mx ) < leastDestance ) {
leastDestance = Math.abs( newX - mx );
newX = mx;
}
else if ( Math.abs( newX + target.width - mx ) < leastDestance ) {
leastDestance = Math.abs( newX + target.width - mx );
newX = mx - target.width;
}
}
}
}
// Collision detection
var isCollided = true;
var currentTrack = trackContainer( target.type )["tracks"].get( target.newTrackId );
if ( currentTrack )
var clips = currentTrack["clips"];
else
clips = [];
for ( j = 0; j < clips.count + 2 && isCollided; ++j ) {
isCollided = false;
for ( k = 0; k < clips.count; ++k ) {
var clip = clips.get( k );
if ( clip.uuid === target.uuid )
continue;
var sw = target.width; // Width of the source clip
var cx = ftop( clip["position"] );
var cw = ftop( clip["end"] - clip["begin"] + 1);
// Set a right position
//
// HACK: If magnetic mode, consider clips bigger.
if ( useMagneticMode === true ) {
if ( cx + cw > newX && newX + sw > cx )
isCollided = true;
cw += 50
cx -= 25
if ( cx + cw > newX && newX + sw > cx ) {
if ( cx > newX ) {
if ( cx - sw > 0 )
newX = cx - sw + 25;
else
newX = oldX;
} else {
newX = cx + cw - 25;
}
}
}
else {
if ( cx + cw > newX && newX + sw > cx ) {
isCollided = true;
if ( cx > newX ) {
if ( cx - sw > 0 )
newX = cx - sw;
else
newX = oldX;
} else {
newX = cx + cw;
}
}
}
if ( isCollided )
break;
}
}
if ( isCollided ) {
for ( k = 0; k < clips.count; ++k ) {
clip = clips.get( k );
cx = ftop( clip["position"] );
cw = ftop( clip["end"] - clip["begin"] + 1);
newX = Math.max( newX, cx + cw );
}
}
return newX;
}
onDropped: {
if ( drop.keys.indexOf( "vlmc/uuid" ) >= 0 ) {
aClipInfo = findClipFromTrack( "Audio", trackId, "tempUuid" );
vClipInfo = findClipFromTrack( "Video", trackId, "tempUuid" );
aClipInfo = findClipFromTrack( "Audio", trackId, "audioUuid" );
vClipInfo = findClipFromTrack( "Video", trackId, "videoUuid" );
if ( aClipInfo ) {
var pos = aClipInfo["position"];
removeClipFromTrack( "Audio", trackId, "tempUuid" );
addClip( "Audio", trackId, workflow.clipInfo( workflow.addClip( currentUuid, trackId, pos, true ) ) );
var audioClipUuid = workflow.addClip( currentUuid, trackId, pos, true );
removeClipFromTrack( "Audio", trackId, "audioUuid" );
addClip( "Audio", trackId, workflow.clipInfo( audioClipUuid ) );
}
if ( vClipInfo ) {
pos = vClipInfo["position"];
removeClipFromTrack( "Video", trackId, "tempUuid" );
addClip( "Video", trackId, workflow.clipInfo( workflow.addClip( currentUuid, trackId, pos, false ) ) );
var videoClipUuid = workflow.addClip( currentUuid, trackId, pos, false );
removeClipFromTrack( "Video", trackId, "videoUuid" );
addClip( "Video", trackId, workflow.clipInfo( videoClipUuid ) );
}
if ( audioClipUuid && videoClipUuid ) {
findClipItem( audioClipUuid ).linkedClip = videoClipUuid;
findClipItem( videoClipUuid ).linkedClip = audioClipUuid;
}
currentUuid = "";
aClipInfo = null;
......@@ -111,8 +198,8 @@ Item {
onExited: {
if ( currentUuid !== "" ) {
removeClipFromTrack( "Audio", trackId, "tempUuid" );
removeClipFromTrack( "Video", trackId, "tempUuid" );
removeClipFromTrack( "Audio", trackId, "audioUuid" );
removeClipFromTrack( "Video", trackId, "videoUuid" );
}
}
......@@ -135,13 +222,19 @@ Item {
var newClipInfo = workflow.clipInfo( drag.getDataAsString( "vlmc/uuid" ) );
currentUuid = "" + newClipInfo["uuid"];
newClipInfo["position"] = ptof( drag.x );
newClipInfo["uuid"] = "tempUuid";
if ( newClipInfo["audio"] )
if ( newClipInfo["audio"] ) {
newClipInfo["uuid"] = "audioUuid";
aClipInfo = addClip( "Audio", trackId, newClipInfo );
if ( newClipInfo["video"] )
}
if ( newClipInfo["video"] ) {
newClipInfo["uuid"] = "videoUuid";
vClipInfo = addClip( "Video", trackId, newClipInfo );
}
}
lastX = drag.x;
}
else
lastX = drag.source.x;
}
onPositionChanged: {
......@@ -173,11 +266,9 @@ Item {
else
deltaX = drag.x - lastX;
var alreadyCalculated = []; // Uuids of clips being already set new x position.
for ( i = 0; i < selectedClips.length; ++i ) {
var target = selectedClips[i];
var oldX = target.pixelPosition();
var newX = Math.max( oldX + deltaX, 0 );
if ( drag.source === target ) {
var oldTrackId = target.newTrackId;
target.newTrackId = trackId;
......@@ -187,102 +278,48 @@ Item {
}
}
if ( isMagneticMode === true ) {
var leastDestance = 25;
// Check two times
for ( var k = 0; k < 2; ++k ) {
for ( j = 0; j < markers.count; ++j ) {
var mx = ftop( markers.get( j ).position );
if ( Math.abs( newX - mx ) < leastDestance ) {
leastDestance = Math.abs( newX - mx );
newX = mx;
}
else if ( Math.abs( newX + target.width - mx ) < leastDestance ) {
leastDestance = Math.abs( newX + target.width - mx );
newX = mx - target.width;
}
}
if ( alreadyCalculated.indexOf( target.uuid ) < 0 ) {
var oldX = target.pixelPosition();
var newX = Math.max( oldX + deltaX, 0 );
// Recalculate deltaX in case of drag.source being moved
if ( drag.source === target ) {
if ( oldTrackId === target.newTrackId )
deltaX = Math.round( newX - oldX );
else
// Don't move other clips if drag.source's track is changed
deltaX = 0;
}
}
// Collision detection
var isCollided = true;
var currentTrack = trackContainer( target.type )["tracks"].get( target.newTrackId );
if ( currentTrack )
var clips = currentTrack["clips"];
else
clips = [];
for ( j = 0; j < clips.count + 2 && isCollided; ++j ) {
isCollided = false;
for ( k = 0; k < clips.count; ++k ) {
var clip = clips.get( k );
if ( clip.uuid === target.uuid )
continue;
var sw = target.width; // Width of the source clip
var cx = ftop( clip["position"] );
var cw = ftop( clip["end"] - clip["begin"] + 1);
// Set a right position
//
// HACK: If magnetic mode, consider clips bigger.
if ( isMagneticMode === true ) {
if ( cx + cw > newX && newX + sw > cx )
isCollided = true;
cw += 50
cx -= 25
if ( cx + cw > newX && newX + sw > cx ) {
if ( cx > newX ) {
if ( cx - sw > 0 )
newX = cx - sw + 25;
else
newX = oldX;
} else {
newX = cx + cw - 25;
}
}
}
else {
if ( cx + cw > newX && newX + sw > cx ) {
isCollided = true;
if ( cx > newX ) {
if ( cx - sw > 0 )
newX = cx - sw;
else
newX = oldX;
} else {
newX = cx + cw;
}
}
}
if ( isCollided )
break;
}
}
newX = findNewPosition( newX, target, isMagneticMode );
if ( isCollided ) {
for ( k = 0; k < clips.count; ++k ) {
clip = clips.get( k );
cx = ftop( clip["position"] );
cw = ftop( clip["end"] - clip["begin"] + 1);
newX = Math.max( newX, cx + cw );
}
}
// Let's find newX of the linked clip
if ( target.linked === true ) {
var linkedClipItem = findClipItem( target.linkedClip );
var newLinkedClipX = findNewPosition( newX, linkedClipItem, isMagneticMode );
// Recalculate deltaX in case of drag.source being moved
if ( drag.source === target ) {
if ( oldTrackId === target.newTrackId )
deltaX = Math.round( newX - oldX );
else
// Don't move other clips if drag.source's track is changed
deltaX = 0;
}
// If linked clip collides
if ( Math.abs( newLinkedClipX - newX ) > 1 ) {
// Recalculate target's newX
// This time, don't use magnets
newX = findNewPosition( newLinkedClipX, target, false );
newLinkedClipX = findNewPosition( newX, target, false );
target.setPixelPosition( newX );
// And if newX collides again, we don't move
if ( Math.abs( newLinkedClipX - newX ) > 1 )
newX = oldX;
}
linkedClipItem.setPixelPosition( newX );
alreadyCalculated.push( target.linkedClip );
}
target.setPixelPosition( newX );
alreadyCalculated.push( target.uuid );
}
// Scroll if needed
if ( length < ptof( newX + target.width ) ) {
length = ptof( newX + target.width );
if ( length < ptof( newX ) ) {
length = ptof( newX );
// Never show the background behind the timeline
var newContentX = sView.flickableItem.contentWidth - sView.width;
if ( newContentX >= 0 )
......@@ -321,6 +358,7 @@ Item {
position: model.position
begin: model.begin
end: model.end
linkedClip: model.linkedClip
clipInfo: model
}
}
......
......@@ -93,6 +93,7 @@ Rectangle {
newDict["uuid"] = clipDict["uuid"];
newDict["trackId"] = trackId;
newDict["name"] = clipDict["name"];
newDict["linkedClip"] = clipDict["linkedClip"] ? clipDict["linkedClip"] : "";
var tracks = trackContainer( trackType )["tracks"];
tracks.get( trackId )["clips"].append( newDict );
......@@ -159,6 +160,14 @@ Rectangle {
return v;
}
function findClipItem( uuid ) {
for ( var i = 0; i < allClips.length; ++i ) {
if ( uuid === allClips[i].uuid )
return allClips[i];
}
return null;
}
function moveClipTo( trackType, uuid, trackId )
{
var clip = findClipFromTrackContainer( trackType, uuid );
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment