Clip.qml 14.1 KB
Newer Older
luyikei's avatar
luyikei committed
1 2 3 4 5
import QtQuick 2.0

Rectangle {
    id: clip
    // NEVER SET X DIRECTLY. BINDING WILL BE REMOVED.
luyikei's avatar
luyikei committed
6
    x: ftop( position )
7
    width: ftop( end - begin + 1 )
luyikei's avatar
luyikei committed
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
    gradient: Gradient {
        GradientStop {
            id: gStop1
            position: 0.00;
            color: "#4276a6";
        }
        GradientStop {
            id: gStop2
            position: 1.00;
            color: "#1f546f";
        }
    }
    radius: 2
    border.color: "#1f546f"
    border.width: 1
23
    opacity: page.dragging === true && selectedClips.indexOf( uuid ) !== -1 ? 0.5 : 1.0
luyikei's avatar
luyikei committed
24 25

    property alias name: text.text
26
    property alias thumbnailSource: thumbnailImage.source
luyikei's avatar
luyikei committed
27
    property int trackId
luyikei's avatar
luyikei committed
28
    // Usualy it is trackId, the clip will be moved to the new track immediately.
luyikei's avatar
luyikei committed
29 30
    property int newTrackId
    property int position
31
    property int lastPosition
luyikei's avatar
luyikei committed
32 33
    property int begin
    property int end
34 35
    property string libraryUuid // Library UUID: For thumbnails
    property string uuid // Instance UUID
36
    property var linkedClips: linkedClipsDict[uuid] ? linkedClipsDict[uuid] : [] // Uuid
luyikei's avatar
luyikei committed
37 38
    property string type
    property bool selected: false
39
    property alias mouseX: dragArea.mouseX
luyikei's avatar
luyikei committed
40 41 42

    property var clipInfo

luyikei's avatar
luyikei committed
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
    function setPixelPosition( pixels )
    {
        if ( pixels >= 0 )
            position = ptof( pixels );
        // FIXME: Binding can be lost because of dragging.
        x = Qt.binding( function() { return ftop( position ); } );
    }

    function pixelPosition()
    {
        return ftop( position );
    }

    function resize() {
        // This function updates Backend
58
        workflow.resizeClip( uuid, begin, end, position );
luyikei's avatar
luyikei committed
59 60 61
    }

    function selectLinkedClip() {
62 63 64 65 66
        if ( selected === false )
            return;
        for ( var i = 0; i < linkedClips.length; ++i )
        {
            var linkedClip = linkedClips[i];
luyikei's avatar
luyikei committed
67
            findClipItem( linkedClip ).selected = true;
68 69 70 71 72 73 74 75
        }
    }

    function linked() {
        if ( selectedClips.length < 2 )
            return false;
        for ( var i = 0; i < selectedClips.length; ++i ) {
            for ( var j = i + 1; j < selectedClips.length; ++j ) {
76
                if ( findClipItem( selectedClips[i] ).linkedClips.indexOf( selectedClips[j] ) === -1 )
77 78 79 80
                    return false;
            }
        }
        return true;
luyikei's avatar
luyikei committed
81 82
    }

83 84 85 86
    function updateEffects( clipInfo ) {
        if ( !clipInfo["filters"] )
            return;

87
        var str = "";
88
        for ( var i = 0; i < clipInfo["filters"].length; ++i ) {
89 90 91
            str += clipInfo["filters"][i]["identifier"];
            if ( i < clipInfo["filters"].length - 1 )
                str += ", "
92
        }
93
        effectsItem.text = str;
94 95
    }

96
    function resizeLinkedClips( oldPos, oldBegin, oldEnd ) {
97 98 99 100 101 102 103 104 105 106 107 108 109
        for ( var i = 0; i < linkedClips.length; ++i )
        {
            var linkedClip = linkedClips[i];
            var lc = findClipItem( linkedClip );
            if ( lc === null )
                return;
            // Don't resize from the begining if the clips didn't shared the same begin position
            if ( lc.position === oldPos ) {
                lc.position = position;
                lc.begin = begin;
            }
            if ( lc.end === oldEnd )
                lc.end = end;
110 111 112
        }
    }

113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
    function scrollToThis() {
        if ( width > sView.width )
            return;

        var newContentX = sView.flickableItem.contentX;

        if ( sView.flickableItem.contentX + sView.width <
                x + mouseX + initPosOfCursor + sView.sViewPadding )
            newContentX = x + mouseX + initPosOfCursor + sView.sViewPadding - sView.width;
        else if ( sView.flickableItem.contentX + sView.sViewPadding > x + mouseX )
            newContentX = x + mouseX - sView.sViewPadding;

        sView.flickableItem.contentX = Math.max( newContentX, 0 );
    }

luyikei's avatar
luyikei committed
128
    onXChanged: {
129 130 131
        if ( sView.width - initPosOfCursor < width )
            return;

luyikei's avatar
luyikei committed
132 133 134
        if ( sView.flickableItem.contentX + sView.width <
                x + width + initPosOfCursor + sView.sViewPadding )
            Drag.hotSpot.x = 0;
135
        else if ( sView.flickableItem.contentX + sView.sViewPadding > x )
luyikei's avatar
luyikei committed
136 137 138
            Drag.hotSpot.x = width;
    }

luyikei's avatar
luyikei committed
139 140 141 142 143 144 145 146 147 148 149 150
    onYChanged: {
        y -= y % trackHeight;
        // Don't move outside its TrackContainer
        // For Top
        var yToMoveUp = track.mapToItem( container, 0, 0 ).y + y;
        if ( yToMoveUp < 0 )
            y -= yToMoveUp;
        // For Bottom
        if ( yToMoveUp + height > container.height )
            y -= yToMoveUp - container.height + height;
    }

luyikei's avatar
luyikei committed
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
    onClipInfoChanged: {
        if ( !clipInfo )
            return;

        position = clipInfo["position"];
        begin = clipInfo["begin"];
        end = clipInfo["end"];
    }

    onPositionChanged: {
        clipInfo["position"] = position;
    }

    onBeginChanged: {
        clipInfo["begin"] = begin;
    }

    onEndChanged: {
        clipInfo["end"] = end;
    }

luyikei's avatar
luyikei committed
172 173
    onSelectedChanged: {
        if ( selected === true ) {
174
            selectedClips.push( uuid );
175

luyikei's avatar
luyikei committed
176
            var group = findGroup( uuid );
177
            for ( var i = 0; i < ( group ? group.length : 0 ); ++i ) {
luyikei's avatar
luyikei committed
178 179 180 181 182 183
                var clipItem = findClipItem( group[i] );
                if ( clipItem )
                    clipItem.selected = true;
            }
            selectLinkedClip();
        }
184 185 186 187 188 189 190
        else
            for ( i = 0; i < selectedClips.length; ++i )
                if ( selectedClips[i] === uuid ) {
                    selectedClips.splice( i, 1 );
                    --i;
                    break;
                }
191 192
    }

luyikei's avatar
luyikei committed
193
    Component.onCompleted: {
194 195 196 197
        for ( var i = 0; i < allClips.length; ++i ) {
            if ( allClips[i].linkedClips.indexOf( uuid ) !== -1 )
                linkedClips.push( allClips[i].uuid );
        }
198 199 200 201
        if ( clipInfo["selected"] === false )
            selected = false;
        else
            selected = true;
luyikei's avatar
luyikei committed
202
        newTrackId = trackId;
luyikei's avatar
luyikei committed
203
        allClips.push( clip );
204
        allClipsDict[uuid] = clip;
205 206

        updateEffects( workflow.clipInfo( uuid ) );
207 208 209

        if ( uuid === "videoUuid" || uuid === "audioUuid" )
            return;
210
        thumbnailSource = "image://thumbnail/" + libraryUuid + "/0";
luyikei's avatar
luyikei committed
211 212 213
    }

    Component.onDestruction: {
luyikei's avatar
luyikei committed
214
        Drag.drop();
luyikei's avatar
luyikei committed
215
        selected = false;
luyikei's avatar
luyikei committed
216

217 218 219
        for ( var i = 0; i < linkedClips.length; ++i )
        {
            var linkedClip = linkedClips[i];
220 221
            var linkedClipItem = findClipItem( linkedClip );
            if ( linkedClipItem )
222 223 224 225 226 227 228 229
                for ( var j = 0; j < linkedClipItem.linkedClips.length; ++j )
                {
                    if ( linkedClipItem.linkedClips[j] === uuid )
                    {
                        linkedClipItem.linkedClips.splice( j, 1 );
                        break;
                    }
                }
230
        }
231

232
        for ( i = 0; i < allClips.length; ++i ) {
luyikei's avatar
luyikei committed
233 234 235 236 237
            if ( allClips[i] === clip ) {
                allClips.splice( i, 1 );
                return;
            }
        }
luyikei's avatar
luyikei committed
238 239
    }

luyikei's avatar
luyikei committed
240 241 242
    Drag.keys: ["Clip"]
    Drag.active: dragArea.drag.active

243 244 245 246
    ListModel {
        id: effects
    }

luyikei's avatar
luyikei committed
247 248 249 250
    Text {
        id: text
        color: "white"
        width: parent.width - 4
251
        height: font.pointSize + 4
luyikei's avatar
luyikei committed
252 253
        x: 4
        y: 4 - font.pointSize / 2
254
        font.pointSize: 7
255 256
        elide: Text.ElideRight
        wrapMode: Text.Wrap
luyikei's avatar
luyikei committed
257 258
    }

259 260 261 262 263 264 265 266 267
    Image {
        id: thumbnailImage
        x: 4
        anchors.top: text.bottom
        anchors.bottom: effectsItem.visible ? effectsItem.top : clip.bottom
        anchors.topMargin: 4
        anchors.bottomMargin: 4
        fillMode: Image.PreserveAspectFit
        visible: width < clip.width
268
        asynchronous: true
269 270
    }

luyikei's avatar
luyikei committed
271 272 273
    MouseArea {
        id: dragArea
        anchors.fill: parent
274
        drag.target: resizing ? null : parent
luyikei's avatar
luyikei committed
275 276
        drag.minimumX: 0
        acceptedButtons: Qt.LeftButton | Qt.RightButton
277 278 279 280 281 282
        hoverEnabled: true
        cursorShape: Qt.OpenHandCursor

        property bool resizing: false

        onPositionChanged: {
luyikei's avatar
luyikei committed
283 284 285
            if ( isCutMode === true )
                return;

luyikei's avatar
luyikei committed
286
            // If it's too short, don't resize.
287 288 289 290 291 292 293 294
            if ( width < 6 ) {
                resizing = false;
                return;
            }

            if ( dragArea.pressed === true ) {
                // Handle resizing
                if ( resizing === true ) {
295 296 297
                    var oldPos = position;
                    var oldBegin = begin;
                    var oldEnd = end;
298
                    if ( mouseX < width / 2 ) {
299 300
                        var newPos = position + ptof( mouseX );
                        var newBegin = begin + ptof( mouseX );
luyikei's avatar
luyikei committed
301
                        if ( newBegin < 0 || newPos < 0 || newBegin >= end )
302
                            return;
luyikei's avatar
luyikei committed
303 304
                        begin = newBegin;
                        position = newPos;
305 306
                    }
                    else {
307
                        var newEnd = begin + ptof( mouseX );
luyikei's avatar
luyikei committed
308
                        if ( newEnd <= begin || newEnd + 1 > clipInfo["length"] )
309
                            return;
luyikei's avatar
luyikei committed
310
                        end = newEnd;
311
                    }
312
                    resizeLinkedClips(oldPos, oldBegin, oldEnd);
313 314 315 316 317 318 319 320 321
                }
            }
            else {
                if ( mouseX < 3 || ( clip.width - mouseX ) < 3 )
                    resizing = true;
                else
                    resizing = false;
            }
        }
luyikei's avatar
luyikei committed
322 323

        onPressed: {
324 325
            clip.Drag.hotSpot = Qt.point( mouseX, clip.height / 2 );

326 327 328 329 330 331 332 333
            if ( mouse.button & Qt.LeftButton ) {
                if ( selected === false ) {
                    if ( !( mouse.modifiers & Qt.ControlModifier ) )
                        clearSelectedClips();
                    selected = true;
                }
                page.dragging = true;
            }
luyikei's avatar
luyikei committed
334 335 336 337
        }

        onClicked: {
            if ( mouse.button & Qt.RightButton ) {
luyikei's avatar
luyikei committed
338
                clipContextMenu.popup();
luyikei's avatar
luyikei committed
339
            }
luyikei's avatar
luyikei committed
340 341 342 343 344 345 346
            else if ( isCutMode === true ) {
                var newClipPos = position + ptof( mouseX );
                var newClipBegin = begin + ptof( mouseX );
                if ( newClipPos - position < 1 || end - newClipBegin < 1 )
                    return;
                workflow.splitClip( uuid, newClipPos, newClipBegin );
            }
347
            page.dragging = false;
luyikei's avatar
luyikei committed
348 349 350
        }

        onReleased: {
351
            // Don't trigger event if the mouse didn't move
luyikei's avatar
luyikei committed
352
            if ( resizing === true && isCutMode === false )
luyikei's avatar
luyikei committed
353
                resize();
354
            else if ( dragArea.drag.active )
luyikei's avatar
luyikei committed
355
                dragFinished();
luyikei's avatar
luyikei committed
356
        }
357 358 359 360

        states: [
            State {
                name: "Normal"
luyikei's avatar
luyikei committed
361 362 363 364 365
                when: isCutMode
                PropertyChanges { target: dragArea; cursorShape: Qt.ArrowCursor }
            },
            State {
                name: "Move"
366 367 368 369 370 371 372 373 374 375 376 377 378 379
                when: !dragArea.pressed && !dragArea.resizing
                PropertyChanges { target: dragArea; cursorShape: Qt.OpenHandCursor }
            },
            State {
                name: "Resizing"
                when: dragArea.resizing
                PropertyChanges { target: dragArea; cursorShape: Qt.SizeHorCursor }
            },
            State {
                name: "Dragging"
                when: dragArea.pressed && !dragArea.resizing
                PropertyChanges { target: dragArea; cursorShape: Qt.ClosedHandCursor }
            }
        ]
luyikei's avatar
luyikei committed
380 381
    }

382 383 384 385 386 387 388 389 390 391
    DropArea {
        id: effectDropArea
        anchors.fill: parent
        keys: ["vlmc/effect_name"]

        onDropped: {
            workflow.addEffect( uuid, drop.getDataAsString( "vlmc/effect_name" ) );
        }
    }

392 393
    Rectangle {
        id: effectsItem
394
        height: 6
395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
        width: clip.width
        anchors.bottom: clip.bottom
        visible: effectsTextItem.text ? true : false
        radius: 2
        gradient: Gradient {
            GradientStop {
                id: eGStop1
                position: 0.00;
            }
            GradientStop {
                id: eGStop2
                position: 1.00;
            }
        }

        property alias text: effectsTextItem.text

        Text {
            id: effectsTextItem
            width: clip.width
            color: "white"
            horizontalAlignment: Text.AlignHCenter
            font.pointSize: parent.height - 2
            anchors.centerIn: parent
            elide: Text.ElideRight
        }

        MouseArea {
            id: effectsItemMouseArea
            anchors.fill: parent
            hoverEnabled: true
            onClicked: {
427
                workflow.showEffectStack( uuid );
428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449
            }
        }

        states: [
            State {
                when: effectsItemMouseArea.pressed
                PropertyChanges { target: eGStop1; color: "#220f3c" }
                PropertyChanges { target: eGStop2; color: "#24245c" }
            },
            State {
                when: effectsItemMouseArea.containsMouse
                PropertyChanges { target: eGStop1; color: "#46469F" }
                PropertyChanges { target: eGStop2; color: "#32215a" }
            },
            State {
                when: !effectsItemMouseArea.containsMouse
                PropertyChanges { target: eGStop1; color: "#24245c" }
                PropertyChanges { target: eGStop2; color: "#200f3c" }
            }
        ]
    }

luyikei's avatar
luyikei committed
450 451
    ClipContextMenu {
        id: clipContextMenu
452
        clip: clip
luyikei's avatar
luyikei committed
453 454
    }

luyikei's avatar
luyikei committed
455
    states: [
456 457 458 459 460 461
        State {
            name: "EffectDrop"
            when: effectDropArea.containsDrag
            PropertyChanges { target: gStop1; color: "#427080" }
            PropertyChanges { target: gStop2; color: "#225060" }
        },
luyikei's avatar
luyikei committed
462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484
        State {
            name: "Selected"
            when: selected
            PropertyChanges { target: gStop1; color: "#6498c8" }
            PropertyChanges { target: gStop2; color: "#427080" }
        },
        State {
            name: "Unselected"
            when: !selected
            PropertyChanges { target: gStop1; color: "#4276a6" }
            PropertyChanges { target: gStop2; color: "#1f546f" }
        }
    ]

    transitions: Transition {
        PropertyAnimation {
            properties: "color"
            easing.type: Easing.InOutQuad
            duration: 100
        }
    }
}