Warning, /multimedia/kdenlive/src/timeline2/view/qml/KeyframeView.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 */ 0005 0006 import QtQuick 2.15 0007 import QtQuick.Controls 2.15 0008 import QtQml.Models 2.15 0009 import com.enums 1.0 0010 0011 Rectangle 0012 { 0013 id: keyframeContainer 0014 property int kfrCount : keyframes.count 0015 anchors.fill: parent 0016 color: Qt.rgba(1,1,0.8, 0.3) 0017 property int activeIndex 0018 property int inPoint 0019 property int outPoint 0020 property int clipId 0021 property int modelStart 0022 property bool selected 0023 property var kfrModel 0024 property int scrollStart 0025 property alias kfrCanvas: keyframecanvas 0026 signal seek(int position) 0027 0028 onKfrCountChanged: { 0029 keyframecanvas.requestPaint() 0030 } 0031 0032 onInPointChanged: { 0033 keyframecanvas.requestPaint() 0034 } 0035 0036 onOutPointChanged: { 0037 keyframecanvas.requestPaint() 0038 } 0039 0040 function resetSelection() { 0041 kfrModel.setActiveKeyframe(-1) 0042 keyframeContainer.activeIndex = -1 0043 keyframeContainer.focus = false 0044 kfrModel.setSelectedKeyframe(-1, false) 0045 } 0046 0047 Keys.onShortcutOverride: { 0048 if (event.key === Qt.Key_Left) { 0049 if (event.modifiers & Qt.AltModifier) { 0050 kfrModel.setActiveKeyframe(Math.max(0, --activeIndex)) 0051 seek(keyframes.itemAt(kfrModel.activeKeyframe).value + keyframeContainer.modelStart - keyframeContainer.inPoint) 0052 event.accepted = true 0053 } else { 0054 var oldFrame = keyframes.itemAt(kfrModel.activeKeyframe).value 0055 var newPos = Math.max(oldFrame - 1 - keyframeContainer.inPoint, 0) 0056 if (newPos != oldFrame) { 0057 timeline.updateEffectKeyframe(clipId, oldFrame, newPos) 0058 event.accepted = true 0059 } 0060 } 0061 } 0062 else if (event.key === Qt.Key_Right) { 0063 if (event.modifiers & Qt.AltModifier) { 0064 kfrModel.setActiveKeyframe(Math.min(keyframes.count - 1, ++activeIndex)) 0065 seek(keyframes.itemAt(kfrModel.activeKeyframe()).value + keyframeContainer.modelStart - keyframeContainer.inPoint) 0066 } else { 0067 var oldFrame = keyframes.itemAt(kfrModel.activeKeyframe()).value 0068 var newPos = Math.min(oldFrame + 1 - keyframeContainer.inPoint, keyframeContainer.outPoint - keyframeContainer.inPoint) 0069 if (newPos != oldFrame) { 0070 timeline.updateEffectKeyframe(clipId, oldFrame, newPos) 0071 } 0072 } 0073 event.accepted = true 0074 } 0075 else if (event.key === Qt.Key_Return || event.key === Qt.Key_Escape) { 0076 keyframeContainer.focus = false 0077 event.accepted = true 0078 } 0079 if ((event.key === Qt.Key_Plus) && !(event.modifiers & Qt.ControlModifier)) { 0080 var newVal = Math.min(keyframes.itemAt(activeIndex).value / parent.height + .05, 1) 0081 kfrModel.updateKeyframe(kfrModel.activeKeyframe(), newVal) 0082 event.accepted = true 0083 } 0084 else if ((event.key === Qt.Key_Minus) && !(event.modifiers & Qt.ControlModifier)) { 0085 var newVal = Math.max(keyframes.itemAt(activeIndex).value / parent.height - .05, 0) 0086 kfrModel.updateKeyframe(kfrModel.activeKeyframe(), newVal) 0087 event.accepted = true 0088 } else { 0089 event.accepted = false 0090 } 0091 } 0092 Item { 0093 // Keyframes container 0094 anchors.fill: parent 0095 z: 5 0096 visible: keyframeContainer.selected && keyframeContainer.width > root.baseUnit * 3 && (kfrCount < (keyframeContainer.width / root.baseUnit)) && kfrCount > 1 0097 Repeater { 0098 id: keyframes 0099 model: kfrModel 0100 Rectangle { 0101 id: keyframe 0102 visible: root.activeTool === ProjectTool.SelectTool 0103 property int frame : model.frame 0104 property int frameType : model.type 0105 property string realValue: model.value 0106 x: (model.frame - keyframeContainer.inPoint) * timeScale 0107 height: parent.height 0108 property int value: parent.height * model.normalizedValue 0109 property int tmpVal : keyframeVal.y + root.baseUnit / 2 0110 property int tmpPos : x + keyframeVal.x + root.baseUnit / 2 0111 property int dragPos : -1 0112 anchors.bottom: parent.bottom 0113 onFrameTypeChanged: { 0114 keyframecanvas.requestPaint() 0115 } 0116 onValueChanged: { 0117 keyframecanvas.requestPaint() 0118 } 0119 onRealValueChanged: { 0120 kf1MouseArea.movingVal = kfrModel.realValue(model.normalizedValue) 0121 } 0122 width: Math.max(1, timeScale) 0123 color: kfMouseArea.containsMouse ? 'darkred' : 'transparent' 0124 MouseArea { 0125 id: kfMouseArea 0126 anchors.fill: parent 0127 anchors.leftMargin: - root.baseUnit/3 0128 anchors.rightMargin: - root.baseUnit/3 0129 hoverEnabled: true 0130 cursorShape: Qt.SizeHorCursor 0131 enabled: parent.x > root.baseUnit / 2 && parent.x < keyframeContainer.width - root.baseUnit / 2 0132 drag.target: parent 0133 drag.smoothed: false 0134 drag.axis: Drag.XAxis 0135 onReleased: { 0136 root.autoScrolling = timeline.autoScroll 0137 dragPos = -1 0138 var newPos = Math.round(parent.x / timeScale) + keyframeContainer.inPoint 0139 if (frame != keyframeContainer.inPoint && newPos != frame) { 0140 if (mouse.modifiers & Qt.ShiftModifier) { 0141 // offset all subsequent keyframes 0142 // TODO: rewrite using timeline to ensure all kf parameters are updated 0143 timeline.offsetKeyframes(clipId, frame, newPos) 0144 } else { 0145 timeline.updateEffectKeyframe(clipId, frame, newPos) 0146 } 0147 } 0148 } 0149 onPositionChanged: { 0150 if (mouse.buttons === Qt.LeftButton) { 0151 if (frame == keyframeContainer.inPoint) { 0152 parent.x = keyframeContainer.inPoint * timeScale 0153 return 0154 } 0155 var newPos = Math.min(Math.round(parent.x / timeScale), Math.round(keyframeContainer.width / timeScale) - 1) 0156 if (newPos < 1) { 0157 newPos = 1 0158 } 0159 if (newPos != dragPos && (newPos == 0 || !timeline.hasKeyframeAt(clipId, frame + newPos))) { 0160 dragPos = newPos 0161 parent.x = newPos * timeScale 0162 keyframecanvas.requestPaint() 0163 } else { 0164 parent.x = dragPos * timeScale 0165 } 0166 } 0167 } 0168 onEntered: { 0169 timeline.showKeyBinding(i18n("<b>Drag</b> to move selected keyframes position. <b>Shift drag</b> to move all keyframes after this one.")) 0170 } 0171 onExited: { 0172 timeline.showKeyBinding() 0173 } 0174 } 0175 Rectangle { 0176 id: keyframeVal 0177 x: - root.baseUnit / 2 0178 y: keyframeContainer.height - keyframe.value - root.baseUnit / 2 0179 width: root.baseUnit 0180 height: width 0181 radius: width / 2 0182 color: model.active ? 'red' : model.selected ? 'orange' : kf1MouseArea.containsMouse || kf1MouseArea.pressed ? root.textColor : root.videoColor 0183 border.color: kf1MouseArea.containsMouse || kf1MouseArea.pressed ? activePalette.highlight : root.textColor 0184 0185 MouseArea { 0186 id: kf1MouseArea 0187 anchors.fill: parent 0188 hoverEnabled: true 0189 cursorShape: shiftPressed ? Qt.SizeVerCursor : Qt.PointingHandCursor 0190 drag.target: parent 0191 drag.smoothed: false 0192 drag.threshold: 1 0193 property string movingVal: kfrModel.realValue(model.normalizedValue) 0194 property double newVal: NaN 0195 property bool shiftPressed: false 0196 onPressed: { 0197 drag.axis = model.moveOnly ? Drag.XAxis : (mouse.modifiers & Qt.ShiftModifier) ? Drag.YAxis : Drag.XAndYAxis 0198 } 0199 onClicked: { 0200 keyframeContainer.focus = true 0201 if (mouse.modifiers & Qt.ControlModifier && model.selected) { 0202 kfrModel.setActiveKeyframe(-1) 0203 keyframeContainer.activeIndex = -1 0204 kfrModel.setSelectedKeyframe(index, true) 0205 } else { 0206 kfrModel.setActiveKeyframe(index) 0207 keyframeContainer.activeIndex = index 0208 kfrModel.setSelectedKeyframe(index, mouse.modifiers & Qt.ControlModifier) 0209 } 0210 var ix = kfrModel.activeKeyframe() 0211 if (ix > -1) { 0212 seek(keyframes.itemAt(ix).frame + keyframeContainer.modelStart - keyframeContainer.inPoint) 0213 } 0214 } 0215 onReleased: { 0216 if (isNaN(newVal)) { 0217 return 0218 } 0219 root.autoScrolling = timeline.autoScroll 0220 var newPos = frame == keyframeContainer.inPoint ? keyframeContainer.inPoint : Math.round((keyframe.x + parent.x + root.baseUnit / 2) / timeScale) + keyframeContainer.inPoint 0221 if (newPos === frame && keyframe.value == keyframe.height - parent.y - root.baseUnit / 2) { 0222 var pos = keyframeContainer.modelStart + frame - keyframeContainer.inPoint 0223 if (proxy.position != pos) { 0224 seek(pos) 0225 } 0226 return 0227 } 0228 if (newVal > 1.5 || newVal < -0.5) { 0229 if (frame != keyframeContainer.inPoint) { 0230 keyframeContainer.resetSelection() 0231 timeline.removeEffectKeyframe(clipId, frame); 0232 } else { 0233 if (newVal < 0) { 0234 newVal = 0; 0235 } else if (newVal > 1) { 0236 newVal = 1; 0237 } 0238 timeline.updateEffectKeyframe(clipId, frame, frame, newVal) 0239 } 0240 } else { 0241 if (newVal < 0) { 0242 newVal = 0; 0243 } else if (newVal > 1) { 0244 newVal = 1; 0245 } 0246 if (model.moveOnly) { 0247 timeline.updateEffectKeyframe(clipId, frame, newPos) 0248 } else { 0249 timeline.updateEffectKeyframe(clipId, frame, frame == keyframeContainer.inPoint ? frame : newPos, newVal) 0250 } 0251 } 0252 } 0253 onPositionChanged: { 0254 shiftPressed = (mouse.modifiers & Qt.ShiftModifier) 0255 if (mouse.buttons === Qt.LeftButton) { 0256 if (frame == keyframeContainer.inPoint) { 0257 parent.x = - root.baseUnit / 2 0258 } else { 0259 var newPos = Math.min(Math.round((parent.x + root.baseUnit / 2) / timeScale), Math.round(keyframeContainer.width / timeScale) - frame + keyframeContainer.inPoint - 1) 0260 if (frame + newPos <= keyframeContainer.inPoint) { 0261 newPos = keyframeContainer.inPoint + 1 - frame 0262 } 0263 if (newPos != dragPos && (newPos == 0 || !timeline.hasKeyframeAt(clipId, frame + newPos))) { 0264 dragPos = newPos 0265 parent.x = newPos * timeScale - root.baseUnit / 2 0266 keyframecanvas.requestPaint() 0267 } else { 0268 parent.x = dragPos * timeScale - root.baseUnit / 2 0269 } 0270 } 0271 keyframecanvas.requestPaint() 0272 newVal = (keyframeContainer.height - (parent.y + mouse.y)) / keyframeContainer.height 0273 movingVal = kfrModel.realValue(Math.min(Math.max(newVal, 0), 1)) 0274 } 0275 } 0276 onDoubleClicked: { 0277 keyframeContainer.resetSelection() 0278 timeline.removeEffectKeyframe(clipId, frame); 0279 } 0280 onEntered: { 0281 timeline.showKeyBinding(i18n("<b>Shift drag</b> to change value of selected keyframes, <b>Ctrl click</b> for multiple keyframe selection.")) 0282 } 0283 onExited: { 0284 timeline.showKeyBinding() 0285 } 0286 ToolTip.visible: (containsMouse || pressed) && movingVal != "" 0287 ToolTip.text: movingVal 0288 } 0289 } 0290 } 0291 } 0292 } 0293 Canvas { 0294 id: keyframecanvas 0295 contextType: "2d" 0296 renderStrategy: Canvas.Threaded 0297 property int offset: scrollStart < 0 || parent.width <= scrollView.width ? 0 : scrollStart 0298 anchors.left: parent.left 0299 anchors.leftMargin: offset 0300 width: kfrCount > 0 ? Math.min(parent.width, scrollView.width) : 0 0301 height: kfrCount > 0 ? parent.height : 0 0302 opacity: keyframeContainer.selected ? 1 : 0.5 0303 Component { 0304 id: comp 0305 PathCurve { } 0306 } 0307 Component { 0308 id: compline 0309 PathLine { } 0310 } 0311 property var paths : [] 0312 Path { 0313 id: myPath 0314 startX: 0 0315 startY: parent.height 0316 } 0317 0318 onPaint: { 0319 if (kfrCount < 1) { 0320 return 0321 } 0322 var ctx = getContext("2d"); 0323 ctx.beginPath() 0324 ctx.fillStyle = Qt.rgba(0,0,0.8, 0.5); 0325 paths = [] 0326 var xpos 0327 var ypos 0328 for(var i = 0; i < keyframes.count; i++) 0329 { 0330 if (i + 1 < keyframes.count) { 0331 if (keyframes.itemAt(i + 1).tmpPos < offset) { 0332 continue; 0333 } 0334 } 0335 xpos = keyframes.itemAt(i).tmpPos - offset 0336 var type = i > 0 ? keyframes.itemAt(i-1).frameType : keyframes.itemAt(i).frameType 0337 switch (type) { 0338 case 0: 0339 // discrete 0340 paths.push(compline.createObject(keyframecanvas, {"x": xpos, "y": ypos} )) 0341 break; 0342 case 2: 0343 // curve 0344 ypos = keyframes.itemAt(i).tmpVal 0345 paths.push(comp.createObject(keyframecanvas, {"x": xpos, "y": ypos} )) 0346 break; 0347 default: 0348 // linear of others 0349 ypos = keyframes.itemAt(i).tmpVal 0350 paths.push(compline.createObject(keyframecanvas, {"x": xpos, "y": ypos} )) 0351 break; 0352 } 0353 if (xpos > scrollView.width) { 0354 break; 0355 } 0356 } 0357 paths.push(compline.createObject(keyframecanvas, {"x": keyframecanvas.width, "y": ypos} )) 0358 paths.push(compline.createObject(keyframecanvas, {"x": keyframecanvas.width, "y": keyframecanvas.height} )) 0359 myPath.pathElements = paths 0360 ctx.clearRect(0,0, width, height); 0361 ctx.path = myPath; 0362 ctx.closePath() 0363 ctx.fill() 0364 } 0365 } 0366 }