Track.qml 16.2 KB
Newer Older
luyikei's avatar
luyikei committed
1 2 3 4
import QtQuick 2.0

Item {
    id: track
5
    width: parent.width
luyikei's avatar
luyikei committed
6 7 8 9 10 11 12
    height: trackHeight
    z: 10

    property int trackId
    property string type
    property ListModel clips

13 14 15 16 17 18
    Rectangle {
        id: clipArea
        x: trackInfo.width
        color: "#222222"
        height: parent.height
        width: track.width - initPosOfCursor
luyikei's avatar
luyikei committed
19

luyikei's avatar
luyikei committed
20
        Rectangle {
21 22 23 24 25 26 27 28 29
            color: "#666666"
            height: 1
            width: parent.width
            anchors.bottom: clipArea.bottom

            Component.onCompleted: {
                if ( track.type === "Video" && track.trackId == 0 )
                {
                    color = "#111111";
luyikei's avatar
luyikei committed
30 31
                }
            }
32
        }
luyikei's avatar
luyikei committed
33

34 35 36 37
        DropArea {
            id: dropArea
            anchors.fill: parent
            keys: ["Clip", "vlmc/uuid"]
luyikei's avatar
luyikei committed
38

39 40 41 42 43 44 45 46 47 48 49
            // Enum for drop mode
            readonly property var dropMode: {
                "New": 0,
                "Move": 1,
            }
            readonly property int magneticMargin: 25

            property string currentUuid
            property var aClipInfo: null
            property var vClipInfo: null

50 51
            property int lastPos: 0
            property int deltaPos: 0
52

53
            function findNewPosition( newPos, target, dragSource, useMagneticMode ) {
54
                if ( useMagneticMode === true ) {
55
                    var leastDistance = ptof( magneticMargin );
56 57 58
                    // Check two times
                    for ( var k = 0; k < 2; ++k ) {
                        for ( var j = 0; j < markers.count; ++j ) {
59 60 61 62
                            var mPos = markers.get( j ).position;
                            if ( Math.abs( newPos - mPos ) < leastDistance ) {
                                leastDistance = Math.abs( newPos - mPos );
                                newPos = mPos;
63
                            }
64 65 66
                            else if ( Math.abs( newPos + target.length - 1 - mPos ) < leastDistance ) {
                                leastDistance = Math.abs( newPos + target.length - 1 - mPos );
                                newPos = mPos - target.length + 1;
67 68 69
                            }
                        }
                    }
70
                    // Magnet for the left edge of the timeline
71 72
                    if ( newPos < ptof( magneticMargin ) )
                        newPos = 0;
73
                }
74

75 76 77 78 79 80
                // Collision detection
                var isCollided = true;
                var currentTrack = trackContainer( target.type )["tracks"].get( target.newTrackId );
                if ( currentTrack )
                    var clips = currentTrack["clips"];
                else
81
                    return target.position;
82 83 84 85
                for ( j = 0; j < clips.count + 2 && isCollided; ++j ) {
                    isCollided = false;
                    for ( k = 0; k < clips.count; ++k ) {
                        var clip = clips.get( k );
86 87 88
                        if ( clip.uuid === target.uuid ||
                             ( clip.uuid === dragSource.uuid && target.newTrackId !== dragSource.newTrackId )
                           )
89
                            continue;
90 91 92 93
                        var cPos = clip.uuid === dragSource.uuid ? ptof( dragSource.x ) : clip["position"];
                        var cEndPos = clip["position"] + clip["length"] - 1;

                        if ( cEndPos >= newPos && newPos + target.length - 1 >= cPos )
94
                            isCollided = true;
95

96 97 98 99 100 101 102 103 104 105
                        // HACK: If magnetic mode, consider clips bigger
                        //       but not if "clip" is also selected because both of them will be moving
                        //       and we want to keep the same distance between them as much as possible
                        var clipMargin = useMagneticMode && findClipItem( clip.uuid ).selected === false ? ptof( magneticMargin ) : 0;
                        cPos += clipMargin * 2;
                        cEndPos -= clipMargin;
                        if ( cEndPos >= newPos && newPos + target.length - 1 >= cPos ) {
                            if ( cPos >= newPos ) {
                                if ( cPos - target.length + 1 > 0 )
                                    newPos = cPos - target.length + clipMargin;
106
                                else
107
                                    newPos = target.position;
108
                            } else {
109
                                newPos = cEndPos - clipMargin + 1;
110 111
                            }
                        }
112

113 114
                        if ( isCollided )
                            break;
115
                    }
116
                }
117

118 119 120
                if ( isCollided ) {
                    for ( k = 0; k < clips.count; ++k ) {
                        clip = clips.get( k );
121 122
                        if ( clip.uuid === target.uuid ||
                             ( clip.uuid === dragSource.uuid && target.newTrackId !== dragSource.newTrackId ) )
123
                            continue;
124 125 126
                        cPos = clip.uuid === dragSource.uuid ? ptof( dragSource.x ) : clip["position"];
                        cEndPos = clip["position"] + clip["length"] - 1;
                        newPos = Math.max( newPos, cEndPos + 1 );
127
                    }
128
                }
129

130
                return newPos;
131
            }
132

133 134 135 136
            onDropped: {
                if ( drop.keys.indexOf( "vlmc/uuid" ) >= 0 ) {
                    aClipInfo = findClipFromTrack( "Audio", trackId, "audioUuid" );
                    vClipInfo = findClipFromTrack( "Video", trackId, "videoUuid" );
137
                    var pos = 0;
138
                    if ( aClipInfo ) {
139
                        pos = aClipInfo["position"];
140
                        removeClipFromTrack( "Audio", trackId, "audioUuid" );
141 142 143
                    }
                    if ( vClipInfo ) {
                        pos = vClipInfo["position"];
144
                        removeClipFromTrack( "Video", trackId, "videoUuid" );
luyikei's avatar
luyikei committed
145
                    }
146
                    workflow.addClip( drop.getDataAsString("vlmc/uuid"), trackId, pos, false );
147 148 149 150 151 152
                    currentUuid = "";
                    aClipInfo = null;
                    vClipInfo = null;
                    clearSelectedClips();
                    adjustTracks( "Audio" );
                    adjustTracks( "Video" );
luyikei's avatar
luyikei committed
153
                }
154
            }
luyikei's avatar
luyikei committed
155

156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
            onExited: {
                if ( currentUuid !== "" ) {
                    removeClipFromTrack( "Audio", trackId, "audioUuid" );
                    removeClipFromTrack( "Video", trackId, "videoUuid" );
                }
            }

            onEntered: {
                if ( drag.keys.indexOf( "vlmc/uuid" ) >= 0 ) {
                    clearSelectedClips();
                    if ( currentUuid === drag.getDataAsString( "vlmc/uuid" ) ) {
                        if ( aClipInfo )
                        {
                            aClipInfo["position"] = ptof( drag.x );
                            aClipInfo = addClip( "Audio", trackId, aClipInfo );
luyikei's avatar
luyikei committed
171
                        }
172 173 174 175
                        if ( vClipInfo )
                        {
                            vClipInfo["position"] = ptof( drag.x );
                            vClipInfo = addClip( "Video", trackId, vClipInfo );
luyikei's avatar
luyikei committed
176 177
                        }
                    }
178
                    else {
179
                        var newClipInfo = workflow.libraryClipInfo( drag.getDataAsString( "vlmc/uuid" ) );
180
                        currentUuid = "" + newClipInfo["uuid"];
181 182 183 184 185 186 187 188 189 190
                        newClipInfo["position"] = ptof( drag.x );
                        if ( newClipInfo["audio"] ) {
                            newClipInfo["uuid"] = "audioUuid";
                            aClipInfo = addClip( "Audio", trackId, newClipInfo );
                        }
                        if ( newClipInfo["video"] ) {
                            newClipInfo["uuid"] = "videoUuid";
                            vClipInfo = addClip( "Video", trackId, newClipInfo );
                        }
                    }
191
                    lastPos = ptof( drag.x );
luyikei's avatar
luyikei committed
192
                }
193
                else {
194
                    lastPos = ptof( drag.source.x );
195 196 197 198
                    // HACK: Call onPositoinChanged forcely here.
                    // x will be rounded so it won't affect actual its position.
                    drag.source.x = drag.source.x + 0.000001;
                }
199
            }
luyikei's avatar
luyikei committed
200

201 202 203 204 205 206 207 208 209 210
            onPositionChanged: {
                // If resizing, ignore
                if ( drag.source.resizing === true )
                    return;

                if ( drag.keys.indexOf( "vlmc/uuid" ) >= 0 )
                    var dMode = dropMode.New;
                else
                    dMode = dropMode.Move;

211
                sortSelectedClips();
212
                var toMove = selectedClips.concat();
213

214
                if ( dMode === dropMode.Move ) {
215 216 217
                    // Move to the top
                    drag.source.parent.parent.z = ++maxZ;

218
                    // Prepare newTrackId for all the selected clips
219 220 221
                    var oldTrackId = drag.source.newTrackId;
                    drag.source.newTrackId = trackId;

222
                    // Check if there is any impossible move
223
                    for ( var i = 0; i < toMove.length; ++i ) {
224
                        var target = findClipItem( toMove[i] );
225
                        if ( target !== drag.source ) {
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
                            var newTrackId = trackId - oldTrackId + target.trackId;
                            if ( newTrackId < 0 )
                            {
                                drag.source.newTrackId = oldTrackId;
                                drag.source.setPixelPosition( drag.source.pixelPosition() );

                                // Direction depends on its type
                                drag.source.y +=
                                        drag.source.type === "Video"
                                        ? -( trackHeight * ( oldTrackId - trackId ) )
                                        : trackHeight * ( oldTrackId - trackId )
                                return;
                            }
                        }
                    }

                    for ( i = 0; i < toMove.length; ++i ) {
                        target = findClipItem( toMove[i] );
                        if ( target !== drag.source ) {
                             newTrackId = trackId - oldTrackId + target.trackId;
                            target.newTrackId = Math.max( 0, newTrackId );
247 248
                            if ( target.newTrackId !== target.trackId ) {
                                // Let's move to the new tracks
249 250 251
                                target.clipInfo["selected"] = true;
                                addClip( target.type, target.newTrackId, target.clipInfo );
                                removeClipFromTrack( target.type, target.trackId, target.uuid );
252
                            }
253
                        }
254
                    }
255

256
                    deltaPos = ptof( drag.source.x ) - lastPos;
257 258
                }
                else
259
                    deltaPos = ptof( drag.x ) - lastPos;
260

261 262
                while ( toMove.length > 0 ) {
                    target = findClipItem( toMove[0] );
263 264 265
                    var oldPos = target.position;
                    var newPos = findNewPosition( Math.max( oldPos + deltaPos, 0 ), target, drag.source, isMagneticMode );
                    deltaPos = newPos - oldPos;
luyikei's avatar
luyikei committed
266

267
                    // Let's find newX of the linked clip
268
                    for ( i = 0; i < target.linkedClips.length; ++i )
269
                    {
270
                        var linkedClipItem = findClipItem( target.linkedClips[i] );
271

272
                        if ( linkedClipItem ) {
273
                            var newLinkedClipPos = findNewPosition( newPos, linkedClipItem, drag.source, isMagneticMode );
274

275
                            // If linked clip collides
276
                            if ( newLinkedClipPos !== newPos ) {
277 278
                                // Recalculate target's newX
                                // This time, don't use magnets
279 280
                                if ( isMagneticMode === true )
                                {
281 282
                                    newLinkedClipPos = findNewPosition( newPos, linkedClipItem, drag.source, false );
                                    newPos = findNewPosition( newPos, target, drag.source, false );
luyikei's avatar
luyikei committed
283

284
                                    // And if newX collides again, we don't move
285 286
                                    if ( newLinkedClipPos !== newPos )
                                        deltaPos = 0
287
                                    else
288
                                        deltaPos = newPos - oldPos;
289 290
                                }
                                else
291
                                    deltaPos = 0;
luyikei's avatar
luyikei committed
292
                            }
293
                            else
294
                                deltaPos = newPos - oldPos;
luyikei's avatar
luyikei committed
295

296 297 298
                            var ind = toMove.indexOf( linkedClipItem.uuid );
                            if ( ind > 0 )
                                toMove.splice( ind, 1 );
299
                        }
300
                    }
301

302
                    newPos = oldPos + deltaPos;
303 304 305 306
                    toMove.splice( 0, 1 );
                }
                // END of while ( toMove.length > 0 )

307 308
                if ( deltaPos === 0 && dMode === dropMode.Move ) {
                    drag.source.forcePosition(); // Use the original position
309 310 311 312 313
                    return;
                }

                for ( i = 0; i < selectedClips.length; ++i ) {
                    target = findClipItem( selectedClips[i] );
314
                    newPos = target.position + deltaPos;
315 316 317

                    // We only want to update the length when the left edge of the timeline
                    // is exposed.
318
                    if ( sView.flickableItem.contentX + page.width > sView.width &&
319 320
                            length < newPos + target.length ) {
                        length = newPos + target.length;
321
                    }
322

323
                    target.position = newPos;
324

325 326
                    // Scroll if needed
                    if ( drag.source === target || dMode === dropMode.New )
327
                        target.scrollToThis();
luyikei's avatar
luyikei committed
328
                }
329 330

                if ( dMode === dropMode.Move )
331
                    lastPos = ptof( drag.source.x );
332
                else
333
                    lastPos = ptof( drag.x );
luyikei's avatar
luyikei committed
334
            }
335
        }
luyikei's avatar
luyikei committed
336

337 338 339 340 341 342 343 344 345
        Repeater {
            id: repeater
            model: clips
            delegate: Clip {
                height: track.height - 3
                name: model.name
                trackId: model.trackId
                type: track.type
                uuid: model.uuid
346
                libraryUuid: model.libraryUuid
347
                position: model.position
348
                lastPosition: model.position
349 350
                begin: model.begin
                end: model.end
351
                length: model.length
352
                clipInfo: model
luyikei's avatar
luyikei committed
353 354 355
            }
        }
    }
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384

    Rectangle {
        id: trackInfo
        x: sView.flickableItem.contentX
        width: initPosOfCursor
        height: parent.height
        color: "#444444"

        Rectangle {
            width: parent.width
            height: 1
            anchors.bottom: parent.bottom
            color: "#111111"
        }

        Rectangle {
            width: 1
            height: parent.height
            anchors.left: parent.left
            color: "#111111"
        }

        Rectangle {
            width: 1
            height: parent.height
            anchors.right: parent.right
            color: "#111111"
        }

385 386
        Text {
            id: trackText
387
            anchors.verticalCenter: parent.verticalCenter
388 389 390
            x: 10
            text: type + " " + ( trackId + 1 )
            color: "white"
391
            font.pointSize: 10
392
        }
393

394 395
        Row {
            anchors.verticalCenter: parent.verticalCenter
396
            x: trackText.y + trackText.contentWidth + 10
397
            spacing: 4
398 399 400 401 402 403 404 405

            PropertyButton {
                id: fxButton
                text: "Fx"
                selected: true

                onSelectedChanged: {
                    if ( selected === false ) {
406
                        workflow.showEffectStack( trackId );
407 408 409 410
                        selected = true;
                    }
                }
            }
411 412
        }
    }
luyikei's avatar
luyikei committed
413 414
}