on xenial help sign in
Use the default system wallpaper instead of our custom one.
commit20b30a4db406a8d2c7253d67c0e4818fbf6a6e79 authorMichael Terry <michael.terry@canonical.com> dateTue 30 Aug 2016, 10:06 (10 years ago) parent80ab351
35 files changed+799−120
debian/control+1 debian/unity8-private.install+1 plugins/CMakeLists.txt+1 plugins/ImageCache/CMakeLists.txt+8 plugins/ImageCache/ImageCache.cpp+136 plugins/ImageCache/ImageCache.h+53 plugins/ImageCache/plugin.cpp+39 plugins/ImageCache/plugin.h+31 plugins/ImageCache/qmldir+2 qml/Components/Lockscreen.qml+1−5 qml/Components/Wallpaper.qml+44 qml/Components/WallpaperResolver.qml+24−38 qml/Dash/DashBackground.qml+2−2 qml/Greeter/CoverPage.qml+5−5 qml/Greeter/Greeter.qml+7 qml/Greeter/NarrowView.qml+4−6 qml/Greeter/WideView.qml+1 qml/Shell.qml+27−4 qml/Stages/DesktopStage.qml+1−3 tests/plugins/CMakeLists.txt+1 tests/plugins/ImageCache/CMakeLists.txt+20 tests/plugins/ImageCache/test.cpp+214 tests/plugins/ImageCache/test.qml+23 tests/qmltests/Components/tst_Lockscreen.qml+1−1 tests/qmltests/Components/tst_WallpaperResolver.qml+21−35 tests/qmltests/Components/tst_ZoomableImage.qml+1−1 tests/qmltests/Dash/Previews/tst_PreviewExpandable.qml+3−3 tests/qmltests/Dash/Previews/tst_PreviewImageGallery.qml+6−6 tests/qmltests/Dash/Previews/tst_PreviewZoomableImage.qml+7−7 tests/qmltests/Greeter/TestView.qml+1 tests/qmltests/Greeter/tst_Greeter.qml+7−1 tests/qmltests/Greeter/tst_NarrowView.qml+13−1 tests/qmltests/Greeter/tst_WideView.qml+12−1 tests/qmltests/Stages/tst_DesktopStage.qml+1−1 tests/qmltests/tst_Shell.qml+80
debian/control+1
@@ -154,6 +154,7 @@ Depends: qml-module-qtquick-layouts,
154154 qtdeclarative5-ubuntu-settings-components (>= 0.7),
155155 qtdeclarative5-unity-notifications-plugin (>= 0.1.2) | unity-notifications-impl,
156156 ubuntu-thumbnailer-impl-0,
157+ ubuntu-wallpapers,
157158 unity-application-impl-20,
158159 unity-notifications-impl-3,
159160 unity-plugin-scopes | unity-scopes-impl,
debian/unity8-private.install+1
@@ -4,6 +4,7 @@ usr/lib/*/unity8/qml/Cursor
44usr/lib/*/unity8/qml/Dash
55usr/lib/*/unity8/qml/GlobalShortcut
66usr/lib/*/unity8/qml/Greeter
7+usr/lib/*/unity8/qml/ImageCache
78usr/lib/*/unity8/qml/LightDM
89usr/lib/*/unity8/qml/Lights
910usr/lib/*/unity8/qml/Powerd
plugins/CMakeLists.txt+1
@@ -15,6 +15,7 @@ add_subdirectory(AccountsService)
1515add_subdirectory(Cursor)
1616add_subdirectory(GlobalShortcut)
1717add_subdirectory(Greeter)
18+add_subdirectory(ImageCache)
1819add_subdirectory(LightDM)
1920add_subdirectory(Lights)
2021add_subdirectory(Dash)
plugins/ImageCache/CMakeLists.txtnew file+8
@@ -0,0 +1,8 @@
1+add_library(ImageCache-qml MODULE
2+ ImageCache.cpp
3+ plugin.cpp
4+ )
5+
6+qt5_use_modules(ImageCache-qml Gui Qml Quick)
7+
8+add_unity8_plugin(ImageCache 0.1 ImageCache TARGETS ImageCache-qml)
plugins/ImageCache/ImageCache.cppnew file+136
@@ -0,0 +1,136 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
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; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include <QDateTime>
18+#include <QDebug>
19+#include <QDir>
20+#include <QImageReader>
21+#include <QUrlQuery>
22+
23+#include "ImageCache.h"
24+
25+ImageCache::ImageCache()
26+ : QQuickImageProvider(QQmlImageProviderBase::Image,
27+ QQmlImageProviderBase::ForceAsynchronousImageLoading)
28+{
29+}
30+
31+QString ImageCache::imageCacheRoot()
32+{
33+ QString xdgCache(qgetenv("XDG_CACHE_HOME"));
34+ if (xdgCache.isEmpty()) {
35+ xdgCache = QDir::homePath() + QStringLiteral("/.cache");
36+ }
37+
38+ return QDir::cleanPath(xdgCache) + QStringLiteral("/unity8/imagecache");
39+}
40+
41+QFileInfo ImageCache::imagePath(const QUrl &image)
42+{
43+ QUrlQuery query(image);
44+
45+ auto name = query.queryItemValue(QStringLiteral("name"));
46+ if (name.isEmpty()) {
47+ name = QStringLiteral("/paths") + image.toLocalFile();
48+ } else {
49+ name = QStringLiteral("/names/") + name;
50+ }
51+
52+ return QFileInfo(imageCacheRoot() + name);
53+}
54+
55+bool ImageCache::needsUpdate(const QUrl &image, const QFileInfo &cachePath, const QSize &requestedSize, QSize &finalSize)
56+{
57+ if (!cachePath.exists())
58+ return true;
59+
60+ QFileInfo imageInfo(image.toLocalFile());
61+ if (imageInfo.lastModified() > cachePath.lastModified())
62+ return true;
63+
64+ QSize cacheSize(QImageReader(cachePath.filePath()).size());
65+ QSize imageSize(QImageReader(imageInfo.filePath()).size());
66+ finalSize = calculateSize(imageSize, requestedSize);
67+ if (finalSize.isValid() && cacheSize != finalSize)
68+ return true;
69+
70+ return false;
71+}
72+
73+QSize ImageCache::calculateSize(const QSize &imageSize, const QSize &requestedSize)
74+{
75+ QSize finalSize(requestedSize);
76+
77+ if (finalSize.width() == 0) {
78+ finalSize.setWidth(imageSize.width() * (((double)finalSize.height()) / imageSize.height()));
79+ } else if (finalSize.height() == 0) {
80+ finalSize.setHeight(imageSize.height() * (((double)finalSize.width()) / imageSize.width()));
81+ }
82+
83+ return finalSize;
84+}
85+
86+QImage ImageCache::loadAndCacheImage(const QUrl &image, const QFileInfo &cachePath, const QSize &finalSize)
87+{
88+ QImageReader reader(image.toLocalFile());
89+ reader.setQuality(100);
90+ reader.setScaledSize(finalSize);
91+ auto format = reader.format(); // can't get this after reading
92+
93+ QImage loadedImage(reader.read());
94+ if (loadedImage.isNull()) {
95+ qWarning() << "ImageCache could not read image" << image.path();
96+ return QImage();
97+ }
98+
99+ cachePath.dir().mkpath(QStringLiteral("."));
100+ loadedImage.save(cachePath.filePath(), format, 100);
101+
102+ return loadedImage;
103+}
104+
105+QImage ImageCache::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
106+{
107+ QUrl image(id);
108+ QSize imageSize(QImageReader(image.toLocalFile()).size());
109+ QImage result;
110+
111+ // Early exit here, with no sourceSize, scaled-up sourceSize, or bad source image
112+ if ((requestedSize.width() <= 0 && requestedSize.height() <= 0) ||
113+ imageSize.isEmpty() ||
114+ requestedSize.height() >= imageSize.height() ||
115+ requestedSize.width() >= imageSize.width()) {
116+ // We're only interested in scaling down, not up.
117+ result = QImage(image.toLocalFile());
118+ *size = result.size();
119+ return result;
120+ }
121+
122+ auto cachePath = imagePath(image);
123+ QSize finalSize;
124+
125+ if (needsUpdate(image, cachePath, requestedSize, finalSize)) {
126+ if (finalSize.isEmpty()) {
127+ finalSize = calculateSize(imageSize, requestedSize);
128+ }
129+ result = loadAndCacheImage(image, cachePath, finalSize);
130+ } else {
131+ result = QImage(cachePath.filePath());
132+ }
133+
134+ *size = result.size();
135+ return result;
136+}
plugins/ImageCache/ImageCache.hnew file+53
@@ -0,0 +1,53 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
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; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#pragma once
18+
19+#include <QFileInfo>
20+#include <QQuickImageProvider>
21+#include <QSize>
22+#include <QUrl>
23+
24+/**
25+ * This class accepts an id formulated like a URL. So you'd use something like:
26+ *
27+ * Image {
28+ * source: "image://unity8imagecache/file:///usr/share/..."
29+ * }
30+ *
31+ * Right now, we only support file:/// URLs. We do accept some flags though:
32+ *
33+ * ?name=NAME
34+ * - This will use NAME as the cache lookup key instead of the provided URL.
35+ *
36+ * We don't do any cleaning of old cached files yet. So you may want to always
37+ * provide a name to avoid leaving lots of files around.
38+ */
39+
40+class ImageCache: public QQuickImageProvider
41+{
42+public:
43+ ImageCache();
44+
45+ QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) override;
46+
47+private:
48+ static QString imageCacheRoot();
49+ static QFileInfo imagePath(const QUrl &image);
50+ static bool needsUpdate(const QUrl &image, const QFileInfo &cachePath, const QSize &requestedSize, QSize &finalSize);
51+ static QSize calculateSize(const QSize &imageSize, const QSize &requestedSize);
52+ static QImage loadAndCacheImage(const QUrl &image, const QFileInfo &cachePath, const QSize &finalSize);
53+};
plugins/ImageCache/plugin.cppnew file+39
@@ -0,0 +1,39 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
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; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include <QQmlEngine>
18+
19+#include "ImageCache.h"
20+#include "plugin.h"
21+
22+#include <QtQml>
23+
24+void ImageCachePlugin::registerTypes(const char *uri)
25+{
26+ Q_ASSERT(uri == QLatin1String("ImageCache"));
27+
28+ qmlRegisterTypeNotAvailable(uri, 0, 1, "__ImageCacheIgnoreMe",
29+ QStringLiteral("Ignore this: QML plugins must contain at least one type"));
30+}
31+
32+void ImageCachePlugin::initializeEngine(QQmlEngine* engine, const char* uri)
33+{
34+ Q_ASSERT(uri == QLatin1String("ImageCache"));
35+
36+ QQmlExtensionPlugin::initializeEngine(engine, uri);
37+
38+ engine->addImageProvider("unity8imagecache", new ImageCache);
39+}
plugins/ImageCache/plugin.hnew file+31
@@ -0,0 +1,31 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
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; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#pragma once
18+
19+#include <QQmlExtensionPlugin>
20+
21+class QQmlEngine;
22+
23+class ImageCachePlugin : public QQmlExtensionPlugin
24+{
25+ Q_OBJECT
26+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
27+
28+public:
29+ void registerTypes(const char* uri) override;
30+ void initializeEngine(QQmlEngine* engine, const char* uri) override;
31+};
plugins/ImageCache/qmldirnew file+2
@@ -0,0 +1,2 @@
1+module ImageCache
2+plugin ImageCache-qml
qml/Components/Lockscreen.qml+1−5
@@ -96,17 +96,13 @@ Showable {
9696 visible: root.background.toString() !== ""
9797 }
9898
99− Image {
99+ Wallpaper {
100100 id: backgroundImage
101101 objectName: "lockscreenBackground"
102102 anchors {
103103 fill: parent
104104 }
105− // Limit how much memory we'll reserve for this image
106− sourceSize.height: height
107− sourceSize.width: width
108105 source: root.required ? root.background : ""
109− fillMode: Image.PreserveAspectCrop
110106 }
111107
112108 // This is to
qml/Components/Wallpaper.qmlnew file+44
@@ -0,0 +1,44 @@
1+/*
2+ * Copyright (C) 2013-2016 Canonical, Ltd.
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; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+import QtQuick.Window 2.2
19+import Ubuntu.Components 1.3
20+
21+Item {
22+ id: root
23+ property url source
24+
25+ CrossFadeImage {
26+ anchors.fill: parent
27+ fillMode: Image.PreserveAspectCrop
28+
29+ // Limit how much memory we reserve and avoid reloading when item size
30+ // changes or is rotated by specifying sourceSize.
31+ //
32+ // FIXME: If the source image has a portrait aspect ratio, we should swap
33+ // sourceSize.width and sourceSize.height to prevent blurriness from double
34+ // scaling. We could easily do that with a tiny image loader to check
35+ // the aspect ratio first, but when we change sourceSize, we lose all
36+ // the benefits of CrossFadeImage. So we need to fix that component
37+ // first to gracefully handle sourceSize changes (LP: #1599203).
38+ readonly property int maxSize: Math.max(Screen.width, Screen.height)
39+ sourceSize.width: 0
40+ sourceSize.height: maxSize
41+
42+ source: root.source
43+ }
44+}
qml/Components/WallpaperResolver.qml+24−38
@@ -15,49 +15,35 @@
1515 */
1616
1717import QtQuick 2.4
18−import AccountsService 0.1
19−import GSettings 1.0
2018import Ubuntu.Components 1.3
2119
22−/*
23− Defines the background URL based on several factors, such as:
24− - default, fallback, background
25− - Background set in AccountSettings, if any
26− - Background set in GSettings, if any
27− */
28−QtObject {
29− // Users should set their UI width here.
30− property real width
31−
32− property url defaultBackground: Qt.resolvedUrl(width >= units.gu(60) ? "../graphics/tablet_background.jpg"
33− : "../graphics/phone_background.jpg")
20+Item {
21+ id: root
3422
35− // That's the property users of this component are going to consume.
36− readonly property url background: asImageTester.status == Image.Ready ? asImageTester.source
37− : gsImageTester.status == Image.Ready ? gsImageTester.source : defaultBackground
38−
39− // This is a dummy image to detect if the custom AS set wallpaper loads successfully.
40− property var _asImageTester: Image {
41− id: asImageTester
42− source: AccountsService.backgroundFile != undefined && AccountsService.backgroundFile.length > 0 ? AccountsService.backgroundFile : ""
43− height: 0
44− width: 0
45− sourceSize.height: 0
46− sourceSize.width: 0
47− }
23+ // Provide a list of wallpapers to resolve here, preferred ones first
24+ property var candidates: []
4825
49− // This is a dummy image to detect if the custom GSettings set wallpaper loads successfully.
50− property var _gsImageTester: Image {
51− id: gsImageTester
52− source: backgroundSettings.pictureUri && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : ""
53− height: 0
54− width: 0
55− sourceSize.height: 0
56− sourceSize.width: 0
26+ readonly property url background: {
27+ for (var i = 0; i < repeater.count; i++) {
28+ if (repeater.itemAt(i).status === Image.Ready)
29+ return candidates[i];
30+ }
31+ if (i > 0) {
32+ return candidates[i - 1]; // last item is last resort
33+ } else {
34+ return "";
35+ }
5736 }
5837
59− property var _gsettings: GSettings {
60− id: backgroundSettings
61− schema.id: "org.gnome.desktop.background"
38+ Repeater {
39+ id: repeater
40+ model: root.candidates
41+ delegate: Image {
42+ source: modelData
43+ height: 0
44+ width: 0
45+ sourceSize.height: 0
46+ sourceSize.width: 0
47+ }
6248 }
6349}
qml/Dash/DashBackground.qml+2−2
@@ -16,10 +16,10 @@
1616
1717import QtQuick 2.4
1818import QtQuick.Window 2.2
19−import Ubuntu.Thumbnailer 0.1
19+import ImageCache 0.1
2020
2121Image {
22− source: "image://thumbnailer/" + Qt.resolvedUrl("graphics/paper.png")
22+ source: "image://unity8imagecache/" + Qt.resolvedUrl("graphics/paper.png")
2323 fillMode: Image.PreserveAspectCrop
2424 horizontalAlignment: Image.AlignRight
2525 verticalAlignment: Image.AlignTop
qml/Greeter/CoverPage.qml+5−5
@@ -25,6 +25,7 @@ Showable {
2525 property real dragHandleLeftMargin
2626 property real launcherOffset
2727 property alias background: greeterBackground.source
28+ property alias hasCustomBackground: backgroundShade.visible
2829 property real backgroundTopMargin
2930 property var infographicModel
3031 property bool draggable: true
@@ -82,24 +83,23 @@ Showable {
8283 color: "black"
8384 }
8485
85− CrossFadeImage {
86+ Wallpaper {
8687 id: greeterBackground
8788 objectName: "greeterBackground"
8889 anchors {
8990 fill: parent
9091 topMargin: root.backgroundTopMargin
9192 }
92− fillMode: Image.PreserveAspectCrop
93− // Limit how much memory we'll reserve for this image
94− sourceSize.height: height
95− sourceSize.width: width
9693 }
9794
9895 // Darkens wallpaper so that we can read text on it and see infographic
9996 Rectangle {
97+ id: backgroundShade
98+ objectName: "backgroundShade"
10099 anchors.fill: parent
101100 color: "black"
102101 opacity: 0.4
102+ visible: false
103103 }
104104
105105 Infographics {
qml/Greeter/Greeter.qml+7
@@ -34,6 +34,7 @@ Showable {
3434 property real dragHandleLeftMargin: 0
3535
3636 property url background
37+ property bool hasCustomBackground
3738
3839 // How far to offset the top greeter layer during a launcher left-drag
3940 property real launcherOffset
@@ -435,6 +436,12 @@ Showable {
435436 value: root.background
436437 }
437438
439+ Binding {
440+ target: loader.item
441+ property: "hasCustomBackground"
442+ value: root.hasCustomBackground
443+ }
444+
438445 Binding {
439446 target: loader.item
440447 property: "locked"
qml/Greeter/NarrowView.qml+4−6
@@ -29,6 +29,7 @@ FocusScope {
2929 property alias delayMinutes: delayedLockscreen.delayMinutes
3030 property alias backgroundTopMargin: coverPage.backgroundTopMargin
3131 property url background
32+ property bool hasCustomBackground
3233 property bool locked
3334 property alias alphanumeric: loginList.alphanumeric
3435 property alias userModel: loginList.model
@@ -123,7 +124,7 @@ FocusScope {
123124 showAnimation: StandardAnimation { property: "opacity"; to: 1 }
124125 hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
125126
126− CrossFadeImage {
127+ Wallpaper {
127128 id: lockscreenBackground
128129 objectName: "lockscreenBackground"
129130 anchors {
@@ -131,17 +132,13 @@ FocusScope {
131132 topMargin: root.backgroundTopMargin
132133 }
133134 source: root.background
134− fillMode: Image.PreserveAspectCrop
135− // Limit how much memory we'll reserve for this image
136− sourceSize.height: Math.max(Screen.width, Screen.height)
137− sourceSize.width: 0
138135 }
139136
140137 // Darken background to match CoverPage
141138 Rectangle {
142139 anchors.fill: parent
143140 color: "black"
144− opacity: 0.4
141+ opacity: root.hasCustomBackground ? 0.4 : 0
145142 }
146143
147144 LoginList {
@@ -191,6 +188,7 @@ FocusScope {
191188 height: parent.height
192189 width: parent.width
193190 background: root.background
191+ hasCustomBackground: root.hasCustomBackground
194192 draggable: !root.waiting
195193 onTease: root.tease()
196194 onClicked: hide()
qml/Greeter/WideView.qml+1
@@ -24,6 +24,7 @@ FocusScope {
2424
2525 property alias background: coverPage.background
2626 property alias backgroundTopMargin: coverPage.backgroundTopMargin
27+ property alias hasCustomBackground: coverPage.hasCustomBackground
2728 property alias dragHandleLeftMargin: coverPage.dragHandleLeftMargin
2829 property alias infographicModel: coverPage.infographicModel
2930 property alias launcherOffset: coverPage.launcherOffset
qml/Shell.qml+27−4
@@ -26,6 +26,7 @@ import Unity.Connectivity 0.1
2626import Unity.Launcher 0.1
2727import GlobalShortcut 1.0 // has to be before Utils, because of WindowInputFilter
2828import GSettings 1.0
29+import ImageCache 0.1
2930import Utils 0.1
3031import Powerd 0.1
3132import SessionBroadcast 0.1
@@ -145,7 +146,28 @@ StyledItem {
145146
146147 WallpaperResolver {
147148 id: wallpaperResolver
148− width: shell.width
149+ objectName: "wallpaperResolver"
150+
151+ readonly property url defaultBackground: "file:///usr/share/backgrounds/warty-final-ubuntu.png"
152+ readonly property bool hasCustomBackground: background != defaultBackground
153+
154+ // Use a cached version of the scaled-down wallpaper (as sometimes the
155+ // image can be quite big compared to the device size, including for
156+ // our default wallpaper). We use a name=wallpaper argument here to
157+ // make sure we don't litter our cache with lots of scaled images. We
158+ // only need to bother caching one at a time.
159+ readonly property url cachedBackground: "image://unity8imagecache/" + background + "?name=wallpaper"
160+
161+ GSettings {
162+ id: backgroundSettings
163+ schema.id: "org.gnome.desktop.background"
164+ }
165+
166+ candidates: [
167+ AccountsService.backgroundFile,
168+ backgroundSettings.pictureUri,
169+ defaultBackground
170+ ]
149171 }
150172
151173 readonly property alias greeter: greeterLoader.item
@@ -353,7 +375,7 @@ StyledItem {
353375 Binding {
354376 target: applicationsDisplayLoader.item
355377 property: "background"
356− value: wallpaperResolver.background
378+ value: wallpaperResolver.cachedBackground
357379 }
358380 Binding {
359381 target: applicationsDisplayLoader.item
@@ -439,7 +461,8 @@ StyledItem {
439461 tabletMode: shell.usageScenario != "phone"
440462 launcherOffset: launcher.progress
441463 forcedUnlock: wizard.active || shell.mode === "full-shell"
442− background: wallpaperResolver.background
464+ background: wallpaperResolver.cachedBackground
465+ hasCustomBackground: wallpaperResolver.hasCustomBackground
443466 allowFingerprint: !dialogs.hasActiveDialog &&
444467 !notifications.topmostIsFullscreen &&
445468 !panel.indicators.shown
@@ -720,7 +743,7 @@ StyledItem {
720743 model: NotificationBackend.Model
721744 margin: units.gu(1)
722745 hasMouse: shell.hasMouse
723− background: wallpaperResolver.background
746+ background: wallpaperResolver.cachedBackground
724747
725748 y: topmostIsFullscreen ? 0 : panel.panelHeight
726749 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
qml/Stages/DesktopStage.qml+1−3
@@ -240,12 +240,10 @@ AbstractStage {
240240 anchors.fill: parent
241241 focus: spread.state !== "altTab"
242242
243− CrossFadeImage {
243+ Wallpaper {
244244 id: wallpaper
245245 anchors.fill: parent
246246 source: root.background
247− sourceSize { height: root.height; width: root.width }
248− fillMode: Image.PreserveAspectCrop
249247 }
250248
251249 TopLevelSurfaceRepeater {
tests/plugins/CMakeLists.txt+1
@@ -3,6 +3,7 @@ add_subdirectory(Cursor)
33add_subdirectory(Dash)
44add_subdirectory(GlobalShortcut)
55add_subdirectory(Greeter)
6+add_subdirectory(ImageCache)
67add_subdirectory(LightDM)
78add_subdirectory(SessionBroadcast)
89add_subdirectory(Ubuntu)
tests/plugins/ImageCache/CMakeLists.txtnew file+20
@@ -0,0 +1,20 @@
1+include_directories(
2+ ${CMAKE_CURRENT_BINARY_DIR}
3+ )
4+
5+include_directories(
6+ SYSTEM
7+ ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
8+ )
9+
10+add_definitions(-DCURRENT_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}")
11+remove_definitions(-DQT_NO_KEYWORDS)
12+
13+add_executable(ImageCacheTestExec
14+ test.cpp
15+ )
16+qt5_use_modules(ImageCacheTestExec Core Quick Test)
17+add_unity8_uitest(ImageCache ImageCacheTestExec
18+ DEPENDS ImageCache-qml
19+ IMPORT_PATHS ${UNITY_MOCKPATH}
20+ )
tests/plugins/ImageCache/test.cppnew file+214
@@ -0,0 +1,214 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
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; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include <QCoreApplication>
18+#include <QFileInfo>
19+#include <QImageReader>
20+#include <QQuickView>
21+#include <QTemporaryDir>
22+#include <QtTestGui>
23+#include <private/qquickimage_p.h>
24+#include <time.h>
25+#include <utime.h>
26+
27+Q_DECLARE_METATYPE(QQuickImage::Status)
28+
29+class ImageCacheTest : public QObject
30+{
31+ Q_OBJECT
32+
33+private:
34+
35+ void setUpImage(const QString &name, const QSize &sourceSize = QSize())
36+ {
37+ QString url = "image://unity8imagecache/file://" + sourceFile(name);
38+ image->setProperty("sourceSize", sourceSize);
39+ image->setProperty("source", url);
40+ }
41+
42+ void waitForImage(QQuickImage::Status status = QQuickImage::Ready)
43+ {
44+ QTRY_COMPARE(image->property("status"), QVariant::fromValue(status));
45+ }
46+
47+ QSize cachedImageSize(const QString &filename)
48+ {
49+ QImageReader reader(filename);
50+ return reader.size();
51+ }
52+
53+ QString sourceFile(const QString &name)
54+ {
55+ return CURRENT_SOURCE_DIR "/graphics/" + name;
56+ }
57+
58+ QString cachedFile(bool isPath, const QString &name)
59+ {
60+ if (isPath)
61+ return home->path() + "/.cache/unity8/imagecache/paths" + sourceFile(name);
62+ else
63+ return home->path() + "/.cache/unity8/imagecache/names/" + name;
64+ }
65+
66+ void createCachedImage(const QString &name, const QString &cachePath, const QSize &cacheSize, time_t mtime_in = 0)
67+ {
68+ QString origPath = sourceFile(name);
69+ QImageReader origImage(origPath);
70+ origImage.setScaledSize(cacheSize);
71+ auto format = origImage.format(); // can't get this after reading
72+
73+ QImage cacheImage(origImage.read());
74+ QVERIFY(!cacheImage.isNull());
75+
76+ QFileInfo(cachePath).dir().mkpath(QStringLiteral("."));
77+ QVERIFY(cacheImage.save(cachePath, format, 100));
78+
79+ // Make file a few seconds old, so we can more easily tell if it gets updated
80+ auto mtime = mtime_in == 0 ? QFileInfo(cachePath).lastModified().toTime_t() - 5 : mtime_in;
81+ struct utimbuf timebuffer;
82+ timebuffer.modtime = mtime;
83+ timebuffer.actime = timebuffer.modtime;
84+ QCOMPARE(utime(cachePath.toUtf8().data(), &timebuffer), 0);
85+ }
86+
87+private Q_SLOTS:
88+
89+ void init()
90+ {
91+ home = new QTemporaryDir();
92+ QVERIFY(home->isValid());
93+ qputenv("HOME", home->path().toUtf8());
94+
95+ view = new QQuickView();
96+ view->setSource(QUrl::fromLocalFile(CURRENT_SOURCE_DIR "/test.qml"));
97+ image = view->rootObject();
98+
99+ view->show();
100+ QTest::qWaitForWindowExposed(view);
101+
102+ waitForImage(QQuickImage::Null);
103+ }
104+
105+ void cleanup()
106+ {
107+ delete view;
108+ delete home;
109+ }
110+
111+ void testFileNotFound()
112+ {
113+ setUpImage("NOTHERE");
114+ waitForImage(QQuickImage::Error);
115+ QVERIFY(!QFile::exists(cachedFile(true, "NOTHERE")));
116+ }
117+
118+ void testNoSourceSize()
119+ {
120+ setUpImage("wide.jpg");
121+ waitForImage();
122+ QVERIFY(!QFile::exists(cachedFile(true, "wide.jpg")));
123+ }
124+
125+ void testNoScalingUp()
126+ {
127+ setUpImage("wide.jpg", QSize(1000, 1000));
128+ waitForImage();
129+ QVERIFY(!QFile::exists(cachedFile(true, "wide.jpg")));
130+ }
131+
132+ void testFullSourceSize()
133+ {
134+ setUpImage("wide.jpg", QSize(100, 100));
135+ waitForImage();
136+ QCOMPARE(cachedImageSize(cachedFile(true, "wide.jpg")), QSize(100, 100));
137+ }
138+
139+ void testWidthSourceSize()
140+ {
141+ setUpImage("wide.jpg", QSize(100, 0));
142+ waitForImage();
143+ // wide.jpg is 500x200
144+ QCOMPARE(cachedImageSize(cachedFile(true, "wide.jpg")), QSize(100, 40));
145+ }
146+
147+ void testHeightSourceSize()
148+ {
149+ setUpImage("wide.jpg", QSize(0, 100));
150+ waitForImage();
151+ // wide.jpg is 500x200
152+ QCOMPARE(cachedImageSize(cachedFile(true, "wide.jpg")), QSize(250, 100));
153+ }
154+
155+ void testNameArg()
156+ {
157+ setUpImage("wide.jpg?name=foo", QSize(0, 100));
158+ waitForImage();
159+ QVERIFY(!QFile::exists(cachedFile(true, ""))); // check for dir itself
160+ QVERIFY(QFile::exists(cachedFile(false, "foo")));
161+ }
162+
163+ void testLoadCache()
164+ {
165+ auto cacheName = cachedFile(false, "foo");
166+ createCachedImage("wide.jpg", cacheName, QSize(250, 100));
167+ auto mtime = QFileInfo(cacheName).lastModified();
168+
169+ auto now = time(NULL);
170+ QVERIFY(QFileInfo(cacheName).lastModified().toTime_t() < now); // sanity check
171+
172+ setUpImage("wide.jpg?name=foo", QSize(0, 100));
173+ waitForImage();
174+ QCOMPARE(QFileInfo(cacheName).lastModified(), mtime); // wasn't recreated
175+ }
176+
177+ void testDifferentSize()
178+ {
179+ auto cacheName = cachedFile(false, "foo");
180+ createCachedImage("wide.jpg", cacheName, QSize(250, 100));
181+
182+ auto now = time(NULL);
183+ QVERIFY(QFileInfo(cacheName).lastModified().toTime_t() < now); // sanity check
184+
185+ setUpImage("wide.jpg?name=foo", QSize(100, 0));
186+ waitForImage();
187+ QVERIFY(QFileInfo(cacheName).lastModified().toTime_t() >= now); // was recreated
188+ }
189+
190+ void testStaleCache()
191+ {
192+ auto sourceName = sourceFile("wide.jpg");
193+ auto mtime = QFileInfo(sourceName).lastModified().toTime_t() - 5;
194+
195+ auto cacheName = cachedFile(false, "foo");
196+ createCachedImage("wide.jpg", cacheName, QSize(250, 100), mtime);
197+
198+ auto now = time(NULL);
199+ QVERIFY(QFileInfo(cacheName).lastModified().toTime_t() < now); // sanity check
200+
201+ setUpImage("wide.jpg?name=foo", QSize(0, 100));
202+ waitForImage();
203+ QVERIFY(QFileInfo(cacheName).lastModified().toTime_t() >= now); // was recreated
204+ }
205+
206+private:
207+ QQuickView *view;
208+ QObject *image;
209+ QTemporaryDir *home;
210+};
211+
212+QTEST_MAIN(ImageCacheTest)
213+
214+#include "test.moc"
tests/plugins/ImageCache/test.qmlnew file+23
@@ -0,0 +1,23 @@
1+/*
2+ * Copyright 2016 Canonical Ltd.
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; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+import ImageCache 0.1
19+
20+Image {
21+ width: 400
22+ height: 400
23+}
tests/qmltests/Components/tst_Lockscreen.qml+1−1
@@ -37,7 +37,7 @@ Rectangle {
3737 alphaNumeric: pinPadCheckBox.checked
3838 minPinLength: minPinLengthTextField.text
3939 maxPinLength: maxPinLengthTextField.text
40− background: "../../../qml/graphics/phone_background.jpg"
40+ background: "/usr/share/backgrounds/warty-final-ubuntu.png"
4141 }
4242
4343 Connections {
tests/qmltests/Components/tst_WallpaperResolver.qml+21−35
@@ -18,8 +18,6 @@ import QtQuick 2.4
1818import QtTest 1.0
1919import Ubuntu.Components 1.3
2020import Unity.Test 0.1
21−import AccountsService 0.1
22−import GSettings 1.0
2321
2422import "../../../qml/Components"
2523
@@ -32,7 +30,6 @@ Image {
3230
3331 WallpaperResolver {
3432 id: wallpaperResolver
35− width: units.gu(70)
3633 }
3734
3835 UnityTestCase {
@@ -42,48 +39,37 @@ Image {
4239
4340 function test_background_data() {
4441 return [
45− {tag: "color",
46− accounts: Qt.resolvedUrl("data:image/svg+xml,<svg><rect width='100%' height='100%' fill='#dd4814'/></svg>"),
47− gsettings: "",
48− output: Qt.resolvedUrl("data:image/svg+xml,<svg><rect width='100%' height='100%' fill='#dd4814'/></svg>")},
42+ {tag: "none-valid",
43+ list: ["/first", "/middle", "/last"],
44+ output: "file:///last"},
4945
50− {tag: "empty", accounts: "", gsettings: "", output: "defaultBackground"},
51−
52− {tag: "as-specified",
53− accounts: Qt.resolvedUrl("../../data/unity/backgrounds/blue.png"),
54− gsettings: "",
46+ {tag: "first-valid",
47+ list: [Qt.resolvedUrl("../../data/unity/backgrounds/blue.png"),
48+ "/middle", "/last"],
5549 output: Qt.resolvedUrl("../../data/unity/backgrounds/blue.png")},
5650
57− {tag: "gs-specified",
58− accounts: "",
59− gsettings: Qt.resolvedUrl("../../data/unity/backgrounds/red.png"),
51+ {tag: "middle-valid",
52+ list: ["/first",
53+ Qt.resolvedUrl("../../data/unity/backgrounds/red.png"),
54+ "/last"],
6055 output: Qt.resolvedUrl("../../data/unity/backgrounds/red.png")},
6156
62− {tag: "both-specified",
63− accounts: Qt.resolvedUrl("../../data/unity/backgrounds/blue.png"),
64− gsettings: Qt.resolvedUrl("../../data/unity/backgrounds/red.png"),
65− output: Qt.resolvedUrl("../../data/unity/backgrounds/blue.png")},
66−
67− {tag: "invalid-as",
68− accounts: Qt.resolvedUrl("../../data/unity/backgrounds/nope.png"),
69− gsettings: Qt.resolvedUrl("../../data/unity/backgrounds/red.png"),
57+ {tag: "last-valid",
58+ list: ["/first",
59+ "/middle",
60+ Qt.resolvedUrl("../../data/unity/backgrounds/red.png")],
7061 output: Qt.resolvedUrl("../../data/unity/backgrounds/red.png")},
7162
72− {tag: "invalid-both",
73− accounts: Qt.resolvedUrl("../../data/unity/backgrounds/nope.png"),
74− gsettings: Qt.resolvedUrl("../../data/unity/backgrounds/stillnope.png"),
75− output: "defaultBackground"},
63+ {tag: "multiple-valid",
64+ list: [Qt.resolvedUrl("../../data/unity/backgrounds/blue.png"),
65+ Qt.resolvedUrl("../../data/unity/backgrounds/red.png")],
66+ output: Qt.resolvedUrl("../../data/unity/backgrounds/blue.png")},
7667 ]
7768 }
78− function test_background(data) {
79− AccountsService.backgroundFile = data.accounts;
80− GSettingsController.setPictureUri(data.gsettings);
8169
82− if (data.output === "defaultBackground") {
83− tryCompare(wallpaperResolver, "background", wallpaperResolver.defaultBackground);
84− } else {
85− tryCompare(wallpaperResolver, "background", data.output);
86− }
70+ function test_background(data) {
71+ wallpaperResolver.candidates = data.list;
72+ tryCompare(wallpaperResolver, "background", data.output);
8773 }
8874 }
8975}
tests/qmltests/Components/tst_ZoomableImage.qml+1−1
@@ -31,7 +31,7 @@ Rectangle {
3131 }
3232
3333 property var widgetData1: {
34− "source": "../../../qml/graphics/phone_background.jpg",
34+ "source": "../../../tests/graphics/avatars/amanda@12.png",
3535 "zoomable": false
3636 }
3737
tests/qmltests/Dash/Previews/tst_PreviewExpandable.qml+3−3
@@ -54,8 +54,8 @@ Rectangle {
5454
5555 property var galleryData: {
5656 "sources": [
57− "../../graphics/phone_background.jpg",
58− "../../graphics/tablet_background.jpg",
57+ "../../../tests/graphics/avatars/amanda@12.png",
58+ "../../../tests/graphics/avatars/funky@12.png",
5959 "../../../tests/graphics/clock@18.png",
6060 "../../graphics/borked"
6161 ]
@@ -71,7 +71,7 @@ Rectangle {
7171 }
7272
7373 property var imageData: {
74− "source": "../../graphics/phone_background.jpg",
74+ "source": "../../../tests/graphics/avatars/amanda@12.png",
7575 "zoomable": false
7676 }
7777
tests/qmltests/Dash/Previews/tst_PreviewImageGallery.qml+6−6
@@ -31,8 +31,8 @@ Rectangle {
3131
3232 property var sourcesModel1: {
3333 "sources": [
34− "../../graphics/phone_background.jpg",
35− "../../graphics/tablet_background.jpg",
34+ "../../../tests/graphics/avatars/amanda@12.png",
35+ "../../../tests/graphics/avatars/funky@12.png",
3636 "../../../tests/graphics/clock@18.png",
3737 "../../graphics/borked"
3838 ]
@@ -40,8 +40,8 @@ Rectangle {
4040
4141 property var sourcesModel1WithFallback: {
4242 "sources": [
43− "../../graphics/phone_background.jpg",
44− "../../graphics/tablet_background.jpg",
43+ "../../../tests/graphics/avatars/amanda@12.png",
44+ "../../../tests/graphics/avatars/funky@12.png",
4545 "../../../tests/graphics/clock@18.png",
4646 "../../graphics/borked"
4747 ]
@@ -50,8 +50,8 @@ Rectangle {
5050
5151 property var sourcesModelEmptyWithFallback: {
5252 "sources": [
53− "../../graphics/phone_background.jpg",
54− "../../graphics/tablet_background.jpg",
53+ "../../../tests/graphics/avatars/amanda@12.png",
54+ "../../../tests/graphics/avatars/funky@12.png",
5555 "../../../tests/graphics/clock@18.png",
5656 ""
5757 ]
tests/qmltests/Dash/Previews/tst_PreviewZoomableImage.qml+7−7
@@ -26,7 +26,7 @@ Rectangle {
2626 color: "lightgrey"
2727
2828 property var widgetData0: {
29− "source": "../../graphics/phone_background.jpg",
29+ "source": "../../../tests/graphics/avatars/amanda@12.png",
3030 "zoomable": false
3131 }
3232
@@ -36,17 +36,17 @@ Rectangle {
3636
3737 property var widgetData2: {
3838 "source": "fadsasf",
39− "fallback": "../../graphics/phone_background.jpg"
39+ "fallback": "../../../tests/graphics/avatars/amanda@12.png"
4040 }
4141
4242 property var widgetData3: {
4343 "source": "",
44− "fallback": "../../graphics/phone_background.jpg"
44+ "fallback": "../../../tests/graphics/avatars/amanda@12.png"
4545 }
4646
4747 property var widgetData4: {
4848 "source": "fadsasf",
49− "fallback": "../../graphics/phone_background.jpg"
49+ "fallback": "../../../tests/graphics/avatars/amanda@12.png"
5050 }
5151
5252 Loader {
@@ -123,11 +123,11 @@ Rectangle {
123123 waitForRendering(zoomableImage);
124124 waitForRendering(lazyImage);
125125 tryCompare(lazyImage, "state", "ready");
126− verify(lazyImage.sourceImage.source.toString().indexOf("phone_background") > -1);
127− zoomableImage.widgetData["source"] = "../../graphics/tablet_background.jpg"
126+ verify(lazyImage.sourceImage.source.toString().indexOf("amanda") > -1);
127+ zoomableImage.widgetData["source"] = "../../../tests/graphics/avatars/funky@12.png"
128128 zoomableImage.widgetDataChanged();
129129 tryCompare(lazyImage, "state", "ready");
130− verify(lazyImage.sourceImage.source.toString().indexOf("tablet_background") > -1);
130+ verify(lazyImage.sourceImage.source.toString().indexOf("funky") > -1);
131131 }
132132 }
133133}
tests/qmltests/Greeter/TestView.qml+1
@@ -27,6 +27,7 @@ Item {
2727 property int delayMinutes
2828 property real backgroundTopMargin
2929 property url background
30+ property bool hasCustomBackground
3031 property bool locked
3132 property bool alphanumeric
3233 property var userModel
tests/qmltests/Greeter/tst_Greeter.qml+7−1
@@ -29,7 +29,7 @@ Item {
2929 width: units.gu(120)
3030 height: units.gu(80)
3131
32− property url defaultBackground: Qt.resolvedUrl("../../../qml/graphics/tablet_background.jpg")
32+ property url defaultBackground: "/usr/share/backgrounds/warty-final-ubuntu.png"
3333
3434 Component.onCompleted: {
3535 // set the mock mode before loading
@@ -368,6 +368,12 @@ Item {
368368 compare(view.background, Qt.resolvedUrl("testing"));
369369 }
370370
371+ function test_hasCustomBackground() {
372+ verify(!view.hasCustomBackground);
373+ greeter.hasCustomBackground = true;
374+ verify(view.hasCustomBackground);
375+ }
376+
371377 function test_notifyAboutToFocusApp() {
372378 greeter.notifyUserRequestedApp("fake-app");
373379 compare(viewTryToUnlockSpy.count, 1);
tests/qmltests/Greeter/tst_NarrowView.qml+13−1
@@ -40,7 +40,7 @@ Item {
4040 property bool itemDestroyed: false
4141 sourceComponent: Component {
4242 NarrowView {
43− background: Qt.resolvedUrl("../../../qml/graphics/phone_background.jpg")
43+ background: "/usr/share/backgrounds/warty-final-ubuntu.png"
4444 userModel: LightDM.Users
4545 infographicModel: LightDM.Infographic
4646
@@ -345,6 +345,18 @@ Item {
345345 compare(teaseSpy.count, data.count);
346346 }
347347
348+ function test_customBackground() {
349+ var lockscreen = findChild(view, "lockscreen");
350+ var backgroundShade = findChild(view, "backgroundShade");
351+
352+ compare(lockscreen.darkenBackground, 0);
353+ verify(!backgroundShade.visible);
354+
355+ view.hasCustomBackground = true;
356+ compare(lockscreen.darkenBackground, 0.4);
357+ verify(backgroundShade.visible);
358+ }
359+
348360 function test_respondedWithPin() {
349361 view.locked = true;
350362 view.showPrompt("", true, true);
tests/qmltests/Greeter/tst_WideView.qml+12−1
@@ -50,7 +50,7 @@ StyledItem {
5050 WideView {
5151 id: view
5252
53− background: Qt.resolvedUrl("../../../qml/graphics/tablet_background.jpg")
53+ background: "/usr/share/backgrounds/warty-final-ubuntu.png"
5454 userModel: LightDM.Users
5555 infographicModel: LightDM.Infographic
5656
@@ -525,6 +525,17 @@ StyledItem {
525525 compare(view.currentIndex, 0); // confirm we didn't change
526526 }
527527
528+ function test_customBackground() {
529+ var coverPage = findChild(view, "coverPage");
530+ var backgroundShade = findChild(coverPage, "backgroundShade");
531+
532+ verify(!view.hasCustomBackground);
533+ verify(!backgroundShade.visible);
534+
535+ view.hasCustomBackground = true;
536+ verify(backgroundShade.visible);
537+ }
538+
528539 function test_respondedWithPassword() {
529540 view.locked = true;
530541 view.showPrompt("Prompt", true, false);
tests/qmltests/Stages/tst_DesktopStage.qml+1−1
@@ -75,7 +75,7 @@ Item {
7575 sourceComponent: Component {
7676 DesktopStage {
7777 anchors.fill: parent
78− background: "../../../qml/graphics/tablet_background.jpg"
78+ background: "/usr/share/backgrounds/warty-final-ubuntu.png"
7979 focus: true
8080
8181 Component.onCompleted: {
tests/qmltests/tst_Shell.qml+80
@@ -549,6 +549,7 @@ Rectangle {
549549
550550 AccountsService.demoEdges = false;
551551 AccountsService.demoEdgesCompleted = [];
552+ AccountsService.backgroundFile = "";
552553 Wizard.System.wizardEnabled = false;
553554 shellLoader.mode = "full-greeter";
554555
@@ -563,6 +564,7 @@ Rectangle {
563564 broadcastHomeSpy.clear();
564565
565566 GSettingsController.setLifecycleExemptAppids([]);
567+ GSettingsController.setPictureUri("");
566568
567569 // there should be only unity8-dash window over there
568570 tryCompare(ApplicationManager, "count", 1);
@@ -1334,6 +1336,31 @@ Rectangle {
13341336 tryCompare(tutorial, "paused", false);
13351337 }
13361338
1339+ function test_customBackground() {
1340+ loadShell("desktop");
1341+ shell.usageScenario = "desktop";
1342+ waitForRendering(shell);
1343+
1344+ var wallpaperResolver = findInvisibleChild(shell, "wallpaperResolver");
1345+ var greeter = findChild(shell, "greeter");
1346+ verify(!greeter.hasCustomBackground);
1347+ compare(greeter.background, wallpaperResolver.defaultBackground);
1348+
1349+ AccountsService.backgroundFile = Qt.resolvedUrl("../graphics/applicationIcons/dash.png");
1350+ tryCompare(greeter, "hasCustomBackground", true);
1351+ compare(greeter.background, AccountsService.backgroundFile);
1352+ }
1353+
1354+ function test_cachedDefaultBackground() {
1355+ loadShell("desktop");
1356+ shell.usageScenario = "desktop";
1357+ waitForRendering(shell);
1358+
1359+ var greeter = findChild(shell, "greeter");
1360+ verify(!greeter.hasCustomBackground);
1361+ compare(greeter.background.toString().indexOf("image://thumbnailer/file:///"), 0);
1362+ }
1363+
13371364 function test_tapOnRightEdgeReachesApplicationSurface() {
13381365 loadShell("phone");
13391366 swipeAwayGreeter();
@@ -2435,6 +2462,59 @@ Rectangle {
24352462 compare(topmostSurfaceItem.touchReleaseCount, 2);
24362463 }
24372464
2465+ function test_background_data() {
2466+ return [
2467+ {tag: "color",
2468+ accounts: Qt.resolvedUrl("data:image/svg+xml,<svg><rect width='100%' height='100%' fill='#dd4814'/></svg>"),
2469+ gsettings: "",
2470+ output: Qt.resolvedUrl("data:image/svg+xml,<svg><rect width='100%' height='100%' fill='#dd4814'/></svg>")},
2471+
2472+ {tag: "empty", accounts: "", gsettings: "", output: "defaultBackground"},
2473+
2474+ {tag: "as-specified",
2475+ accounts: Qt.resolvedUrl("../data/unity/backgrounds/blue.png"),
2476+ gsettings: "",
2477+ output: Qt.resolvedUrl("../data/unity/backgrounds/blue.png")},
2478+
2479+ {tag: "gs-specified",
2480+ accounts: "",
2481+ gsettings: Qt.resolvedUrl("../data/unity/backgrounds/red.png"),
2482+ output: Qt.resolvedUrl("../data/unity/backgrounds/red.png")},
2483+
2484+ {tag: "both-specified",
2485+ accounts: Qt.resolvedUrl("../data/unity/backgrounds/blue.png"),
2486+ gsettings: Qt.resolvedUrl("../data/unity/backgrounds/red.png"),
2487+ output: Qt.resolvedUrl("../data/unity/backgrounds/blue.png")},
2488+
2489+ {tag: "invalid-as",
2490+ accounts: Qt.resolvedUrl("../data/unity/backgrounds/nope.png"),
2491+ gsettings: Qt.resolvedUrl("../data/unity/backgrounds/red.png"),
2492+ output: Qt.resolvedUrl("../data/unity/backgrounds/red.png")},
2493+
2494+ {tag: "invalid-both",
2495+ accounts: Qt.resolvedUrl("../data/unity/backgrounds/nope.png"),
2496+ gsettings: Qt.resolvedUrl("../data/unity/backgrounds/stillnope.png"),
2497+ output: "defaultBackground"},
2498+ ]
2499+ }
2500+ function test_background(data) {
2501+ loadShell("phone");
2502+ shell.usageScenario = "phone";
2503+ waitForRendering(shell);
2504+
2505+ AccountsService.backgroundFile = data.accounts;
2506+ GSettingsController.setPictureUri(data.gsettings);
2507+
2508+ var wallpaperResolver = findChild(shell, "wallpaperResolver");
2509+ if (data.output === "defaultBackground") {
2510+ tryCompare(wallpaperResolver, "background", wallpaperResolver.defaultBackground);
2511+ verify(!wallpaperResolver.hasCustomBackground);
2512+ } else {
2513+ tryCompare(wallpaperResolver, "background", data.output);
2514+ verify(wallpaperResolver.hasCustomBackground);
2515+ }
2516+ }
2517+
24382518 function test_greeterModeBroadcastsApp() {
24392519 setLightDMMockMode("single-pin");
24402520 shellLoader.mode = "greeter";