Composition.qml 15.3 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/***************************************************************************
 *   Copyright (C) 2017 by Jean-Baptiste Mardelle                          *
 *   This file is part of Kdenlive. See www.kdenlive.org.                  *
 *   Based on work by Dan Dennedy <dan@dennedy.org> (Shotcut)              *
 *                                                                         *
 *   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 2 of the License, or     *
 *   (at your option) version 3 or any later version accepted by the       *
 *   membership of KDE e.V. (or its successor approved  by the membership  *
 *   of KDE e.V.), which shall act as a proxy defined in Section 14 of     *
 *   version 3 of the license.                                             *
 *                                                                         *
 *   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/>. *
 ***************************************************************************/

23
24
25
import QtQuick 2.11
import QtQuick.Controls 2.4
import QtQml.Models 2.11
26
import QtQuick.Window 2.2
27
import 'Timeline.js' as Logic
28
29

Item {
30
    id: compositionRoot
31
    property real timeScale: 1
32
33
34
    property string clipName: ''
    property string clipResource: ''
    property string mltService: ''
Jean-Baptiste Mardelle's avatar
Jean-Baptiste Mardelle committed
35
    property int modelStart
36
    property int displayHeight: 0
37
    property var parentTrack: trackRoot
38
39
40
41
    property int inPoint: 0
    property int outPoint: 0
    property int clipDuration: 0
    property bool isAudio: false
42
    property bool showKeyframes: false
43
    property var itemType: 0
44
    property bool isGrabbed: false
45
    property var keyframeModel
46
47
    property bool grouped: false
    property int binId: 0
48
    property int scrollX: 0
49
    property int trackHeight
50
51
    property int trackIndex //Index in track repeater
    property int trackId: -42    //Id in the model
52
    property int aTrack: -1
53
54
    property int clipId     //Id of the clip in the model
    property int originalTrackId: trackId
55
    property bool isComposition: true
56
57
58
59
60
61
    property int originalX: x
    property int originalDuration: clipDuration
    property int lastValidDuration: clipDuration
    property int draggedX: x
    property bool selected: false
    property double speed: 1.0
62
    property color color: displayRect.color
63
    property color borderColor: 'black'
64
    property bool hideCompoViews
65
    property int scrollStart: scrollView.contentX - modelStart * timeline.scaleFactor
66
    property int mouseXPos: mouseArea.mouseX
67
68
69
70

    signal moved(var clip)
    signal dragged(var clip, var mouse)
    signal dropped(var clip)
71
    signal draggedToTrack(var clip, int pos, int xpos)
72
73
74
75
76
    signal trimmingIn(var clip, real newDuration, var mouse)
    signal trimmedIn(var clip)
    signal trimmingOut(var clip, real newDuration, var mouse)
    signal trimmedOut(var clip)

77
    onScrollStartChanged: {
78
        compositionRoot.hideCompoViews = compositionRoot.scrollStart > width || compositionRoot.scrollStart + scrollView.contentItem.width < 0
79
80
    }

81
    onKeyframeModelChanged: {
82
83
84
        if (effectRow.keyframecanvas) {
            effectRow.keyframecanvas.requestPaint()
        }
85
86
    }

87
88
89
90
    onModelStartChanged: {
        x = modelStart * timeScale;
    }

91
92
    onIsGrabbedChanged: {
        if (compositionRoot.isGrabbed) {
93
            grabItem()
94
95
        }
    }
96
97
98
99
100
101

    function grabItem() {
        compositionRoot.forceActiveFocus()
        mouseArea.focus = true
    }

102
103
104
105
    onTrackIdChanged: {
        compositionRoot.parentTrack = Logic.getTrackById(trackId)
        compositionRoot.y = compositionRoot.originalTrackId == -1 || trackId == originalTrackId ? 0 : parentTrack.y - Logic.getTrackById(compositionRoot.originalTrackId).y;
    }
106
    onClipDurationChanged: {
107
        width = clipDuration * timeScale;
108
    }
109
    onTimeScaleChanged: {
110
        x = modelStart * timeScale;
111
        width = clipDuration * timeScale;
112
        labelRect.x = scrollX > modelStart * timeScale ? scrollX - modelStart * timeScale : 0
113
    }
114
115
    onScrollXChanged: {
        labelRect.x = scrollX > modelStart * timeScale ? scrollX - modelStart * timeScale : 0
116
    }
117
/*    function reparent(track) {
118
119
        parent = track
        isAudio = track.isAudio
120
        parentTrack = track
121
        displayHeight = track.height / 2
122
123
        compositionRoot.trackId = parentTrack.trackId
    }
124
*/
125
    function updateDrag() {
126
        console.log('XXXXXXXXXXXXXXX\n\nXXXXXXXXXXXXX \nUPDATING COMPO DRAG')
127
128
129
        var itemPos = mapToItem(tracksContainerArea, 0, displayRect.y, displayRect.width, displayRect.height)
        initDrag(compositionRoot, itemPos, compositionRoot.clipId, compositionRoot.modelStart, compositionRoot.trackId, true)
    }
130
131
    MouseArea {
        id: mouseArea
132
        anchors.fill: displayRect
133
        acceptedButtons: Qt.RightButton
134
135
        enabled: root.activeTool === 0
        hoverEnabled: root.activeTool === 0
136
        Keys.onShortcutOverride: event.accepted = compositionRoot.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)
137
        Keys.onLeftPressed: {
138
139
            var offset = event.modifiers === Qt.ShiftModifier ? timeline.fps() : 1
            controller.requestCompositionMove(compositionRoot.clipId, compositionRoot.originalTrackId, compositionRoot.modelStart - offset, true, true)
140
141
        }
        Keys.onRightPressed: {
142
143
            var offset = event.modifiers === Qt.ShiftModifier ? timeline.fps() : 1
            controller.requestCompositionMove(compositionRoot.clipId, compositionRoot.originalTrackId, compositionRoot.modelStart + offset, true, true)
144
145
146
147
148
149
150
        }
        Keys.onUpPressed: {
            controller.requestCompositionMove(compositionRoot.clipId, controller.getNextTrackId(compositionRoot.originalTrackId), compositionRoot.modelStart, true, true)
        }
        Keys.onDownPressed: {
            controller.requestCompositionMove(compositionRoot.clipId, controller.getPreviousTrackId(compositionRoot.originalTrackId), compositionRoot.modelStart, true, true)
        }
151
152
153
        Keys.onEscapePressed: {
            timeline.grabCurrent()
        }
154
        cursorShape: (trimInMouseArea.drag.active || trimOutMouseArea.drag.active)? Qt.SizeHorCursor : dragProxyArea.cursorShape
155
156

        onPressed: {
157
                root.autoScrolling = false
158
                compositionRoot.forceActiveFocus();
159
                root.mainItemId = compositionRoot.clipId
160
                if (mouse.button == Qt.RightButton) {
161
                    if (timeline.selection.indexOf(compositionRoot.clipId) == -1) {
162
                        controller.requestAddToSelection(compositionRoot.clipId, true)
163
                    }
164
                    root.showCompositionMenu()
165
                }
166
            }
167
        onReleased: {
168
            root.autoScrolling = timeline.autoScroll
169
        }
170
171
172
        onEntered: {
            var itemPos = mapToItem(tracksContainerArea, 0, 0, width, height)
            initDrag(compositionRoot, itemPos, compositionRoot.clipId, compositionRoot.modelStart, compositionRoot.trackId, true)
173
        }
174
175
        onExited: {
            endDrag()
176
        }
177
178
179
180
181
182
183
184
        onDoubleClicked: {
            if (mouse.modifiers & Qt.ShiftModifier) {
                if (keyframeModel && showKeyframes) {
                    // Add new keyframe
                    var xPos = Math.round(mouse.x  / timeline.scaleFactor)
                    var yPos = (compositionRoot.height - mouse.y) / compositionRoot.height
                    keyframeModel.addKeyframe(xPos + compositionRoot.inPoint, yPos)
                } else {
185
                    proxy.position = compositionRoot.x / timeline.scaleFactor
186
187
188
189
190
                }
            } else {
                timeline.editItemDuration(clipId)
            }
        }
191
        onPositionChanged: {
192
193
194
            if (parentTrack) {
                var mapped = parentTrack.mapFromItem(compositionRoot, mouse.x, mouse.y).x
                root.mousePosChanged(Math.round(mapped / timeline.scaleFactor))
195
196
            }
        }
197
198
199
        onWheel: zoomByWheel(wheel)
    }

200
201
202
203
204
    Rectangle {
        id: displayRect
        anchors.top: compositionRoot.top
        anchors.right: compositionRoot.right
        anchors.left: compositionRoot.left
205
206
        anchors.topMargin: displayHeight
        height: parentTrack.height - displayHeight + (compositionRoot.selected ? Math.min(Logic.getTrackHeightByPos(Logic.getTrackIndexFromId(parentTrack.trackInternalId) + 1) / 3, root.baseUnit) : 0)
207
        color: selected ? 'mediumpurple' : Qt.darker('mediumpurple')
208
        border.color: selected ? root.selectionColor : grouped ? root.groupColor : borderColor
209
210
211
212
213
214
215
216
217
218
219
        border.width: isGrabbed ? 8 : 1.5
        opacity: dragProxyArea.drag.active && dragProxy.draggedItem == clipId ? 0.5 : 1.0
        Item {
            // clipping container
            id: container
            anchors.fill: displayRect
            anchors.margins:1.5
            clip: true
            Rectangle {
                // text background
                id: labelRect
220
                color: compositionRoot.aTrack > -1 ? 'yellow' : 'lightgray'
221
222
223
224
225
                anchors.top: container.top
                width: label.width + 2
                height: label.height
                Text {
                    id: label
226
                    text: clipName + (compositionRoot.aTrack > -1 ? ' > ' + timeline.getTrackNameFromMltIndex(compositionRoot.aTrack) : '')
227
                    font: miniFont
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
                    anchors {
                        top: labelRect.top
                        left: labelRect.left
                        topMargin: 1
                        leftMargin: 1
                    }
                    color: 'black'
                }
            }
            KeyframeView {
                id: effectRow
                visible: compositionRoot.showKeyframes && compositionRoot.keyframeModel
                selected: compositionRoot.selected
                inPoint: 0
                outPoint: compositionRoot.clipDuration
                masterObject: compositionRoot
244
                kfrModel: compositionRoot.hideCompoViews ? 0 : compositionRoot.keyframeModel
245
            }
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
            ToolTip {
                visible: mouseArea.containsMouse && !trimInMouseArea.containsMouse && !trimOutMouseArea.containsMouse && !trimInMouseArea.drag.active && !trimOutMouseArea.drag.active
                delay: 1000
                timeout: 5000
                background: Rectangle {
                    color: activePalette.alternateBase
                    border.color: activePalette.light
                }
                contentItem: Label {
                    color: activePalette.text
                    font: miniFont
                    text: '%1\n%4: %5'.arg(label.text)
                        .arg(i18n("Duration"))
                        .arg(timeline.simplifiedTC(compositionRoot.clipDuration))
                }
            }
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
        }
        /*Drag.active: mouseArea.drag.active
        Drag.proposedAction: Qt.MoveAction*/

    states: [
        State {
            name: 'normal'
            when: !compositionRoot.selected
            PropertyChanges {
                target: compositionRoot
                z: 0
            }
        },
        State {
            name: 'selected'
            when: compositionRoot.selected
            PropertyChanges {
                target: compositionRoot
                z: 1
                color: 'mediumpurple'
            }
        }
    ]

286
287
    Rectangle {
        id: trimIn
288
        anchors.left: displayRect.left
289
        anchors.leftMargin: 0
290
        height: displayRect.height
291
        width: root.baseUnit / 3
292
293
294
295
        color: isAudio? 'green' : 'lawngreen'
        opacity: 0
        Drag.active: trimInMouseArea.drag.active
        Drag.proposedAction: Qt.MoveAction
296
        enabled: !compositionRoot.grouped
297
        visible: trimInMouseArea.pressed || (root.activeTool === 0 && !dragProxyArea.drag.active && compositionRoot.width > 4 * width)
298
299
300
301
302
303
304
305

        MouseArea {
            id: trimInMouseArea
            anchors.fill: parent
            hoverEnabled: true
            cursorShape: Qt.SizeHorCursor
            drag.target: parent
            drag.axis: Drag.XAxis
306
            drag.smoothed: false
307
            visible: root.activeTool === 0
308
309

            onPressed: {
310
                root.autoScrolling = false
311
                compositionRoot.originalX = compositionRoot.x
312
                compositionRoot.originalDuration = clipDuration
313
314
315
                parent.anchors.left = undefined
            }
            onReleased: {
316
                root.autoScrolling = timeline.autoScroll
317
                parent.anchors.left = displayRect.left
318
                compositionRoot.trimmedIn(compositionRoot)
319
320
321
322
                parent.opacity = 0
            }
            onPositionChanged: {
                if (mouse.buttons === Qt.LeftButton) {
323
                    var delta = Math.round((trimIn.x) / timeScale)
324
325
326
                    if (delta < -modelStart) {
                        delta = -modelStart
                    }
327
                    if (delta !== 0) {
328
329
                        var newDuration = compositionRoot.clipDuration - delta
                        compositionRoot.trimmingIn(compositionRoot, newDuration, mouse)
330
331
332
333
334
335
336
337
338
                    }
                }
            }
            onEntered: parent.opacity = 0.5
            onExited: parent.opacity = 0
        }
    }
    Rectangle {
        id: trimOut
339
        anchors.right: displayRect.right
340
        anchors.rightMargin: 0
341
        height: displayRect.height
342
        width: root.baseUnit / 3
343
344
345
346
        color: 'red'
        opacity: 0
        Drag.active: trimOutMouseArea.drag.active
        Drag.proposedAction: Qt.MoveAction
347
        enabled: !compositionRoot.grouped
348
        visible: trimOutMouseArea.pressed || (root.activeTool === 0 && !dragProxyArea.drag.active && compositionRoot.width > 4 * width)
349
350
351
352
353
354
355
356

        MouseArea {
            id: trimOutMouseArea
            anchors.fill: parent
            hoverEnabled: true
            cursorShape: Qt.SizeHorCursor
            drag.target: parent
            drag.axis: Drag.XAxis
357
            drag.smoothed: false
358
            visible: root.activeTool === 0
359
360

            onPressed: {
361
                root.autoScrolling = false
362
                compositionRoot.originalDuration = clipDuration
363
364
365
                parent.anchors.right = undefined
            }
            onReleased: {
366
                root.autoScrolling = timeline.autoScroll
367
                parent.anchors.right = displayRect.right
368
                compositionRoot.trimmedOut(compositionRoot)
369
370
371
372
            }
            onPositionChanged: {
                if (mouse.buttons === Qt.LeftButton) {
                    var newDuration = Math.round((parent.x + parent.width) / timeScale)
373
                    compositionRoot.trimmingOut(compositionRoot, newDuration, mouse)
374
375
376
377
378
379
                }
            }
            onEntered: parent.opacity = 0.5
            onExited: parent.opacity = 0
        }
    }
380
}
381
}