Clip.qml 12.3 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 23 24
    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

    property alias name: text.text
25
    property alias thumbnailSource: thumbnailImage.source
luyikei's avatar
luyikei committed
26
    property int trackId
luyikei's avatar
luyikei committed
27
    // Usualy it is trackId, the clip will be moved to the new track immediately.
luyikei's avatar
luyikei committed
28 29 30 31
    property int newTrackId
    property int position
    property int begin
    property int end
luyikei's avatar
luyikei committed
32
    property string uuid
luyikei's avatar
luyikei committed
33 34
    property string linkedClip // Uuid
    property bool linked: false
luyikei's avatar
luyikei committed
35 36 37 38 39
    property string type
    property bool selected: false

    property var clipInfo

luyikei's avatar
luyikei committed
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
    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 move() {
        moveClipTo( track.type, uuid, newTrackId, position );
    }

    function resize() {
        // This function updates Backend
        workflow.resizeClip( uuid, begin, end, position )
    }

    function selectLinkedClip() {
        if ( selected === true && linked === true && linkedClip )
            findClipItem( linkedClip ).selected = true;
    }

luyikei's avatar
luyikei committed
67 68 69 70
    function updateEffects( clipInfo ) {
        if ( !clipInfo["filters"] )
            return;

71
        var str = "";
luyikei's avatar
luyikei committed
72
        for ( var i = 0; i < clipInfo["filters"].length; ++i ) {
73 74 75
            str += clipInfo["filters"][i]["identifier"];
            if ( i < clipInfo["filters"].length - 1 )
                str += ", "
luyikei's avatar
luyikei committed
76
        }
77
        effectsItem.text = str;
luyikei's avatar
luyikei committed
78 79
    }

80 81 82 83
    function updateThumbnail( pos ) {
        thumbnailSource = "image://thumbnail/" + uuid + "/" + pos;
    }

luyikei's avatar
luyikei committed
84
    onXChanged: {
85 86 87
        if ( sView.width - initPosOfCursor < width )
            return;

luyikei's avatar
luyikei committed
88 89 90
        if ( sView.flickableItem.contentX + sView.width <
                x + width + initPosOfCursor + sView.sViewPadding )
            Drag.hotSpot.x = 0;
91
        else if ( sView.flickableItem.contentX + sView.sViewPadding > x )
luyikei's avatar
luyikei committed
92 93 94
            Drag.hotSpot.x = width;
    }

luyikei's avatar
luyikei committed
95 96 97 98 99 100 101 102 103 104 105 106
    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
107 108 109 110 111 112 113
    onClipInfoChanged: {
        if ( !clipInfo )
            return;

        position = clipInfo["position"];
        begin = clipInfo["begin"];
        end = clipInfo["end"];
luyikei's avatar
luyikei committed
114
        linkedClip = clipInfo["linkedClip"];
luyikei's avatar
luyikei committed
115 116 117 118 119 120 121 122 123 124 125 126 127 128
    }

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

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

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

luyikei's avatar
luyikei committed
129 130 131 132 133
    onLinkedClipChanged: {
        clipInfo["linkedClip"] = linkedClip;
        if ( linkedClip ) {
            linked = true;
            var linkedClipItem = findClipItem( linkedClip );
134 135
            if ( linkedClipItem ) {
                linkedClipItem.linkedClip = clip.uuid;
luyikei's avatar
luyikei committed
136
                linkedClipItem.linked = true;
137
            }
luyikei's avatar
luyikei committed
138 139 140 141 142 143 144 145 146 147 148
        }
        else
            linked = false;
    }

    onLinkedChanged: {
        selectLinkedClip();

        if ( !linkedClip )
            return;

luyikei's avatar
luyikei committed
149 150 151 152
        var linkedClipItem = findClipItem( linkedClip );
        if ( !linkedClipItem )
            return;

luyikei's avatar
luyikei committed
153
        if ( linked === true )
luyikei's avatar
luyikei committed
154
            linkedClipItem.linked = true;
luyikei's avatar
luyikei committed
155
        else
luyikei's avatar
luyikei committed
156
            linkedClipItem.linked = false;
luyikei's avatar
luyikei committed
157 158
    }

luyikei's avatar
luyikei committed
159 160 161 162 163 164
    onSelectedChanged: {
        for ( var i = 0; i < selectedClips.length; ++i )
            if ( !selectedClips[i] || selectedClips[i] === clip ) {
                selectedClips.splice( i, 1 );
                --i;
            }
luyikei's avatar
luyikei committed
165

luyikei's avatar
luyikei committed
166 167
        if ( selected === true ) {
            selectedClips.push( clip );
luyikei's avatar
luyikei committed
168

luyikei's avatar
luyikei committed
169 170 171 172 173 174 175 176
            var group = findGroup( uuid );
            for ( i = 0; i < ( group ? group.length : 0 ); ++i ) {
                var clipItem = findClipItem( group[i] );
                if ( clipItem )
                    clipItem.selected = true;
            }
            selectLinkedClip();
        }
luyikei's avatar
luyikei committed
177 178
    }

luyikei's avatar
luyikei committed
179
    Component.onCompleted: {
180 181 182 183
        if ( clipInfo["selected"] === false )
            selected = false;
        else
            selected = true;
luyikei's avatar
luyikei committed
184
        newTrackId = trackId;
luyikei's avatar
luyikei committed
185
        allClips.push( clip );
luyikei's avatar
luyikei committed
186 187

        updateEffects( workflow.clipInfo( uuid ) );
188 189 190 191

        if ( uuid === "videoUuid" || uuid === "audioUuid" )
            return;

192 193
        if ( thumbnailProvider.hasImage( uuid, begin ) )
            updateThumbnail( begin );
194
        else
195
            workflow.takeThumbnail( uuid, begin );
luyikei's avatar
luyikei committed
196 197 198
    }

    Component.onDestruction: {
luyikei's avatar
luyikei committed
199
        Drag.drop();
luyikei's avatar
luyikei committed
200
        selected = false;
luyikei's avatar
luyikei committed
201

202 203 204 205 206
        if ( linkedClip ) {
            var linkedClipItem = findClipItem( linkedClip );
            if ( linkedClipItem )
                linkedClipItem.linkedClip = "";
        }
207

luyikei's avatar
luyikei committed
208 209 210 211 212 213
        for ( var i = 0; i < allClips.length; ++i ) {
            if ( allClips[i] === clip ) {
                allClips.splice( i, 1 );
                return;
            }
        }
luyikei's avatar
luyikei committed
214 215
    }

luyikei's avatar
luyikei committed
216 217 218
    Drag.keys: ["Clip"]
    Drag.active: dragArea.drag.active

luyikei's avatar
luyikei committed
219 220 221 222
    ListModel {
        id: effects
    }

luyikei's avatar
luyikei committed
223 224 225 226
    Text {
        id: text
        color: "white"
        width: parent.width - 4
227
        height: font.pointSize + 4
luyikei's avatar
luyikei committed
228 229
        x: 4
        y: 4 - font.pointSize / 2
230
        font.pointSize: 7
luyikei's avatar
luyikei committed
231 232
        elide: Text.ElideRight
        wrapMode: Text.Wrap
luyikei's avatar
luyikei committed
233 234
    }

235 236 237 238 239 240 241 242 243 244 245
    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
    }

luyikei's avatar
luyikei committed
246 247 248
    MouseArea {
        id: dragArea
        anchors.fill: parent
luyikei's avatar
luyikei committed
249
        drag.target: resizing ? null : parent
luyikei's avatar
luyikei committed
250 251
        drag.minimumX: 0
        acceptedButtons: Qt.LeftButton | Qt.RightButton
luyikei's avatar
luyikei committed
252 253 254 255 256 257
        hoverEnabled: true
        cursorShape: Qt.OpenHandCursor

        property bool resizing: false

        onPositionChanged: {
luyikei's avatar
luyikei committed
258 259 260
            if ( isCutMode === true )
                return;

luyikei's avatar
luyikei committed
261
            // If it's too short, don't resize.
luyikei's avatar
luyikei committed
262 263 264 265 266 267 268 269 270
            if ( width < 6 ) {
                resizing = false;
                return;
            }

            if ( dragArea.pressed === true ) {
                // Handle resizing
                if ( resizing === true ) {
                    if ( mouseX < width / 2 ) {
271 272
                        var newPos = position + ptof( mouseX );
                        var newBegin = begin + ptof( mouseX );
luyikei's avatar
luyikei committed
273
                        if ( newBegin < 0 || newPos < 0 || newBegin >= end )
luyikei's avatar
luyikei committed
274
                            return;
luyikei's avatar
luyikei committed
275 276
                        begin = newBegin;
                        position = newPos;
luyikei's avatar
luyikei committed
277 278
                    }
                    else {
279
                        var newEnd = begin + ptof( mouseX );
luyikei's avatar
luyikei committed
280
                        if ( newEnd <= begin || newEnd + 1 > clipInfo["length"] )
luyikei's avatar
luyikei committed
281
                            return;
luyikei's avatar
luyikei committed
282
                        end = newEnd;
luyikei's avatar
luyikei committed
283 284 285 286 287 288 289 290 291 292
                    }
                }
            }
            else {
                if ( mouseX < 3 || ( clip.width - mouseX ) < 3 )
                    resizing = true;
                else
                    resizing = false;
            }
        }
luyikei's avatar
luyikei committed
293 294

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

luyikei's avatar
luyikei committed
297 298 299 300 301 302 303 304 305 306 307 308
            if ( selected === true )
                return;

            if ( !( mouse.modifiers & Qt.ControlModifier ) )
                clearSelectedClips();

            if ( mouse.button & Qt.LeftButton )
                selected = true;
        }

        onClicked: {
            if ( mouse.button & Qt.RightButton ) {
luyikei's avatar
luyikei committed
309
                clipContextMenu.popup();
luyikei's avatar
luyikei committed
310
            }
luyikei's avatar
luyikei committed
311 312 313 314 315 316 317
            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 );
            }
luyikei's avatar
luyikei committed
318 319 320
        }

        onReleased: {
luyikei's avatar
luyikei committed
321
            if ( resizing === true && isCutMode === false )
luyikei's avatar
luyikei committed
322 323 324
                resize();
            else
                dragFinished();
luyikei's avatar
luyikei committed
325
        }
luyikei's avatar
luyikei committed
326 327 328 329

        states: [
            State {
                name: "Normal"
luyikei's avatar
luyikei committed
330 331 332 333 334
                when: isCutMode
                PropertyChanges { target: dragArea; cursorShape: Qt.ArrowCursor }
            },
            State {
                name: "Move"
luyikei's avatar
luyikei committed
335 336 337 338 339 340 341 342 343 344 345 346 347 348
                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
349 350
    }

351 352 353 354 355 356 357 358 359 360
    DropArea {
        id: effectDropArea
        anchors.fill: parent
        keys: ["vlmc/effect_name"]

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

361 362
    Rectangle {
        id: effectsItem
363
        height: 6
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
        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: {
396
                workflow.showEffectStack( uuid );
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
            }
        }

        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
419 420
    ClipContextMenu {
        id: clipContextMenu
luyikei's avatar
luyikei committed
421
        clip: clip
luyikei's avatar
luyikei committed
422 423
    }

luyikei's avatar
luyikei committed
424
    states: [
425 426 427 428 429 430
        State {
            name: "EffectDrop"
            when: effectDropArea.containsDrag
            PropertyChanges { target: gStop1; color: "#427080" }
            PropertyChanges { target: gStop2; color: "#225060" }
        },
luyikei's avatar
luyikei committed
431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453
        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
        }
    }
}