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