on xenial help sign in
merge trunk
commit7159c6f510b010022cb0647aa2a23860bd5f7040 authorLukáš Tinkl <lukas.tinkl@canonical.com> dateMon 8 Aug 2016, 12:16 (10 years ago) parent53b52d1166ff93
74 files changed+1130−495
debian/changelog+52 debian/control+9−8 plugins/Cursor/Cursor.qml+5−2 plugins/Cursor/CursorImageInfo.cpp+21−1 plugins/Cursor/CursorImageInfo.h+10 plugins/Cursor/CursorImageProvider.cpp+33−21 plugins/Cursor/CursorImageProvider.h+9−6 plugins/Dash/CardCreator.js+70−78 plugins/Dash/abstractdashview.cpp+13 plugins/Dash/abstractdashview.h+2−1 plugins/Dash/verticaljournal.cpp+9−1 plugins/Dash/verticaljournal.h+3 plugins/Unity/Indicators/indicatorsmanager.cpp+4−6 plugins/Wizard/System.cpp+1 po/unity8.pot+21−21 qml/Components/Dialogs.qml+7−2 qml/Components/KeymapSwitcher.qml+27−1 qml/Dash/DashNavigationList.qml+48−45 qml/Dash/DashPageHeader.qml+49−22 qml/Dash/Filters/FilterExpandableWidget.qml+1 qml/Dash/Filters/FilterOptionSelector.qml+2 qml/Dash/GenericScopeView.qml+1−1 qml/Greeter/Greeter.qml+9−3 qml/Panel/IndicatorItemRow.qml+13−2 qml/Panel/Indicators/MenuItemFactory.qml+65 qml/Shell.qml+2 qml/Stages/AbstractStage.qml+9 qml/Stages/ApplicationWindow.qml+6 qml/Stages/DesktopStage.qml+8−8 qml/Stages/PhoneStage.qml+8−2 qml/Stages/SpreadDelegate.qml+71−81 qml/Stages/TabletStage.qml+8−2 src/Dash/main.cpp+1−1 src/main.cpp+1−1 tests/mocks/Unity/Application/MirSurfaceItem.cpp+37−10 tests/mocks/Unity/Application/MirSurfaceItem.h+2 tests/mocks/Unity/Indicators/IndicatorsModel.qml+41−1 tests/plugins/Cursor/tst_Cursor.qml+8−1 tests/plugins/Dash/cardcreator/1.res+8−11 tests/plugins/Dash/cardcreator/1.res.cardcreator+6−11 tests/plugins/Dash/cardcreator/10.res+1−1 tests/plugins/Dash/cardcreator/10.res.cardcreator+1−1 tests/plugins/Dash/cardcreator/11.res+9−12 tests/plugins/Dash/cardcreator/11.res.cardcreator+6−11 tests/plugins/Dash/cardcreator/12.res+8−11 tests/plugins/Dash/cardcreator/12.res.cardcreator+6−11 tests/plugins/Dash/cardcreator/2.res+1−1 tests/plugins/Dash/cardcreator/2.res.cardcreator+1−1 tests/plugins/Dash/cardcreator/3.res+7−10 tests/plugins/Dash/cardcreator/3.res.cardcreator+6−11 tests/plugins/Dash/cardcreator/4.res+1−1 tests/plugins/Dash/cardcreator/4.res.cardcreator+1−1 tests/plugins/Dash/cardcreator/5.res+9−12 tests/plugins/Dash/cardcreator/5.res.cardcreator+7−12 tests/plugins/Dash/cardcreator/6.res+1−1 tests/plugins/Dash/cardcreator/6.res.cardcreator+1−1 tests/plugins/Dash/cardcreator/7.res+1−1 tests/plugins/Dash/cardcreator/7.res.cardcreator+1−1 tests/plugins/Dash/cardcreator/8.res+1−1 tests/plugins/Dash/cardcreator/8.res.cardcreator+1−1 tests/plugins/Dash/verticaljournaltest.cpp+62 tests/qmltests/Dash/Previews/tst_PreviewIconActions.qml+1−1 tests/qmltests/Dash/tst_DashContent.qml+42−5 tests/qmltests/Dash/tst_DashPageHeader.qml+44−3 tests/qmltests/Panel/tst_IndicatorItemRow.qml+26−1 tests/qmltests/Panel/tst_IndicatorsBar.qml+3−3 tests/qmltests/Panel/tst_IndicatorsMenu.qml+4−3 tests/qmltests/Stages/tst_SpreadDelegate.qml+115−12 tests/qmltests/tst_DisabledScreenNotice.qml+1−1 tests/qmltests/tst_OrientedShell.qml+1 tests/qmltests/tst_Shell.qml+43−7 tests/utils/modules/Unity/Test/TouchEventSequenceWrapper.cpp+8−3 tests/utils/modules/Unity/Test/TouchEventSequenceWrapper.h+4−2 tools/scopetool.cpp+6−2
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+
153unity8 (8.14+16.10.20160728-0ubuntu1) yakkety; urgency=medium
254
355 [ Zoltán Balogh ]
debian/control+9−8
@@ -8,7 +8,7 @@ Build-Depends: cmake,
88 dh-apparmor,
99 doxyqml,
1010# 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++
1212# on an x86 CPU for example, when cross-compiling.
1313 g++:native,
1414 libandroid-properties-dev,
@@ -17,9 +17,9 @@ Build-Depends: cmake,
1717 libconnectivity-qt1-dev (>= 0.7.1),
1818 libevdev-dev,
1919 libgeonames-dev (>= 0.2),
20− libgl1-mesa-dev[!armhf] | libgl-dev[!armhf],
20+ libgl1-mesa-dev[!arm64 !armhf] | libgl-dev[!arm64 !armhf],
2121 libgl1-mesa-dri,
22− libgles2-mesa-dev[armhf],
22+ libgles2-mesa-dev[arm64 armhf],
2323 libglib2.0-dev,
2424 libgsettings-qt-dev,
2525 libhardware-dev,
@@ -37,9 +37,9 @@ Build-Depends: cmake,
3737 libunity-api-dev (>= 7.116),
3838 libusermetricsoutput1-dev,
3939# Need those X11 libs touch emulation from mouse events in manual QML tests on a X11 desktop
40− libx11-dev[!armhf],
41− libxcb1-dev[!armhf],
42− libxi-dev[!armhf],
40+ libx11-dev[!arm64 !armhf],
41+ libxcb1-dev[!arm64 !armhf],
42+ libxi-dev[!arm64 !armhf],
4343# End of X11 libs
4444 pkg-config,
4545 python3-all:any,
@@ -74,7 +74,7 @@ XS-Testsuite: autopkgtest
7474X-Ubuntu-Use-Langpack: yes
7575
7676Package: indicators-client
77−Architecture: amd64 armhf i386
77+Architecture: any
7878Depends: qmenumodel-qml (>= 0.2.9),
7979 qml-module-ubuntu-components (>= 1.3.2030) | qml-module-ubuntu-components-gles (>= 1.3.2030),
8080 unity8 (= ${binary:Version}),
@@ -130,7 +130,8 @@ Depends: dmz-cursor-theme,
130130 unity8-private | unity-launcher-impl,
131131 ${misc:Depends},
132132 ${shlibs:Depends},
133−Recommends: indicator-session,
133+Recommends: indicator-keyboard,
134+ indicator-session,
134135 unity-scope-click,
135136 unity-scope-mediascanner2,
136137Breaks: indicator-network (<< 0.5.1+14.10.20141014),
plugins/Cursor/Cursor.qml+5−2
@@ -25,6 +25,7 @@ MousePointer {
2525 id: imageInfo
2626 themeName: mousePointer.themeName
2727 cursorName: mousePointer.cursorName
28+ cursorHeight: mousePointer.height
2829 }
2930
3031 Loader {
@@ -32,7 +33,7 @@ MousePointer {
3233 sourceComponent: AnimatedSprite {
3334 x: -imageInfo.hotspot.x
3435 y: -imageInfo.hotspot.y
35− source: "image://cursor/" + mousePointer.themeName + "/" + mousePointer.cursorName
36+ source: imageInfo.imageSource
3637
3738 interpolate: false
3839
@@ -53,7 +54,9 @@ MousePointer {
5354 sourceComponent: Image {
5455 x: -imageInfo.hotspot.x
5556 y: -imageInfo.hotspot.y
56− source: "image://cursor/" + mousePointer.themeName + "/" + mousePointer.cursorName
57+ source: imageInfo.imageSource
58+ width: sourceSize.width
59+ height: sourceSize.height
5760 }
5861 }
5962}
plugins/Cursor/CursorImageInfo.cpp+21−1
@@ -28,6 +28,15 @@ void CursorImageInfo::setCursorName(const QString &cursorName)
2828 update();
2929 Q_EMIT cursorNameChanged();
3030 }
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+ }
3140}
3241
3342void CursorImageInfo::setThemeName(const QString &themeName)
@@ -41,13 +50,14 @@ void CursorImageInfo::setThemeName(const QString &themeName)
4150
4251void CursorImageInfo::update()
4352{
44− m_cursorImage = CursorImageProvider::instance()->fetchCursor(m_themeName, m_cursorName);
53+ m_cursorImage = CursorImageProvider::instance()->fetchCursor(m_themeName, m_cursorName, (int) m_cursorHeight);
4554
4655 Q_EMIT hotspotChanged();
4756 Q_EMIT frameWidthChanged();
4857 Q_EMIT frameHeightChanged();
4958 Q_EMIT frameCountChanged();
5059 Q_EMIT frameDurationChanged();
60+ Q_EMIT imageSourceChanged();
5161}
5262
5363QPoint CursorImageInfo::hotspot() const
@@ -94,3 +104,13 @@ int CursorImageInfo::frameDuration() const
94104 return 0;
95105 }
96106}
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 @@
2121
2222#include <QObject>
2323#include <QString>
24+#include <QUrl>
2425
2526class CursorImageInfo : public QObject
2627{
@@ -28,12 +29,14 @@ class CursorImageInfo : public QObject
2829
2930 Q_PROPERTY(QString themeName READ themeName WRITE setThemeName NOTIFY themeNameChanged)
3031 Q_PROPERTY(QString cursorName READ cursorName WRITE setCursorName NOTIFY cursorNameChanged)
32+ Q_PROPERTY(qreal cursorHeight READ cursorHeight WRITE setCursorHeight NOTIFY cursorHeightChanged)
3133
3234 Q_PROPERTY(QPoint hotspot READ hotspot NOTIFY hotspotChanged)
3335 Q_PROPERTY(qreal frameWidth READ frameWidth NOTIFY frameWidthChanged)
3436 Q_PROPERTY(qreal frameHeight READ frameHeight NOTIFY frameHeightChanged)
3537 Q_PROPERTY(int frameCount READ frameCount NOTIFY frameCountChanged)
3638 Q_PROPERTY(int frameDuration READ frameDuration NOTIFY frameDurationChanged)
39+ Q_PROPERTY(QUrl imageSource READ imageSource NOTIFY imageSourceChanged)
3740
3841public:
3942 CursorImageInfo(QObject *parent = nullptr);
@@ -44,20 +47,26 @@ public:
4447 QString cursorName() const { return m_cursorName; }
4548 void setCursorName(const QString &);
4649
50+ qreal cursorHeight() const { return m_cursorHeight; }
51+ void setCursorHeight(qreal);
52+
4753 QPoint hotspot() const;
4854 qreal frameWidth() const;
4955 qreal frameHeight() const;
5056 int frameCount() const;
5157 int frameDuration() const;
58+ QUrl imageSource() const;
5259
5360Q_SIGNALS:
5461 void themeNameChanged();
5562 void cursorNameChanged();
63+ void cursorHeightChanged();
5664 void hotspotChanged();
5765 void frameWidthChanged();
5866 void frameHeightChanged();
5967 void frameCountChanged();
6068 void frameDurationChanged();
69+ void imageSourceChanged();
6170
6271private Q_SLOTS:
6372 void update();
@@ -65,6 +74,7 @@ private Q_SLOTS:
6574private:
6675 QString m_themeName;
6776 QString m_cursorName;
77+ qreal m_cursorHeight{0};
6878
6979 CursorImage *m_cursorImage{nullptr};
7080};
plugins/Cursor/CursorImageProvider.cpp+33−21
@@ -27,7 +27,7 @@ CursorImageProvider *CursorImageProvider::m_instance = nullptr;
2727/////
2828// BuiltInCursorImage
2929
30−BuiltInCursorImage::BuiltInCursorImage()
30+BuiltInCursorImage::BuiltInCursorImage(int cursorHeight)
3131{
3232 const char *svgString =
3333 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"
@@ -42,12 +42,14 @@ BuiltInCursorImage::BuiltInCursorImage()
4242 " 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\" />"
4343 "</svg>";
4444
45− qimage = QImage(20, 32, QImage::Format_ARGB32);
45+ // NB: Original image dimension is 20x32. Ensure aspect ratio is kept
46+ qimage = QImage((20./32.)*cursorHeight, cursorHeight, QImage::Format_ARGB32);
4647 qimage.fill(Qt::transparent);
4748 QPainter imagePainter(&qimage);
4849
4950 frameWidth = qimage.width();
5051 frameHeight = qimage.height();
52+ requestedHeight = cursorHeight;
5153
5254 QSvgRenderer *svgRenderer = new QSvgRenderer(QByteArray(svgString));
5355 svgRenderer->render(&imagePainter);
@@ -81,11 +83,9 @@ CustomCursorImage::CustomCursorImage(const QCursor &cursor)
8183/////
8284// XCursorImage
8385
84−XCursorImage::XCursorImage(const QString &theme, const QString &file)
86+XCursorImage::XCursorImage(const QString &theme, const QString &file, int preferredCursorHeightPx)
8587{
86− // TODO: Consider grid unit value
87− // Hardcoding to a medium size for now
88− int preferredCursorHeightPx = 32;
88+ requestedHeight = preferredCursorHeightPx;
8989
9090 XcursorImages *xcursorImages = XcursorLibraryLoadImages(QFile::encodeName(file), QFile::encodeName(theme),
9191 preferredCursorHeightPx);
@@ -96,7 +96,7 @@ XCursorImage::XCursorImage(const QString &theme, const QString &file)
9696 frameCount = xcursorImages->nimage;
9797
9898 for (int i = 0; i < xcursorImages->nimage; ++i) {
99− XcursorImage *xcursorImage = xcursorImages->images[0];
99+ XcursorImage *xcursorImage = xcursorImages->images[i];
100100 if (frameWidth < (int)xcursorImage->width) {
101101 frameWidth = xcursorImage->width;
102102 }
@@ -235,34 +235,43 @@ CursorImageProvider::~CursorImageProvider()
235235 m_instance = nullptr;
236236}
237237
238−QImage CursorImageProvider::requestImage(const QString &cursorThemeAndName, QSize *size, const QSize & /*requestedSize*/)
238+QImage CursorImageProvider::requestImage(const QString &cursorThemeAndNameAndHeight, QSize *size, const QSize & /*requestedSize*/)
239239{
240− CursorImage *cursorImage = fetchCursor(cursorThemeAndName);
240+ CursorImage *cursorImage = fetchCursor(cursorThemeAndNameAndHeight);
241241 size->setWidth(cursorImage->qimage.width());
242242 size->setHeight(cursorImage->qimage.height());
243243
244244 return cursorImage->qimage;
245245}
246246
247−CursorImage *CursorImageProvider::fetchCursor(const QString &cursorThemeAndName)
247+CursorImage *CursorImageProvider::fetchCursor(const QString &cursorThemeAndNameAndHeight)
248248{
249249 QString themeName;
250250 QString cursorName;
251+ int cursorHeight;
251252 {
252− QStringList themeAndNameList = cursorThemeAndName.split('/');
253− if (themeAndNameList.size() != 2) {
253+ QStringList themeAndNameList = cursorThemeAndNameAndHeight.split('/');
254+ if (themeAndNameList.size() != 3) {
254255 return nullptr;
255256 }
256257 themeName = themeAndNameList[0];
257258 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+ }
258267 }
259268
260− return fetchCursor(themeName, cursorName);
269+ return fetchCursor(themeName, cursorName, cursorHeight);
261270}
262271
263−CursorImage *CursorImageProvider::fetchCursor(const QString &themeName, const QString &cursorName)
272+CursorImage *CursorImageProvider::fetchCursor(const QString &themeName, const QString &cursorName, int cursorHeight)
264273{
265− CursorImage *cursorImage = fetchCursorHelper(themeName, cursorName);
274+ CursorImage *cursorImage = fetchCursorHelper(themeName, cursorName, cursorHeight);
266275
267276 // Try some fallbacks
268277 if (cursorImage->qimage.isNull()) {
@@ -271,7 +280,7 @@ CursorImage *CursorImageProvider::fetchCursor(const QString &themeName, const QS
271280 int i = 0;
272281 while (cursorImage->qimage.isNull() && i < fallbackNames.count()) {
273282 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);
275284 ++i;
276285 }
277286 }
@@ -281,14 +290,14 @@ CursorImage *CursorImageProvider::fetchCursor(const QString &themeName, const QS
281290 if (cursorImage->qimage.isNull() && cursorName != QLatin1String("left_ptr")) {
282291 qDebug() << "CursorImageProvider:" << cursorName
283292 << "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);
285294 }
286295
287296 if (cursorImage->qimage.isNull()) {
288297 // finally, go for the built-in cursor
289298 qWarning() << "CursorImageProvider: couldn't find any cursors. Using the built-in one";
290− if (!m_builtInCursorImage) {
291− m_builtInCursorImage.reset(new BuiltInCursorImage);
299+ if (!m_builtInCursorImage || m_builtInCursorImage->requestedHeight != cursorHeight) {
300+ m_builtInCursorImage.reset(new BuiltInCursorImage(cursorHeight));
292301 }
293302 cursorImage = m_builtInCursorImage.data();
294303 }
@@ -296,7 +305,7 @@ CursorImage *CursorImageProvider::fetchCursor(const QString &themeName, const QS
296305 return cursorImage;
297306}
298307
299−CursorImage *CursorImageProvider::fetchCursorHelper(const QString &themeName, const QString &cursorName)
308+CursorImage *CursorImageProvider::fetchCursorHelper(const QString &themeName, const QString &cursorName, int cursorHeight)
300309{
301310 if (cursorName == QLatin1String("blank")) {
302311 return &m_blankCursorImage;
@@ -306,7 +315,10 @@ CursorImage *CursorImageProvider::fetchCursorHelper(const QString &themeName, co
306315 QMap<QString, CursorImage*> &themeCursors = m_cursors[themeName];
307316
308317 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);
310322 }
311323
312324 return themeCursors[cursorName];
plugins/Cursor/CursorImageProvider.h+9−6
@@ -42,17 +42,20 @@ public:
4242 int frameHeight{0};
4343 int frameCount{1};
4444 int frameDuration{40};
45+
46+ // Requested height when creating this cursor.
47+ int requestedHeight{0};
4548};
4649
4750class XCursorImage : public CursorImage {
4851public:
49− XCursorImage(const QString &theme, const QString &file);
52+ XCursorImage(const QString &theme, const QString &file, int preferredCursorHeightPx);
5053 virtual ~XCursorImage();
5154};
5255
5356class BuiltInCursorImage : public CursorImage {
5457public:
55− BuiltInCursorImage();
58+ BuiltInCursorImage(int cursorHeight);
5659};
5760
5861class BlankCursorImage : public CursorImage {
@@ -74,15 +77,15 @@ public:
7477 static CursorImageProvider *instance() { return m_instance; }
7578
7679
77− QImage requestImage(const QString &cursorName, QSize *size, const QSize &requestedSize) override;
80+ QImage requestImage(const QString &cursorThemeAndNameAndHeight, QSize *size, const QSize &requestedSize) override;
7881
79− CursorImage *fetchCursor(const QString &themeName, const QString &cursorName);
82+ CursorImage *fetchCursor(const QString &themeName, const QString &cursorName, int cursorHeight);
8083
8184 void setCustomCursor(const QCursor &customCursor);
8285
8386private:
84− CursorImage *fetchCursor(const QString &cursorThemeAndName);
85− CursorImage *fetchCursorHelper(const QString &themeName, const QString &cursorName);
87+ CursorImage *fetchCursor(const QString &cursorThemeAndNameAndHeight);
88+ CursorImage *fetchCursorHelper(const QString &themeName, const QString &cursorName, int cursorHeight);
8689
8790 // themeName -> (cursorName -> cursorImage)
8891 // TODO: discard old, unused, cursors
plugins/Dash/CardCreator.js+70−78
@@ -25,7 +25,7 @@ var kBackgroundLoaderCode = 'Loader {\n\
2525 objectName: "backgroundLoader"; \n\
2626 anchors.fill: parent; \n\
2727 asynchronous: %3; \n\
28− visible: status == Loader.Ready; \n\
28+ visible: status === Loader.Ready; \n\
2929 sourceComponent: UbuntuShape { \n\
3030 objectName: "background"; \n\
3131 radius: "medium"; \n\
@@ -75,7 +75,7 @@ var kArtProportionalShapeCode = 'ProportionalShape { \n\
7575 aspect: UbuntuShape.DropShadow; \n\
7676 }';
7777
78−// %1 is used as anchors of artShapeHolder
78+// %1 is used as anchors of artShapeLoader
7979// %2 is used as image width
8080// %3 is used as image height
8181// %4 is whether the image should be visible
@@ -83,42 +83,39 @@ var kArtProportionalShapeCode = 'ProportionalShape { \n\
8383// %6 is the shape code we want to use
8484// %7 is injected as code to artImage
8585// %8 is used as image fallback
86−var kArtShapeHolderCode = 'Item { \n\
87− id: artShapeHolder; \n\
86+var kArtShapeHolderCode = 'Loader { \n\
87+ id: artShapeLoader; \n\
8888 height: root.fixedArtShapeSize.height; \n\
8989 width: root.fixedArtShapeSize.width; \n\
9090 anchors { %1 } \n\
91− Loader { \n\
92− id: artShapeLoader; \n\
93− objectName: "artShapeLoader"; \n\
94− readonly property string cardArt: cardData && cardData["art"] || %8; \n\
95− onCardArtChanged: { if (item) { item.image.source = cardArt; } } \n\
96− active: cardArt != ""; \n\
97− asynchronous: %5; \n\
98− visible: status == Loader.Ready; \n\
99− sourceComponent: Item { \n\
100− id: artShape; \n\
101− objectName: "artShape"; \n\
102− visible: image.status == Image.Ready; \n\
103− readonly property alias image: artImage; \n\
104− %6 \n\
105− width: root.fixedArtShapeSize.width; \n\
106− height: root.fixedArtShapeSize.height; \n\
107− CroppedImageMinimumSourceSize { \n\
108− id: artImage; \n\
109− objectName: "artImage"; \n\
110− source: artShapeLoader.cardArt; \n\
111− asynchronous: %5; \n\
112− visible: %4; \n\
113− width: %2; \n\
114− height: %3; \n\
115− %7 \n\
116− } \n\
91+ objectName: "artShapeLoader"; \n\
92+ readonly property string cardArt: cardData && cardData["art"] || %8; \n\
93+ onCardArtChanged: { if (item) { item.image.source = cardArt; } } \n\
94+ active: cardArt != ""; \n\
95+ asynchronous: %5; \n\
96+ visible: status === Loader.Ready; \n\
97+ sourceComponent: Item { \n\
98+ id: artShape; \n\
99+ objectName: "artShape"; \n\
100+ visible: image.status === Image.Ready; \n\
101+ readonly property alias image: artImage; \n\
102+ %6 \n\
103+ width: root.fixedArtShapeSize.width; \n\
104+ height: root.fixedArtShapeSize.height; \n\
105+ CroppedImageMinimumSourceSize { \n\
106+ id: artImage; \n\
107+ objectName: "artImage"; \n\
108+ source: artShapeLoader.cardArt; \n\
109+ asynchronous: %5; \n\
110+ visible: %4; \n\
111+ width: %2; \n\
112+ height: %3; \n\
113+ %7 \n\
117114 } \n\
118115 } \n\
119116 }\n';
120117
121−// %1 is used as anchors of artShapeHolder
118+// %1 is used as anchors of artShapeLoader
122119// %2 is used as image width
123120// %3 is used as image height
124121// %4 is whether the image should be visible
@@ -126,37 +123,32 @@ var kArtShapeHolderCode = 'Item { \n\
126123// %6 is the shape code we want to use
127124// %7 is injected as code to artImage
128125// %8 is used as image fallback
129−var kArtShapeHolderCodeCardToolCard = 'Item { \n\
130− id: artShapeHolder; \n\
131− height: artShapeLoader.height; \n\
132− width: artShapeLoader.width; \n\
126+var kArtShapeHolderCodeCardToolCard = 'Loader { \n\
127+ id: artShapeLoader; \n\
133128 anchors { %1 } \n\
134− Loader { \n\
135− id: artShapeLoader; \n\
136− objectName: "artShapeLoader"; \n\
137− readonly property string cardArt: cardData && cardData["art"] || %8; \n\
138− onCardArtChanged: { if (item) { item.image.source = cardArt; } } \n\
139− active: cardArt != ""; \n\
140− asynchronous: %5; \n\
141− visible: status == Loader.Ready; \n\
142− sourceComponent: Item { \n\
143− id: artShape; \n\
144− objectName: "artShape"; \n\
145− visible: image.status == Image.Ready; \n\
146− readonly property alias image: artImage; \n\
147− %6 \n\
148− width: image.status !== Image.Ready ? 0 : image.width; \n\
149− height: image.status !== Image.Ready ? 0 : image.height; \n\
150− CroppedImageMinimumSourceSize { \n\
151− id: artImage; \n\
152− objectName: "artImage"; \n\
153− source: artShapeLoader.cardArt; \n\
154− asynchronous: %5; \n\
155− visible: %4; \n\
156− width: %2; \n\
157− height: %3; \n\
158− %7 \n\
159− } \n\
129+ objectName: "artShapeLoader"; \n\
130+ readonly property string cardArt: cardData && cardData["art"] || %8; \n\
131+ onCardArtChanged: { if (item) { item.image.source = cardArt; } } \n\
132+ active: cardArt != ""; \n\
133+ asynchronous: %5; \n\
134+ visible: status === Loader.Ready; \n\
135+ sourceComponent: Item { \n\
136+ id: artShape; \n\
137+ objectName: "artShape"; \n\
138+ visible: image.status === Image.Ready; \n\
139+ readonly property alias image: artImage; \n\
140+ %6 \n\
141+ width: image.status !== Image.Ready ? 0 : image.width; \n\
142+ height: image.status !== Image.Ready ? 0 : image.height; \n\
143+ CroppedImageMinimumSourceSize { \n\
144+ id: artImage; \n\
145+ objectName: "artImage"; \n\
146+ source: artShapeLoader.cardArt; \n\
147+ asynchronous: %5; \n\
148+ visible: %4; \n\
149+ width: %2; \n\
150+ height: %3; \n\
151+ %7 \n\
160152 } \n\
161153 } \n\
162154 }\n';
@@ -214,10 +206,10 @@ var kAudioButtonCode = 'AbstractButton { \n\
214206var kOverlayLoaderCode = 'Loader { \n\
215207 id: overlayLoader; \n\
216208 readonly property real overlayHeight: %2 + units.gu(2); \n\
217− anchors.fill: artShapeHolder; \n\
209+ anchors.fill: artShapeLoader; \n\
218210 active: artShapeLoader.active && artShapeLoader.item && artShapeLoader.item.image.status === Image.Ready || false; \n\
219211 asynchronous: %1; \n\
220− visible: showHeader && status == Loader.Ready; \n\
212+ visible: showHeader && status === Loader.Ready; \n\
221213 sourceComponent: UbuntuShapeOverlay { \n\
222214 id: overlay; \n\
223215 property real luminance: Style.luminance(overlayColor); \n\
@@ -274,7 +266,7 @@ var kMascotShapeLoaderCode = 'Loader { \n\
274266 objectName: "mascotShapeLoader"; \n\
275267 asynchronous: %2; \n\
276268 active: mascotImage.status === Image.Ready; \n\
277− visible: showHeader && active && status == Loader.Ready; \n\
269+ visible: showHeader && active && status === Loader.Ready; \n\
278270 width: units.gu(6); \n\
279271 height: units.gu(5.625); \n\
280272 sourceComponent: UbuntuShape { image: mascotImage } \n\
@@ -592,15 +584,15 @@ function cardString(template, components, isCardTool, artShapeStyle) {
592584
593585 var headerVerticalAnchors;
594586 if (headerAsOverlay) {
595− headerVerticalAnchors = 'bottom: artShapeHolder.bottom; \n\
587+ headerVerticalAnchors = 'bottom: artShapeLoader.bottom; \n\
596588 bottomMargin: units.gu(1);\n';
597589 } else {
598590 if (hasArt) {
599591 if (isHorizontal) {
600− headerVerticalAnchors = 'top: artShapeHolder.top; \n\
592+ headerVerticalAnchors = 'top: artShapeLoader.top; \n\
601593 topMargin: units.gu(1);\n';
602594 } else {
603− headerVerticalAnchors = 'top: artShapeHolder.bottom; \n\
595+ headerVerticalAnchors = 'top: artShapeLoader.bottom; \n\
604596 topMargin: units.gu(1);\n';
605597 }
606598 } else {
@@ -612,7 +604,7 @@ function cardString(template, components, isCardTool, artShapeStyle) {
612604 var headerLeftAnchor;
613605 var headerLeftAnchorHasMargin = false;
614606 if (isHorizontal && hasArt) {
615− headerLeftAnchor = 'left: artShapeHolder.right; \n\
607+ headerLeftAnchor = 'left: artShapeLoader.right; \n\
616608 leftMargin: units.gu(1);\n';
617609 headerLeftAnchorHasMargin = true;
618610 } else if (isHorizontal && isAudio) {
@@ -866,7 +858,7 @@ function cardString(template, components, isCardTool, artShapeStyle) {
866858 var audioButtonWidth;
867859 var audioButtonHeight;
868860 if (hasArt) {
869− audioButtonAnchorsFill = 'artShapeHolder';
861+ audioButtonAnchorsFill = 'artShapeLoader';
870862 audioButtonWidth = 'undefined';
871863 audioButtonHeight = 'undefined';
872864 } else {
@@ -880,15 +872,15 @@ function cardString(template, components, isCardTool, artShapeStyle) {
880872
881873 if (hasSummary) {
882874 var summaryTopAnchor;
883− if (isHorizontal && hasArt) summaryTopAnchor = 'artShapeHolder.bottom';
884− else if (headerAsOverlay && hasArt) summaryTopAnchor = 'artShapeHolder.bottom';
875+ if (isHorizontal && hasArt) summaryTopAnchor = 'artShapeLoader.bottom';
876+ else if (headerAsOverlay && hasArt) summaryTopAnchor = 'artShapeLoader.bottom';
885877 else if (hasHeaderRow) summaryTopAnchor = 'row.bottom';
886878 else if (hasTitleContainer) summaryTopAnchor = 'headerTitleContainer.bottom';
887879 else if (hasMascot) summaryTopAnchor = 'mascotImage.bottom';
888880 else if (hasAttributes) summaryTopAnchor = 'attributesRow.bottom';
889881 else if (hasSubtitle) summaryTopAnchor = 'subtitleLabel.bottom';
890882 else if (hasTitle) summaryTopAnchor = 'titleLabel.bottom';
891− else if (hasArt) summaryTopAnchor = 'artShapeHolder.bottom';
883+ else if (hasArt) summaryTopAnchor = 'artShapeLoader.bottom';
892884 else summaryTopAnchor = 'parent.top';
893885
894886 var summaryColor;
@@ -908,15 +900,15 @@ function cardString(template, components, isCardTool, artShapeStyle) {
908900 var socialTopAnchor;
909901
910902 if (hasSummary) socialTopAnchor = 'summary.bottom;';
911− else if (isHorizontal && hasArt) socialTopAnchor = 'artShapeHolder.bottom;';
912− else if (headerAsOverlay && hasArt) socialTopAnchor = 'artShapeHolder.bottom;';
903+ else if (isHorizontal && hasArt) socialTopAnchor = 'artShapeLoader.bottom;';
904+ else if (headerAsOverlay && hasArt) socialTopAnchor = 'artShapeLoader.bottom;';
913905 else if (hasHeaderRow) socialTopAnchor = 'row.bottom;';
914906 else if (hasTitleContainer) socialTopAnchor = 'headerTitleContainer.bottom;';
915907 else if (hasMascot) socialTopAnchor = 'mascotImage.bottom;';
916908 else if (hasAttributes) socialTopAnchor = 'attributesRow.bottom;';
917909 else if (hasSubtitle) socialTopAnchor = 'subtitleLabel.bottom;';
918910 else if (hasTitle) socialTopAnchor = 'titleLabel.bottom;';
919− else if (hasArt) socialTopAnchor = 'artShapeHolder.bottom;';
911+ else if (hasArt) socialTopAnchor = 'artShapeLoader.bottom;';
920912 else socialTopAnchor = 'parent.top';
921913
922914 socialAnchors = 'top: ' + socialTopAnchor + ' left: parent.left; right: parent.right; topMargin: units.gu(1);'
@@ -936,7 +928,7 @@ function cardString(template, components, isCardTool, artShapeStyle) {
936928 if (hasBackground) {
937929 touchdownAnchors = 'fill: backgroundLoader';
938930 } else if (touchdownOnArtShape) {
939− touchdownAnchors = 'fill: artShapeHolder';
931+ touchdownAnchors = 'fill: artShapeLoader';
940932 } else {
941933 touchdownAnchors = 'fill: root'
942934 }
@@ -951,7 +943,7 @@ function cardString(template, components, isCardTool, artShapeStyle) {
951943 } else if (isAudio) {
952944 implicitHeight += 'audioButton.height;\n';
953945 } else if (headerAsOverlay) {
954− implicitHeight += 'artShapeHolder.height;\n';
946+ implicitHeight += 'artShapeLoader.height;\n';
955947 } else if (hasHeaderRow) {
956948 implicitHeight += 'row.y + row.height + units.gu(1);\n';
957949 } else if (hasMascot) {
@@ -965,7 +957,7 @@ function cardString(template, components, isCardTool, artShapeStyle) {
965957 } else if (hasTitle) {
966958 implicitHeight += 'titleLabel.y + titleLabel.height + units.gu(1);\n';
967959 } else if (hasArt) {
968− implicitHeight += 'artShapeHolder.height;\n';
960+ implicitHeight += 'artShapeLoader.height;\n';
969961 } else {
970962 implicitHeight = '';
971963 }
plugins/Dash/abstractdashview.cpp+13
@@ -16,6 +16,8 @@
1616
1717#include "abstractdashview.h"
1818
19+#include <private/qquickitem_p.h>
20+
1921AbstractDashView::AbstractDashView()
2022 : m_delegateModel(nullptr)
2123 , m_asyncRequestedIndex(-1)
@@ -249,6 +251,7 @@ QQuickItem *AbstractDashView::createItem(int modelIndex, bool asynchronous)
249251 }
250252 return nullptr;
251253 } else {
254+ QQuickItemPrivate::get(item)->addItemChangeListener(this, QQuickItemPrivate::Geometry);
252255 addItemToView(modelIndex, item);
253256 return item;
254257 }
@@ -256,6 +259,7 @@ QQuickItem *AbstractDashView::createItem(int modelIndex, bool asynchronous)
256259
257260void AbstractDashView::releaseItem(QQuickItem *item)
258261{
262+ QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
259263 QQmlDelegateModel::ReleaseFlags flags = m_delegateModel->release(item);
260264 if (flags & QQmlDelegateModel::Destroyed) {
261265 item->setParentItem(nullptr);
@@ -294,6 +298,15 @@ void AbstractDashView::onModelUpdated(const QQmlChangeSet &changeSet, bool reset
294298 cleanupExistingItems();
295299 } else {
296300 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+ }
297310 }
298311 polish();
299312}
plugins/Dash/abstractdashview.h+2−1
@@ -23,9 +23,10 @@ class QAbstractItemModel;
2323class QQmlComponent;
2424
2525#include <private/qqmldelegatemodel_p.h>
26+#include <private/qquickitemchangelistener_p.h>
2627#include <qqmlinfo.h>
2728
28−class AbstractDashView : public QQuickItem
29+class AbstractDashView : public QQuickItem, public QQuickItemChangeListener
2930{
3031 Q_OBJECT
3132
plugins/Dash/verticaljournal.cpp+9−1
@@ -96,7 +96,7 @@ void VerticalJournal::findTopModelIndexToAdd(int *modelIndex, qreal *yPos)
9696
9797 if (*modelIndex > 0) {
9898 Q_ASSERT(m_indexColumnMap.contains(*modelIndex));
99− while (m_indexColumnMap[*modelIndex] != columnToAddTo) {
99+ while (*modelIndex > 0 && m_indexColumnMap[*modelIndex] != columnToAddTo) {
100100 // We found out that we have to add to columnToAddTo
101101 // and thought that we had to add *modelIndex, but history tells
102102 // 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> &
270270 }
271271 }
272272}
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:
6565 qreal columnWidth() const;
6666 void setColumnWidth(qreal columnWidth);
6767
68+protected:
69+ void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) override;
70+
6871Q_SIGNALS:
6972 void columnWidthChanged();
7073
plugins/Unity/Indicators/indicatorsmanager.cpp+4−6
@@ -114,10 +114,6 @@ void IndicatorsManager::loadFile(const QFileInfo& file_info)
114114 QSettings indicator_settings(file_info.absoluteFilePath(), QSettings::IniFormat, this);
115115 const QString name = indicator_settings.value(QStringLiteral("Indicator Service/Name")).toString();
116116
117− if (m_platform.isPC() && name == QLatin1String("indicator-keyboard")) {
118− return; // convergence: skip this indicator until it works in Mir
119− }
120−
121117 auto iter = m_indicatorsData.constFind(name);
122118 if (iter != m_indicatorsData.constEnd())
123119 {
@@ -285,13 +281,15 @@ Indicator::Ptr IndicatorsManager::indicator(const QString& indicator_name)
285281
286282 // convergence:
287283 // 1) enable session indicator
288− // 2) on a PC, switch the battery/power indicator to desktop mode,
284+ // 2) enable keyboard indicator
285+ // 3) on a PC, switch the battery/power indicator to desktop mode,
289286 // can't control brightness for now and phone-on-desktop broken (FIXME)
290287 //
291288 // The rest of the indicators respect their default profile (which is "phone", even on desktop PCs)
292289 if ((new_indicator->identifier() == QStringLiteral("indicator-session"))
290+ || new_indicator->identifier() == QStringLiteral("indicator-keyboard")
293291 || (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")));
295293 } else {
296294 new_indicator->setProfile(m_profile);
297295 }
plugins/Wizard/System.cpp+1
@@ -105,6 +105,7 @@ void System::updateSessionLocale(const QString &locale)
105105 initctl stop scope-registry; \
106106 initctl stop smart-scopes-proxy; \
107107 initctl emit --no-wait indicator-services-start; \
108+ initctl restart --no-wait ubuntu-location-service-trust-stored; \
108109 initctl restart --no-wait maliit-server; \
109110 initctl restart --no-wait indicator-messages; \
110111 initctl restart --no-wait unity8-dash\""));
po/unity8.pot+21−21
@@ -8,7 +8,7 @@ msgid ""
88msgstr ""
99"Project-Id-Version: unity8\n"
1010"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"
1212"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
1313"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1414"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -54,65 +54,65 @@ msgid_plural "Please wait %1 minutes and then try again…"
5454msgstr[0] ""
5555msgstr[1] ""
5656
57−#: qml/Components/Dialogs.qml:144
57+#: qml/Components/Dialogs.qml:149
5858msgctxt "Title: Lock/Log out dialog"
5959msgid "Log out"
6060msgstr ""
6161
62−#: qml/Components/Dialogs.qml:145
62+#: qml/Components/Dialogs.qml:150
6363msgid "Are you sure you want to log out?"
6464msgstr ""
6565
66−#: qml/Components/Dialogs.qml:147
66+#: qml/Components/Dialogs.qml:152
6767msgctxt "Button: Lock the system"
6868msgid "Lock"
6969msgstr ""
7070
71−#: qml/Components/Dialogs.qml:154
71+#: qml/Components/Dialogs.qml:159
7272msgctxt "Button: Log out from the system"
7373msgid "Log Out"
7474msgstr ""
7575
76−#: qml/Components/Dialogs.qml:161 qml/Components/Dialogs.qml:217
77−#: qml/Dash/DashPageHeader.qml:295 qml/Wizard/Pages/passcode-confirm.qml:32
76+#: qml/Components/Dialogs.qml:166 qml/Components/Dialogs.qml:222
77+#: qml/Dash/DashPageHeader.qml:322 qml/Wizard/Pages/passcode-confirm.qml:32
7878#: qml/Wizard/Pages/passcode-set.qml:32
7979msgid "Cancel"
8080msgstr ""
8181
82−#: qml/Components/Dialogs.qml:173
82+#: qml/Components/Dialogs.qml:178
8383msgctxt "Title: Reboot dialog"
8484msgid "Reboot"
8585msgstr ""
8686
87−#: qml/Components/Dialogs.qml:174
87+#: qml/Components/Dialogs.qml:179
8888msgid "Are you sure you want to reboot?"
8989msgstr ""
9090
91−#: qml/Components/Dialogs.qml:176
91+#: qml/Components/Dialogs.qml:181
9292msgid "No"
9393msgstr ""
9494
95−#: qml/Components/Dialogs.qml:182
95+#: qml/Components/Dialogs.qml:187
9696msgid "Yes"
9797msgstr ""
9898
99−#: qml/Components/Dialogs.qml:197
99+#: qml/Components/Dialogs.qml:202
100100msgctxt "Title: Power off/Restart dialog"
101101msgid "Power"
102102msgstr ""
103103
104−#: qml/Components/Dialogs.qml:198
104+#: qml/Components/Dialogs.qml:203
105105msgid ""
106106"Are you sure you would like\n"
107107"to power off?"
108108msgstr ""
109109
110−#: qml/Components/Dialogs.qml:200
110+#: qml/Components/Dialogs.qml:205
111111msgctxt "Button: Power off the system"
112112msgid "Power off"
113113msgstr ""
114114
115−#: qml/Components/Dialogs.qml:209
115+#: qml/Components/Dialogs.qml:214
116116msgctxt "Button: Restart the system"
117117msgid "Restart"
118118msgstr ""
@@ -329,26 +329,26 @@ msgstr ""
329329msgid "Preview Share Item"
330330msgstr ""
331331
332−#: qml/Dash/DashPageHeader.qml:333
332+#: qml/Dash/DashPageHeader.qml:360
333333msgctxt "Button: Open the Ubuntu Store"
334334msgid "Store"
335335msgstr ""
336336
337−#: qml/Dash/DashPageHeader.qml:340
337+#: qml/Dash/DashPageHeader.qml:367
338338msgctxt "Button: Start a search in the current dash scope"
339339msgid "Search"
340340msgstr ""
341341
342−#: qml/Dash/DashPageHeader.qml:350
342+#: qml/Dash/DashPageHeader.qml:377
343343msgctxt "Button: Show the current dash scope settings"
344344msgid "Settings"
345345msgstr ""
346346
347−#: qml/Dash/DashPageHeader.qml:357
347+#: qml/Dash/DashPageHeader.qml:384
348348msgid "Remove from Favorites"
349349msgstr ""
350350
351−#: qml/Dash/DashPageHeader.qml:357
351+#: qml/Dash/DashPageHeader.qml:384
352352msgid "Add to Favorites"
353353msgstr ""
354354
@@ -436,7 +436,7 @@ msgstr ""
436436msgid "Unlock"
437437msgstr ""
438438
439−#: qml/Greeter/Greeter.qml:555
439+#: qml/Greeter/Greeter.qml:561
440440msgid "Try again"
441441msgstr ""
442442
qml/Components/Dialogs.qml+7−2
@@ -15,7 +15,7 @@
1515 */
1616
1717import QtQuick 2.4
18−
18+import QtQuick.Window 2.2
1919import Unity.Application 0.1
2020import Unity.Session 0.1
2121import GlobalShortcut 1.0
@@ -41,6 +41,7 @@ Item {
4141 }
4242 }
4343 property string usageScenario
44+ property size screenSize: Qt.size(Screen.width, Screen.height)
4445
4546 signal powerOffClicked();
4647
@@ -49,7 +50,11 @@ Item {
4950 }
5051
5152 onUsageScenarioChanged: {
52− if (usageScenario != "desktop" && legacyAppsModel.count > 0 && !d.modeSwitchWarningPopup) {
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+
57+ if (usageScenario != "desktop" && legacyAppsModel.count > 0 && !d.modeSwitchWarningPopup && !isTabletSize) {
5358 var comp = Qt.createComponent(Qt.resolvedUrl("ModeSwitchWarningDialog.qml"))
5459 d.modeSwitchWarningPopup = comp.createObject(root, {model: legacyAppsModel});
5560 d.modeSwitchWarningPopup.forceClose.connect(function() {
qml/Components/KeymapSwitcher.qml+27−1
@@ -17,6 +17,7 @@
1717import QtQuick 2.4
1818import AccountsService 0.1
1919import GlobalShortcut 1.0
20+import QMenuModel 0.1
2021import Unity.Application 0.1
2122
2223QtObject {
@@ -36,7 +37,8 @@ QtObject {
3637
3738 readonly property var keymaps: AccountsService.keymaps
3839 readonly property int keymapCount: keymaps.length
39− property int currentKeymapIndex: 0
40+ // default keymap, either the one remembered by the indicator, or the 1st one selected by user
41+ property int currentKeymapIndex: actionGroup.currentAction.valid ? actionGroup.currentAction.state : 0
4042 readonly property string currentKeymap: keymaps[currentKeymapIndex]
4143
4244 function nextKeymap() {
@@ -62,4 +64,28 @@ QtObject {
6264 property: "keymap"
6365 value: root.currentKeymap
6466 }
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+ }
6591}
qml/Dash/DashNavigationList.qml+48−45
@@ -53,61 +53,64 @@ Item {
5353 Repeater {
5454 model: navigation && navigation.loaded ? navigation : null
5555 clip: true
56− // FIXME Move to ListItem (and remove import) once 1556971 is fixed
57− delegate: ListItems.Empty {
58− objectName: root.objectName + "child" + index
56+ delegate: Loader {
57+ asynchronous: true
5958 height: root.itemHeight
6059 width: column.width
61− anchors {
62− left: column.left
63− right: column.right
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
60+ // FIXME Move to ListItem (and remove import) once 1556971 is fixed
61+ sourceComponent: ListItems.Empty {
62+ objectName: root.objectName + "child" + index
7263 anchors {
73− verticalCenter: parent.verticalCenter
7464 left: parent.left
65+ right: parent.right
66+ leftMargin: units.gu(2)
67+ rightMargin: units.gu(2)
7568 }
76− height: units.gu(2)
77− width: height
78− name: "tick"
79− color: "#3EB34F"
80− visible: isActive
81− }
8269
83− Label {
84− anchors {
85− verticalCenter: parent.verticalCenter
86− left: leftIcon.right
87− leftMargin: units.gu(1)
88− right: rightIcon.left
89− rightMargin: units.gu(2)
70+ onClicked: root.enterNavigation(navigationId, allLabel != "" ? allLabel : label, hasChildren)
71+
72+ Icon {
73+ id: leftIcon
74+ anchors {
75+ verticalCenter: parent.verticalCenter
76+ left: parent.left
77+ }
78+ height: units.gu(2)
79+ width: height
80+ name: "tick"
81+ color: "#3EB34F"
82+ visible: isActive
9083 }
91− text: label
92− color: isActive ? "#333333" : "#888888"
93− wrapMode: Text.Wrap
94− maximumLineCount: 2
95− elide: Text.ElideMiddle
96− }
9784
98− Icon {
99− id: rightIcon
100− anchors {
101− verticalCenter: parent.verticalCenter
102− right: parent.right
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+
100+ Icon {
101+ id: rightIcon
102+ anchors {
103+ verticalCenter: parent.verticalCenter
104+ right: parent.right
105+ }
106+ height: units.gu(2)
107+ width: height
108+ name: "go-next"
109+ visible: hasChildren
103110 }
104− height: units.gu(2)
105− width: height
106− name: "go-next"
107− visible: hasChildren
108− }
109111
110− divider.visible: index != navigation.count - 1
112+ divider.visible: index != navigation.count - 1
113+ }
111114 }
112115 }
113116 }
qml/Dash/DashPageHeader.qml+49−22
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013,2015 Canonical, Ltd.
2+ * Copyright (C) 2013,2015,2016 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -81,7 +81,8 @@ Item {
8181 function closePopup(keepFocus, keepSearch) {
8282 if (extraPanel.visible) {
8383 extraPanel.visible = false;
84− } else if (!keepFocus) {
84+ }
85+ if (!keepFocus) {
8586 unfocus(keepSearch);
8687 }
8788 if (!keepSearch && !searchTextField.text && !root.navigationTag && searchHistory.count == 0) {
@@ -131,47 +132,62 @@ Item {
131132 anchors { fill: parent; margins: units.gu(1); bottomMargin: units.gu(3) + (extraPanel ? extraPanel.height : 0) }
132133 visible: headerContainer.showSearch
133134 onPressed: {
134− extraPanel.visible = false;
135135 closePopup(/* keepFocus */false);
136136 mouse.accepted = false;
137137 }
138138 }
139139
140− Flickable {
140+ Item {
141141 id: headerContainer
142142 objectName: "headerContainer"
143− clip: contentY < height
144143 anchors { left: parent.left; top: parent.top; right: parent.right }
145144 height: header.__styleInstance.contentHeight
146− contentHeight: headersColumn.height
147− interactive: false
148− contentY: showSearch ? 0 : height
149145
150146 property bool showSearch: false
151147
152− Background {
153− id: background
154− objectName: "headerBackground"
155− style: scopeStyle.headerBackground
148+ state: headerContainer.showSearch ? "search" : ""
149+
150+ states: State {
151+ name: "search"
152+
153+ AnchorChanges {
154+ target: headersColumn
155+ anchors.top: parent.top
156+ anchors.bottom: undefined
157+ }
156158 }
157159
158− Behavior on contentY {
159− UbuntuNumberAnimation {
160− id: openSearchAnimation
161− property bool openPopup: false
160+ transitions: Transition {
161+ id: openSearchAnimation
162+ AnchorAnimation {
163+ duration: UbuntuAnimation.FastDuration
164+ easing: UbuntuAnimation.StandardEasing
165+ }
162166
163− onRunningChanged: {
164− if (!running && openSearchAnimation.openPopup) {
165− openSearchAnimation.openPopup = false;
166− root.openPopup();
167− }
167+ property bool openPopup: false
168+
169+ onRunningChanged: {
170+ headerContainer.clip = running;
171+ if (!running && openSearchAnimation.openPopup) {
172+ openSearchAnimation.openPopup = false;
173+ root.openPopup();
168174 }
169175 }
170176 }
171177
178+ Background {
179+ id: background
180+ objectName: "headerBackground"
181+ style: scopeStyle.headerBackground
182+ }
183+
172184 Column {
173185 id: headersColumn
174− anchors { left: parent.left; right: parent.right }
186+ anchors {
187+ left: parent.left
188+ right: parent.right
189+ bottom: parent.bottom
190+ }
175191
176192 PageHeader {
177193 id: searchHeader
@@ -187,6 +203,15 @@ Item {
187203 contents: Item {
188204 anchors.fill: parent
189205
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+
190215 TextField {
191216 id: searchTextField
192217 objectName: "searchTextField"
@@ -226,6 +251,8 @@ Item {
226251 anchors.fill: parent
227252 anchors.margins: units.gu(1)
228253 source: "image://theme/clear"
254+ sourceSize.width: width
255+ sourceSize.height: height
229256 opacity: parent.enabled
230257 visible: opacity > 0
231258 Behavior on opacity {
qml/Dash/Filters/FilterExpandableWidget.qml+1
@@ -65,6 +65,7 @@ FilterWidget {
6565 anchors.rightMargin: units.gu(2)
6666 anchors.verticalCenter: parent.verticalCenter
6767 source: expandingItem.expanded ? "image://theme/up" : "image://theme/down"
68+ sourceSize.height: height
6869 }
6970 }
7071
qml/Dash/Filters/FilterOptionSelector.qml+2
@@ -60,6 +60,7 @@ FilterWidget {
6060 anchors.right: parent.right
6161 anchors.verticalCenter: parent.verticalCenter
6262 source: expandingItem.expanded ? "image://theme/up" : "image://theme/down"
63+ sourceSize.height: height
6364 }
6465 }
6566
@@ -81,6 +82,7 @@ FilterWidget {
8182 anchors.right: parent.right
8283 anchors.verticalCenter: parent.verticalCenter
8384 source: "image://theme/tick"
85+ sourceSize.height: height
8486 visible: checked
8587 }
8688
qml/Dash/GenericScopeView.qml+1−1
@@ -662,7 +662,7 @@ FocusScope {
662662 }
663663 onShowFiltersPopup: { // item
664664 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); } ) } );
666666 scopeView.filtersPopover.Component.onDestruction.connect(function () {
667667 categoryView.pageHeader.closePopup(false, true);
668668 categoryView.pageHeader.unfocus(true); // remove the focus from the search field
qml/Greeter/Greeter.qml+9−3
@@ -76,9 +76,16 @@ Showable {
7676 // already shown, do it manually.
7777 d.selectUser(d.currentIndex, true);
7878 }
79+
7980 // Even though we may already be shown, we want to call show() for its
8081 // possible side effects, like hiding indicators and such.
81− showNow();
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) {
87+ showNow();
88+ }
8289 }
8390
8491 function notifyAppFocusRequested(appId) {
@@ -427,8 +434,7 @@ Showable {
427434 target: LightDMService.greeter
428435
429436 onShowGreeter: root.forceShow()
430−
431− onHideGreeter: d.login()
437+ onHideGreeter: root.forcedUnlock = true
432438
433439 onShowMessage: {
434440 // inefficient, but we only rarely deal with messages
qml/Panel/IndicatorItemRow.qml+13−2
@@ -18,6 +18,10 @@ import QtQuick 2.4
1818import QtQuick.Window 2.2
1919import Ubuntu.Components 1.3
2020
21+// for indicator-keyboard
22+import AccountsService 0.1
23+import Unity.InputInfo 0.1
24+
2125Item {
2226 id: root
2327 width: row.width
@@ -127,6 +131,11 @@ Item {
127131 property bool forceAlignmentAnimationDisabled: false
128132 }
129133
134+ InputDeviceModel {
135+ id: keyboardsModel
136+ deviceFilter: InputInfo.Keyboard
137+ }
138+
130139 onCurrentItemChanged: {
131140 if (d.previousItem) {
132141 d.firstItemSwitch = false;
@@ -184,9 +193,11 @@ Item {
184193
185194 property int ownIndex: index
186195 property bool overflow: row.width - x > overFlowWidth
187− property bool hidden: !expanded && (overflow || !indicatorVisible || hideSessionIndicator)
196+ property bool hidden: !expanded && (overflow || !indicatorVisible || hideSessionIndicator || hideKeyboardIndicator)
188197 // HACK for indicator-session
189198 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)
190201
191202 height: row.height
192203 expanded: root.expanded
@@ -202,7 +213,7 @@ Item {
202213 NumberAnimation { duration: UbuntuAnimation.SnapDuration; easing: UbuntuAnimation.StandardEasing }
203214 }
204215
205− width: ((expanded || indicatorVisible) && !hideSessionIndicator) ? implicitWidth : 0
216+ width: ((expanded || indicatorVisible) && !hideSessionIndicator && !hideKeyboardIndicator) ? implicitWidth : 0
206217
207218 Behavior on width {
208219 NumberAnimation { duration: UbuntuAnimation.SnapDuration; easing: UbuntuAnimation.StandardEasing }
qml/Panel/Indicators/MenuItemFactory.qml+65
@@ -985,6 +985,62 @@ Item {
985985 }
986986 }
987987
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+
9881044 function load(modelData, context) {
9891045 // tweak indicator-session items
9901046 if (context === "indicator-session") {
@@ -995,6 +1051,15 @@ Item {
9951051 }
9961052 }
9971053
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+
9981063 if (modelData.type !== undefined && modelData.type !== "") {
9991064 var component = undefined;
10001065
qml/Shell.qml+2
@@ -775,6 +775,8 @@ StyledItem {
775775 when: cursor.mouseNeverMoved && cursor.visible
776776 }
777777
778+ height: units.gu(3)
779+
778780 onPushedLeftBoundary: {
779781 if (buttons === Qt.NoButton) {
780782 launcher.pushEdge(amount);
qml/Stages/AbstractStage.qml+9
@@ -16,6 +16,7 @@
1616
1717import QtQuick 2.4
1818import Ubuntu.Components 1.3
19+import GlobalShortcut 1.0
1920import GSettings 1.0
2021
2122FocusScope {
@@ -78,4 +79,12 @@ FocusScope {
7879 color: "#060606"
7980 anchors.fill: parent
8081 }
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+ }
8190}
qml/Stages/ApplicationWindow.qml+6
@@ -46,6 +46,7 @@ FocusScope {
4646 property alias resizeSurface: surfaceContainer.resizeSurface
4747 property int requestedWidth: -1
4848 property int requestedHeight: -1
49+ property real splashRotation: 0
4950
5051 readonly property int minimumWidth: surface ? surface.minimumWidth : 0
5152 readonly property int minimumHeight: surface ? surface.minimumHeight : 0
@@ -184,6 +185,11 @@ FocusScope {
184185 backgroundColor: d.splashColor
185186 headerColor: d.splashColorHeader
186187 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
187193 }
188194 }
189195 }
qml/Stages/DesktopStage.qml+8−8
@@ -36,6 +36,11 @@ AbstractStage {
3636 edgeBarrier.push(amount);
3737 }
3838 }
39+ function closeFocusedDelegate() {
40+ if (priv.focusedAppDelegate && !priv.focusedAppDelegate.isDash) {
41+ priv.focusedAppDelegate.close();
42+ }
43+ }
3944
4045 // Used by TutorialRight
4146 property bool spreadShown: spread.state == "altTab"
@@ -47,13 +52,6 @@ AbstractStage {
4752
4853 orientationChangesEnabled: true
4954
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−
5755 GlobalShortcut {
5856 id: showSpreadShortcut
5957 shortcut: Qt.MetaModifier|Qt.Key_W
@@ -205,7 +203,7 @@ AbstractStage {
205203 Binding {
206204 target: PanelState
207205 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
209207 }
210208
211209 Component.onDestruction: {
@@ -337,6 +335,8 @@ AbstractStage {
337335 readonly property var surface: model.surface
338336 readonly property alias resizeArea: resizeArea
339337
338+ readonly property bool isDash: model.application.appId == "unity8-dash"
339+
340340 function claimFocus() {
341341 if (spread.state == "altTab") {
342342 spread.cancel();
qml/Stages/PhoneStage.qml+8−2
@@ -108,6 +108,12 @@ AbstractStage {
108108 }
109109 }
110110
111+ function closeFocusedDelegate() {
112+ if (priv.focusedAppDelegate && priv.focusedAppDelegate.closeable) {
113+ priv.focusedAppDelegate.closed();
114+ }
115+ }
116+
111117 mainApp: priv.focusedAppDelegate ? priv.focusedAppDelegate.application : null
112118
113119 orientationChangesEnabled: priv.focusedAppOrientationChangesEnabled
@@ -465,13 +471,13 @@ AbstractStage {
465471 model: topLevelSurfaceList
466472
467473 onItemRemoved: {
468− // Unless we're closing the app ourselves in the spread,
474+ // Unless we're closing the app ourselves,
469475 // lets make sure the spread doesn't mess up by the changing app list.
470476 if (spreadView.closingIndex == -1) {
471477 spreadView.phase = 0;
472478 spreadView.contentX = -spreadView.shift;
473− focusTopMostApp();
474479 }
480+ focusTopMostApp();
475481 }
476482 function focusTopMostApp() {
477483 if (spreadRepeater.count > 0) {
qml/Stages/SpreadDelegate.qml+71−81
@@ -82,12 +82,69 @@ FocusScope {
8282
8383 QtObject {
8484 id: priv
85+ objectName: "spreadDelegatePriv"
8586 property bool startingUp: true
8687 }
8788
8889 Component.onCompleted: { finishStartUpTimer.start(); }
8990 Timer { id: finishStartUpTimer; interval: 400; onTriggered: priv.startingUp = false }
9091
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+
91148 Rectangle {
92149 id: background
93150 color: "black"
@@ -129,9 +186,7 @@ FocusScope {
129186 }
130187
131188 state: {
132− if (priv.startingUp) {
133− return "startingUp";
134− } else if (root.application && root.application.rotatesWindowContents) {
189+ if (root.application && root.application.rotatesWindowContents) {
135190 return "counterRotate";
136191 } else if (orientationChangeAnimation.running) {
137192 return "animatingRotation";
@@ -149,118 +204,53 @@ FocusScope {
149204 }
150205
151206 states: [
152− // Sets the initial orientationAngle of the window, when it first slides into view
153− // (with the splash screen likely being displayed). At that point we just try to
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.
207+ // A base state containing bindings common to others
208+ // Ensures appWindowWithShadow fills the root area.
157209 State {
158− name: "startingUp"
210+ name: "fillRootArea"
159211 PropertyChanges {
160212 target: appWindowWithShadow
161213 restoreEntryValues: false
162− orientationAngle: {
163− if (!root.application || root.application.rotatesWindowContents) {
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− }
214+ width: appWindowWithShadow.rotation == 0 || appWindowWithShadow.rotation == 180 ? root.width : root.height
215+ height: appWindowWithShadow.rotation == 0 || appWindowWithShadow.rotation == 180 ? root.height : root.width
207216 }
208217 },
209218 // In this state we stick to our currently set orientationAngle, which may change only due
210219 // to calls made to matchShellOrientation() or animateToShellOrientation()
211220 State {
212− id: keepSceneRotationState
213221 name: "keepSceneRotation"
214−
215− StateChangeScript { script: {
216− // break binding
217− appWindowWithShadow.orientationAngle = appWindowWithShadow.orientationAngle;
218− } }
222+ extend: "fillRootArea"
219223 PropertyChanges {
220224 target: appWindowWithShadow
221225 restoreEntryValues: false
222226 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− }
236227 }
237228 },
238229 // In this state we counteract any shell rotation so that the window, in scene coordinates,
239230 // remains unrotated.
231+ // But the splash screen should still obey the set orientationAngle set by shell
240232 State {
241233 name: "counterRotate"
242− StateChangeScript { script: {
243− // break binding
244− appWindowWithShadow.orientationAngle = appWindowWithShadow.orientationAngle;
245− } }
234+ extend: "fillRootArea"
246235 PropertyChanges {
247236 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
250237 rotation: normalizeAngle(-root.shellOrientationAngle)
251238 }
252239 PropertyChanges {
253240 target: appWindow
254241 surfaceOrientationAngle: appWindowWithShadow.orientationAngle
242+ splashRotation: appWindowWithShadow.orientationAngle
255243 }
256244 },
245+ // Dummy state.
246+ // The animation may indiscriminately break any bindings assigned to appWindowWithShadow rotation,
247+ // width or height.
257248 State {
258249 name: "animatingRotation"
259250 }
260251 ]
261252
262− x: (parent.width - width) / 2
263− y: (parent.height - height) / 2
253+ anchors.centerIn: parent
264254
265255 BorderImage {
266256 anchors {
qml/Stages/TabletStage.qml+8−2
@@ -84,6 +84,12 @@ AbstractStage {
8484 }
8585 }
8686
87+ function closeFocusedDelegate() {
88+ if (priv.focusedAppDelegate && priv.focusedAppDelegate.closeable) {
89+ priv.focusedAppDelegate.closed();
90+ }
91+ }
92+
8793 orientationChangesEnabled: priv.mainAppOrientationChangesEnabled
8894
8995 mainApp: {
@@ -707,13 +713,13 @@ AbstractStage {
707713
708714 onItemRemoved: {
709715 priv.updateMainAndSideStageIndexes();
710− // Unless we're closing the app ourselves in the spread,
716+ // Unless we're closing the app ourselves,
711717 // lets make sure the spread doesn't mess up by the changing app list.
712718 if (spreadView.closingIndex == -1) {
713719 spreadView.phase = 0;
714720 spreadView.contentX = -spreadView.shift;
715− focusTopMostApp();
716721 }
722+ focusTopMostApp();
717723 }
718724 function focusTopMostApp() {
719725 if (spreadRepeater.count > 0) {
src/Dash/main.cpp+1−1
@@ -37,7 +37,7 @@
3737
3838int main(int argc, const char *argv[])
3939{
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}");
4141 if (enableQmlDebugger(argc, argv)) {
4242 QQmlDebuggingEnabler qQmlEnableDebuggingHelper(true);
4343 }
src/main.cpp+1−1
@@ -21,7 +21,7 @@
2121
2222int main(int argc, const char *argv[])
2323{
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}");
2525
2626 bool isMirServer = false;
2727 if (qgetenv("QT_QPA_PLATFORM") == "ubuntumirclient") {
tests/mocks/Unity/Application/MirSurfaceItem.cpp+37−10
@@ -43,6 +43,7 @@ MirSurfaceItem::MirSurfaceItem(QQuickItem *parent)
4343 , m_qmlContentComponent(nullptr)
4444 , m_qmlItem(nullptr)
4545 , m_consumesInput(false)
46+ , m_orientationAngle(nullptr)
4647 , m_surfaceWidth(0)
4748 , m_surfaceHeight(0)
4849 , m_touchPressCount(0)
@@ -68,6 +69,7 @@ MirSurfaceItem::~MirSurfaceItem()
6869{
6970 DEBUG_MSG((void*)(this) << name());
7071 setSurface(nullptr);
72+ delete m_orientationAngle;
7173}
7274
7375void MirSurfaceItem::printComponentErrors()
@@ -125,7 +127,10 @@ Mir::ShellChrome MirSurfaceItem::shellChrome() const
125127
126128Mir::OrientationAngle MirSurfaceItem::orientationAngle() const
127129{
128− if (m_qmlSurface) {
130+ if (m_orientationAngle) {
131+ Q_ASSERT(!m_qmlSurface);
132+ return *m_orientationAngle;
133+ } else if (m_qmlSurface) {
129134 return m_qmlSurface->orientationAngle();
130135 } else {
131136 return Mir::Angle0;
@@ -134,16 +139,24 @@ Mir::OrientationAngle MirSurfaceItem::orientationAngle() const
134139
135140void MirSurfaceItem::setOrientationAngle(Mir::OrientationAngle angle)
136141{
137− if (!m_qmlSurface)
138− return;
139−
140− if (m_qmlSurface->orientationAngle() == angle)
141− return;
142+ DEBUG_MSG(angle);
142143
143− m_qmlSurface->setOrientationAngle(angle);
144+ if (m_qmlSurface) {
145+ Q_ASSERT(!m_orientationAngle);
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+ }
144155
145− QQmlProperty orientationProp(m_qmlItem, "orientationAngle");
146− orientationProp.write(QVariant::fromValue(m_qmlSurface->orientationAngle()));
156+ if (m_qmlItem) {
157+ QQmlProperty orientationProp(m_qmlItem, "orientationAngle");
158+ orientationProp.write(QVariant::fromValue(orientationAngle()));
159+ }
147160}
148161
149162void MirSurfaceItem::updateScreenshot(QUrl screenshotUrl)
@@ -175,6 +188,11 @@ void MirSurfaceItem::createQmlContentItem()
175188 QQmlProperty screenshotSource(m_qmlItem, "screenshotSource");
176189 screenshotSource.write(QVariant::fromValue(m_qmlSurface->screenshotUrl()));
177190 }
191+
192+ {
193+ QQmlProperty orientationProp(m_qmlItem, "orientationAngle");
194+ orientationProp.write(QVariant::fromValue(orientationAngle()));
195+ }
178196}
179197
180198void MirSurfaceItem::touchEvent(QTouchEvent * event)
@@ -255,7 +273,16 @@ void MirSurfaceItem::setSurface(MirSurfaceInterface* surface)
255273 updateSurfaceSize();
256274 updateMirSurfaceVisibility();
257275
258− connect(m_qmlSurface, &MirSurface::orientationAngleChanged, this, &MirSurfaceItem::orientationAngleChanged);
276+ if (m_orientationAngle) {
277+ m_qmlSurface->setOrientationAngle(*m_orientationAngle);
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+
259286 connect(m_qmlSurface, &MirSurface::screenshotUrlChanged, this, &MirSurfaceItem::updateScreenshot);
260287 connect(m_qmlSurface, &MirSurface::liveChanged, this, &MirSurfaceItem::liveChanged);
261288 connect(m_qmlSurface, &MirSurface::stateChanged, this, &MirSurfaceItem::surfaceStateChanged);
tests/mocks/Unity/Application/MirSurfaceItem.h+2
@@ -117,6 +117,8 @@ private:
117117
118118 bool m_consumesInput;
119119
120+ Mir::OrientationAngle *m_orientationAngle;
121+
120122 int m_surfaceWidth;
121123 int m_surfaceHeight;
122124
tests/mocks/Unity/Indicators/IndicatorsModel.qml+41−1
@@ -16,11 +16,22 @@
1616
1717import QtQuick 2.4
1818import Unity.Indicators 0.1 as Indicators
19+import Unity.InputInfo 0.1
20+import AccountsService 0.1
1921
2022Indicators.FakeIndicatorsModel {
2123 id: root
2224
2325 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+ },
2435 {
2536 "identifier": "fake-indicator-bluetooth",
2637 "indicatorProperties": {
@@ -74,13 +85,38 @@ Indicators.FakeIndicatorsModel {
7485 "menuObjectPath": "/com/canonical/indicators/fake6",
7586 "actionsObjectPath": "/com/canonical/indicators/fake6"
7687 }
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+ }
7797 }
7898 ]
7999
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+
80111 function load(profile) {
81112 unload();
82113 root.modelData = originalModelData;
83114
115+ Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicators/fake0",
116+ getUnityMenuModelData("indicator-keyboard",
117+ "Czech (F)",
118+ "",
119+ [ "image://theme/input-keyboard-symbolic" ]));
84120 Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicators/fake1",
85121 getUnityMenuModelData("fake-indicator-bluetooth",
86122 "Bluetooth (F)",
@@ -111,6 +147,11 @@ Indicators.FakeIndicatorsModel {
111147 "Upcoming Events (F)",
112148 "12:04",
113149 []));
150+ Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicators/fake7",
151+ getUnityMenuModelData("fake-indicator-session",
152+ "System (F)",
153+ "",
154+ ["image://theme/system-devices-panel"]));
114155 }
115156
116157 function getUnityMenuModelData(identifier, title, label, icons) {
@@ -173,6 +214,5 @@ Indicators.FakeIndicatorsModel {
173214 break;
174215 }
175216 }
176−
177217 }
178218}
tests/plugins/Cursor/tst_Cursor.qml+8−1
@@ -26,11 +26,13 @@ Rectangle {
2626
2727 property string themeName: "default"
2828 property string cursorName: "left_ptr"
29+ property string cursorHeight: "32"
2930
3031 CursorImageInfo {
3132 id: imageInfo
3233 themeName: root.themeName
3334 cursorName: root.cursorName
35+ cursorHeight: root.cursorHeight
3436 }
3537
3638 Item {
@@ -43,7 +45,7 @@ Rectangle {
4345
4446 x: -imageInfo.hotspot.x
4547 y: -imageInfo.hotspot.y
46− source: "image://cursor/" + root.themeName + "/" + root.cursorName
48+ source: "image://cursor/" + root.themeName + "/" + root.cursorName + "/" + root.cursorHeight
4749
4850 interpolate: false
4951
@@ -110,6 +112,10 @@ Rectangle {
110112
111113 Item {width: 10; height: 40}
112114
115+ TextEntry { id: cursorHeightEntry; name: "cursorHeight"; value: "32" }
116+
117+ Item {width: 10; height: 40}
118+
113119 Rectangle {
114120 color: applyMouseArea.pressed ? "green" : "lightslategray"
115121 width: parent.width - 20
@@ -121,6 +127,7 @@ Rectangle {
121127 onClicked: {
122128 root.themeName = themeNameEntry.value;
123129 root.cursorName = cursorNameEntry.value;
130+ root.cursorHeight = cursorHeightEntry.value;
124131 }
125132 }
126133 }
tests/plugins/Dash/cardcreator/1.res+8−11
@@ -12,23 +12,21 @@ AbstractButton {
1212 property size fixedArtShapeSize: Qt.size(-1, -1);
1313signal action(var actionId);
1414readonly 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 {
2116 id: artShapeLoader;
17+ height: root.fixedArtShapeSize.height;
18+ width: root.fixedArtShapeSize.width;
19+ anchors { horizontalCenter: parent.horizontalCenter; }
2220 objectName: "artShapeLoader";
2321 readonly property string cardArt: cardData && cardData["art"] || "";
2422 onCardArtChanged: { if (item) { item.image.source = cardArt; } }
2523 active: cardArt != "";
2624 asynchronous: true;
27− visible: status == Loader.Ready;
25+ visible: status === Loader.Ready;
2826 sourceComponent: Item {
2927 id: artShape;
3028 objectName: "artShape";
31− visible: image.status == Image.Ready;
29+ visible: image.status === Image.Ready;
3230 readonly property alias image: artImage;
3331 UbuntuShape {
3432 anchors.fill: parent;
@@ -50,14 +48,13 @@ Item {
5048 }
5149 }
5250 }
53− }
5451readonly property int headerHeight: titleLabel.height;
5552Label {
5653 id: titleLabel;
5754 objectName: "titleLabel";
5855 anchors { right: parent.right;
5956 left: parent.left;
60− top: artShapeHolder.bottom;
57+ top: artShapeLoader.bottom;
6158 topMargin: units.gu(1);
6259 }
6360 elide: Text.ElideRight;
@@ -75,7 +72,7 @@ Label {
7572UbuntuShape {
7673 id: touchdown;
7774 objectName: "touchdown";
78− anchors { fill: artShapeHolder }
75+ anchors { fill: artShapeLoader }
7976 visible: root.pressed;
8077 radius: "medium";
8178 borderSource: "radius_pressed.sci"
tests/plugins/Dash/cardcreator/1.res.cardcreator+6−11
@@ -10,23 +10,19 @@ AbstractButton {
1010 enabled: true;
1111signal action(var actionId);
1212readonly 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 {
1914 id: artShapeLoader;
15+ anchors { horizontalCenter: parent.horizontalCenter; }
2016 objectName: "artShapeLoader";
2117 readonly property string cardArt: cardData && cardData["art"] || "";
2218 onCardArtChanged: { if (item) { item.image.source = cardArt; } }
2319 active: cardArt != "";
2420 asynchronous: false;
25− visible: status == Loader.Ready;
21+ visible: status === Loader.Ready;
2622 sourceComponent: Item {
2723 id: artShape;
2824 objectName: "artShape";
29− visible: image.status == Image.Ready;
25+ visible: image.status === Image.Ready;
3026 readonly property alias image: artImage;
3127 UbuntuShape {
3228 anchors.fill: parent;
@@ -47,7 +43,6 @@ Item {
4743 height: width / 1.6;
4844 }
4945 }
50− }
5146 }
5247readonly property int headerHeight: titleLabel.height;
5348Label {
@@ -55,7 +50,7 @@ Label {
5550 objectName: "titleLabel";
5651 anchors { right: parent.right;
5752 left: parent.left;
58− top: artShapeHolder.bottom;
53+ top: artShapeLoader.bottom;
5954 topMargin: units.gu(1);
6055 }
6156 elide: Text.ElideRight;
@@ -73,7 +68,7 @@ Label {
7368UbuntuShape {
7469 id: touchdown;
7570 objectName: "touchdown";
76− anchors { fill: artShapeHolder }
71+ anchors { fill: artShapeLoader }
7772 visible: root.pressed;
7873 radius: "medium";
7974 borderSource: "radius_pressed.sci"
tests/plugins/Dash/cardcreator/10.res+1−1
@@ -16,7 +16,7 @@ Loader {
1616 objectName: "backgroundLoader";
1717 anchors.fill: parent;
1818 asynchronous: true;
19− visible: status == Loader.Ready;
19+ visible: status === Loader.Ready;
2020 sourceComponent: UbuntuShape {
2121 objectName: "background";
2222 radius: "medium";
tests/plugins/Dash/cardcreator/10.res.cardcreator+1−1
@@ -14,7 +14,7 @@ Loader {
1414 objectName: "backgroundLoader";
1515 anchors.fill: parent;
1616 asynchronous: false;
17− visible: status == Loader.Ready;
17+ visible: status === Loader.Ready;
1818 sourceComponent: UbuntuShape {
1919 objectName: "background";
2020 radius: "medium";
tests/plugins/Dash/cardcreator/11.res+9−12
@@ -16,7 +16,7 @@ Loader {
1616 objectName: "backgroundLoader";
1717 anchors.fill: parent;
1818 asynchronous: true;
19− visible: status == Loader.Ready;
19+ visible: status === Loader.Ready;
2020 sourceComponent: UbuntuShape {
2121 objectName: "background";
2222 radius: "medium";
@@ -50,23 +50,21 @@ Loader {
5050 }
5151 }
5252readonly 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 {
5954 id: artShapeLoader;
55+ height: root.fixedArtShapeSize.height;
56+ width: root.fixedArtShapeSize.width;
57+ anchors { horizontalCenter: parent.horizontalCenter; }
6058 objectName: "artShapeLoader";
6159 readonly property string cardArt: cardData && cardData["art"] || decodeURI("%5C");
6260 onCardArtChanged: { if (item) { item.image.source = cardArt; } }
6361 active: cardArt != "";
6462 asynchronous: true;
65− visible: status == Loader.Ready;
63+ visible: status === Loader.Ready;
6664 sourceComponent: Item {
6765 id: artShape;
6866 objectName: "artShape";
69− visible: image.status == Image.Ready;
67+ visible: image.status === Image.Ready;
7068 readonly property alias image: artImage;
7169 UbuntuShape {
7270 anchors.fill: parent;
@@ -87,8 +85,7 @@ Item {
8785 height: width / (root.fixedArtShapeSize.width / root.fixedArtShapeSize.height);
8886 onStatusChanged: if (status === Image.Error) source = decodeURI("%5C");
8987 }
90− }
91− }
88+ }
9289 }
9390readonly property int headerHeight: row.height;
9491Row {
@@ -97,7 +94,7 @@ Row {
9794 property real margins: units.gu(1);
9895 spacing: margins;
9996 height: root.fixedHeaderHeight;
100− anchors { top: artShapeHolder.bottom;
97+ anchors { top: artShapeLoader.bottom;
10198 topMargin: units.gu(1);
10299left: parent.left;
103100 }
tests/plugins/Dash/cardcreator/11.res.cardcreator+6−11
@@ -14,7 +14,7 @@ Loader {
1414 objectName: "backgroundLoader";
1515 anchors.fill: parent;
1616 asynchronous: false;
17− visible: status == Loader.Ready;
17+ visible: status === Loader.Ready;
1818 sourceComponent: UbuntuShape {
1919 objectName: "background";
2020 radius: "medium";
@@ -48,23 +48,19 @@ Loader {
4848 }
4949 }
5050readonly 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 {
5752 id: artShapeLoader;
53+ anchors { horizontalCenter: parent.horizontalCenter; }
5854 objectName: "artShapeLoader";
5955 readonly property string cardArt: cardData && cardData["art"] || "";
6056 onCardArtChanged: { if (item) { item.image.source = cardArt; } }
6157 active: cardArt != "";
6258 asynchronous: false;
63− visible: status == Loader.Ready;
59+ visible: status === Loader.Ready;
6460 sourceComponent: Item {
6561 id: artShape;
6662 objectName: "artShape";
67− visible: image.status == Image.Ready;
63+ visible: image.status === Image.Ready;
6864 readonly property alias image: artImage;
6965 UbuntuShape {
7066 anchors.fill: parent;
@@ -85,7 +81,6 @@ Item {
8581 height: width / 1;
8682 }
8783 }
88− }
8984 }
9085readonly property int headerHeight: row.height;
9186Row {
@@ -93,7 +88,7 @@ Row {
9388 objectName: "outerRow";
9489 property real margins: units.gu(1);
9590 spacing: margins;
96− anchors { top: artShapeHolder.bottom;
91+ anchors { top: artShapeLoader.bottom;
9792 topMargin: units.gu(1);
9893left: parent.left;
9994 }
tests/plugins/Dash/cardcreator/12.res+8−11
@@ -12,23 +12,21 @@ AbstractButton {
1212 property size fixedArtShapeSize: Qt.size(-1, -1);
1313signal action(var actionId);
1414readonly 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 {
2116 id: artShapeLoader;
17+ height: root.fixedArtShapeSize.height;
18+ width: root.fixedArtShapeSize.width;
19+ anchors { horizontalCenter: parent.horizontalCenter; }
2220 objectName: "artShapeLoader";
2321 readonly property string cardArt: cardData && cardData["art"] || "";
2422 onCardArtChanged: { if (item) { item.image.source = cardArt; } }
2523 active: cardArt != "";
2624 asynchronous: true;
27− visible: status == Loader.Ready;
25+ visible: status === Loader.Ready;
2826 sourceComponent: Item {
2927 id: artShape;
3028 objectName: "artShape";
31− visible: image.status == Image.Ready;
29+ visible: image.status === Image.Ready;
3230 readonly property alias image: artImage;
3331 UbuntuShape {
3432 anchors.fill: parent;
@@ -49,7 +47,6 @@ Item {
4947 height: width / (root.fixedArtShapeSize.width / root.fixedArtShapeSize.height);
5048 }
5149 }
52− }
5350 }
5451readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin;
5552Label {
@@ -57,7 +54,7 @@ Label {
5754 objectName: "titleLabel";
5855 anchors { right: parent.right;
5956 left: parent.left;
60− top: artShapeHolder.bottom;
57+ top: artShapeLoader.bottom;
6158 topMargin: units.gu(1);
6259 }
6360 elide: Text.ElideRight;
@@ -101,7 +98,7 @@ CardSocialActions {
10198UbuntuShape {
10299 id: touchdown;
103100 objectName: "touchdown";
104− anchors { fill: artShapeHolder }
101+ anchors { fill: artShapeLoader }
105102 visible: root.pressed;
106103 radius: "medium";
107104 borderSource: "radius_pressed.sci"
tests/plugins/Dash/cardcreator/12.res.cardcreator+6−11
@@ -10,23 +10,19 @@ AbstractButton {
1010 enabled: true;
1111signal action(var actionId);
1212readonly 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 {
1914 id: artShapeLoader;
15+ anchors { horizontalCenter: parent.horizontalCenter; }
2016 objectName: "artShapeLoader";
2117 readonly property string cardArt: cardData && cardData["art"] || "";
2218 onCardArtChanged: { if (item) { item.image.source = cardArt; } }
2319 active: cardArt != "";
2420 asynchronous: false;
25− visible: status == Loader.Ready;
21+ visible: status === Loader.Ready;
2622 sourceComponent: Item {
2723 id: artShape;
2824 objectName: "artShape";
29− visible: image.status == Image.Ready;
25+ visible: image.status === Image.Ready;
3026 readonly property alias image: artImage;
3127 UbuntuShape {
3228 anchors.fill: parent;
@@ -47,7 +43,6 @@ Item {
4743 height: width / 0.75;
4844 }
4945 }
50− }
5146 }
5247readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin;
5348Label {
@@ -55,7 +50,7 @@ Label {
5550 objectName: "titleLabel";
5651 anchors { right: parent.right;
5752 left: parent.left;
58− top: artShapeHolder.bottom;
53+ top: artShapeLoader.bottom;
5954 topMargin: units.gu(1);
6055 }
6156 elide: Text.ElideRight;
@@ -99,7 +94,7 @@ CardSocialActions {
9994UbuntuShape {
10095 id: touchdown;
10196 objectName: "touchdown";
102− anchors { fill: artShapeHolder }
97+ anchors { fill: artShapeLoader }
10398 visible: root.pressed;
10499 radius: "medium";
105100 borderSource: "radius_pressed.sci"
tests/plugins/Dash/cardcreator/2.res+1−1
@@ -16,7 +16,7 @@ Loader {
1616 objectName: "backgroundLoader";
1717 anchors.fill: parent;
1818 asynchronous: true;
19− visible: status == Loader.Ready;
19+ visible: status === Loader.Ready;
2020 sourceComponent: UbuntuShape {
2121 objectName: "background";
2222 radius: "medium";
tests/plugins/Dash/cardcreator/2.res.cardcreator+1−1
@@ -14,7 +14,7 @@ Loader {
1414 objectName: "backgroundLoader";
1515 anchors.fill: parent;
1616 asynchronous: false;
17− visible: status == Loader.Ready;
17+ visible: status === Loader.Ready;
1818 sourceComponent: UbuntuShape {
1919 objectName: "background";
2020 radius: "medium";
tests/plugins/Dash/cardcreator/3.res+7−10
@@ -12,23 +12,21 @@ AbstractButton {
1212 property size fixedArtShapeSize: Qt.size(-1, -1);
1313signal action(var actionId);
1414readonly 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 {
2116 id: artShapeLoader;
17+ height: root.fixedArtShapeSize.height;
18+ width: root.fixedArtShapeSize.width;
19+ anchors { horizontalCenter: parent.horizontalCenter; }
2220 objectName: "artShapeLoader";
2321 readonly property string cardArt: cardData && cardData["art"] || decodeURI("IHAVE%5C%22ESCAPED%5C%22QUOTES%5C%22");
2422 onCardArtChanged: { if (item) { item.image.source = cardArt; } }
2523 active: cardArt != "";
2624 asynchronous: true;
27− visible: status == Loader.Ready;
25+ visible: status === Loader.Ready;
2826 sourceComponent: Item {
2927 id: artShape;
3028 objectName: "artShape";
31− visible: image.status == Image.Ready;
29+ visible: image.status === Image.Ready;
3230 readonly property alias image: artImage;
3331 ProportionalShape {
3432 anchors.left: parent.left;
@@ -49,7 +47,6 @@ Item {
4947 onStatusChanged: if (status === Image.Error) source = decodeURI("IHAVE%5C%22ESCAPED%5C%22QUOTES%5C%22");
5048 }
5149 }
52− }
5350 }
5451readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin;
5552Label {
@@ -57,7 +54,7 @@ Label {
5754 objectName: "titleLabel";
5855 anchors { right: parent.right;
5956 left: parent.left;
60− top: artShapeHolder.bottom;
57+ top: artShapeLoader.bottom;
6158 topMargin: units.gu(1);
6259 }
6360 elide: Text.ElideRight;
tests/plugins/Dash/cardcreator/3.res.cardcreator+6−11
@@ -10,23 +10,19 @@ AbstractButton {
1010 enabled: true;
1111signal action(var actionId);
1212readonly 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 {
19− id: artShapeLoader;
13+Loader {
14+ id: artShapeLoader;
15+ anchors { horizontalCenter: parent.horizontalCenter; }
2016 objectName: "artShapeLoader";
2117 readonly property string cardArt: cardData && cardData["art"] || "";
2218 onCardArtChanged: { if (item) { item.image.source = cardArt; } }
2319 active: cardArt != "";
2420 asynchronous: false;
25− visible: status == Loader.Ready;
21+ visible: status === Loader.Ready;
2622 sourceComponent: Item {
2723 id: artShape;
2824 objectName: "artShape";
29− visible: image.status == Image.Ready;
25+ visible: image.status === Image.Ready;
3026 readonly property alias image: artImage;
3127 ProportionalShape {
3228 anchors.left: parent.left;
@@ -46,7 +42,6 @@ Item {
4642 height: width / 0.75;
4743 }
4844 }
49− }
5045 }
5146readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin;
5247Label {
@@ -54,7 +49,7 @@ Label {
5449 objectName: "titleLabel";
5550 anchors { right: parent.right;
5651 left: parent.left;
57− top: artShapeHolder.bottom;
52+ top: artShapeLoader.bottom;
5853 topMargin: units.gu(1);
5954 }
6055 elide: Text.ElideRight;
tests/plugins/Dash/cardcreator/4.res+1−1
@@ -32,7 +32,7 @@ Loader {
3232 objectName: "mascotShapeLoader";
3333 asynchronous: true;
3434 active: mascotImage.status === Image.Ready;
35− visible: showHeader && active && status == Loader.Ready;
35+ visible: showHeader && active && status === Loader.Ready;
3636 width: units.gu(6);
3737 height: units.gu(5.625);
3838 sourceComponent: UbuntuShape { image: mascotImage }
tests/plugins/Dash/cardcreator/4.res.cardcreator+1−1
@@ -29,7 +29,7 @@ Loader {
2929 objectName: "mascotShapeLoader";
3030 asynchronous: false;
3131 active: mascotImage.status === Image.Ready;
32− visible: showHeader && active && status == Loader.Ready;
32+ visible: showHeader && active && status === Loader.Ready;
3333 width: units.gu(6);
3434 height: units.gu(5.625);
3535 sourceComponent: UbuntuShape { image: mascotImage }
tests/plugins/Dash/cardcreator/5.res+9−12
@@ -12,23 +12,21 @@ AbstractButton {
1212 property size fixedArtShapeSize: Qt.size(-1, -1);
1313signal action(var actionId);
1414readonly 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 {
2116 id: artShapeLoader;
17+ height: root.fixedArtShapeSize.height;
18+ width: root.fixedArtShapeSize.width;
19+ anchors { horizontalCenter: parent.horizontalCenter; }
2220 objectName: "artShapeLoader";
2321 readonly property string cardArt: cardData && cardData["art"] || "";
2422 onCardArtChanged: { if (item) { item.image.source = cardArt; } }
2523 active: cardArt != "";
2624 asynchronous: true;
27− visible: status == Loader.Ready;
25+ visible: status === Loader.Ready;
2826 sourceComponent: Item {
2927 id: artShape;
3028 objectName: "artShape";
31− visible: image.status == Image.Ready;
29+ visible: image.status === Image.Ready;
3230 readonly property alias image: artImage;
3331 width: root.fixedArtShapeSize.width;
3432 height: root.fixedArtShapeSize.height;
@@ -42,15 +40,14 @@ Item {
4240 height: width / (root.fixedArtShapeSize.width / root.fixedArtShapeSize.height);
4341 }
4442 }
45− }
4643 }
4744Loader {
4845 id: overlayLoader;
4946 readonly property real overlayHeight: root.fixedHeaderHeight + units.gu(2);
50− anchors.fill: artShapeHolder;
47+ anchors.fill: artShapeLoader;
5148 active: artShapeLoader.active && artShapeLoader.item && artShapeLoader.item.image.status === Image.Ready || false;
5249 asynchronous: true;
53− visible: showHeader && status == Loader.Ready;
50+ visible: showHeader && status === Loader.Ready;
5451 sourceComponent: UbuntuShapeOverlay {
5552 id: overlay;
5653 property real luminance: Style.luminance(overlayColor);
@@ -102,5 +99,5 @@ Label {
10299 text: cardData && cardData["subtitle"] || "";
103100 font.weight: Font.Light;
104101 }
105−implicitHeight: artShapeHolder.height;
102+implicitHeight: artShapeLoader.height;
106103}
tests/plugins/Dash/cardcreator/5.res.cardcreator+7−12
@@ -10,23 +10,19 @@ AbstractButton {
1010 enabled: false;
1111signal action(var actionId);
1212readonly 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 {
1914 id: artShapeLoader;
15+ anchors { horizontalCenter: parent.horizontalCenter; }
2016 objectName: "artShapeLoader";
2117 readonly property string cardArt: cardData && cardData["art"] || "";
2218 onCardArtChanged: { if (item) { item.image.source = cardArt; } }
2319 active: cardArt != "";
2420 asynchronous: false;
25− visible: status == Loader.Ready;
21+ visible: status === Loader.Ready;
2622 sourceComponent: Item {
2723 id: artShape;
2824 objectName: "artShape";
29− visible: image.status == Image.Ready;
25+ visible: image.status === Image.Ready;
3026 readonly property alias image: artImage;
3127 width: image.status !== Image.Ready ? 0 : image.width;
3228 height: image.status !== Image.Ready ? 0 : image.height;
@@ -40,15 +36,14 @@ Item {
4036 height: width / 1;
4137 }
4238 }
43− }
4439 }
4540Loader {
4641 id: overlayLoader;
4742 readonly property real overlayHeight: headerHeight + units.gu(2);
48− anchors.fill: artShapeHolder;
43+ anchors.fill: artShapeLoader;
4944 active: artShapeLoader.active && artShapeLoader.item && artShapeLoader.item.image.status === Image.Ready || false;
5045 asynchronous: false;
51− visible: showHeader && status == Loader.Ready;
46+ visible: showHeader && status === Loader.Ready;
5247 sourceComponent: UbuntuShapeOverlay {
5348 id: overlay;
5449 property real luminance: Style.luminance(overlayColor);
@@ -100,5 +95,5 @@ Label {
10095 text: cardData && cardData["subtitle"] || "";
10196 font.weight: Font.Light;
10297 }
103−implicitHeight: artShapeHolder.height;
98+implicitHeight: artShapeLoader.height;
10499}
tests/plugins/Dash/cardcreator/6.res+1−1
@@ -16,7 +16,7 @@ Loader {
1616 objectName: "backgroundLoader";
1717 anchors.fill: parent;
1818 asynchronous: true;
19− visible: status == Loader.Ready;
19+ visible: status === Loader.Ready;
2020 sourceComponent: UbuntuShape {
2121 objectName: "background";
2222 radius: "medium";
tests/plugins/Dash/cardcreator/6.res.cardcreator+1−1
@@ -14,7 +14,7 @@ Loader {
1414 objectName: "backgroundLoader";
1515 anchors.fill: parent;
1616 asynchronous: false;
17− visible: status == Loader.Ready;
17+ visible: status === Loader.Ready;
1818 sourceComponent: UbuntuShape {
1919 objectName: "background";
2020 radius: "medium";
tests/plugins/Dash/cardcreator/7.res+1−1
@@ -16,7 +16,7 @@ Loader {
1616 objectName: "backgroundLoader";
1717 anchors.fill: parent;
1818 asynchronous: true;
19− visible: status == Loader.Ready;
19+ visible: status === Loader.Ready;
2020 sourceComponent: UbuntuShape {
2121 objectName: "background";
2222 radius: "medium";
tests/plugins/Dash/cardcreator/7.res.cardcreator+1−1
@@ -14,7 +14,7 @@ Loader {
1414 objectName: "backgroundLoader";
1515 anchors.fill: parent;
1616 asynchronous: false;
17− visible: status == Loader.Ready;
17+ visible: status === Loader.Ready;
1818 sourceComponent: UbuntuShape {
1919 objectName: "background";
2020 radius: "medium";
tests/plugins/Dash/cardcreator/8.res+1−1
@@ -16,7 +16,7 @@ Loader {
1616 objectName: "backgroundLoader";
1717 anchors.fill: parent;
1818 asynchronous: true;
19− visible: status == Loader.Ready;
19+ visible: status === Loader.Ready;
2020 sourceComponent: UbuntuShape {
2121 objectName: "background";
2222 radius: "medium";
tests/plugins/Dash/cardcreator/8.res.cardcreator+1−1
@@ -14,7 +14,7 @@ Loader {
1414 objectName: "backgroundLoader";
1515 anchors.fill: parent;
1616 asynchronous: false;
17− visible: status == Loader.Ready;
17+ visible: status === Loader.Ready;
1818 sourceComponent: UbuntuShape {
1919 objectName: "background";
2020 radius: "medium";
tests/plugins/Dash/verticaljournaltest.cpp+62
@@ -72,6 +72,20 @@ public:
7272 endRemoveRows();
7373 }
7474
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+
7589private:
7690 QStringList m_list;
7791};
@@ -488,6 +502,54 @@ private Q_SLOTS:
488502 QTRY_COMPARE(vj->implicitHeight(), 0.);
489503 }
490504
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+
491553private:
492554 QQuickView *view;
493555 VerticalJournal *vj;
tests/qmltests/Dash/Previews/tst_PreviewIconActions.qml+1−1
@@ -82,7 +82,7 @@ Rectangle {
8282
8383 UT.UnityTestCase {
8484 id: testcase
85− name: "PreviewIconActionTest"
85+ name: "PreviewIconActions"
8686 when: windowShown
8787
8888 function cleanup()
tests/qmltests/Dash/tst_DashContent.qml+42−5
@@ -1,5 +1,5 @@
11/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright (C) 2013,2016 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -320,7 +320,8 @@ Item {
320320 tryCompare(navigationListView.currentItem.navigation, "navigationId", "root");
321321
322322 var headerContainer = findChild(dashContentList.currentItem, "headerContainer");
323− tryCompare(headerContainer, "contentY", headerContainer.height);
323+ tryCompare(headerContainer, "clip", false);
324+ verify(headerContainer.state !== "search");
324325 mouseClick(searchButton);
325326 tryCompare(peExtraPanel, "visible", true);
326327 waitForRendering(navigationListView);
@@ -384,7 +385,8 @@ Item {
384385 mouseClick(header0);
385386 compare(peExtraPanel.visible, false);
386387
387− tryCompare(headerContainer, "contentY", headerContainer.height);
388+ tryCompare(headerContainer, "clip", false);
389+ verify(headerContainer.state !== "search");
388390 mouseClick(searchButton);
389391 tryCompare(peExtraPanel, "visible", true);
390392 tryCompare(navigationListView.currentItem.navigation, "loaded", true);
@@ -524,7 +526,6 @@ Item {
524526 var peExtraPanel = findChild(dashContentList.currentItem, "peExtraPanel");
525527 var headerContainer = findChild(pageHeader, "headerContainer");
526528
527− // test that closing the filters popover without a search unfocuses and removes the navigation
528529 mouseClick(searchButton);
529530 tryCompare(peExtraPanel, "visible", true);
530531
@@ -536,6 +537,19 @@ Item {
536537 filtersPopover = findChild(shell, "filtersPopover")
537538 verify(filtersPopover);
538539
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
539553 mouseClick(shell, shell.width - 1, shell.height - 1);
540554
541555 tryCompare(pageHeader.extraPanel, "visible", false);
@@ -544,7 +558,8 @@ Item {
544558
545559 mouseClick(cancelButton);
546560 tryCompare(headerContainer, "showSearch", false);
547− tryCompare(headerContainer, "contentY", headerContainer.height);
561+ tryCompare(headerContainer, "clip", false);
562+ verify(headerContainer.state !== "search");
548563
549564 // test within a navigation
550565 goToSecondLevel();
@@ -690,5 +705,27 @@ Item {
690705 tryCompare(pageHeader.extraPanel, "visible", false);
691706 compare(searchTextField.focus, false);
692707 }
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+ }
693730 }
694731}
tests/qmltests/Dash/tst_DashPageHeader.qml+44−3
@@ -1,5 +1,5 @@
11/*
2− * Copyright 2013,2015 Canonical Ltd.
2+ * Copyright (C) 2013,2015,2016 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -28,6 +28,12 @@ Item {
2828 width: units.gu(110)
2929 height: units.gu(50)
3030
31+ SignalSpy {
32+ id: escapeSpy
33+ target: pageHeader
34+ signalName: "clearSearch"
35+ }
36+
3137 UT.UnityTestCase {
3238 name: "PageHeaderLabelTest"
3339 when: windowShown
@@ -37,13 +43,15 @@ Item {
3743 property var headerContainer: findChild(pageHeader, "headerContainer")
3844
3945 function doTypeString(text) {
40− tryCompare(headerContainer, "contentY", 0);
46+ tryCompare(headerContainer, "clip", false);
47+ compare(headerContainer.state, "search");
4148 typeString(text);
4249 }
4350
4451 function doResetSearch() {
4552 pageHeader.resetSearch();
46− tryCompare(headerContainer, "contentY", headerContainer.height);
53+ tryCompare(headerContainer, "clip", false);
54+ verify(headerContainer.state !== "search");
4755 }
4856
4957 function init() {
@@ -145,6 +153,7 @@ Item {
145153
146154 function cleanup() {
147155 doResetSearch();
156+ escapeSpy.clear();
148157 }
149158
150159 function test_popup_closing_data() {
@@ -201,6 +210,38 @@ Item {
201210 doResetSearch();
202211 }
203212
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+
204245 function test_search_change_shows_search() {
205246 var headerContainer = findChild(pageHeader, "headerContainer");
206247 compare(headerContainer.showSearch, false);
tests/qmltests/Panel/tst_IndicatorItemRow.qml+26−1
@@ -21,6 +21,8 @@ import "../../../qml/Panel"
2121import Ubuntu.Components 1.3
2222import Unity.Test 0.1 as UT
2323import Unity.Indicators 0.1 as Indicators
24+import AccountsService 0.1
25+import Unity.InputInfo 0.1
2426
2527IndicatorTest {
2628 id: root
@@ -194,7 +196,7 @@ IndicatorTest {
194196 verify(dataItem !== null);
195197
196198 indicatorsRow.selectItemAt(dataItem.x + dataItem.width/2);
197− compare(indicatorsRow.currentItem, dataItem);
199+ tryCompare(indicatorsRow, "currentItem", dataItem);
198200 }
199201
200202 // tests item default selection (no item at position X)
@@ -285,5 +287,28 @@ IndicatorTest {
285287 // should go back to 0
286288 tryCompare(findChild(indicatorsRow, "highlight"), "highlightCenterOffset", 0);
287289 }
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+ }
288313 }
289314}
tests/qmltests/Panel/tst_IndicatorsBar.qml+3−3
@@ -136,6 +136,7 @@ IndicatorTest {
136136 }
137137
138138 function wait_for_expansion_to_settle() {
139+ waitUntilTransitionsEnd(indicatorsBar);
139140 tryCompare(heightAnimation, "running", false);
140141 wait(UbuntuAnimation.SnapDuration); // put a little extra wait in for things to settle
141142 }
@@ -151,7 +152,6 @@ IndicatorTest {
151152 indicatorsBar.expanded = true;
152153 wait_for_expansion_to_settle();
153154
154−
155155 // mappedPosition contained within mappedRect
156156 tryCompareFunction(function() {
157157 var mappedRect = indicatorsBar.mapFromItem(dataItem, 0, 0, dataItem.width, dataItem.height);
@@ -203,8 +203,8 @@ IndicatorTest {
203203
204204 function test_visibleIndicators_data() {
205205 return [
206− { visible: [true, false, true, false, true, true] },
207− { visible: [false, false, false, false, false, false] }
206+ { visible: [true, false, true, false, true, true, false, true] },
207+ { visible: [false, false, false, false, false, false, true, false] }
208208 ];
209209 }
210210
tests/qmltests/Panel/tst_IndicatorsMenu.qml+4−3
@@ -219,8 +219,8 @@ IndicatorTest {
219219 mappedPosition.x, indicatorsMenu.openedHeight / 2,
220220 true /* beginTouch */, false /* endTouch */);
221221
222− compare(indicatorItemRow.currentItem, indicatorItem,
223− "Incorrect item activated at position " + i);
222+ tryCompare(indicatorItemRow, "currentItem", indicatorItem, undefined /*timeout, default */,
223+ "Incorrect item activated at position " + i);
224224
225225 touchFlick(indicatorItemRow,
226226 mappedPosition.x, indicatorsMenu.openedHeight / 2,
@@ -265,7 +265,8 @@ IndicatorTest {
265265 false /* beginTouch */, false /* endTouch */,
266266 units.gu(50) /* speed */, 5 /* iterations */); // more samples needed for accurate velocity
267267
268− compare(indicatorItemRow.currentItem, firstItem, "First indicator should still be the current item");
268+ tryCompare(indicatorItemRow, "currentItem", firstItem, undefined /* timeout, default */,
269+ "First indicator should still be the current item");
269270 // after waiting in the same spot with touch down, it should update to the next item.
270271 tryCompare(indicatorItemRow, "currentItem", nextItem);
271272
tests/qmltests/Stages/tst_SpreadDelegate.qml+115−12
@@ -1,5 +1,5 @@
11/*
2− * Copyright 2014-2015 Canonical Ltd.
2+ * Copyright 2014-2016 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -132,10 +132,12 @@ Rectangle {
132132 property int value: selectedIndex * 90
133133 }
134134 Button {
135+ id: matchShellButton
135136 text: "matchShellOrientation()"
136137 onClicked: { spreadDelegateLoader.item.matchShellOrientation(); }
137138 }
138139 Button {
140+ id: animateToShellButton
139141 text: "animateToShellOrientation()"
140142 onClicked: { spreadDelegateLoader.item.animateToShellOrientation(); }
141143 }
@@ -197,6 +199,77 @@ Rectangle {
197199
198200 }
199201
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+
200273 function test_swipeToClose_data() {
201274 return [
202275 {tag: "swipeToClose=true closeable=true -> appWindow moves away",
@@ -278,12 +351,12 @@ Rectangle {
278351 function test_keepsSceneTransformationWhenShellRotates(data) {
279352 loadWithGalleryApp.clicked();
280353
354+ waitUntilAppWindowIsFullyLoaded();
355+
281356 var appWindowWithShadow = findChild(spreadDelegate, "appWindowWithShadow");
282357 verify(appWindowWithShadow);
283358
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");
287360
288361 shellOrientationAngleSelector.selectedIndex = data.selectedIndex;
289362
@@ -293,14 +366,7 @@ Rectangle {
293366
294367
295368 // and scene transformation must be the identity (ie, no rotation or translation)
296− var pointInDelegateCoords = appWindowWithShadow.mapToItem(root, 0, 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);
369+ checkAppWindowTransformationMatchesRotation(appWindowWithShadow, 0);
304370 }
305371
306372 function waitForCloseAnimationToFinish() {
@@ -316,5 +382,42 @@ Rectangle {
316382 spreadDelegateLoader.item.highlightShown = true;
317383 tryCompare(highlightRect, "visible", true)
318384 }
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+ }
319422 }
320423}
tests/qmltests/tst_DisabledScreenNotice.qml+1−1
@@ -106,7 +106,7 @@ Item {
106106 orientationSelector.selectedIndex = data.selectedOrientation;
107107
108108 // 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.");
110110 tryCompareFunction(function() { return content.rotation != oldRotation; }, true, 300);
111111
112112 // Now uncheck the rotation lock
tests/qmltests/tst_OrientedShell.qml+1
@@ -1117,6 +1117,7 @@ Rectangle {
11171117
11181118 function test_attachRemoveInputDevices(data) {
11191119 loadShell("mako")
1120+ MockInputDeviceBackend.removeDevice("/indicator_kbd0");
11201121 var shell = findChild(orientedShell, "shell");
11211122 var inputMethod = findChild(shell, "inputMethod");
11221123
tests/qmltests/tst_Shell.qml+43−7
@@ -2154,8 +2154,10 @@ Rectangle {
21542154
21552155 function test_switchToStagedForcesLegacyAppClosing_data() {
21562156 return [
2157− {tag: "forceClose", replug: false },
2158− {tag: "replug", replug: true }
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)) },
2160+ {tag: "replug+tablet", replug: true, tabletMode: true, screenSize: Qt.size(units.gu(90), units.gu(65)) }
21592161 ];
21602162 }
21612163
@@ -2164,6 +2166,11 @@ Rectangle {
21642166 shell.usageScenario = "desktop"
21652167 waitForRendering(shell);
21662168
2169+ // setup some screen size
2170+ var dialogs = findChild(root, "dialogs");
2171+ verify(dialogs);
2172+ dialogs.screenSize = data.screenSize;
2173+
21672174 ApplicationManager.startApplication("camera-app")
21682175
21692176 shell.usageScenario = "phone"
@@ -2182,14 +2189,13 @@ Rectangle {
21822189 shell.usageScenario = "phone"
21832190 waitForRendering(shell);
21842191
2185− // The popup must appear now
2192+ // The popup must appear now, unless in "tablet" mode
21862193 popup = findChild(root, "modeSwitchWarningDialog");
2187− compare(popup !== null, true);
2194+ compare(popup !== null, !data.tabletMode);
21882195
2189− if (data.replug) {
2196+ if (data.replug || data.tabletMode) {
21902197 shell.usageScenario = "desktop"
21912198 waitForRendering(shell);
2192−
21932199 } else {
21942200 var forceCloseButton = findChild(popup, "forceCloseButton");
21952201 mouseClick(forceCloseButton, forceCloseButton.width / 2, forceCloseButton.height / 2);
@@ -2200,7 +2206,7 @@ Rectangle {
22002206 popup = findChild(root, "modeSwitchWarningDialog");
22012207 tryCompareFunction(function() { return popup === null}, true);
22022208
2203− if (data.replug) {
2209+ if (data.replug || data.tabletMode) {
22042210 // Libreoffice must still be running
22052211 compare(ApplicationManager.findApplication("libreoffice") !== null, true);
22062212 } else {
@@ -2506,5 +2512,35 @@ Rectangle {
25062512 verify(greeter.shown);
25072513 verify(greeter.locked);
25082514 }
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+ }
25092545 }
25102546}
tests/utils/modules/Unity/Test/TouchEventSequenceWrapper.cpp+8−3
@@ -1,5 +1,5 @@
11/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013-2016 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -26,9 +26,14 @@ TouchEventSequenceWrapper::TouchEventSequenceWrapper(QTest::QTouchEventSequence
2626
2727void TouchEventSequenceWrapper::commit(bool processEvents)
2828{
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+
2933 m_eventSequence.commit(processEvents);
30− if (m_item->window()) {
31− QQuickWindowPrivate *wp = QQuickWindowPrivate::get(m_item->window());
34+
35+ if (window) {
36+ QQuickWindowPrivate *wp = QQuickWindowPrivate::get(window);
3237 wp->flushDelayedTouchEvent();
3338 }
3439}
tests/utils/modules/Unity/Test/TouchEventSequenceWrapper.h+4−2
@@ -1,5 +1,5 @@
11/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013-2016 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -21,6 +21,8 @@
2121#include <QtTest/QTest>
2222#include <QtQml/qqml.h>
2323
24+#include <QPointer>
25+
2426class QQuickItem;
2527
2628class TouchEventSequenceWrapper : public QObject
@@ -37,7 +39,7 @@ public:
3739
3840private:
3941 QTest::QTouchEventSequence m_eventSequence;
40− QQuickItem *m_item;
42+ QPointer<QQuickItem> m_item;
4143};
4244
4345QML_DECLARE_TYPE(TouchEventSequenceWrapper)
tools/scopetool.cpp+6−2
@@ -59,7 +59,7 @@ int main(int argc, char *argv[])
5959 "Otherwise passing a path to a scope config file will open a session with\n"
6060 "only that scope (assuming that the binary implementing the scope can be\n"
6161 "found in the same directory as the config file).");
62− parser.addHelpOption();
62+ QCommandLineOption helpOption = parser.addHelpOption();
6363 parser.addPositionalArgument("scopes", "Paths to scope config files to spawn, optionally.", "[scopes...]");
6464
6565 QCommandLineOption includeSystemScopes("include-system-scopes",
@@ -70,7 +70,11 @@ int main(int argc, char *argv[])
7070 parser.addOption(includeServerScopes);
7171 parser.addOption(includeSystemScopes);
7272
73− parser.process(*application);
73+ parser.parse(application->arguments());
74+
75+ if (parser.isSet(helpOption)) {
76+ parser.showHelp();
77+ }
7478
7579 QStringList extraScopes = parser.positionalArguments();
7680