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
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: false
74
    property var groupTrimData
75
    property int scrollStart: scrollView.contentX - (clipRoot.modelStart * timeline.scaleFactor)
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
    onScrollStartChanged: {
86
        clipRoot.hideClipViews = scrollStart > (clipDuration * timeline.scaleFactor) || scrollStart + scrollView.contentItem.width < 0
87
    }
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
123
            font: miniFont
124
125
126
127
128
            text: '%1 (%2-%3)\n%4: %5'.arg(label.text)
                        .arg(timeline.simplifiedTC(clipRoot.inPoint))
                        .arg(timeline.simplifiedTC(clipRoot.outPoint))
                        .arg(i18n("Duration"))
                        .arg(timeline.simplifiedTC(clipRoot.clipDuration))
129
130
131
        }
    }

132
    onKeyframeModelChanged: {
133
        if (effectRow.keyframecanvas) {
134
            console.log('keyframe model changed............')
135
136
            effectRow.keyframecanvas.requestPaint()
        }
137
138
    }

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

147
148
149
    onModelStartChanged: {
        x = modelStart * timeScale;
    }
150

151
152
153
154
155
    onFakePositionChanged: {
        x = fakePosition * timeScale;
    }
    onFakeTidChanged: {
        if (clipRoot.fakeTid > -1 && parentTrack) {
156
157
158
159
160
161
162
            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
            }
163
164
            clipRoot.y = Logic.getTrackById(clipRoot.fakeTid).y
        }
165
166
    }

167
    onForceReloadThumbChanged: {
168
        // TODO: find a way to force reload of clip thumbs
169
        if (thumbsLoader.item) {
170
            thumbsLoader.item.reload(0)
171
        }
172
173
    }

174
    onTimeScaleChanged: {
175
        x = modelStart * timeScale;
176
        width = clipDuration * timeScale;
177
        labelRect.x = scrollX > modelStart * timeScale ? scrollX - modelStart * timeScale : clipRoot.border.width
178
179
    }
    onScrollXChanged: {
180
        labelRect.x = scrollX > modelStart * timeScale ? scrollX - modelStart * timeScale : clipRoot.border.width
181
182
    }

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

186
187
188
189
190
    function updateDrag() {
        var itemPos = mapToItem(tracksContainerArea, 0, 0, clipRoot.width, clipRoot.height)
        initDrag(clipRoot, itemPos, clipRoot.clipId, clipRoot.modelStart, clipRoot.trackId, false)
    }

191
    function getColor() {
192
193
194
        if (clipStatus == ClipState.Disabled) {
            return 'grey'
        }
195
196
197
        if (itemType == ProducerType.Text) {
            return titleColor
        }
198
        if (itemType == ProducerType.Image) {
199
200
            return imageColor
        }
201
202
203
        if (itemType == ProducerType.SlideShow) {
            return slideshowColor
        }
204
        if (itemType == ProducerType.Color) {
205
206
207
            var color = clipResource.substring(clipResource.length - 9)
            if (color[0] == '#') {
                return color
208
            }
209
            return '#' + color.substring(color.length - 8, color.length - 2)
210
        }
211
        return isAudio? root.audioColor : root.videoColor
212
213
    }

214
/*    function reparent(track) {
215
        console.log('TrackId: ',trackId)
216
217
        parent = track
        height = track.height
218
        parentTrack = track
219
        trackId = parentTrack.trackId
220
        console.log('Reparenting clip to Track: ', trackId)
221
        //generateWaveform()
222
    }
223
*/
224
    property bool noThumbs: (isAudio || itemType == ProducerType.Color || mltService === '')
225
    property string baseThumbPath: noThumbs ? '' : 'image://thumbnail/' + binId + '/' + documentId + '/#'
226

227
    DropArea { //Drop area for clips
228
229
230
        anchors.fill: clipRoot
        keys: 'kdenlive/effect'
        property string dropData
231
        property string dropSource
232
        property int dropRow: -1
233
234
        onEntered: {
            dropData = drag.getDataAsString('kdenlive/effect')
235
            dropSource = drag.getDataAsString('kdenlive/effectsource')
236
237
238
        }
        onDropped: {
            console.log("Add effect: ", dropData)
239
            if (dropSource == '') {
240
241
242
                // drop from effects list
                controller.addClipEffect(clipRoot.clipId, dropData);
            } else {
243
                controller.copyClipEffect(clipRoot.clipId, dropSource);
244
            }
245
            dropSource = ''
246
            dropRow = -1
247
            drag.acceptProposedAction
248
249
        }
    }
250
251
    MouseArea {
        id: mouseArea
252
        enabled: root.activeTool === 0
253
        anchors.fill: clipRoot
254
        acceptedButtons: Qt.RightButton
255
        hoverEnabled: root.activeTool === 0
256
        cursorShape: (trimInMouseArea.drag.active || trimOutMouseArea.drag.active)? Qt.SizeHorCursor : dragProxyArea.cursorShape
257
        onPressed: {
258
            root.autoScrolling = false
259
            root.mainItemId = clipRoot.clipId
260
            if (mouse.button == Qt.RightButton) {
261
                if (timeline.selection.indexOf(clipRoot.clipId) == -1) {
262
                    controller.requestAddToSelection(clipRoot.clipId, true)
263
                }
264
                root.mainFrame = Math.round(mouse.x / timeline.scaleFactor)
265
                root.showClipMenu(clipRoot.clipId)
266
            }
267
        }
268
        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)
269
        Keys.onLeftPressed: {
270
271
            var offset = event.modifiers === Qt.ShiftModifier ? timeline.fps() : 1
            controller.requestClipMove(clipRoot.clipId, clipRoot.trackId, clipRoot.modelStart - offset, true, true, true);
272
273
        }
        Keys.onRightPressed: {
274
275
            var offset = event.modifiers === Qt.ShiftModifier ? timeline.fps() : 1
            controller.requestClipMove(clipRoot.clipId, clipRoot.trackId, clipRoot.modelStart + offset, true, true, true);
276
        }
277
278
279
280
281
282
        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);
        }
283
284
        Keys.onEscapePressed: {
            timeline.grabCurrent()
285
            //focus = false
286
        }
287
        onPositionChanged: {
288
289
            var mapped = parentTrack.mapFromItem(clipRoot, mouse.x, mouse.y).x
            root.mousePosChanged(Math.round(mapped / timeline.scaleFactor))
290
        }
291
292
293
294
        onEntered: {
            var itemPos = mapToItem(tracksContainerArea, 0, 0, width, height)
            initDrag(clipRoot, itemPos, clipRoot.clipId, clipRoot.modelStart, clipRoot.trackId, false)
        }
295

296
297
        onExited: {
            endDrag()
298
        }
299
        onWheel: zoomByWheel(wheel)
300

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

315
316
317
318
        Item {
            // Clipping container
            id: container
            anchors.fill: parent
319
320
            anchors.margins: clipRoot.border.width
            //clip: true
321
            property bool showDetails: (!clipRoot.selected || !effectRow.visible) && container.height > 2.2 * labelRect.height
322
323

            Repeater {
324
                // Clip markers
325
326
327
328
                model: markers
                delegate:
                Item {
                    anchors.fill: parent
329
                    visible: markerBase.x >= 0 && markerBase.x < clipRoot.width
330
331
332
333
                    Rectangle {
                        id: markerBase
                        width: 1
                        height: parent.height
334
                        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;
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
                        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
354
                            onDoubleClicked: timeline.editMarker(clipRoot.clipId, model.frame)
355
                            onClicked: proxy.position = (clipRoot.x + markerBase.x) / timeline.scaleFactor
356
357
358
359
360
361
                        }
                    }
                    Text {
                        id: mlabel
                        visible: timeline.showMarkers && parent.width > width * 1.5
                        text: model.comment
362
                        font: miniFont
363
364
365
366
367
368
369
370
                        x: markerBase.x
                        anchors {
                            bottom: parent.verticalCenter
                            topMargin: 2
                            leftMargin: 2
                        }
                        color: 'white'
                    }
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
371
                }
372
373
            }

374
375
376
            MouseArea {
                // Left resize handle
                id: trimInMouseArea
377
                x: -clipRoot.border.width
378
379
                height: parent.height
                width: root.baseUnit / 2
380
                enabled: !isLocked && (pressed || clipRoot.width > 3 * width)
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
                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
                    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
402
                    x = -clipRoot.border.width
403
404
405
406
407
408
409
                    if (sizeChanged) {
                        clipRoot.trimmedIn(clipRoot, shiftTrim, controlTrim)
                        sizeChanged = false
                    }
                }
                onPositionChanged: {
                    if (mouse.buttons === Qt.LeftButton) {
410
411
412
                        var currentFrame = Math.round((clipRoot.x + (x + clipRoot.border.width)) / timeScale)
                        var currentClipPos = clipRoot.modelStart
                        var delta = currentFrame - currentClipPos
413
                        if (delta !== 0) {
414
                            if (maxDuration > 0 && delta < -inPoint && !(mouse.modifiers & Qt.ControlModifier)) {
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
                                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
440
                    visible: trimInMouseArea.pressed || (root.activeTool === 0 && !mouseArea.drag.active && parent.enabled)
441
442
443
444
445
446
447
448
449
450
451

                    ToolTip {
                        visible: trimInMouseArea.containsMouse && !trimInMouseArea.pressed
                        delay: 1000
                        timeout: 5000
                        background: Rectangle {
                            color: activePalette.alternateBase
                            border.color: activePalette.light
                        }
                        contentItem: Label {
                            color: activePalette.text
452
                            font: miniFont
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
                            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
468
                enabled: !isLocked && (pressed || clipRoot.width > 3 * width)
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
                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)
499
                        if (maxDuration > 0 && (newDuration > maxDuration - inPoint) && !(mouse.modifiers & Qt.ControlModifier)) {
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
                            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
524
                        font: miniFont
525
526
527
528
529
530
531
532
533
534
535
536
                        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
537
                    visible: trimOutMouseArea.pressed || (root.activeTool === 0 && !mouseArea.drag.active && parent.enabled)
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
                }
            }

            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) + '%]': '')
580
                        font: miniFont
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
                        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
620
                                font: miniFont
621
622
623
624
625
626
                                text: positionOffset < 0 ? i18n("Offset: -%1", timeline.simplifiedTC(-positionOffset)) : i18n("Offset: %1", timeline.simplifiedTC(positionOffset))
                            }
                        }
                        Text {
                            id: offsetLabel
                            text: positionOffset
627
                            font: miniFont
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
                            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
653
                        font: miniFont
654
655
656
657
658
659
660
661
662
663
664
665
666
                        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'
                    }
                }
            }
667
668
669
670
671
672
673
674
675
676
677
678

            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
            }
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
707
708
709
710
711

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

712
713
714
        MouseArea {
            // Add start composition area
            id: compInArea
715
716
            anchors.left: parent.left
            anchors.bottom: parent.bottom
717
            width: Math.min(root.baseUnit, container.height / 3)
718
            height: width
719
720
721
722
723
724
725
726
727
728
729
730
731
732
            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
733
                }
734
735
                contentItem: Label {
                    color: activePalette.text
736
                    font: miniFont
737
                    text: i18n("Click to add composition")
738
739
                }
            }
740
741
742
743
744
            Rectangle {
                // Start composition box
                id: compositionIn
                anchors.bottom: parent.bottom
                anchors.left: parent.left
745
                width: compInArea.containsMouse ? parent.width : 5
746
747
748
749
750
751
752
753
                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 } }
            }
754
        }
755
756
757
758

        MouseArea {
            // Add end composition area
            id: compOutArea
759
760
            anchors.right: parent.right
            anchors.bottom: parent.bottom
761
            width: Math.min(root.baseUnit, container.height / 3)
762
            height: width
763
764
765
766
767
768
            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)
769
            }
770
            ToolTip {
771
                visible: compOutArea.containsMouse && !dragProxyArea.pressed
772
773
774
775
776
777
778
779
                delay: 1000
                timeout: 5000
                background: Rectangle {
                    color: activePalette.alternateBase
                    border.color: activePalette.light
                }
                contentItem: Label {
                    color: activePalette.text
780
                    font: miniFont
781
                    text: i18n("Click to add composition")
782
783
                }
            }
784
785
786
787
788
            Rectangle {
                // End composition box
                id: compositionOut
                anchors.bottom: parent.bottom
                anchors.right: parent.right
789
                width: compOutArea.containsMouse ? parent.height : 5
790
791
792
793
794
795
796
                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 } }
797
798
            }
        }
799
800
801

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

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