Changeset View
Changeset View
Standalone View
Standalone View
wallpapers/image/imagepackage/contents/ui/main.qml
Show All 13 Lines | |||||
14 | * GNU General Public License for more details. | 14 | * GNU General Public License for more details. | ||
15 | * | 15 | * | ||
16 | * You should have received a copy of the GNU General Public License | 16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | 17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. | 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. | ||
19 | */ | 19 | */ | ||
20 | 20 | | |||
21 | import QtQuick 2.5 | 21 | import QtQuick 2.5 | ||
22 | import QtQuick.Controls 2.1 as QQC2 | ||||
22 | import QtQuick.Window 2.2 | 23 | import QtQuick.Window 2.2 | ||
23 | import QtGraphicalEffects 1.0 | 24 | import QtGraphicalEffects 1.0 | ||
24 | import org.kde.plasma.wallpapers.image 2.0 as Wallpaper | 25 | import org.kde.plasma.wallpapers.image 2.0 as Wallpaper | ||
25 | import org.kde.plasma.core 2.0 as PlasmaCore | 26 | import org.kde.plasma.core 2.0 as PlasmaCore | ||
26 | 27 | | |||
27 | Item { | 28 | QQC2.StackView { | ||
28 | id: root | 29 | id: root | ||
29 | 30 | | |||
30 | readonly property string configuredImage: wallpaper.configuration.Image | | |||
31 | readonly property string modelImage: imageWallpaper.wallpaperPath | 31 | readonly property string modelImage: imageWallpaper.wallpaperPath | ||
32 | property Item currentImage: imageB | 32 | readonly property string configuredImage: wallpaper.configuration.Image | ||
33 | property Item currentBlurBackground: blurBackgroundB | | |||
34 | property Item otherImage: imageA | | |||
35 | property Item otherBlurBackground: blurBackgroundA | | |||
36 | readonly property int fillMode: wallpaper.configuration.FillMode | 33 | readonly property int fillMode: wallpaper.configuration.FillMode | ||
37 | property size sourceSize: Qt.size(root.width * Screen.devicePixelRatio, root.height * Screen.devicePixelRatio) | 34 | readonly property string configColor: wallpaper.configuration.Color | ||
35 | readonly property bool blur: wallpaper.configuration.Blur | ||||
36 | readonly property size sourceSize: Qt.size(root.width * Screen.devicePixelRatio, root.height * Screen.devicePixelRatio) | ||||
38 | 37 | | |||
39 | //public API, the C++ part will look for those | 38 | //public API, the C++ part will look for those | ||
40 | function setUrl(url) { | 39 | function setUrl(url) { | ||
41 | wallpaper.configuration.Image = url | 40 | wallpaper.configuration.Image = url | ||
42 | imageWallpaper.addUsersWallpaper(url); | 41 | imageWallpaper.addUsersWallpaper(url); | ||
43 | } | 42 | } | ||
44 | 43 | | |||
45 | function action_next() { | 44 | function action_next() { | ||
46 | imageWallpaper.nextSlide(); | 45 | imageWallpaper.nextSlide(); | ||
47 | } | 46 | } | ||
48 | 47 | | |||
49 | function action_open() { | 48 | function action_open() { | ||
50 | Qt.openUrlExternally(currentImage.source) | 49 | Qt.openUrlExternally(currentImage.source) | ||
51 | } | 50 | } | ||
52 | 51 | | |||
53 | //private | 52 | //private | ||
54 | function setupImage() { | | |||
55 | currentImage.sourceSize = root.sourceSize; | | |||
56 | currentImage.fillMode = root.fillMode; | | |||
57 | currentImage.source = modelImage; | | |||
58 | } | | |||
59 | | ||||
60 | function fadeWallpaper() { | | |||
61 | if (startupTimer.running) { | | |||
62 | setupImage(); | | |||
63 | return; | | |||
64 | } | | |||
65 | | ||||
66 | fadeAnim.running = false | | |||
67 | swapImages() | | |||
68 | currentImage.source = modelImage | | |||
69 | currentImage.sourceSize = root.sourceSize | | |||
70 | // Prevent source size change when image has just been setup anyway | | |||
71 | sourceSizeTimer.stop() | | |||
72 | currentImage.opacity = 0 | | |||
73 | currentBlurBackground.opacity = 0 | | |||
74 | otherImage.z = 0 | | |||
75 | currentImage.z = 1 | | |||
76 | | ||||
77 | // only cross-fade if the new image could be smaller than the old one | | |||
78 | fadeOtherAnimator.enabled = Qt.binding(function() { | | |||
79 | return currentImage.paintedWidth < otherImage.paintedWidth || currentImage.paintedHeight < otherImage.paintedHeight | | |||
80 | }) | | |||
81 | | ||||
82 | // Alleviate stuttering by waiting with the fade animation until the image is loaded (or failed to) | | |||
83 | fadeAnim.running = Qt.binding(function() { | | |||
84 | return currentImage.status !== Image.Loading && otherImage.status !== Image.Loading | | |||
85 | }) | | |||
86 | } | | |||
87 | | ||||
88 | function fadeFillMode() { | | |||
89 | if (startupTimer.running) { | | |||
90 | setupImage(); | | |||
91 | return; | | |||
92 | } | | |||
93 | | ||||
94 | fadeAnim.running = false | | |||
95 | swapImages() | | |||
96 | currentImage.sourceSize = root.sourceSize | | |||
97 | sourceSizeTimer.stop() | | |||
98 | currentImage.source = modelImage | | |||
99 | currentImage.opacity = 0 | | |||
100 | currentBlurBackground.opacity = 0 | | |||
101 | otherImage.z = 0 | | |||
102 | currentImage.fillMode = fillMode | | |||
103 | currentImage.z = 1 | | |||
104 | | ||||
105 | // only cross-fade if the new image could be smaller than the old one | | |||
106 | fadeOtherAnimator.enabled = Qt.binding(function() { | | |||
107 | return currentImage.paintedWidth < otherImage.paintedWidth || currentImage.paintedHeight < otherImage.paintedHeight | | |||
108 | }) | | |||
109 | | ||||
110 | fadeAnim.running = Qt.binding(function() { | | |||
111 | return currentImage.status !== Image.Loading && otherImage.status !== Image.Loading | | |||
112 | }) | | |||
113 | } | | |||
114 | | ||||
115 | function fadeSourceSize() { | | |||
116 | if (currentImage.sourceSize === root.sourceSize) { | | |||
117 | return | | |||
118 | } | | |||
119 | | ||||
120 | if (startupTimer.running) { | | |||
121 | setupImage(); | | |||
122 | return; | | |||
123 | } | | |||
124 | | ||||
125 | fadeAnim.running = false | | |||
126 | swapImages() | | |||
127 | currentImage.sourceSize = root.sourceSize | | |||
128 | currentImage.opacity = 0 | | |||
129 | currentBlurBackground.opacity = 0 | | |||
130 | currentImage.source = otherImage.source | | |||
131 | otherImage.z = 0 | | |||
132 | currentImage.z = 1 | | |||
133 | | ||||
134 | fadeOtherAnimator.enabled = false // the image size didn't change, avoid cross-dissolve | | |||
135 | fadeAnim.running = Qt.binding(function() { | | |||
136 | return currentImage.status !== Image.Loading && otherImage.status !== Image.Loading | | |||
137 | }) | | |||
138 | } | | |||
139 | | ||||
140 | function startFadeSourceTimer() { | | |||
141 | if (width > 0 && height > 0 && (imageA.status !== Image.Null || imageB.status !== Image.Null)) { | | |||
142 | sourceSizeTimer.restart() | | |||
143 | } | | |||
144 | } | | |||
145 | | ||||
146 | function swapImages() { | | |||
147 | if (currentImage == imageA) { | | |||
148 | currentImage = imageB | | |||
149 | currentBlurBackground = blurBackgroundB | | |||
150 | otherImage = imageA | | |||
151 | otherBlurBackground = blurBackgroundA | | |||
152 | } else { | | |||
153 | currentImage = imageA | | |||
154 | currentBlurBackground = blurBackgroundA | | |||
155 | otherImage = imageB | | |||
156 | otherBlurBackground = blurBackgroundB | | |||
157 | } | | |||
158 | } | | |||
159 | | ||||
160 | onWidthChanged: startFadeSourceTimer() | | |||
161 | onHeightChanged: startFadeSourceTimer() | | |||
162 | | ||||
163 | // HACK prevent fades and transitions during startup | | |||
164 | Timer { | | |||
165 | id: startupTimer | | |||
166 | interval: 100 | | |||
167 | running: true | | |||
168 | } | | |||
169 | | ||||
170 | Timer { | | |||
171 | id: sourceSizeTimer | | |||
172 | interval: 1000 // always delay reloading the image even when animations are turned off | | |||
173 | onTriggered: fadeSourceSize() | | |||
174 | } | | |||
175 | 53 | | |||
54 | onConfiguredImageChanged: { | ||||
55 | imageWallpaper.addUrl(configuredImage) | ||||
56 | } | ||||
176 | Component.onCompleted: { | 57 | Component.onCompleted: { | ||
177 | if (wallpaper.pluginName == "org.kde.slideshow") { | 58 | if (wallpaper.pluginName == "org.kde.slideshow") { | ||
178 | wallpaper.setAction("open", i18n("Open Wallpaper Image"), "document-open"); | 59 | wallpaper.setAction("open", i18n("Open Wallpaper Image"), "document-open"); | ||
179 | wallpaper.setAction("next", i18n("Next Wallpaper Image"), "user-desktop"); | 60 | wallpaper.setAction("next", i18n("Next Wallpaper Image"), "user-desktop"); | ||
180 | } | 61 | } | ||
181 | } | 62 | } | ||
mart: are you sure none of the *changed signals will be triggered i nthe beginning? this could call… | |||||
182 | 63 | | |||
183 | Wallpaper.Image { | 64 | Wallpaper.Image { | ||
184 | id: imageWallpaper | 65 | id: imageWallpaper | ||
185 | //the oneliner of difference between image and slideshow wallpapers | 66 | //the oneliner of difference between image and slideshow wallpapers | ||
186 | renderingMode: (wallpaper.pluginName == "org.kde.image") ? Wallpaper.Image.SingleImage : Wallpaper.Image.SlideShow | 67 | renderingMode: (wallpaper.pluginName == "org.kde.image") ? Wallpaper.Image.SingleImage : Wallpaper.Image.SlideShow | ||
187 | targetSize: Qt.size(root.width, root.height) | 68 | targetSize: Qt.size(root.width, root.height) | ||
188 | slidePaths: wallpaper.configuration.SlidePaths | 69 | slidePaths: wallpaper.configuration.SlidePaths | ||
189 | slideTimer: wallpaper.configuration.SlideInterval | 70 | slideTimer: wallpaper.configuration.SlideInterval | ||
190 | } | 71 | } | ||
191 | 72 | | |||
192 | onFillModeChanged: { | 73 | onFillModeChanged: Qt.callLater(loadImage); | ||
193 | fadeFillMode(); | 74 | onModelImageChanged: Qt.callLater(loadImage); | ||
75 | onConfigColorChanged: Qt.callLater(loadImage); | ||||
76 | onBlurChanged: Qt.callLater(loadImage); | ||||
77 | onWidthChanged: Qt.callLater(loadImage); | ||||
78 | onHeightChanged: Qt.callLater(loadImage); | ||||
79 | | ||||
80 | function loadImage() { | ||||
81 | var isFirst = (root.currentItem == undefined); | ||||
82 | var pendingImage = baseImage.createObject(root, { "source": root.modelImage, | ||||
83 | "fillMode": root.fillMode, | ||||
84 | "sourceSize": root.sourceSize, | ||||
85 | "color": root.configColor, | ||||
86 | "blur": root.blur, | ||||
87 | "opacity": isFirst ? 1: 0}); | ||||
88 | | ||||
89 | function replaceWhenLoaded() { | ||||
90 | if (pendingImage.status != Image.Loading) { | ||||
91 | root.replace(pendingImage, {}, | ||||
92 | isFirst ? QQC2.StackView.Immediate : QQC2.StackView.Transition);//dont' animate first show | ||||
93 | pendingImage.statusChanged.disconnect(replaceWhenLoaded); | ||||
194 | } | 94 | } | ||
195 | onConfiguredImageChanged: { | | |||
196 | imageWallpaper.addUrl(configuredImage) | | |||
197 | } | 95 | } | ||
198 | onModelImageChanged: { | 96 | pendingImage.statusChanged.connect(replaceWhenLoaded); | ||
199 | fadeWallpaper(); | 97 | replaceWhenLoaded(); | ||
200 | } | 98 | } | ||
201 | 99 | | |||
202 | SequentialAnimation { | 100 | Component { | ||
203 | id: fadeAnim | 101 | id: baseImage | ||
204 | running: false | | |||
205 | 102 | | |||
206 | ParallelAnimation { | 103 | Image { | ||
207 | OpacityAnimator { | 104 | id: mainImage | ||
208 | target: currentBlurBackground | | |||
209 | from: 0 | | |||
210 | to: 1 | | |||
211 | duration: fadeOtherAnimator.duration | | |||
212 | } | | |||
213 | OpacityAnimator { | | |||
214 | target: otherBlurBackground | | |||
215 | from: 1 | | |||
216 | // cannot disable an animation individually, so we just fade from 1 to 1 | | |||
217 | to: enabled ? 0 : 1 | | |||
218 | | ||||
219 | //use configured duration if animations are enabled | | |||
220 | duration: units.longDuration && wallpaper.configuration.TransitionAnimationDuration | | |||
221 | } | | |||
222 | OpacityAnimator { | | |||
223 | target: currentImage | | |||
224 | from: 0 | | |||
225 | to: 1 | | |||
226 | duration: fadeOtherAnimator.duration | | |||
227 | } | | |||
228 | OpacityAnimator { | | |||
229 | id: fadeOtherAnimator | | |||
230 | property bool enabled: true | | |||
231 | target: otherImage | | |||
232 | from: 1 | | |||
233 | // cannot disable an animation individually, so we just fade from 1 to 1 | | |||
234 | to: enabled ? 0 : 1 | | |||
235 | | ||||
236 | //use configured duration if animations are enabled | | |||
237 | duration: units.longDuration && wallpaper.configuration.TransitionAnimationDuration | | |||
238 | } | | |||
239 | } | | |||
240 | ScriptAction { | | |||
241 | script: { | | |||
242 | otherImage.source = ""; | | |||
243 | otherImage.fillMode = fillMode; | | |||
244 | } | | |||
245 | } | | |||
246 | } | | |||
247 | 105 | | |||
248 | Rectangle { | 106 | property alias color: backgroundColor.color | ||
249 | id: backgroundColor | 107 | property alias blur: blurEffect.visible | ||
250 | anchors.fill: parent | | |||
251 | visible: currentImage.status === Image.Ready || otherImage.status === Image.Ready | | |||
252 | color: wallpaper.configuration.Color | | |||
253 | Behavior on color { | | |||
254 | ColorAnimation { duration: units.longDuration } | | |||
255 | enabled: !startupTimer.running | | |||
256 | } | | |||
257 | } | | |||
258 | 108 | | |||
259 | Image { | | |||
260 | id: blurBackgroundSourceA | | |||
261 | visible: wallpaper.configuration.Blur | | |||
262 | anchors.fill: parent | | |||
263 | asynchronous: true | 109 | asynchronous: true | ||
264 | cache: false | 110 | cache: false | ||
265 | fillMode: Image.PreserveAspectCrop | 111 | autoTransform: true | ||
266 | source: imageA.source | | |||
267 | z: -1 | 112 | z: -1 | ||
268 | } | | |||
269 | 113 | | |||
270 | GaussianBlur { | 114 | QQC2.StackView.onRemoved: destroy() | ||
271 | id: blurBackgroundA | | |||
272 | visible: wallpaper.configuration.Blur | | |||
273 | anchors.fill: parent | | |||
274 | source: blurBackgroundSourceA | | |||
275 | radius: 32 | | |||
276 | samples: 65 | | |||
277 | z: imageA.z | | |||
278 | } | | |||
279 | 115 | | |||
280 | Image { | 116 | Rectangle { | ||
281 | id: blurBackgroundSourceB | 117 | id: backgroundColor | ||
282 | visible: wallpaper.configuration.Blur | | |||
283 | anchors.fill: parent | 118 | anchors.fill: parent | ||
284 | asynchronous: true | 119 | visible: mainImage.status === Image.Ready | ||
285 | cache: false | 120 | z: -2 | ||
286 | fillMode: Image.PreserveAspectCrop | | |||
287 | source: imageB.source | | |||
288 | z: -1 | | |||
289 | } | 121 | } | ||
290 | 122 | | |||
291 | GaussianBlur { | 123 | GaussianBlur { | ||
292 | id: blurBackgroundB | 124 | id: blurEffect | ||
the result will be the same with a 0 interval, but would be more correct-correct using loadTimer.restart() instead? mart: the result will be the same with a 0 interval, but would be more correct-correct using… | |||||
293 | visible: wallpaper.configuration.Blur | | |||
294 | anchors.fill: parent | 125 | anchors.fill: parent | ||
295 | source: blurBackgroundSourceB | 126 | source: mainImage | ||
296 | radius: 32 | 127 | radius: 32 | ||
297 | samples: 65 | 128 | samples: 65 | ||
298 | z: imageB.z | 129 | z: mainImage.z | ||
130 | } | ||||
299 | } | 131 | } | ||
300 | | ||||
301 | Image { | | |||
302 | id: imageA | | |||
303 | anchors.fill: parent | | |||
304 | asynchronous: true | | |||
305 | cache: false | | |||
306 | fillMode: wallpaper.configuration.FillMode | | |||
307 | autoTransform: true //new API in Qt 5.5, do not backport into Plasma 5.4. | | |||
308 | } | 132 | } | ||
309 | 133 | | |||
310 | Image { | 134 | replaceEnter: Transition { | ||
311 | id: imageB | 135 | OpacityAnimator { | ||
312 | anchors.fill: parent | 136 | from: 0 | ||
313 | asynchronous: true | 137 | to: 1 | ||
314 | cache: false | 138 | duration: wallpaper.configuration.TransitionAnimationDuration | ||
315 | fillMode: wallpaper.configuration.FillMode | 139 | } | ||
316 | autoTransform: true //new API in Qt 5.5, do not backport into Plasma 5.4. | 140 | } | ||
141 | // Keep the old image around till the new one is fully faded in | ||||
142 | // If we fade both at the same time you can see the background behind glimpse through | ||||
143 | replaceExit: Transition{ | ||||
144 | PauseAnimation { | ||||
145 | duration: wallpaper.configuration.TransitionAnimationDuration | ||||
146 | } | ||||
317 | } | 147 | } | ||
318 | } | 148 | } | ||
if the new image is fading in, i don't think the old image should be fading out, as at some point of the anmiation both items would be semitransparent, in that case the black on the back of everything would be creeping in for a while, so if you transition between 2 light wallpaper, you would see a transition to a darker result, then going lighter again replaceExit: Transition { PauseAnimation { duration: wallpaper.configuration.TransitionAnimationDuration } } seems to have a more pleasing effect mart: if the new image is fading in, i don't think the old image should be fading out, as at some… |
are you sure none of the *changed signals will be triggered i nthe beginning? this could call loadImage twice