TrackHead.qml 24.5 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
import QtQuick 2.11
20
import QtQuick.Controls 2.4
21
22
23

Rectangle {
    id: trackHeadRoot
24
    property string trackName
25
26
    property string effectNames
    property bool isStackEnabled
27
    property bool isDisabled
28
    property bool collapsed: false
29
    property int isComposite
30
31
    property bool isLocked: false
    property bool isActive: false
32
    property bool isAudio
33
    property bool showAudioRecord: false
34
    property bool current: false
35
    property int trackId : -42
36
    property string trackTag
37
    property int thumbsFormat: 0
38
    border.width: 1
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
39
    border.color: root.frameColor
40

41
    onIsLockedChanged: {
42
        flashLock.restart();
43
44
    }

45
    color: getTrackColor(isAudio, true)
46
47
    //border.color: selected? 'red' : 'transparent'
    //border.width: selected? 1 : 0
48
49
50
51
52
53
54
55
56
57
58
59
    clip: true
    state: 'normal'
    states: [
        State {
            name: 'current'
            when: trackHeadRoot.current
            PropertyChanges {
                target: trackHeadRoot
                color: selectedTrackColor
            }
        },
        State {
60
            when: !trackHeadRoot.current
61
62
63
            name: 'normal'
            PropertyChanges {
                target: trackHeadRoot
64
                color: getTrackColor(isAudio, true)
65
66
67
68
            }
        }
    ]

69
70
71
72
73
74
    Keys.onDownPressed: {
        root.moveSelectedTrack(1)
    }
    Keys.onUpPressed: {
        root.moveSelectedTrack(-1)
    }
75

76
    MouseArea {
77
        id: headerMouseArea
78
        anchors.fill: parent
79
        hoverEnabled: true
80
        acceptedButtons: Qt.LeftButton | Qt.RightButton
81
        onPressed: {
82
            timeline.activeTrack = trackId
83
            if (mouse.button == Qt.RightButton) {
84
                root.showHeaderMenu()
85
86
87
            }
        }
        onClicked: {
88
            console.log('TRACK ID: ', trackId)
89
            parent.forceActiveFocus()
90
            nameEdit.visible = false
91
92
93
            if (mouse.button == Qt.LeftButton) {
                timeline.showTrackAsset(trackId)
            }
94
95
        }
    }
96
97
98
99
100
101
102
103
    Label {
        id: trackTarget
        property color bgColor: 'grey'
        font: miniFont
        color: timeline.targetTextColor
        background: Rectangle {
            color: trackTarget.bgColor
        }
104
        width: 1.5 * root.baseUnit
105
        height: trackHeadRoot.height
106
        verticalAlignment: Text.AlignTop
107
        horizontalAlignment: Text.AlignHCenter
108
        visible: trackHeadRoot.isAudio ? timeline.hasAudioTarget > 0 : timeline.hasVideoTarget
109
110
111
        anchors.top: parent.top
        anchors.bottom: parent.bottom
        anchors.margins: 1
112

113
114
        MouseArea {
            id: targetArea
115
            anchors.fill: parent
116
117
118
119
120
121
122
123
124
            hoverEnabled: true
            acceptedButtons: Qt.LeftButton | Qt.RightButton
            cursorShape: Qt.PointingHandCursor
            onClicked: {
                if (mouse.button == Qt.RightButton) {
                    if (trackHeadRoot.isAudio) {
                        root.showTargetMenu(trackId)
                    } else {
                        root.showHeaderMenu()
125
126
                    }
                }
127
128
129
130
131
132
133
134
                else {
                    if (trackHeadRoot.isAudio) {
                        timeline.switchAudioTarget(trackHeadRoot.trackId);
                    } else {
                        if (trackHeadRoot.trackId == timeline.videoTarget) {
                            timeline.videoTarget = -1;
                        } else if (timeline.hasVideoTarget) {
                            timeline.videoTarget = trackHeadRoot.trackId;
135
136
                        }
                    }
137
138
                }
            }
139
            ToolButton {
140
                id: targetMouse
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
                focusPolicy: Qt.NoFocus
                visible: trackHeadRoot.isAudio && timeline.clipTargets > 1 && trackHeadRoot.height > (2 * expandButton.height)
                background: Rectangle {
                    color: Qt.darker(trackTarget.bgColor, 1.5)
                    border.color: activePalette.light
                }
                anchors.bottom: parent.bottom
                width: parent.width
                height: width
                contentItem: Item {
                    Image {
                        source: "image://icon/go-down"
                        anchors.fill: parent
                    }
                }
                onClicked: {
                    root.showTargetMenu(trackId)
                }
159
160
161
162
163
164
165
166
167
168
169
170
171
172
                ToolTip {
                    visible: targetMouse.hovered
                    font: miniFont
                    delay: 1500
                    timeout: 5000
                    background: Rectangle {
                        color: activePalette.alternateBase
                        border.color: activePalette.light
                    }
                    contentItem: Label {
                        color: activePalette.text
                        text: timeline.actionText("switch_target_stream")
                    }
                }
173
            }
174
175
        }
        ToolTip {
176
            visible: targetArea.containsMouse && !targetMouse.hovered
177
178
179
180
181
182
183
184
185
186
            font: miniFont
            delay: 1500
            timeout: 5000
            background: Rectangle {
                color: activePalette.alternateBase
                border.color: activePalette.light
            }
            contentItem: Label {
                color: activePalette.text
                text: i18n("Click to toggle track as target. Target tracks will receive the inserted clips")
187
188
            }
        }
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
    state:  'normalTarget'
    states: [
        State {
            name: 'target'
            when: (trackHeadRoot.isAudio && timeline.audioTarget.indexOf(trackHeadRoot.trackId) > -1) || (!trackHeadRoot.isAudio && trackHeadRoot.trackId == timeline.videoTarget)
            PropertyChanges {
                target: trackTarget
                bgColor: timeline.targetColor
                text: trackHeadRoot.isAudio ? timeline.audioTargetName(trackHeadRoot.trackId) : ''
            }
        },
        State {
            name: 'inactiveTarget'
            when: (trackHeadRoot.isAudio && timeline.lastAudioTarget.indexOf(trackHeadRoot.trackId) > -1) || (!trackHeadRoot.isAudio && trackHeadRoot.trackId == timeline.lastVideoTarget)
            PropertyChanges {
                target: trackTarget
                opacity: 0.3
                bgColor: activePalette.text
                text: trackHeadRoot.isAudio ? timeline.audioTargetName(trackHeadRoot.trackId) : ''
            }
        },
        State {
            name: 'noTarget'
            when: !trackHeadRoot.isLocked && !trackHeadRoot.isDisabled
            PropertyChanges {
                target: trackTarget
                bgColor: activePalette.base
                text: ''
            }
        }
    ]
    transitions: [
        Transition {
            to: '*'
            ColorAnimation { target: trackTarget; duration: 300 }
        }
    ]
226
    }
227
    Item {
228
        id: trackHeadColumn
229
        anchors.fill: parent
230
        anchors.leftMargin: trackTarget.width
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
231
        anchors.topMargin: 0
232
233
234

        ToolButton {
            id: expandButton
235
            focusPolicy: Qt.NoFocus
236
            property var modifier: 0
237
238
239
240
241
242
243
244
245
            contentItem: Item {
                Image {
                    source: trackHeadRoot.collapsed ? "image://icon/go-next" : "image://icon/go-down"
                    anchors.centerIn: parent
                    width: root.collapsedHeight - 4
                    height: root.collapsedHeight - 4
                    cache: root.paletteUnchanged
                }
            }
246
            onClicked: {
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
                if (modifier & Qt.ShiftModifier) {
                    // Collapse / expand all tracks
                    timeline.collapseAllTrackHeight(trackId, !trackHeadRoot.collapsed, root.collapsedHeight)
                } else {
                    if (trackHeadRoot.collapsed) {
                        var newHeight = Math.max(root.collapsedHeight * 1.5, controller.getTrackProperty(trackId, "kdenlive:trackheight"))
                        controller.setTrackProperty(trackId, "kdenlive:trackheight", newHeight)
                        controller.setTrackProperty(trackId, "kdenlive:collapsed", "0")
                    } else {
                        controller.setTrackProperty(trackId, "kdenlive:collapsed", root.collapsedHeight)
                    }
                }
            }
            MouseArea {
                // Used to pass modifier state to expand button
                anchors.fill: parent
                acceptedButtons: Qt.LeftButton
                onPressed: {
                    expandButton.modifier = mouse.modifiers
                    mouse.accepted = false
                }
268
            }
269
270
271
            anchors.left: parent.left
            width: root.collapsedHeight
            height: root.collapsedHeight
272
273
            ToolTip {
                visible: expandButton.hovered
274
                font: miniFont
275
276
277
278
279
280
281
282
283
                delay: 1500
                timeout: 5000
                background: Rectangle {
                    color: activePalette.alternateBase
                    border.color: activePalette.light
                }
                contentItem: Label {
                    color: activePalette.text
                    text: trackLabel.visible? i18n("Minimize") : i18n("Expand")
284
285
                }
            }
286
        }
287
288
289
        Label {
            id: trackLed
            property color bgColor: Qt.darker(trackHeadRoot.color, 0.55)
290
            anchors.left: expandButton.right
291
292
293
294
295
296
            font: miniFont
            text: trackHeadRoot.trackTag
            color: activePalette.text
            background: Rectangle {
                color: trackLed.bgColor
            }
297
            width: root.trackTagWidth
298
            height: root.collapsedHeight - 2
299
300
301
302
303
            y: 1
            verticalAlignment: Text.AlignVCenter
            horizontalAlignment: Text.AlignHCenter
            MouseArea {
                id: tagMouseArea
304
                anchors.fill: parent
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
                hoverEnabled: true
                cursorShape: Qt.PointingHandCursor
                onClicked: {
                    timeline.switchTrackActive(trackHeadRoot.trackId)
                }
            }
            ToolTip {
                visible: tagMouseArea.containsMouse
                font: miniFont
                delay: 1500
                timeout: 5000
                background: Rectangle {
                    color: activePalette.alternateBase
                    border.color: activePalette.light
                }
                contentItem: Label {
321
                    color: activePalette.text
322
                    text: i18n("Click to make track active/inactive. Active tracks will react to editing operations")
323
324
                }
                }
325
326
327
328
329
330
331
332
        state:  'normalled'
            states: [
                State {
                    name: 'locked'
                    when: trackHeadRoot.isLocked
                    PropertyChanges {
                        target: trackLed
                        bgColor: 'red'
333
                    }
334
335
336
337
338
339
340
341
                },
                State {
                    name: 'active'
                    when: trackHeadRoot.isActive
                    PropertyChanges {
                        target: trackLed
                        bgColor: timeline.targetColor
                        color: timeline.targetTextColor
342
                    }
343
344
345
346
347
348
349
                },
                State {
                    name: 'inactive'
                    when: !trackHeadRoot.isLocked && !trackHeadRoot.isActive
                    PropertyChanges {
                        target: trackLed
                        bgColor: Qt.darker(trackHeadRoot.color, 0.55)
350
                    }
351
352
353
354
355
356
357
358
                }
            ]
            transitions: [
                Transition {
                    to: '*'
                    ColorAnimation { target: trackLed; duration: 300 }
                }
            ]
359
360
        }
        Label {
361
            anchors.left: trackLed.right
362
            anchors.top: parent.top
363
            anchors.leftMargin: 2
364
            height: trackLed.height
365
            width: buttonsRow.x - x
366
            text: trackHeadRoot.trackName
367
            elide: Text.ElideRight
368
            font: miniFont
369
            verticalAlignment: Text.AlignVCenter
370
371
            horizontalAlignment: Text.AlignLeft
            visible: !trackLabel.visible && trackHeadRoot.width > (trackTarget.width + expandButton.width + trackLed.width + (4 * muteButton.width) + 4)
372
373
        }
        Row {
374
            id: buttonsRow
375
            width: childrenRect.width
376
            x: Math.max(2 * root.collapsedHeight + 2, parent.width - width - 4)
377
            spacing: 0
378
            ToolButton {
379
                id: effectButton
380
381
382
383
384
385
386
387
388
389
390
                focusPolicy: Qt.NoFocus
                contentItem: Item {
                    Image {
                        source: "image://icon/tools-wizard"
                        anchors.centerIn: parent
                        width: root.collapsedHeight - 4
                        height: root.collapsedHeight - 4
                        cache: root.paletteUnchanged
                        opacity: effectButton.enabled ? 1 : 0.5
                    }
                }
391
                enabled: trackHeadRoot.effectNames != ''
392
                checkable: true
393
394
395
396
                checked: enabled && trackHeadRoot.isStackEnabled
                onClicked: {
                    timeline.showTrackAsset(trackId)
                    controller.setTrackStackEnabled(trackId, !isStackEnabled)
397
                }
398
399
                width: root.collapsedHeight
                height: root.collapsedHeight
400
            }
401
402
            ToolButton {
                id: muteButton
403
                focusPolicy: Qt.NoFocus
404
405
406
407
408
409
410
411
412
413
414
                contentItem: Item {
                    Image {
                        source: isAudio ? (isDisabled ? "image://icon/kdenlive-hide-audio" : "image://icon/kdenlive-show-audio") : (isDisabled ? "image://icon/kdenlive-hide-video" : "image://icon/kdenlive-show-video")
                        anchors.centerIn: parent
                        width: root.collapsedHeight - 4
                        height: root.collapsedHeight - 4
                        cache: root.paletteUnchanged
                    }
                }
                width: root.collapsedHeight
                height: root.collapsedHeight
415
                onClicked: controller.hideTrack(trackId, isDisabled ? (isAudio ? '1' : '2') : '3')
416
417
                ToolTip {
                    visible: muteButton.hovered
418
                    font: miniFont
419
420
421
422
423
424
425
426
427
428
429
                    delay: 1500
                    timeout: 5000
                    background: Rectangle {
                        color: activePalette.alternateBase
                        border.color: activePalette.light
                    }
                    contentItem: Label {
                        color: activePalette.text
                        text: isAudio ? (isDisabled? i18n("Unmute") : i18n("Mute")) : (isDisabled? i18n("Show") : i18n("Hide"))
                    }
                }
430
431
432
433
            }

            ToolButton {
                id: lockButton
434
435
                width: root.collapsedHeight
                height: root.collapsedHeight
436
                focusPolicy: Qt.NoFocus
437
438
439
440
441
442
443
444
445
                contentItem: Item {
                    Image {
                        source: trackHeadRoot.isLocked ? "image://icon/kdenlive-lock" : "image://icon/kdenlive-unlock"
                        anchors.centerIn: parent
                        width: root.collapsedHeight - 4
                        height: root.collapsedHeight - 4
                        cache: root.paletteUnchanged
                    }
                }
446
                onClicked: controller.setTrackLockedState(trackId, !isLocked)
447
448
                ToolTip {
                    visible: lockButton.hovered
449
                    font: miniFont
450
451
452
453
454
455
456
457
458
459
460
                    delay: 1500
                    timeout: 5000
                    background: Rectangle {
                        color: activePalette.alternateBase
                        border.color: activePalette.light
                    }
                    contentItem: Label {
                        color: activePalette.text
                        text: isLocked? i18n("Unlock track") : i18n("Lock track")
                    }
                }
461

462
                SequentialAnimation {
463
464
465
466
467
                    id: flashLock
                    loops: 1
                    ScaleAnimator {
                        target: lockButton
                        from: 1
468
469
                        to: 1.6
                        duration: 200
470
471
472
                    }
                    ScaleAnimator {
                        target: lockButton
473
                        from: 1.6
474
                        to: 1
475
                        duration: 200
476
477
478
                    }
                 }
            }
479
        }
480
481
        Item {
            id: recLayout
482
            y: root.collapsedHeight + 4
483
484
485
            anchors.left: trackHeadColumn.left
            anchors.right: trackHeadColumn.right
            anchors.margins: 2
486
            height: showAudioRecord ? root.collapsedHeight : 0
487
488
489
490
491
492
493
494
            Loader {
                id: audioVuMeter
                anchors.fill: parent
                visible: showAudioRecord && (trackHeadRoot.height >= 2 * muteButton.height + resizer.height)
                source: isAudio && showAudioRecord ? "AudioLevels.qml" : ""
                onLoaded: item.trackId = trackId
            }
        }
495
496
497
498
499
500
        Item {
            anchors.bottom: trackHeadColumn.bottom
            anchors.left: trackHeadColumn.left
            anchors.right: trackHeadColumn.right
            anchors.margins: 2
            height: nameEdit.height
501
502
503
504
            Rectangle {
                id: trackLabel
                color: 'transparent'
                radius: 2
505
                anchors.fill: parent
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
506
                border.color: trackNameMouseArea.containsMouse ? activePalette.highlight : 'transparent'
507
                visible: (trackHeadRoot.height >= trackLabel.height + muteButton.height + resizer.height + recLayout.height)
508
509
510
511
                MouseArea {
                    id: trackNameMouseArea
                    anchors.fill: parent
                    hoverEnabled: true
512
                    propagateComposedEvents: true
513
                    cursorShape: Qt.IBeamCursor
514
                    onDoubleClicked: {
515
516
517
518
                        nameEdit.visible = true
                        nameEdit.focus = true
                        nameEdit.selectAll()
                    }
519
                    onClicked: {
520
                        timeline.showTrackAsset(trackId)
521
                        timeline.activeTrack = trackId
522
                        trackHeadRoot.focus = true
523
                    }
524
                }
525
526
527
528
                Label {
                    text: trackName
                    anchors.verticalCenter: parent.verticalCenter
                    anchors.left: parent.left
529
530
                    anchors.leftMargin: 4
                    elide: Qt.ElideRight
531
                    font: miniFont
532
533
534
                }
                Label {
                    id: placeHolder
535
                    visible: trackName == '' && (trackNameMouseArea.containsMouse || headerMouseArea.containsMouse)
536
                    enabled: false
Yuri Chornoivan's avatar
Yuri Chornoivan committed
537
                    text: i18n("Edit track name")
538
539
                    anchors.verticalCenter: parent.verticalCenter
                    anchors.left: parent.left
540
541
                    anchors.leftMargin: 4
                    elide: Qt.ElideRight
542
                    font: miniFont
543
                }
544
545
546
547
548
                TextField {
                    id: nameEdit
                    visible: false
                    width: parent.width
                    text: trackName
549
                    font: miniFont
550
551
552
553
554
555
                    background: Rectangle {
                        radius: 2
                        color: activePalette.window
                        anchors.fill: parent
                    }
                    /*style: TextFieldStyle {
556
557
558
                        padding.top:0
                        padding.bottom: 0
                        background: Rectangle {
559
560
                            radius: 2
                            color: activePalette.window
561
562
                            anchors.fill: parent
                        }
563
                    }*/
564
                    onEditingFinished: {
565
                        controller.setTrackName(trackId, text)
566
567
                        visible = false
                    }
568
569
570
                }
            }
        }
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
571
572
    }
    Rectangle {
Nicolas Carion's avatar
Nicolas Carion committed
573
            id: resizer
574
            height: 4
Nicolas Carion's avatar
Nicolas Carion committed
575
576
577
578
            color: 'red'
            opacity: 0
            Drag.active: trimInMouseArea.drag.active
            Drag.proposedAction: Qt.MoveAction
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
579
580
            width: trackHeadRoot.width
            y: trackHeadRoot.height - height
581

Nicolas Carion's avatar
Nicolas Carion committed
582
583
584
585
586
587
588
            MouseArea {
                id: trimInMouseArea
                anchors.fill: parent
                hoverEnabled: true
                cursorShape: Qt.SizeVerCursor
                drag.target: parent
                drag.axis: Drag.YAxis
589
                drag.minimumY: root.collapsedHeight - resizer.height
Nicolas Carion's avatar
Nicolas Carion committed
590
591
                property double startY
                property double originalY
592
                drag.smoothed: false
593
                property bool dragStarted: false
594

Nicolas Carion's avatar
Nicolas Carion committed
595
                onPressed: {
596
                    root.autoScrolling = false
597
                    dragStarted = false
Nicolas Carion's avatar
Nicolas Carion committed
598
599
600
601
                    startY = mapToItem(null, x, y).y
                    originalY = trackHeadRoot.height // reusing originalX to accumulate delta for bubble help
                }
                onReleased: {
602
                    root.autoScrolling = timeline.autoScroll
603
604
605
                    if (!trimInMouseArea.containsMouse) {
                        parent.opacity = 0
                    }
606
                    if (mouse.modifiers & Qt.ShiftModifier && dragStarted) {
607
                        timeline.adjustAllTrackHeight(trackHeadRoot.trackId, trackHeadRoot.height)
608
                    }
Nicolas Carion's avatar
Nicolas Carion committed
609
                }
610
                onEntered: parent.opacity = 0.3
Nicolas Carion's avatar
Nicolas Carion committed
611
                onExited: parent.opacity = 0
612
613
614
                onDoubleClicked: {
                    timeline.defaultTrackHeight(mouse.modifiers & Qt.ShiftModifier ? -1 : trackHeadRoot.trackId)
                }
Nicolas Carion's avatar
Nicolas Carion committed
615
616
617
                onPositionChanged: {
                    if (mouse.buttons === Qt.LeftButton) {
                        parent.opacity = 0.5
618
619
620
                        if (!dragStarted && Math.abs(mapToItem(null, x, y).y - startY) > 2) {
                            dragStarted = true
                        }
Nicolas Carion's avatar
Nicolas Carion committed
621
                        var newHeight = originalY + (mapToItem(null, x, y).y - startY)
622
                        newHeight =  Math.max(root.collapsedHeight, newHeight)
623
624
625
626
627
628
                        if (newHeight == root.collapsedHeight) {
                            controller.setTrackProperty(trackId, "kdenlive:collapsed", root.collapsedHeight)
                        } else {
                            controller.setTrackProperty(trackId, "kdenlive:trackheight", newHeight)
                            controller.setTrackProperty(trackId, "kdenlive:collapsed", "0")
                        }
Nicolas Carion's avatar
Nicolas Carion committed
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
    DropArea { //Drop area for tracks
        anchors.fill: trackHeadRoot
        keys: 'kdenlive/effect'
        property string dropData
        property string dropSource
        property int dropRow: -1
        onEntered: {
            dropData = drag.getDataAsString('kdenlive/effect')
            dropSource = drag.getDataAsString('kdenlive/effectsource')
        }
        onDropped: {
            console.log("Add effect: ", dropData)
            if (dropSource == '') {
                // drop from effects list
                controller.addTrackEffect(trackHeadRoot.trackId, dropData);
            } else {
                controller.copyTrackEffect(trackHeadRoot.trackId, dropSource);
            }
            dropSource = ''
            dropRow = -1
            drag.acceptProposedAction
        }
    }
656
657
}