Warning, /multimedia/kdenlive/src/timeline2/view/qml/Composition.qml is written in an unsupported language. File is not indexed.

0001 /*
0002     SPDX-FileCopyrightText: 2017 Jean-Baptiste Mardelle
0003     SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0004     This file is part of Kdenlive. See www.kdenlive.org.
0005     Based on work by Dan Dennedy <dan@dennedy.org> (Shotcut)
0006 */
0007 
0008 import QtQuick 2.15
0009 import QtQuick.Controls 2.15
0010 import QtQml.Models 2.15
0011 import QtQuick.Window 2.15
0012 import 'Timeline.js' as Logic
0013 
0014 Item {
0015     id: compositionRoot
0016     property real timeScale: 1
0017     property string clipName: ''
0018     property string clipResource: ''
0019     property string mltService: ''
0020     property int modelStart
0021     property int displayHeight: 0
0022     property var parentTrack: trackRoot
0023     property int inPoint: 0
0024     property int outPoint: 0
0025     property int clipDuration: 0
0026     property bool isAudio: false
0027     property bool showKeyframes: false
0028     property var itemType: 0
0029     property bool isGrabbed: false
0030     property var keyframeModel
0031     property bool grouped: false
0032     property int binId: 0
0033     property int trackHeight
0034     property int trackIndex //Index in track repeater
0035     property int trackId: -42    //Id in the model
0036     property int aTrack: -1
0037     property int clipId     //Id of the clip in the model
0038     property int originalTrackId: trackId
0039     property bool isComposition: true
0040     property int originalX: x
0041     property int originalDuration: clipDuration
0042     property int lastValidDuration: clipDuration
0043     property int draggedX: x
0044     property bool selected: false
0045     property double speed: 1.0
0046     property color color: displayRect.color
0047     property color borderColor: 'black'
0048     property bool hideCompoViews: !visible || width < root.minClipWidthForViews
0049     property int scrollStart: scrollView.contentX - (compositionRoot.modelStart * root.timeScale)
0050     visible: scrollView.width + compositionRoot.scrollStart >= 0 && compositionRoot.scrollStart < compositionRoot.width
0051 
0052     property int mouseXPos: mouseArea.mouseX
0053     // We set coordinates to ensure the item can be found using childAt in timeline.qml getItemAtPosq
0054     property int trackOffset: 5
0055     y: trackOffset
0056     height: 5
0057     enabled: !compoArea.containsDrag
0058 
0059 
0060     signal trimmingIn(var clip, real newDuration)
0061     signal trimmedIn(var clip)
0062     signal trimmingOut(var clip, real newDuration)
0063     signal trimmedOut(var clip)
0064 
0065     onScrollStartChanged: {
0066         if (!compositionRoot.visible) {
0067             return
0068         }
0069         updateLabelOffset()
0070         if (!compositionRoot.hideClipViews && compositionRoot.width > scrollView.width) {
0071             if (effectRow.item && effectRow.item.kfrCanvas) {
0072                 effectRow.item.kfrCanvas.requestPaint()
0073             }
0074         }
0075     }
0076 
0077     /*onKeyframeModelChanged: {
0078         if (effectRow.item && effectRow.item.keyframecanvas) {
0079             effectRow.item.keyframecanvas.requestPaint()
0080         }
0081     }*/
0082 
0083     onModelStartChanged: {
0084         x = modelStart * timeScale;
0085     }
0086 
0087     onIsGrabbedChanged: {
0088         if (compositionRoot.isGrabbed) {
0089             grabItem()
0090         }
0091     }
0092 
0093     function itemHeight() {
0094         return displayRect.height
0095     }
0096 
0097     function grabItem() {
0098         compositionRoot.forceActiveFocus()
0099         mouseArea.focus = true
0100     }
0101 
0102     function resetSelection() {
0103         if (effectRow.visible) {
0104             effectRow.item.resetSelection()
0105         }
0106     }
0107 
0108     function doesContainMouse(pnt) {
0109         return pnt.x >= 0 && pnt.x < displayRect.width && (pnt.y > displayRect.y - trackOffset)
0110     }
0111 
0112     onTrackIdChanged: {
0113         compositionRoot.parentTrack = Logic.getTrackById(trackId)
0114         compositionRoot.y = compositionRoot.originalTrackId == -1 || trackId == originalTrackId ? 0 : parentTrack.y - Logic.getTrackById(compositionRoot.originalTrackId).y;
0115     }
0116     onClipDurationChanged: {
0117         width = clipDuration * timeScale;
0118     }
0119     onTimeScaleChanged: {
0120         x = modelStart * timeScale;
0121         width = clipDuration * timeScale;
0122         if (compositionRoot.visible) {
0123             updateLabelOffset()
0124             if (!compositionRoot.hideClipViews) {
0125                 if (effectRow.item && effectRow.item.kfrCanvas) {
0126                     effectRow.item.kfrCanvas.requestPaint()
0127                 }
0128             }
0129         }
0130     }
0131 
0132     function updateLabelOffset()
0133     {
0134         labelRect.anchors.leftMargin = compositionRoot.scrollStart > 0 ? (labelRect.width > compositionRoot.width ? 0 : compositionRoot.scrollStart) : 0
0135     }
0136 
0137 /*    function reparent(track) {
0138         parent = track
0139         isAudio = track.isAudio
0140         parentTrack = track
0141         displayHeight = track.height / 2
0142         compositionRoot.trackId = parentTrack.trackId
0143     }
0144 */
0145     function updateDrag() {
0146         console.log('XXXXXXXXXXXXXXX\n\nXXXXXXXXXXXXX \nUPDATING COMPO DRAG')
0147         var itemPos = mapToItem(tracksContainerArea, 0, displayRect.y, displayRect.width, displayRect.height)
0148         initDrag(compositionRoot, itemPos, compositionRoot.clipId, compositionRoot.modelStart, compositionRoot.trackId, true)
0149     }
0150 
0151     Rectangle {
0152         id: displayRect
0153         anchors.top: compositionRoot.top
0154         anchors.right: compositionRoot.right
0155         anchors.left: compositionRoot.left
0156         anchors.topMargin: displayHeight - compositionRoot.trackOffset
0157         height: parentTrack.height - displayHeight
0158         color: Qt.darker('mediumpurple')
0159         border.color: grouped ? root.groupColor : mouseArea.containsMouse ? activePalette.highlight : borderColor
0160         border.width: isGrabbed ? 8 : 2
0161         opacity: dragProxyArea.drag.active && dragProxy.draggedItem == clipId ? 0.5 : 1.0
0162         onWidthChanged: {
0163             console.log('TRIM AREA ENABLED: ',trimOutMouseArea.enabled)
0164         }
0165 
0166         /*Drag.active: mouseArea.drag.active
0167         Drag.proposedAction: Qt.MoveAction*/
0168 
0169         states: [
0170             State {
0171                 name: 'normal'
0172                 when: !compositionRoot.selected
0173                 PropertyChanges {
0174                     target: compositionRoot
0175                     z: 0
0176                 }
0177             },
0178             State {
0179                 name: 'selected'
0180                 when: compositionRoot.selected
0181                 PropertyChanges {
0182                     target: compositionRoot
0183                     z: 1
0184                 }
0185                 PropertyChanges {
0186                     target: displayRect
0187                     height: parentTrack.height - displayHeight + Math.min(Logic.getTrackHeightByPos(Logic.getTrackIndexFromId(parentTrack.trackInternalId) + 1) / 3, root.baseUnit)
0188                     color: 'mediumpurple'
0189                     border.color: root.selectionColor
0190                 }
0191             }
0192         ]
0193         transitions: Transition {
0194             NumberAnimation {
0195                 properties: "height";
0196                 easing.type: Easing.InOutQuad;
0197                 duration: 150;
0198             }
0199         }
0200         MouseArea {
0201             id: mouseArea
0202             anchors.fill: parent
0203             acceptedButtons: Qt.RightButton
0204             enabled: root.activeTool === 0
0205             hoverEnabled: root.activeTool === 0
0206             Keys.onShortcutOverride: event => {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)}
0207             Keys.onLeftPressed: event => {
0208                 var offset = event.modifiers === Qt.ShiftModifier ? timeline.fps() : 1
0209                 controller.requestCompositionMove(compositionRoot.clipId, compositionRoot.originalTrackId, compositionRoot.modelStart - offset, true, true)
0210             }
0211             Keys.onRightPressed: event => {
0212                 var offset = event.modifiers === Qt.ShiftModifier ? timeline.fps() : 1
0213                 controller.requestCompositionMove(compositionRoot.clipId, compositionRoot.originalTrackId, compositionRoot.modelStart + offset, true, true)
0214             }
0215             Keys.onUpPressed: {
0216                 controller.requestCompositionMove(compositionRoot.clipId, controller.getNextTrackId(compositionRoot.originalTrackId), compositionRoot.modelStart, true, true)
0217             }
0218             Keys.onDownPressed: {
0219                 controller.requestCompositionMove(compositionRoot.clipId, controller.getPreviousTrackId(compositionRoot.originalTrackId), compositionRoot.modelStart, true, true)
0220             }
0221             Keys.onEscapePressed: {
0222                 timeline.grabCurrent()
0223             }
0224             cursorShape: (trimInMouseArea.drag.active || trimOutMouseArea.drag.active)? Qt.SizeHorCursor : dragProxyArea.cursorShape
0225 
0226             onPressed: mouse => {
0227                 root.autoScrolling = false
0228                 compositionRoot.forceActiveFocus();
0229                 root.mainItemId = compositionRoot.clipId
0230                 if (mouse.button == Qt.RightButton) {
0231                     if (timeline.selection.indexOf(compositionRoot.clipId) === -1) {
0232                         controller.requestAddToSelection(compositionRoot.clipId, true)
0233                     }
0234                     root.showCompositionMenu()
0235                 }
0236             }
0237             onReleased: {
0238                 root.autoScrolling = timeline.autoScroll
0239             }
0240             onEntered: {
0241                 var itemPos = mapToItem(tracksContainerArea, 0, 0, width, height)
0242                 initDrag(compositionRoot, itemPos, compositionRoot.clipId, compositionRoot.modelStart, compositionRoot.trackId, true)
0243                 var s = i18n("%1, Position: %2, Duration: %3".arg(label.text).arg(timeline.simplifiedTC(compositionRoot.modelStart)).arg(timeline.simplifiedTC(compositionRoot.clipDuration)))
0244                 timeline.showToolTip(s)
0245             }
0246             onExited: {
0247                 endDrag()
0248                 if (!trimInMouseArea.containsMouse && !trimOutMouseArea.containsMouse) {
0249                     timeline.showToolTip()
0250                 }
0251             }
0252             onDoubleClicked: mouse => {
0253                 if (mouse.modifiers & Qt.ShiftModifier) {
0254                     if (keyframeModel && showKeyframes) {
0255                         // Add new keyframe
0256                         var xPos = Math.round(mouse.x  / timeline.scaleFactor)
0257                         var yPos = (compositionRoot.height - mouse.y) / compositionRoot.height
0258                         keyframeModel.addKeyframe(xPos + compositionRoot.inPoint, yPos)
0259                     } else {
0260                         proxy.position = compositionRoot.x / timeline.scaleFactor
0261                     }
0262                 } else {
0263                     timeline.editItemDuration(clipId)
0264                 }
0265             }
0266             onPositionChanged: mouse => {
0267                 if (parentTrack) {
0268                     var mapped = parentTrack.mapFromItem(compositionRoot, mouse.x, mouse.y).x
0269                     root.mousePosChanged(Math.round(mapped / timeline.scaleFactor))
0270                 }
0271             }
0272             onWheel: wheel => zoomByWheel(wheel)
0273 
0274             MouseArea {
0275                 id: trimInMouseArea
0276                 x: enabled ? -displayRect.border.width : 0
0277                 height: parent.height
0278                 width: root.baseUnit / 2
0279                 visible: enabled && root.activeTool === 0
0280                 enabled: !compositionRoot.grouped && (pressed || displayRect.width > 3 * width)
0281                 hoverEnabled: true
0282                 cursorShape: (enabled && (containsMouse || pressed) ? Qt.SizeHorCursor : Qt.OpenHandCursor)
0283                 drag.target: trimInMouseArea
0284                 drag.axis: Drag.XAxis
0285                 drag.smoothed: false
0286                 onPressed: {
0287                     root.autoScrolling = false
0288                     root.trimInProgress = true;
0289                     compositionRoot.originalX = compositionRoot.x
0290                     compositionRoot.originalDuration = clipDuration
0291                     anchors.left = undefined
0292                     trimIn.opacity = 0
0293                 }
0294                 onReleased: {
0295                     root.autoScrolling = timeline.autoScroll
0296                     anchors.left = parent.left
0297                     compositionRoot.trimmedIn(compositionRoot)
0298                     trimIn.opacity = 0
0299                     updateDrag()
0300                     root.trimInProgress = false;
0301                 }
0302                 onPositionChanged: mouse => {
0303                     if (mouse.buttons === Qt.LeftButton) {
0304                         var delta = Math.round(x / timeScale)
0305                         if (delta < -modelStart) {
0306                             delta = -modelStart
0307                         }
0308                         if (delta !== 0) {
0309                             var newDuration = compositionRoot.clipDuration - delta
0310                             compositionRoot.trimmingIn(compositionRoot, newDuration)
0311                         }
0312                     }
0313                 }
0314                 onEntered: {
0315                     if (!pressed) {
0316                         trimIn.opacity = 1
0317                         timeline.showKeyBinding(i18n("<b>Drag</b> to resize"))
0318                     }
0319                 }
0320                 onExited: {
0321                     trimIn.opacity = 0
0322                     if (!mouseArea.containsMouse) {
0323                         timeline.showToolTip()
0324                     }
0325                     if (!trimInMouseArea.containsMouse) {
0326                         timeline.showKeyBinding()
0327                     }
0328                 }
0329                 Rectangle {
0330                     id: trimIn
0331                     anchors.left: parent.left
0332                     width: displayRect.border.width
0333                     height: parent.height
0334                     color: 'lawngreen'
0335                     opacity: 0
0336                     Drag.active: trimInMouseArea.drag.active
0337                     Drag.proposedAction: Qt.MoveAction
0338                     visible: trimInMouseArea.pressed || (root.activeTool === 0 && !mouseArea.drag.active && parent.enabled)
0339                 }
0340             }
0341 
0342             MouseArea {
0343                 id: trimOutMouseArea
0344                 anchors.right: parent.right
0345                 anchors.rightMargin: enabled ? -displayRect.border.width : 0
0346                 height: displayRect.height
0347                 width: root.baseUnit / 2
0348                 hoverEnabled: true
0349                 cursorShape: (enabled && (containsMouse || pressed) ? Qt.SizeHorCursor : Qt.OpenHandCursor)
0350                 drag.target: trimOutMouseArea
0351                 drag.axis: Drag.XAxis
0352                 drag.smoothed: false
0353                 visible: enabled && root.activeTool === 0
0354                 enabled: !compositionRoot.grouped && (pressed || displayRect.width > 3 * width)
0355 
0356                 onPressed: {
0357                     root.autoScrolling = false
0358                     root.trimInProgress = true;
0359                     compositionRoot.originalDuration = clipDuration
0360                     anchors.right = undefined
0361                     trimOut.opacity = 0
0362                 }
0363                 onReleased: {
0364                     root.autoScrolling = timeline.autoScroll
0365                     anchors.right = parent.right
0366                     compositionRoot.trimmedOut(compositionRoot)
0367                     updateDrag()
0368                     root.trimInProgress = false;
0369                 }
0370                 onPositionChanged: mouse => {
0371                     if (mouse.buttons === Qt.LeftButton) {
0372                         var newDuration = Math.round((x + width) / timeScale)
0373                         compositionRoot.trimmingOut(compositionRoot, newDuration)
0374                     }
0375                 }
0376                 onEntered: {
0377                     if (!pressed) {
0378                         trimOut.opacity = 1
0379                         timeline.showKeyBinding(i18n("<b>Drag</b> to resize"))
0380                     }
0381                 }
0382                 onExited: {
0383                     trimOut.opacity = 0
0384                     if (!mouseArea.containsMouse) {
0385                         timeline.showToolTip()
0386                     }
0387                     if (!trimOutMouseArea.containsMouse) {
0388                         timeline.showKeyBinding()
0389                     }
0390                 }
0391                 Rectangle {
0392                     id: trimOut
0393                     anchors.right: parent.right
0394                     width: displayRect.border.width
0395                     height: parent.height
0396                     color: 'red'
0397                     opacity: 0
0398                     Drag.active: trimOutMouseArea.drag.active
0399                     Drag.proposedAction: Qt.MoveAction
0400                     visible: trimOutMouseArea.pressed || (root.activeTool === 0 && !mouseArea.drag.active && parent.enabled)
0401                 }
0402             }
0403             Item {
0404             // clipping container
0405             id: container
0406             anchors.fill: parent
0407             anchors.margins: displayRect.border.width
0408             clip: true
0409             Rectangle {
0410                 // Debug: Clip Id background
0411                 id: debugCidRect
0412                 color: 'magenta'
0413                 width: debugCid.width
0414                 height: debugCid.height
0415                 visible: root.debugmode
0416                 anchors.left: labelRect.right
0417                 Text {
0418                     // Composition ID text
0419                     id: debugCid
0420                     text: compositionRoot.clipId
0421                     font: miniFont
0422                     anchors {
0423                         top: debugCidRect.top
0424                         left: debugCidRect.left
0425                         topMargin: 1
0426                         leftMargin: 1
0427                     }
0428                     color: 'white'
0429                 }
0430             }
0431             Rectangle {
0432                 // text background
0433                 id: labelRect
0434                 anchors.top: parent.top
0435                 anchors.left: parent.left
0436                 anchors.leftMargin: compositionRoot.scrollStart > 0 ? (labelRect.width > compositionRoot.width ? 0 : compositionRoot.scrollStart) : 0
0437                 color: compositionRoot.aTrack > -1 ? 'yellow' : 'lightgray'
0438                 visible: compositionRoot.width > root.baseUnit
0439                 width: label.width + 2
0440                 height: label.height
0441                 Text {
0442                     id: label
0443                     text: clipName + (compositionRoot.aTrack > -1 ? ' > ' + timeline.getTrackNameFromMltIndex(compositionRoot.aTrack) : '')
0444                     font: miniFont
0445                     anchors {
0446                         top: labelRect.top
0447                         left: labelRect.left
0448                         topMargin: 1
0449                         leftMargin: 1
0450                     }
0451                     color: 'black'
0452                 }
0453             }
0454         }
0455         Loader {
0456             // keyframes container
0457             id: effectRow
0458             clip: true
0459             anchors.fill: parent
0460             //asynchronous: true
0461             visible: status == Loader.Ready && compositionRoot.showKeyframes && compositionRoot.keyframeModel && compositionRoot.width > 2 * root.baseUnit
0462             source: compositionRoot.hideClipViews || compositionRoot.keyframeModel == undefined ? "" : "KeyframeView.qml"
0463             Binding {
0464                     target: effectRow.item
0465                     property: "kfrModel"
0466                     value: compositionRoot.hideClipViews ? undefined : compositionRoot.keyframeModel
0467                     when: effectRow.status == Loader.Ready && effectRow.item
0468                 }
0469                 Binding {
0470                     target: effectRow.item
0471                     property: "selected"
0472                     value: compositionRoot.selected
0473                     when: effectRow.status == Loader.Ready && effectRow.item
0474                 }
0475                 Binding {
0476                     target: effectRow.item
0477                     property: "inPoint"
0478                     value: 0
0479                     when: effectRow.status == Loader.Ready && effectRow.item
0480                 }
0481                 Binding {
0482                     target: effectRow.item
0483                     property: "outPoint"
0484                     value: compositionRoot.clipDuration
0485                     when: effectRow.status == Loader.Ready && effectRow.item
0486                 }
0487                 Binding {
0488                     target: effectRow.item
0489                     property: "modelStart"
0490                     value: compositionRoot.modelStart
0491                     when: effectRow.status == Loader.Ready && effectRow.item
0492                 }
0493                 Binding {
0494                     target: effectRow.item
0495                     property: "scrollStart"
0496                     value: compositionRoot.scrollStart
0497                     when: effectRow.status == Loader.Ready && effectRow.item
0498                 }
0499                 Binding {
0500                     target: effectRow.item
0501                     property: "clipId"
0502                     value: compositionRoot.clipId
0503                     when: effectRow.status == Loader.Ready && effectRow.item
0504                 }
0505             }
0506             Connections {
0507                 target: effectRow.item
0508                 function onSeek(position) { proxy.position = position }
0509             }
0510         }
0511     }
0512 }