1/* 2 * Copyright (c) 2020-2021 Meltytech, LLC 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18import QtQuick 2.12 19import QtQuick.Controls 2.12 20import QtQuick.Layouts 1.12 21import Shotcut.Controls 1.0 as Shotcut 22 23Item { 24 property string rectProperty: 'rect' 25 property rect filterRect 26 property string startValue: '_shotcut:startValue' 27 property string middleValue: '_shotcut:middleValue' 28 property string endValue: '_shotcut:endValue' 29 30 width: 350 31 height: 180 32 33 Component.onCompleted: { 34 filter.blockSignals = true 35 var rect = defaultRect() 36 filter.set(middleValue, rect) 37 filter.set(startValue, rect) 38 filter.set(endValue, rect) 39 if (filter.isNew) { 40 // Add default preset. 41 filter.set(rectProperty, '' + rect.x + '/' + rect.y + ':' + rect.width + 'x' + rect.height) 42 filter.set("blur", 4) 43 filter.savePreset(preset.parameters) 44 } else { 45 filter.set(middleValue, filter.getRect(rectProperty, filter.animateIn + 1)) 46 if (filter.animateIn > 0) 47 filter.set(startValue, filter.getRect(rectProperty, 0)) 48 if (filter.animateOut > 0) 49 filter.set(endValue, filter.getRect(rectProperty, filter.duration - 1)) 50 } 51 filter.blockSignals = false 52 setControls() 53 setKeyframedControls() 54 if (filter.isNew) 55 filter.set(rectProperty, filter.getRect(rectProperty)) 56 } 57 58 function defaultRect() { 59 var result 60 if (producer.displayAspectRatio > profile.aspectRatio) { 61 result = Qt.rect(0, 0, profile.width, Math.round(profile.width / producer.displayAspectRatio)) 62 } else { 63 result = Qt.rect(0, 0, Math.round(profile.height * producer.displayAspectRatio), profile.height) 64 } 65 result.x = Math.round((profile.width - result.width) / 2) 66 result.y = Math.round((profile.height - result.height) / 2) 67 return result 68 } 69 70 function getPosition() { 71 return Math.max(producer.position - (filter.in - producer.in), 0) 72 } 73 74 function setFilter(position) { 75 if (position !== null) { 76 filter.blockSignals = true 77 if (position <= 0 && filter.animateIn > 0) 78 filter.set(startValue, filterRect) 79 else if (position >= filter.duration - 1 && filter.animateOut > 0) 80 filter.set(endValue, filterRect) 81 else 82 filter.set(middleValue, filterRect) 83 filter.blockSignals = false 84 } 85 86 if (filter.animateIn > 0 || filter.animateOut > 0) { 87 filter.resetProperty(rectProperty) 88 positionKeyframesButton.checked = false 89 if (filter.animateIn > 0) { 90 filter.set(rectProperty, filter.getRect(startValue), 1.0, 0) 91 filter.set(rectProperty, filter.getRect(middleValue), 1.0, filter.animateIn - 1) 92 } 93 if (filter.animateOut > 0) { 94 filter.set(rectProperty, filter.getRect(middleValue), 1.0, filter.duration - filter.animateOut) 95 filter.set(rectProperty, filter.getRect(endValue), 1.0, filter.duration - 1) 96 } 97 } else if (!positionKeyframesButton.checked) { 98 filter.resetProperty(rectProperty) 99 filter.set(rectProperty, filter.getRect(middleValue)) 100 } else if (position !== null) { 101 filter.set(rectProperty, filterRect, 1.0, position) 102 } 103 } 104 105 function setControls() { 106 amountSlider.value = filter.get('blur') 107 } 108 109 function setKeyframedControls() { 110 var position = getPosition() 111 var newValue = filter.getRect(rectProperty, position) 112 if (filterRect !== newValue) { 113 filterRect = newValue 114 rectX.text = filterRect.x.toFixed() 115 rectY.text = filterRect.y.toFixed() 116 rectW.text = filterRect.width.toFixed() 117 rectH.text = filterRect.height.toFixed() 118 } 119 var enabled = position <= 0 || (position >= (filter.animateIn - 1) && position <= (filter.duration - filter.animateOut)) || position >= (filter.duration - 1) 120 rectX.enabled = enabled 121 rectY.enabled = enabled 122 rectW.enabled = enabled 123 rectH.enabled = enabled 124 positionKeyframesButton.checked = filter.keyframeCount(rectProperty) > 0 && filter.animateIn <= 0 && filter.animateOut <= 0 125 } 126 127 GridLayout { 128 columns: 4 129 anchors.fill: parent 130 anchors.margins: 8 131 132 Label { 133 text: qsTr('Preset') 134 Layout.alignment: Qt.AlignRight 135 } 136 Shotcut.Preset { 137 id: preset 138 parameters: [rectProperty] 139 Layout.columnSpan: 3 140 onBeforePresetLoaded: { 141 filter.resetProperty(rectProperty) 142 } 143 onPresetSelected: { 144 setControls() 145 setKeyframedControls() 146 positionKeyframesButton.checked = filter.keyframeCount(rectProperty) > 0 && filter.animateIn <= 0 && filter.animateOut <= 0 147 filter.blockSignals = true 148 filter.set(middleValue, filter.getRect(rectProperty, filter.animateIn + 1)) 149 if (filter.animateIn > 0) 150 filter.set(startValue, filter.getRect(rectProperty, 0)) 151 if (filter.animateOut > 0) 152 filter.set(endValue, filter.getRect(rectProperty, filter.duration - 1)) 153 filter.blockSignals = false 154 } 155 } 156 157 Label { 158 text: qsTr('Position') 159 Layout.alignment: Qt.AlignRight 160 } 161 RowLayout { 162 TextField { 163 id: rectX 164 horizontalAlignment: Qt.AlignRight 165 selectByMouse: true 166 onEditingFinished: if (filterRect.x !== parseFloat(text)) { 167 filterRect.x = parseFloat(text) 168 setFilter(getPosition()) 169 } 170 } 171 Label { text: ',' } 172 TextField { 173 id: rectY 174 horizontalAlignment: Qt.AlignRight 175 selectByMouse: true 176 onEditingFinished: if (filterRect.y !== parseFloat(text)) { 177 filterRect.y = parseFloat(text) 178 setFilter(getPosition()) 179 } 180 } 181 } 182 Shotcut.UndoButton { 183 onClicked: { 184 var rect = defaultRect() 185 filterRect.x = rectX.text = rect.x 186 filterRect.y = rectY.text = rect.y 187 setFilter(getPosition()) 188 } 189 } 190 Shotcut.KeyframesButton { 191 id: positionKeyframesButton 192 Layout.rowSpan: 2 193 onToggled: { 194 if (checked) { 195 filter.clearSimpleAnimation(rectProperty) 196 filter.set(rectProperty, filterRect, 1.0, getPosition()) 197 } else { 198 filter.resetProperty(rectProperty) 199 filter.set(rectProperty, filterRect) 200 } 201 } 202 } 203 204 Label { 205 text: qsTr('Size') 206 Layout.alignment: Qt.AlignRight 207 } 208 RowLayout { 209 TextField { 210 id: rectW 211 horizontalAlignment: Qt.AlignRight 212 selectByMouse: true 213 onEditingFinished: if (filterRect.width !== parseFloat(text)) { 214 filterRect.width = parseFloat(text) 215 setFilter(getPosition()) 216 } 217 } 218 Label { text: 'x' } 219 TextField { 220 id: rectH 221 horizontalAlignment: Qt.AlignRight 222 selectByMouse: true 223 onEditingFinished: if (filterRect.height !== parseFloat(text)) { 224 filterRect.height = parseFloat(text) 225 setFilter(getPosition()) 226 } 227 } 228 } 229 Shotcut.UndoButton { 230 onClicked: { 231 var rect = defaultRect() 232 filterRect.width = rectW.text = rect.width 233 filterRect.height = rectH.text = rect.height 234 setFilter(getPosition()) 235 } 236 } 237 238 Label { 239 text: qsTr('Blur') 240 Layout.alignment: Qt.AlignRight 241 } 242 Shotcut.SliderSpinner { 243 id: amountSlider 244 minimumValue: 0 245 maximumValue: 10.0 246 stepSize: .1 247 decimals: 2 248 suffix: ' %' 249 onValueChanged: filter.set("blur", value) 250 } 251 Shotcut.UndoButton { 252 onClicked: amountSlider.value = 4 253 } 254 Item { width: 1 } 255 256 257 Item { Layout.fillHeight: true } 258 } 259 260 Connections { 261 target: filter 262 onChanged: setKeyframedControls() 263 onInChanged: setFilter(null) 264 onOutChanged: setFilter(null) 265 onAnimateInChanged: setFilter(null) 266 onAnimateOutChanged: setFilter(null) 267 } 268 269 Connections { 270 target: producer 271 onPositionChanged: setKeyframedControls() 272 } 273} 274