1/*
2 * Copyright (c) 2014-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 var defaultParameters: ['radius','blur_mix','highlight_cutoff']
25    property bool blockUpdate: true
26    property var startValues:  [0.0,  0.0, 0.1]
27    property var middleValues: [20.0, 1.0, 0.2]
28    property var endValues:    [0.0,  0.0, 0.1]
29    width: 350
30    height: 125
31    Component.onCompleted: {
32        if (filter.isNew) {
33            // Set default parameter values
34            filter.set('radius', 20.0)
35            filter.set('blur_mix', 1.0)
36            filter.set('highlight_cutoff', 0.2)
37            filter.savePreset(defaultParameters)
38        } else {
39            initSimpleAnimation()
40        }
41        setControls()
42    }
43
44    function initSimpleAnimation() {
45        middleValues = [filter.getDouble(defaultParameters[0], filter.animateIn),
46                        filter.getDouble(defaultParameters[1], filter.animateIn),
47                        filter.getDouble(defaultParameters[2], filter.animateIn)]
48        if (filter.animateIn > 0) {
49            startValues = [filter.getDouble(defaultParameters[0], 0),
50                           filter.getDouble(defaultParameters[1], 0),
51                           filter.getDouble(defaultParameters[2], 0)]
52        }
53        if (filter.animateOut > 0) {
54            endValues = [filter.getDouble(defaultParameters[0], filter.duration - 1),
55                         filter.getDouble(defaultParameters[1], filter.duration - 1),
56                         filter.getDouble(defaultParameters[2], filter.duration - 1)]
57        }
58    }
59
60    function getPosition() {
61        return Math.max(producer.position - (filter.in - producer.in), 0)
62    }
63
64    function setControls() {
65        var position = getPosition()
66        blockUpdate = true
67        radiusslider.value = filter.getDouble('radius', position)
68        blurslider.value = filter.getDouble('blur_mix', position)
69        cutoffslider.value = filter.getDouble('highlight_cutoff', position)
70        blockUpdate = false
71        radiusslider.enabled = blurslider.enabled = cutoffslider.enabled
72            = position <= 0 || (position >= (filter.animateIn - 1) && position <= (filter.duration - filter.animateOut)) || position >= (filter.duration - 1)
73    }
74
75    function updateFilter(parameter, value, position, button) {
76        if (blockUpdate) return
77        var index = defaultParameters.indexOf(parameter)
78
79        if (position !== null) {
80            if (position <= 0 && filter.animateIn > 0)
81                startValues[index] = value
82            else if (position >= filter.duration - 1 && filter.animateOut > 0)
83                endValues[index] = value
84            else
85                middleValues[index] = value
86        }
87
88        if (filter.animateIn > 0 || filter.animateOut > 0) {
89            filter.resetProperty(parameter)
90            button.checked = false
91            if (filter.animateIn > 0) {
92                filter.set(parameter, startValues[index], 0)
93                filter.set(parameter, middleValues[index], filter.animateIn - 1)
94            }
95            if (filter.animateOut > 0) {
96                filter.set(parameter, middleValues[index], filter.duration - filter.animateOut)
97                filter.set(parameter, endValues[index], filter.duration - 1)
98            }
99        } else if (!button.checked) {
100            filter.resetProperty(parameter)
101            filter.set(parameter, middleValues[index])
102        } else if (position !== null) {
103            filter.set(parameter, value, position)
104        }
105    }
106
107    function onKeyframesButtonClicked(checked, parameter, value) {
108        if (checked) {
109            blockUpdate = true
110            radiusslider.enabled = blurslider.enabled = cutoffslider.enabled = true
111            if (filter.animateIn > 0 || filter.animateOut > 0) {
112                filter.resetProperty('radius')
113                filter.resetProperty('blur_mix')
114                filter.resetProperty('highlight_cutoff')
115                filter.animateIn = filter.animateOut = 0
116            } else {
117                filter.clearSimpleAnimation(parameter)
118            }
119            blockUpdate = false
120            filter.set(parameter, value, getPosition())
121        } else {
122            filter.resetProperty(parameter)
123            filter.set(parameter, value)
124        }
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            Layout.columnSpan: 3
138            parameters: defaultParameters
139            onBeforePresetLoaded: {
140                filter.resetProperty('radius')
141                filter.resetProperty('blur_mix')
142                filter.resetProperty('highlight_cutoff')
143            }
144            onPresetSelected: {
145                setControls()
146                initSimpleAnimation()
147            }
148        }
149
150        // Row 1
151        Label {
152            text: qsTr('Radius')
153            Layout.alignment: Qt.AlignRight
154        }
155        Shotcut.SliderSpinner {
156            id: radiusslider
157            minimumValue: 0
158            maximumValue: 100
159            decimals: 1
160            onValueChanged: updateFilter('radius', value, getPosition(), radiusKeyframesButton)
161        }
162        Shotcut.UndoButton {
163            onClicked: radiusslider.value = 20
164        }
165        Shotcut.KeyframesButton {
166            id: radiusKeyframesButton
167            checked: filter.animateIn <= 0 && filter.animateOut <= 0 && filter.keyframeCount('radius') > 0
168            onToggled: onKeyframesButtonClicked(checked, 'radius', radiusslider.value)
169        }
170
171        // Row 2
172        Label {
173            text: qsTr('Highlight blurriness')
174            Layout.alignment: Qt.AlignRight
175        }
176        Shotcut.SliderSpinner {
177            id: blurslider
178            minimumValue: 0.0
179            maximumValue: 1.0
180            decimals: 2
181            onValueChanged: updateFilter('blur_mix', value, getPosition(), blurKeyframesButton)
182        }
183        Shotcut.UndoButton {
184            onClicked: blurslider.value = 1.0
185        }
186        Shotcut.KeyframesButton {
187            id: blurKeyframesButton
188            checked: filter.animateIn <= 0 && filter.animateOut <= 0 && filter.keyframeCount('blur_mix') > 0
189            onToggled: onKeyframesButtonClicked(checked, 'blur_mix', blurslider.value)
190        }
191
192        // Row 3
193        Label {
194            text: qsTr('Highlight cutoff')
195            Layout.alignment: Qt.AlignRight
196        }
197        Shotcut.SliderSpinner {
198            id: cutoffslider
199            minimumValue: 0.1
200            maximumValue: 1.0
201            decimals: 2
202            onValueChanged: updateFilter('highlight_cutoff', value, getPosition(), cutoffKeyframesButton)
203        }
204        Shotcut.UndoButton {
205            onClicked: cutoffslider.value = 0.2
206        }
207        Shotcut.KeyframesButton {
208            id: cutoffKeyframesButton
209            checked: filter.animateIn <= 0 && filter.animateOut <= 0 && filter.keyframeCount('highlight_cutoff') > 0
210            onToggled: onKeyframesButtonClicked(checked, 'highlight_cutoff', cutoffslider.value)
211        }
212
213        Item {
214            Layout.fillHeight: true
215        }
216    }
217
218    function updateSimpleAnimation() {
219        updateFilter('radius', radiusslider.value, getPosition(), radiusKeyframesButton)
220        updateFilter('blur_mix', blurslider.value, getPosition(), blurKeyframesButton)
221        updateFilter('highlight_cutoff', cutoffslider.value, getPosition(), cutoffKeyframesButton)
222    }
223
224    Connections {
225        target: filter
226        onInChanged: updateSimpleAnimation()
227        onOutChanged: updateSimpleAnimation()
228        onAnimateInChanged: updateSimpleAnimation()
229        onAnimateOutChanged: updateSimpleAnimation()
230    }
231
232    Connections {
233        target: producer
234        onPositionChanged: setControls()
235    }
236}
237