TrackHead.qml 21 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 myTrackHeight
36
    property int collapsedHeight : expandButton.height
37
    property int trackId : -42
38
    property string trackTag
39
    property int thumbsFormat: 0
40
    border.width: 1
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
41
    border.color: root.frameColor
42
43

    function pulseLockButton() {
44
        flashLock.restart();
45
46
    }

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

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

78
79
80
    MouseArea {
        anchors.fill: parent
        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
            parent.forceActiveFocus()
89
            nameEdit.visible = false
90
91
92
            if (mouse.button == Qt.LeftButton) {
                timeline.showTrackAsset(trackId)
            }
93
94
        }
    }
95
    Item {
96
        id: targetColumn
97
        width: root.collapsedHeight * .4
98
99
        height: trackHeadRoot.height
        Item {
100
101
            anchors.fill: parent
            anchors.margins: 1
102
103
104
105
106
107
            Rectangle {
                id: trackTarget
                color: 'grey'
                anchors.fill: parent
                width: height
                border.width: 0
108
                visible: trackHeadRoot.isAudio ? timeline.hasAudioTarget : timeline.hasVideoTarget
109
                MouseArea {
110
                    id: targetArea
111
                    anchors.fill: parent
112
113
                    hoverEnabled: true
                    cursorShape: Qt.PointingHandCursor
114
115
116
117
                    onClicked: {
                        if (trackHeadRoot.isAudio) {
                            if (trackHeadRoot.trackId == timeline.audioTarget) {
                                timeline.audioTarget = -1;
118
                            } else if (timeline.hasAudioTarget) {
119
120
121
122
123
                                timeline.audioTarget = trackHeadRoot.trackId;
                            }
                        } else {
                            if (trackHeadRoot.trackId == timeline.videoTarget) {
                                timeline.videoTarget = -1;
124
                            } else if (timeline.hasVideoTarget) {
125
126
127
128
129
                                timeline.videoTarget = trackHeadRoot.trackId;
                            }
                        }
                    }
                }
130
                ToolTip {
131
                        visible: targetArea.containsMouse
132
                        font: miniFont
133
134
135
136
137
138
139
140
                        delay: 1500
                        timeout: 5000
                        background: Rectangle {
                            color: activePalette.alternateBase
                            border.color: activePalette.light
                        }
                        contentItem: Label {
                            color: activePalette.text
Yuri Chornoivan's avatar
Yuri Chornoivan committed
141
                            text: i18n("Click to toggle track as target. Target tracks will receive the inserted clips")
142
143
                        }
                    }
144
145
146
147
148
149
150
                state:  'normalTarget'
                states: [
                    State {
                        name: 'target'
                        when: (trackHeadRoot.isAudio && trackHeadRoot.trackId == timeline.audioTarget) || (!trackHeadRoot.isAudio && trackHeadRoot.trackId == timeline.videoTarget)
                        PropertyChanges {
                            target: trackTarget
151
                            color: timeline.targetColor
152
153
                        }
                    },
154
155
156
157
158
159
160
161
162
                    State {
                        name: 'inactiveTarget'
                        when: (trackHeadRoot.isAudio && trackHeadRoot.trackId == timeline.lastAudioTarget) || (!trackHeadRoot.isAudio && trackHeadRoot.trackId == timeline.lastVideoTarget)
                        PropertyChanges {
                            target: trackTarget
                            opacity: 0.3
                            color: activePalette.text
                        }
                    },
163
164
165
166
167
                    State {
                        name: 'noTarget'
                        when: !trackHeadRoot.isLocked && !trackHeadRoot.isDisabled
                        PropertyChanges {
                            target: trackTarget
168
                            color: activePalette.base
169
170
171
172
173
174
175
176
177
178
179
180
                        }
                    }
                ]
                transitions: [
                    Transition {
                        to: '*'
                        ColorAnimation { target: trackTarget; duration: 300 }
                    }
                ]
            }
        }
    }
181
    Item {
182
        id: trackHeadColumn
183
        anchors.fill: parent
184
        anchors.leftMargin: targetColumn.width
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
185
        anchors.topMargin: 0
186
187
188

        ToolButton {
            id: expandButton
189
            anchors.left: parent.left
190
            focusPolicy: Qt.NoFocus
191
192
            //icon.width: trackHeadRoot.iconSize
            //icon.height: trackHeadRoot.iconSize
193
194
            icon.name: trackHeadRoot.collapsed ? 'arrow-right' : 'arrow-down'
            onClicked: {
195
                trackHeadRoot.myTrackHeight = trackHeadRoot.collapsed ? Math.max(root.collapsedHeight * 1.5, controller.getTrackProperty(trackId, "kdenlive:trackheight")) : root.collapsedHeight
196
197
198
            }
            ToolTip {
                visible: expandButton.hovered
199
                font: miniFont
200
201
202
203
204
205
206
207
208
                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")
209
210
                }
            }
211
        }
212
213
214
        Label {
            id: trackLed
            property color bgColor: Qt.darker(trackHeadRoot.color, 0.55)
215
            anchors.left: expandButton.right
216
217
218
219
220
221
            font: miniFont
            text: trackHeadRoot.trackTag
            color: activePalette.text
            background: Rectangle {
                color: trackLed.bgColor
            }
222
            width: fontMetrics.boundingRect("M").width * trackHeadRoot.trackTag.length
223
            height: expandButton.height
224
225
226
227
228
            y: 1
            verticalAlignment: Text.AlignVCenter
            horizontalAlignment: Text.AlignHCenter
            MouseArea {
                id: tagMouseArea
229
                anchors.fill: parent
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
                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 {
246
                    color: activePalette.text
247
                    text: i18n("Click to make track active/inactive. Active tracks will react to editing operations")
248
249
                }
                }
250
251
252
253
254
255
256
257
        state:  'normalled'
            states: [
                State {
                    name: 'locked'
                    when: trackHeadRoot.isLocked
                    PropertyChanges {
                        target: trackLed
                        bgColor: 'red'
258
                    }
259
260
261
262
263
264
265
                },
                State {
                    name: 'active'
                    when: trackHeadRoot.isActive
                    PropertyChanges {
                        target: trackLed
                        bgColor: timeline.targetColor
266
                    }
267
268
269
                    PropertyChanges {
                        target: trackLed
                        color: timeline.targetTextColor
270
                    }
271
272
273
274
275
276
277
                },
                State {
                    name: 'inactive'
                    when: !trackHeadRoot.isLocked && !trackHeadRoot.isActive
                    PropertyChanges {
                        target: trackLed
                        bgColor: Qt.darker(trackHeadRoot.color, 0.55)
278
                    }
279
280
281
282
283
284
285
286
                }
            ]
            transitions: [
                Transition {
                    to: '*'
                    ColorAnimation { target: trackLed; duration: 300 }
                }
            ]
287
288
        }
        Label {
289
            anchors.left: trackLed.right
290
291
292
            anchors.leftMargin: 2
            anchors.verticalCenter: parent.verticalCenter
            text: trackHeadRoot.trackName
293
            font: miniFont
294
295
            verticalAlignment: Text.AlignVCenter
            horizontalAlignment: Text.AlignHCenter
296
            visible: trackHeadRoot.collapsed && trackHeadRoot.width > trackTarget.width + expandButton.width + trackLed.width + (4 * muteButton.width) + 4
297
298
299
        }
        Row {
            width: childrenRect.width
300
            x: Math.max(2 * root.collapsedHeight + 2, parent.width - width - 4)
301
            spacing: 0
302
            id: buttonsRow
303
            ToolButton {
304
305
                id: effectButton
                icon.name: 'tools-wizard'
306
307
308
                checkable: true
                enabled: trackHeadRoot.effectNames != ''
                checked: enabled && trackHeadRoot.isStackEnabled
309
                focusPolicy: Qt.NoFocus
310
311
                //icon.width: trackHeadRoot.iconSize
                //icon.height: trackHeadRoot.iconSize
312
313
314
                onClicked: {
                    timeline.showTrackAsset(trackId)
                    controller.setTrackStackEnabled(trackId, !isStackEnabled)
315
                }
316
            }
317
318
            ToolButton {
                id: muteButton
319
                focusPolicy: Qt.NoFocus
320
321
                //icon.width: trackHeadRoot.iconSize
                //icon.height: trackHeadRoot.iconSize
322
                icon.name: isAudio ? (isDisabled ? 'kdenlive-hide-audio' : 'kdenlive-show-audio') : (isDisabled ? 'kdenlive-hide-video' : 'kdenlive-show-video')
323
                onClicked: controller.hideTrack(trackId, isDisabled ? (isAudio ? '1' : '2') : '3')
324
325
                ToolTip {
                    visible: muteButton.hovered
326
                    font: miniFont
327
328
329
330
331
332
333
334
335
336
337
                    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"))
                    }
                }
338
339
340
341
            }

            ToolButton {
                id: lockButton
342
                focusPolicy: Qt.NoFocus
343
344
                //icon.width: trackHeadRoot.iconSize
                //icon.height: trackHeadRoot.iconSize
345
                icon.name: isLocked ? 'kdenlive-lock' : 'kdenlive-unlock'
346
                onClicked: controller.setTrackLockedState(trackId, !isLocked)
347
348
                ToolTip {
                    visible: lockButton.hovered
349
                    font: miniFont
350
351
352
353
354
355
356
357
358
359
360
                    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")
                    }
                }
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378

                 SequentialAnimation {
                    id: flashLock
                    loops: 1
                    ScaleAnimator {
                        target: lockButton
                        from: 1
                        to: 2
                        duration: 500
                    }
                    ScaleAnimator {
                        target: lockButton
                        from: 2
                        to: 1
                        duration: 500
                    }
                 }
            }
379
        }
380
381
        Item {
            id: recLayout
382
            y: root.collapsedHeight + 4
383
384
385
386
            //width: trackHeadRoot.width
            anchors.left: trackHeadColumn.left
            anchors.right: trackHeadColumn.right
            anchors.margins: 2
387
            height: showAudioRecord ? root.collapsedHeight : 0
388
389
390
391
392
393
394
395
            Loader {
                id: audioVuMeter
                anchors.fill: parent
                visible: showAudioRecord && (trackHeadRoot.height >= 2 * muteButton.height + resizer.height)
                source: isAudio && showAudioRecord ? "AudioLevels.qml" : ""
                onLoaded: item.trackId = trackId
            }
        }
396
397
398
399
400
401
        Item {
            anchors.bottom: trackHeadColumn.bottom
            anchors.left: trackHeadColumn.left
            anchors.right: trackHeadColumn.right
            anchors.margins: 2
            height: nameEdit.height
402
403
404
405
            Rectangle {
                id: trackLabel
                color: 'transparent'
                radius: 2
406
                anchors.fill: parent
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
407
                border.color: trackNameMouseArea.containsMouse ? activePalette.highlight : 'transparent'
408
                visible: (trackHeadRoot.height >= trackLabel.height + muteButton.height + resizer.height + recLayout.height)
409
410
411
412
                MouseArea {
                    id: trackNameMouseArea
                    anchors.fill: parent
                    hoverEnabled: true
413
414
                    propagateComposedEvents: true
                    onDoubleClicked: {
415
416
417
418
                        nameEdit.visible = true
                        nameEdit.focus = true
                        nameEdit.selectAll()
                    }
419
                    onClicked: {
420
                        timeline.showTrackAsset(trackId)
421
                        timeline.activeTrack = trackId
422
                        trackHeadRoot.focus = true
423
                    }
424
425
426
427
428
429
430
431
432
433
                    onEntered: {
                        if (nameEdit.visible == false && trackName == '') {
                            placeHolder.visible = true
                        }
                    }
                    onExited: {
                        if (placeHolder.visible == true) {
                            placeHolder.visible = false
                        }
                    }
434
                }
435
436
437
438
                Label {
                    text: trackName
                    anchors.verticalCenter: parent.verticalCenter
                    anchors.left: parent.left
439
440
                    anchors.leftMargin: 4
                    elide: Qt.ElideRight
441
                    font: miniFont
442
443
444
445
446
                }
                Label {
                    id: placeHolder
                    visible: false
                    enabled: false
Yuri Chornoivan's avatar
Yuri Chornoivan committed
447
                    text: i18n("Edit track name")
448
449
                    anchors.verticalCenter: parent.verticalCenter
                    anchors.left: parent.left
450
451
                    anchors.leftMargin: 4
                    elide: Qt.ElideRight
452
                    font: miniFont
453
                }
454
455
456
457
458
                TextField {
                    id: nameEdit
                    visible: false
                    width: parent.width
                    text: trackName
459
                    font: miniFont
460
461
462
463
464
465
                    background: Rectangle {
                        radius: 2
                        color: activePalette.window
                        anchors.fill: parent
                    }
                    /*style: TextFieldStyle {
466
467
468
                        padding.top:0
                        padding.bottom: 0
                        background: Rectangle {
469
470
                            radius: 2
                            color: activePalette.window
471
472
                            anchors.fill: parent
                        }
473
                    }*/
474
                    onEditingFinished: {
475
                        controller.setTrackName(trackId, text)
476
477
                        visible = false
                    }
478
479
480
                }
            }
        }
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
481
482
    }
    Rectangle {
Nicolas Carion's avatar
Nicolas Carion committed
483
            id: resizer
484
            height: 4
Nicolas Carion's avatar
Nicolas Carion committed
485
486
487
488
            color: 'red'
            opacity: 0
            Drag.active: trimInMouseArea.drag.active
            Drag.proposedAction: Qt.MoveAction
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
489
490
            width: trackHeadRoot.width
            y: trackHeadRoot.height - height
491

Nicolas Carion's avatar
Nicolas Carion committed
492
493
494
495
496
497
498
            MouseArea {
                id: trimInMouseArea
                anchors.fill: parent
                hoverEnabled: true
                cursorShape: Qt.SizeVerCursor
                drag.target: parent
                drag.axis: Drag.YAxis
499
                drag.minimumY: root.collapsedHeight - resizer.height
Nicolas Carion's avatar
Nicolas Carion committed
500
501
                property double startY
                property double originalY
502
                drag.smoothed: false
503
                property bool dragStarted: false
504

Nicolas Carion's avatar
Nicolas Carion committed
505
                onPressed: {
506
                    root.autoScrolling = false
507
                    dragStarted = false
Nicolas Carion's avatar
Nicolas Carion committed
508
509
510
511
                    startY = mapToItem(null, x, y).y
                    originalY = trackHeadRoot.height // reusing originalX to accumulate delta for bubble help
                }
                onReleased: {
512
                    root.autoScrolling = timeline.autoScroll
513
514
515
                    if (!trimInMouseArea.containsMouse) {
                        parent.opacity = 0
                    }
516
                    if (mouse.modifiers & Qt.ShiftModifier && dragStarted) {
517
518
                        timeline.adjustAllTrackHeight(trackHeadRoot.trackId, trackHeadRoot.myTrackHeight)
                    }
Nicolas Carion's avatar
Nicolas Carion committed
519
                }
520
                onEntered: parent.opacity = 0.3
Nicolas Carion's avatar
Nicolas Carion committed
521
                onExited: parent.opacity = 0
522
523
524
                onDoubleClicked: {
                    timeline.defaultTrackHeight(mouse.modifiers & Qt.ShiftModifier ? -1 : trackHeadRoot.trackId)
                }
Nicolas Carion's avatar
Nicolas Carion committed
525
526
527
                onPositionChanged: {
                    if (mouse.buttons === Qt.LeftButton) {
                        parent.opacity = 0.5
528
529
530
                        if (!dragStarted && Math.abs(mapToItem(null, x, y).y - startY) > 2) {
                            dragStarted = true
                        }
Nicolas Carion's avatar
Nicolas Carion committed
531
                        var newHeight = originalY + (mapToItem(null, x, y).y - startY)
532
                        newHeight =  Math.max(root.collapsedHeight, newHeight)
Nicolas Carion's avatar
Nicolas Carion committed
533
534
                        trackHeadRoot.myTrackHeight = newHeight
                    }
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
    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
        }
    }
561
562
}