Track.qml 14.8 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 50 51 52
            // 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

            property int lastX: 0
            property int deltaX: 0

53
            function findNewPosition( newX, target, dragSource, useMagneticMode ) {
54 55 56
                var oldX = target.pixelPosition();

                if ( useMagneticMode === true ) {
luyikei's avatar
luyikei committed
57
                    var leastDistance = magneticMargin;
58 59 60 61
                    // 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 );
luyikei's avatar
luyikei committed
62 63
                            if ( Math.abs( newX - mx ) < leastDistance ) {
                                leastDistance = Math.abs( newX - mx );
64 65
                                newX = mx;
                            }
luyikei's avatar
luyikei committed
66 67
                            else if ( Math.abs( newX + target.width - mx ) < leastDistance ) {
                                leastDistance = Math.abs( newX + target.width - mx );
68
                                newX = mx - target.width;
69 70 71
                            }
                        }
                    }
72 73 74
                    // Magnet for the left edge of the timeline
                    if ( newX < magneticMargin )
                        newX = 0;
75
                }
76

77 78 79 80 81 82
                // Collision detection
                var isCollided = true;
                var currentTrack = trackContainer( target.type )["tracks"].get( target.newTrackId );
                if ( currentTrack )
                    var clips = currentTrack["clips"];
                else
83
                    return oldX;
84 85 86 87
                for ( j = 0; j < clips.count + 2 && isCollided; ++j ) {
                    isCollided = false;
                    for ( k = 0; k < clips.count; ++k ) {
                        var clip = clips.get( k );
88 89 90
                        if ( clip.uuid === target.uuid ||
                             ( clip.uuid === dragSource.uuid && target.newTrackId !== dragSource.newTrackId )
                           )
91 92
                            continue;
                        var sw = target.width; // Width of the source clip
93
                        var cx = clip.uuid === dragSource.uuid ? dragSource.x : ftop( clip["position"] );
94 95 96
                        var cw = ftop( clip["end"] - clip["begin"] + 1);
                        // Set a right position
                        //
97 98 99 100
                        // HACK: If magnetic mode, consider clips bigger
                        //       but not if it's 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 ? magneticMargin : 0;
101 102
                        if ( cx  + cw > newX && newX + sw > cx )
                            isCollided = true;
103

104 105 106 107 108 109 110 111 112 113
                        cw += clipMargin * 2
                        cx -= clipMargin
                        if ( cx + cw > newX && newX + sw > cx ) {
                            if ( cx > newX ) {
                                if ( cx - sw > 0 )
                                    newX = cx - sw + clipMargin;
                                else
                                    newX = oldX;
                            } else {
                                newX = cx + cw - clipMargin;
114 115
                            }
                        }
116 117
                        if ( isCollided )
                            break;
118
                    }
119
                }
120

121 122 123
                if ( isCollided ) {
                    for ( k = 0; k < clips.count; ++k ) {
                        clip = clips.get( k );
124 125
                        if ( clip.uuid === target.uuid ||
                             ( clip.uuid === dragSource.uuid && target.newTrackId !== dragSource.newTrackId ) )
126
                            continue;
127
                        cx = clip.uuid === dragSource.uuid ? dragSource.x : ftop( clip["position"] );
128 129
                        cw = ftop( clip["end"] - clip["begin"] + 1);
                        newX = Math.max( newX, cx + cw );
130
                    }
131
                }
132

133 134
                return newX;
            }
135

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

159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
            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
174
                        }
175 176 177 178
                        if ( vClipInfo )
                        {
                            vClipInfo["position"] = ptof( drag.x );
                            vClipInfo = addClip( "Video", trackId, vClipInfo );
luyikei's avatar
luyikei committed
179 180
                        }
                    }
181
                    else {
182
                        var newClipInfo = workflow.libraryClipInfo( drag.getDataAsString( "vlmc/uuid" ) );
183
                        currentUuid = "" + newClipInfo["uuid"];
184 185 186 187 188 189 190 191 192 193 194
                        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 );
                        }
                    }
                    lastX = drag.x;
luyikei's avatar
luyikei committed
195
                }
196
                else {
197
                    lastX = drag.source.x;
198 199 200 201
                    // 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;
                }
202
            }
luyikei's avatar
luyikei committed
203

204 205 206 207 208 209 210 211 212 213
            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;

214
                sortSelectedClips();
215
                var toMove = selectedClips.concat();
216

217
                if ( dMode === dropMode.Move ) {
218 219 220
                    // Move to the top
                    drag.source.parent.parent.z = ++maxZ;

221
                    // Prepare newTrackId for all the selected clips
222 223 224
                    var oldTrackId = drag.source.newTrackId;
                    drag.source.newTrackId = trackId;

225
                    for ( var i = 0; i < toMove.length; ++i ) {
226
                        var target = findClipItem( toMove[i] );
227 228 229 230
                        if ( target !== drag.source ) {
                            target.newTrackId = Math.max( 0, trackId - oldTrackId + target.trackId );
                            if ( target.newTrackId !== target.trackId ) {
                                // Let's move to the new tracks
231 232 233
                                target.clipInfo["selected"] = true;
                                addClip( target.type, target.newTrackId, target.clipInfo );
                                removeClipFromTrack( target.type, target.trackId, target.uuid );
234
                            }
235
                        }
236
                    }
237

238
                    deltaX = drag.source.x - lastX;
239 240 241 242
                }
                else
                    deltaX = drag.x - lastX;

243 244
                while ( toMove.length > 0 ) {
                    target = findClipItem( toMove[0] );
245

246
                    var uuid = target.uuid;
247
                    var oldX = target.pixelPosition();
248
                    var newX = findNewPosition( Math.max( oldX + deltaX, 0 ), target, drag.source, isMagneticMode );
luyikei's avatar
luyikei committed
249

250
                    // Let's find newX of the linked clip
251
                    for ( i = 0; i < target.linkedClips.length; ++i )
252
                    {
253
                        var linkedClipItem = findClipItem( target.linkedClips[i] );
254

255
                        if ( linkedClipItem ) {
256
                            var newLinkedClipX = findNewPosition( newX, linkedClipItem, drag.source, isMagneticMode );
257

258 259 260 261
                            // If linked clip collides
                            if ( ptof( Math.abs( newLinkedClipX - newX ) ) !== 0 ) {
                                // Recalculate target's newX
                                // This time, don't use magnets
262 263
                                if ( isMagneticMode === true )
                                {
264 265
                                    newLinkedClipX = findNewPosition( newX, linkedClipItem, drag.source, false );
                                    newX = findNewPosition( newX, target, drag.source, false );
luyikei's avatar
luyikei committed
266

267 268 269 270 271
                                    // And if newX collides again, we don't move
                                    if ( ptof( Math.abs( newLinkedClipX - newX ) ) !== 0 )
                                        newX = oldX;
                                }
                                else
272 273
                                    newX = oldX;
                            }
luyikei's avatar
luyikei committed
274

275 276 277 278
                            // We only want to update the length when the left edge of the timeline
                            // is exposed.
                            if ( sView.flickableItem.contentX + page.width > sView.width &&
                                    length < ptof( newX + linkedClipItem.width ) ) {
279
                                length = ptof( newX + linkedClipItem.width );
luyikei's avatar
luyikei committed
280 281
                            }

282 283 284 285
                            linkedClipItem.setPixelPosition( newX );
                            var ind = toMove.indexOf( linkedClipItem.uuid );
                            if ( ind > 0 )
                                toMove.splice( ind, 1 );
286
                        }
287
                    }
288

289 290
                    if ( sView.flickableItem.contentX + page.width > sView.width &&
                            length < ptof( newX + target.width ) ) {
291 292
                        length = ptof( newX + target.width );
                    }
293

294 295 296
                    target.setPixelPosition( newX );
                    toMove.splice( 0, 1 );

297 298
                    // Scroll if needed
                    if ( drag.source === target || dMode === dropMode.New )
299
                        target.scrollToThis();
luyikei's avatar
luyikei committed
300
                }
luyikei's avatar
luyikei committed
301
                // END of while ( toMove.length > 0 )
302 303 304 305 306

                if ( dMode === dropMode.Move )
                    lastX = drag.source.x;
                else
                    lastX = drag.x;
luyikei's avatar
luyikei committed
307
            }
308
        }
luyikei's avatar
luyikei committed
309

310 311 312 313 314 315 316 317 318
        Repeater {
            id: repeater
            model: clips
            delegate: Clip {
                height: track.height - 3
                name: model.name
                trackId: model.trackId
                type: track.type
                uuid: model.uuid
319
                libraryUuid: model.libraryUuid
320
                position: model.position
321
                lastPosition: model.lastPosition
322 323 324
                begin: model.begin
                end: model.end
                clipInfo: model
luyikei's avatar
luyikei committed
325 326 327
            }
        }
    }
328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356

    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"
        }

357 358
        Text {
            id: trackText
359
            anchors.verticalCenter: parent.verticalCenter
360 361 362
            x: 10
            text: type + " " + ( trackId + 1 )
            color: "white"
363
            font.pointSize: 10
364
        }
365

366 367
        Row {
            anchors.verticalCenter: parent.verticalCenter
368
            x: trackText.y + trackText.contentWidth + 10
369
            spacing: 4
370 371 372 373 374 375 376 377

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

                onSelectedChanged: {
                    if ( selected === false ) {
378
                        workflow.showEffectStack( trackId );
379 380 381 382
                        selected = true;
                    }
                }
            }
383 384
        }
    }
luyikei's avatar
luyikei committed
385 386
}