Clip.qml 38.4 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
 * Copyright (c) 2013-2016 Meltytech, LLC
 * Author: Dan Dennedy <dan@dennedy.org>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

19
20
import QtQuick 2.11
import QtQuick.Controls 2.4
21
import Kdenlive.Controls 1.0
22
import QtQml.Models 2.11
23
import QtQuick.Window 2.2
24
import 'Timeline.js' as Logic
25
import com.enums 1.0
26
27
28

Rectangle {
    id: clipRoot
29
    property real timeScale: 1.0
30
31
32
    property string clipName: ''
    property string clipResource: ''
    property string mltService: ''
33
    property string effectNames
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
34
    property int modelStart
35
    property real scrollX: 0
36
37
38
    property int inPoint: 0
    property int outPoint: 0
    property int clipDuration: 0
39
    property int maxDuration: 0
40
    property bool isAudio: false
41
    property int audioChannels
42
    property bool showKeyframes: false
43
    property bool isGrabbed: false
44
    property bool grouped: false
45
    property var markers
46
    property var keyframeModel
47
48
    property int clipStatus: 0
    property int itemType: 0
49
50
    property int fadeIn: 0
    property int fadeOut: 0
51
    property int binId: 0
52
    property int positionOffset: 0
53
    property var parentTrack
54
55
    property int trackIndex //Index in track repeater
    property int clipId     //Id of the clip in the model
56
57
58
    property int trackId: -1 // Id of the parent track in the model
    property int fakeTid: -1
    property int fakePosition: 0
59
    property int originalTrackId: -1
60
    property int originalX: x
61
62
    property int originalDuration: clipDuration
    property int lastValidDuration: clipDuration
63
    property int draggedX: x
64
    property bool selected: false
65
    property bool isLocked: parentTrack && parentTrack.isLocked == true
66
    property bool hasAudio
67
68
    property bool canBeAudio
    property bool canBeVideo
69
    property double speed: 1.0
70
    property color borderColor: 'black'
71
    property bool forceReloadThumb
72
    property bool isComposition: false
73
    property bool hideClipViews
74
    property var groupTrimData
75
    property int scrollStart: scrollView.flickableItem.contentX - clipRoot.modelStart * timeline.scaleFactor
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
76
    width : clipDuration * timeScale;
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
77
    opacity: dragProxyArea.drag.active && dragProxy.draggedItem == clipId ? 0.8 : 1.0
78

79
80
    signal trimmingIn(var clip, real newDuration, var mouse, bool shiftTrim, bool controlTrim)
    signal trimmedIn(var clip, bool shiftTrim, bool controlTrim)
81
    signal initGroupTrim(var clip)
82
83
    signal trimmingOut(var clip, real newDuration, var mouse, bool shiftTrim, bool controlTrim)
    signal trimmedOut(var clip, bool shiftTrim, bool controlTrim)
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
84

85
86
87
    onScrollStartChanged: {
        clipRoot.hideClipViews = scrollStart > width || scrollStart + scrollView.viewport.width < 0
    }
88

89
90
    onIsGrabbedChanged: {
        if (clipRoot.isGrabbed) {
91
            grabItem()
92
93
        } else {
            mouseArea.focus = false
94
95
96
        }
    }

97
98
99
100
101
    function grabItem() {
        clipRoot.forceActiveFocus()
        mouseArea.focus = true
    }

102
103
    function clearAndMove(offset) {
        controller.requestClearSelection()
104
105
        controller.requestClipMove(clipRoot.clipId, clipRoot.trackId, clipRoot.modelStart - offset, true, true, true)
        controller.requestAddToSelection(clipRoot.clipId)
106
107
    }

108
    onClipResourceChanged: {
109
        if (itemType == ProducerType.Color) {
110
            color: Qt.darker(getColor(), 1.5)
111
112
        }
    }
113
    ToolTip {
114
        visible: mouseArea.containsMouse && !dragProxyArea.pressed
115
116
117
118
119
120
121
122
        delay: 1000
        timeout: 5000
        background: Rectangle {
            color: activePalette.alternateBase
            border.color: activePalette.light
        }
        contentItem: Label {
            color: activePalette.text
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
123
            font.pointSize: root.fontUnit
124
            text: label.text + ' (' + timeline.simplifiedTC(clipRoot.inPoint) + '-' + timeline.simplifiedTC(clipRoot.outPoint) + ')'
125
126
127
        }
    }

128
    onKeyframeModelChanged: {
129
        if (effectRow.keyframecanvas) {
130
            console.log('keyframe model changed............')
131
132
            effectRow.keyframecanvas.requestPaint()
        }
133
134
    }

Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
135
136
    onClipDurationChanged: {
        width = clipDuration * timeScale;
137
138
139
140
        if (parentTrack && parentTrack.isAudio && thumbsLoader.item) {
            // Duration changed, we may need a different number of repeaters
            thumbsLoader.item.reload()
        }
141
    }
142

143
144
145
    onModelStartChanged: {
        x = modelStart * timeScale;
    }
146
147
148
149
150
    onFakePositionChanged: {
        x = fakePosition * timeScale;
    }
    onFakeTidChanged: {
        if (clipRoot.fakeTid > -1 && parentTrack) {
151
152
153
154
155
156
157
            if (clipRoot.parent != dragContainer) {
                var pos = clipRoot.mapToGlobal(clipRoot.x, clipRoot.y);
                clipRoot.parent = dragContainer
                pos = clipRoot.mapFromGlobal(pos.x, pos.y)
                clipRoot.x = pos.x
                clipRoot.y = pos.y
            }
158
159
            clipRoot.y = Logic.getTrackById(clipRoot.fakeTid).y
        }
160
161
    }

162
    onForceReloadThumbChanged: {
163
        // TODO: find a way to force reload of clip thumbs
164
165
166
        if (thumbsLoader.item) {
            thumbsLoader.item.reload()
        }
167
168
    }

169
    onTimeScaleChanged: {
170
        x = modelStart * timeScale;
171
        width = clipDuration * timeScale;
172
        labelRect.x = scrollX > modelStart * timeScale ? scrollX - modelStart * timeScale : clipRoot.border.width
173
174
    }
    onScrollXChanged: {
175
        labelRect.x = scrollX > modelStart * timeScale ? scrollX - modelStart * timeScale : clipRoot.border.width
176
177
    }

Vincent Pinon's avatar
Vincent Pinon committed
178
    border.color: selected ? root.selectionColor : grouped ? root.groupColor : borderColor
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
179
    border.width: isGrabbed ? 8 : 2
180

181
182
183
184
185
    function updateDrag() {
        var itemPos = mapToItem(tracksContainerArea, 0, 0, clipRoot.width, clipRoot.height)
        initDrag(clipRoot, itemPos, clipRoot.clipId, clipRoot.modelStart, clipRoot.trackId, false)
    }

186
    function getColor() {
187
188
189
        if (clipStatus == ClipState.Disabled) {
            return 'grey'
        }
190
        if (itemType == ProducerType.Color) {
191
192
193
            var color = clipResource.substring(clipResource.length - 9)
            if (color[0] == '#') {
                return color
194
            }
195
            return '#' + color.substring(color.length - 8, color.length - 2)
196
        }
197
        return isAudio? root.audioColor : root.videoColor
198
199
    }

200
/*    function reparent(track) {
201
        console.log('TrackId: ',trackId)
202
203
        parent = track
        height = track.height
204
        parentTrack = track
205
        trackId = parentTrack.trackId
206
        console.log('Reparenting clip to Track: ', trackId)
207
        //generateWaveform()
208
    }
209
*/
210
211
    property bool variableThumbs: (isAudio || itemType == ProducerType.Color || mltService === '')
    property bool isImage: itemType == ProducerType.Image
212
    property string baseThumbPath: variableThumbs ? '' : 'image://thumbnail/' + binId + '/' + documentId + '/' + (isImage ? '#0' : '#')
213

214
    DropArea { //Drop area for clips
215
216
217
        anchors.fill: clipRoot
        keys: 'kdenlive/effect'
        property string dropData
218
        property string dropSource
219
        property int dropRow: -1
220
221
        onEntered: {
            dropData = drag.getDataAsString('kdenlive/effect')
222
            dropSource = drag.getDataAsString('kdenlive/effectsource')
223
224
225
        }
        onDropped: {
            console.log("Add effect: ", dropData)
226
            if (dropSource == '') {
227
228
229
                // drop from effects list
                controller.addClipEffect(clipRoot.clipId, dropData);
            } else {
230
                controller.copyClipEffect(clipRoot.clipId, dropSource);
231
            }
232
            dropSource = ''
233
            dropRow = -1
234
            drag.acceptProposedAction
235
236
        }
    }
237
238
    MouseArea {
        id: mouseArea
239
        enabled: root.activeTool === 0
240
        anchors.fill: clipRoot
241
        acceptedButtons: Qt.RightButton
242
        hoverEnabled: root.activeTool === 0
243
        cursorShape: (trimInMouseArea.drag.active || trimOutMouseArea.drag.active)? Qt.SizeHorCursor : dragProxyArea.cursorShape
244
        onPressed: {
245
            root.autoScrolling = false
246
            if (mouse.button == Qt.RightButton) {
247
                if (timeline.selection.indexOf(clipRoot.clipId) == -1) {
248
                    controller.requestAddToSelection(clipRoot.clipId, true)
249
250
251
                }
                clipMenu.clipId = clipRoot.clipId
                clipMenu.clipStatus = clipRoot.clipStatus
252
                clipMenu.clipFrame = Math.round(mouse.x / timeline.scaleFactor)
253
254
255
256
257
                clipMenu.grouped = clipRoot.grouped
                clipMenu.trackId = clipRoot.trackId
                clipMenu.canBeAudio = clipRoot.canBeAudio
                clipMenu.canBeVideo = clipRoot.canBeVideo
                clipMenu.popup()
258
            }
259
        }
260
        Keys.onShortcutOverride: event.accepted = clipRoot.isGrabbed && (event.key === Qt.Key_Left || event.key === Qt.Key_Right || event.key === Qt.Key_Up || event.key === Qt.Key_Down || event.key === Qt.Key_Escape)
261
        Keys.onLeftPressed: {
262
263
            var offset = event.modifiers === Qt.ShiftModifier ? timeline.fps() : 1
            controller.requestClipMove(clipRoot.clipId, clipRoot.trackId, clipRoot.modelStart - offset, true, true, true);
264
265
        }
        Keys.onRightPressed: {
266
267
            var offset = event.modifiers === Qt.ShiftModifier ? timeline.fps() : 1
            controller.requestClipMove(clipRoot.clipId, clipRoot.trackId, clipRoot.modelStart + offset, true, true, true);
268
        }
269
270
271
272
273
274
        Keys.onUpPressed: {
            controller.requestClipMove(clipRoot.clipId, controller.getNextTrackId(clipRoot.trackId), clipRoot.modelStart, true, true, true);
        }
        Keys.onDownPressed: {
            controller.requestClipMove(clipRoot.clipId, controller.getPreviousTrackId(clipRoot.trackId), clipRoot.modelStart, true, true, true);
        }
275
276
        Keys.onEscapePressed: {
            timeline.grabCurrent()
277
            //focus = false
278
        }
279
        onPositionChanged: {
280
281
            var mapped = parentTrack.mapFromItem(clipRoot, mouse.x, mouse.y).x
            root.mousePosChanged(Math.round(mapped / timeline.scaleFactor))
282
        }
283
284
285
286
        onEntered: {
            var itemPos = mapToItem(tracksContainerArea, 0, 0, width, height)
            initDrag(clipRoot, itemPos, clipRoot.clipId, clipRoot.modelStart, clipRoot.trackId, false)
        }
287

288
289
        onExited: {
            endDrag()
290
        }
291
        onWheel: zoomByWheel(wheel)
292

293
294
        Item {
            // Thumbs container
295
            anchors.fill: parent
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
296
297
            anchors.leftMargin: parentTrack.isAudio ? 0 : clipRoot.border.width
            anchors.rightMargin: parentTrack.isAudio ? 0 : clipRoot.border.width
298
299
300
301
302
303
304
305
            anchors.topMargin: clipRoot.border.width
            anchors.bottomMargin: clipRoot.border.width
            clip: true
            Loader {
                id: thumbsLoader
                asynchronous: true
                visible: status == Loader.Ready
                anchors.fill: parent
306
                source: clipRoot.hideClipViews ? "" : parentTrack.isAudio ? (timeline.showAudioThumbnails ? "ClipAudioThumbs.qml" : "") : itemType == ProducerType.Color ? "" : timeline.showThumbnails ? "ClipThumbs.qml" : ""
307
            }
308
        }
309

310
311
312
313
        Item {
            // Clipping container
            id: container
            anchors.fill: parent
314
315
            anchors.margins: clipRoot.border.width
            //clip: true
316
            property bool showDetails: (!clipRoot.selected || !effectRow.visible) && container.height > 2.2 * labelRect.height
317
318

            Repeater {
319
                // Clip markers
320
321
322
323
                model: markers
                delegate:
                Item {
                    anchors.fill: parent
324
                    visible: markerBase.x >= 0 && markerBase.x < clipRoot.width
325
326
327
328
                    Rectangle {
                        id: markerBase
                        width: 1
                        height: parent.height
329
                        x: clipRoot.speed < 0 ? clipRoot.clipDuration * timeScale + (Math.round(model.frame / clipRoot.speed) - (clipRoot.maxDuration - clipRoot.outPoint)) * timeScale - clipRoot.border.width : (Math.round(model.frame / clipRoot.speed) - clipRoot.inPoint) * timeScale - clipRoot.border.width;
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
                        color: model.color
                    }
                    Rectangle {
                        visible: mlabel.visible
                        opacity: 0.7
                        x: markerBase.x
                        radius: 2
                        width: mlabel.width + 4
                        height: mlabel.height
                        anchors {
                            bottom: parent.verticalCenter
                        }
                        color: model.color
                        MouseArea {
                            z: 10
                            anchors.fill: parent
                            acceptedButtons: Qt.LeftButton
                            cursorShape: Qt.PointingHandCursor
                            hoverEnabled: true
349
                            onDoubleClicked: timeline.editMarker(clipRoot.clipId, model.frame)
350
                            onClicked: proxy.position = (clipRoot.x + markerBase.x) / timeline.scaleFactor
351
352
353
354
355
356
                        }
                    }
                    Text {
                        id: mlabel
                        visible: timeline.showMarkers && parent.width > width * 1.5
                        text: model.comment
357
                        font.pointSize: root.fontUnit
358
359
360
361
362
363
364
365
                        x: markerBase.x
                        anchors {
                            bottom: parent.verticalCenter
                            topMargin: 2
                            leftMargin: 2
                        }
                        color: 'white'
                    }
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
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
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
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
            MouseArea {
                // Left resize handle
                id: trimInMouseArea
                anchors.left: container.left
                anchors.leftMargin: -clipRoot.border.width
                height: parent.height
                width: root.baseUnit / 2
                enabled: !isLocked
                hoverEnabled: true
                drag.target: trimInMouseArea
                drag.axis: Drag.XAxis
                drag.smoothed: false
                property bool shiftTrim: false
                property bool controlTrim: false
                property bool sizeChanged: false
                cursorShape: (containsMouse ? Qt.SizeHorCursor : Qt.ClosedHandCursor);
                onPressed: {
                    root.autoScrolling = false
                    clipRoot.originalX = clipRoot.x
                    clipRoot.originalDuration = clipDuration
                    anchors.left = undefined
                    shiftTrim = mouse.modifiers & Qt.ShiftModifier
                    controlTrim = mouse.modifiers & Qt.ControlModifier
                    if (!shiftTrim && clipRoot.grouped) {
                        clipRoot.initGroupTrim(clipRoot)
                    }
                    trimIn.opacity = 0
                }
                onReleased: {
                    root.autoScrolling = timeline.autoScroll
                    anchors.left = parent.left
                    if (sizeChanged) {
                        clipRoot.trimmedIn(clipRoot, shiftTrim, controlTrim)
                        sizeChanged = false
                    }
                }
                onPositionChanged: {
                    if (mouse.buttons === Qt.LeftButton) {
                        var delta = Math.round(x / timeScale)
                        if (delta !== 0) {
                            if (maxDuration > 0 && delta < -inPoint) {
                                delta = -inPoint
                            }
                            var newDuration =  clipDuration - delta
                            sizeChanged = true
                            clipRoot.trimmingIn(clipRoot, newDuration, mouse, shiftTrim, controlTrim)
                        }
                    }
                }
                onEntered: {
                    if (!pressed) {
                        trimIn.opacity = 1
                    }
                }
                onExited: {
                    trimIn.opacity = 0
                }
                Rectangle {
                    id: trimIn
                    anchors.left: parent.left
                    width: clipRoot.border.width
                    height: parent.height
                    color: 'lawngreen'
                    opacity: 0
                    Drag.active: trimInMouseArea.drag.active
                    Drag.proposedAction: Qt.MoveAction
                    visible: trimInMouseArea.pressed || (root.activeTool === 0 && !mouseArea.drag.active && clipRoot.width > 4 * width)

                    ToolTip {
                        visible: trimInMouseArea.containsMouse && !trimInMouseArea.pressed
                        delay: 1000
                        timeout: 5000
                        background: Rectangle {
                            color: activePalette.alternateBase
                            border.color: activePalette.light
                        }
                        contentItem: Label {
                            color: activePalette.text
                            font.pointSize: root.fontUnit
                            text: i18n("In:%1\nPosition:%2", timeline.simplifiedTC(clipRoot.inPoint),timeline.simplifiedTC(clipRoot.modelStart))
                        }
                    }
                }
            }

            MouseArea {
                // Right resize handle
                id: trimOutMouseArea
                anchors.right: parent.right
                anchors.rightMargin: -clipRoot.border.width
                anchors.top: parent.top
                height: parent.height
                width: root.baseUnit / 2
                hoverEnabled: true
                enabled: !isLocked
                property bool shiftTrim: false
                property bool controlTrim: false
                property bool sizeChanged: false
                cursorShape: (containsMouse ? Qt.SizeHorCursor : Qt.ClosedHandCursor);
                drag.target: trimOutMouseArea
                drag.axis: Drag.XAxis
                drag.smoothed: false

                onPressed: {
                    root.autoScrolling = false
                    clipRoot.originalDuration = clipDuration
                    anchors.right = undefined
                    shiftTrim = mouse.modifiers & Qt.ShiftModifier
                    controlTrim = mouse.modifiers & Qt.ControlModifier
                    if (!shiftTrim && clipRoot.grouped) {
                        clipRoot.initGroupTrim(clipRoot)
                    }
                    trimOut.opacity = 0
                }
                onReleased: {
                    root.autoScrolling = timeline.autoScroll
                    anchors.right = parent.right
                    if (sizeChanged) {
                        clipRoot.trimmedOut(clipRoot, shiftTrim, controlTrim)
                        sizeChanged = false
                    }
                }
                onPositionChanged: {
                    if (mouse.buttons === Qt.LeftButton) {
                        var newDuration = Math.round((x + width) / timeScale)
                        if (maxDuration > 0 && newDuration > maxDuration - inPoint) {
                            newDuration = maxDuration - inPoint
                        }
                        if (newDuration != clipDuration) {
                            sizeChanged = true
                            clipRoot.trimmingOut(clipRoot, newDuration, mouse, shiftTrim, controlTrim)
                        }
                    }
                }
                onEntered: {
                    if (!pressed) {
                        trimOut.opacity = 1
                    }
                }
                onExited: trimOut.opacity = 0
                ToolTip {
                    visible: trimOutMouseArea.containsMouse && !trimOutMouseArea.pressed
                    delay: 1000
                    timeout: 5000
                    background: Rectangle {
                        color: activePalette.alternateBase
                        border.color: activePalette.light
                    }
                    contentItem: Label {
                        color: activePalette.text
                        font.pointSize: root.fontUnit
                        text: i18n("Out: ") + timeline.simplifiedTC(clipRoot.outPoint)
                    }
                }
                Rectangle {
                    id: trimOut
                    anchors.right: parent.right
                    width: clipRoot.border.width
                    height: parent.height
                    color: 'red'
                    opacity: 0
                    Drag.active: trimOutMouseArea.drag.active
                    Drag.proposedAction: Qt.MoveAction
                    visible: trimOutMouseArea.pressed || (root.activeTool === 0 && !mouseArea.drag.active && clipRoot.width > 4 * width)
                }
            }

            TimelineTriangle {
                // Green fade in triangle
                id: fadeInTriangle
                fillColor: 'green'
                width: Math.min(clipRoot.fadeIn * timeScale, container.width)
                height: parent.height
                anchors.left: parent.left
                anchors.top: parent.top
                opacity: 0.4
            }

            TimelineTriangle {
                // Red fade out triangle
                id: fadeOutCanvas
                fillColor: 'red'
                width: Math.min(clipRoot.fadeOut * timeScale, container.width)
                height: parent.height
                anchors.right: parent.right
                anchors.top: parent.top
                opacity: 0.4
                transform: Scale { xScale: -1; origin.x: fadeOutCanvas.width / 2}
            }

            Item {
                // Clipping container for clip names
                anchors.fill: parent
                clip: true
                Rectangle {
                    // Clip name background
                    id: labelRect
                    color: clipRoot.selected ? 'darkred' : '#66000000'
                    y: 0
                    width: label.width + 2 * clipRoot.border.width
                    height: label.height
                    visible: clipRoot.width > width / 2
                    Text {
                        // Clip name text
                        id: label
                        text: clipName + (clipRoot.speed != 1.0 ? ' [' + Math.round(clipRoot.speed*100) + '%]': '')
                        font.pointSize: root.fontUnit
                        anchors {
                            top: labelRect.top
                            left: labelRect.left
                            leftMargin: clipRoot.border.width
                        }
                        color: 'white'
                        //style: Text.Outline
                        //styleColor: 'black'
                    }
                }

                Rectangle {
                    // Offset info
                    id: offsetRect
                    color: 'darkgreen'
                    width: offsetLabel.width + radius
                    height: offsetLabel.height
                    radius: height/3
                    x: labelRect.width + 4
                    y: 2
                    visible: labelRect.visible && positionOffset != 0
                    MouseArea {
                        id: offsetArea
                        hoverEnabled: true
                        cursorShape: Qt.PointingHandCursor
                        anchors.fill: parent
                        onClicked: {
                            clearAndMove(positionOffset)
                        }
                        ToolTip {
                            visible: offsetArea.containsMouse
                            delay: 1000
                            timeout: 5000
                            background: Rectangle {
                                color: activePalette.alternateBase
                                border.color: activePalette.light
                            }
                            contentItem: Label {
                                color: activePalette.text
                                font.pointSize: root.fontUnit
                                text: positionOffset < 0 ? i18n("Offset: -%1", timeline.simplifiedTC(-positionOffset)) : i18n("Offset: %1", timeline.simplifiedTC(positionOffset))
                            }
                        }
                        Text {
                            id: offsetLabel
                            text: positionOffset
                            font.pointSize: root.fontUnit
                            anchors {
                                horizontalCenter: parent.horizontalCenter
                                topMargin: 1
                                leftMargin: 1
                            }
                            color: 'white'
                            style: Text.Outline
                            styleColor: 'black'
                        }
                    }
                }

                Rectangle {
                    // effect names background
                    id: effectsRect
                    color: '#555555'
                    width: effectLabel.width + 2
                    height: effectLabel.height
                    x: labelRect.x
                    anchors.top: labelRect.bottom
                    visible: labelRect.visible && clipRoot.effectNames != '' && container.showDetails
                    Text {
                        // Effect names text
                        id: effectLabel
                        text: clipRoot.effectNames
                        font.pointSize: root.fontUnit
                        visible: effectsRect.visible
                        anchors {
                            top: effectsRect.top
                            left: effectsRect.left
                            leftMargin: 1
                            // + ((isAudio || !settings.timelineShowThumbnails) ? 0 : inThumbnail.width) + 1
                        }
                        color: 'white'
                        //style: Text.Outline
                        styleColor: 'black'
                    }
                }
            }
662
663
664
665
666
667
668
669
670
671
672
673

            KeyframeView {
                id: effectRow
                clip: true
                anchors.fill: parent
                visible: clipRoot.showKeyframes && clipRoot.keyframeModel
                selected: clipRoot.selected
                inPoint: clipRoot.inPoint
                outPoint: clipRoot.outPoint
                masterObject: clipRoot
                kfrModel: clipRoot.hideClipViews ? 0 : clipRoot.keyframeModel
            }
674
        }
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706

        states: [
            State {
                name: 'locked'
                when: isLocked
                PropertyChanges {
                    target: clipRoot
                    color: root.lockedColor
                    opacity: 0.8
                    z: 0
                }
            },
            State {
                name: 'normal'
                when: clipRoot.selected === false
                PropertyChanges {
                    target: clipRoot
                    color: Qt.darker(getColor(), 1.5)
                    z: 0
                }
            },
            State {
                name: 'selected'
                when: clipRoot.selected === true
                PropertyChanges {
                    target: clipRoot
                    color: getColor()
                    z: 3
                }
            }
        ]

707
708
709
        MouseArea {
            // Add start composition area
            id: compInArea
710
711
            anchors.left: parent.left
            anchors.bottom: parent.bottom
712
            width: Math.min(root.baseUnit, container.height / 3)
713
            height: width
714
715
716
717
718
719
720
721
722
723
724
725
726
727
            hoverEnabled: true
            cursorShape: Qt.PointingHandCursor
            visible: !clipRoot.isAudio
            enabled: !clipRoot.isAudio && dragProxy.draggedItem === clipRoot.clipId && compositionIn.visible
            onPressed: {
                timeline.addCompositionToClip('', clipRoot.clipId, 0)
            }
            ToolTip {
                visible: compInArea.containsMouse && !dragProxyArea.pressed
                delay: 1000
                timeout: 5000
                background: Rectangle {
                    color: activePalette.alternateBase
                    border.color: activePalette.light
728
                }
729
730
731
732
                contentItem: Label {
                    color: activePalette.text
                    font.pointSize: root.fontUnit
                    text: i18n("Click to add composition")
733
734
                }
            }
735
736
737
738
739
            Rectangle {
                // Start composition box
                id: compositionIn
                anchors.bottom: parent.bottom
                anchors.left: parent.left
740
                width: compInArea.containsMouse ? parent.width : 5
741
742
743
744
745
746
747
748
                height: width
                radius: width / 2
                visible: clipRoot.width > 4 * parent.width && mouseArea.containsMouse && !dragProxyArea.pressed
                color: Qt.darker('mediumpurple')
                border.width: 3
                border.color: 'mediumpurple'
                Behavior on width { NumberAnimation { duration: 100 } }
            }
749
        }
750
751
752
753

        MouseArea {
            // Add end composition area
            id: compOutArea
754
755
            anchors.right: parent.right
            anchors.bottom: parent.bottom
756
            width: Math.min(root.baseUnit, container.height / 3)
757
            height: width
758
759
760
761
762
763
            hoverEnabled: true
            cursorShape: Qt.PointingHandCursor
            enabled: !clipRoot.isAudio && dragProxy.draggedItem === clipRoot.clipId && compositionOut.visible
            visible: !clipRoot.isAudio
            onPressed: {
                timeline.addCompositionToClip('', clipRoot.clipId, clipRoot.clipDuration - 1)
764
            }
765
            ToolTip {
766
                visible: compOutArea.containsMouse && !dragProxyArea.pressed
767
768
769
770
771
772
773
774
775
                delay: 1000
                timeout: 5000
                background: Rectangle {
                    color: activePalette.alternateBase
                    border.color: activePalette.light
                }
                contentItem: Label {
                    color: activePalette.text
                    font.pointSize: root.fontUnit
776
                    text: i18n("Click to add composition")
777
778
                }
            }
779
780
781
782
783
            Rectangle {
                // End composition box
                id: compositionOut
                anchors.bottom: parent.bottom
                anchors.right: parent.right
784
                width: compOutArea.containsMouse ? parent.height : 5
785
786
787
788
789
790
791
                height: width
                radius: width / 2
                visible: clipRoot.width > 4 * parent.width && mouseArea.containsMouse && !dragProxyArea.pressed
                color: Qt.darker('mediumpurple')
                border.width: 3
                border.color: 'mediumpurple'
                Behavior on width { NumberAnimation { duration: 100 } }
792
793
            }
        }
794
795
796

        MouseArea {
            // Fade out drag zone
797
            id: fadeOutMouseArea
798
799
800
            anchors.right: parent.right
            anchors.rightMargin: clipRoot.fadeOut <= 0 ? 0 : fadeOutCanvas.width - width / 2
            anchors.top: parent.top
801
            width: Math.min(root.baseUnit, container.height / 3)
802
            height: width
803
            hoverEnabled: true
804
805
            cursorShape: Qt.PointingHandCursor
            drag.target: fadeOutMouseArea
806
            drag.axis: Drag.XAxis
807
            drag.minimumX: - Math.ceil(width / 2)
808
809
            drag.maximumX: container.width + Math.ceil(width / 4)
            visible: clipRoot.width > 3 * width && mouseArea.containsMouse && !dragProxyArea.pressed
810
811
            property int startFadeOut
            property int lastDuration: -1
812
813
814
815
            drag.smoothed: false
            onClicked: {
                if (clipRoot.fadeOut == 0) {
                    timeline.adjustFade(clipRoot.clipId, 'fadeout', 0, -2)
816
817
                }
            }
818
            onPressed: {
819
                root.autoScrolling = false
820
821
                startFadeOut = clipRoot.fadeOut
                anchors.right = undefined
822
                fadeOutCanvas.opacity = 0.6
823
824
            }
            onReleased: {
825
                fadeOutCanvas.opacity = 0.4
826
                root.autoScrolling = timeline.autoScroll
827
                anchors.right = parent.right
828
829
830
                var duration = clipRoot.fadeOut
                if (duration > 0) {
                    duration += 1
831
                }
832
833
                timeline.adjustFade(clipRoot.clipId, 'fadeout', duration, startFadeOut)
                bubbleHelp.hide()
834
835
836
            }
            onPositionChanged: {
                if (mouse.buttons === Qt.LeftButton) {
837
                    var delta = clipRoot.clipDuration - Math.floor((x + width / 2 - clipRoot.border.width)/ timeScale)
838
839
840
841
842
843
844
845
                    var duration = Math.max(0, delta)
                    duration = Math.min(duration, clipRoot.clipDuration)
                    if (lastDuration != duration) {
                        lastDuration = duration
                        timeline.adjustFade(clipRoot.clipId, 'fadeout', duration, -1)
                        // Show fade duration as time in a "bubble" help.
                        var s = timeline.simplifiedTC(clipRoot.fadeOut + (clipRoot.fadeOut > 0 ? 1 : 0))
                        bubbleHelp.show(clipRoot.x + x, parentTrack.y + parentTrack.height, s)
846
                    }
847
848
                }
            }
849
850
            Rectangle {
                id: fadeOutControl
851
852
853
                anchors.top: parent.top
                anchors.right: clipRoot.fadeOut > 0 ? undefined : parent.right
                anchors.horizontalCenter: clipRoot.fadeOut > 0 ? parent.horizontalCenter : undefined
854
                width: fadeOutMouseArea.containsMouse || Drag.active ? parent.width : 5
855
                height: width
856
                radius: width / 2
857
858
                color: 'darkred'
                border.width: 3
859
860
861
                border.color: 'red'
                enabled: !isLocked && !dragProxy.isComposition
                Drag.active: fadeOutMouseArea.drag.active
862
                Behavior on width { NumberAnimation { duration: 100 } }
863
864
865
866
867
                Rectangle {
                    id: fadeOutMarker
                    anchors.horizontalCenter: parent.horizontalCenter
                    anchors.top: parent.top
                    color: 'red'
868
                    height: container.height
869
                    width: 1
870
                    visible : clipRoot.fadeOut > 0 && (fadeOutMouseArea.containsMouse || fadeOutMouseArea.drag.active)
871
                }
872
            }
873
        }
874

875
        MouseArea {
876
            // Fade in drag zone
877
            id: fadeInMouseArea
878
879
880
            anchors.left: container.left
            anchors.leftMargin: clipRoot.fadeIn <= 0 ? 0 : (fadeInTriangle.width - width / 2)
            anchors.top: parent.top
881
            width: Math.min(root.baseUnit, container.height / 3)
882
            height: width
883
            hoverEnabled: true
884
885
886
887
            cursorShape: Qt.PointingHandCursor
            drag.target: fadeInMouseArea
            drag.minimumX: - Math.ceil(width / 2)
            drag.maximumX: container.width - width / 2
888
            drag.axis: Drag.XAxis
889
            drag.smoothed: false
890
            property int startFadeIn
891
892
893
894
            visible: clipRoot.width > 3 * width && mouseArea.containsMouse && !dragProxyArea.pressed
            onClicked: {
                if (clipRoot.fadeIn == 0) {
                    timeline.adjustFade(clipRoot.clipId, 'fadein', 0, -2)
895
896
                }
            }
897
            onPressed: {
898
                root.autoScrolling = false
899
900
                startFadeIn = clipRoot.fadeIn
                anchors.left = undefined
901
                fadeInTriangle.opacity = 0.6
902
                // parentTrack.clipSelected(clipRoot, parentTrack) TODO
903
904
            }
            onReleased: {
905
                root.autoScrolling = timeline.autoScroll
906
                fadeInTriangle.opacity = 0.4
907
908
                timeline.adjustFade(clipRoot.clipId, 'fadein', clipRoot.fadeIn, startFadeIn)
                bubbleHelp.hide()
909
                anchors.left = container.left
910
911
912
            }
            onPositionChanged: {
                if (mouse.buttons === Qt.LeftButton) {
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
913
                    var delta = Math.round((x + width / 2) / timeScale)
914
915
916
917
918
919
920
                    var duration = Math.max(0, delta)
                    duration = Math.min(duration, clipRoot.clipDuration - 1)
                    if (duration != clipRoot.fadeIn) {
                        timeline.adjustFade(clipRoot.clipId, 'fadein', duration, -1)
                        // Show fade duration as time in a "bubble" help.
                        var s = timeline.simplifiedTC(clipRoot.fadeIn)
                        bubbleHelp.show(clipRoot.x + x, parentTrack.y + parentTrack.height, s)
921
                    }
922
923
                }
            }
924
925
            Rectangle {
                id: fadeInControl
926
927
928
                anchors.top: parent.top
                anchors.left: clipRoot.fadeIn > 0 ? undefined : parent.left
                anchors.horizontalCenter: clipRoot.fadeIn > 0 ? parent.horizontalCenter : undefined
929
                width: fadeInMouseArea.containsMouse || Drag.active ? parent.width : 5
930
                height: width
931
                radius: width / 2
932
933
934
                color: 'green'
                border.width: 3
                border.color: '#FF66FFFF'
935
936
                enabled: !isLocked && !dragProxy.isComposition
                Drag.active: fadeInMouseArea.drag.active
937
                Behavior on width { NumberAnimation { duration: 100 } }
938
939
940
941
                Rectangle {
                    id: fadeInMarker
                    anchors.horizontalCenter: parent.horizontalCenter
                    anchors.top: parent.top
942
943
                    color: '#FF66FFFF'
                    height: container.height
944
                    width: 1
945
                    visible : clipRoot.fadeIn > 0 && (fadeInMouseArea.containsMouse || fadeInMouseArea.drag.active)
946
947
                }
            }
948
        }
949
    }
950
}