on xenial help sign in
merge trunk
eae1155Lukáš Tinkl
debian/changelog+52
@@ -1,3 +1,55 @@
1+unity8 (8.14+16.10.20160803-0ubuntu1) yakkety; urgency=medium
2+
3+ [ Albert Astals Cid ]
4+ * Fix DisabledScreenNotice::test_rotation
5+ * Make the delegate of DashNavigationList an async Loader
6+ * unity-scope-tool: Use parse instead of process
7+ * VerticalJournal improvements regarding model insertions and item
8+ height changes (LP: #1599754)
9+ * Add math.h includes for compilation in yakkety (LP: #1605502)
10+
11+ [ Andrea Cimitan ]
12+ * Unfocus the search text field in the dash page header when requested
13+ (LP: #1590820)
14+ * Use binding for filter popover contentWidth (LP: #1595116)
15+ * Add few sourceSize for Image we forgot (LP: #1595113)
16+ * Replace Flickable with an Item inside DashPageHeader for
17+ headerContainer (LP: #1599235)
18+
19+ [ Albert Astals Cid, Andrea Cimitan ]
20+ * Unbox the artshapeLoader
21+
22+ [ Daniel d'Andrada ]
23+ * Fix splashscreen orientation when app rotates own contents (LP:
24+ #1586050)
25+ * tst_PreviewIconActions: Make test name match filename
26+ * Cursor: make its size grid unit based (LP: #1604014)
27+ * TouchEventSequenceWrapper: item might get deleted when touch
28+ sequence is committed (LP: #1607686)
29+
30+ [ Lukáš Tinkl ]
31+ * Implement frontend support for running keyboard indicator
32+ * Provide window/surface close shortcuts across all the stages (LP:
33+ #1578392, #1606528)
34+ * Restart the location trust prompt service when exiting the wizard
35+ (LP: #1594430)
36+ * Implement clearing the search and closing the panel on pressing
37+ Escape key in the dash header
38+ * Fix log timestamps (LP: #1602196)
39+ * Don't display the mode switch warning dialog on tablets (LP:
40+ #1600290)
41+
42+ [ Michael Terry ]
43+ * Fix indicator profile bug that caused sound indicator to say Mute
44+ instead of Silent Mode. (LP: #1604205)
45+ * Fix lockscreen appearing right after unlocking a locked session on
46+ the desktop. (LP: #1604374)
47+
48+ [ Michał Sawicz ]
49+ * Add arm64 support, drop unnecessary B-D on web plugin
50+
51+ -- Michał Sawicz <michal.sawicz@canonical.com> Wed, 03 Aug 2016 15:56:27 +0000
52+
1unity8 (8.14+16.10.20160728-0ubuntu1) yakkety; urgency=medium
53unity8 (8.14+16.10.20160728-0ubuntu1) yakkety; urgency=medium
2
54
3 [ Zoltán Balogh ]
55 [ Zoltán Balogh ]
debian/control+9−8
@@ -8,7 +8,7 @@ Build-Depends: cmake,
8 dh-apparmor,
8 dh-apparmor,
9 doxyqml,
9 doxyqml,
10# To allow cross-compiling to work, we also must append :native
10# To allow cross-compiling to work, we also must append :native
11−# to g++ so we don't try to run armhf g++
11+# to g++ so we don't try to run arm g++
12# on an x86 CPU for example, when cross-compiling.
12# on an x86 CPU for example, when cross-compiling.
13 g++:native,
13 g++:native,
14 libandroid-properties-dev,
14 libandroid-properties-dev,
@@ -18,9 +18,9 @@ Build-Depends: cmake,
18 libevdev-dev,
18 libevdev-dev,
19 libgeonames-dev (>= 0.2),
19 libgeonames-dev (>= 0.2),
20 libgnome-desktop-3-dev,
20 libgnome-desktop-3-dev,
21− libgl1-mesa-dev[!armhf] | libgl-dev[!armhf],
21+ libgl1-mesa-dev[!arm64 !armhf] | libgl-dev[!arm64 !armhf],
22 libgl1-mesa-dri,
22 libgl1-mesa-dri,
23− libgles2-mesa-dev[armhf],
23+ libgles2-mesa-dev[arm64 armhf],
24 libglib2.0-dev,
24 libglib2.0-dev,
25 libgsettings-qt-dev,
25 libgsettings-qt-dev,
26 libhardware-dev,
26 libhardware-dev,
@@ -38,9 +38,9 @@ Build-Depends: cmake,
38 libunity-api-dev (>= 7.116),
38 libunity-api-dev (>= 7.116),
39 libusermetricsoutput1-dev,
39 libusermetricsoutput1-dev,
40# Need those X11 libs touch emulation from mouse events in manual QML tests on a X11 desktop
40# Need those X11 libs touch emulation from mouse events in manual QML tests on a X11 desktop
41− libx11-dev[!armhf],
41+ libx11-dev[!arm64 !armhf],
42− libxcb1-dev[!armhf],
42+ libxcb1-dev[!arm64 !armhf],
43− libxi-dev[!armhf],
43+ libxi-dev[!arm64 !armhf],
44# End of X11 libs
44# End of X11 libs
45 pkg-config,
45 pkg-config,
46 python3-all:any,
46 python3-all:any,
@@ -75,7 +75,7 @@ XS-Testsuite: autopkgtest
75X-Ubuntu-Use-Langpack: yes
75X-Ubuntu-Use-Langpack: yes
76
76
77Package: indicators-client
77Package: indicators-client
78−Architecture: amd64 armhf i386
78+Architecture: any
79Depends: qmenumodel-qml (>= 0.2.9),
79Depends: qmenumodel-qml (>= 0.2.9),
80 qml-module-ubuntu-components (>= 1.3.2030) | qml-module-ubuntu-components-gles (>= 1.3.2030),
80 qml-module-ubuntu-components (>= 1.3.2030) | qml-module-ubuntu-components-gles (>= 1.3.2030),
81 unity8 (= ${binary:Version}),
81 unity8 (= ${binary:Version}),
@@ -131,7 +131,8 @@ Depends: dmz-cursor-theme,
131 unity8-private | unity-launcher-impl,
131 unity8-private | unity-launcher-impl,
132 ${misc:Depends},
132 ${misc:Depends},
133 ${shlibs:Depends},
133 ${shlibs:Depends},
134−Recommends: indicator-session,
134+Recommends: indicator-keyboard,
135+ indicator-session,
135 unity-scope-click,
136 unity-scope-click,
136 unity-scope-mediascanner2,
137 unity-scope-mediascanner2,
137Breaks: indicator-network (<< 0.5.1+14.10.20141014),
138Breaks: indicator-network (<< 0.5.1+14.10.20141014),
plugins/Cursor/Cursor.qml+5−2
@@ -25,6 +25,7 @@ MousePointer {
25 id: imageInfo
25 id: imageInfo
26 themeName: mousePointer.themeName
26 themeName: mousePointer.themeName
27 cursorName: mousePointer.cursorName
27 cursorName: mousePointer.cursorName
28+ cursorHeight: mousePointer.height
28 }
29 }
29
30
30 Loader {
31 Loader {
@@ -32,7 +33,7 @@ MousePointer {
32 sourceComponent: AnimatedSprite {
33 sourceComponent: AnimatedSprite {
33 x: -imageInfo.hotspot.x
34 x: -imageInfo.hotspot.x
34 y: -imageInfo.hotspot.y
35 y: -imageInfo.hotspot.y
35− source: "image://cursor/" + mousePointer.themeName + "/" + mousePointer.cursorName
36+ source: imageInfo.imageSource
36
37
37 interpolate: false
38 interpolate: false
38
39
@@ -53,7 +54,9 @@ MousePointer {
53 sourceComponent: Image {
54 sourceComponent: Image {
54 x: -imageInfo.hotspot.x
55 x: -imageInfo.hotspot.x
55 y: -imageInfo.hotspot.y
56 y: -imageInfo.hotspot.y
56− source: "image://cursor/" + mousePointer.themeName + "/" + mousePointer.cursorName
57+ source: imageInfo.imageSource
58+ width: sourceSize.width
59+ height: sourceSize.height
57 }
60 }
58 }
61 }
59}
62}
plugins/Cursor/CursorImageInfo.cpp+21−1
@@ -28,6 +28,15 @@ void CursorImageInfo::setCursorName(const QString &cursorName)
28 update();
28 update();
29 Q_EMIT cursorNameChanged();
29 Q_EMIT cursorNameChanged();
30 }
30 }
31+
32+}
33+void CursorImageInfo::setCursorHeight(qreal cursorHeight)
34+{
35+ if (cursorHeight != m_cursorHeight) {
36+ m_cursorHeight = cursorHeight;
37+ update();
38+ Q_EMIT cursorHeightChanged();
39+ }
31}
40}
32
41
33void CursorImageInfo::setThemeName(const QString &themeName)
42void CursorImageInfo::setThemeName(const QString &themeName)
@@ -41,13 +50,14 @@ void CursorImageInfo::setThemeName(const QString &themeName)
41
50
42void CursorImageInfo::update()
51void CursorImageInfo::update()
43{
52{
44− m_cursorImage = CursorImageProvider::instance()->fetchCursor(m_themeName, m_cursorName);
53+ m_cursorImage = CursorImageProvider::instance()->fetchCursor(m_themeName, m_cursorName, (int) m_cursorHeight);
45
54
46 Q_EMIT hotspotChanged();
55 Q_EMIT hotspotChanged();
47 Q_EMIT frameWidthChanged();
56 Q_EMIT frameWidthChanged();
48 Q_EMIT frameHeightChanged();
57 Q_EMIT frameHeightChanged();
49 Q_EMIT frameCountChanged();
58 Q_EMIT frameCountChanged();
50 Q_EMIT frameDurationChanged();
59 Q_EMIT frameDurationChanged();
60+ Q_EMIT imageSourceChanged();
51}
61}
52
62
53QPoint CursorImageInfo::hotspot() const
63QPoint CursorImageInfo::hotspot() const
@@ -94,3 +104,13 @@ int CursorImageInfo::frameDuration() const
94 return 0;
104 return 0;
95 }
105 }
96}
106}
107+
108+QUrl CursorImageInfo::imageSource() const
109+{
110+ auto urlString = QString("image://cursor/%1/%2/%3")
111+ .arg(m_themeName)
112+ .arg(m_cursorName)
113+ .arg(m_cursorHeight);
114+
115+ return QUrl(urlString);
116+}
plugins/Cursor/CursorImageInfo.h+10
@@ -21,6 +21,7 @@
21
21
22#include <QObject>
22#include <QObject>
23#include <QString>
23#include <QString>
24+#include <QUrl>
24
25
25class CursorImageInfo : public QObject
26class CursorImageInfo : public QObject
26{
27{
@@ -28,12 +29,14 @@ class CursorImageInfo : public QObject
28
29
29 Q_PROPERTY(QString themeName READ themeName WRITE setThemeName NOTIFY themeNameChanged)
30 Q_PROPERTY(QString themeName READ themeName WRITE setThemeName NOTIFY themeNameChanged)
30 Q_PROPERTY(QString cursorName READ cursorName WRITE setCursorName NOTIFY cursorNameChanged)
31 Q_PROPERTY(QString cursorName READ cursorName WRITE setCursorName NOTIFY cursorNameChanged)
32+ Q_PROPERTY(qreal cursorHeight READ cursorHeight WRITE setCursorHeight NOTIFY cursorHeightChanged)
31
33
32 Q_PROPERTY(QPoint hotspot READ hotspot NOTIFY hotspotChanged)
34 Q_PROPERTY(QPoint hotspot READ hotspot NOTIFY hotspotChanged)
33 Q_PROPERTY(qreal frameWidth READ frameWidth NOTIFY frameWidthChanged)
35 Q_PROPERTY(qreal frameWidth READ frameWidth NOTIFY frameWidthChanged)
34 Q_PROPERTY(qreal frameHeight READ frameHeight NOTIFY frameHeightChanged)
36 Q_PROPERTY(qreal frameHeight READ frameHeight NOTIFY frameHeightChanged)
35 Q_PROPERTY(int frameCount READ frameCount NOTIFY frameCountChanged)
37 Q_PROPERTY(int frameCount READ frameCount NOTIFY frameCountChanged)
36 Q_PROPERTY(int frameDuration READ frameDuration NOTIFY frameDurationChanged)
38 Q_PROPERTY(int frameDuration READ frameDuration NOTIFY frameDurationChanged)
39+ Q_PROPERTY(QUrl imageSource READ imageSource NOTIFY imageSourceChanged)
37
40
38public:
41public:
39 CursorImageInfo(QObject *parent = nullptr);
42 CursorImageInfo(QObject *parent = nullptr);
@@ -44,20 +47,26 @@ public:
44 QString cursorName() const { return m_cursorName; }
47 QString cursorName() const { return m_cursorName; }
45 void setCursorName(const QString &);
48 void setCursorName(const QString &);
46
49
50+ qreal cursorHeight() const { return m_cursorHeight; }
51+ void setCursorHeight(qreal);
52+
47 QPoint hotspot() const;
53 QPoint hotspot() const;
48 qreal frameWidth() const;
54 qreal frameWidth() const;
49 qreal frameHeight() const;
55 qreal frameHeight() const;
50 int frameCount() const;
56 int frameCount() const;
51 int frameDuration() const;
57 int frameDuration() const;
58+ QUrl imageSource() const;
52
59
53Q_SIGNALS:
60Q_SIGNALS:
54 void themeNameChanged();
61 void themeNameChanged();
55 void cursorNameChanged();
62 void cursorNameChanged();
63+ void cursorHeightChanged();
56 void hotspotChanged();
64 void hotspotChanged();
57 void frameWidthChanged();
65 void frameWidthChanged();
58 void frameHeightChanged();
66 void frameHeightChanged();
59 void frameCountChanged();
67 void frameCountChanged();
60 void frameDurationChanged();
68 void frameDurationChanged();
69+ void imageSourceChanged();
61
70
62private Q_SLOTS:
71private Q_SLOTS:
63 void update();
72 void update();
@@ -65,6 +74,7 @@ private Q_SLOTS:
65private:
74private:
66 QString m_themeName;
75 QString m_themeName;
67 QString m_cursorName;
76 QString m_cursorName;
77+ qreal m_cursorHeight{0};
68
78
69 CursorImage *m_cursorImage{nullptr};
79 CursorImage *m_cursorImage{nullptr};
70};
80};
plugins/Cursor/CursorImageProvider.cpp+33−21
@@ -27,7 +27,7 @@ CursorImageProvider *CursorImageProvider::m_instance = nullptr;
27/////
27/////
28// BuiltInCursorImage
28// BuiltInCursorImage
29
29
30−BuiltInCursorImage::BuiltInCursorImage()
30+BuiltInCursorImage::BuiltInCursorImage(int cursorHeight)
31{
31{
32 const char *svgString =
32 const char *svgString =
33 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"
33 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"
@@ -42,12 +42,14 @@ BuiltInCursorImage::BuiltInCursorImage()
42 " d=\"M 20.504,50.94931 460.42533,518.14486 266.47603,515.61948 366.48114,719.16522 274.05218,770.68296 172.53185,559.56112 20.504,716.13476 Z\" />"
42 " d=\"M 20.504,50.94931 460.42533,518.14486 266.47603,515.61948 366.48114,719.16522 274.05218,770.68296 172.53185,559.56112 20.504,716.13476 Z\" />"
43 "</svg>";
43 "</svg>";
44
44
45+ // NB: Original image dimension is 20x32. Ensure aspect ratio is kept
45− qimage = QImage(20, 32, QImage::Format_ARGB32);
46+ qimage = QImage((20./32.)*cursorHeight, cursorHeight, QImage::Format_ARGB32);
46 qimage.fill(Qt::transparent);
47 qimage.fill(Qt::transparent);
47 QPainter imagePainter(&qimage);
48 QPainter imagePainter(&qimage);
48
49
49 frameWidth = qimage.width();
50 frameWidth = qimage.width();
50 frameHeight = qimage.height();
51 frameHeight = qimage.height();
52+ requestedHeight = cursorHeight;
51
53
52 QSvgRenderer *svgRenderer = new QSvgRenderer(QByteArray(svgString));
54 QSvgRenderer *svgRenderer = new QSvgRenderer(QByteArray(svgString));
53 svgRenderer->render(&imagePainter);
55 svgRenderer->render(&imagePainter);
@@ -81,11 +83,9 @@ CustomCursorImage::CustomCursorImage(const QCursor &cursor)
81/////
83/////
82// XCursorImage
84// XCursorImage
83
85
84−XCursorImage::XCursorImage(const QString &theme, const QString &file)
86+XCursorImage::XCursorImage(const QString &theme, const QString &file, int preferredCursorHeightPx)
85{
87{
86− // TODO: Consider grid unit value
87− // Hardcoding to a medium size for now
88− int preferredCursorHeightPx = 32;
88+ requestedHeight = preferredCursorHeightPx;
89
89
90 XcursorImages *xcursorImages = XcursorLibraryLoadImages(QFile::encodeName(file), QFile::encodeName(theme),
90 XcursorImages *xcursorImages = XcursorLibraryLoadImages(QFile::encodeName(file), QFile::encodeName(theme),
91 preferredCursorHeightPx);
91 preferredCursorHeightPx);
@@ -96,7 +96,7 @@ XCursorImage::XCursorImage(const QString &theme, const QString &file)
96 frameCount = xcursorImages->nimage;
96 frameCount = xcursorImages->nimage;
97
97
98 for (int i = 0; i < xcursorImages->nimage; ++i) {
98 for (int i = 0; i < xcursorImages->nimage; ++i) {
99− XcursorImage *xcursorImage = xcursorImages->images[0];
99+ XcursorImage *xcursorImage = xcursorImages->images[i];
100 if (frameWidth < (int)xcursorImage->width) {
100 if (frameWidth < (int)xcursorImage->width) {
101 frameWidth = xcursorImage->width;
101 frameWidth = xcursorImage->width;
102 }
102 }
@@ -235,34 +235,43 @@ CursorImageProvider::~CursorImageProvider()
235 m_instance = nullptr;
235 m_instance = nullptr;
236}
236}
237
237
238−QImage CursorImageProvider::requestImage(const QString &cursorThemeAndName, QSize *size, const QSize & /*requestedSize*/)
238+QImage CursorImageProvider::requestImage(const QString &cursorThemeAndNameAndHeight, QSize *size, const QSize & /*requestedSize*/)
239{
239{
240− CursorImage *cursorImage = fetchCursor(cursorThemeAndName);
240+ CursorImage *cursorImage = fetchCursor(cursorThemeAndNameAndHeight);
241 size->setWidth(cursorImage->qimage.width());
241 size->setWidth(cursorImage->qimage.width());
242 size->setHeight(cursorImage->qimage.height());
242 size->setHeight(cursorImage->qimage.height());
243
243
244 return cursorImage->qimage;
244 return cursorImage->qimage;
245}
245}
246
246
247−CursorImage *CursorImageProvider::fetchCursor(const QString &cursorThemeAndName)
247+CursorImage *CursorImageProvider::fetchCursor(const QString &cursorThemeAndNameAndHeight)
248{
248{
249 QString themeName;
249 QString themeName;
250 QString cursorName;
250 QString cursorName;
251+ int cursorHeight;
251 {
252 {
252− QStringList themeAndNameList = cursorThemeAndName.split('/');
253+ QStringList themeAndNameList = cursorThemeAndNameAndHeight.split('/');
253− if (themeAndNameList.size() != 2) {
254+ if (themeAndNameList.size() != 3) {
254 return nullptr;
255 return nullptr;
255 }
256 }
256 themeName = themeAndNameList[0];
257 themeName = themeAndNameList[0];
257 cursorName = themeAndNameList[1];
258 cursorName = themeAndNameList[1];
259+
260+ bool ok;
261+ cursorHeight = themeAndNameList[2].toInt(&ok);
262+ if (!ok) {
263+ cursorHeight = 32;
264+ qWarning().nospace() << "CursorImageProvider: invalid cursor height ("<<themeAndNameList[2]<<")."
265+ " Falling back to "<<cursorHeight<<" pixels";
266+ }
258 }
267 }
259
268
260− return fetchCursor(themeName, cursorName);
269+ return fetchCursor(themeName, cursorName, cursorHeight);
261}
270}
262
271
263−CursorImage *CursorImageProvider::fetchCursor(const QString &themeName, const QString &cursorName)
272+CursorImage *CursorImageProvider::fetchCursor(const QString &themeName, const QString &cursorName, int cursorHeight)
264{
273{
265− CursorImage *cursorImage = fetchCursorHelper(themeName, cursorName);
274+ CursorImage *cursorImage = fetchCursorHelper(themeName, cursorName, cursorHeight);
266
275
267 // Try some fallbacks
276 // Try some fallbacks
268 if (cursorImage->qimage.isNull()) {
277 if (cursorImage->qimage.isNull()) {
@@ -271,7 +280,7 @@ CursorImage *CursorImageProvider::fetchCursor(const QString &themeName, const QS
271 int i = 0;
280 int i = 0;
272 while (cursorImage->qimage.isNull() && i < fallbackNames.count()) {
281 while (cursorImage->qimage.isNull() && i < fallbackNames.count()) {
273 qDebug().nospace() << "CursorImageProvider: "<< cursorName <<" not found, trying " << fallbackNames.at(i);
282 qDebug().nospace() << "CursorImageProvider: "<< cursorName <<" not found, trying " << fallbackNames.at(i);
274− cursorImage = fetchCursorHelper(themeName, fallbackNames.at(i));
283+ cursorImage = fetchCursorHelper(themeName, fallbackNames.at(i), cursorHeight);
275 ++i;
284 ++i;
276 }
285 }
277 }
286 }
@@ -281,14 +290,14 @@ CursorImage *CursorImageProvider::fetchCursor(const QString &themeName, const QS
281 if (cursorImage->qimage.isNull() && cursorName != QLatin1String("left_ptr")) {
290 if (cursorImage->qimage.isNull() && cursorName != QLatin1String("left_ptr")) {
282 qDebug() << "CursorImageProvider:" << cursorName
291 qDebug() << "CursorImageProvider:" << cursorName
283 << "not found (nor its fallbacks, if any). Going for \"left_ptr\" as a last resort.";
292 << "not found (nor its fallbacks, if any). Going for \"left_ptr\" as a last resort.";
284− cursorImage = fetchCursorHelper(themeName, QStringLiteral("left_ptr"));
293+ cursorImage = fetchCursorHelper(themeName, QStringLiteral("left_ptr"), cursorHeight);
285 }
294 }
286
295
287 if (cursorImage->qimage.isNull()) {
296 if (cursorImage->qimage.isNull()) {
288 // finally, go for the built-in cursor
297 // finally, go for the built-in cursor
289 qWarning() << "CursorImageProvider: couldn't find any cursors. Using the built-in one";
298 qWarning() << "CursorImageProvider: couldn't find any cursors. Using the built-in one";
290− if (!m_builtInCursorImage) {
299+ if (!m_builtInCursorImage || m_builtInCursorImage->requestedHeight != cursorHeight) {
291− m_builtInCursorImage.reset(new BuiltInCursorImage);
300+ m_builtInCursorImage.reset(new BuiltInCursorImage(cursorHeight));
292 }
301 }
293 cursorImage = m_builtInCursorImage.data();
302 cursorImage = m_builtInCursorImage.data();
294 }
303 }
@@ -296,7 +305,7 @@ CursorImage *CursorImageProvider::fetchCursor(const QString &themeName, const QS
296 return cursorImage;
305 return cursorImage;
297}
306}
298
307
299−CursorImage *CursorImageProvider::fetchCursorHelper(const QString &themeName, const QString &cursorName)
308+CursorImage *CursorImageProvider::fetchCursorHelper(const QString &themeName, const QString &cursorName, int cursorHeight)
300{
309{
301 if (cursorName == QLatin1String("blank")) {
310 if (cursorName == QLatin1String("blank")) {
302 return &m_blankCursorImage;
311 return &m_blankCursorImage;
@@ -306,7 +315,10 @@ CursorImage *CursorImageProvider::fetchCursorHelper(const QString &themeName, co
306 QMap<QString, CursorImage*> &themeCursors = m_cursors[themeName];
315 QMap<QString, CursorImage*> &themeCursors = m_cursors[themeName];
307
316
308 if (!themeCursors.contains(cursorName)) {
317 if (!themeCursors.contains(cursorName)) {
309− themeCursors[cursorName] = new XCursorImage(themeName, cursorName);
318+ themeCursors[cursorName] = new XCursorImage(themeName, cursorName, cursorHeight);
319+ } else if (themeCursors[cursorName]->requestedHeight != cursorHeight) {
320+ delete themeCursors.take(cursorName);
321+ themeCursors[cursorName] = new XCursorImage(themeName, cursorName, cursorHeight);
310 }
322 }
311
323
312 return themeCursors[cursorName];
324 return themeCursors[cursorName];
plugins/Cursor/CursorImageProvider.h+9−6
@@ -42,17 +42,20 @@ public:
42 int frameHeight{0};
42 int frameHeight{0};
43 int frameCount{1};
43 int frameCount{1};
44 int frameDuration{40};
44 int frameDuration{40};
45+
46+ // Requested height when creating this cursor.
47+ int requestedHeight{0};
45};
48};
46
49
47class XCursorImage : public CursorImage {
50class XCursorImage : public CursorImage {
48public:
51public:
49− XCursorImage(const QString &theme, const QString &file);
52+ XCursorImage(const QString &theme, const QString &file, int preferredCursorHeightPx);
50 virtual ~XCursorImage();
53 virtual ~XCursorImage();
51};
54};
52
55
53class BuiltInCursorImage : public CursorImage {
56class BuiltInCursorImage : public CursorImage {
54public:
57public:
55− BuiltInCursorImage();
58+ BuiltInCursorImage(int cursorHeight);
56};
59};
57
60
58class BlankCursorImage : public CursorImage {
61class BlankCursorImage : public CursorImage {
@@ -74,15 +77,15 @@ public:
74 static CursorImageProvider *instance() { return m_instance; }
77 static CursorImageProvider *instance() { return m_instance; }
75
78
76
79
77− QImage requestImage(const QString &cursorName, QSize *size, const QSize &requestedSize) override;
80+ QImage requestImage(const QString &cursorThemeAndNameAndHeight, QSize *size, const QSize &requestedSize) override;
78
81
79− CursorImage *fetchCursor(const QString &themeName, const QString &cursorName);
82+ CursorImage *fetchCursor(const QString &themeName, const QString &cursorName, int cursorHeight);
80
83
81 void setCustomCursor(const QCursor &customCursor);
84 void setCustomCursor(const QCursor &customCursor);
82
85
83private:
86private:
84− CursorImage *fetchCursor(const QString &cursorThemeAndName);
87+ CursorImage *fetchCursor(const QString &cursorThemeAndNameAndHeight);
85− CursorImage *fetchCursorHelper(const QString &themeName, const QString &cursorName);
88+ CursorImage *fetchCursorHelper(const QString &themeName, const QString &cursorName, int cursorHeight);
86
89
87 // themeName -> (cursorName -> cursorImage)
90 // themeName -> (cursorName -> cursorImage)
88 // TODO: discard old, unused, cursors
91 // TODO: discard old, unused, cursors
plugins/Dash/CardCreator.js+70−78
@@ -25,7 +25,7 @@ var kBackgroundLoaderCode = 'Loader {\n\
25 objectName: "backgroundLoader"; \n\
25 objectName: "backgroundLoader"; \n\
26 anchors.fill: parent; \n\
26 anchors.fill: parent; \n\
27 asynchronous: %3; \n\
27 asynchronous: %3; \n\
28− visible: status == Loader.Ready; \n\
28+ visible: status === Loader.Ready; \n\
29 sourceComponent: UbuntuShape { \n\
29 sourceComponent: UbuntuShape { \n\
30 objectName: "background"; \n\
30 objectName: "background"; \n\
31 radius: "medium"; \n\
31 radius: "medium"; \n\
@@ -75,7 +75,7 @@ var kArtProportionalShapeCode = 'ProportionalShape { \n\
75 aspect: UbuntuShape.DropShadow; \n\
75 aspect: UbuntuShape.DropShadow; \n\
76 }';
76 }';
77
77
78−// %1 is used as anchors of artShapeHolder
78+// %1 is used as anchors of artShapeLoader
79// %2 is used as image width
79// %2 is used as image width
80// %3 is used as image height
80// %3 is used as image height
81// %4 is whether the image should be visible
81// %4 is whether the image should be visible
@@ -83,42 +83,39 @@ var kArtProportionalShapeCode = 'ProportionalShape { \n\
83// %6 is the shape code we want to use
83// %6 is the shape code we want to use
84// %7 is injected as code to artImage
84// %7 is injected as code to artImage
85// %8 is used as image fallback
85// %8 is used as image fallback
86−var kArtShapeHolderCode = 'Item { \n\
86+var kArtShapeHolderCode = 'Loader { \n\
87− id: artShapeHolder; \n\
87+ id: artShapeLoader; \n\
88 height: root.fixedArtShapeSize.height; \n\
88 height: root.fixedArtShapeSize.height; \n\
89 width: root.fixedArtShapeSize.width; \n\
89 width: root.fixedArtShapeSize.width; \n\
90 anchors { %1 } \n\
90 anchors { %1 } \n\
91− Loader { \n\
92− id: artShapeLoader; \n\
93− objectName: "artShapeLoader"; \n\
91+ objectName: "artShapeLoader"; \n\
94− readonly property string cardArt: cardData && cardData["art"] || %8; \n\
92+ readonly property string cardArt: cardData && cardData["art"] || %8; \n\
95− onCardArtChanged: { if (item) { item.image.source = cardArt; } } \n\
93+ onCardArtChanged: { if (item) { item.image.source = cardArt; } } \n\
96− active: cardArt != ""; \n\
94+ active: cardArt != ""; \n\
97− asynchronous: %5; \n\
95+ asynchronous: %5; \n\
98− visible: status == Loader.Ready; \n\
96+ visible: status === Loader.Ready; \n\
99− sourceComponent: Item { \n\
97+ sourceComponent: Item { \n\
100− id: artShape; \n\
98+ id: artShape; \n\
101− objectName: "artShape"; \n\
99+ objectName: "artShape"; \n\
102− visible: image.status == Image.Ready; \n\
100+ visible: image.status === Image.Ready; \n\
103− readonly property alias image: artImage; \n\
101+ readonly property alias image: artImage; \n\
104− %6 \n\
102+ %6 \n\
105− width: root.fixedArtShapeSize.width; \n\
103+ width: root.fixedArtShapeSize.width; \n\
106− height: root.fixedArtShapeSize.height; \n\
104+ height: root.fixedArtShapeSize.height; \n\
107− CroppedImageMinimumSourceSize { \n\
105+ CroppedImageMinimumSourceSize { \n\
108− id: artImage; \n\
106+ id: artImage; \n\
109− objectName: "artImage"; \n\
107+ objectName: "artImage"; \n\
110− source: artShapeLoader.cardArt; \n\
108+ source: artShapeLoader.cardArt; \n\
111− asynchronous: %5; \n\
109+ asynchronous: %5; \n\
112− visible: %4; \n\
110+ visible: %4; \n\
113− width: %2; \n\
111+ width: %2; \n\
114− height: %3; \n\
112+ height: %3; \n\
115− %7 \n\
113+ %7 \n\
116− } \n\
117 } \n\
114 } \n\
118 } \n\
115 } \n\
119 }\n';
116 }\n';
120
117
121−// %1 is used as anchors of artShapeHolder
118+// %1 is used as anchors of artShapeLoader
122// %2 is used as image width
119// %2 is used as image width
123// %3 is used as image height
120// %3 is used as image height
124// %4 is whether the image should be visible
121// %4 is whether the image should be visible
@@ -126,37 +123,32 @@ var kArtShapeHolderCode = 'Item { \n\
126// %6 is the shape code we want to use
123// %6 is the shape code we want to use
127// %7 is injected as code to artImage
124// %7 is injected as code to artImage
128// %8 is used as image fallback
125// %8 is used as image fallback
129−var kArtShapeHolderCodeCardToolCard = 'Item { \n\
126+var kArtShapeHolderCodeCardToolCard = 'Loader { \n\
130− id: artShapeHolder; \n\
131− height: artShapeLoader.height; \n\
132− width: artShapeLoader.width; \n\
127+ id: artShapeLoader; \n\
133 anchors { %1 } \n\
128 anchors { %1 } \n\
134− Loader { \n\
135− id: artShapeLoader; \n\
136− objectName: "artShapeLoader"; \n\
129+ objectName: "artShapeLoader"; \n\
137− readonly property string cardArt: cardData && cardData["art"] || %8; \n\
130+ readonly property string cardArt: cardData && cardData["art"] || %8; \n\
138− onCardArtChanged: { if (item) { item.image.source = cardArt; } } \n\
131+ onCardArtChanged: { if (item) { item.image.source = cardArt; } } \n\
139− active: cardArt != ""; \n\
132+ active: cardArt != ""; \n\
140− asynchronous: %5; \n\
133+ asynchronous: %5; \n\
141− visible: status == Loader.Ready; \n\
134+ visible: status === Loader.Ready; \n\
142− sourceComponent: Item { \n\
135+ sourceComponent: Item { \n\
143− id: artShape; \n\
136+ id: artShape; \n\
144− objectName: "artShape"; \n\
137+ objectName: "artShape"; \n\
145− visible: image.status == Image.Ready; \n\
138+ visible: image.status === Image.Ready; \n\
146− readonly property alias image: artImage; \n\
139+ readonly property alias image: artImage; \n\
147− %6 \n\
140+ %6 \n\
148− width: image.status !== Image.Ready ? 0 : image.width; \n\
141+ width: image.status !== Image.Ready ? 0 : image.width; \n\
149− height: image.status !== Image.Ready ? 0 : image.height; \n\
142+ height: image.status !== Image.Ready ? 0 : image.height; \n\
150− CroppedImageMinimumSourceSize { \n\
143+ CroppedImageMinimumSourceSize { \n\
151− id: artImage; \n\
144+ id: artImage; \n\
152− objectName: "artImage"; \n\
145+ objectName: "artImage"; \n\
153− source: artShapeLoader.cardArt; \n\
146+ source: artShapeLoader.cardArt; \n\
154− asynchronous: %5; \n\
147+ asynchronous: %5; \n\
155− visible: %4; \n\
148+ visible: %4; \n\
156− width: %2; \n\
149+ width: %2; \n\
157− height: %3; \n\
150+ height: %3; \n\
158− %7 \n\
151+ %7 \n\
159− } \n\
160 } \n\
152 } \n\
161 } \n\
153 } \n\
162 }\n';
154 }\n';
@@ -214,10 +206,10 @@ var kAudioButtonCode = 'AbstractButton { \n\
214var kOverlayLoaderCode = 'Loader { \n\
206var kOverlayLoaderCode = 'Loader { \n\
215 id: overlayLoader; \n\
207 id: overlayLoader; \n\
216 readonly property real overlayHeight: %2 + units.gu(2); \n\
208 readonly property real overlayHeight: %2 + units.gu(2); \n\
217− anchors.fill: artShapeHolder; \n\
209+ anchors.fill: artShapeLoader; \n\
218 active: artShapeLoader.active && artShapeLoader.item && artShapeLoader.item.image.status === Image.Ready || false; \n\
210 active: artShapeLoader.active && artShapeLoader.item && artShapeLoader.item.image.status === Image.Ready || false; \n\
219 asynchronous: %1; \n\
211 asynchronous: %1; \n\
220− visible: showHeader && status == Loader.Ready; \n\
212+ visible: showHeader && status === Loader.Ready; \n\
221 sourceComponent: UbuntuShapeOverlay { \n\
213 sourceComponent: UbuntuShapeOverlay { \n\
222 id: overlay; \n\
214 id: overlay; \n\
223 property real luminance: Style.luminance(overlayColor); \n\
215 property real luminance: Style.luminance(overlayColor); \n\
@@ -274,7 +266,7 @@ var kMascotShapeLoaderCode = 'Loader { \n\
274 objectName: "mascotShapeLoader"; \n\
266 objectName: "mascotShapeLoader"; \n\
275 asynchronous: %2; \n\
267 asynchronous: %2; \n\
276 active: mascotImage.status === Image.Ready; \n\
268 active: mascotImage.status === Image.Ready; \n\
277− visible: showHeader && active && status == Loader.Ready; \n\
269+ visible: showHeader && active && status === Loader.Ready; \n\
278 width: units.gu(6); \n\
270 width: units.gu(6); \n\
279 height: units.gu(5.625); \n\
271 height: units.gu(5.625); \n\
280 sourceComponent: UbuntuShape { image: mascotImage } \n\
272 sourceComponent: UbuntuShape { image: mascotImage } \n\
@@ -592,15 +584,15 @@ function cardString(template, components, isCardTool, artShapeStyle) {
592
584
593 var headerVerticalAnchors;
585 var headerVerticalAnchors;
594 if (headerAsOverlay) {
586 if (headerAsOverlay) {
595− headerVerticalAnchors = 'bottom: artShapeHolder.bottom; \n\
587+ headerVerticalAnchors = 'bottom: artShapeLoader.bottom; \n\
596 bottomMargin: units.gu(1);\n';
588 bottomMargin: units.gu(1);\n';
597 } else {
589 } else {
598 if (hasArt) {
590 if (hasArt) {
599 if (isHorizontal) {
591 if (isHorizontal) {
600− headerVerticalAnchors = 'top: artShapeHolder.top; \n\
592+ headerVerticalAnchors = 'top: artShapeLoader.top; \n\
601 topMargin: units.gu(1);\n';
593 topMargin: units.gu(1);\n';
602 } else {
594 } else {
603− headerVerticalAnchors = 'top: artShapeHolder.bottom; \n\
595+ headerVerticalAnchors = 'top: artShapeLoader.bottom; \n\
604 topMargin: units.gu(1);\n';
596 topMargin: units.gu(1);\n';
605 }
597 }
606 } else {
598 } else {
@@ -612,7 +604,7 @@ function cardString(template, components, isCardTool, artShapeStyle) {
612 var headerLeftAnchor;
604 var headerLeftAnchor;
613 var headerLeftAnchorHasMargin = false;
605 var headerLeftAnchorHasMargin = false;
614 if (isHorizontal && hasArt) {
606 if (isHorizontal && hasArt) {
615− headerLeftAnchor = 'left: artShapeHolder.right; \n\
607+ headerLeftAnchor = 'left: artShapeLoader.right; \n\
616 leftMargin: units.gu(1);\n';
608 leftMargin: units.gu(1);\n';
617 headerLeftAnchorHasMargin = true;
609 headerLeftAnchorHasMargin = true;
618 } else if (isHorizontal && isAudio) {
610 } else if (isHorizontal && isAudio) {
@@ -866,7 +858,7 @@ function cardString(template, components, isCardTool, artShapeStyle) {
866 var audioButtonWidth;
858 var audioButtonWidth;
867 var audioButtonHeight;
859 var audioButtonHeight;
868 if (hasArt) {
860 if (hasArt) {
869− audioButtonAnchorsFill = 'artShapeHolder';
861+ audioButtonAnchorsFill = 'artShapeLoader';
870 audioButtonWidth = 'undefined';
862 audioButtonWidth = 'undefined';
871 audioButtonHeight = 'undefined';
863 audioButtonHeight = 'undefined';
872 } else {
864 } else {
@@ -880,15 +872,15 @@ function cardString(template, components, isCardTool, artShapeStyle) {
880
872
881 if (hasSummary) {
873 if (hasSummary) {
882 var summaryTopAnchor;
874 var summaryTopAnchor;
883− if (isHorizontal && hasArt) summaryTopAnchor = 'artShapeHolder.bottom';
875+ if (isHorizontal && hasArt) summaryTopAnchor = 'artShapeLoader.bottom';
884− else if (headerAsOverlay && hasArt) summaryTopAnchor = 'artShapeHolder.bottom';
876+ else if (headerAsOverlay && hasArt) summaryTopAnchor = 'artShapeLoader.bottom';
885 else if (hasHeaderRow) summaryTopAnchor = 'row.bottom';
877 else if (hasHeaderRow) summaryTopAnchor = 'row.bottom';
886 else if (hasTitleContainer) summaryTopAnchor = 'headerTitleContainer.bottom';
878 else if (hasTitleContainer) summaryTopAnchor = 'headerTitleContainer.bottom';
887 else if (hasMascot) summaryTopAnchor = 'mascotImage.bottom';
879 else if (hasMascot) summaryTopAnchor = 'mascotImage.bottom';
888 else if (hasAttributes) summaryTopAnchor = 'attributesRow.bottom';
880 else if (hasAttributes) summaryTopAnchor = 'attributesRow.bottom';
889 else if (hasSubtitle) summaryTopAnchor = 'subtitleLabel.bottom';
881 else if (hasSubtitle) summaryTopAnchor = 'subtitleLabel.bottom';
890 else if (hasTitle) summaryTopAnchor = 'titleLabel.bottom';
882 else if (hasTitle) summaryTopAnchor = 'titleLabel.bottom';
891− else if (hasArt) summaryTopAnchor = 'artShapeHolder.bottom';
883+ else if (hasArt) summaryTopAnchor = 'artShapeLoader.bottom';
892 else summaryTopAnchor = 'parent.top';
884 else summaryTopAnchor = 'parent.top';
893
885
894 var summaryColor;
886 var summaryColor;
@@ -908,15 +900,15 @@ function cardString(template, components, isCardTool, artShapeStyle) {
908 var socialTopAnchor;
900 var socialTopAnchor;
909
901
910 if (hasSummary) socialTopAnchor = 'summary.bottom;';
902 if (hasSummary) socialTopAnchor = 'summary.bottom;';
911− else if (isHorizontal && hasArt) socialTopAnchor = 'artShapeHolder.bottom;';
903+ else if (isHorizontal && hasArt) socialTopAnchor = 'artShapeLoader.bottom;';
912− else if (headerAsOverlay && hasArt) socialTopAnchor = 'artShapeHolder.bottom;';
904+ else if (headerAsOverlay && hasArt) socialTopAnchor = 'artShapeLoader.bottom;';
913 else if (hasHeaderRow) socialTopAnchor = 'row.bottom;';
905 else if (hasHeaderRow) socialTopAnchor = 'row.bottom;';
914 else if (hasTitleContainer) socialTopAnchor = 'headerTitleContainer.bottom;';
906 else if (hasTitleContainer) socialTopAnchor = 'headerTitleContainer.bottom;';
915 else if (hasMascot) socialTopAnchor = 'mascotImage.bottom;';
907 else if (hasMascot) socialTopAnchor = 'mascotImage.bottom;';
916 else if (hasAttributes) socialTopAnchor = 'attributesRow.bottom;';
908 else if (hasAttributes) socialTopAnchor = 'attributesRow.bottom;';
917 else if (hasSubtitle) socialTopAnchor = 'subtitleLabel.bottom;';
909 else if (hasSubtitle) socialTopAnchor = 'subtitleLabel.bottom;';
918 else if (hasTitle) socialTopAnchor = 'titleLabel.bottom;';
910 else if (hasTitle) socialTopAnchor = 'titleLabel.bottom;';
919− else if (hasArt) socialTopAnchor = 'artShapeHolder.bottom;';
911+ else if (hasArt) socialTopAnchor = 'artShapeLoader.bottom;';
920 else socialTopAnchor = 'parent.top';
912 else socialTopAnchor = 'parent.top';
921
913
922 socialAnchors = 'top: ' + socialTopAnchor + ' left: parent.left; right: parent.right; topMargin: units.gu(1);'
914 socialAnchors = 'top: ' + socialTopAnchor + ' left: parent.left; right: parent.right; topMargin: units.gu(1);'
@@ -936,7 +928,7 @@ function cardString(template, components, isCardTool, artShapeStyle) {
936 if (hasBackground) {
928 if (hasBackground) {
937 touchdownAnchors = 'fill: backgroundLoader';
929 touchdownAnchors = 'fill: backgroundLoader';
938 } else if (touchdownOnArtShape) {
930 } else if (touchdownOnArtShape) {
939− touchdownAnchors = 'fill: artShapeHolder';
931+ touchdownAnchors = 'fill: artShapeLoader';
940 } else {
932 } else {
941 touchdownAnchors = 'fill: root'
933 touchdownAnchors = 'fill: root'
942 }
934 }
@@ -951,7 +943,7 @@ function cardString(template, components, isCardTool, artShapeStyle) {
951 } else if (isAudio) {
943 } else if (isAudio) {
952 implicitHeight += 'audioButton.height;\n';
944 implicitHeight += 'audioButton.height;\n';
953 } else if (headerAsOverlay) {
945 } else if (headerAsOverlay) {
954− implicitHeight += 'artShapeHolder.height;\n';
946+ implicitHeight += 'artShapeLoader.height;\n';
955 } else if (hasHeaderRow) {
947 } else if (hasHeaderRow) {
956 implicitHeight += 'row.y + row.height + units.gu(1);\n';
948 implicitHeight += 'row.y + row.height + units.gu(1);\n';
957 } else if (hasMascot) {
949 } else if (hasMascot) {
@@ -965,7 +957,7 @@ function cardString(template, components, isCardTool, artShapeStyle) {
965 } else if (hasTitle) {
957 } else if (hasTitle) {
966 implicitHeight += 'titleLabel.y + titleLabel.height + units.gu(1);\n';
958 implicitHeight += 'titleLabel.y + titleLabel.height + units.gu(1);\n';
967 } else if (hasArt) {
959 } else if (hasArt) {
968− implicitHeight += 'artShapeHolder.height;\n';
960+ implicitHeight += 'artShapeLoader.height;\n';
969 } else {
961 } else {
970 implicitHeight = '';
962 implicitHeight = '';
971 }
963 }
plugins/Dash/abstractdashview.cpp+13
@@ -16,6 +16,8 @@
16
16
17#include "abstractdashview.h"
17#include "abstractdashview.h"
18
18
19+#include <private/qquickitem_p.h>
20+
19AbstractDashView::AbstractDashView()
21AbstractDashView::AbstractDashView()
20 : m_delegateModel(nullptr)
22 : m_delegateModel(nullptr)
21 , m_asyncRequestedIndex(-1)
23 , m_asyncRequestedIndex(-1)
@@ -249,6 +251,7 @@ QQuickItem *AbstractDashView::createItem(int modelIndex, bool asynchronous)
249 }
251 }
250 return nullptr;
252 return nullptr;
251 } else {
253 } else {
254+ QQuickItemPrivate::get(item)->addItemChangeListener(this, QQuickItemPrivate::Geometry);
252 addItemToView(modelIndex, item);
255 addItemToView(modelIndex, item);
253 return item;
256 return item;
254 }
257 }
@@ -256,6 +259,7 @@ QQuickItem *AbstractDashView::createItem(int modelIndex, bool asynchronous)
256
259
257void AbstractDashView::releaseItem(QQuickItem *item)
260void AbstractDashView::releaseItem(QQuickItem *item)
258{
261{
262+ QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
259 QQmlDelegateModel::ReleaseFlags flags = m_delegateModel->release(item);
263 QQmlDelegateModel::ReleaseFlags flags = m_delegateModel->release(item);
260 if (flags & QQmlDelegateModel::Destroyed) {
264 if (flags & QQmlDelegateModel::Destroyed) {
261 item->setParentItem(nullptr);
265 item->setParentItem(nullptr);
@@ -294,6 +298,15 @@ void AbstractDashView::onModelUpdated(const QQmlChangeSet &changeSet, bool reset
294 cleanupExistingItems();
298 cleanupExistingItems();
295 } else {
299 } else {
296 processModelRemoves(changeSet.removes());
300 processModelRemoves(changeSet.removes());
301+
302+ // The current AbstractDashViews do not support insertions that are not at the end
303+ // so reset if that happens
304+ Q_FOREACH(const QQmlChangeSet::Change insert, changeSet.inserts()) {
305+ if (insert.index < m_delegateModel->count() - 1) {
306+ cleanupExistingItems();
307+ break;
308+ }
309+ }
297 }
310 }
298 polish();
311 polish();
299}
312}
plugins/Dash/abstractdashview.h+2−1
@@ -23,9 +23,10 @@ class QAbstractItemModel;
23class QQmlComponent;
23class QQmlComponent;
24
24
25#include <private/qqmldelegatemodel_p.h>
25#include <private/qqmldelegatemodel_p.h>
26+#include <private/qquickitemchangelistener_p.h>
26#include <qqmlinfo.h>
27#include <qqmlinfo.h>
27
28
28−class AbstractDashView : public QQuickItem
29+class AbstractDashView : public QQuickItem, public QQuickItemChangeListener
29{
30{
30 Q_OBJECT
31 Q_OBJECT
31
32
plugins/Dash/verticaljournal.cpp+9−1
@@ -96,7 +96,7 @@ void VerticalJournal::findTopModelIndexToAdd(int *modelIndex, qreal *yPos)
96
96
97 if (*modelIndex > 0) {
97 if (*modelIndex > 0) {
98 Q_ASSERT(m_indexColumnMap.contains(*modelIndex));
98 Q_ASSERT(m_indexColumnMap.contains(*modelIndex));
99− while (m_indexColumnMap[*modelIndex] != columnToAddTo) {
99+ while (*modelIndex > 0 && m_indexColumnMap[*modelIndex] != columnToAddTo) {
100 // We found out that we have to add to columnToAddTo
100 // We found out that we have to add to columnToAddTo
101 // and thought that we had to add *modelIndex, but history tells
101 // and thought that we had to add *modelIndex, but history tells
102 // it is not correct, so find up from *modelIndex until we found the index
102 // it is not correct, so find up from *modelIndex until we found the index
@@ -270,3 +270,11 @@ void VerticalJournal::processModelRemoves(const QVector<QQmlChangeSet::Change> &
270 }
270 }
271 }
271 }
272}
272}
273+
274+void VerticalJournal::itemGeometryChanged(QQuickItem * /*item*/, const QRectF &newGeometry, const QRectF &oldGeometry)
275+{
276+ const qreal heightDiff = newGeometry.height() - oldGeometry.height();
277+ if (heightDiff != 0) {
278+ relayout();
279+ }
280+}
plugins/Dash/verticaljournal.h+3
@@ -65,6 +65,9 @@ public:
65 qreal columnWidth() const;
65 qreal columnWidth() const;
66 void setColumnWidth(qreal columnWidth);
66 void setColumnWidth(qreal columnWidth);
67
67
68+protected:
69+ void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) override;
70+
68Q_SIGNALS:
71Q_SIGNALS:
69 void columnWidthChanged();
72 void columnWidthChanged();
70
73
plugins/Unity/Indicators/indicatorsmanager.cpp+4−6
@@ -114,10 +114,6 @@ void IndicatorsManager::loadFile(const QFileInfo& file_info)
114 QSettings indicator_settings(file_info.absoluteFilePath(), QSettings::IniFormat, this);
114 QSettings indicator_settings(file_info.absoluteFilePath(), QSettings::IniFormat, this);
115 const QString name = indicator_settings.value(QStringLiteral("Indicator Service/Name")).toString();
115 const QString name = indicator_settings.value(QStringLiteral("Indicator Service/Name")).toString();
116
116
117− if (m_platform.isPC() && name == QLatin1String("indicator-keyboard")) {
118− return; // convergence: skip this indicator until it works in Mir
119− }
120−
121 auto iter = m_indicatorsData.constFind(name);
117 auto iter = m_indicatorsData.constFind(name);
122 if (iter != m_indicatorsData.constEnd())
118 if (iter != m_indicatorsData.constEnd())
123 {
119 {
@@ -285,13 +281,15 @@ Indicator::Ptr IndicatorsManager::indicator(const QString& indicator_name)
285
281
286 // convergence:
282 // convergence:
287 // 1) enable session indicator
283 // 1) enable session indicator
284+ // 2) enable keyboard indicator
288− // 2) on a PC, switch the battery/power indicator to desktop mode,
285+ // 3) on a PC, switch the battery/power indicator to desktop mode,
289 // can't control brightness for now and phone-on-desktop broken (FIXME)
286 // can't control brightness for now and phone-on-desktop broken (FIXME)
290 //
287 //
291 // The rest of the indicators respect their default profile (which is "phone", even on desktop PCs)
288 // The rest of the indicators respect their default profile (which is "phone", even on desktop PCs)
292 if ((new_indicator->identifier() == QStringLiteral("indicator-session"))
289 if ((new_indicator->identifier() == QStringLiteral("indicator-session"))
290+ || new_indicator->identifier() == QStringLiteral("indicator-keyboard")
293 || (new_indicator->identifier() == QStringLiteral("indicator-power") && m_platform.isPC())) {
291 || (new_indicator->identifier() == QStringLiteral("indicator-power") && m_platform.isPC())) {
294− new_indicator->setProfile(m_profile.replace(QStringLiteral("phone"), QStringLiteral("desktop")));
292+ new_indicator->setProfile(QString(m_profile).replace(QStringLiteral("phone"), QStringLiteral("desktop")));
295 } else {
293 } else {
296 new_indicator->setProfile(m_profile);
294 new_indicator->setProfile(m_profile);
297 }
295 }
plugins/Wizard/System.cpp+1
@@ -105,6 +105,7 @@ void System::updateSessionLocale(const QString &locale)
105 initctl stop scope-registry; \
105 initctl stop scope-registry; \
106 initctl stop smart-scopes-proxy; \
106 initctl stop smart-scopes-proxy; \
107 initctl emit --no-wait indicator-services-start; \
107 initctl emit --no-wait indicator-services-start; \
108+ initctl restart --no-wait ubuntu-location-service-trust-stored; \
108 initctl restart --no-wait maliit-server; \
109 initctl restart --no-wait maliit-server; \
109 initctl restart --no-wait indicator-messages; \
110 initctl restart --no-wait indicator-messages; \
110 initctl restart --no-wait unity8-dash\""));
111 initctl restart --no-wait unity8-dash\""));
po/unity8.pot+21−21
@@ -8,7 +8,7 @@ msgid ""
8msgstr ""
8msgstr ""
9"Project-Id-Version: unity8\n"
9"Project-Id-Version: unity8\n"
10"Report-Msgid-Bugs-To: \n"
10"Report-Msgid-Bugs-To: \n"
11−"POT-Creation-Date: 2016-07-28 08:17+0000\n"
11+"POT-Creation-Date: 2016-08-03 15:56+0000\n"
12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14"Language-Team: LANGUAGE <LL@li.org>\n"
14"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -54,65 +54,65 @@ msgid_plural "Please wait %1 minutes and then try again…"
54msgstr[0] ""
54msgstr[0] ""
55msgstr[1] ""
55msgstr[1] ""
56
56
57−#: qml/Components/Dialogs.qml:144
57+#: qml/Components/Dialogs.qml:149
58msgctxt "Title: Lock/Log out dialog"
58msgctxt "Title: Lock/Log out dialog"
59msgid "Log out"
59msgid "Log out"
60msgstr ""
60msgstr ""
61
61
62−#: qml/Components/Dialogs.qml:145
62+#: qml/Components/Dialogs.qml:150
63msgid "Are you sure you want to log out?"
63msgid "Are you sure you want to log out?"
64msgstr ""
64msgstr ""
65
65
66−#: qml/Components/Dialogs.qml:147
66+#: qml/Components/Dialogs.qml:152
67msgctxt "Button: Lock the system"
67msgctxt "Button: Lock the system"
68msgid "Lock"
68msgid "Lock"
69msgstr ""
69msgstr ""
70
70
71−#: qml/Components/Dialogs.qml:154
71+#: qml/Components/Dialogs.qml:159
72msgctxt "Button: Log out from the system"
72msgctxt "Button: Log out from the system"
73msgid "Log Out"
73msgid "Log Out"
74msgstr ""
74msgstr ""
75
75
76−#: qml/Components/Dialogs.qml:161 qml/Components/Dialogs.qml:217
76+#: qml/Components/Dialogs.qml:166 qml/Components/Dialogs.qml:222
77−#: qml/Dash/DashPageHeader.qml:295 qml/Wizard/Pages/passcode-confirm.qml:32
77+#: qml/Dash/DashPageHeader.qml:322 qml/Wizard/Pages/passcode-confirm.qml:32
78#: qml/Wizard/Pages/passcode-set.qml:32
78#: qml/Wizard/Pages/passcode-set.qml:32
79msgid "Cancel"
79msgid "Cancel"
80msgstr ""
80msgstr ""
81
81
82−#: qml/Components/Dialogs.qml:173
82+#: qml/Components/Dialogs.qml:178
83msgctxt "Title: Reboot dialog"
83msgctxt "Title: Reboot dialog"
84msgid "Reboot"
84msgid "Reboot"
85msgstr ""
85msgstr ""
86
86
87−#: qml/Components/Dialogs.qml:174
87+#: qml/Components/Dialogs.qml:179
88msgid "Are you sure you want to reboot?"
88msgid "Are you sure you want to reboot?"
89msgstr ""
89msgstr ""
90
90
91−#: qml/Components/Dialogs.qml:176
91+#: qml/Components/Dialogs.qml:181
92msgid "No"
92msgid "No"
93msgstr ""
93msgstr ""
94
94
95−#: qml/Components/Dialogs.qml:182
95+#: qml/Components/Dialogs.qml:187
96msgid "Yes"
96msgid "Yes"
97msgstr ""
97msgstr ""
98
98
99−#: qml/Components/Dialogs.qml:197
99+#: qml/Components/Dialogs.qml:202
100msgctxt "Title: Power off/Restart dialog"
100msgctxt "Title: Power off/Restart dialog"
101msgid "Power"
101msgid "Power"
102msgstr ""
102msgstr ""
103
103
104−#: qml/Components/Dialogs.qml:198
104+#: qml/Components/Dialogs.qml:203
105msgid ""
105msgid ""
106"Are you sure you would like\n"
106"Are you sure you would like\n"
107"to power off?"
107"to power off?"
108msgstr ""
108msgstr ""
109
109
110−#: qml/Components/Dialogs.qml:200
110+#: qml/Components/Dialogs.qml:205
111msgctxt "Button: Power off the system"
111msgctxt "Button: Power off the system"
112msgid "Power off"
112msgid "Power off"
113msgstr ""
113msgstr ""
114
114
115−#: qml/Components/Dialogs.qml:209
115+#: qml/Components/Dialogs.qml:214
116msgctxt "Button: Restart the system"
116msgctxt "Button: Restart the system"
117msgid "Restart"
117msgid "Restart"
118msgstr ""
118msgstr ""
@@ -329,26 +329,26 @@ msgstr ""
329msgid "Preview Share Item"
329msgid "Preview Share Item"
330msgstr ""
330msgstr ""
331
331
332−#: qml/Dash/DashPageHeader.qml:333
332+#: qml/Dash/DashPageHeader.qml:360
333msgctxt "Button: Open the Ubuntu Store"
333msgctxt "Button: Open the Ubuntu Store"
334msgid "Store"
334msgid "Store"
335msgstr ""
335msgstr ""
336
336
337−#: qml/Dash/DashPageHeader.qml:340
337+#: qml/Dash/DashPageHeader.qml:367
338msgctxt "Button: Start a search in the current dash scope"
338msgctxt "Button: Start a search in the current dash scope"
339msgid "Search"
339msgid "Search"
340msgstr ""
340msgstr ""
341
341
342−#: qml/Dash/DashPageHeader.qml:350
342+#: qml/Dash/DashPageHeader.qml:377
343msgctxt "Button: Show the current dash scope settings"
343msgctxt "Button: Show the current dash scope settings"
344msgid "Settings"
344msgid "Settings"
345msgstr ""
345msgstr ""
346
346
347−#: qml/Dash/DashPageHeader.qml:357
347+#: qml/Dash/DashPageHeader.qml:384
348msgid "Remove from Favorites"
348msgid "Remove from Favorites"
349msgstr ""
349msgstr ""
350
350
351−#: qml/Dash/DashPageHeader.qml:357
351+#: qml/Dash/DashPageHeader.qml:384
352msgid "Add to Favorites"
352msgid "Add to Favorites"
353msgstr ""
353msgstr ""
354
354
@@ -436,7 +436,7 @@ msgstr ""
436msgid "Unlock"
436msgid "Unlock"
437msgstr ""
437msgstr ""
438
438
439−#: qml/Greeter/Greeter.qml:555
439+#: qml/Greeter/Greeter.qml:561
440msgid "Try again"
440msgid "Try again"
441msgstr ""
441msgstr ""
442
442
qml/Components/Dialogs.qml+7−2
@@ -15,7 +15,7 @@
15 */
15 */
16
16
17import QtQuick 2.4
17import QtQuick 2.4
18−
18+import QtQuick.Window 2.2
19import Unity.Application 0.1
19import Unity.Application 0.1
20import Unity.Session 0.1
20import Unity.Session 0.1
21import GlobalShortcut 1.0
21import GlobalShortcut 1.0
@@ -41,6 +41,7 @@ Item {
41 }
41 }
42 }
42 }
43 property string usageScenario
43 property string usageScenario
44+ property size screenSize: Qt.size(Screen.width, Screen.height)
44
45
45 signal powerOffClicked();
46 signal powerOffClicked();
46
47
@@ -49,7 +50,11 @@ Item {
49 }
50 }
50
51
51 onUsageScenarioChanged: {
52 onUsageScenarioChanged: {
53+ // if we let the user switch manually to desktop mode, don't display the warning dialog
54+ // see MenuItemFactory.qml, for the Desktop Mode switch logic
55+ var isTabletSize = Math.min(screenSize.width, screenSize.height) > units.gu(60);
56+
52− if (usageScenario != "desktop" && legacyAppsModel.count > 0 && !d.modeSwitchWarningPopup) {
57+ if (usageScenario != "desktop" && legacyAppsModel.count > 0 && !d.modeSwitchWarningPopup && !isTabletSize) {
53 var comp = Qt.createComponent(Qt.resolvedUrl("ModeSwitchWarningDialog.qml"))
58 var comp = Qt.createComponent(Qt.resolvedUrl("ModeSwitchWarningDialog.qml"))
54 d.modeSwitchWarningPopup = comp.createObject(root, {model: legacyAppsModel});
59 d.modeSwitchWarningPopup = comp.createObject(root, {model: legacyAppsModel});
55 d.modeSwitchWarningPopup.forceClose.connect(function() {
60 d.modeSwitchWarningPopup.forceClose.connect(function() {
qml/Components/KeymapSwitcher.qml+27−1
@@ -17,6 +17,7 @@
17import QtQuick 2.4
17import QtQuick 2.4
18import AccountsService 0.1
18import AccountsService 0.1
19import GlobalShortcut 1.0
19import GlobalShortcut 1.0
20+import QMenuModel 0.1
20import Unity.Application 0.1
21import Unity.Application 0.1
21
22
22QtObject {
23QtObject {
@@ -36,7 +37,8 @@ QtObject {
36
37
37 readonly property var keymaps: AccountsService.keymaps
38 readonly property var keymaps: AccountsService.keymaps
38 readonly property int keymapCount: keymaps.length
39 readonly property int keymapCount: keymaps.length
40+ // default keymap, either the one remembered by the indicator, or the 1st one selected by user
39− property int currentKeymapIndex: 0
41+ property int currentKeymapIndex: actionGroup.currentAction.valid ? actionGroup.currentAction.state : 0
40 readonly property string currentKeymap: keymaps[currentKeymapIndex]
42 readonly property string currentKeymap: keymaps[currentKeymapIndex]
41
43
42 function nextKeymap() {
44 function nextKeymap() {
@@ -62,4 +64,28 @@ QtObject {
62 property: "keymap"
64 property: "keymap"
63 value: root.currentKeymap
65 value: root.currentKeymap
64 }
66 }
67+
68+ // indicator
69+ property QDBusActionGroup actionGroup: QDBusActionGroup {
70+ busType: DBus.SessionBus
71+ busName: "com.canonical.indicator.keyboard"
72+ objectPath: "/com/canonical/indicator/keyboard"
73+
74+ property variant currentAction: action("current")
75+ property variant activeAction: action("active")
76+
77+ Component.onCompleted: actionGroup.start();
78+ }
79+
80+ onCurrentKeymapIndexChanged: {
81+ actionGroup.currentAction.updateState(currentKeymapIndex);
82+ }
83+
84+ readonly property int activeActionState: actionGroup.activeAction.valid ? actionGroup.activeAction.state : -1
85+
86+ onActiveActionStateChanged: {
87+ if (activeActionState != -1) {
88+ currentKeymapIndex = activeActionState;
89+ }
90+ }
65}
91}
qml/Dash/DashNavigationList.qml+48−45
@@ -53,61 +53,64 @@ Item {
53 Repeater {
53 Repeater {
54 model: navigation && navigation.loaded ? navigation : null
54 model: navigation && navigation.loaded ? navigation : null
55 clip: true
55 clip: true
56− // FIXME Move to ListItem (and remove import) once 1556971 is fixed
57− delegate: ListItems.Empty {
56+ delegate: Loader {
58− objectName: root.objectName + "child" + index
57+ asynchronous: true
59 height: root.itemHeight
58 height: root.itemHeight
60 width: column.width
59 width: column.width
61− anchors {
60+ // FIXME Move to ListItem (and remove import) once 1556971 is fixed
62− left: column.left
61+ sourceComponent: ListItems.Empty {
63− right: column.right
62+ objectName: root.objectName + "child" + index
64− leftMargin: units.gu(2)
65− rightMargin: units.gu(2)
66− }
67−
68− onClicked: root.enterNavigation(navigationId, allLabel != "" ? allLabel : label, hasChildren)
69−
70− Icon {
71− id: leftIcon
72 anchors {
63 anchors {
73− verticalCenter: parent.verticalCenter
74 left: parent.left
64 left: parent.left
65+ right: parent.right
66+ leftMargin: units.gu(2)
67+ rightMargin: units.gu(2)
75 }
68 }
76− height: units.gu(2)
77− width: height
78− name: "tick"
79− color: "#3EB34F"
80− visible: isActive
81− }
82
69
83− Label {
70+ onClicked: root.enterNavigation(navigationId, allLabel != "" ? allLabel : label, hasChildren)
84− anchors {
71+
85− verticalCenter: parent.verticalCenter
72+ Icon {
86− left: leftIcon.right
73+ id: leftIcon
87− leftMargin: units.gu(1)
74+ anchors {
88− right: rightIcon.left
75+ verticalCenter: parent.verticalCenter
76+ left: parent.left
77+ }
89− rightMargin: units.gu(2)
78+ height: units.gu(2)
79+ width: height
80+ name: "tick"
81+ color: "#3EB34F"
82+ visible: isActive
90 }
83 }
91− text: label
92− color: isActive ? "#333333" : "#888888"
93− wrapMode: Text.Wrap
94− maximumLineCount: 2
95− elide: Text.ElideMiddle
96− }
97
84
85+ Label {
86+ anchors {
87+ verticalCenter: parent.verticalCenter
88+ left: leftIcon.right
89+ leftMargin: units.gu(1)
90+ right: rightIcon.left
91+ rightMargin: units.gu(2)
92+ }
93+ text: label
94+ color: isActive ? "#333333" : "#888888"
95+ wrapMode: Text.Wrap
96+ maximumLineCount: 2
97+ elide: Text.ElideMiddle
98+ }
99+
98− Icon {
100+ Icon {
99− id: rightIcon
101+ id: rightIcon
100− anchors {
102+ anchors {
101− verticalCenter: parent.verticalCenter
103+ verticalCenter: parent.verticalCenter
102− right: parent.right
104+ right: parent.right
105+ }
106+ height: units.gu(2)
107+ width: height
108+ name: "go-next"
109+ visible: hasChildren
103 }
110 }
104− height: units.gu(2)
105− width: height
106− name: "go-next"
107− visible: hasChildren
108− }
109
111
110− divider.visible: index != navigation.count - 1
112+ divider.visible: index != navigation.count - 1
113+ }
111 }
114 }
112 }
115 }
113 }
116 }
qml/Dash/DashPageHeader.qml+49−22
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013,2015 Canonical, Ltd.
2+ * Copyright (C) 2013,2015,2016 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -81,7 +81,8 @@ Item {
81 function closePopup(keepFocus, keepSearch) {
81 function closePopup(keepFocus, keepSearch) {
82 if (extraPanel.visible) {
82 if (extraPanel.visible) {
83 extraPanel.visible = false;
83 extraPanel.visible = false;
84+ }
84− } else if (!keepFocus) {
85+ if (!keepFocus) {
85 unfocus(keepSearch);
86 unfocus(keepSearch);
86 }
87 }
87 if (!keepSearch && !searchTextField.text && !root.navigationTag && searchHistory.count == 0) {
88 if (!keepSearch && !searchTextField.text && !root.navigationTag && searchHistory.count == 0) {
@@ -131,47 +132,62 @@ Item {
131 anchors { fill: parent; margins: units.gu(1); bottomMargin: units.gu(3) + (extraPanel ? extraPanel.height : 0) }
132 anchors { fill: parent; margins: units.gu(1); bottomMargin: units.gu(3) + (extraPanel ? extraPanel.height : 0) }
132 visible: headerContainer.showSearch
133 visible: headerContainer.showSearch
133 onPressed: {
134 onPressed: {
134− extraPanel.visible = false;
135 closePopup(/* keepFocus */false);
135 closePopup(/* keepFocus */false);
136 mouse.accepted = false;
136 mouse.accepted = false;
137 }
137 }
138 }
138 }
139
139
140− Flickable {
140+ Item {
141 id: headerContainer
141 id: headerContainer
142 objectName: "headerContainer"
142 objectName: "headerContainer"
143− clip: contentY < height
144 anchors { left: parent.left; top: parent.top; right: parent.right }
143 anchors { left: parent.left; top: parent.top; right: parent.right }
145 height: header.__styleInstance.contentHeight
144 height: header.__styleInstance.contentHeight
146− contentHeight: headersColumn.height
147− interactive: false
148− contentY: showSearch ? 0 : height
149
145
150 property bool showSearch: false
146 property bool showSearch: false
151
147
152− Background {
148+ state: headerContainer.showSearch ? "search" : ""
153− id: background
149+
154− objectName: "headerBackground"
150+ states: State {
155− style: scopeStyle.headerBackground
151+ name: "search"
152+
153+ AnchorChanges {
154+ target: headersColumn
155+ anchors.top: parent.top
156+ anchors.bottom: undefined
157+ }
156 }
158 }
157
159
158− Behavior on contentY {
160+ transitions: Transition {
159− UbuntuNumberAnimation {
160− id: openSearchAnimation
161+ id: openSearchAnimation
161− property bool openPopup: false
162+ AnchorAnimation {
163+ duration: UbuntuAnimation.FastDuration
164+ easing: UbuntuAnimation.StandardEasing
165+ }
162
166
167+ property bool openPopup: false
168+
163− onRunningChanged: {
169+ onRunningChanged: {
170+ headerContainer.clip = running;
164− if (!running && openSearchAnimation.openPopup) {
171+ if (!running && openSearchAnimation.openPopup) {
165− openSearchAnimation.openPopup = false;
172+ openSearchAnimation.openPopup = false;
166− root.openPopup();
173+ root.openPopup();
167− }
168 }
174 }
169 }
175 }
170 }
176 }
171
177
178+ Background {
179+ id: background
180+ objectName: "headerBackground"
181+ style: scopeStyle.headerBackground
182+ }
183+
172 Column {
184 Column {
173 id: headersColumn
185 id: headersColumn
186+ anchors {
187+ left: parent.left
174− anchors { left: parent.left; right: parent.right }
188+ right: parent.right
189+ bottom: parent.bottom
190+ }
175
191
176 PageHeader {
192 PageHeader {
177 id: searchHeader
193 id: searchHeader
@@ -187,6 +203,15 @@ Item {
187 contents: Item {
203 contents: Item {
188 anchors.fill: parent
204 anchors.fill: parent
189
205
206+ Keys.onEscapePressed: { // clear the search text, dismiss the search in the second step
207+ if (searchTextField.text != "") {
208+ root.clearSearch(true);
209+ } else {
210+ root.clearSearch(false);
211+ headerContainer.showSearch = false;
212+ }
213+ }
214+
190 TextField {
215 TextField {
191 id: searchTextField
216 id: searchTextField
192 objectName: "searchTextField"
217 objectName: "searchTextField"
@@ -226,6 +251,8 @@ Item {
226 anchors.fill: parent
251 anchors.fill: parent
227 anchors.margins: units.gu(1)
252 anchors.margins: units.gu(1)
228 source: "image://theme/clear"
253 source: "image://theme/clear"
254+ sourceSize.width: width
255+ sourceSize.height: height
229 opacity: parent.enabled
256 opacity: parent.enabled
230 visible: opacity > 0
257 visible: opacity > 0
231 Behavior on opacity {
258 Behavior on opacity {
qml/Dash/Filters/FilterExpandableWidget.qml+1
@@ -65,6 +65,7 @@ FilterWidget {
65 anchors.rightMargin: units.gu(2)
65 anchors.rightMargin: units.gu(2)
66 anchors.verticalCenter: parent.verticalCenter
66 anchors.verticalCenter: parent.verticalCenter
67 source: expandingItem.expanded ? "image://theme/up" : "image://theme/down"
67 source: expandingItem.expanded ? "image://theme/up" : "image://theme/down"
68+ sourceSize.height: height
68 }
69 }
69 }
70 }
70
71
qml/Dash/Filters/FilterOptionSelector.qml+2
@@ -60,6 +60,7 @@ FilterWidget {
60 anchors.right: parent.right
60 anchors.right: parent.right
61 anchors.verticalCenter: parent.verticalCenter
61 anchors.verticalCenter: parent.verticalCenter
62 source: expandingItem.expanded ? "image://theme/up" : "image://theme/down"
62 source: expandingItem.expanded ? "image://theme/up" : "image://theme/down"
63+ sourceSize.height: height
63 }
64 }
64 }
65 }
65
66
@@ -81,6 +82,7 @@ FilterWidget {
81 anchors.right: parent.right
82 anchors.right: parent.right
82 anchors.verticalCenter: parent.verticalCenter
83 anchors.verticalCenter: parent.verticalCenter
83 source: "image://theme/tick"
84 source: "image://theme/tick"
85+ sourceSize.height: height
84 visible: checked
86 visible: checked
85 }
87 }
86
88
qml/Dash/GenericScopeView.qml+1−1
@@ -662,7 +662,7 @@ FocusScope {
662 }
662 }
663 onShowFiltersPopup: { // item
663 onShowFiltersPopup: { // item
664 extraPanel.visible = false;
664 extraPanel.visible = false;
665− scopeView.filtersPopover = PopupUtils.open(Qt.resolvedUrl("FiltersPopover.qml"), item, { "contentWidth": scopeView.width - units.gu(2) } );
665+ scopeView.filtersPopover = PopupUtils.open(Qt.resolvedUrl("FiltersPopover.qml"), item, { "contentWidth": Qt.binding(function() { return scopeView.width - units.gu(2); } ) } );
666 scopeView.filtersPopover.Component.onDestruction.connect(function () {
666 scopeView.filtersPopover.Component.onDestruction.connect(function () {
667 categoryView.pageHeader.closePopup(false, true);
667 categoryView.pageHeader.closePopup(false, true);
668 categoryView.pageHeader.unfocus(true); // remove the focus from the search field
668 categoryView.pageHeader.unfocus(true); // remove the focus from the search field
qml/Greeter/Greeter.qml+9−3
@@ -76,9 +76,16 @@ Showable {
76 // already shown, do it manually.
76 // already shown, do it manually.
77 d.selectUser(d.currentIndex, true);
77 d.selectUser(d.currentIndex, true);
78 }
78 }
79+
79 // Even though we may already be shown, we want to call show() for its
80 // Even though we may already be shown, we want to call show() for its
80 // possible side effects, like hiding indicators and such.
81 // possible side effects, like hiding indicators and such.
82+ //
83+ // We re-check forcedUnlock here, because selectUser above might
84+ // process events during authentication, and a request to unlock could
85+ // have come in in the meantime.
86+ if (!forcedUnlock) {
81− showNow();
87+ showNow();
88+ }
82 }
89 }
83
90
84 function notifyAppFocusRequested(appId) {
91 function notifyAppFocusRequested(appId) {
@@ -427,8 +434,7 @@ Showable {
427 target: LightDMService.greeter
434 target: LightDMService.greeter
428
435
429 onShowGreeter: root.forceShow()
436 onShowGreeter: root.forceShow()
430−
431− onHideGreeter: d.login()
437+ onHideGreeter: root.forcedUnlock = true
432
438
433 onShowMessage: {
439 onShowMessage: {
434 // inefficient, but we only rarely deal with messages
440 // inefficient, but we only rarely deal with messages
qml/Panel/IndicatorItemRow.qml+13−2
@@ -18,6 +18,10 @@ import QtQuick 2.4
18import QtQuick.Window 2.2
18import QtQuick.Window 2.2
19import Ubuntu.Components 1.3
19import Ubuntu.Components 1.3
20
20
21+// for indicator-keyboard
22+import AccountsService 0.1
23+import Unity.InputInfo 0.1
24+
21Item {
25Item {
22 id: root
26 id: root
23 width: row.width
27 width: row.width
@@ -127,6 +131,11 @@ Item {
127 property bool forceAlignmentAnimationDisabled: false
131 property bool forceAlignmentAnimationDisabled: false
128 }
132 }
129
133
134+ InputDeviceModel {
135+ id: keyboardsModel
136+ deviceFilter: InputInfo.Keyboard
137+ }
138+
130 onCurrentItemChanged: {
139 onCurrentItemChanged: {
131 if (d.previousItem) {
140 if (d.previousItem) {
132 d.firstItemSwitch = false;
141 d.firstItemSwitch = false;
@@ -184,9 +193,11 @@ Item {
184
193
185 property int ownIndex: index
194 property int ownIndex: index
186 property bool overflow: row.width - x > overFlowWidth
195 property bool overflow: row.width - x > overFlowWidth
187− property bool hidden: !expanded && (overflow || !indicatorVisible || hideSessionIndicator)
196+ property bool hidden: !expanded && (overflow || !indicatorVisible || hideSessionIndicator || hideKeyboardIndicator)
188 // HACK for indicator-session
197 // HACK for indicator-session
189 readonly property bool hideSessionIndicator: identifier == "indicator-session" && Math.min(Screen.width, Screen.height) <= units.gu(60)
198 readonly property bool hideSessionIndicator: identifier == "indicator-session" && Math.min(Screen.width, Screen.height) <= units.gu(60)
199+ // HACK for indicator-keyboard
200+ readonly property bool hideKeyboardIndicator: identifier == "indicator-keyboard" && (AccountsService.keymaps.length < 2 || keyboardsModel.count == 0)
190
201
191 height: row.height
202 height: row.height
192 expanded: root.expanded
203 expanded: root.expanded
@@ -202,7 +213,7 @@ Item {
202 NumberAnimation { duration: UbuntuAnimation.SnapDuration; easing: UbuntuAnimation.StandardEasing }
213 NumberAnimation { duration: UbuntuAnimation.SnapDuration; easing: UbuntuAnimation.StandardEasing }
203 }
214 }
204
215
205− width: ((expanded || indicatorVisible) && !hideSessionIndicator) ? implicitWidth : 0
216+ width: ((expanded || indicatorVisible) && !hideSessionIndicator && !hideKeyboardIndicator) ? implicitWidth : 0
206
217
207 Behavior on width {
218 Behavior on width {
208 NumberAnimation { duration: UbuntuAnimation.SnapDuration; easing: UbuntuAnimation.StandardEasing }
219 NumberAnimation { duration: UbuntuAnimation.SnapDuration; easing: UbuntuAnimation.StandardEasing }
qml/Panel/Indicators/MenuItemFactory.qml+65
@@ -985,6 +985,62 @@ Item {
985 }
985 }
986 }
986 }
987
987
988+ Component {
989+ id: keymapMenu;
990+
991+ // FIXME this should use a "radio button" menu, once we have it in the SDK
992+ ListItems.Empty {
993+ id: checkItem
994+ objectName: "keymapMenu"
995+ property QtObject menuData: null
996+ property int menuIndex: -1
997+ readonly property bool serverChecked: menuData && menuData.isToggled || false
998+
999+ enabled: menuData && menuData.sensitive || false
1000+ highlightWhenPressed: false
1001+ __acceptEvents: !serverChecked
1002+
1003+ CheckBox {
1004+ id: checkbox
1005+ __acceptEvents: !checkItem.serverChecked
1006+ anchors {
1007+ left: parent.left
1008+ leftMargin: checkItem.__contentsMargins
1009+ verticalCenter: parent.verticalCenter
1010+ }
1011+ }
1012+
1013+ ServerPropertySynchroniser {
1014+ objectName: "sync"
1015+ syncTimeout: Utils.Constants.indicatorValueTimeout
1016+
1017+ serverTarget: checkItem
1018+ serverProperty: "serverChecked"
1019+ userTarget: checkbox
1020+ userProperty: "checked"
1021+
1022+ onSyncTriggered: {
1023+ menuModel.activate(checkItem.menuIndex);
1024+ }
1025+ }
1026+
1027+ Label {
1028+ id: label
1029+ anchors {
1030+ left: checkbox.right
1031+ leftMargin: checkItem.__contentsMargins
1032+ right: parent.right
1033+ rightMargin: checkItem.__contentsMargins
1034+ verticalCenter: parent.verticalCenter
1035+ }
1036+ elide: Text.ElideRight
1037+ text: checkItem.menuData && checkItem.menuData.label || ""
1038+ }
1039+
1040+ onClicked: menuModel.activate(menuIndex);
1041+ }
1042+ }
1043+
988 function load(modelData, context) {
1044 function load(modelData, context) {
989 // tweak indicator-session items
1045 // tweak indicator-session items
990 if (context === "indicator-session") {
1046 if (context === "indicator-session") {
@@ -995,6 +1051,15 @@ Item {
995 }
1051 }
996 }
1052 }
997
1053
1054+ // specialize for indicator-keyboard
1055+ if (context === "indicator-keyboard") {
1056+ if (modelData.isRadio) {
1057+ return keymapMenu;
1058+ } else if (modelData.action === "indicator.map" || modelData.action === "indicator.chart") {
1059+ return null; // map and chart not available
1060+ }
1061+ }
1062+
998 if (modelData.type !== undefined && modelData.type !== "") {
1063 if (modelData.type !== undefined && modelData.type !== "") {
999 var component = undefined;
1064 var component = undefined;
1000
1065
qml/Shell.qml+2
@@ -775,6 +775,8 @@ StyledItem {
775 when: cursor.mouseNeverMoved && cursor.visible
775 when: cursor.mouseNeverMoved && cursor.visible
776 }
776 }
777
777
778+ height: units.gu(3)
779+
778 onPushedLeftBoundary: {
780 onPushedLeftBoundary: {
779 if (buttons === Qt.NoButton) {
781 if (buttons === Qt.NoButton) {
780 launcher.pushEdge(amount);
782 launcher.pushEdge(amount);
qml/Stages/AbstractStage.qml+9
@@ -16,6 +16,7 @@
16
16
17import QtQuick 2.4
17import QtQuick 2.4
18import Ubuntu.Components 1.3
18import Ubuntu.Components 1.3
19+import GlobalShortcut 1.0
19import GSettings 1.0
20import GSettings 1.0
20
21
21FocusScope {
22FocusScope {
@@ -78,4 +79,12 @@ FocusScope {
78 color: "#060606"
79 color: "#060606"
79 anchors.fill: parent
80 anchors.fill: parent
80 }
81 }
82+
83+ // shared Alt+F4 functionality
84+ function closeFocusedDelegate() {} // to be implemented by stages
85+
86+ GlobalShortcut {
87+ shortcut: Qt.AltModifier|Qt.Key_F4
88+ onTriggered: closeFocusedDelegate()
89+ }
81}
90}
qml/Stages/ApplicationWindow.qml+6
@@ -46,6 +46,7 @@ FocusScope {
46 property alias resizeSurface: surfaceContainer.resizeSurface
46 property alias resizeSurface: surfaceContainer.resizeSurface
47 property int requestedWidth: -1
47 property int requestedWidth: -1
48 property int requestedHeight: -1
48 property int requestedHeight: -1
49+ property real splashRotation: 0
49
50
50 readonly property int minimumWidth: surface ? surface.minimumWidth : 0
51 readonly property int minimumWidth: surface ? surface.minimumWidth : 0
51 readonly property int minimumHeight: surface ? surface.minimumHeight : 0
52 readonly property int minimumHeight: surface ? surface.minimumHeight : 0
@@ -184,6 +185,11 @@ FocusScope {
184 backgroundColor: d.splashColor
185 backgroundColor: d.splashColor
185 headerColor: d.splashColorHeader
186 headerColor: d.splashColorHeader
186 footerColor: d.splashColorFooter
187 footerColor: d.splashColorFooter
188+
189+ rotation: root.splashRotation
190+ anchors.centerIn: parent
191+ width: rotation == 0 || rotation == 180 ? root.width : root.height
192+ height: rotation == 0 || rotation == 180 ? root.height : root.width
187 }
193 }
188 }
194 }
189 }
195 }
qml/Stages/DesktopStage.qml+8−8
@@ -36,6 +36,11 @@ AbstractStage {
36 edgeBarrier.push(amount);
36 edgeBarrier.push(amount);
37 }
37 }
38 }
38 }
39+ function closeFocusedDelegate() {
40+ if (priv.focusedAppDelegate && !priv.focusedAppDelegate.isDash) {
41+ priv.focusedAppDelegate.close();
42+ }
43+ }
39
44
40 // Used by TutorialRight
45 // Used by TutorialRight
41 property bool spreadShown: spread.state == "altTab"
46 property bool spreadShown: spread.state == "altTab"
@@ -47,13 +52,6 @@ AbstractStage {
47
52
48 orientationChangesEnabled: true
53 orientationChangesEnabled: true
49
54
50− GlobalShortcut {
51− id: closeWindowShortcut
52− shortcut: Qt.AltModifier|Qt.Key_F4
53− onTriggered: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.close(); } }
54− active: priv.focusedAppDelegate !== null
55− }
56−
57 GlobalShortcut {
55 GlobalShortcut {
58 id: showSpreadShortcut
56 id: showSpreadShortcut
59 shortcut: Qt.MetaModifier|Qt.Key_W
57 shortcut: Qt.MetaModifier|Qt.Key_W
@@ -205,7 +203,7 @@ AbstractStage {
205 Binding {
203 Binding {
206 target: PanelState
204 target: PanelState
207 property: "closeButtonShown"
205 property: "closeButtonShown"
208− value: priv.focusedAppDelegate && priv.focusedAppDelegate.maximized && priv.focusedAppDelegate.application.appId !== "unity8-dash"
206+ value: priv.focusedAppDelegate && priv.focusedAppDelegate.maximized && !priv.focusedAppDelegate.isDash
209 }
207 }
210
208
211 Component.onDestruction: {
209 Component.onDestruction: {
@@ -337,6 +335,8 @@ AbstractStage {
337 readonly property var surface: model.surface
335 readonly property var surface: model.surface
338 readonly property alias resizeArea: resizeArea
336 readonly property alias resizeArea: resizeArea
339
337
338+ readonly property bool isDash: model.application.appId == "unity8-dash"
339+
340 function claimFocus() {
340 function claimFocus() {
341 if (spread.state == "altTab") {
341 if (spread.state == "altTab") {
342 spread.cancel();
342 spread.cancel();
qml/Stages/PhoneStage.qml+8−2
@@ -108,6 +108,12 @@ AbstractStage {
108 }
108 }
109 }
109 }
110
110
111+ function closeFocusedDelegate() {
112+ if (priv.focusedAppDelegate && priv.focusedAppDelegate.closeable) {
113+ priv.focusedAppDelegate.closed();
114+ }
115+ }
116+
111 mainApp: priv.focusedAppDelegate ? priv.focusedAppDelegate.application : null
117 mainApp: priv.focusedAppDelegate ? priv.focusedAppDelegate.application : null
112
118
113 orientationChangesEnabled: priv.focusedAppOrientationChangesEnabled
119 orientationChangesEnabled: priv.focusedAppOrientationChangesEnabled
@@ -465,13 +471,13 @@ AbstractStage {
465 model: topLevelSurfaceList
471 model: topLevelSurfaceList
466
472
467 onItemRemoved: {
473 onItemRemoved: {
468− // Unless we're closing the app ourselves in the spread,
474+ // Unless we're closing the app ourselves,
469 // lets make sure the spread doesn't mess up by the changing app list.
475 // lets make sure the spread doesn't mess up by the changing app list.
470 if (spreadView.closingIndex == -1) {
476 if (spreadView.closingIndex == -1) {
471 spreadView.phase = 0;
477 spreadView.phase = 0;
472 spreadView.contentX = -spreadView.shift;
478 spreadView.contentX = -spreadView.shift;
473− focusTopMostApp();
474 }
479 }
480+ focusTopMostApp();
475 }
481 }
476 function focusTopMostApp() {
482 function focusTopMostApp() {
477 if (spreadRepeater.count > 0) {
483 if (spreadRepeater.count > 0) {
qml/Stages/SpreadDelegate.qml+71−81
@@ -82,12 +82,69 @@ FocusScope {
82
82
83 QtObject {
83 QtObject {
84 id: priv
84 id: priv
85+ objectName: "spreadDelegatePriv"
85 property bool startingUp: true
86 property bool startingUp: true
86 }
87 }
87
88
88 Component.onCompleted: { finishStartUpTimer.start(); }
89 Component.onCompleted: { finishStartUpTimer.start(); }
89 Timer { id: finishStartUpTimer; interval: 400; onTriggered: priv.startingUp = false }
90 Timer { id: finishStartUpTimer; interval: 400; onTriggered: priv.startingUp = false }
90
91
92+ // JS version of QPlatformScreen::angleBetween C++ implementation.
93+ // So don't ask me how it works because I don't know.
94+ // Calling Screen.angleBetween from within a Binding component doesn't work for some reason.
95+ function angleBetween(a, b) {
96+ if (a == b)
97+ return 0;
98+
99+ var ia = Math.log(a) / Math.LN2;
100+ var ib = Math.log(b) / Math.LN2;
101+
102+ var delta = ia - ib;
103+
104+ if (delta < 0)
105+ delta = delta + 4;
106+
107+ var angles = [ 0, 90, 180, 270 ];
108+ return angles[delta];
109+ }
110+
111+ // Sets the initial orientationAngle of the window, when it first slides into view
112+ // (with the splash screen likely being displayed). At that point we just try to
113+ // match shell's current orientation. We need a bit of time in this state as the
114+ // information we need to decide orientationAngle may take a few cycles to
115+ // be set.
116+ Binding {
117+ target: appWindowWithShadow
118+ property: "orientationAngle"
119+ when: priv.startingUp
120+ value: {
121+ var supportedOrientations = root.supportedOrientations;
122+
123+ if (supportedOrientations === Qt.PrimaryOrientation) {
124+ supportedOrientations = root.orientations.primary;
125+ }
126+
127+ // If it doesn't support shell's current orientation
128+ // then simply pick some arbitraty one that it does support
129+ var chosenOrientation = 0;
130+ if (supportedOrientations & root.shellOrientation) {
131+ chosenOrientation = root.shellOrientation;
132+ } else if (supportedOrientations & Qt.PortraitOrientation) {
133+ chosenOrientation = root.orientations.portrait;
134+ } else if (supportedOrientations & Qt.LandscapeOrientation) {
135+ chosenOrientation = root.orientations.landscape;
136+ } else if (supportedOrientations & Qt.InvertedPortraitOrientation) {
137+ chosenOrientation = root.orientations.invertedPortrait;
138+ } else if (supportedOrientations & Qt.InvertedLandscapeOrientation) {
139+ chosenOrientation = root.orientations.invertedLandscape;
140+ } else {
141+ chosenOrientation = root.orientations.primary;
142+ }
143+
144+ return angleBetween(root.orientations.native_, chosenOrientation);
145+ }
146+ }
147+
91 Rectangle {
148 Rectangle {
92 id: background
149 id: background
93 color: "black"
150 color: "black"
@@ -129,9 +186,7 @@ FocusScope {
129 }
186 }
130
187
131 state: {
188 state: {
132− if (priv.startingUp) {
133− return "startingUp";
134− } else if (root.application && root.application.rotatesWindowContents) {
189+ if (root.application && root.application.rotatesWindowContents) {
135 return "counterRotate";
190 return "counterRotate";
136 } else if (orientationChangeAnimation.running) {
191 } else if (orientationChangeAnimation.running) {
137 return "animatingRotation";
192 return "animatingRotation";
@@ -149,118 +204,53 @@ FocusScope {
149 }
204 }
150
205
151 states: [
206 states: [
152− // Sets the initial orientationAngle of the window, when it first slides into view
207+ // A base state containing bindings common to others
153− // (with the splash screen likely being displayed). At that point we just try to
208+ // Ensures appWindowWithShadow fills the root area.
154− // match shell's current orientation. We need a bit of time in this state as the
155− // information we need to decide orientationAngle may take a few cycles to
156− // be set.
157 State {
209 State {
158− name: "startingUp"
210+ name: "fillRootArea"
159 PropertyChanges {
211 PropertyChanges {
160 target: appWindowWithShadow
212 target: appWindowWithShadow
161 restoreEntryValues: false
213 restoreEntryValues: false
162− orientationAngle: {
214+ width: appWindowWithShadow.rotation == 0 || appWindowWithShadow.rotation == 180 ? root.width : root.height
163− if (!root.application || root.application.rotatesWindowContents) {
215+ height: appWindowWithShadow.rotation == 0 || appWindowWithShadow.rotation == 180 ? root.height : root.width
164− return 0;
165− }
166−
167− var supportedOrientations = root.supportedOrientations;
168−
169− if (supportedOrientations === Qt.PrimaryOrientation) {
170− supportedOrientations = root.orientations.primary;
171− }
172−
173− // If it doesn't support shell's current orientation
174− // then simply pick some arbitraty one that it does support
175− var chosenOrientation = 0;
176− if (supportedOrientations & root.shellOrientation) {
177− chosenOrientation = root.shellOrientation;
178− } else if (supportedOrientations & Qt.PortraitOrientation) {
179− chosenOrientation = root.orientations.portrait;
180− } else if (supportedOrientations & Qt.LandscapeOrientation) {
181− chosenOrientation = root.orientations.landscape;
182− } else if (supportedOrientations & Qt.InvertedPortraitOrientation) {
183− chosenOrientation = root.orientations.invertedPortrait;
184− } else if (supportedOrientations & Qt.InvertedLandscapeOrientation) {
185− chosenOrientation = root.orientations.invertedLandscape;
186− } else {
187− chosenOrientation = root.orientations.primary;
188− }
189−
190− return Screen.angleBetween(root.orientations.native_, chosenOrientation);
191− }
192−
193− rotation: normalizeAngle(appWindowWithShadow.orientationAngle - root.shellOrientationAngle)
194− width: {
195− if (rotation == 0 || rotation == 180) {
196− return root.width;
197− } else {
198− return root.height;
199− }
200− }
201− height: {
202− if (rotation == 0 || rotation == 180)
203− return root.height;
204− else
205− return root.width;
206− }
207 }
216 }
208 },
217 },
209 // In this state we stick to our currently set orientationAngle, which may change only due
218 // In this state we stick to our currently set orientationAngle, which may change only due
210 // to calls made to matchShellOrientation() or animateToShellOrientation()
219 // to calls made to matchShellOrientation() or animateToShellOrientation()
211 State {
220 State {
212− id: keepSceneRotationState
213 name: "keepSceneRotation"
221 name: "keepSceneRotation"
214−
222+ extend: "fillRootArea"
215− StateChangeScript { script: {
216− // break binding
217− appWindowWithShadow.orientationAngle = appWindowWithShadow.orientationAngle;
218− } }
219 PropertyChanges {
223 PropertyChanges {
220 target: appWindowWithShadow
224 target: appWindowWithShadow
221 restoreEntryValues: false
225 restoreEntryValues: false
222 rotation: normalizeAngle(appWindowWithShadow.orientationAngle - root.shellOrientationAngle)
226 rotation: normalizeAngle(appWindowWithShadow.orientationAngle - root.shellOrientationAngle)
223− width: {
224− if (rotation == 0 || rotation == 180) {
225− return root.width;
226− } else {
227− return root.height;
228− }
229− }
230− height: {
231− if (rotation == 0 || rotation == 180)
232− return root.height;
233− else
234− return root.width;
235− }
236 }
227 }
237 },
228 },
238 // In this state we counteract any shell rotation so that the window, in scene coordinates,
229 // In this state we counteract any shell rotation so that the window, in scene coordinates,
239 // remains unrotated.
230 // remains unrotated.
231+ // But the splash screen should still obey the set orientationAngle set by shell
240 State {
232 State {
241 name: "counterRotate"
233 name: "counterRotate"
242− StateChangeScript { script: {
234+ extend: "fillRootArea"
243− // break binding
244− appWindowWithShadow.orientationAngle = appWindowWithShadow.orientationAngle;
245− } }
246 PropertyChanges {
235 PropertyChanges {
247 target: appWindowWithShadow
236 target: appWindowWithShadow
248− width: root.shellOrientationAngle == 0 || root.shellOrientationAngle == 180 ? root.width : root.height
249− height: root.shellOrientationAngle == 0 || root.shellOrientationAngle == 180 ? root.height : root.width
250 rotation: normalizeAngle(-root.shellOrientationAngle)
237 rotation: normalizeAngle(-root.shellOrientationAngle)
251 }
238 }
252 PropertyChanges {
239 PropertyChanges {
253 target: appWindow
240 target: appWindow
254 surfaceOrientationAngle: appWindowWithShadow.orientationAngle
241 surfaceOrientationAngle: appWindowWithShadow.orientationAngle
242+ splashRotation: appWindowWithShadow.orientationAngle
255 }
243 }
256 },
244 },
245+ // Dummy state.
246+ // The animation may indiscriminately break any bindings assigned to appWindowWithShadow rotation,
247+ // width or height.
257 State {
248 State {
258 name: "animatingRotation"
249 name: "animatingRotation"
259 }
250 }
260 ]
251 ]
261
252
262− x: (parent.width - width) / 2
253+ anchors.centerIn: parent
263− y: (parent.height - height) / 2
264
254
265 BorderImage {
255 BorderImage {
266 anchors {
256 anchors {
qml/Stages/TabletStage.qml+8−2
@@ -84,6 +84,12 @@ AbstractStage {
84 }
84 }
85 }
85 }
86
86
87+ function closeFocusedDelegate() {
88+ if (priv.focusedAppDelegate && priv.focusedAppDelegate.closeable) {
89+ priv.focusedAppDelegate.closed();
90+ }
91+ }
92+
87 orientationChangesEnabled: priv.mainAppOrientationChangesEnabled
93 orientationChangesEnabled: priv.mainAppOrientationChangesEnabled
88
94
89 mainApp: {
95 mainApp: {
@@ -707,13 +713,13 @@ AbstractStage {
707
713
708 onItemRemoved: {
714 onItemRemoved: {
709 priv.updateMainAndSideStageIndexes();
715 priv.updateMainAndSideStageIndexes();
710− // Unless we're closing the app ourselves in the spread,
716+ // Unless we're closing the app ourselves,
711 // lets make sure the spread doesn't mess up by the changing app list.
717 // lets make sure the spread doesn't mess up by the changing app list.
712 if (spreadView.closingIndex == -1) {
718 if (spreadView.closingIndex == -1) {
713 spreadView.phase = 0;
719 spreadView.phase = 0;
714 spreadView.contentX = -spreadView.shift;
720 spreadView.contentX = -spreadView.shift;
715− focusTopMostApp();
716 }
721 }
722+ focusTopMostApp();
717 }
723 }
718 function focusTopMostApp() {
724 function focusTopMostApp() {
719 if (spreadRepeater.count > 0) {
725 if (spreadRepeater.count > 0) {
src/Dash/main.cpp+1−1
@@ -37,7 +37,7 @@
37
37
38int main(int argc, const char *argv[])
38int main(int argc, const char *argv[])
39{
39{
40− qSetMessagePattern("[%{time yyyy-mm-dd:hh:mm:ss.zzz}] %{if-category}%{category}: %{endif}%{message}");
40+ qSetMessagePattern("[%{time yyyy-MM-dd:hh:mm:ss.zzz}] %{if-category}%{category}: %{endif}%{message}");
41 if (enableQmlDebugger(argc, argv)) {
41 if (enableQmlDebugger(argc, argv)) {
42 QQmlDebuggingEnabler qQmlEnableDebuggingHelper(true);
42 QQmlDebuggingEnabler qQmlEnableDebuggingHelper(true);
43 }
43 }
src/main.cpp+1−1
@@ -21,7 +21,7 @@
21
21
22int main(int argc, const char *argv[])
22int main(int argc, const char *argv[])
23{
23{
24− qSetMessagePattern("[%{time yyyy-mm-dd:hh:mm:ss.zzz}] %{if-category}%{category}: %{endif}%{message}");
24+ qSetMessagePattern("[%{time yyyy-MM-dd:hh:mm:ss.zzz}] %{if-category}%{category}: %{endif}%{message}");
25
25
26 bool isMirServer = false;
26 bool isMirServer = false;
27 if (qgetenv("QT_QPA_PLATFORM") == "ubuntumirclient") {
27 if (qgetenv("QT_QPA_PLATFORM") == "ubuntumirclient") {
tests/mocks/Unity/Application/MirSurfaceItem.cpp+37−10
@@ -43,6 +43,7 @@ MirSurfaceItem::MirSurfaceItem(QQuickItem *parent)
43 , m_qmlContentComponent(nullptr)
43 , m_qmlContentComponent(nullptr)
44 , m_qmlItem(nullptr)
44 , m_qmlItem(nullptr)
45 , m_consumesInput(false)
45 , m_consumesInput(false)
46+ , m_orientationAngle(nullptr)
46 , m_surfaceWidth(0)
47 , m_surfaceWidth(0)
47 , m_surfaceHeight(0)
48 , m_surfaceHeight(0)
48 , m_touchPressCount(0)
49 , m_touchPressCount(0)
@@ -68,6 +69,7 @@ MirSurfaceItem::~MirSurfaceItem()
68{
69{
69 DEBUG_MSG((void*)(this) << name());
70 DEBUG_MSG((void*)(this) << name());
70 setSurface(nullptr);
71 setSurface(nullptr);
72+ delete m_orientationAngle;
71}
73}
72
74
73void MirSurfaceItem::printComponentErrors()
75void MirSurfaceItem::printComponentErrors()
@@ -125,7 +127,10 @@ Mir::ShellChrome MirSurfaceItem::shellChrome() const
125
127
126Mir::OrientationAngle MirSurfaceItem::orientationAngle() const
128Mir::OrientationAngle MirSurfaceItem::orientationAngle() const
127{
129{
130+ if (m_orientationAngle) {
131+ Q_ASSERT(!m_qmlSurface);
132+ return *m_orientationAngle;
128− if (m_qmlSurface) {
133+ } else if (m_qmlSurface) {
129 return m_qmlSurface->orientationAngle();
134 return m_qmlSurface->orientationAngle();
130 } else {
135 } else {
131 return Mir::Angle0;
136 return Mir::Angle0;
@@ -134,16 +139,24 @@ Mir::OrientationAngle MirSurfaceItem::orientationAngle() const
134
139
135void MirSurfaceItem::setOrientationAngle(Mir::OrientationAngle angle)
140void MirSurfaceItem::setOrientationAngle(Mir::OrientationAngle angle)
136{
141{
137− if (!m_qmlSurface)
142+ DEBUG_MSG(angle);
138− return;
139−
140− if (m_qmlSurface->orientationAngle() == angle)
141− return;
142
143
144+ if (m_qmlSurface) {
145+ Q_ASSERT(!m_orientationAngle);
143− m_qmlSurface->setOrientationAngle(angle);
146+ m_qmlSurface->setOrientationAngle(angle);
147+ } else if (!m_orientationAngle) {
148+ m_orientationAngle = new Mir::OrientationAngle;
149+ *m_orientationAngle = angle;
150+ Q_EMIT orientationAngleChanged(angle);
151+ } else if (*m_orientationAngle != angle) {
152+ *m_orientationAngle = angle;
153+ Q_EMIT orientationAngleChanged(angle);
154+ }
144
155
156+ if (m_qmlItem) {
145− QQmlProperty orientationProp(m_qmlItem, "orientationAngle");
157+ QQmlProperty orientationProp(m_qmlItem, "orientationAngle");
146− orientationProp.write(QVariant::fromValue(m_qmlSurface->orientationAngle()));
158+ orientationProp.write(QVariant::fromValue(orientationAngle()));
159+ }
147}
160}
148
161
149void MirSurfaceItem::updateScreenshot(QUrl screenshotUrl)
162void MirSurfaceItem::updateScreenshot(QUrl screenshotUrl)
@@ -175,6 +188,11 @@ void MirSurfaceItem::createQmlContentItem()
175 QQmlProperty screenshotSource(m_qmlItem, "screenshotSource");
188 QQmlProperty screenshotSource(m_qmlItem, "screenshotSource");
176 screenshotSource.write(QVariant::fromValue(m_qmlSurface->screenshotUrl()));
189 screenshotSource.write(QVariant::fromValue(m_qmlSurface->screenshotUrl()));
177 }
190 }
191+
192+ {
193+ QQmlProperty orientationProp(m_qmlItem, "orientationAngle");
194+ orientationProp.write(QVariant::fromValue(orientationAngle()));
195+ }
178}
196}
179
197
180void MirSurfaceItem::touchEvent(QTouchEvent * event)
198void MirSurfaceItem::touchEvent(QTouchEvent * event)
@@ -255,7 +273,16 @@ void MirSurfaceItem::setSurface(MirSurfaceInterface* surface)
255 updateSurfaceSize();
273 updateSurfaceSize();
256 updateMirSurfaceVisibility();
274 updateMirSurfaceVisibility();
257
275
276+ if (m_orientationAngle) {
277+ m_qmlSurface->setOrientationAngle(*m_orientationAngle);
258− connect(m_qmlSurface, &MirSurface::orientationAngleChanged, this, &MirSurfaceItem::orientationAngleChanged);
278+ connect(m_qmlSurface, &MirSurfaceInterface::orientationAngleChanged, this, &MirSurfaceItem::orientationAngleChanged);
279+ delete m_orientationAngle;
280+ m_orientationAngle = nullptr;
281+ } else {
282+ connect(m_qmlSurface, &MirSurfaceInterface::orientationAngleChanged, this, &MirSurfaceItem::orientationAngleChanged);
283+ Q_EMIT orientationAngleChanged(m_qmlSurface->orientationAngle());
284+ }
285+
259 connect(m_qmlSurface, &MirSurface::screenshotUrlChanged, this, &MirSurfaceItem::updateScreenshot);
286 connect(m_qmlSurface, &MirSurface::screenshotUrlChanged, this, &MirSurfaceItem::updateScreenshot);
260 connect(m_qmlSurface, &MirSurface::liveChanged, this, &MirSurfaceItem::liveChanged);
287 connect(m_qmlSurface, &MirSurface::liveChanged, this, &MirSurfaceItem::liveChanged);
261 connect(m_qmlSurface, &MirSurface::stateChanged, this, &MirSurfaceItem::surfaceStateChanged);
288 connect(m_qmlSurface, &MirSurface::stateChanged, this, &MirSurfaceItem::surfaceStateChanged);
tests/mocks/Unity/Application/MirSurfaceItem.h+2
@@ -117,6 +117,8 @@ private:
117
117
118 bool m_consumesInput;
118 bool m_consumesInput;
119
119
120+ Mir::OrientationAngle *m_orientationAngle;
121+
120 int m_surfaceWidth;
122 int m_surfaceWidth;
121 int m_surfaceHeight;
123 int m_surfaceHeight;
122
124
tests/mocks/Unity/Indicators/IndicatorsModel.qml+41−1
@@ -16,11 +16,22 @@
16
16
17import QtQuick 2.4
17import QtQuick 2.4
18import Unity.Indicators 0.1 as Indicators
18import Unity.Indicators 0.1 as Indicators
19+import Unity.InputInfo 0.1
20+import AccountsService 0.1
19
21
20Indicators.FakeIndicatorsModel {
22Indicators.FakeIndicatorsModel {
21 id: root
23 id: root
22
24
23 property var originalModelData: [
25 property var originalModelData: [
26+ {
27+ "identifier": "indicator-keyboard",
28+ "indicatorProperties": {
29+ "enabled": true,
30+ "busName": "com.canonical.indicators.fake0",
31+ "menuObjectPath": "/com/canonical/indicators/fake0",
32+ "actionsObjectPath": "/com/canonical/indicators/fake0"
33+ }
34+ },
24 {
35 {
25 "identifier": "fake-indicator-bluetooth",
36 "identifier": "fake-indicator-bluetooth",
26 "indicatorProperties": {
37 "indicatorProperties": {
@@ -74,13 +85,38 @@ Indicators.FakeIndicatorsModel {
74 "menuObjectPath": "/com/canonical/indicators/fake6",
85 "menuObjectPath": "/com/canonical/indicators/fake6",
75 "actionsObjectPath": "/com/canonical/indicators/fake6"
86 "actionsObjectPath": "/com/canonical/indicators/fake6"
76 }
87 }
88+ },
89+ {
90+ "identifier": "fake-indicator-session",
91+ "indicatorProperties": {
92+ "enabled": true,
93+ "busName": "com.canonical.indicators.fake7",
94+ "menuObjectPath": "/com/canonical/indicators/fake7",
95+ "actionsObjectPath": "/com/canonical/indicators/fake7"
96+ }
77 }
97 }
78 ]
98 ]
79
99
100+ Component.onCompleted: {
101+ // init data for the fake indicator-keyboard
102+ MockInputDeviceBackend.addMockDevice("/indicator_kbd0", InputInfo.Keyboard);
103+ AccountsService.keymaps = ["us", "cs"];
104+ }
105+
106+ Component.onDestruction: {
107+ MockInputDeviceBackend.removeDevice("/indicator_kbd0");
108+ AccountsService.keymaps = ["us"];
109+ }
110+
80 function load(profile) {
111 function load(profile) {
81 unload();
112 unload();
82 root.modelData = originalModelData;
113 root.modelData = originalModelData;
83
114
115+ Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicators/fake0",
116+ getUnityMenuModelData("indicator-keyboard",
117+ "Czech (F)",
118+ "",
119+ [ "image://theme/input-keyboard-symbolic" ]));
84 Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicators/fake1",
120 Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicators/fake1",
85 getUnityMenuModelData("fake-indicator-bluetooth",
121 getUnityMenuModelData("fake-indicator-bluetooth",
86 "Bluetooth (F)",
122 "Bluetooth (F)",
@@ -111,6 +147,11 @@ Indicators.FakeIndicatorsModel {
111 "Upcoming Events (F)",
147 "Upcoming Events (F)",
112 "12:04",
148 "12:04",
113 []));
149 []));
150+ Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicators/fake7",
151+ getUnityMenuModelData("fake-indicator-session",
152+ "System (F)",
153+ "",
154+ ["image://theme/system-devices-panel"]));
114 }
155 }
115
156
116 function getUnityMenuModelData(identifier, title, label, icons) {
157 function getUnityMenuModelData(identifier, title, label, icons) {
@@ -173,6 +214,5 @@ Indicators.FakeIndicatorsModel {
173 break;
214 break;
174 }
215 }
175 }
216 }
176−
177 }
217 }
178}
218}
tests/plugins/Cursor/tst_Cursor.qml+8−1
@@ -26,11 +26,13 @@ Rectangle {
26
26
27 property string themeName: "default"
27 property string themeName: "default"
28 property string cursorName: "left_ptr"
28 property string cursorName: "left_ptr"
29+ property string cursorHeight: "32"
29
30
30 CursorImageInfo {
31 CursorImageInfo {
31 id: imageInfo
32 id: imageInfo
32 themeName: root.themeName
33 themeName: root.themeName
33 cursorName: root.cursorName
34 cursorName: root.cursorName
35+ cursorHeight: root.cursorHeight
34 }
36 }
35
37
36 Item {
38 Item {
@@ -43,7 +45,7 @@ Rectangle {
43
45
44 x: -imageInfo.hotspot.x
46 x: -imageInfo.hotspot.x
45 y: -imageInfo.hotspot.y
47 y: -imageInfo.hotspot.y
46− source: "image://cursor/" + root.themeName + "/" + root.cursorName
48+ source: "image://cursor/" + root.themeName + "/" + root.cursorName + "/" + root.cursorHeight
47
49
48 interpolate: false
50 interpolate: false
49
51
@@ -110,6 +112,10 @@ Rectangle {
110
112
111 Item {width: 10; height: 40}
113 Item {width: 10; height: 40}
112
114
115+ TextEntry { id: cursorHeightEntry; name: "cursorHeight"; value: "32" }
116+
117+ Item {width: 10; height: 40}
118+
113 Rectangle {
119 Rectangle {
114 color: applyMouseArea.pressed ? "green" : "lightslategray"
120 color: applyMouseArea.pressed ? "green" : "lightslategray"
115 width: parent.width - 20
121 width: parent.width - 20
@@ -121,6 +127,7 @@ Rectangle {
121 onClicked: {
127 onClicked: {
122 root.themeName = themeNameEntry.value;
128 root.themeName = themeNameEntry.value;
123 root.cursorName = cursorNameEntry.value;
129 root.cursorName = cursorNameEntry.value;
130+ root.cursorHeight = cursorHeightEntry.value;
124 }
131 }
125 }
132 }
126 }
133 }
tests/plugins/Dash/cardcreator/1.res+8−11
@@ -12,23 +12,21 @@ AbstractButton {
12 property size fixedArtShapeSize: Qt.size(-1, -1);
12 property size fixedArtShapeSize: Qt.size(-1, -1);
13signal action(var actionId);
13signal action(var actionId);
14readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
14readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
15−Item {
16− id: artShapeHolder;
17− height: root.fixedArtShapeSize.height;
18− width: root.fixedArtShapeSize.width;
19− anchors { horizontalCenter: parent.horizontalCenter; }
20− Loader {
15+Loader {
21 id: artShapeLoader;
16 id: artShapeLoader;
17+ height: root.fixedArtShapeSize.height;
18+ width: root.fixedArtShapeSize.width;
19+ anchors { horizontalCenter: parent.horizontalCenter; }
22 objectName: "artShapeLoader";
20 objectName: "artShapeLoader";
23 readonly property string cardArt: cardData && cardData["art"] || "";
21 readonly property string cardArt: cardData && cardData["art"] || "";
24 onCardArtChanged: { if (item) { item.image.source = cardArt; } }
22 onCardArtChanged: { if (item) { item.image.source = cardArt; } }
25 active: cardArt != "";
23 active: cardArt != "";
26 asynchronous: true;
24 asynchronous: true;
27− visible: status == Loader.Ready;
25+ visible: status === Loader.Ready;
28 sourceComponent: Item {
26 sourceComponent: Item {
29 id: artShape;
27 id: artShape;
30 objectName: "artShape";
28 objectName: "artShape";
31− visible: image.status == Image.Ready;
29+ visible: image.status === Image.Ready;
32 readonly property alias image: artImage;
30 readonly property alias image: artImage;
33 UbuntuShape {
31 UbuntuShape {
34 anchors.fill: parent;
32 anchors.fill: parent;
@@ -50,14 +48,13 @@ Item {
50 }
48 }
51 }
49 }
52 }
50 }
53− }
54readonly property int headerHeight: titleLabel.height;
51readonly property int headerHeight: titleLabel.height;
55Label {
52Label {
56 id: titleLabel;
53 id: titleLabel;
57 objectName: "titleLabel";
54 objectName: "titleLabel";
58 anchors { right: parent.right;
55 anchors { right: parent.right;
59 left: parent.left;
56 left: parent.left;
60− top: artShapeHolder.bottom;
57+ top: artShapeLoader.bottom;
61 topMargin: units.gu(1);
58 topMargin: units.gu(1);
62 }
59 }
63 elide: Text.ElideRight;
60 elide: Text.ElideRight;
@@ -75,7 +72,7 @@ Label {
75UbuntuShape {
72UbuntuShape {
76 id: touchdown;
73 id: touchdown;
77 objectName: "touchdown";
74 objectName: "touchdown";
78− anchors { fill: artShapeHolder }
75+ anchors { fill: artShapeLoader }
79 visible: root.pressed;
76 visible: root.pressed;
80 radius: "medium";
77 radius: "medium";
81 borderSource: "radius_pressed.sci"
78 borderSource: "radius_pressed.sci"
tests/plugins/Dash/cardcreator/1.res.cardcreator+6−11
@@ -10,23 +10,19 @@ AbstractButton {
10 enabled: true;
10 enabled: true;
11signal action(var actionId);
11signal action(var actionId);
12readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
12readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
13−Item {
14− id: artShapeHolder;
15− height: artShapeLoader.height;
16− width: artShapeLoader.width;
17− anchors { horizontalCenter: parent.horizontalCenter; }
18− Loader {
13+Loader {
19 id: artShapeLoader;
14 id: artShapeLoader;
15+ anchors { horizontalCenter: parent.horizontalCenter; }
20 objectName: "artShapeLoader";
16 objectName: "artShapeLoader";
21 readonly property string cardArt: cardData && cardData["art"] || "";
17 readonly property string cardArt: cardData && cardData["art"] || "";
22 onCardArtChanged: { if (item) { item.image.source = cardArt; } }
18 onCardArtChanged: { if (item) { item.image.source = cardArt; } }
23 active: cardArt != "";
19 active: cardArt != "";
24 asynchronous: false;
20 asynchronous: false;
25− visible: status == Loader.Ready;
21+ visible: status === Loader.Ready;
26 sourceComponent: Item {
22 sourceComponent: Item {
27 id: artShape;
23 id: artShape;
28 objectName: "artShape";
24 objectName: "artShape";
29− visible: image.status == Image.Ready;
25+ visible: image.status === Image.Ready;
30 readonly property alias image: artImage;
26 readonly property alias image: artImage;
31 UbuntuShape {
27 UbuntuShape {
32 anchors.fill: parent;
28 anchors.fill: parent;
@@ -47,7 +43,6 @@ Item {
47 height: width / 1.6;
43 height: width / 1.6;
48 }
44 }
49 }
45 }
50− }
51 }
46 }
52readonly property int headerHeight: titleLabel.height;
47readonly property int headerHeight: titleLabel.height;
53Label {
48Label {
@@ -55,7 +50,7 @@ Label {
55 objectName: "titleLabel";
50 objectName: "titleLabel";
56 anchors { right: parent.right;
51 anchors { right: parent.right;
57 left: parent.left;
52 left: parent.left;
58− top: artShapeHolder.bottom;
53+ top: artShapeLoader.bottom;
59 topMargin: units.gu(1);
54 topMargin: units.gu(1);
60 }
55 }
61 elide: Text.ElideRight;
56 elide: Text.ElideRight;
@@ -73,7 +68,7 @@ Label {
73UbuntuShape {
68UbuntuShape {
74 id: touchdown;
69 id: touchdown;
75 objectName: "touchdown";
70 objectName: "touchdown";
76− anchors { fill: artShapeHolder }
71+ anchors { fill: artShapeLoader }
77 visible: root.pressed;
72 visible: root.pressed;
78 radius: "medium";
73 radius: "medium";
79 borderSource: "radius_pressed.sci"
74 borderSource: "radius_pressed.sci"
tests/plugins/Dash/cardcreator/10.res+1−1
@@ -16,7 +16,7 @@ Loader {
16 objectName: "backgroundLoader";
16 objectName: "backgroundLoader";
17 anchors.fill: parent;
17 anchors.fill: parent;
18 asynchronous: true;
18 asynchronous: true;
19− visible: status == Loader.Ready;
19+ visible: status === Loader.Ready;
20 sourceComponent: UbuntuShape {
20 sourceComponent: UbuntuShape {
21 objectName: "background";
21 objectName: "background";
22 radius: "medium";
22 radius: "medium";
tests/plugins/Dash/cardcreator/10.res.cardcreator+1−1
@@ -14,7 +14,7 @@ Loader {
14 objectName: "backgroundLoader";
14 objectName: "backgroundLoader";
15 anchors.fill: parent;
15 anchors.fill: parent;
16 asynchronous: false;
16 asynchronous: false;
17− visible: status == Loader.Ready;
17+ visible: status === Loader.Ready;
18 sourceComponent: UbuntuShape {
18 sourceComponent: UbuntuShape {
19 objectName: "background";
19 objectName: "background";
20 radius: "medium";
20 radius: "medium";
tests/plugins/Dash/cardcreator/11.res+9−12
@@ -16,7 +16,7 @@ Loader {
16 objectName: "backgroundLoader";
16 objectName: "backgroundLoader";
17 anchors.fill: parent;
17 anchors.fill: parent;
18 asynchronous: true;
18 asynchronous: true;
19− visible: status == Loader.Ready;
19+ visible: status === Loader.Ready;
20 sourceComponent: UbuntuShape {
20 sourceComponent: UbuntuShape {
21 objectName: "background";
21 objectName: "background";
22 radius: "medium";
22 radius: "medium";
@@ -50,23 +50,21 @@ Loader {
50 }
50 }
51 }
51 }
52readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
52readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
53−Item {
54− id: artShapeHolder;
55− height: root.fixedArtShapeSize.height;
56− width: root.fixedArtShapeSize.width;
57− anchors { horizontalCenter: parent.horizontalCenter; }
58− Loader {
53+Loader {
59 id: artShapeLoader;
54 id: artShapeLoader;
55+ height: root.fixedArtShapeSize.height;
56+ width: root.fixedArtShapeSize.width;
57+ anchors { horizontalCenter: parent.horizontalCenter; }
60 objectName: "artShapeLoader";
58 objectName: "artShapeLoader";
61 readonly property string cardArt: cardData && cardData["art"] || decodeURI("%5C");
59 readonly property string cardArt: cardData && cardData["art"] || decodeURI("%5C");
62 onCardArtChanged: { if (item) { item.image.source = cardArt; } }
60 onCardArtChanged: { if (item) { item.image.source = cardArt; } }
63 active: cardArt != "";
61 active: cardArt != "";
64 asynchronous: true;
62 asynchronous: true;
65− visible: status == Loader.Ready;
63+ visible: status === Loader.Ready;
66 sourceComponent: Item {
64 sourceComponent: Item {
67 id: artShape;
65 id: artShape;
68 objectName: "artShape";
66 objectName: "artShape";
69− visible: image.status == Image.Ready;
67+ visible: image.status === Image.Ready;
70 readonly property alias image: artImage;
68 readonly property alias image: artImage;
71 UbuntuShape {
69 UbuntuShape {
72 anchors.fill: parent;
70 anchors.fill: parent;
@@ -87,8 +85,7 @@ Item {
87 height: width / (root.fixedArtShapeSize.width / root.fixedArtShapeSize.height);
85 height: width / (root.fixedArtShapeSize.width / root.fixedArtShapeSize.height);
88 onStatusChanged: if (status === Image.Error) source = decodeURI("%5C");
86 onStatusChanged: if (status === Image.Error) source = decodeURI("%5C");
89 }
87 }
90− }
88+ }
91− }
92 }
89 }
93readonly property int headerHeight: row.height;
90readonly property int headerHeight: row.height;
94Row {
91Row {
@@ -97,7 +94,7 @@ Row {
97 property real margins: units.gu(1);
94 property real margins: units.gu(1);
98 spacing: margins;
95 spacing: margins;
99 height: root.fixedHeaderHeight;
96 height: root.fixedHeaderHeight;
100− anchors { top: artShapeHolder.bottom;
97+ anchors { top: artShapeLoader.bottom;
101 topMargin: units.gu(1);
98 topMargin: units.gu(1);
102left: parent.left;
99left: parent.left;
103 }
100 }
tests/plugins/Dash/cardcreator/11.res.cardcreator+6−11
@@ -14,7 +14,7 @@ Loader {
14 objectName: "backgroundLoader";
14 objectName: "backgroundLoader";
15 anchors.fill: parent;
15 anchors.fill: parent;
16 asynchronous: false;
16 asynchronous: false;
17− visible: status == Loader.Ready;
17+ visible: status === Loader.Ready;
18 sourceComponent: UbuntuShape {
18 sourceComponent: UbuntuShape {
19 objectName: "background";
19 objectName: "background";
20 radius: "medium";
20 radius: "medium";
@@ -48,23 +48,19 @@ Loader {
48 }
48 }
49 }
49 }
50readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
50readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
51−Item {
52− id: artShapeHolder;
53− height: artShapeLoader.height;
54− width: artShapeLoader.width;
55− anchors { horizontalCenter: parent.horizontalCenter; }
56− Loader {
51+Loader {
57 id: artShapeLoader;
52 id: artShapeLoader;
53+ anchors { horizontalCenter: parent.horizontalCenter; }
58 objectName: "artShapeLoader";
54 objectName: "artShapeLoader";
59 readonly property string cardArt: cardData && cardData["art"] || "";
55 readonly property string cardArt: cardData && cardData["art"] || "";
60 onCardArtChanged: { if (item) { item.image.source = cardArt; } }
56 onCardArtChanged: { if (item) { item.image.source = cardArt; } }
61 active: cardArt != "";
57 active: cardArt != "";
62 asynchronous: false;
58 asynchronous: false;
63− visible: status == Loader.Ready;
59+ visible: status === Loader.Ready;
64 sourceComponent: Item {
60 sourceComponent: Item {
65 id: artShape;
61 id: artShape;
66 objectName: "artShape";
62 objectName: "artShape";
67− visible: image.status == Image.Ready;
63+ visible: image.status === Image.Ready;
68 readonly property alias image: artImage;
64 readonly property alias image: artImage;
69 UbuntuShape {
65 UbuntuShape {
70 anchors.fill: parent;
66 anchors.fill: parent;
@@ -85,7 +81,6 @@ Item {
85 height: width / 1;
81 height: width / 1;
86 }
82 }
87 }
83 }
88− }
89 }
84 }
90readonly property int headerHeight: row.height;
85readonly property int headerHeight: row.height;
91Row {
86Row {
@@ -93,7 +88,7 @@ Row {
93 objectName: "outerRow";
88 objectName: "outerRow";
94 property real margins: units.gu(1);
89 property real margins: units.gu(1);
95 spacing: margins;
90 spacing: margins;
96− anchors { top: artShapeHolder.bottom;
91+ anchors { top: artShapeLoader.bottom;
97 topMargin: units.gu(1);
92 topMargin: units.gu(1);
98left: parent.left;
93left: parent.left;
99 }
94 }
tests/plugins/Dash/cardcreator/12.res+8−11
@@ -12,23 +12,21 @@ AbstractButton {
12 property size fixedArtShapeSize: Qt.size(-1, -1);
12 property size fixedArtShapeSize: Qt.size(-1, -1);
13signal action(var actionId);
13signal action(var actionId);
14readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
14readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
15−Item {
16− id: artShapeHolder;
17− height: root.fixedArtShapeSize.height;
18− width: root.fixedArtShapeSize.width;
19− anchors { horizontalCenter: parent.horizontalCenter; }
20− Loader {
15+Loader {
21 id: artShapeLoader;
16 id: artShapeLoader;
17+ height: root.fixedArtShapeSize.height;
18+ width: root.fixedArtShapeSize.width;
19+ anchors { horizontalCenter: parent.horizontalCenter; }
22 objectName: "artShapeLoader";
20 objectName: "artShapeLoader";
23 readonly property string cardArt: cardData && cardData["art"] || "";
21 readonly property string cardArt: cardData && cardData["art"] || "";
24 onCardArtChanged: { if (item) { item.image.source = cardArt; } }
22 onCardArtChanged: { if (item) { item.image.source = cardArt; } }
25 active: cardArt != "";
23 active: cardArt != "";
26 asynchronous: true;
24 asynchronous: true;
27− visible: status == Loader.Ready;
25+ visible: status === Loader.Ready;
28 sourceComponent: Item {
26 sourceComponent: Item {
29 id: artShape;
27 id: artShape;
30 objectName: "artShape";
28 objectName: "artShape";
31− visible: image.status == Image.Ready;
29+ visible: image.status === Image.Ready;
32 readonly property alias image: artImage;
30 readonly property alias image: artImage;
33 UbuntuShape {
31 UbuntuShape {
34 anchors.fill: parent;
32 anchors.fill: parent;
@@ -49,7 +47,6 @@ Item {
49 height: width / (root.fixedArtShapeSize.width / root.fixedArtShapeSize.height);
47 height: width / (root.fixedArtShapeSize.width / root.fixedArtShapeSize.height);
50 }
48 }
51 }
49 }
52− }
53 }
50 }
54readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin;
51readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin;
55Label {
52Label {
@@ -57,7 +54,7 @@ Label {
57 objectName: "titleLabel";
54 objectName: "titleLabel";
58 anchors { right: parent.right;
55 anchors { right: parent.right;
59 left: parent.left;
56 left: parent.left;
60− top: artShapeHolder.bottom;
57+ top: artShapeLoader.bottom;
61 topMargin: units.gu(1);
58 topMargin: units.gu(1);
62 }
59 }
63 elide: Text.ElideRight;
60 elide: Text.ElideRight;
@@ -101,7 +98,7 @@ CardSocialActions {
101UbuntuShape {
98UbuntuShape {
102 id: touchdown;
99 id: touchdown;
103 objectName: "touchdown";
100 objectName: "touchdown";
104− anchors { fill: artShapeHolder }
101+ anchors { fill: artShapeLoader }
105 visible: root.pressed;
102 visible: root.pressed;
106 radius: "medium";
103 radius: "medium";
107 borderSource: "radius_pressed.sci"
104 borderSource: "radius_pressed.sci"
tests/plugins/Dash/cardcreator/12.res.cardcreator+6−11
@@ -10,23 +10,19 @@ AbstractButton {
10 enabled: true;
10 enabled: true;
11signal action(var actionId);
11signal action(var actionId);
12readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
12readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
13−Item {
14− id: artShapeHolder;
15− height: artShapeLoader.height;
16− width: artShapeLoader.width;
17− anchors { horizontalCenter: parent.horizontalCenter; }
18− Loader {
13+Loader {
19 id: artShapeLoader;
14 id: artShapeLoader;
15+ anchors { horizontalCenter: parent.horizontalCenter; }
20 objectName: "artShapeLoader";
16 objectName: "artShapeLoader";
21 readonly property string cardArt: cardData && cardData["art"] || "";
17 readonly property string cardArt: cardData && cardData["art"] || "";
22 onCardArtChanged: { if (item) { item.image.source = cardArt; } }
18 onCardArtChanged: { if (item) { item.image.source = cardArt; } }
23 active: cardArt != "";
19 active: cardArt != "";
24 asynchronous: false;
20 asynchronous: false;
25− visible: status == Loader.Ready;
21+ visible: status === Loader.Ready;
26 sourceComponent: Item {
22 sourceComponent: Item {
27 id: artShape;
23 id: artShape;
28 objectName: "artShape";
24 objectName: "artShape";
29− visible: image.status == Image.Ready;
25+ visible: image.status === Image.Ready;
30 readonly property alias image: artImage;
26 readonly property alias image: artImage;
31 UbuntuShape {
27 UbuntuShape {
32 anchors.fill: parent;
28 anchors.fill: parent;
@@ -47,7 +43,6 @@ Item {
47 height: width / 0.75;
43 height: width / 0.75;
48 }
44 }
49 }
45 }
50− }
51 }
46 }
52readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin;
47readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin;
53Label {
48Label {
@@ -55,7 +50,7 @@ Label {
55 objectName: "titleLabel";
50 objectName: "titleLabel";
56 anchors { right: parent.right;
51 anchors { right: parent.right;
57 left: parent.left;
52 left: parent.left;
58− top: artShapeHolder.bottom;
53+ top: artShapeLoader.bottom;
59 topMargin: units.gu(1);
54 topMargin: units.gu(1);
60 }
55 }
61 elide: Text.ElideRight;
56 elide: Text.ElideRight;
@@ -99,7 +94,7 @@ CardSocialActions {
99UbuntuShape {
94UbuntuShape {
100 id: touchdown;
95 id: touchdown;
101 objectName: "touchdown";
96 objectName: "touchdown";
102− anchors { fill: artShapeHolder }
97+ anchors { fill: artShapeLoader }
103 visible: root.pressed;
98 visible: root.pressed;
104 radius: "medium";
99 radius: "medium";
105 borderSource: "radius_pressed.sci"
100 borderSource: "radius_pressed.sci"
tests/plugins/Dash/cardcreator/2.res+1−1
@@ -16,7 +16,7 @@ Loader {
16 objectName: "backgroundLoader";
16 objectName: "backgroundLoader";
17 anchors.fill: parent;
17 anchors.fill: parent;
18 asynchronous: true;
18 asynchronous: true;
19− visible: status == Loader.Ready;
19+ visible: status === Loader.Ready;
20 sourceComponent: UbuntuShape {
20 sourceComponent: UbuntuShape {
21 objectName: "background";
21 objectName: "background";
22 radius: "medium";
22 radius: "medium";
tests/plugins/Dash/cardcreator/2.res.cardcreator+1−1
@@ -14,7 +14,7 @@ Loader {
14 objectName: "backgroundLoader";
14 objectName: "backgroundLoader";
15 anchors.fill: parent;
15 anchors.fill: parent;
16 asynchronous: false;
16 asynchronous: false;
17− visible: status == Loader.Ready;
17+ visible: status === Loader.Ready;
18 sourceComponent: UbuntuShape {
18 sourceComponent: UbuntuShape {
19 objectName: "background";
19 objectName: "background";
20 radius: "medium";
20 radius: "medium";
tests/plugins/Dash/cardcreator/3.res+7−10
@@ -12,23 +12,21 @@ AbstractButton {
12 property size fixedArtShapeSize: Qt.size(-1, -1);
12 property size fixedArtShapeSize: Qt.size(-1, -1);
13signal action(var actionId);
13signal action(var actionId);
14readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
14readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
15−Item {
16− id: artShapeHolder;
17− height: root.fixedArtShapeSize.height;
18− width: root.fixedArtShapeSize.width;
19− anchors { horizontalCenter: parent.horizontalCenter; }
20− Loader {
15+Loader {
21 id: artShapeLoader;
16 id: artShapeLoader;
17+ height: root.fixedArtShapeSize.height;
18+ width: root.fixedArtShapeSize.width;
19+ anchors { horizontalCenter: parent.horizontalCenter; }
22 objectName: "artShapeLoader";
20 objectName: "artShapeLoader";
23 readonly property string cardArt: cardData && cardData["art"] || decodeURI("IHAVE%5C%22ESCAPED%5C%22QUOTES%5C%22");
21 readonly property string cardArt: cardData && cardData["art"] || decodeURI("IHAVE%5C%22ESCAPED%5C%22QUOTES%5C%22");
24 onCardArtChanged: { if (item) { item.image.source = cardArt; } }
22 onCardArtChanged: { if (item) { item.image.source = cardArt; } }
25 active: cardArt != "";
23 active: cardArt != "";
26 asynchronous: true;
24 asynchronous: true;
27− visible: status == Loader.Ready;
25+ visible: status === Loader.Ready;
28 sourceComponent: Item {
26 sourceComponent: Item {
29 id: artShape;
27 id: artShape;
30 objectName: "artShape";
28 objectName: "artShape";
31− visible: image.status == Image.Ready;
29+ visible: image.status === Image.Ready;
32 readonly property alias image: artImage;
30 readonly property alias image: artImage;
33 ProportionalShape {
31 ProportionalShape {
34 anchors.left: parent.left;
32 anchors.left: parent.left;
@@ -49,7 +47,6 @@ Item {
49 onStatusChanged: if (status === Image.Error) source = decodeURI("IHAVE%5C%22ESCAPED%5C%22QUOTES%5C%22");
47 onStatusChanged: if (status === Image.Error) source = decodeURI("IHAVE%5C%22ESCAPED%5C%22QUOTES%5C%22");
50 }
48 }
51 }
49 }
52− }
53 }
50 }
54readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin;
51readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin;
55Label {
52Label {
@@ -57,7 +54,7 @@ Label {
57 objectName: "titleLabel";
54 objectName: "titleLabel";
58 anchors { right: parent.right;
55 anchors { right: parent.right;
59 left: parent.left;
56 left: parent.left;
60− top: artShapeHolder.bottom;
57+ top: artShapeLoader.bottom;
61 topMargin: units.gu(1);
58 topMargin: units.gu(1);
62 }
59 }
63 elide: Text.ElideRight;
60 elide: Text.ElideRight;
tests/plugins/Dash/cardcreator/3.res.cardcreator+6−11
@@ -10,23 +10,19 @@ AbstractButton {
10 enabled: true;
10 enabled: true;
11signal action(var actionId);
11signal action(var actionId);
12readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
12readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
13−Item {
13+Loader {
14− id: artShapeHolder;
15− height: artShapeLoader.height;
16− width: artShapeLoader.width;
14+ id: artShapeLoader;
17− anchors { horizontalCenter: parent.horizontalCenter; }
15+ anchors { horizontalCenter: parent.horizontalCenter; }
18− Loader {
19− id: artShapeLoader;
20 objectName: "artShapeLoader";
16 objectName: "artShapeLoader";
21 readonly property string cardArt: cardData && cardData["art"] || "";
17 readonly property string cardArt: cardData && cardData["art"] || "";
22 onCardArtChanged: { if (item) { item.image.source = cardArt; } }
18 onCardArtChanged: { if (item) { item.image.source = cardArt; } }
23 active: cardArt != "";
19 active: cardArt != "";
24 asynchronous: false;
20 asynchronous: false;
25− visible: status == Loader.Ready;
21+ visible: status === Loader.Ready;
26 sourceComponent: Item {
22 sourceComponent: Item {
27 id: artShape;
23 id: artShape;
28 objectName: "artShape";
24 objectName: "artShape";
29− visible: image.status == Image.Ready;
25+ visible: image.status === Image.Ready;
30 readonly property alias image: artImage;
26 readonly property alias image: artImage;
31 ProportionalShape {
27 ProportionalShape {
32 anchors.left: parent.left;
28 anchors.left: parent.left;
@@ -46,7 +42,6 @@ Item {
46 height: width / 0.75;
42 height: width / 0.75;
47 }
43 }
48 }
44 }
49− }
50 }
45 }
51readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin;
46readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin;
52Label {
47Label {
@@ -54,7 +49,7 @@ Label {
54 objectName: "titleLabel";
49 objectName: "titleLabel";
55 anchors { right: parent.right;
50 anchors { right: parent.right;
56 left: parent.left;
51 left: parent.left;
57− top: artShapeHolder.bottom;
52+ top: artShapeLoader.bottom;
58 topMargin: units.gu(1);
53 topMargin: units.gu(1);
59 }
54 }
60 elide: Text.ElideRight;
55 elide: Text.ElideRight;
tests/plugins/Dash/cardcreator/4.res+1−1
@@ -32,7 +32,7 @@ Loader {
32 objectName: "mascotShapeLoader";
32 objectName: "mascotShapeLoader";
33 asynchronous: true;
33 asynchronous: true;
34 active: mascotImage.status === Image.Ready;
34 active: mascotImage.status === Image.Ready;
35− visible: showHeader && active && status == Loader.Ready;
35+ visible: showHeader && active && status === Loader.Ready;
36 width: units.gu(6);
36 width: units.gu(6);
37 height: units.gu(5.625);
37 height: units.gu(5.625);
38 sourceComponent: UbuntuShape { image: mascotImage }
38 sourceComponent: UbuntuShape { image: mascotImage }
tests/plugins/Dash/cardcreator/4.res.cardcreator+1−1
@@ -29,7 +29,7 @@ Loader {
29 objectName: "mascotShapeLoader";
29 objectName: "mascotShapeLoader";
30 asynchronous: false;
30 asynchronous: false;
31 active: mascotImage.status === Image.Ready;
31 active: mascotImage.status === Image.Ready;
32− visible: showHeader && active && status == Loader.Ready;
32+ visible: showHeader && active && status === Loader.Ready;
33 width: units.gu(6);
33 width: units.gu(6);
34 height: units.gu(5.625);
34 height: units.gu(5.625);
35 sourceComponent: UbuntuShape { image: mascotImage }
35 sourceComponent: UbuntuShape { image: mascotImage }
tests/plugins/Dash/cardcreator/5.res+9−12
@@ -12,23 +12,21 @@ AbstractButton {
12 property size fixedArtShapeSize: Qt.size(-1, -1);
12 property size fixedArtShapeSize: Qt.size(-1, -1);
13signal action(var actionId);
13signal action(var actionId);
14readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
14readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
15−Item {
16− id: artShapeHolder;
17− height: root.fixedArtShapeSize.height;
18− width: root.fixedArtShapeSize.width;
19− anchors { horizontalCenter: parent.horizontalCenter; }
20− Loader {
15+Loader {
21 id: artShapeLoader;
16 id: artShapeLoader;
17+ height: root.fixedArtShapeSize.height;
18+ width: root.fixedArtShapeSize.width;
19+ anchors { horizontalCenter: parent.horizontalCenter; }
22 objectName: "artShapeLoader";
20 objectName: "artShapeLoader";
23 readonly property string cardArt: cardData && cardData["art"] || "";
21 readonly property string cardArt: cardData && cardData["art"] || "";
24 onCardArtChanged: { if (item) { item.image.source = cardArt; } }
22 onCardArtChanged: { if (item) { item.image.source = cardArt; } }
25 active: cardArt != "";
23 active: cardArt != "";
26 asynchronous: true;
24 asynchronous: true;
27− visible: status == Loader.Ready;
25+ visible: status === Loader.Ready;
28 sourceComponent: Item {
26 sourceComponent: Item {
29 id: artShape;
27 id: artShape;
30 objectName: "artShape";
28 objectName: "artShape";
31− visible: image.status == Image.Ready;
29+ visible: image.status === Image.Ready;
32 readonly property alias image: artImage;
30 readonly property alias image: artImage;
33 width: root.fixedArtShapeSize.width;
31 width: root.fixedArtShapeSize.width;
34 height: root.fixedArtShapeSize.height;
32 height: root.fixedArtShapeSize.height;
@@ -42,15 +40,14 @@ Item {
42 height: width / (root.fixedArtShapeSize.width / root.fixedArtShapeSize.height);
40 height: width / (root.fixedArtShapeSize.width / root.fixedArtShapeSize.height);
43 }
41 }
44 }
42 }
45− }
46 }
43 }
47Loader {
44Loader {
48 id: overlayLoader;
45 id: overlayLoader;
49 readonly property real overlayHeight: root.fixedHeaderHeight + units.gu(2);
46 readonly property real overlayHeight: root.fixedHeaderHeight + units.gu(2);
50− anchors.fill: artShapeHolder;
47+ anchors.fill: artShapeLoader;
51 active: artShapeLoader.active && artShapeLoader.item && artShapeLoader.item.image.status === Image.Ready || false;
48 active: artShapeLoader.active && artShapeLoader.item && artShapeLoader.item.image.status === Image.Ready || false;
52 asynchronous: true;
49 asynchronous: true;
53− visible: showHeader && status == Loader.Ready;
50+ visible: showHeader && status === Loader.Ready;
54 sourceComponent: UbuntuShapeOverlay {
51 sourceComponent: UbuntuShapeOverlay {
55 id: overlay;
52 id: overlay;
56 property real luminance: Style.luminance(overlayColor);
53 property real luminance: Style.luminance(overlayColor);
@@ -102,5 +99,5 @@ Label {
102 text: cardData && cardData["subtitle"] || "";
99 text: cardData && cardData["subtitle"] || "";
103 font.weight: Font.Light;
100 font.weight: Font.Light;
104 }
101 }
105−implicitHeight: artShapeHolder.height;
102+implicitHeight: artShapeLoader.height;
106}
103}
tests/plugins/Dash/cardcreator/5.res.cardcreator+7−12
@@ -10,23 +10,19 @@ AbstractButton {
10 enabled: false;
10 enabled: false;
11signal action(var actionId);
11signal action(var actionId);
12readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
12readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
13−Item {
14− id: artShapeHolder;
15− height: artShapeLoader.height;
16− width: artShapeLoader.width;
17− anchors { horizontalCenter: parent.horizontalCenter; }
18− Loader {
13+Loader {
19 id: artShapeLoader;
14 id: artShapeLoader;
15+ anchors { horizontalCenter: parent.horizontalCenter; }
20 objectName: "artShapeLoader";
16 objectName: "artShapeLoader";
21 readonly property string cardArt: cardData && cardData["art"] || "";
17 readonly property string cardArt: cardData && cardData["art"] || "";
22 onCardArtChanged: { if (item) { item.image.source = cardArt; } }
18 onCardArtChanged: { if (item) { item.image.source = cardArt; } }
23 active: cardArt != "";
19 active: cardArt != "";
24 asynchronous: false;
20 asynchronous: false;
25− visible: status == Loader.Ready;
21+ visible: status === Loader.Ready;
26 sourceComponent: Item {
22 sourceComponent: Item {
27 id: artShape;
23 id: artShape;
28 objectName: "artShape";
24 objectName: "artShape";
29− visible: image.status == Image.Ready;
25+ visible: image.status === Image.Ready;
30 readonly property alias image: artImage;
26 readonly property alias image: artImage;
31 width: image.status !== Image.Ready ? 0 : image.width;
27 width: image.status !== Image.Ready ? 0 : image.width;
32 height: image.status !== Image.Ready ? 0 : image.height;
28 height: image.status !== Image.Ready ? 0 : image.height;
@@ -40,15 +36,14 @@ Item {
40 height: width / 1;
36 height: width / 1;
41 }
37 }
42 }
38 }
43− }
44 }
39 }
45Loader {
40Loader {
46 id: overlayLoader;
41 id: overlayLoader;
47 readonly property real overlayHeight: headerHeight + units.gu(2);
42 readonly property real overlayHeight: headerHeight + units.gu(2);
48− anchors.fill: artShapeHolder;
43+ anchors.fill: artShapeLoader;
49 active: artShapeLoader.active && artShapeLoader.item && artShapeLoader.item.image.status === Image.Ready || false;
44 active: artShapeLoader.active && artShapeLoader.item && artShapeLoader.item.image.status === Image.Ready || false;
50 asynchronous: false;
45 asynchronous: false;
51− visible: showHeader && status == Loader.Ready;
46+ visible: showHeader && status === Loader.Ready;
52 sourceComponent: UbuntuShapeOverlay {
47 sourceComponent: UbuntuShapeOverlay {
53 id: overlay;
48 id: overlay;
54 property real luminance: Style.luminance(overlayColor);
49 property real luminance: Style.luminance(overlayColor);
@@ -100,5 +95,5 @@ Label {
100 text: cardData && cardData["subtitle"] || "";
95 text: cardData && cardData["subtitle"] || "";
101 font.weight: Font.Light;
96 font.weight: Font.Light;
102 }
97 }
103−implicitHeight: artShapeHolder.height;
98+implicitHeight: artShapeLoader.height;
104}
99}
tests/plugins/Dash/cardcreator/6.res+1−1
@@ -16,7 +16,7 @@ Loader {
16 objectName: "backgroundLoader";
16 objectName: "backgroundLoader";
17 anchors.fill: parent;
17 anchors.fill: parent;
18 asynchronous: true;
18 asynchronous: true;
19− visible: status == Loader.Ready;
19+ visible: status === Loader.Ready;
20 sourceComponent: UbuntuShape {
20 sourceComponent: UbuntuShape {
21 objectName: "background";
21 objectName: "background";
22 radius: "medium";
22 radius: "medium";
tests/plugins/Dash/cardcreator/6.res.cardcreator+1−1
@@ -14,7 +14,7 @@ Loader {
14 objectName: "backgroundLoader";
14 objectName: "backgroundLoader";
15 anchors.fill: parent;
15 anchors.fill: parent;
16 asynchronous: false;
16 asynchronous: false;
17− visible: status == Loader.Ready;
17+ visible: status === Loader.Ready;
18 sourceComponent: UbuntuShape {
18 sourceComponent: UbuntuShape {
19 objectName: "background";
19 objectName: "background";
20 radius: "medium";
20 radius: "medium";
tests/plugins/Dash/cardcreator/7.res+1−1
@@ -16,7 +16,7 @@ Loader {
16 objectName: "backgroundLoader";
16 objectName: "backgroundLoader";
17 anchors.fill: parent;
17 anchors.fill: parent;
18 asynchronous: true;
18 asynchronous: true;
19− visible: status == Loader.Ready;
19+ visible: status === Loader.Ready;
20 sourceComponent: UbuntuShape {
20 sourceComponent: UbuntuShape {
21 objectName: "background";
21 objectName: "background";
22 radius: "medium";
22 radius: "medium";
tests/plugins/Dash/cardcreator/7.res.cardcreator+1−1
@@ -14,7 +14,7 @@ Loader {
14 objectName: "backgroundLoader";
14 objectName: "backgroundLoader";
15 anchors.fill: parent;
15 anchors.fill: parent;
16 asynchronous: false;
16 asynchronous: false;
17− visible: status == Loader.Ready;
17+ visible: status === Loader.Ready;
18 sourceComponent: UbuntuShape {
18 sourceComponent: UbuntuShape {
19 objectName: "background";
19 objectName: "background";
20 radius: "medium";
20 radius: "medium";
tests/plugins/Dash/cardcreator/8.res+1−1
@@ -16,7 +16,7 @@ Loader {
16 objectName: "backgroundLoader";
16 objectName: "backgroundLoader";
17 anchors.fill: parent;
17 anchors.fill: parent;
18 asynchronous: true;
18 asynchronous: true;
19− visible: status == Loader.Ready;
19+ visible: status === Loader.Ready;
20 sourceComponent: UbuntuShape {
20 sourceComponent: UbuntuShape {
21 objectName: "background";
21 objectName: "background";
22 radius: "medium";
22 radius: "medium";
tests/plugins/Dash/cardcreator/8.res.cardcreator+1−1
@@ -14,7 +14,7 @@ Loader {
14 objectName: "backgroundLoader";
14 objectName: "backgroundLoader";
15 anchors.fill: parent;
15 anchors.fill: parent;
16 asynchronous: false;
16 asynchronous: false;
17− visible: status == Loader.Ready;
17+ visible: status === Loader.Ready;
18 sourceComponent: UbuntuShape {
18 sourceComponent: UbuntuShape {
19 objectName: "background";
19 objectName: "background";
20 radius: "medium";
20 radius: "medium";
tests/plugins/Dash/verticaljournaltest.cpp+62
@@ -72,6 +72,20 @@ public:
72 endRemoveRows();
72 endRemoveRows();
73 }
73 }
74
74
75+ void insertString(int index, const QString& string)
76+ {
77+ beginInsertRows(QModelIndex(), index, index);
78+ m_list.insert(index, string);
79+ endInsertRows();
80+ }
81+
82+ void changeString(int i, const QString& string)
83+ {
84+ m_list[i] = string;
85+ auto idx = index(i, 0);
86+ dataChanged(idx, idx);
87+ }
88+
75private:
89private:
76 QStringList m_list;
90 QStringList m_list;
77};
91};
@@ -488,6 +502,54 @@ private Q_SLOTS:
488 QTRY_COMPARE(vj->implicitHeight(), 0.);
502 QTRY_COMPARE(vj->implicitHeight(), 0.);
489 }
503 }
490
504
505+ void testInsertItem()
506+ {
507+ // Remove a few items from the end so the verifyItem list is smaller
508+ for (int i = 0; i < 12; ++i)
509+ model->removeLast();
510+
511+ model->insertString(0, "200");
512+
513+ QTRY_COMPARE(vj->m_columnVisibleItems.count(), 3);
514+ QTRY_COMPARE(vj->m_columnVisibleItems[0].count(), 2);
515+ QTRY_COMPARE(vj->m_columnVisibleItems[1].count(), 4);
516+ QTRY_COMPARE(vj->m_columnVisibleItems[2].count(), 3);
517+ verifyItem(vj->m_columnVisibleItems[0][0], 0, 0, 0, true);
518+ verifyItem(vj->m_columnVisibleItems[1][0], 1, 160, 0, true);
519+ verifyItem(vj->m_columnVisibleItems[2][0], 2, 320, 0, true);
520+ verifyItem(vj->m_columnVisibleItems[2][1], 3, 320, 60, true);
521+ verifyItem(vj->m_columnVisibleItems[1][1], 4, 160, 110, true);
522+ verifyItem(vj->m_columnVisibleItems[1][2], 5, 160, 130, true);
523+ verifyItem(vj->m_columnVisibleItems[1][3], 6, 160, 180, true);
524+ verifyItem(vj->m_columnVisibleItems[2][2], 7, 320, 195, true);
525+ verifyItem(vj->m_columnVisibleItems[0][1], 8, 0, 210, true);
526+ QCOMPARE(vj->implicitHeight(), 395.);
527+ }
528+
529+ void testChangeItem()
530+ {
531+ // Remove a few items from the end so the verifyItem list is smaller
532+ for (int i = 0; i < 11; ++i)
533+ model->removeLast();
534+
535+ model->changeString(0, "200");
536+
537+ QTRY_COMPARE(vj->m_columnVisibleItems.count(), 3);
538+ QTRY_COMPARE(vj->m_columnVisibleItems[0].count(), 2);
539+ QTRY_COMPARE(vj->m_columnVisibleItems[1].count(), 5);
540+ QTRY_COMPARE(vj->m_columnVisibleItems[2].count(), 2);
541+ verifyItem(vj->m_columnVisibleItems[0][0], 0, 0, 0, true);
542+ verifyItem(vj->m_columnVisibleItems[1][0], 1, 160, 0, true);
543+ verifyItem(vj->m_columnVisibleItems[2][0], 2, 320, 0, true);
544+ verifyItem(vj->m_columnVisibleItems[1][1], 3, 160, 60, true);
545+ verifyItem(vj->m_columnVisibleItems[1][2], 4, 160, 80, true);
546+ verifyItem(vj->m_columnVisibleItems[1][3], 5, 160, 130, true);
547+ verifyItem(vj->m_columnVisibleItems[2][1], 6, 320, 135, true);
548+ verifyItem(vj->m_columnVisibleItems[0][1], 7, 0, 210, true);
549+ verifyItem(vj->m_columnVisibleItems[1][4], 8, 160, 210, true);
550+ QCOMPARE(vj->implicitHeight(), 370.);
551+ }
552+
491private:
553private:
492 QQuickView *view;
554 QQuickView *view;
493 VerticalJournal *vj;
555 VerticalJournal *vj;
tests/qmltests/Dash/Previews/tst_PreviewIconActions.qml+1−1
@@ -82,7 +82,7 @@ Rectangle {
82
82
83 UT.UnityTestCase {
83 UT.UnityTestCase {
84 id: testcase
84 id: testcase
85− name: "PreviewIconActionTest"
85+ name: "PreviewIconActions"
86 when: windowShown
86 when: windowShown
87
87
88 function cleanup()
88 function cleanup()
tests/qmltests/Dash/tst_DashContent.qml+42−5
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright (C) 2013,2016 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -320,7 +320,8 @@ Item {
320 tryCompare(navigationListView.currentItem.navigation, "navigationId", "root");
320 tryCompare(navigationListView.currentItem.navigation, "navigationId", "root");
321
321
322 var headerContainer = findChild(dashContentList.currentItem, "headerContainer");
322 var headerContainer = findChild(dashContentList.currentItem, "headerContainer");
323− tryCompare(headerContainer, "contentY", headerContainer.height);
323+ tryCompare(headerContainer, "clip", false);
324+ verify(headerContainer.state !== "search");
324 mouseClick(searchButton);
325 mouseClick(searchButton);
325 tryCompare(peExtraPanel, "visible", true);
326 tryCompare(peExtraPanel, "visible", true);
326 waitForRendering(navigationListView);
327 waitForRendering(navigationListView);
@@ -384,7 +385,8 @@ Item {
384 mouseClick(header0);
385 mouseClick(header0);
385 compare(peExtraPanel.visible, false);
386 compare(peExtraPanel.visible, false);
386
387
387− tryCompare(headerContainer, "contentY", headerContainer.height);
388+ tryCompare(headerContainer, "clip", false);
389+ verify(headerContainer.state !== "search");
388 mouseClick(searchButton);
390 mouseClick(searchButton);
389 tryCompare(peExtraPanel, "visible", true);
391 tryCompare(peExtraPanel, "visible", true);
390 tryCompare(navigationListView.currentItem.navigation, "loaded", true);
392 tryCompare(navigationListView.currentItem.navigation, "loaded", true);
@@ -524,7 +526,6 @@ Item {
524 var peExtraPanel = findChild(dashContentList.currentItem, "peExtraPanel");
526 var peExtraPanel = findChild(dashContentList.currentItem, "peExtraPanel");
525 var headerContainer = findChild(pageHeader, "headerContainer");
527 var headerContainer = findChild(pageHeader, "headerContainer");
526
528
527− // test that closing the filters popover without a search unfocuses and removes the navigation
528 mouseClick(searchButton);
529 mouseClick(searchButton);
529 tryCompare(peExtraPanel, "visible", true);
530 tryCompare(peExtraPanel, "visible", true);
530
531
@@ -536,6 +537,19 @@ Item {
536 filtersPopover = findChild(shell, "filtersPopover")
537 filtersPopover = findChild(shell, "filtersPopover")
537 verify(filtersPopover);
538 verify(filtersPopover);
538
539
540+ // test that popover resizes
541+ var shellWidth = shell.width;
542+ var popoverWidth = filtersPopover.contentWidth;
543+
544+ shell.width = shellWidth + units.gu(60);
545+ waitForRendering(shell);
546+
547+ tryCompare(filtersPopover, "contentWidth", popoverWidth + units.gu(60));
548+
549+ shell.width = shellWidth;
550+ waitForRendering(shell);
551+
552+ // test that closing the filters popover without a search unfocuses and removes the navigation
539 mouseClick(shell, shell.width - 1, shell.height - 1);
553 mouseClick(shell, shell.width - 1, shell.height - 1);
540
554
541 tryCompare(pageHeader.extraPanel, "visible", false);
555 tryCompare(pageHeader.extraPanel, "visible", false);
@@ -544,7 +558,8 @@ Item {
544
558
545 mouseClick(cancelButton);
559 mouseClick(cancelButton);
546 tryCompare(headerContainer, "showSearch", false);
560 tryCompare(headerContainer, "showSearch", false);
547− tryCompare(headerContainer, "contentY", headerContainer.height);
561+ tryCompare(headerContainer, "clip", false);
562+ verify(headerContainer.state !== "search");
548
563
549 // test within a navigation
564 // test within a navigation
550 goToSecondLevel();
565 goToSecondLevel();
@@ -690,5 +705,27 @@ Item {
690 tryCompare(pageHeader.extraPanel, "visible", false);
705 tryCompare(pageHeader.extraPanel, "visible", false);
691 compare(searchTextField.focus, false);
706 compare(searchTextField.focus, false);
692 }
707 }
708+
709+ function test_cancelSearch() {
710+ var dashContentList = findChild(dashContent, "dashContentList");
711+ var pageHeader = findChild(dashContentList.currentItem, "scopePageHeader")
712+ pageHeader.searchEntryEnabled = true;
713+ pageHeader.searchHistory.clear();
714+
715+ pageHeader.searchHistory.addQuery("Search1");
716+ pageHeader.searchHistory.addQuery("Search2");
717+
718+ pageHeader.triggerSearch();
719+ tryCompare(pageHeader.extraPanel, "visible", true);
720+
721+ var searchTextField = findChild(pageHeader, "searchTextField");
722+ compare(searchTextField.focus, true);
723+
724+ var cancelButton = findChild(dashContentList.currentItem, "cancelButton")
725+
726+ mouseClick(cancelButton);
727+ tryCompare(pageHeader.extraPanel, "visible", false);
728+ compare(searchTextField.focus, false);
729+ }
693 }
730 }
694}
731}
tests/qmltests/Dash/tst_DashPageHeader.qml+44−3
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2013,2015 Canonical Ltd.
2+ * Copyright (C) 2013,2015,2016 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -28,6 +28,12 @@ Item {
28 width: units.gu(110)
28 width: units.gu(110)
29 height: units.gu(50)
29 height: units.gu(50)
30
30
31+ SignalSpy {
32+ id: escapeSpy
33+ target: pageHeader
34+ signalName: "clearSearch"
35+ }
36+
31 UT.UnityTestCase {
37 UT.UnityTestCase {
32 name: "PageHeaderLabelTest"
38 name: "PageHeaderLabelTest"
33 when: windowShown
39 when: windowShown
@@ -37,13 +43,15 @@ Item {
37 property var headerContainer: findChild(pageHeader, "headerContainer")
43 property var headerContainer: findChild(pageHeader, "headerContainer")
38
44
39 function doTypeString(text) {
45 function doTypeString(text) {
40− tryCompare(headerContainer, "contentY", 0);
46+ tryCompare(headerContainer, "clip", false);
47+ compare(headerContainer.state, "search");
41 typeString(text);
48 typeString(text);
42 }
49 }
43
50
44 function doResetSearch() {
51 function doResetSearch() {
45 pageHeader.resetSearch();
52 pageHeader.resetSearch();
46− tryCompare(headerContainer, "contentY", headerContainer.height);
53+ tryCompare(headerContainer, "clip", false);
54+ verify(headerContainer.state !== "search");
47 }
55 }
48
56
49 function init() {
57 function init() {
@@ -145,6 +153,7 @@ Item {
145
153
146 function cleanup() {
154 function cleanup() {
147 doResetSearch();
155 doResetSearch();
156+ escapeSpy.clear();
148 }
157 }
149
158
150 function test_popup_closing_data() {
159 function test_popup_closing_data() {
@@ -201,6 +210,38 @@ Item {
201 doResetSearch();
210 doResetSearch();
202 }
211 }
203
212
213+ function test_popup_closing_with_escape() {
214+ searchEnabled = true;
215+ pageHeader.searchHistory.clear();
216+
217+ pageHeader.searchHistory.addQuery("Search1");
218+ pageHeader.searchHistory.addQuery("Search2");
219+
220+ pageHeader.triggerSearch();
221+
222+ var headerContainer = findChild(pageHeader, "headerContainer");
223+ tryCompare(pageHeader.extraPanel, "visible", true);
224+
225+ pageHeader.searchQuery = "foobar";
226+
227+ // press Esc once, the search should be cleared
228+ keyClick(Qt.Key_Escape);
229+ pageHeader.searchQuery = ""; // simulate clearing the text field, the clear button doesn't do anything on its own
230+ compare(escapeSpy.count, 1);
231+ compare(escapeSpy.signalArguments[0][0], true);
232+
233+ escapeSpy.clear();
234+
235+ // press Escape a second time, the whole search should be hidden
236+ keyClick(Qt.Key_Escape);
237+ tryCompare(headerContainer, "showSearch", false);
238+ compare(escapeSpy.count, 1);
239+ compare(escapeSpy.signalArguments[0][0], false);
240+ tryCompare(pageHeader.extraPanel, "visible", false);
241+
242+ doResetSearch();
243+ }
244+
204 function test_search_change_shows_search() {
245 function test_search_change_shows_search() {
205 var headerContainer = findChild(pageHeader, "headerContainer");
246 var headerContainer = findChild(pageHeader, "headerContainer");
206 compare(headerContainer.showSearch, false);
247 compare(headerContainer.showSearch, false);
tests/qmltests/Panel/tst_IndicatorItemRow.qml+26−1
@@ -21,6 +21,8 @@ import "../../../qml/Panel"
21import Ubuntu.Components 1.3
21import Ubuntu.Components 1.3
22import Unity.Test 0.1 as UT
22import Unity.Test 0.1 as UT
23import Unity.Indicators 0.1 as Indicators
23import Unity.Indicators 0.1 as Indicators
24+import AccountsService 0.1
25+import Unity.InputInfo 0.1
24
26
25IndicatorTest {
27IndicatorTest {
26 id: root
28 id: root
@@ -194,7 +196,7 @@ IndicatorTest {
194 verify(dataItem !== null);
196 verify(dataItem !== null);
195
197
196 indicatorsRow.selectItemAt(dataItem.x + dataItem.width/2);
198 indicatorsRow.selectItemAt(dataItem.x + dataItem.width/2);
197− compare(indicatorsRow.currentItem, dataItem);
199+ tryCompare(indicatorsRow, "currentItem", dataItem);
198 }
200 }
199
201
200 // tests item default selection (no item at position X)
202 // tests item default selection (no item at position X)
@@ -285,5 +287,28 @@ IndicatorTest {
285 // should go back to 0
287 // should go back to 0
286 tryCompare(findChild(indicatorsRow, "highlight"), "highlightCenterOffset", 0);
288 tryCompare(findChild(indicatorsRow, "highlight"), "highlightCenterOffset", 0);
287 }
289 }
290+
291+ function test_hidingKeyboardIndicator_data() {
292+ return [
293+ { tag: "No keyboard, no keymap", keyboard: false, keymaps: [], hidden: true },
294+ { tag: "No keyboard, one keymap", keyboard: false, keymaps: ["us"], hidden: true },
295+ { tag: "No keyboard, 2 keymaps", keyboard: false, keymaps: ["us", "cs"], hidden: true },
296+ { tag: "Keyboard, no keymap", keyboard: true, keymaps: [], hidden: true },
297+ { tag: "Keyboard, one keymap", keyboard: true, keymaps: ["us"], hidden: true },
298+ { tag: "Keyboard, 2 keymaps", keyboard: true, keymaps: ["us", "cs"], hidden: false }
299+ ];
300+ }
301+
302+ function test_hidingKeyboardIndicator(data) {
303+ var item = findChild(indicatorsRow, "indicator-keyboard-panelItem");
304+ AccountsService.keymaps = data.keymaps;
305+ if (data.keyboard) {
306+ MockInputDeviceBackend.addMockDevice("/indicator_kbd0", InputInfo.Keyboard);
307+ } else {
308+ MockInputDeviceBackend.removeDevice("/indicator_kbd0");
309+ }
310+
311+ compare(item.hidden, data.hidden);
312+ }
288 }
313 }
289}
314}
tests/qmltests/Panel/tst_IndicatorsBar.qml+3−3
@@ -136,6 +136,7 @@ IndicatorTest {
136 }
136 }
137
137
138 function wait_for_expansion_to_settle() {
138 function wait_for_expansion_to_settle() {
139+ waitUntilTransitionsEnd(indicatorsBar);
139 tryCompare(heightAnimation, "running", false);
140 tryCompare(heightAnimation, "running", false);
140 wait(UbuntuAnimation.SnapDuration); // put a little extra wait in for things to settle
141 wait(UbuntuAnimation.SnapDuration); // put a little extra wait in for things to settle
141 }
142 }
@@ -151,7 +152,6 @@ IndicatorTest {
151 indicatorsBar.expanded = true;
152 indicatorsBar.expanded = true;
152 wait_for_expansion_to_settle();
153 wait_for_expansion_to_settle();
153
154
154−
155 // mappedPosition contained within mappedRect
155 // mappedPosition contained within mappedRect
156 tryCompareFunction(function() {
156 tryCompareFunction(function() {
157 var mappedRect = indicatorsBar.mapFromItem(dataItem, 0, 0, dataItem.width, dataItem.height);
157 var mappedRect = indicatorsBar.mapFromItem(dataItem, 0, 0, dataItem.width, dataItem.height);
@@ -203,8 +203,8 @@ IndicatorTest {
203
203
204 function test_visibleIndicators_data() {
204 function test_visibleIndicators_data() {
205 return [
205 return [
206− { visible: [true, false, true, false, true, true] },
206+ { visible: [true, false, true, false, true, true, false, true] },
207− { visible: [false, false, false, false, false, false] }
207+ { visible: [false, false, false, false, false, false, true, false] }
208 ];
208 ];
209 }
209 }
210
210
tests/qmltests/Panel/tst_IndicatorsMenu.qml+4−3
@@ -219,8 +219,8 @@ IndicatorTest {
219 mappedPosition.x, indicatorsMenu.openedHeight / 2,
219 mappedPosition.x, indicatorsMenu.openedHeight / 2,
220 true /* beginTouch */, false /* endTouch */);
220 true /* beginTouch */, false /* endTouch */);
221
221
222− compare(indicatorItemRow.currentItem, indicatorItem,
222+ tryCompare(indicatorItemRow, "currentItem", indicatorItem, undefined /*timeout, default */,
223− "Incorrect item activated at position " + i);
223+ "Incorrect item activated at position " + i);
224
224
225 touchFlick(indicatorItemRow,
225 touchFlick(indicatorItemRow,
226 mappedPosition.x, indicatorsMenu.openedHeight / 2,
226 mappedPosition.x, indicatorsMenu.openedHeight / 2,
@@ -265,7 +265,8 @@ IndicatorTest {
265 false /* beginTouch */, false /* endTouch */,
265 false /* beginTouch */, false /* endTouch */,
266 units.gu(50) /* speed */, 5 /* iterations */); // more samples needed for accurate velocity
266 units.gu(50) /* speed */, 5 /* iterations */); // more samples needed for accurate velocity
267
267
268+ tryCompare(indicatorItemRow, "currentItem", firstItem, undefined /* timeout, default */,
268− compare(indicatorItemRow.currentItem, firstItem, "First indicator should still be the current item");
269+ "First indicator should still be the current item");
269 // after waiting in the same spot with touch down, it should update to the next item.
270 // after waiting in the same spot with touch down, it should update to the next item.
270 tryCompare(indicatorItemRow, "currentItem", nextItem);
271 tryCompare(indicatorItemRow, "currentItem", nextItem);
271
272
tests/qmltests/Stages/tst_SpreadDelegate.qml+115−12
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2014-2015 Canonical Ltd.
2+ * Copyright 2014-2016 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -132,10 +132,12 @@ Rectangle {
132 property int value: selectedIndex * 90
132 property int value: selectedIndex * 90
133 }
133 }
134 Button {
134 Button {
135+ id: matchShellButton
135 text: "matchShellOrientation()"
136 text: "matchShellOrientation()"
136 onClicked: { spreadDelegateLoader.item.matchShellOrientation(); }
137 onClicked: { spreadDelegateLoader.item.matchShellOrientation(); }
137 }
138 }
138 Button {
139 Button {
140+ id: animateToShellButton
139 text: "animateToShellOrientation()"
141 text: "animateToShellOrientation()"
140 onClicked: { spreadDelegateLoader.item.animateToShellOrientation(); }
142 onClicked: { spreadDelegateLoader.item.animateToShellOrientation(); }
141 }
143 }
@@ -197,6 +199,77 @@ Rectangle {
197
199
198 }
200 }
199
201
202+ function waitUntilAppWindowIsFullyLoaded() {
203+ var appWindowStateGroup = findInvisibleChild(spreadDelegate, "applicationWindowStateGroup");
204+ tryCompareFunction(function() { return appWindowStateGroup.state === "surface" }, true);
205+ waitUntilTransitionsEnd(appWindowStateGroup);
206+
207+ var priv = findInvisibleChild(spreadDelegate, "spreadDelegatePriv");
208+ verify(priv);
209+ tryCompare(priv, "startingUp", false);
210+ }
211+
212+ function rotateShellTo(angle) {
213+ shellOrientationAngleSelector.selectedIndex = angle / 90;
214+ }
215+
216+ function waitUntilRotationAnimationStops() {
217+ var orientationChangeAnimation = findInvisibleChild(spreadDelegate, "orientationChangeAnimation");
218+ verify(orientationChangeAnimation);
219+ tryCompare(orientationChangeAnimation, "running", false);
220+ }
221+
222+ function checkAppWindowTransformationMatchesRotation(appWindow, angle) {
223+
224+ var topLeftX = 0;
225+ var topLeftY = 0;
226+ var bottomRightX = 0;
227+ var bottomRightY = 0;
228+
229+ switch (angle) {
230+ case 0:
231+ topLeftX = 0;
232+ topLeftY = 0;
233+ bottomRightX = fakeShell.shortestDimension;
234+ bottomRightY = fakeShell.longestDimension;
235+ break;
236+ case 90:
237+ topLeftX = fakeShell.shortestDimension;
238+ topLeftY = 0;
239+ bottomRightX = 0;
240+ bottomRightY = fakeShell.longestDimension;
241+ break;
242+ default:
243+ // TODO implement 180 and 270 once we need them
244+ verify(false);
245+ }
246+
247+ var appWindowTopLeftInRootCoords = appWindow.mapToItem(root, 0, 0);
248+
249+ if (appWindowTopLeftInRootCoords.x !== topLeftX) {
250+ qtest_fail("appWindow topLeft.x ("+appWindowTopLeftInRootCoords.x+")"
251+ +" doesn't match expectations ("+topLeftX+").", 1);
252+ }
253+ if (appWindowTopLeftInRootCoords.y !== topLeftY) {
254+ qtest_fail("appWindow topLeft.y ("+appWindowTopLeftInRootCoords.y+")"
255+ +" doesn't match expectations ("+topLeftY+").", 1);
256+ }
257+
258+ var appWindowBottomRightInRootCoords = appWindow.mapToItem(root, appWindow.width,
259+ appWindow.height);
260+ compare(appWindowBottomRightInRootCoords.x, bottomRightX);
261+ compare(appWindowBottomRightInRootCoords.y, bottomRightY);
262+
263+ if (appWindowBottomRightInRootCoords.x !== bottomRightX) {
264+ qtest_fail("appWindow bottomRight.x ("+appWindowBottomRightInRootCoords.x+")"
265+ +" doesn't match expectations ("+bottomRightX+").", 1);
266+ }
267+ if (appWindowBottomRightInRootCoords.y !== bottomRightY) {
268+ qtest_fail("appWindow bottomRight.y ("+appWindowBottomRightInRootCoords.y+")"
269+ +" doesn't match expectations ("+bottomRightY+").", 1);
270+ }
271+ }
272+
200 function test_swipeToClose_data() {
273 function test_swipeToClose_data() {
201 return [
274 return [
202 {tag: "swipeToClose=true closeable=true -> appWindow moves away",
275 {tag: "swipeToClose=true closeable=true -> appWindow moves away",
@@ -278,12 +351,12 @@ Rectangle {
278 function test_keepsSceneTransformationWhenShellRotates(data) {
351 function test_keepsSceneTransformationWhenShellRotates(data) {
279 loadWithGalleryApp.clicked();
352 loadWithGalleryApp.clicked();
280
353
354+ waitUntilAppWindowIsFullyLoaded();
355+
281 var appWindowWithShadow = findChild(spreadDelegate, "appWindowWithShadow");
356 var appWindowWithShadow = findChild(spreadDelegate, "appWindowWithShadow");
282 verify(appWindowWithShadow);
357 verify(appWindowWithShadow);
283
358
284− // Wait until it reaches the state we are interested on.
285− // It begins with "startingUp"
286− tryCompare(appWindowWithShadow, "state", "keepSceneRotation");
359+ compare(appWindowWithShadow.state, "keepSceneRotation");
287
360
288 shellOrientationAngleSelector.selectedIndex = data.selectedIndex;
361 shellOrientationAngleSelector.selectedIndex = data.selectedIndex;
289
362
@@ -293,14 +366,7 @@ Rectangle {
293
366
294
367
295 // and scene transformation must be the identity (ie, no rotation or translation)
368 // and scene transformation must be the identity (ie, no rotation or translation)
296− var pointInDelegateCoords = appWindowWithShadow.mapToItem(root, 0, 0);
369+ checkAppWindowTransformationMatchesRotation(appWindowWithShadow, 0);
297− compare(pointInDelegateCoords.x, 0);
298− compare(pointInDelegateCoords.y, 0);
299−
300− pointInDelegateCoords = appWindowWithShadow.mapToItem(root,
301− fakeShell.shortestDimension, fakeShell.longestDimension);
302− compare(pointInDelegateCoords.x, fakeShell.shortestDimension);
303− compare(pointInDelegateCoords.y, fakeShell.longestDimension);
304 }
370 }
305
371
306 function waitForCloseAnimationToFinish() {
372 function waitForCloseAnimationToFinish() {
@@ -316,5 +382,42 @@ Rectangle {
316 spreadDelegateLoader.item.highlightShown = true;
382 spreadDelegateLoader.item.highlightShown = true;
317 tryCompare(highlightRect, "visible", true)
383 tryCompare(highlightRect, "visible", true)
318 }
384 }
385+
386+ /*
387+ Checks that the ApplicationWindow position, size and rotation is correct after
388+ a sequence of shell rotations folowed by rotations (immediate or not) to match them
389+ */
390+ function test_animateToShellThenMatchShell() {
391+ loadWithGalleryApp.clicked();
392+ waitUntilAppWindowIsFullyLoaded();
393+
394+ var appWindowWithShadow = findChild(spreadDelegate, "appWindowWithShadow");
395+ verify(appWindowWithShadow);
396+
397+ // appWindow transformation is 0 relative to display
398+ checkAppWindowTransformationMatchesRotation(appWindowWithShadow, 0);
399+
400+ rotateShellTo(90);
401+
402+ // appWindow transformation remains 0 relative to display
403+ checkAppWindowTransformationMatchesRotation(appWindowWithShadow, 0);
404+
405+ animateToShellButton.clicked();
406+ waitUntilRotationAnimationStops();
407+
408+ // appWindow transformation is now 90 relative to display, just like shell
409+ checkAppWindowTransformationMatchesRotation(appWindowWithShadow, 90);
410+
411+ rotateShellTo(0);
412+
413+ // appWindow transformation is remains 90 relative to display.
414+ checkAppWindowTransformationMatchesRotation(appWindowWithShadow, 90);
415+
416+ matchShellButton.clicked();
417+
418+ // appWindow transformation is back to 0 relative to display, just like shell
419+ checkAppWindowTransformationMatchesRotation(appWindowWithShadow, 0);
420+
421+ }
319 }
422 }
320}
423}
tests/qmltests/tst_DisabledScreenNotice.qml+1−1
@@ -106,7 +106,7 @@ Item {
106 orientationSelector.selectedIndex = data.selectedOrientation;
106 orientationSelector.selectedIndex = data.selectedOrientation;
107
107
108 // Make sure it is still in the old rotation (as we have the lock turned on)
108 // Make sure it is still in the old rotation (as we have the lock turned on)
109− expectFail("", "Rotation lock set. Expecting automatic rotation to fail.");
109+ expectFailContinue("", "Rotation lock set. Expecting automatic rotation to fail.");
110 tryCompareFunction(function() { return content.rotation != oldRotation; }, true, 300);
110 tryCompareFunction(function() { return content.rotation != oldRotation; }, true, 300);
111
111
112 // Now uncheck the rotation lock
112 // Now uncheck the rotation lock
tests/qmltests/tst_OrientedShell.qml+1
@@ -1117,6 +1117,7 @@ Rectangle {
1117
1117
1118 function test_attachRemoveInputDevices(data) {
1118 function test_attachRemoveInputDevices(data) {
1119 loadShell("mako")
1119 loadShell("mako")
1120+ MockInputDeviceBackend.removeDevice("/indicator_kbd0");
1120 var shell = findChild(orientedShell, "shell");
1121 var shell = findChild(orientedShell, "shell");
1121 var inputMethod = findChild(shell, "inputMethod");
1122 var inputMethod = findChild(shell, "inputMethod");
1122
1123
tests/qmltests/tst_Shell.qml+43−7
@@ -2154,8 +2154,10 @@ Rectangle {
2154
2154
2155 function test_switchToStagedForcesLegacyAppClosing_data() {
2155 function test_switchToStagedForcesLegacyAppClosing_data() {
2156 return [
2156 return [
2157− {tag: "forceClose", replug: false },
2157+ {tag: "forceClose", replug: false, tabletMode: false, screenSize: Qt.size(units.gu(20), units.gu(40)) },
2158+ {tag: "replug", replug: true, tabletMode: false, screenSize: Qt.size(units.gu(20), units.gu(40)) },
2159+ {tag: "forceClose+tablet", replug: false, tabletMode: true, screenSize: Qt.size(units.gu(90), units.gu(65)) },
2158− {tag: "replug", replug: true }
2160+ {tag: "replug+tablet", replug: true, tabletMode: true, screenSize: Qt.size(units.gu(90), units.gu(65)) }
2159 ];
2161 ];
2160 }
2162 }
2161
2163
@@ -2164,6 +2166,11 @@ Rectangle {
2164 shell.usageScenario = "desktop"
2166 shell.usageScenario = "desktop"
2165 waitForRendering(shell);
2167 waitForRendering(shell);
2166
2168
2169+ // setup some screen size
2170+ var dialogs = findChild(root, "dialogs");
2171+ verify(dialogs);
2172+ dialogs.screenSize = data.screenSize;
2173+
2167 ApplicationManager.startApplication("camera-app")
2174 ApplicationManager.startApplication("camera-app")
2168
2175
2169 shell.usageScenario = "phone"
2176 shell.usageScenario = "phone"
@@ -2182,14 +2189,13 @@ Rectangle {
2182 shell.usageScenario = "phone"
2189 shell.usageScenario = "phone"
2183 waitForRendering(shell);
2190 waitForRendering(shell);
2184
2191
2185− // The popup must appear now
2192+ // The popup must appear now, unless in "tablet" mode
2186 popup = findChild(root, "modeSwitchWarningDialog");
2193 popup = findChild(root, "modeSwitchWarningDialog");
2187− compare(popup !== null, true);
2194+ compare(popup !== null, !data.tabletMode);
2188
2195
2189− if (data.replug) {
2196+ if (data.replug || data.tabletMode) {
2190 shell.usageScenario = "desktop"
2197 shell.usageScenario = "desktop"
2191 waitForRendering(shell);
2198 waitForRendering(shell);
2192−
2193 } else {
2199 } else {
2194 var forceCloseButton = findChild(popup, "forceCloseButton");
2200 var forceCloseButton = findChild(popup, "forceCloseButton");
2195 mouseClick(forceCloseButton, forceCloseButton.width / 2, forceCloseButton.height / 2);
2201 mouseClick(forceCloseButton, forceCloseButton.width / 2, forceCloseButton.height / 2);
@@ -2200,7 +2206,7 @@ Rectangle {
2200 popup = findChild(root, "modeSwitchWarningDialog");
2206 popup = findChild(root, "modeSwitchWarningDialog");
2201 tryCompareFunction(function() { return popup === null}, true);
2207 tryCompareFunction(function() { return popup === null}, true);
2202
2208
2203− if (data.replug) {
2209+ if (data.replug || data.tabletMode) {
2204 // Libreoffice must still be running
2210 // Libreoffice must still be running
2205 compare(ApplicationManager.findApplication("libreoffice") !== null, true);
2211 compare(ApplicationManager.findApplication("libreoffice") !== null, true);
2206 } else {
2212 } else {
@@ -2506,5 +2512,35 @@ Rectangle {
2506 verify(greeter.shown);
2512 verify(greeter.shown);
2507 verify(greeter.locked);
2513 verify(greeter.locked);
2508 }
2514 }
2515+
2516+ function test_closeFocusedDelegate_data() {
2517+ return [
2518+ { tag: "phone" },
2519+ { tag: "tablet" },
2520+ { tag: "desktop" }
2521+ ]
2522+ }
2523+
2524+ function test_closeFocusedDelegate(data) {
2525+ loadShell(data.tag);
2526+ shell.usageScenario = data.tag;
2527+ waitForRendering(shell);
2528+ swipeAwayGreeter();
2529+
2530+ var app2SurfaceId = topLevelSurfaceList.nextId;
2531+ var app2 = ApplicationManager.startApplication("calendar-app");
2532+ waitUntilAppWindowIsFullyLoaded(app2SurfaceId);
2533+
2534+ var app1SurfaceId = topLevelSurfaceList.nextId;
2535+ var app1 = ApplicationManager.startApplication("dialer-app")
2536+ waitUntilAppWindowIsFullyLoaded(app1SurfaceId);
2537+
2538+ var countBeforeClose = topLevelSurfaceList.count;
2539+
2540+ keyClick(Qt.Key_F4, Qt.AltModifier);
2541+
2542+ tryCompare(topLevelSurfaceList, "count", countBeforeClose - 1);
2543+ tryCompareFunction(function() { return ApplicationManager.focusedApplicationId; }, "calendar-app");
2544+ }
2509 }
2545 }
2510}
2546}
tests/utils/modules/Unity/Test/TouchEventSequenceWrapper.cpp+8−3
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013-2016 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -26,9 +26,14 @@ TouchEventSequenceWrapper::TouchEventSequenceWrapper(QTest::QTouchEventSequence
26
26
27void TouchEventSequenceWrapper::commit(bool processEvents)
27void TouchEventSequenceWrapper::commit(bool processEvents)
28{
28{
29+ // Item might be deleted as a consequence of this event sequence being handled
30+ // So store its window beforehand
31+ QQuickWindow *window = m_item->window();
32+
29 m_eventSequence.commit(processEvents);
33 m_eventSequence.commit(processEvents);
34+
30− if (m_item->window()) {
35+ if (window) {
31− QQuickWindowPrivate *wp = QQuickWindowPrivate::get(m_item->window());
36+ QQuickWindowPrivate *wp = QQuickWindowPrivate::get(window);
32 wp->flushDelayedTouchEvent();
37 wp->flushDelayedTouchEvent();
33 }
38 }
34}
39}
tests/utils/modules/Unity/Test/TouchEventSequenceWrapper.h+4−2
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013-2016 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -21,6 +21,8 @@
21#include <QtTest/QTest>
21#include <QtTest/QTest>
22#include <QtQml/qqml.h>
22#include <QtQml/qqml.h>
23
23
24+#include <QPointer>
25+
24class QQuickItem;
26class QQuickItem;
25
27
26class TouchEventSequenceWrapper : public QObject
28class TouchEventSequenceWrapper : public QObject
@@ -37,7 +39,7 @@ public:
37
39
38private:
40private:
39 QTest::QTouchEventSequence m_eventSequence;
41 QTest::QTouchEventSequence m_eventSequence;
40− QQuickItem *m_item;
42+ QPointer<QQuickItem> m_item;
41};
43};
42
44
43QML_DECLARE_TYPE(TouchEventSequenceWrapper)
45QML_DECLARE_TYPE(TouchEventSequenceWrapper)
tools/scopetool.cpp+6−2
@@ -59,7 +59,7 @@ int main(int argc, char *argv[])
59 "Otherwise passing a path to a scope config file will open a session with\n"
59 "Otherwise passing a path to a scope config file will open a session with\n"
60 "only that scope (assuming that the binary implementing the scope can be\n"
60 "only that scope (assuming that the binary implementing the scope can be\n"
61 "found in the same directory as the config file).");
61 "found in the same directory as the config file).");
62− parser.addHelpOption();
62+ QCommandLineOption helpOption = parser.addHelpOption();
63 parser.addPositionalArgument("scopes", "Paths to scope config files to spawn, optionally.", "[scopes...]");
63 parser.addPositionalArgument("scopes", "Paths to scope config files to spawn, optionally.", "[scopes...]");
64
64
65 QCommandLineOption includeSystemScopes("include-system-scopes",
65 QCommandLineOption includeSystemScopes("include-system-scopes",
@@ -70,7 +70,11 @@ int main(int argc, char *argv[])
70 parser.addOption(includeServerScopes);
70 parser.addOption(includeServerScopes);
71 parser.addOption(includeSystemScopes);
71 parser.addOption(includeSystemScopes);
72
72
73− parser.process(*application);
73+ parser.parse(application->arguments());
74+
75+ if (parser.isSet(helpOption)) {
76+ parser.showHelp();
77+ }
74
78
75 QStringList extraScopes = parser.positionalArguments();
79 QStringList extraScopes = parser.positionalArguments();
76
80