on xenial help sign in
Merge from trunk
d5b7050Michael Terry
debian/changelog+57
@@ -1,3 +1,60 @@
1+unity8 (8.01+15.04.20141202-0ubuntu1) vivid; urgency=low
2+
3+ [ Michael Terry ]
4+ * Make sure that there is no window of opportunity for swiping away
5+ greeter before the lockscreen appears.
6+ * Don't allow dragging the greeter out from the left edge if it's
7+ hidden there. (LP: #1372952)
8+
9+ [ Michał Sawicz ]
10+ * Move from Theme colour to direct UbuntuColors.orange for activity
11+ indicator and preview buttons
12+
13+ [ Albert Astals ]
14+ * Use QImageReader not to load the image into memory twice (LP:
15+ #1384374)
16+ * Don't create the whole current scope delegates, just height * 3 (LP:
17+ #1384393, #1384374)
18+ * Make CroppedImageSizer async
19+ * Use ubuntu::connectivity::NetworkingStatus instead of
20+ QNetworkConfigurationManager
21+ * Compile with Qt 5.4 (LP: #1395636)
22+
23+ [ Michael Zanetti ]
24+ * close previews and temp scopes on left edge/BFB (LP: #1391798)
25+ * make spread reversible (LP: #1368668, #1355284, #1368677)
26+
27+ [ Andrea Cimitan ]
28+ * Different drag behaviour for not closable apps in spread (LP:
29+ #1368287)
30+
31+ [ Daniel d'Andrada ]
32+ * Make UnityTestCase.touchFlick properly map event coordinates
33+
34+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 02 Dec 2014 09:26:49 +0000
35+
36+unity8 (8.01+15.04.20141125.2-0ubuntu1) vivid; urgency=low
37+
38+ [ Michael Zanetti ]
39+ * improve snapping after dragging
40+ * Make the launcher BFB rounded on one end (LP: #1382596)
41+ * disable swipeToClose while some snapping is happening (LP: #1378938)
42+ * increase threshold and avoid jumping in SpreadDelegate dragging (LP:
43+ #1352842, #1350803)
44+
45+ [ Ying-Chun Liu ]
46+ * Notifications should be on right side when wide. (LP: 1379384) (LP:
47+ #1379384)
48+
49+ [ Albert Astals ]
50+ * Fix qml warnings
51+ * Fix some qmluitests
52+
53+ [ Lars Uebernickel ]
54+ * Use Icon instead of StatusIcon
55+
56+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 25 Nov 2014 13:54:47 +0000
57+
1unity8 (8.01+15.04.20141117-0ubuntu1) vivid; urgency=low
58unity8 (8.01+15.04.20141117-0ubuntu1) vivid; urgency=low
2
59
3 [ josharenson ]
60 [ josharenson ]
debian/control+4−4
@@ -12,6 +12,7 @@ Build-Depends: cmake,
12# in libstdc++ causing us issues, we explicitly select a G++
12# in libstdc++ causing us issues, we explicitly select a G++
13# version.
13# version.
14 g++-4.9,
14 g++-4.9,
15+ libconnectivity-qt1-dev,
15 libgl1-mesa-dev[!armhf] | libgl-dev[!armhf],
16 libgl1-mesa-dev[!armhf] | libgl-dev[!armhf],
16 libgl1-mesa-dri,
17 libgl1-mesa-dri,
17 libgles2-mesa-dev[armhf],
18 libgles2-mesa-dev[armhf],
@@ -44,7 +45,7 @@ Build-Depends: cmake,
44 qtdeclarative5-private-dev (>= 5.2.1),
45 qtdeclarative5-private-dev (>= 5.2.1),
45 qtdeclarative5-qtmultimedia-plugin,
46 qtdeclarative5-qtmultimedia-plugin,
46 qtdeclarative5-ubuntu-settings-components (>= 0.4),
47 qtdeclarative5-ubuntu-settings-components (>= 0.4),
47− qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 0.1.49) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 0.1.49),
48+ qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.1.1239) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.1.1239),
48 ttf-ubuntu-font-family,
49 ttf-ubuntu-font-family,
49Standards-Version: 3.9.4
50Standards-Version: 3.9.4
50Homepage: http://launchpad.net/unity
51Homepage: http://launchpad.net/unity
@@ -58,7 +59,7 @@ X-Ubuntu-Use-Langpack: yes
58Package: indicators-client
59Package: indicators-client
59Architecture: amd64 armhf i386
60Architecture: amd64 armhf i386
60Depends: qmenumodel-qml (>= 0.2.8),
61Depends: qmenumodel-qml (>= 0.2.8),
61− qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 0.1.49) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 0.1.49),
62+ qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.1.1239) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.1.1239),
62 unity8 (= ${binary:Version}),
63 unity8 (= ${binary:Version}),
63 ${misc:Depends},
64 ${misc:Depends},
64 ${shlibs:Depends},
65 ${shlibs:Depends},
@@ -108,7 +109,7 @@ Architecture: all
108Depends: qml-module-qtquick-layouts,
109Depends: qml-module-qtquick-layouts,
109 qtdeclarative5-ubuntu-settings-components (>= 0.4),
110 qtdeclarative5-ubuntu-settings-components (>= 0.4),
110 qtdeclarative5-ubuntu-thumbnailer0.1 | ubuntu-thumbnailer-impl,
111 qtdeclarative5-ubuntu-thumbnailer0.1 | ubuntu-thumbnailer-impl,
111− qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 0.1.49) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 0.1.49),
112+ qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.1.1239) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.1.1239),
112 qtdeclarative5-unity-notifications-plugin (>= 0.1.2) | unity-notifications-impl,
113 qtdeclarative5-unity-notifications-plugin (>= 0.1.2) | unity-notifications-impl,
113 ubuntu-thumbnailer-impl-0,
114 ubuntu-thumbnailer-impl-0,
114 unity-application-impl-4,
115 unity-application-impl-4,
@@ -173,7 +174,6 @@ Depends: accountsservice-ubuntu-schemas (>= 0.0.3),
173 libhardware2,
174 libhardware2,
174 unity-schemas (>= 7.3.1+14.10.20140915),
175 unity-schemas (>= 7.3.1+14.10.20140915),
175 pay-service,
176 pay-service,
176− qml-module-ubuntu-connectivity,
177 ${misc:Depends},
177 ${misc:Depends},
178 ${shlibs:Depends},
178 ${shlibs:Depends},
179Provides: unity-launcher-impl,
179Provides: unity-launcher-impl,
plugins/Dash/CMakeLists.txt+3−2
@@ -11,7 +11,6 @@ include_directories(
11 ${CMAKE_CURRENT_SOURCE_DIR}
11 ${CMAKE_CURRENT_SOURCE_DIR}
12 ${CMAKE_CURRENT_BINARY_DIR}
12 ${CMAKE_CURRENT_BINARY_DIR}
13 ${Qt5Qml_PRIVATE_INCLUDE_DIRS}
13 ${Qt5Qml_PRIVATE_INCLUDE_DIRS}
14− ${Qt5Quick_INCLUDE_DIRS}
15 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
14 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
16 ${Qt5V8_PRIVATE_INCLUDE_DIR}
15 ${Qt5V8_PRIVATE_INCLUDE_DIR}
17)
16)
@@ -25,6 +24,8 @@ set(QMLPLUGIN_SRC
25 verticaljournal.cpp
24 verticaljournal.cpp
26 horizontaljournal.cpp
25 horizontaljournal.cpp
27 organicgrid.cpp
26 organicgrid.cpp
27+ croppedimagesizer.cpp
28+ croppedimagesizerasyncworker.cpp
28 )
29 )
29
30
30add_library(Dash-qml MODULE
31add_library(Dash-qml MODULE
@@ -36,6 +37,6 @@ target_link_libraries(Dash-qml
36 ${Qt5Quick_LIBRARIES}
37 ${Qt5Quick_LIBRARIES}
37 )
38 )
38
39
39−qt5_use_modules(Dash-qml Qml Quick)
40+qt5_use_modules(Dash-qml Qml Quick Concurrent)
40
41
41add_unity8_plugin(Dash 0.1 Dash TARGETS Dash-qml)
42add_unity8_plugin(Dash 0.1 Dash TARGETS Dash-qml)
plugins/Dash/CardAttributes.qml+1−3
@@ -17,7 +17,6 @@
17import QtQuick 2.2
17import QtQuick 2.2
18import QtQuick.Layouts 1.1
18import QtQuick.Layouts 1.1
19import Ubuntu.Components 1.1
19import Ubuntu.Components 1.1
20−import Ubuntu.Settings.Components 0.1
21
20
22GridLayout {
21GridLayout {
23 id: grid
22 id: grid
@@ -47,10 +46,9 @@ GridLayout {
47 Layout.maximumWidth: Math.max(icon.width, label.x + label.implicitWidth)
46 Layout.maximumWidth: Math.max(icon.width, label.x + label.implicitWidth)
48 Layout.fillWidth: true
47 Layout.fillWidth: true
49 height: units.gu(2)
48 height: units.gu(2)
50− StatusIcon {
49+ Icon {
51 id: icon
50 id: icon
52 height: units.gu(2)
51 height: units.gu(2)
53− sets: ["actions", "status", "apps"]
54 source: "icon" in modelData && modelData["icon"] || ""
52 source: "icon" in modelData && modelData["icon"] || ""
55 color: grid.color
53 color: grid.color
56 }
54 }
plugins/Dash/CardCreator.js+9−11
@@ -82,16 +82,16 @@ var kArtShapeHolderCode = 'Item { \n\
82 height = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.height }); \n\
82 height = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.height }); \n\
83 } \n\
83 } \n\
84 } \n\
84 } \n\
85− image: CroppedImageMinimumSourceSize { \n\
85+ CroppedImageMinimumSourceSize { \n\
86+ id: artImage; \n\
86 objectName: "artImage"; \n\
87 objectName: "artImage"; \n\
87− property bool doLoadSource: !NetworkingStatus.limitedBandwith; \n\
88− source: { if (root.visible) doLoadSource = true; return doLoadSource && cardData && cardData["art"] || ""; } \n\
88+ source: cardData && cardData["art"] || ""; \n\
89− cache: true; \n\
90 asynchronous: root.asynchronous; \n\
89 asynchronous: root.asynchronous; \n\
91 visible: false; \n\
90 visible: false; \n\
92 width: %2; \n\
91 width: %2; \n\
93 height: %3; \n\
92 height: %3; \n\
94 } \n\
93 } \n\
94+ image: artImage.image; \n\
95 } \n\
95 } \n\
96 } \n\
96 } \n\
97 }\n';
97 }\n';
@@ -181,11 +181,11 @@ var kMascotShapeLoaderCode = 'Loader { \n\
181 id: mascotShapeLoader; \n\
181 id: mascotShapeLoader; \n\
182 objectName: "mascotShapeLoader"; \n\
182 objectName: "mascotShapeLoader"; \n\
183 asynchronous: root.asynchronous; \n\
183 asynchronous: root.asynchronous; \n\
184− active: mascotImage.status === Image.Ready; \n\
184+ active: mascotImage.image.status === Image.Ready; \n\
185 visible: showHeader && active && status == Loader.Ready; \n\
185 visible: showHeader && active && status == Loader.Ready; \n\
186 width: units.gu(6); \n\
186 width: units.gu(6); \n\
187 height: units.gu(5.625); \n\
187 height: units.gu(5.625); \n\
188− sourceComponent: UbuntuShape { image: mascotImage } \n\
188+ sourceComponent: UbuntuShape { image: mascotImage.image } \n\
189 anchors { %1 } \n\
189 anchors { %1 } \n\
190 }\n';
190 }\n';
191
191
@@ -195,8 +195,7 @@ var kMascotImageCode = 'CroppedImageMinimumSourceSize { \n\
195 id: mascotImage; \n\
195 id: mascotImage; \n\
196 objectName: "mascotImage"; \n\
196 objectName: "mascotImage"; \n\
197 anchors { %1 } \n\
197 anchors { %1 } \n\
198− property bool doLoadSource: !NetworkingStatus.limitedBandwith; \n\
199− source: { if (root.visible) doLoadSource = true; return doLoadSource && cardData && cardData["mascot"] || ""; } \n\
198+ source: cardData && cardData["mascot"] || ""; \n\
200 width: units.gu(6); \n\
199 width: units.gu(6); \n\
201 height: units.gu(5.625); \n\
200 height: units.gu(5.625); \n\
202 horizontalAlignment: Image.AlignHCenter; \n\
201 horizontalAlignment: Image.AlignHCenter; \n\
@@ -225,7 +224,7 @@ var kTitleLabelCode = 'Label { \n\
225
224
226// %1 is used as extra anchors of emblemIcon
225// %1 is used as extra anchors of emblemIcon
227// %2 is used as color of emblemIcon
226// %2 is used as color of emblemIcon
228−var kEmblemIconCode = 'StatusIcon { \n\
227+var kEmblemIconCode = 'Icon { \n\
229 id: emblemIcon; \n\
228 id: emblemIcon; \n\
230 objectName: "emblemIcon"; \n\
229 objectName: "emblemIcon"; \n\
231 anchors { \n\
230 anchors { \n\
@@ -435,7 +434,7 @@ function cardString(template, components) {
435 mascotShapeCode = kMascotShapeLoaderCode.arg(mascotAnchors);
434 mascotShapeCode = kMascotShapeLoaderCode.arg(mascotAnchors);
436 }
435 }
437
436
438− var mascotImageVisible = useMascotShape ? 'false' : 'showHeader && resized';
437+ var mascotImageVisible = useMascotShape ? 'false' : 'showHeader';
439 mascotCode = kMascotImageCode.arg(mascotAnchors).arg(mascotImageVisible);
438 mascotCode = kMascotImageCode.arg(mascotAnchors).arg(mascotImageVisible);
440 }
439 }
441
440
@@ -655,7 +654,6 @@ function createCardComponent(parent, template, components) {
655 var imports = 'import QtQuick 2.2; \n\
654 var imports = 'import QtQuick 2.2; \n\
656 import Ubuntu.Components 1.1; \n\
655 import Ubuntu.Components 1.1; \n\
657 import Ubuntu.Settings.Components 0.1; \n\
656 import Ubuntu.Settings.Components 0.1; \n\
658− import Ubuntu.Connectivity 1.0; \n\
659 import Dash 0.1;\n\
657 import Dash 0.1;\n\
660 import Utils 0.1;\n';
658 import Utils 0.1;\n';
661 var card = cardString(template, components);
659 var card = cardString(template, components);
plugins/Dash/CroppedImageMinimumSourceSize.qml+24−18
@@ -15,24 +15,30 @@
15 */
15 */
16
16
17import QtQuick 2.3
17import QtQuick 2.3
18+import Dash 0.1
18
19
19−Image {
20+Item {
20− property bool resized: false
21+ id: root
21− property bool resizing: false
22+
23+ property string source
24+ property alias image: innerImage
25+ property alias asynchronous: innerImage.asynchronous
26+ property alias verticalAlignment: innerImage.verticalAlignment
27+ property alias horizontalAlignment: innerImage.horizontalAlignment
28+ property alias fillMode: innerImage.fillMode
29+
30+ CroppedImageSizer {
31+ id: sizer
32+ source: root.source
33+ width: root.width
34+ height: root.height
35+ }
36+
37+ Image {
38+ id: innerImage
39+ anchors.fill: parent
22− fillMode: Image.PreserveAspectCrop
40+ fillMode: Image.PreserveAspectCrop
23− visible: resized
41+ sourceSize: sizer.sourceSize.width == 0 && sizer.sourceSize.height == 0 ? undefined : sizer.sourceSize
24− onSourceSizeChanged: {
25− if (!resized && !resizing) {
26− resizing = true;
27− var ar = width / height;
28− var ssar = sourceSize.width / sourceSize.height;
42+ source: sizer.sourceSize.width == -1 && sizer.sourceSize.height == -1 ? "" : root.source
29− if (ar > ssar) {
30− sourceSize = Qt.size(width, 0);
31− } else {
32− sourceSize = Qt.size(0, height);
33− }
34− resizing = false;
35− resized = true;
36− }
37 }
43 }
38}
44}
plugins/Dash/ScopeStyle.qml+1−1
@@ -70,7 +70,7 @@ QtObject {
70 readonly property url navigationBackground: "navigation-background" in d.headerStyle ? d.headerStyle["navigation-background"] : "color:///#f5f5f5"
70 readonly property url navigationBackground: "navigation-background" in d.headerStyle ? d.headerStyle["navigation-background"] : "color:///#f5f5f5"
71
71
72 /// Color of the primary preview button
72 /// Color of the primary preview button
73− readonly property color previewButtonColor: "preview-button-color" in style ? style["preview-button-color"] : Theme.palette.selected.foreground
73+ readonly property color previewButtonColor: "preview-button-color" in style ? style["preview-button-color"] : UbuntuColors.orange
74
74
75 //! @cond
75 //! @cond
76 property var d: QtObject {
76 property var d: QtObject {
plugins/Dash/abstractdashview.h+4
@@ -104,7 +104,11 @@ private:
104 virtual void doRelayout() = 0;
104 virtual void doRelayout() = 0;
105 virtual void updateItemCulling(qreal visibleFromY, qreal visibleToY) = 0;
105 virtual void updateItemCulling(qreal visibleFromY, qreal visibleToY) = 0;
106 virtual void calculateImplicitHeight() = 0;
106 virtual void calculateImplicitHeight() = 0;
107+#if (QT_VERSION < QT_VERSION_CHECK(5, 4, 0))
107 virtual void processModelRemoves(const QVector<QQmlChangeSet::Remove> &removes) = 0;
108 virtual void processModelRemoves(const QVector<QQmlChangeSet::Remove> &removes) = 0;
109+#else
110+ virtual void processModelRemoves(const QVector<QQmlChangeSet::Change> &removes) = 0;
111+#endif
108
112
109 QQmlDelegateModel *m_delegateModel;
113 QQmlDelegateModel *m_delegateModel;
110
114
plugins/Dash/croppedimagesizer.cppnew file+136
@@ -0,0 +1,136 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "croppedimagesizer.h"
18+
19+#include "croppedimagesizerasyncworker.h"
20+
21+#include <QNetworkAccessManager>
22+#include <QNetworkRequest>
23+#include <QQmlEngine>
24+#include <QQuickItem>
25+
26+CroppedImageSizer::CroppedImageSizer()
27+ : m_width(0),
28+ m_height(0),
29+ m_sourceSize(QSize(-1, -1)),
30+ m_worker(nullptr)
31+{
32+ connect(this, &CroppedImageSizer::inputParamsChanged, this, &CroppedImageSizer::calculateSourceSize);
33+ connect(this, &CroppedImageSizer::sourceChanged, this, &CroppedImageSizer::requestImage);
34+}
35+
36+CroppedImageSizer::~CroppedImageSizer()
37+{
38+ if (m_worker) {
39+ m_worker->abort();
40+ }
41+}
42+
43+QUrl CroppedImageSizer::source() const
44+{
45+ return m_source;
46+}
47+
48+void CroppedImageSizer::setSource(const QUrl &source)
49+{
50+ if (source != m_source) {
51+ m_source = source;
52+ Q_EMIT sourceChanged();
53+ }
54+}
55+
56+qreal CroppedImageSizer::width() const
57+{
58+ return m_width;
59+}
60+
61+void CroppedImageSizer::setWidth(qreal width)
62+{
63+ if (width != m_width) {
64+ m_width = width;
65+ Q_EMIT inputParamsChanged();
66+ }
67+}
68+
69+qreal CroppedImageSizer::height() const
70+{
71+ return m_height;
72+}
73+
74+void CroppedImageSizer::setHeight(qreal height)
75+{
76+ if (height != m_height) {
77+ m_height = height;
78+ Q_EMIT inputParamsChanged();
79+ }
80+}
81+
82+QSize CroppedImageSizer::sourceSize() const
83+{
84+ return m_sourceSize;
85+}
86+
87+void CroppedImageSizer::setSourceSize(const QSize &sourceSize)
88+{
89+ if (sourceSize != m_sourceSize) {
90+ m_sourceSize = sourceSize;
91+ Q_EMIT sourceSizeChanged();
92+ }
93+}
94+
95+void CroppedImageSizer::setImageSize(const QSize &imageSize)
96+{
97+ m_imageSize = imageSize;
98+ m_worker = nullptr;
99+ calculateSourceSize();
100+}
101+
102+void CroppedImageSizer::requestImage()
103+{
104+ if (m_worker) {
105+ m_worker->abort();
106+ m_worker = nullptr;
107+ }
108+
109+ if (m_source.isValid() && qmlEngine(this) && qmlEngine(this)->networkAccessManager()) {
110+ QNetworkRequest request(m_source);
111+ QNetworkReply *reply = qmlEngine(this)->networkAccessManager()->get(request);
112+ m_worker = new CroppedImageSizerAsyncWorker(this, reply);
113+ } else {
114+ setSourceSize(QSize(-1, -1));
115+ }
116+}
117+
118+void CroppedImageSizer::calculateSourceSize()
119+{
120+ if (m_source.isValid() && m_width > 0 && m_height > 0 && !m_worker) {
121+ if (!m_imageSize.isEmpty()) {
122+ const qreal ar = m_width / m_height;
123+ const qreal ssar = m_imageSize.width() / (qreal)m_imageSize.height();
124+ if (ar > ssar) {
125+ setSourceSize(QSize(m_width, 0));
126+ } else {
127+ setSourceSize(QSize(0, m_height));
128+ }
129+ } else {
130+ qWarning() << "Invalid size for " << m_source << m_imageSize;
131+ setSourceSize(QSize(0, 0));
132+ }
133+ } else {
134+ setSourceSize(QSize(-1, -1));
135+ }
136+}
plugins/Dash/croppedimagesizer.hnew file+72
@@ -0,0 +1,72 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef CROPPEDIMAGESIZER_H
18+#define CROPPEDIMAGESIZER_H
19+
20+#include <QImageReader>
21+#include <QObject>
22+#include <QSize>
23+#include <QUrl>
24+
25+class CroppedImageSizerAsyncWorker;
26+
27+class CroppedImageSizer : public QObject
28+{
29+ Q_OBJECT
30+
31+ Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
32+ Q_PROPERTY(qreal width READ width WRITE setWidth NOTIFY inputParamsChanged)
33+ Q_PROPERTY(qreal height READ height WRITE setHeight NOTIFY inputParamsChanged)
34+ Q_PROPERTY(QSize sourceSize READ sourceSize NOTIFY sourceSizeChanged)
35+
36+public:
37+ CroppedImageSizer();
38+ ~CroppedImageSizer();
39+
40+ QUrl source() const;
41+ void setSource(const QUrl &source);
42+
43+ qreal width() const;
44+ void setWidth(qreal width);
45+
46+ qreal height() const;
47+ void setHeight(qreal height);
48+
49+ QSize sourceSize() const;
50+ void setSourceSize(const QSize &sourceSize);
51+
52+ Q_INVOKABLE void setImageSize(const QSize &imageSize);
53+
54+Q_SIGNALS:
55+ void inputParamsChanged();
56+ void sourceChanged();
57+ void sourceSizeChanged();
58+
59+private Q_SLOT:
60+ void calculateSourceSize();
61+ void requestImage();
62+
63+private:
64+ QUrl m_source;
65+ qreal m_width;
66+ qreal m_height;
67+ QSize m_sourceSize;
68+ QSize m_imageSize;
69+ QPointer<CroppedImageSizerAsyncWorker> m_worker;
70+};
71+
72+#endif
plugins/Dash/croppedimagesizerasyncworker.cppnew file+74
@@ -0,0 +1,74 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "croppedimagesizerasyncworker.h"
18+
19+#include "croppedimagesizer.h"
20+
21+#include <QNetworkReply>
22+#include <QtConcurrentRun>
23+
24+CroppedImageSizerAsyncWorker::CroppedImageSizerAsyncWorker(CroppedImageSizer *sizer, QNetworkReply *reply)
25+ : m_sizer(sizer),
26+ m_reply(reply),
27+ m_ignoreAbort(false)
28+{
29+ connect(m_reply, &QNetworkReply::finished, this, &CroppedImageSizerAsyncWorker::requestFinished);
30+}
31+
32+void CroppedImageSizerAsyncWorker::abort()
33+{
34+ // This runs on main thread
35+ QMutexLocker locker(&m_mutex);
36+ m_sizer = nullptr;
37+ // If we already started the future run we can't abort the reply
38+ // since we don't know at which stage the future is, just let it finish
39+ if (!m_ignoreAbort) {
40+ QMetaObject::invokeMethod(m_reply, "abort", Qt::QueuedConnection);
41+ }
42+}
43+
44+void CroppedImageSizerAsyncWorker::requestFinished()
45+{
46+ // This runs on main thread
47+ QMutexLocker locker(&m_mutex);
48+ if (m_sizer) {
49+ m_ignoreAbort = true;
50+ QtConcurrent::run(processRequestFinished, this);
51+ } else {
52+ // We were aborted delete ourselves
53+ m_reply->deleteLater();
54+ deleteLater();
55+ }
56+}
57+
58+void CroppedImageSizerAsyncWorker::processRequestFinished(CroppedImageSizerAsyncWorker *worker)
59+{
60+ // This runs on non main thread
61+ // m_reply has finished at this point and is protected against change by m_ignoreAbort
62+ QImageReader reader(worker->m_reply);
63+ const QSize imageSize = reader.size();
64+
65+ worker->m_mutex.lock();
66+ if (worker->m_sizer) {
67+ QMetaObject::invokeMethod(worker->m_sizer, "setImageSize", Qt::QueuedConnection, Q_ARG(QSize, imageSize));
68+ }
69+ worker->m_mutex.unlock();
70+
71+ // All work is done, delete ourselves
72+ worker->m_reply->deleteLater();
73+ worker->deleteLater();
74+}
plugins/Dash/croppedimagesizerasyncworker.hnew file+47
@@ -0,0 +1,47 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef CROPPEDIMAGESIZERASYNCWORKER_H
18+#define CROPPEDIMAGESIZERASYNCWORKER_H
19+
20+#include <QMutex>
21+#include <QObject>
22+
23+class CroppedImageSizer;
24+
25+class QNetworkReply;
26+
27+class CroppedImageSizerAsyncWorker : public QObject
28+{
29+ Q_OBJECT
30+public:
31+ CroppedImageSizerAsyncWorker(CroppedImageSizer *sizer, QNetworkReply *reply);
32+
33+ void abort();
34+
35+private Q_SLOTS:
36+ void requestFinished();
37+
38+private:
39+ static void processRequestFinished(CroppedImageSizerAsyncWorker *worker);
40+
41+ QMutex m_mutex;
42+ CroppedImageSizer *m_sizer;
43+ QNetworkReply *m_reply;
44+ bool m_ignoreAbort;
45+};
46+
47+#endif
plugins/Dash/horizontaljournal.cpp+5−1
@@ -184,9 +184,13 @@ void HorizontalJournal::calculateImplicitHeight()
184 }
184 }
185}
185}
186
186
187+#if (QT_VERSION < QT_VERSION_CHECK(5, 4, 0))
187void HorizontalJournal::processModelRemoves(const QVector<QQmlChangeSet::Remove> &removes)
188void HorizontalJournal::processModelRemoves(const QVector<QQmlChangeSet::Remove> &removes)
189+#else
190+void HorizontalJournal::processModelRemoves(const QVector<QQmlChangeSet::Change> &removes)
191+#endif
188{
192{
189− Q_FOREACH(const QQmlChangeSet::Remove &remove, removes) {
193+ Q_FOREACH(const QQmlChangeSet::Change &remove, removes) {
190 for (int i = remove.count - 1; i >= 0; --i) {
194 for (int i = remove.count - 1; i >= 0; --i) {
191 const int indexToRemove = remove.index + i;
195 const int indexToRemove = remove.index + i;
192 // We only support removing from the end so
196 // We only support removing from the end so
plugins/Dash/horizontaljournal.h+4
@@ -52,7 +52,11 @@ private:
52 void calculateImplicitHeight() override;
52 void calculateImplicitHeight() override;
53 void doRelayout() override;
53 void doRelayout() override;
54 void updateItemCulling(qreal visibleFromY, qreal visibleToY) override;
54 void updateItemCulling(qreal visibleFromY, qreal visibleToY) override;
55+#if (QT_VERSION < QT_VERSION_CHECK(5, 4, 0))
55 void processModelRemoves(const QVector<QQmlChangeSet::Remove> &removes) override;
56 void processModelRemoves(const QVector<QQmlChangeSet::Remove> &removes) override;
57+#else
58+ void processModelRemoves(const QVector<QQmlChangeSet::Change> &removes) override;
59+#endif
56
60
57 int m_firstVisibleIndex;
61 int m_firstVisibleIndex;
58 QList<QQuickItem*> m_visibleItems;
62 QList<QQuickItem*> m_visibleItems;
plugins/Dash/listviewwithpageheader.cpp+2−2
@@ -936,7 +936,7 @@ void ListViewWithPageHeader::onModelUpdated(const QQmlChangeSet &changeSet, bool
936// qDebug() << "ListViewWithPageHeader::onModelUpdated" << changeSet << reset;
936// qDebug() << "ListViewWithPageHeader::onModelUpdated" << changeSet << reset;
937 const auto oldFirstVisibleIndex = m_firstVisibleIndex;
937 const auto oldFirstVisibleIndex = m_firstVisibleIndex;
938
938
939− Q_FOREACH(const QQmlChangeSet::Remove &remove, changeSet.removes()) {
939+ Q_FOREACH(const QQmlChangeSet::Change &remove, changeSet.removes()) {
940// qDebug() << "ListViewWithPageHeader::onModelUpdated Remove" << remove.index << remove.count;
940// qDebug() << "ListViewWithPageHeader::onModelUpdated Remove" << remove.index << remove.count;
941 if (remove.index + remove.count > m_firstVisibleIndex && remove.index < m_firstVisibleIndex + m_visibleItems.count()) {
941 if (remove.index + remove.count > m_firstVisibleIndex && remove.index < m_firstVisibleIndex + m_visibleItems.count()) {
942 const qreal oldFirstValidIndexPos = m_visibleItems.first()->y();
942 const qreal oldFirstValidIndexPos = m_visibleItems.first()->y();
@@ -990,7 +990,7 @@ void ListViewWithPageHeader::onModelUpdated(const QQmlChangeSet &changeSet, bool
990 }
990 }
991 }
991 }
992
992
993− Q_FOREACH(const QQmlChangeSet::Insert &insert, changeSet.inserts()) {
993+ Q_FOREACH(const QQmlChangeSet::Change &insert, changeSet.inserts()) {
994// qDebug() << "ListViewWithPageHeader::onModelUpdated Insert" << insert.index << insert.count;
994// qDebug() << "ListViewWithPageHeader::onModelUpdated Insert" << insert.index << insert.count;
995 const bool insertingInValidIndexes = insert.index > m_firstVisibleIndex && insert.index < m_firstVisibleIndex + m_visibleItems.count();
995 const bool insertingInValidIndexes = insert.index > m_firstVisibleIndex && insert.index < m_firstVisibleIndex + m_visibleItems.count();
996 const bool firstItemWithViewOnTop = insert.index == 0 && m_firstVisibleIndex == 0 && m_visibleItems.first()->y() + m_clipItem->y() > contentY();
996 const bool firstItemWithViewOnTop = insert.index == 0 && m_firstVisibleIndex == 0 && m_visibleItems.first()->y() + m_clipItem->y() > contentY();
plugins/Dash/organicgrid.cpp+5−1
@@ -237,9 +237,13 @@ void OrganicGrid::calculateImplicitHeight()
237 }
237 }
238}
238}
239
239
240+#if (QT_VERSION < QT_VERSION_CHECK(5, 4, 0))
240void OrganicGrid::processModelRemoves(const QVector<QQmlChangeSet::Remove> &removes)
241void OrganicGrid::processModelRemoves(const QVector<QQmlChangeSet::Remove> &removes)
242+#else
243+void OrganicGrid::processModelRemoves(const QVector<QQmlChangeSet::Change> &removes)
244+#endif
241{
245{
242− Q_FOREACH(const QQmlChangeSet::Remove &remove, removes) {
246+ Q_FOREACH(const QQmlChangeSet::Change &remove, removes) {
243 for (int i = remove.count - 1; i >= 0; --i) {
247 for (int i = remove.count - 1; i >= 0; --i) {
244 const int indexToRemove = remove.index + i;
248 const int indexToRemove = remove.index + i;
245 // We only support removing from the end
249 // We only support removing from the end
plugins/Dash/organicgrid.h+4
@@ -79,7 +79,11 @@ private:
79 void doRelayout() override;
79 void doRelayout() override;
80 void updateItemCulling(qreal visibleFromY, qreal visibleToY) override;
80 void updateItemCulling(qreal visibleFromY, qreal visibleToY) override;
81 void calculateImplicitHeight() override;
81 void calculateImplicitHeight() override;
82+#if (QT_VERSION < QT_VERSION_CHECK(5, 4, 0))
82 void processModelRemoves(const QVector<QQmlChangeSet::Remove> &removes) override;
83 void processModelRemoves(const QVector<QQmlChangeSet::Remove> &removes) override;
84+#else
85+ void processModelRemoves(const QVector<QQmlChangeSet::Change> &removes) override;
86+#endif
83
87
84 QSizeF m_smallDelegateSize;
88 QSizeF m_smallDelegateSize;
85 QSizeF m_bigDelegateSize;
89 QSizeF m_bigDelegateSize;
plugins/Dash/plugin.cpp+2
@@ -17,6 +17,7 @@
17
17
18#include "plugin.h"
18#include "plugin.h"
19
19
20+#include "croppedimagesizer.h"
20#include "horizontaljournal.h"
21#include "horizontaljournal.h"
21#include "listviewwithpageheader.h"
22#include "listviewwithpageheader.h"
22#include "organicgrid.h"
23#include "organicgrid.h"
@@ -32,4 +33,5 @@ void DashPlugin::registerTypes(const char *uri)
32 qmlRegisterType<ListViewWithPageHeader>(uri, 0, 1, "ListViewWithPageHeader");
33 qmlRegisterType<ListViewWithPageHeader>(uri, 0, 1, "ListViewWithPageHeader");
33 qmlRegisterType<OrganicGrid>(uri, 0, 1, "OrganicGrid");
34 qmlRegisterType<OrganicGrid>(uri, 0, 1, "OrganicGrid");
34 qmlRegisterType<VerticalJournal>(uri, 0, 1, "VerticalJournal");
35 qmlRegisterType<VerticalJournal>(uri, 0, 1, "VerticalJournal");
36+ qmlRegisterType<CroppedImageSizer>(uri, 0, 1, "CroppedImageSizer");
35}
37}
plugins/Dash/verticaljournal.cpp+5−1
@@ -239,9 +239,13 @@ void VerticalJournal::updateItemCulling(qreal visibleFromY, qreal visibleToY)
239 }
239 }
240}
240}
241
241
242+#if (QT_VERSION < QT_VERSION_CHECK(5, 4, 0))
242void VerticalJournal::processModelRemoves(const QVector<QQmlChangeSet::Remove> &removes)
243void VerticalJournal::processModelRemoves(const QVector<QQmlChangeSet::Remove> &removes)
244+#else
245+void VerticalJournal::processModelRemoves(const QVector<QQmlChangeSet::Change> &removes)
246+#endif
243{
247{
244− Q_FOREACH(const QQmlChangeSet::Remove &remove, removes) {
248+ Q_FOREACH(const QQmlChangeSet::Change &remove, removes) {
245 for (int i = remove.count - 1; i >= 0; --i) {
249 for (int i = remove.count - 1; i >= 0; --i) {
246 const int indexToRemove = remove.index + i;
250 const int indexToRemove = remove.index + i;
247 // Since we only support removing from the end, indexToRemove
251 // Since we only support removing from the end, indexToRemove
plugins/Dash/verticaljournal.h+4
@@ -90,7 +90,11 @@ private:
90 void calculateImplicitHeight() override;
90 void calculateImplicitHeight() override;
91 void doRelayout() override;
91 void doRelayout() override;
92 void updateItemCulling(qreal visibleFromY, qreal visibleToY) override;
92 void updateItemCulling(qreal visibleFromY, qreal visibleToY) override;
93+#if (QT_VERSION < QT_VERSION_CHECK(5, 4, 0))
93 void processModelRemoves(const QVector<QQmlChangeSet::Remove> &removes) override;
94 void processModelRemoves(const QVector<QQmlChangeSet::Remove> &removes) override;
95+#else
96+ void processModelRemoves(const QVector<QQmlChangeSet::Change> &removes) override;
97+#endif
94
98
95 QVector<QList<ViewItem>> m_columnVisibleItems;
99 QVector<QList<ViewItem>> m_columnVisibleItems;
96 QHash<int, int> m_indexColumnMap;
100 QHash<int, int> m_indexColumnMap;
qml/Dash/CardCarousel.qml+1
@@ -52,6 +52,7 @@ DashRenderer {
52 function pressAndHold() { cardCarousel.pressAndHold(index, model.result, model) }
52 function pressAndHold() { cardCarousel.pressAndHold(index, model.result, model) }
53
53
54 sourceComponent: cardTool.cardComponent
54 sourceComponent: cardTool.cardComponent
55+ asynchronous: true
55 onLoaded: {
56 onLoaded: {
56 item.fixedHeaderHeight = Qt.binding(function() { return carousel.headerHeight; });
57 item.fixedHeaderHeight = Qt.binding(function() { return carousel.headerHeight; });
57 item.height = Qt.binding(function() { return cardTool.cardHeight; });
58 item.height = Qt.binding(function() { return cardTool.cardHeight; });
qml/Dash/CardGrid.qml+1−1
@@ -55,11 +55,11 @@ DashRenderer {
55 delegate: Item {
55 delegate: Item {
56 width: grid.cellWidth
56 width: grid.cellWidth
57 height: grid.cellHeight
57 height: grid.cellHeight
58− visible: y + height >= root.visibleRangeBegin && y <= root.visibleRangeEnd
59 Loader {
58 Loader {
60 id: loader
59 id: loader
61 sourceComponent: cardTool.cardComponent
60 sourceComponent: cardTool.cardComponent
62 anchors.horizontalCenter: parent.horizontalCenter
61 anchors.horizontalCenter: parent.horizontalCenter
62+ asynchronous: true
63 onLoaded: {
63 onLoaded: {
64 item.objectName = "delegate" + index;
64 item.objectName = "delegate" + index;
65 item.width = Qt.binding(function() { return cardTool.cardWidth; });
65 item.width = Qt.binding(function() { return cardTool.cardWidth; });
qml/Dash/CardHorizontalList.qml+1
@@ -42,6 +42,7 @@ DashRenderer {
42 sourceComponent: cardTool.cardComponent
42 sourceComponent: cardTool.cardComponent
43 anchors { top: parent.top; bottom: parent.bottom }
43 anchors { top: parent.top; bottom: parent.bottom }
44 width: cardTool.cardWidth
44 width: cardTool.cardWidth
45+ asynchronous: true
45 onLoaded: {
46 onLoaded: {
46 item.objectName = "delegate" + index;
47 item.objectName = "delegate" + index;
47 item.fixedArtShapeSize = Qt.binding(function() { return cardTool.artShapeSize; });
48 item.fixedArtShapeSize = Qt.binding(function() { return cardTool.artShapeSize; });
qml/Dash/CardTool.qml+1−1
@@ -178,7 +178,7 @@ Item {
178 property var model: []
178 property var model: []
179 property bool hasAttributes: {
179 property bool hasAttributes: {
180 var attributes = components["attributes"];
180 var attributes = components["attributes"];
181− var hasAttributesFlag = (attributes != undefined) && attributes["field"];
181+ var hasAttributesFlag = (attributes != undefined) && (attributes["field"] != undefined);
182
182
183 if (hasAttributesFlag) {
183 if (hasAttributesFlag) {
184 if (attributes["max-count"]) {
184 if (attributes["max-count"]) {
qml/Dash/CardVerticalJournal.qml−1
@@ -66,7 +66,6 @@ DashRenderer {
66 id: loader
66 id: loader
67 sourceComponent: cardTool.cardComponent
67 sourceComponent: cardTool.cardComponent
68 width: cardTool.cardWidth
68 width: cardTool.cardWidth
69− visible: y + height >= root.visibleRangeBegin && y <= root.visibleRangeEnd
70 onLoaded: {
69 onLoaded: {
71 item.objectName = "delegate" + index;
70 item.objectName = "delegate" + index;
72 item.fixedArtShapeSize = Qt.binding(function() { return cardTool.artShapeSize; });
71 item.fixedArtShapeSize = Qt.binding(function() { return cardTool.artShapeSize; });
qml/Dash/Dash.qml+11−3
@@ -31,9 +31,10 @@ Showable {
31 DashCommunicatorService {
31 DashCommunicatorService {
32 objectName: "dashCommunicatorService"
32 objectName: "dashCommunicatorService"
33 onSetCurrentScopeRequested: {
33 onSetCurrentScopeRequested: {
34− if (!isSwipe || !window.active || overviewController.progress != 0) {
34+ if (!isSwipe || !window.active || overviewController.progress != 0 || scopeItem.scope || dashContent.subPageShown) {
35 if (overviewController.progress != 0 && window.active) animate = false;
35 if (overviewController.progress != 0 && window.active) animate = false;
36 dashContent.setCurrentScopeAtIndex(index, animate, isSwipe)
36 dashContent.setCurrentScopeAtIndex(index, animate, isSwipe)
37+ // Close dash overview and nested temp scopes in it
37 if (overviewController.progress != 0) {
38 if (overviewController.progress != 0) {
38 if (window.active) {
39 if (window.active) {
39 dashContentCache.scheduleUpdate();
40 dashContentCache.scheduleUpdate();
@@ -42,6 +43,14 @@ Showable {
42 overviewController.progress = 0;
43 overviewController.progress = 0;
43 scopesOverview.closeTempScope();
44 scopesOverview.closeTempScope();
44 }
45 }
46+ // Close normal temp scopes (e.g. App Store)
47+ if (scopeItem.scope) {
48+ scopeItem.backClicked();
49+ }
50+ // Close previews
51+ if (dashContent.subPageShown) {
52+ dashContent.closePreview();
53+ }
45 }
54 }
46 }
55 }
47 }
56 }
@@ -309,7 +318,7 @@ Showable {
309 id: orange
318 id: orange
310 anchors { top: parent.top; bottom: parent.bottom }
319 anchors { top: parent.top; bottom: parent.bottom }
311 width: parent.width / 4
320 width: parent.width / 4
312− color: Theme.palette.selected.foreground
321+ color: UbuntuColors.orange
313
322
314 SequentialAnimation {
323 SequentialAnimation {
315 running: indicator.visible
324 running: indicator.visible
@@ -389,5 +398,4 @@ Showable {
389 }
398 }
390 }
399 }
391 }
400 }
392−
393}
401}
qml/Dash/DashRenderer.qml−4
@@ -27,10 +27,6 @@ Item {
27
27
28 property int displayMarginEnd: 0
28 property int displayMarginEnd: 0
29
29
30− property int visibleRangeBegin: 0
31−
32− property int visibleRangeEnd: 0
33−
34 property real originY: 0
30 property real originY: 0
35
31
36 // The model to renderer
32 // The model to renderer
qml/Dash/GenericScopeView.qml+22−25
@@ -379,32 +379,29 @@ FocusScope {
379 }
379 }
380 }
380 }
381
381
382− if (item && item.hasOwnProperty("visibleRangeBegin")) {
383− item.visibleRangeBegin = Math.max(-baseItem.y, 0)
384− item.visibleRangeEnd = item.visibleRangeBegin + Math.min(categoryView.height, rendererLoader.height)
385− }
386−
387 if (item && item.hasOwnProperty("displayMarginBeginning")) {
382 if (item && item.hasOwnProperty("displayMarginBeginning")) {
388− // TODO do we need item.originY here, test 1300302 once we have a silo
383+ // A item view is considered visible from
389− // and we can run it on the phone
384+ // -displayMarginBeginning
385+ // to
386+ // height + item.displayMarginEnd
387+ // We adjust displayMarginBeginning and displayMarginEnd so
388+ // * In non visible scopes only the viewport is considered visible
389+ // that way when you switch to it the visible items are there
390+ // * For visible scopes we increase the visible range by categoryView.height * 1.5
391+ // in both directions to make scrolling nicer by mantaining a higher number of
392+ // cached items
393+ // TODO Improvements
394+ // - For non visible scopes we should always have a visible range of 0 and
395+ // make sure the items in the viewport are created with the cache buffer feature
396+ // - For visible scopes we should always the have a visible range be exactly the
397+ // viewport and make sure the rest of items are created with the cache buffer feature
398+ // To be able to implement that feature VerticalJournal/AbstractDashView needs to
399+ // make the cache buffer value setable externally
390− if (scopeView.isCurrent) {
400+ var extraMargins = scopeView.isCurrent ? categoryView.height * 1.5 : 0;
391− // 1073741823 is s^30 -1. A quite big number so that you have "infinite" display margin, but not so
401+
392− // big so that if you add if with itself you're outside the 2^31 int range
393− item.displayMarginBeginning = 1073741823;
394− item.displayMarginEnd = 1073741823;
395− } else if (baseItem.y + baseItem.height <= 0) {
396− // Not visible (item at top of the list viewport)
397− item.displayMarginBeginning = -baseItem.height;
398− item.displayMarginEnd = 0;
399− } else if (baseItem.y >= categoryView.height) {
400− // Not visible (item at bottom of the list viewport)
401− item.displayMarginBeginning = 0;
402− item.displayMarginEnd = -baseItem.height;
403− } else {
404− item.displayMarginBeginning = Math.round(-Math.max(-baseItem.y, 0));
402+ item.displayMarginBeginning = Math.round(-Math.max(-baseItem.y - extraMargins, 0));
405− item.displayMarginEnd = -Math.round(Math.max(baseItem.height - seeAll.height -
403+ item.displayMarginEnd = -Math.round(Math.max(baseItem.height - extraMargins - seeAll.height -
406− categoryView.height + baseItem.y, 0));
404+ categoryView.height + baseItem.y, 0));
407− }
408 }
405 }
409 }
406 }
410 }
407 }
qml/Dash/Previews/PreviewActions.qml+2−1
@@ -15,6 +15,7 @@
15 */
15 */
16
16
17import QtQuick 2.1
17import QtQuick 2.1
18+import Ubuntu.Components 1.1
18
19
19/*! This preview widget shows either one button, two buttons or one button
20/*! This preview widget shows either one button, two buttons or one button
20 * and a combo button depending on the number of items in widgetData["actions"].
21 * and a combo button depending on the number of items in widgetData["actions"].
@@ -64,7 +65,7 @@ PreviewWidget {
64 visible: row.actions && row.actions.length > 0
65 visible: row.actions && row.actions.length > 0
65 onTriggeredAction: root.triggered(root.widgetId, actionData.id, actionData)
66 onTriggeredAction: root.triggered(root.widgetId, actionData.id, actionData)
66 width: (root.width - units.gu(1)) / 2
67 width: (root.width - units.gu(1)) / 2
67− color: root.scopeStyle ? root.scopeStyle.previewButtonColor : Theme.palette.selected.foreground
68+ color: root.scopeStyle ? root.scopeStyle.previewButtonColor : UbuntuColors.orange
68 }
69 }
69 }
70 }
70}
71}
qml/Dash/Previews/PreviewPayments.qml+1−1
@@ -35,7 +35,7 @@ PreviewWidget {
35 id: paymentButton
35 id: paymentButton
36 objectName: "paymentButton"
36 objectName: "paymentButton"
37
37
38− color: Theme.palette.selected.foreground
38+ color: UbuntuColors.orange
39 text: paymentClient.formattedPrice
39 text: paymentClient.formattedPrice
40 onClicked: {
40 onClicked: {
41 paymentClient.start();
41 paymentClient.start();
qml/Dash/Previews/PreviewZoomableImage.qml+1
@@ -40,6 +40,7 @@ PreviewWidget {
40 }
40 }
41 scaleTo: "height"
41 scaleTo: "height"
42 source: widgetData["source"]
42 source: widgetData["source"]
43+ asynchronous: true
43
44
44 borderSource: mouseArea.pressed ? "radius_pressed.sci" : "radius_idle.sci"
45 borderSource: mouseArea.pressed ? "radius_pressed.sci" : "radius_idle.sci"
45
46
qml/Dash/ScopesOverviewAll.qml−2
@@ -43,8 +43,6 @@ Flickable {
43 id: cardGrid
43 id: cardGrid
44 width: root.width
44 width: root.width
45 height: parent.height
45 height: parent.height
46− visibleRangeBegin: root.contentY
47− visibleRangeEnd: root.contentY + root.height
48
46
49 onClicked: {
47 onClicked: {
50 root.clicked(index, result, item, itemModel);
48 root.clicked(index, result, item, itemModel);
qml/Launcher/LauncherPanel.qml+27−7
@@ -50,12 +50,19 @@ Rectangle {
50 fill: parent
50 fill: parent
51 }
51 }
52
52
53− Rectangle {
53+ Item {
54 objectName: "buttonShowDashHome"
54 objectName: "buttonShowDashHome"
55 width: parent.width
55 width: parent.width
56 height: units.gu(7)
56 height: units.gu(7)
57+
58+ UbuntuShape {
59+ anchors {
60+ fill: parent
61+ topMargin: -units.gu(2)
62+ }
63+ borderSource: "none"
57− color: UbuntuColors.orange
64+ color: UbuntuColors.orange
58− z: 1
65+ }
59
66
60 Image {
67 Image {
61 objectName: "dashItem"
68 objectName: "dashItem"
@@ -137,6 +144,20 @@ Rectangle {
137 property bool draggingTransitionRunning: false
144 property bool draggingTransitionRunning: false
138 property int scheduledMoveTo: -1
145 property int scheduledMoveTo: -1
139
146
147+ UbuntuNumberAnimation {
148+ id: snapToBottomAnimation
149+ target: launcherListView
150+ property: "contentY"
151+ to: launcherListView.originY
152+ }
153+
154+ UbuntuNumberAnimation {
155+ id: snapToTopAnimation
156+ target: launcherListView
157+ property: "contentY"
158+ to: launcherListView.contentHeight - launcherListView.height + launcherListView.originY
159+ }
160+
140 displaced: Transition {
161 displaced: Transition {
141 NumberAnimation { properties: "x,y"; duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
162 NumberAnimation { properties: "x,y"; duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
142 }
163 }
@@ -342,10 +363,9 @@ Rectangle {
342 progressiveScrollingTimer.stop();
363 progressiveScrollingTimer.stop();
343 launcherListView.interactive = true;
364 launcherListView.interactive = true;
344 if (droppedIndex >= launcherListView.count - 2 && postDragging) {
365 if (droppedIndex >= launcherListView.count - 2 && postDragging) {
345− launcherListView.flick(0, -launcherListView.clickFlickSpeed);
366+ snapToBottomAnimation.start();
346− }
347− if (droppedIndex == 0 && postDragging) {
367+ } else if (droppedIndex < 2 && postDragging) {
348− launcherListView.flick(0, launcherListView.clickFlickSpeed);
368+ snapToTopAnimation.start();
349 }
369 }
350 }
370 }
351
371
qml/Panel/IndicatorItem.qml+1−2
@@ -99,12 +99,11 @@ IndicatorDelegate {
99
99
100 model: d.useFallbackIcon ? [ "image://theme/settings" ] : root.icons
100 model: d.useFallbackIcon ? [ "image://theme/settings" ] : root.icons
101
101
102− StatusIcon {
102+ Icon {
103 id: itemImage
103 id: itemImage
104 objectName: "icon"+index
104 objectName: "icon"+index
105 height: iconHeight
105 height: iconHeight
106 source: modelData
106 source: modelData
107− sets: ["status", "actions"]
108 color: root.color
107 color: root.color
109 Behavior on color { ColorAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing } }
108 Behavior on color { ColorAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing } }
110
109
qml/Shell.qml+11−4
@@ -499,7 +499,7 @@ Item {
499 // Just a tiny wrapper to adjust greeter's x without messing with its own dragging
499 // Just a tiny wrapper to adjust greeter's x without messing with its own dragging
500 id: greeterWrapper
500 id: greeterWrapper
501 objectName: "greeterWrapper"
501 objectName: "greeterWrapper"
502− x: greeter.narrowMode ? launcher.progress : 0
502+ x: (greeter.narrowMode && greeter.showProgress > 0) ? launcher.progress : 0
503 y: panel.panelHeight
503 y: panel.panelHeight
504 width: parent.width
504 width: parent.width
505 height: parent.height - panel.panelHeight
505 height: parent.height - panel.panelHeight
@@ -770,19 +770,26 @@ Item {
770 margin: units.gu(1)
770 margin: units.gu(1)
771
771
772 y: topmostIsFullscreen ? 0 : panel.panelHeight
772 y: topmostIsFullscreen ? 0 : panel.panelHeight
773− width: parent.width
774 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
773 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
775
774
776 states: [
775 states: [
777 State {
776 State {
778 name: "narrow"
777 name: "narrow"
779 when: overlay.width <= units.gu(60)
778 when: overlay.width <= units.gu(60)
779+ AnchorChanges {
780+ target: notifications
780− AnchorChanges { target: notifications; anchors.left: parent.left }
781+ anchors.left: parent.left
782+ anchors.right: parent.right
783+ }
781 },
784 },
782 State {
785 State {
783 name: "wide"
786 name: "wide"
784 when: overlay.width > units.gu(60)
787 when: overlay.width > units.gu(60)
788+ AnchorChanges {
789+ target: notifications
785− AnchorChanges { target: notifications; anchors.left: undefined }
790+ anchors.left: undefined
791+ anchors.right: parent.right
792+ }
786 PropertyChanges { target: notifications; width: units.gu(38) }
793 PropertyChanges { target: notifications; width: units.gu(38) }
787 }
794 }
788 ]
795 ]
qml/Stages/PhoneStage.qml+18−34
@@ -145,16 +145,16 @@ Rectangle {
145
145
146 // Those markers mark the various positions in the spread (ratio to screen width from right to left):
146 // Those markers mark the various positions in the spread (ratio to screen width from right to left):
147 // 0 - 1: following finger, snap back to the beginning on release
147 // 0 - 1: following finger, snap back to the beginning on release
148− property real positionMarker1: 0.3
148+ property real positionMarker1: 0.2
149 // 1 - 2: curved snapping movement, snap to app 1 on release
149 // 1 - 2: curved snapping movement, snap to app 1 on release
150− property real positionMarker2: 0.45
150+ property real positionMarker2: 0.3
151 // 2 - 3: movement follows finger, snaps back to app 1 on release
151 // 2 - 3: movement follows finger, snaps back to app 1 on release
152− property real positionMarker3: 0.6
152+ property real positionMarker3: 0.35
153 // passing 3, we detach movement from the finger and snap to 4
153 // passing 3, we detach movement from the finger and snap to 4
154 property real positionMarker4: 0.9
154 property real positionMarker4: 0.9
155
155
156 // This is where the first app snaps to when bringing it in from the right edge.
156 // This is where the first app snaps to when bringing it in from the right edge.
157− property real snapPosition: 0.75
157+ property real snapPosition: 0.7
158
158
159 // Phase of the animation:
159 // Phase of the animation:
160 // 0: Starting from right edge, a new app (index 1) comes in from the right
160 // 0: Starting from right edge, a new app (index 1) comes in from the right
@@ -178,7 +178,7 @@ Rectangle {
178 case 1:
178 case 1:
179 if (shiftedContentX < width * positionMarker2) {
179 if (shiftedContentX < width * positionMarker2) {
180 phase = 0;
180 phase = 0;
181− } else if (shiftedContentX >= width * positionMarker4) {
181+ } else if (shiftedContentX >= width * positionMarker4 && !spreadDragArea.dragging) {
182 phase = 2;
182 phase = 2;
183 }
183 }
184 break;
184 break;
@@ -270,6 +270,7 @@ Rectangle {
270
270
271 Repeater {
271 Repeater {
272 id: spreadRepeater
272 id: spreadRepeater
273+ objectName: "spreadRepeater"
273 model: ApplicationManager
274 model: ApplicationManager
274 delegate: TransformedSpreadDelegate {
275 delegate: TransformedSpreadDelegate {
275 id: appDelegate
276 id: appDelegate
@@ -286,7 +287,7 @@ Rectangle {
286 otherSelected: spreadView.selectedIndex >= 0 && !selected
287 otherSelected: spreadView.selectedIndex >= 0 && !selected
287 interactive: !spreadView.interactive && spreadView.phase === 0
288 interactive: !spreadView.interactive && spreadView.phase === 0
288 && spreadView.shiftedContentX === 0 && root.interactive && index === 0
289 && spreadView.shiftedContentX === 0 && root.interactive && index === 0
289− swipeToCloseEnabled: spreadView.interactive
290+ swipeToCloseEnabled: spreadView.interactive && !snapAnimation.running
290 maximizedAppTopMargin: root.maximizedAppTopMargin
291 maximizedAppTopMargin: root.maximizedAppTopMargin
291 dropShadow: spreadView.active ||
292 dropShadow: spreadView.active ||
292 (priv.focusedAppDelegate && priv.focusedAppDelegate.x !== 0)
293 (priv.focusedAppDelegate && priv.focusedAppDelegate.x !== 0)
@@ -367,8 +368,9 @@ Rectangle {
367 if (spreadView.phase == 0 && index < 2) {
368 if (spreadView.phase == 0 && index < 2) {
368 if (progress < spreadView.positionMarker1) {
369 if (progress < spreadView.positionMarker1) {
369 return progress;
370 return progress;
370− } else if (progress < spreadView.positionMarker1 + snappingCurve.period){
371+ } else if (progress < spreadView.positionMarker1 + 0.05){
371− return spreadView.positionMarker1 + snappingCurve.value * 3;
372+ // p : 0.05 = x : pm2
373+ return spreadView.positionMarker1 + (progress - spreadView.positionMarker1) * (spreadView.positionMarker2 - spreadView.positionMarker1) / 0.05
372 } else {
374 } else {
373 return spreadView.positionMarker2;
375 return spreadView.positionMarker2;
374 }
376 }
@@ -380,13 +382,6 @@ Rectangle {
380 visible: (progress >= 0 && progress < 1.7) ||
382 visible: (progress >= 0 && progress < 1.7) ||
381 (isDash && priv.focusedAppDelegate.x !== 0)
383 (isDash && priv.focusedAppDelegate.x !== 0)
382
384
383− EasingCurve {
384− id: snappingCurve
385− type: EasingCurve.Linear
386− period: 0.05
387− progress: appDelegate.progress - spreadView.positionMarker1
388− }
389−
390 Binding {
385 Binding {
391 target: appDelegate
386 target: appDelegate
392 property: "orientation"
387 property: "orientation"
@@ -425,16 +420,11 @@ Rectangle {
425 id: spreadDragArea
420 id: spreadDragArea
426 objectName: "spreadDragArea"
421 objectName: "spreadDragArea"
427 direction: Direction.Leftwards
422 direction: Direction.Leftwards
428− enabled: spreadView.phase != 2 && root.spreadEnabled
423+ enabled: (spreadView.phase != 2 && root.spreadEnabled) || dragging
429
424
430 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
425 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
431 width: root.dragAreaWidth
426 width: root.dragAreaWidth
432
427
433− // Sitting at the right edge of the screen, this EdgeDragArea directly controls the spreadView when
434− // attachedToView is true. When the finger movement passes positionMarker3 we detach it from the
435− // spreadView and make the spreadView snap to positionMarker4.
436− property bool attachedToView: true
437−
438 property var gesturePoints: new Array()
428 property var gesturePoints: new Array()
439
429
440 onTouchXChanged: {
430 onTouchXChanged: {
@@ -443,15 +433,13 @@ Rectangle {
443 spreadView.phase = 0;
433 spreadView.phase = 0;
444 spreadView.contentX = -spreadView.shift;
434 spreadView.contentX = -spreadView.shift;
445 }
435 }
446− if (dragging && status == DirectionalDragArea.Recognized && attachedToView) {
436+ if (dragging && status == DirectionalDragArea.Recognized) {
447 // Gesture recognized. Let's move the spreadView with the finger
437 // Gesture recognized. Let's move the spreadView with the finger
448− var finalX = Math.min(touchX + width, width);
438+ var dragX = Math.min(touchX + width, width); // Prevent dragging rightwards
449− spreadView.contentX = -finalX + spreadDragArea.width - spreadView.shift;
439+ dragX = -dragX + spreadDragArea.width - spreadView.shift;
450− }
440+ // Don't allow dragging further than the animation crossing with phase2's animation
451− if (attachedToView && spreadView.shiftedContentX >= spreadView.width * spreadView.positionMarker3) {
441+ var maxMovement = spreadView.width * spreadView.positionMarker4 - spreadView.shift;
452− // We passed positionMarker3. Detach from spreadView and snap it.
442+ spreadView.contentX = Math.min(dragX, maxMovement);
453− attachedToView = false;
454− spreadView.snap();
455 }
443 }
456 gesturePoints.push(touchX);
444 gesturePoints.push(touchX);
457 }
445 }
@@ -462,10 +450,6 @@ Rectangle {
462 onStatusChanged: {
450 onStatusChanged: {
463 previousStatus = currentStatus;
451 previousStatus = currentStatus;
464 currentStatus = status;
452 currentStatus = status;
465−
466− if (status == DirectionalDragArea.Recognized) {
467− attachedToView = true;
468− }
469 }
453 }
470
454
471 onDraggingChanged: {
455 onDraggingChanged: {
@@ -493,7 +477,7 @@ Rectangle {
493 // If it was a short one-way movement, do the Alt+Tab switch
477 // If it was a short one-way movement, do the Alt+Tab switch
494 // no matter if we didn't cross positionMarker1 yet.
478 // no matter if we didn't cross positionMarker1 yet.
495 spreadView.snapTo(1);
479 spreadView.snapTo(1);
496− } else if (!dragging && attachedToView) {
480+ } else if (!dragging) {
497 // otherwise snap to the closest snap position we can find
481 // otherwise snap to the closest snap position we can find
498 // (might be back to start, to app 1 or to spread)
482 // (might be back to start, to app 1 or to spread)
499 spreadView.snap();
483 spreadView.snap();
qml/Stages/SpreadDelegate.qml+20−3
@@ -41,10 +41,17 @@ Item {
41 Item {
41 Item {
42 objectName: "appWindowWithShadow"
42 objectName: "appWindowWithShadow"
43
43
44+ readonly property real limit: root.height / 4
45+
44− y: dragArea.distance
46+ y: root.closeable ? dragArea.distance : elastic(dragArea.distance)
45 width: parent.width
47 width: parent.width
46 height: parent.height
48 height: parent.height
47
49
50+ function elastic(distance) {
51+ var k = distance < 0 ? -limit : limit
52+ return k * (1 - Math.pow((k - 1) / k, distance))
53+ }
54+
48 BorderImage {
55 BorderImage {
49 anchors {
56 anchors {
50 fill: appWindow
57 fill: appWindow
@@ -75,6 +82,8 @@ Item {
75
82
76 property bool moving: false
83 property bool moving: false
77 property real distance: 0
84 property real distance: 0
85+ readonly property int threshold: units.gu(2)
86+ property int offset: 0
78
87
79 readonly property real minSpeedToClose: units.gu(40)
88 readonly property real minSpeedToClose: units.gu(40)
80
89
@@ -82,9 +91,17 @@ Item {
82 if (!dragging) {
91 if (!dragging) {
83 return;
92 return;
84 }
93 }
85− moving = moving || Math.abs(dragValue) > units.gu(1)
94+ moving = moving || Math.abs(dragValue) > threshold;
95+ if (moving) {
96+ distance = dragValue + offset;
97+ }
98+ }
99+
100+ onMovingChanged: {
86 if (moving) {
101 if (moving) {
87− distance = dragValue;
102+ offset = (dragValue > 0 ? -threshold: threshold)
103+ } else {
104+ offset = 0;
88 }
105 }
89 }
106 }
90
107
qml/Stages/TabletStage.qml+6−15
@@ -490,7 +490,7 @@ Rectangle {
490 otherSelected: spreadView.selectedIndex >= 0 && !selected
490 otherSelected: spreadView.selectedIndex >= 0 && !selected
491 isInSideStage: priv.sideStageAppId == model.appId
491 isInSideStage: priv.sideStageAppId == model.appId
492 interactive: !spreadView.interactive && spreadView.phase === 0 && root.interactive
492 interactive: !spreadView.interactive && spreadView.phase === 0 && root.interactive
493− swipeToCloseEnabled: spreadView.interactive
493+ swipeToCloseEnabled: spreadView.interactive && !snapAnimation.running
494 maximizedAppTopMargin: root.maximizedAppTopMargin
494 maximizedAppTopMargin: root.maximizedAppTopMargin
495 dragOffset: !isDash && model.appId == priv.mainStageAppId && root.inverseProgress > 0 ? root.inverseProgress : 0
495 dragOffset: !isDash && model.appId == priv.mainStageAppId && root.inverseProgress > 0 ? root.inverseProgress : 0
496 application: ApplicationManager.get(index)
496 application: ApplicationManager.get(index)
@@ -590,7 +590,6 @@ Rectangle {
590 width: root.dragAreaWidth
590 width: root.dragAreaWidth
591 direction: Direction.Leftwards
591 direction: Direction.Leftwards
592
592
593− property bool attachedToView: false
594 property var gesturePoints: new Array()
593 property var gesturePoints: new Array()
595
594
596 onTouchXChanged: {
595 onTouchXChanged: {
@@ -599,22 +598,14 @@ Rectangle {
599 spreadView.contentX = -spreadView.shift;
598 spreadView.contentX = -spreadView.shift;
600 }
599 }
601
600
602− if (dragging && attachedToView) {
601+ if (dragging) {
603− spreadView.contentX = -touchX + spreadDragArea.width - spreadView.shift;
602+ var dragX = -touchX + spreadDragArea.width - spreadView.shift;
604− if (spreadView.shiftedContentX > spreadView.phase0Width + spreadView.phase1Width / 2) {
603+ var maxDrag = spreadView.width * spreadView.positionMarker4 - spreadView.shift;
605− attachedToView = false;
604+ spreadView.contentX = Math.min(dragX, maxDrag);
606− spreadView.snap();
607− }
608 }
605 }
609 gesturePoints.push(touchX);
606 gesturePoints.push(touchX);
610 }
607 }
611
608
612− onStatusChanged: {
613− if (status == DirectionalDragArea.Recognized) {
614− attachedToView = true;
615− }
616− }
617−
618 onDraggingChanged: {
609 onDraggingChanged: {
619 if (dragging) {
610 if (dragging) {
620 // Gesture recognized. Start recording this gesture
611 // Gesture recognized. Start recording this gesture
@@ -630,7 +621,7 @@ Rectangle {
630 // If it was a short one-way movement, do the Alt+Tab switch
621 // If it was a short one-way movement, do the Alt+Tab switch
631 // no matter if we didn't cross positionMarker1 yet.
622 // no matter if we didn't cross positionMarker1 yet.
632 spreadView.snapTo(spreadView.nextInStack);
623 spreadView.snapTo(spreadView.nextInStack);
633− } else if (!dragging && attachedToView) {
624+ } else if (!dragging) {
634 if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker1) {
625 if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker1) {
635 spreadView.snap();
626 spreadView.snap();
636 } else if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker2) {
627 } else if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker2) {
qml/graphics/applicationIcons/dash@18.png → qml/graphics/applicationIcons/dash.pngrenamed

Binary file not shown.

src/CMakeLists.txt+4−1
@@ -1,6 +1,9 @@
1+pkg_check_modules(CONNECTIVITY REQUIRED connectivity-qt1)
2+
1include_directories(
3include_directories(
2 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
4 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
3 ${CMAKE_SOURCE_DIR}/libs/UbuntuGestures
5 ${CMAKE_SOURCE_DIR}/libs/UbuntuGestures
6+ ${CONNECTIVITY_INCLUDE_DIRS}
4)
7)
5
8
6file(GLOB_RECURSE QML_FILES
9file(GLOB_RECURSE QML_FILES
@@ -30,7 +33,7 @@ if (NOT "${XCB_INCLUDE_DIRS}" STREQUAL "")
30 target_link_libraries(${SHELL_APP} ${XCB_LDFLAGS})
33 target_link_libraries(${SHELL_APP} ${XCB_LDFLAGS})
31endif()
34endif()
32
35
33−target_link_libraries(${SHELL_APP} UbuntuGestures)
36+target_link_libraries(${SHELL_APP} UbuntuGestures connectivity-qt1)
34
37
35# For it to find libUbuntuGestures.so
38# For it to find libUbuntuGestures.so
36set_target_properties(${SHELL_APP} PROPERTIES INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${SHELL_PRIVATE_LIBDIR}")
39set_target_properties(${SHELL_APP} PROPERTIES INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${SHELL_PRIVATE_LIBDIR}")
src/CachingNetworkManagerFactory.cpp+2−11
@@ -20,25 +20,16 @@
20#include <QNetworkDiskCache>
20#include <QNetworkDiskCache>
21#include <QNetworkAccessManager>
21#include <QNetworkAccessManager>
22#include <QStandardPaths>
22#include <QStandardPaths>
23−#include <QNetworkConfigurationManager>
24
23
25CachingNetworkAccessManager::CachingNetworkAccessManager(QObject *parent)
24CachingNetworkAccessManager::CachingNetworkAccessManager(QObject *parent)
26 : QNetworkAccessManager(parent)
25 : QNetworkAccessManager(parent)
27{
26{
28− m_networkManager = new QNetworkConfigurationManager(this);
27+ m_networkingStatus = new ubuntu::connectivity::NetworkingStatus(this);
29−
30− QObject::connect(m_networkManager, &QNetworkConfigurationManager::onlineStateChanged, this, &CachingNetworkAccessManager::onlineStateChanged);
31− m_isOnline = m_networkManager->isOnline();
32−}
33−
34−void CachingNetworkAccessManager::onlineStateChanged(bool isOnline)
35−{
36− m_isOnline = isOnline;
37}
28}
38
29
39QNetworkReply* CachingNetworkAccessManager::createRequest(Operation op, const QNetworkRequest &request, QIODevice *outgoingData)
30QNetworkReply* CachingNetworkAccessManager::createRequest(Operation op, const QNetworkRequest &request, QIODevice *outgoingData)
40{
31{
41− if (!m_isOnline) {
32+ if (m_networkingStatus->status() != ubuntu::connectivity::NetworkingStatus::Status::Online) {
42 QNetworkRequest req(request);
33 QNetworkRequest req(request);
43 req.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysCache);
34 req.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysCache);
44 return QNetworkAccessManager::createRequest(op, req, outgoingData);
35 return QNetworkAccessManager::createRequest(op, req, outgoingData);
src/CachingNetworkManagerFactory.h+2−6
@@ -21,7 +21,7 @@
21#include <QQmlNetworkAccessManagerFactory>
21#include <QQmlNetworkAccessManagerFactory>
22#include <QNetworkAccessManager>
22#include <QNetworkAccessManager>
23
23
24−class QNetworkConfigurationManager;
24+#include <ubuntu/connectivity/networking-status.h>
25
25
26class CachingNetworkAccessManager : public QNetworkAccessManager
26class CachingNetworkAccessManager : public QNetworkAccessManager
27{
27{
@@ -31,12 +31,8 @@ public:
31protected:
31protected:
32 QNetworkReply* createRequest(Operation op, const QNetworkRequest &req, QIODevice *outgoingData = 0) override;
32 QNetworkReply* createRequest(Operation op, const QNetworkRequest &req, QIODevice *outgoingData = 0) override;
33
33
34−private Q_SLOTS:
35− void onlineStateChanged(bool isOnline);
36−
37private:
34private:
38− QNetworkConfigurationManager* m_networkManager;
35+ ubuntu::connectivity::NetworkingStatus* m_networkingStatus;
39− bool m_isOnline;
40};
36};
41
37
42class CachingNetworkManagerFactory : public QQmlNetworkAccessManagerFactory
38class CachingNetworkManagerFactory : public QQmlNetworkAccessManagerFactory
src/Dash/CMakeLists.txt+1−1
@@ -12,7 +12,7 @@ qt5_use_modules(unity8-dash Gui Qml Quick Test)
12# For it to find libUbuntuGestures.so, needed by Ubuntu.Gestures QML module.
12# For it to find libUbuntuGestures.so, needed by Ubuntu.Gestures QML module.
13set_target_properties(unity8-dash PROPERTIES INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${SHELL_PRIVATE_LIBDIR}")
13set_target_properties(unity8-dash PROPERTIES INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${SHELL_PRIVATE_LIBDIR}")
14
14
15−target_link_libraries(unity8-dash UbuntuGestures)
15+target_link_libraries(unity8-dash UbuntuGestures connectivity-qt1)
16
16
17set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden")
17set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden")
18
18
tests/autopilot/unity8/application_lifecycle/tests/test_application_lifecycle.py+1−1
@@ -47,7 +47,7 @@ class ApplicationLifecycleTests(tests.ApplicationLifeCycleTestCase):
47 height = self.main_window.height
47 height = self.main_window.height
48 start_x = width
48 start_x = width
49 start_y = int(height/2)
49 start_y = int(height/2)
50− end_x = int(width/2)
50+ end_x = int(width*3/4)
51 end_y = start_y
51 end_y = start_y
52
52
53 logger.info("Swiping screen from the right edge")
53 logger.info("Swiping screen from the right edge")
tests/autopilot/unity8/shell/emulators/dash.py+1−1
@@ -204,7 +204,7 @@ class GenericScopeView(emulators.UnityEmulatorBase):
204
204
205 """
205 """
206 category_element = self._get_category_element(category)
206 category_element = self._get_category_element(category)
207− icon = category_element.select_single('AbstractButton', title=title)
207+ icon = category_element.wait_select_single('AbstractButton', title=title)
208 self.pointing_device.click_object(icon)
208 self.pointing_device.click_object(icon)
209
209
210 def _get_category_element(self, category):
210 def _get_category_element(self, category):
tests/plugins/Dash/CMakeLists.txt+1
@@ -80,3 +80,4 @@ set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "LC_ALL=C")
80add_qml_test(. ScopeStyle IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
80add_qml_test(. ScopeStyle IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
81add_qml_test(. ListViewWithPageHeaderQML IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
81add_qml_test(. ListViewWithPageHeaderQML IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
82add_qml_test(. CardAttributes IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
82add_qml_test(. CardAttributes IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
83+add_qml_test(. CroppedImageMinimumSourceSize IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
tests/plugins/Dash/cardcreator/1.res+4−4
@@ -46,16 +46,16 @@ Item {
46 height = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.height });
46 height = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.height });
47 }
47 }
48 }
48 }
49− image: CroppedImageMinimumSourceSize {
49+ CroppedImageMinimumSourceSize {
50+ id: artImage;
50 objectName: "artImage";
51 objectName: "artImage";
51− property bool doLoadSource: !NetworkingStatus.limitedBandwith;
52− source: { if (root.visible) doLoadSource = true; return doLoadSource && cardData && cardData["art"] || ""; }
52+ source: cardData && cardData["art"] || "";
53− cache: true;
54 asynchronous: root.asynchronous;
53 asynchronous: root.asynchronous;
55 visible: false;
54 visible: false;
56 width: root.width;
55 width: root.width;
57 height: width / artShape.aspect;
56 height: width / artShape.aspect;
58 }
57 }
58+ image: artImage.image;
59 }
59 }
60 }
60 }
61 }
61 }
tests/plugins/Dash/cardcreator/2.res+2−3
@@ -68,13 +68,12 @@ CroppedImageMinimumSourceSize {
68 id: mascotImage;
68 id: mascotImage;
69 objectName: "mascotImage";
69 objectName: "mascotImage";
70 anchors { verticalCenter: parent.verticalCenter; }
70 anchors { verticalCenter: parent.verticalCenter; }
71− property bool doLoadSource: !NetworkingStatus.limitedBandwith;
72− source: { if (root.visible) doLoadSource = true; return doLoadSource && cardData && cardData["mascot"] || ""; }
71+ source: cardData && cardData["mascot"] || "";
73 width: units.gu(6);
72 width: units.gu(6);
74 height: units.gu(5.625);
73 height: units.gu(5.625);
75 horizontalAlignment: Image.AlignHCenter;
74 horizontalAlignment: Image.AlignHCenter;
76 verticalAlignment: Image.AlignVCenter;
75 verticalAlignment: Image.AlignVCenter;
77− visible: showHeader && resized;
76+ visible: showHeader;
78 }
77 }
79,Item {
78,Item {
80 id: headerTitleContainer;
79 id: headerTitleContainer;
tests/plugins/Dash/cardcreator/3.res+10−10
@@ -46,16 +46,16 @@ Item {
46 height = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.height });
46 height = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.height });
47 }
47 }
48 }
48 }
49− image: CroppedImageMinimumSourceSize {
49+ CroppedImageMinimumSourceSize {
50+ id: artImage;
50− objectName: "artImage";
51+ objectName: "artImage";
51− property bool doLoadSource: !NetworkingStatus.limitedBandwith;
52− source: { if (root.visible) doLoadSource = true; return doLoadSource && cardData && cardData["art"] || ""; }
52+ source: cardData && cardData["art"] || "";
53− cache: true;
54− asynchronous: root.asynchronous;
53+ asynchronous: root.asynchronous;
55− visible: false;
54+ visible: false;
56− width: root.width;
55+ width: root.width;
57− height: width / artShape.aspect;
56+ height: width / artShape.aspect;
58− }
57+ }
58+ image: artImage.image;
59 }
59 }
60 }
60 }
61 }
61 }
tests/plugins/Dash/cardcreator/4.res+3−4
@@ -35,11 +35,11 @@ Loader {
35 id: mascotShapeLoader;
35 id: mascotShapeLoader;
36 objectName: "mascotShapeLoader";
36 objectName: "mascotShapeLoader";
37 asynchronous: root.asynchronous;
37 asynchronous: root.asynchronous;
38− active: mascotImage.status === Image.Ready;
38+ active: mascotImage.image.status === Image.Ready;
39 visible: showHeader && active && status == Loader.Ready;
39 visible: showHeader && active && status == Loader.Ready;
40 width: units.gu(6);
40 width: units.gu(6);
41 height: units.gu(5.625);
41 height: units.gu(5.625);
42− sourceComponent: UbuntuShape { image: mascotImage }
42+ sourceComponent: UbuntuShape { image: mascotImage.image }
43 anchors { verticalCenter: parent.verticalCenter; }
43 anchors { verticalCenter: parent.verticalCenter; }
44 }
44 }
45
45
@@ -47,8 +47,7 @@ Loader {
47 id: mascotImage;
47 id: mascotImage;
48 objectName: "mascotImage";
48 objectName: "mascotImage";
49 anchors { verticalCenter: parent.verticalCenter; }
49 anchors { verticalCenter: parent.verticalCenter; }
50− property bool doLoadSource: !NetworkingStatus.limitedBandwith;
51− source: { if (root.visible) doLoadSource = true; return doLoadSource && cardData && cardData["mascot"] || ""; }
50+ source: cardData && cardData["mascot"] || "";
52 width: units.gu(6);
51 width: units.gu(6);
53 height: units.gu(5.625);
52 height: units.gu(5.625);
54 horizontalAlignment: Image.AlignHCenter;
53 horizontalAlignment: Image.AlignHCenter;
tests/plugins/Dash/cardcreator/5.res+10−10
@@ -46,16 +46,16 @@ Item {
46 height = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.height });
46 height = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.height });
47 }
47 }
48 }
48 }
49− image: CroppedImageMinimumSourceSize {
49+ CroppedImageMinimumSourceSize {
50+ id: artImage;
50− objectName: "artImage";
51+ objectName: "artImage";
51− property bool doLoadSource: !NetworkingStatus.limitedBandwith;
52− source: { if (root.visible) doLoadSource = true; return doLoadSource && cardData && cardData["art"] || ""; }
52+ source: cardData && cardData["art"] || "";
53− cache: true;
54− asynchronous: root.asynchronous;
53+ asynchronous: root.asynchronous;
55− visible: false;
54+ visible: false;
56− width: root.width;
55+ width: root.width;
57− height: width / artShape.aspect;
56+ height: width / artShape.aspect;
58− }
57+ }
58+ image: artImage.image;
59 }
59 }
60 }
60 }
61 }
61 }
tests/plugins/Dash/cardcreator/6.res+1−1
@@ -95,7 +95,7 @@ Item {
95 text: cardData && cardData["subtitle"] || "";
95 text: cardData && cardData["subtitle"] || "";
96 font.weight: Font.Light;
96 font.weight: Font.Light;
97 }
97 }
98−,StatusIcon {
98+,Icon {
99 id: emblemIcon;
99 id: emblemIcon;
100 objectName: "emblemIcon";
100 objectName: "emblemIcon";
101 anchors {
101 anchors {
tests/plugins/Dash/cardcreator/7.res+2−3
@@ -68,13 +68,12 @@ CroppedImageMinimumSourceSize {
68 id: mascotImage;
68 id: mascotImage;
69 objectName: "mascotImage";
69 objectName: "mascotImage";
70 anchors { verticalCenter: parent.verticalCenter; }
70 anchors { verticalCenter: parent.verticalCenter; }
71− property bool doLoadSource: !NetworkingStatus.limitedBandwith;
72− source: { if (root.visible) doLoadSource = true; return doLoadSource && cardData && cardData["mascot"] || ""; }
71+ source: cardData && cardData["mascot"] || "";
73 width: units.gu(6);
72 width: units.gu(6);
74 height: units.gu(5.625);
73 height: units.gu(5.625);
75 horizontalAlignment: Image.AlignHCenter;
74 horizontalAlignment: Image.AlignHCenter;
76 verticalAlignment: Image.AlignVCenter;
75 verticalAlignment: Image.AlignVCenter;
77− visible: showHeader && resized;
76+ visible: showHeader;
78 }
77 }
79
78
80,Item {
79,Item {
tests/plugins/Dash/tst_CroppedImageMinimumSourceSize.qmlnew file+56
@@ -0,0 +1,56 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.3
18+import QtTest 1.0
19+import Ubuntu.Components 1.1
20+import Dash 0.1
21+
22+Rectangle {
23+ width: 500
24+ height: 300
25+
26+ color: "red"
27+
28+ CroppedImageMinimumSourceSize {
29+ id: cimss
30+ x: 100
31+ y: 100
32+ width: 100
33+ height: 100
34+ source: Qt.resolvedUrl("../../qmltests/Dash/artwork/music-player-design.png")
35+ asynchronous: true
36+ }
37+
38+ TestCase {
39+ id: testCase
40+ name: "ScopeStyle"
41+ when: windowShown
42+
43+ function test_croppedSource() {
44+ tryCompare(cimss.image.sourceSize, "width", 100);
45+ tryCompare(cimss.image.sourceSize, "height", 0);
46+
47+ cimss.width = 40;
48+ tryCompare(cimss.image.sourceSize, "width", 0);
49+ tryCompare(cimss.image.sourceSize, "height", 100);
50+
51+ cimss.width = 100;
52+ tryCompare(cimss.image.sourceSize, "width", 100);
53+ tryCompare(cimss.image.sourceSize, "height", 0);
54+ }
55+ }
56+}
tests/plugins/Dash/tst_ScopeStyle.qml+1−1
@@ -174,7 +174,7 @@ Rectangle {
174
174
175 function test_previewButtonColor_data() {
175 function test_previewButtonColor_data() {
176 return [
176 return [
177− { tag: "default", index: 0, previewButtonColor: Theme.palette.selected.foreground },
177+ { tag: "default", index: 0, previewButtonColor: UbuntuColors.orange },
178 { tag: "red", index: 1, previewButtonColor: "red" },
178 { tag: "red", index: 1, previewButtonColor: "red" },
179 ];
179 ];
180 }
180 }
tests/qmltests/Dash/tst_Card.qml−22
@@ -17,7 +17,6 @@
17import QtQuick 2.0
17import QtQuick 2.0
18import QtTest 1.0
18import QtTest 1.0
19import Ubuntu.Components 0.1
19import Ubuntu.Components 0.1
20−import Ubuntu.Connectivity 1.0
21import Unity.Test 0.1 as UT
20import Unity.Test 0.1 as UT
22import "../../../qml/Dash"
21import "../../../qml/Dash"
23import "CardHelpers.js" as Helpers
22import "CardHelpers.js" as Helpers
@@ -221,7 +220,6 @@ Rectangle {
221
220
222 function init() {
221 function init() {
223 loader.visible = true;
222 loader.visible = true;
224− NetworkingStatus.limitedBandwith = false;
225 }
223 }
226
224
227 function cleanup() {
225 function cleanup() {
@@ -585,25 +583,5 @@ Rectangle {
585 verify((card.width - titleToCard.x - titleToCard.width) === units.gu(1));
583 verify((card.width - titleToCard.x - titleToCard.width) === units.gu(1));
586 }
584 }
587 }
585 }
588−
589− function test_load_images_visibility_network_data() {
590− return [
591− { tag: "Visible, network", visible: true, limitedBandwith: false },
592− { tag: "Visible, no network", visible: true, limitedBandwith: true },
593− { tag: "Not Visible, network", visible: false, limitedBandwith: false },
594− { tag: "Not Visible, no network", visible: false, limitedBandwith: true }
595− ];
596− }
597−
598− function test_load_images_visibility_network(data) {
599− loader.visible = data.visible;
600− NetworkingStatus.limitedBandwith = data.limitedBandwith;
601−
602− selector.selectedIndex = 0;
603− waitForRendering(selector);
604− waitForRendering(card);
605−
606− verify(data.visible || !data.limitedBandwith || artImage.source == "");
607− }
608 }
586 }
609}
587}
tests/qmltests/Dash/tst_Dash.qml+20−2
@@ -243,8 +243,10 @@ Item {
243 tryCompare(scopesOverviewRepeater, "count", 0);
243 tryCompare(scopesOverviewRepeater, "count", 0);
244
244
245 // Click on a temp scope in the search
245 // Click on a temp scope in the search
246+ tryCompareFunction( function() {
246− var dashCategorysearchA = findChild(searchResultsViewer, "dashCategorysearchA");
247+ return findChild(findChild(searchResultsViewer, "dashCategorysearchA"), "delegate2") != null;
248+ }, true);
247− var cardTempScope = findChild(dashCategorysearchA, "delegate2");
249+ var cardTempScope = findChild(findChild(searchResultsViewer, "dashCategorysearchA"), "delegate2");
248 verify(cardTempScope, "Could not find delegate2");
250 verify(cardTempScope, "Could not find delegate2");
249
251
250 waitForRendering(cardTempScope);
252 waitForRendering(cardTempScope);
@@ -298,6 +300,22 @@ Item {
298 tryCompare(dashContentList, "currentIndex", 1)
300 tryCompare(dashContentList, "currentIndex", 1)
299 }
301 }
300
302
303+ function test_setCurrentScopeClosesPreview() {
304+ var dashContent = findChild(dash, "dashContent");
305+ waitForRendering(dash)
306+
307+ var delegate0 = findChild(dash, "delegate0");
308+ mouseClick(delegate0, delegate0.width / 2, delegate0.height / 2);
309+
310+ tryCompare(dashContent, "subPageShown", true)
311+ waitForRendering(dash);
312+
313+ var dashCommunicatorService = findInvisibleChild(dash, "dashCommunicatorService");
314+ dashCommunicatorService.mockSetCurrentScope(0, true, false);
315+
316+ tryCompare(dashContent, "subPageShown", false)
317+ }
318+
301 function test_processing_indicator() {
319 function test_processing_indicator() {
302 var processingIndicator = findChild(dash, "processingIndicator");
320 var processingIndicator = findChild(dash, "processingIndicator");
303 verify(processingIndicator, "Can't find the processing indicator.");
321 verify(processingIndicator, "Can't find the processing indicator.");
tests/qmltests/Dash/tst_GenericScopeView.qml+3−2
@@ -293,6 +293,7 @@ Item {
293 },
293 },
294 true);
294 true);
295 var tile = findChild(findChild(genericScopeView, "dashCategory"+category), "delegate"+delegate);
295 var tile = findChild(findChild(genericScopeView, "dashCategory"+category), "delegate"+delegate);
296+ waitForRendering(tile);
296 mouseClick(tile, tile.width / 2, tile.height / 2);
297 mouseClick(tile, tile.width / 2, tile.height / 2);
297 tryCompare(testCase.subPageLoader, "open", true);
298 tryCompare(testCase.subPageLoader, "open", true);
298 tryCompare(testCase.subPageLoader, "x", 0);
299 tryCompare(testCase.subPageLoader, "x", 0);
@@ -461,8 +462,8 @@ Item {
461
462
462 var innerHeader = findChild(header, "innerPageHeader");
463 var innerHeader = findChild(header, "innerPageHeader");
463 verify(innerHeader, "Could not find the inner header");
464 verify(innerHeader, "Could not find the inner header");
464− verify(Qt.colorEqual(innerHeader.textColor, data.foreground),
465+ verify(Qt.colorEqual(innerHeader.config.foregroundColor, data.foreground),
465− "Foreground color not equal: %1 != %2".arg(innerHeader.textColor).arg(data.foreground));
466+ "Foreground color not equal: %1 != %2".arg(innerHeader.config.foregroundColor).arg(data.foreground));
466
467
467 var background = findChild(header, "headerBackground");
468 var background = findChild(header, "headerBackground");
468 verify(background, "Could not find the background");
469 verify(background, "Could not find the background");
tests/qmltests/Dash/tst_PreviewListView.qml+2−2
@@ -100,8 +100,8 @@ Rectangle {
100
100
101 var innerHeader = findChild(header, "innerPageHeader");
101 var innerHeader = findChild(header, "innerPageHeader");
102 verify(innerHeader, "Could not find the inner header");
102 verify(innerHeader, "Could not find the inner header");
103− verify(Qt.colorEqual(innerHeader.textColor, UbuntuColors.darkGrey),
103+ verify(Qt.colorEqual(innerHeader.config.foregroundColor, UbuntuColors.darkGrey),
104− "Foreground color not equal: %1 != %2".arg(innerHeader.textColor).arg(UbuntuColors.darkGrey));
104+ "Foreground color not equal: %1 != %2".arg(innerHeader.config.foregroundColor).arg(UbuntuColors.darkGrey));
105
105
106 var background = findChild(header, "headerBackground");
106 var background = findChild(header, "headerBackground");
107 verify(background, "Could not find the background");
107 verify(background, "Could not find the background");
tests/qmltests/Panel/tst_IndicatorsBar.qml+1−1
@@ -136,8 +136,8 @@ IndicatorTest {
136 var dataItem = findChild(indicatorsBar, root.originalModelData[data.index]["identifier"] + "-panelItem");
136 var dataItem = findChild(indicatorsBar, root.originalModelData[data.index]["identifier"] + "-panelItem");
137 verify(dataItem !== null);
137 verify(dataItem !== null);
138
138
139+ waitForRendering(dataItem);
139 var mappedPosition = indicatorsBar.mapFromItem(dataItem, dataItem.width/2, dataItem.height/2);
140 var mappedPosition = indicatorsBar.mapFromItem(dataItem, dataItem.width/2, dataItem.height/2);
140−
141 indicatorsBar.selectItemAt(mappedPosition.x);
141 indicatorsBar.selectItemAt(mappedPosition.x);
142 indicatorsBar.expanded = true;
142 indicatorsBar.expanded = true;
143 wait_for_expansion_to_settle();
143 wait_for_expansion_to_settle();
tests/qmltests/Panel/tst_IndicatorsMenu.qml+3−3
@@ -174,7 +174,7 @@ IndicatorTest {
174 // and expose the header
174 // and expose the header
175 function test_hint() {
175 function test_hint() {
176 var indicatorItem = get_indicator_item(0);
176 var indicatorItem = get_indicator_item(0);
177− var mappedPosition = root.mapFromItem(indicatorItem, indicatorItem.width/2, indicatorItem.height/2);
177+ var mappedPosition = indicatorsMenu.mapFromItem(indicatorItem, indicatorItem.width/2, indicatorItem.height/2);
178
178
179 touchPress(indicatorsMenu, mappedPosition.x, indicatorsMenu.minimizedPanelHeight / 2);
179 touchPress(indicatorsMenu, mappedPosition.x, indicatorsMenu.minimizedPanelHeight / 2);
180
180
@@ -195,7 +195,7 @@ IndicatorTest {
195 for (var i = 0; i < root.originalModelData.length; i++) {
195 for (var i = 0; i < root.originalModelData.length; i++) {
196 var indicatorItem = get_indicator_item(i);
196 var indicatorItem = get_indicator_item(i);
197
197
198− var mappedPosition = root.mapFromItem(indicatorItem, indicatorItem.width/2, indicatorItem.height/2);
198+ var mappedPosition = indicatorsMenu.mapFromItem(indicatorItem, indicatorItem.width/2, indicatorItem.height/2);
199
199
200 touchFlick(indicatorsMenu,
200 touchFlick(indicatorsMenu,
201 mappedPosition.x, mappedPosition.y,
201 mappedPosition.x, mappedPosition.y,
@@ -227,7 +227,7 @@ IndicatorTest {
227
227
228 // Get the first indicator
228 // Get the first indicator
229 var firstItem = get_indicator_item(0);
229 var firstItem = get_indicator_item(0);
230− var firstItemMappedPosition = root.mapFromItem(firstItem, firstItem.width/2, firstItem.height/2);
230+ var firstItemMappedPosition = indicatorsMenu.mapFromItem(firstItem, firstItem.width/2, firstItem.height/2);
231
231
232 // 1) Drag the mouse down to hint a bit
232 // 1) Drag the mouse down to hint a bit
233 touchFlick(indicatorsMenu,
233 touchFlick(indicatorsMenu,
tests/qmltests/Panel/tst_Panel.qml+5−10
@@ -186,11 +186,10 @@ IndicatorTest {
186 for (var i = 0; i < root.originalModelData.length; i++) {
186 for (var i = 0; i < root.originalModelData.length; i++) {
187 var indicatorItem = get_indicator_item(i);
187 var indicatorItem = get_indicator_item(i);
188
188
189− var startXPosition = root.mapFromItem(indicatorItem, indicatorItem.width / 2, 0).x;
189+ var startXPosition = panel.mapFromItem(indicatorItem, indicatorItem.width / 2, 0).x;
190− var startYPosition = root.mapFromItem(panel, 0, 0).y;
191
190
192 touchFlick(panel,
191 touchFlick(panel,
193− startXPosition, startYPosition,
192+ startXPosition, 0,
194 startXPosition, panel.height,
193 startXPosition, panel.height,
195 true /* beginTouch */, false /* endTouch */, units.gu(5), 15);
194 true /* beginTouch */, false /* endTouch */, units.gu(5), 15);
196
195
@@ -244,17 +243,13 @@ IndicatorTest {
244 // (switches between normal and fullscreen modes are animated)
243 // (switches between normal and fullscreen modes are animated)
245 tryCompareFunction(function() { return indicatorArea.y }, data.indicatorY);
244 tryCompareFunction(function() { return indicatorArea.y }, data.indicatorY);
246
245
247− var startXPosition = root.mapFromItem(panel.indicators, panel.indicators.width / 2, 0).x;
248− var startYPosition = root.mapFromItem(panel, 0, panel.height).y;
249−
250 panel.indicators.show();
246 panel.indicators.show();
251 tryCompare(panel.indicators.showAnimation, "running", false);
247 tryCompare(panel.indicators.showAnimation, "running", false);
252 tryCompare(panel.indicators, "unitProgress", 1);
248 tryCompare(panel.indicators, "unitProgress", 1);
253
249
254−
255 touchFlick(panel.indicators,
250 touchFlick(panel.indicators,
256− startXPosition, startYPosition,
251+ panel.indicators.width / 2, panel.height,
257− startXPosition, 0,
252+ panel.indicators.width / 2, 0,
258 true /* beginTouch */, false /* endTouch */, units.gu(5), 15);
253 true /* beginTouch */, false /* endTouch */, units.gu(5), 15);
259
254
260 // Indicators height should follow the drag, and therefore increase accordingly.
255 // Indicators height should follow the drag, and therefore increase accordingly.
@@ -263,7 +258,7 @@ IndicatorTest {
263 function() {return panel.indicators.height <= panel.height * 0.5},
258 function() {return panel.indicators.height <= panel.height * 0.5},
264 true);
259 true);
265
260
266− touchRelease(panel.indicators, startXPosition, 0);
261+ touchRelease(panel.indicators, panel.indicators.width / 2, 0);
267
262
268 tryCompare(panel.indicators.hideAnimation, "running", true);
263 tryCompare(panel.indicators.hideAnimation, "running", true);
269 tryCompare(panel.indicators.hideAnimation, "running", false);
264 tryCompare(panel.indicators.hideAnimation, "running", false);
tests/qmltests/Stages/tst_PhoneStage.qml+41−6
@@ -128,17 +128,20 @@ Item {
128 var startX = phoneStage.width - 2;
128 var startX = phoneStage.width - 2;
129 var startY = phoneStage.height / 2;
129 var startY = phoneStage.height / 2;
130 var endY = startY;
130 var endY = startY;
131− var endX = units.gu(2);
131+ var endX = phoneStage.width / 2;
132
132
133 touchFlick(phoneStage, startX, startY, endX, endY,
133 touchFlick(phoneStage, startX, startY, endX, endY,
134 true /* beginTouch */, true /* endTouch */, units.gu(10), 50);
134 true /* beginTouch */, true /* endTouch */, units.gu(10), 50);
135+
136+ tryCompare(spreadView, "phase", 2);
137+ waitForRendering(phoneStage);
135 }
138 }
136
139
137 function test_shortFlick() {
140 function test_shortFlick() {
138 addApps(2)
141 addApps(2)
139 var startX = phoneStage.width - units.gu(1);
142 var startX = phoneStage.width - units.gu(1);
140 var startY = phoneStage.height / 2;
143 var startY = phoneStage.height / 2;
141− var endX = phoneStage.width / 2;
144+ var endX = startX - units.gu(4);
142 var endY = startY;
145 var endY = startY;
143
146
144 var activeApp = ApplicationManager.get(0);
147 var activeApp = ApplicationManager.get(0);
@@ -164,7 +167,7 @@ Item {
164 {tag: "<position2 (non-linear)", positionMarker: "positionMarker2", linear: false, offset: -1, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
167 {tag: "<position2 (non-linear)", positionMarker: "positionMarker2", linear: false, offset: -1, endPhase: 0, targetPhase: 0, newFocusedIndex: 1 },
165 {tag: ">position2", positionMarker: "positionMarker2", linear: true, offset: +1, endPhase: 1, targetPhase: 0, newFocusedIndex: 1 },
168 {tag: ">position2", positionMarker: "positionMarker2", linear: true, offset: +1, endPhase: 1, targetPhase: 0, newFocusedIndex: 1 },
166 {tag: "<position3", positionMarker: "positionMarker3", linear: true, offset: -1, endPhase: 1, targetPhase: 0, newFocusedIndex: 1 },
169 {tag: "<position3", positionMarker: "positionMarker3", linear: true, offset: -1, endPhase: 1, targetPhase: 0, newFocusedIndex: 1 },
167− {tag: ">position3", positionMarker: "positionMarker3", linear: true, offset: +1, endPhase: 2, targetPhase: 2, newFocusedIndex: 2 },
170+ {tag: ">position3", positionMarker: "positionMarker3", linear: true, offset: +1, endPhase: 1, targetPhase: 2, newFocusedIndex: 2 },
168 ];
171 ];
169 }
172 }
170
173
@@ -274,7 +277,7 @@ Item {
274 compare(ApplicationManager.focusedApplicationId, selectedApp.appId);
277 compare(ApplicationManager.focusedApplicationId, selectedApp.appId);
275 }
278 }
276
279
277− function test_orientation_change_sent_to_focused_app() {
280+ function test_orientationChangeSentToFocusedApp() {
278 phoneStage.orientation = Qt.PortraitOrientation;
281 phoneStage.orientation = Qt.PortraitOrientation;
279 addApps(1);
282 addApps(1);
280
283
@@ -286,7 +289,7 @@ Item {
286 tryCompare(app, "orientation", Qt.LandscapeOrientation);
289 tryCompare(app, "orientation", Qt.LandscapeOrientation);
287 }
290 }
288
291
289− function test_orientation_change_not_sent_to_apps_while_spread_open() {
292+ function test_orientationChangeNotSentToAppsWhileSpreadOpen() {
290 phoneStage.orientation = Qt.PortraitOrientation;
293 phoneStage.orientation = Qt.PortraitOrientation;
291 addApps(1);
294 addApps(1);
292
295
@@ -299,7 +302,7 @@ Item {
299 tryCompare(app, "orientation", Qt.PortraitOrientation);
302 tryCompare(app, "orientation", Qt.PortraitOrientation);
300 }
303 }
301
304
302− function test_orientation_change_not_sent_to_unfocused_app_until_it_focused() {
305+ function test_orientationChangeNotSentToUnfocusedAppUntilItFocused() {
303 phoneStage.orientation = Qt.PortraitOrientation;
306 phoneStage.orientation = Qt.PortraitOrientation;
304 addApps(1);
307 addApps(1);
305
308
@@ -357,5 +360,37 @@ Item {
357 tryCompare(firstApp, "state", ApplicationInfoInterface.Running);
360 tryCompare(firstApp, "state", ApplicationInfoInterface.Running);
358 tryCompare(firstApp, "focused", true);
361 tryCompare(firstApp, "focused", true);
359 }
362 }
363+
364+ function test_cantCloseWhileSnapping() {
365+ addApps(2);
366+
367+ goToSpread();
368+
369+ var spreadView = findChild(phoneStage, "spreadView");
370+ var selectedApp = ApplicationManager.get(2);
371+
372+ goToSpread();
373+
374+ var app0 = findChild(spreadView, "appDelegate0");
375+ var app1 = findChild(spreadView, "appDelegate1");
376+ var app2 = findChild(spreadView, "appDelegate2");
377+
378+ var dragArea0 = findChild(app0, "dragArea");
379+ var dragArea1 = findChild(app1, "dragArea");
380+ var dragArea2 = findChild(app2, "dragArea");
381+
382+ compare(dragArea0.enabled, true);
383+ compare(dragArea1.enabled, true);
384+ compare(dragArea2.enabled, true);
385+
386+ phoneStage.select(selectedApp.appId);
387+
388+ // Make sure all drag areas are disabled instantly. Don't use tryCompare here!
389+ compare(dragArea0.enabled, false);
390+ compare(dragArea1.enabled, false);
391+ compare(dragArea2.enabled, false);
392+
393+ tryCompare(spreadView, "contentX", -spreadView.shift)
394+ }
360 }
395 }
361}
396}
tests/qmltests/Stages/tst_SpreadDelegate.qml+11−5
@@ -110,7 +110,7 @@ Rectangle {
110 {tag: "swipeToClose=true closeable=true -> appWindow moves away",
110 {tag: "swipeToClose=true closeable=true -> appWindow moves away",
111 swipeToClose: true, closeable: true },
111 swipeToClose: true, closeable: true },
112
112
113− {tag: "swipeToClose=true closeable=alse -> appWindow bounces back",
113+ {tag: "swipeToClose=true closeable=false -> appWindow bounces back",
114 swipeToClose: true, closeable: false },
114 swipeToClose: true, closeable: false },
115
115
116 {tag: "swipeToClose=false -> appWindow stays put",
116 {tag: "swipeToClose=false -> appWindow stays put",
@@ -126,18 +126,24 @@ Rectangle {
126 swipeToCloseCheckbox.checked = data.swipeToClose;
126 swipeToCloseCheckbox.checked = data.swipeToClose;
127 closeableCheckbox.checked = data.closeable;
127 closeableCheckbox.checked = data.closeable;
128
128
129− var dragDistance = spreadDelegateLoader.item.height / 2;
129+ var dragDistance = spreadDelegateLoader.item.height * 0.8;
130 var touchX = spreadDelegateLoader.item.width / 2;
130 var touchX = spreadDelegateLoader.item.width / 2;
131− var fromY = spreadDelegateLoader.item.height / 2;
131+ var fromY = spreadDelegateLoader.item.height * 0.9;
132 var toY = fromY - dragDistance;
132 var toY = fromY - dragDistance;
133 touchFlick(spreadDelegateLoader.item,
133 touchFlick(spreadDelegateLoader.item,
134 touchX /* fromX */, fromY, touchX /* toX */, toY,
134 touchX /* fromX */, fromY, touchX /* toX */, toY,
135 true /* beginTouch */, false /* endTouch */, dragArea.minSpeedToClose * 1.1 /* speed */);
135 true /* beginTouch */, false /* endTouch */, dragArea.minSpeedToClose * 1.1 /* speed */);
136
136
137−
138 if (data.swipeToClose) {
137 if (data.swipeToClose) {
139 verify(appWindowWithShadow.y < 0);
138 verify(appWindowWithShadow.y < 0);
139+ var threshold = findChild(spreadDelegateLoader.item, "dragArea").threshold
140+ if (data.closeable) {
141+ // Verify that the delegate started moving exactly "threshold" after the finger movement
142+ // and did not jump up to the finger, but lags the threshold behind
143+ compare(Math.abs(Math.abs(appWindowWithShadow.y) - dragDistance), threshold);
144+ } else {
140− verify(Math.abs(Math.abs(appWindowWithShadow.y) - dragDistance) < units.gu(1));
145+ verify(Math.abs(Math.abs(appWindowWithShadow.y) - dragDistance) > threshold);
146+ }
141
147
142 touchRelease(spreadDelegateLoader.item, touchX, toY - units.gu(1));
148 touchRelease(spreadDelegateLoader.item, touchX, toY - units.gu(1));
143
149
tests/qmltests/tst_Shell.qml+2−2
@@ -502,8 +502,8 @@ Item {
502 // Swipe upwards over the launcher to ensure that this icon
502 // Swipe upwards over the launcher to ensure that this icon
503 // at the bottom is not folded and faded away.
503 // at the bottom is not folded and faded away.
504 var touchStartX = launcherPanel.width / 2;
504 var touchStartX = launcherPanel.width / 2;
505− var touchStartY = launcherPanel.height / 2;
505+ var touchEndY = launcherPanel.height / 2;
506− touchFlick(launcherPanel, touchStartX, touchStartY, touchStartX, 0);
506+ touchFlick(launcherPanel, touchStartX, 0, touchStartX, touchEndY);
507 tryCompare(launcherPanel, "moving", false);
507 tryCompare(launcherPanel, "moving", false);
508
508
509 tap(appIcon, appIcon.width / 2, appIcon.height / 2);
509 tap(appIcon, appIcon.width / 2, appIcon.height / 2);
tests/utils/modules/Unity/Test/UnityTestCase.qml+11−7
@@ -221,6 +221,10 @@ TestCase {
221 // Make sure the item is rendered
221 // Make sure the item is rendered
222 waitForRendering(item);
222 waitForRendering(item);
223
223
224+ var root = fetchRootItem(item);
225+ var rootFrom = item.mapToItem(root, x, y);
226+ var rootTo = item.mapToItem(root, toX, toY);
227+
224 // Default to true for beginTouch if not present
228 // Default to true for beginTouch if not present
225 beginTouch = (beginTouch !== undefined) ? beginTouch : true
229 beginTouch = (beginTouch !== undefined) ? beginTouch : true
226
230
@@ -233,17 +237,17 @@ TestCase {
233 // Set a default iterations if not specified
237 // Set a default iterations if not specified
234 var iterations = (iterations !== undefined) ? iterations : 5
238 var iterations = (iterations !== undefined) ? iterations : 5
235
239
236− var distance = Math.sqrt(Math.pow(toX - x, 2) + Math.pow(toY - y, 2))
240+ var distance = Math.sqrt(Math.pow(rootTo.x - rootFrom.x, 2) + Math.pow(rootTo.Y - rootFrom.y, 2))
237 var totalTime = (distance / speed) * 1000 /* converting speed to pixels/ms */
241 var totalTime = (distance / speed) * 1000 /* converting speed to pixels/ms */
238
242
239 var timeStep = totalTime / iterations
243 var timeStep = totalTime / iterations
240− var diffX = (toX - x) / iterations
244+ var diffX = (rootTo.x - rootFrom.x) / iterations
241− var diffY = (toY - y) / iterations
245+ var diffY = (rootTo.y - rootFrom.y) / iterations
242 if (beginTouch) {
246 if (beginTouch) {
243 fakeDateTime.currentTimeMs += timeStep
247 fakeDateTime.currentTimeMs += timeStep
244
248
245 var event = touchEvent()
249 var event = touchEvent()
246− event.press(0 /* touchId */, x, y)
250+ event.press(0 /* touchId */, rootFrom.x, rootFrom.y)
247 event.commit()
251 event.commit()
248 }
252 }
249 for (var i = 0; i < iterations; ++i) {
253 for (var i = 0; i < iterations; ++i) {
@@ -253,19 +257,19 @@ TestCase {
253 // the point specified
257 // the point specified
254 wait(iterations / speed)
258 wait(iterations / speed)
255 var event = touchEvent()
259 var event = touchEvent()
256− event.move(0 /* touchId */, toX, toY)
260+ event.move(0 /* touchId */, rootTo.x, rootTo.y)
257 event.commit()
261 event.commit()
258 } else {
262 } else {
259 wait(iterations / speed)
263 wait(iterations / speed)
260 var event = touchEvent()
264 var event = touchEvent()
261− event.move(0 /* touchId */, x + (i + 1) * diffX, y + (i + 1) * diffY)
265+ event.move(0 /* touchId */, rootFrom.x + (i + 1) * diffX, rootFrom.y + (i + 1) * diffY)
262 event.commit()
266 event.commit()
263 }
267 }
264 }
268 }
265 if (endTouch) {
269 if (endTouch) {
266 fakeDateTime.currentTimeMs += timeStep
270 fakeDateTime.currentTimeMs += timeStep
267 var event = touchEvent()
271 var event = touchEvent()
268− event.release(0 /* touchId */, toX, toY)
272+ event.release(0 /* touchId */, rootTo.x, rootTo.y)
269 event.commit()
273 event.commit()
270 }
274 }
271 }
275 }