on xenial help sign in
Merge trunk
fb99a0fAlbert Astals
CMakeLists.txt+2−2
@@ -64,13 +64,13 @@ if(SHELL_INSTALL_QML STREQUAL "")
64 message(FATAL_ERROR "Could not determine plugin installation dir.")
64 message(FATAL_ERROR "Could not determine plugin installation dir.")
65endif()
65endif()
66
66
67+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden")
68+
67execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} --variable=plugindir unity-shell-api OUTPUT_VARIABLE SHELL_PLUGINDIR OUTPUT_STRIP_TRAILING_WHITESPACE)
69execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} --variable=plugindir unity-shell-api OUTPUT_VARIABLE SHELL_PLUGINDIR OUTPUT_STRIP_TRAILING_WHITESPACE)
68if(SHELL_PLUGINDIR STREQUAL "")
70if(SHELL_PLUGINDIR STREQUAL "")
69 message(FATAL_ERROR "Could not determine plugin import dir.")
71 message(FATAL_ERROR "Could not determine plugin import dir.")
70endif()
72endif()
71
73
72−set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden")
73−
74set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-permissive -pedantic -Wall -Wextra")
74set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-permissive -pedantic -Wall -Wextra")
75
75
76if ("${CMAKE_BUILD_TYPE}" STREQUAL "release" OR "${CMAKE_BUILD_TYPE}" STREQUAL "relwithdebinfo")
76if ("${CMAKE_BUILD_TYPE}" STREQUAL "release" OR "${CMAKE_BUILD_TYPE}" STREQUAL "relwithdebinfo")
build.sh+1
@@ -51,6 +51,7 @@ install_dependencies() {
51 qtdeclarative5-ubuntu-settings-components \
51 qtdeclarative5-ubuntu-settings-components \
52 qtdeclarative5-ubuntu-telephony0.1 \
52 qtdeclarative5-ubuntu-telephony0.1 \
53 qtdeclarative5-ubuntu-thumbnailer0.1 \
53 qtdeclarative5-ubuntu-thumbnailer0.1 \
54+ qml-module-ubuntu-connectivity \
54 unity-notifications-impl \
55 unity-notifications-impl \
55 ubuntu-mobile-icons \
56 ubuntu-mobile-icons \
56 unity-scope-home \
57 unity-scope-home \
cmake/modules/QmlTest.cmake+2−1
@@ -151,7 +151,7 @@ macro(add_qml_test_internal SUBPATH COMPONENT_NAME ITERATIONS)
151 add_manual_qml_test(${SUBPATH} ${COMPONENT_NAME} ${ARGN})
151 add_manual_qml_test(${SUBPATH} ${COMPONENT_NAME} ${ARGN})
152endmacro(add_qml_test_internal)
152endmacro(add_qml_test_internal)
153
153
154−macro(add_binary_qml_test CLASS_NAME LD_PATH DEPS)
154+macro(add_binary_qml_test CLASS_NAME LD_PATH DEPS ENVVAR)
155 set(testCommand
155 set(testCommand
156 LD_LIBRARY_PATH=${LD_PATH}
156 LD_LIBRARY_PATH=${LD_PATH}
157 ${CMAKE_CURRENT_BINARY_DIR}/${CLASS_NAME}TestExec
157 ${CMAKE_CURRENT_BINARY_DIR}/${CLASS_NAME}TestExec
@@ -168,6 +168,7 @@ macro(add_binary_qml_test CLASS_NAME LD_PATH DEPS)
168 endif()
168 endif()
169 set(xvfbtestCommand
169 set(xvfbtestCommand
170 ${LD_PRELOAD_PATH}
170 ${LD_PRELOAD_PATH}
171+ ${ENVVAR}
171 LD_LIBRARY_PATH=${LD_PATH}
172 LD_LIBRARY_PATH=${LD_PATH}
172 xvfb-run --server-args "-screen 0 1024x768x24" --auto-servernum
173 xvfb-run --server-args "-screen 0 1024x768x24" --auto-servernum
173 ${CMAKE_CURRENT_BINARY_DIR}/${CLASS_NAME}TestExec
174 ${CMAKE_CURRENT_BINARY_DIR}/${CLASS_NAME}TestExec
debian/changelog+40
@@ -1,3 +1,43 @@
1+unity8 (8.00+14.10.20141006-0ubuntu1) utopic; urgency=low
2+
3+ [ Andrea Cimitan ]
4+ * Tweak card header to match the spec
5+ * Add preview image slideshow (LP: #1351537)
6+
7+ [ Michał Sawicz ]
8+ * Cache more things in memory, so flicking scopes should be faster
9+ (LP: #1336724)
10+ * Tweak card header to match the spec
11+ * Save texture memory by limiting sourceSize (LP: #1338430)
12+
13+ [ Ying-Chun Liu ]
14+ * Add attributes to Preview. (LP: #1282460)
15+
16+ [ Albert Astals ]
17+ * Update pot
18+ * Cache more things in memory, so flicking scopes should be faster
19+ (LP: #1336724)
20+ * Save texture memory by limiting sourceSize (LP: #1338430)
21+ * Clip the settings list
22+ * Fix unlocking from the left again
23+ * Add wait_ makes tests more reliable
24+
25+ [ Michael Zanetti ]
26+ * fix fading out the launcher instead of sliding it out on left-edge
27+ minimizing an app.
28+ * Make the DashCommunicator async and more flexible to handle a
29+ lifecycle-suspended dash (LP: #1339883)
30+
31+ [ Michael Terry ]
32+ * Retry unlock-device script if it fails, as there is always a risk of
33+ a small race with boot-up. (LP: #1370644)
34+ * Add pull-to-refresh functionality to scopes. (LP: #1368336)
35+
36+ [ CI bot ]
37+ * Resync trunk
38+
39+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 06 Oct 2014 08:03:23 +0000
40+
1unity8 (8.00+14.10.20140930.2-0ubuntu1) utopic; urgency=low
41unity8 (8.00+14.10.20140930.2-0ubuntu1) utopic; urgency=low
2
42
3 [ Alexandros Frantzis ]
43 [ Alexandros Frantzis ]
debian/control+2−1
@@ -25,7 +25,7 @@ Build-Depends: cmake,
25 libpulse-dev,
25 libpulse-dev,
26 libqmenumodel-dev (>= 0.2.8),
26 libqmenumodel-dev (>= 0.2.8),
27 libqt5xmlpatterns5-dev,
27 libqt5xmlpatterns5-dev,
28− libunity-api-dev (>= 7.91),
28+ libunity-api-dev (>= 7.92),
29 libusermetricsoutput1-dev,
29 libusermetricsoutput1-dev,
30 libxcb1-dev,
30 libxcb1-dev,
31 pkg-config,
31 pkg-config,
@@ -174,6 +174,7 @@ Depends: accountsservice-ubuntu-schemas (>= 0.0.3),
174 libhardware2,
174 libhardware2,
175 unity-schemas (>= 7.3.1+14.10.20140915),
175 unity-schemas (>= 7.3.1+14.10.20140915),
176 pay-service,
176 pay-service,
177+ qml-module-ubuntu-connectivity,
177 ${misc:Depends},
178 ${misc:Depends},
178 ${shlibs:Depends},
179 ${shlibs:Depends},
179Provides: unity-launcher-impl,
180Provides: unity-launcher-impl,
debian/unity8-private.install+1
@@ -1,3 +1,4 @@
1+usr/lib/*/libunity8-private.*
1usr/lib/*/unity8/qml/AccountsService
2usr/lib/*/unity8/qml/AccountsService
2usr/lib/*/unity8/qml/Dash
3usr/lib/*/unity8/qml/Dash
3usr/lib/*/unity8/qml/HudClient
4usr/lib/*/unity8/qml/HudClient
plugins/Dash/CardAttributes.qml+1−1
@@ -59,7 +59,7 @@ GridLayout {
59 text: "value" in modelData && modelData["value"] || "";
59 text: "value" in modelData && modelData["value"] || "";
60 elide: Text.ElideRight
60 elide: Text.ElideRight
61 maximumLineCount: 1
61 maximumLineCount: 1
62− font.weight: "style" in modelData && modelData["style"] == "highlighted" ? Font.DemiBold : Font.Light
62+ font.weight: "style" in modelData && modelData["style"] === "highlighted" ? Font.Bold : Font.Light
63 fontSize: "small"
63 fontSize: "small"
64 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale)
64 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale)
65 color: grid.color
65 color: grid.color
plugins/Dash/CardCreator.js+15−16
@@ -82,12 +82,13 @@ 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: Image { \n\
85+ image: CroppedImageMinimumSourceSize { \n\
86 objectName: "artImage"; \n\
86 objectName: "artImage"; \n\
87+ property bool doLoadSource: !NetworkingStatus.limitedBandwith; \n\
87− source: cardData && cardData["art"] || ""; \n\
88+ source: { if (root.visible) doLoadSource = true; return doLoadSource && cardData && cardData["art"] || ""; } \n\
88 cache: true; \n\
89 cache: true; \n\
89 asynchronous: root.asynchronous; \n\
90 asynchronous: root.asynchronous; \n\
90− fillMode: Image.PreserveAspectCrop; \n\
91+ visible: false; \n\
91 width: %2; \n\
92 width: %2; \n\
92 height: %3; \n\
93 height: %3; \n\
93 } \n\
94 } \n\
@@ -190,16 +191,14 @@ var kMascotShapeLoaderCode = 'Loader { \n\
190
191
191// %1 is used as anchors of mascotImage
192// %1 is used as anchors of mascotImage
192// %2 is used as visible of mascotImage
193// %2 is used as visible of mascotImage
193−var kMascotImageCode = 'Image { \n\
194+var kMascotImageCode = 'CroppedImageMinimumSourceSize { \n\
194 id: mascotImage; \n\
195 id: mascotImage; \n\
195 objectName: "mascotImage"; \n\
196 objectName: "mascotImage"; \n\
196 anchors { %1 } \n\
197 anchors { %1 } \n\
197− readonly property int maxSize: Math.max(width, height) * 4; \n\
198+ property bool doLoadSource: !NetworkingStatus.limitedBandwith; \n\
198− source: cardData && cardData["mascot"] || ""; \n\
199+ source: { if (root.visible) doLoadSource = true; return doLoadSource && cardData && cardData["mascot"] || ""; } \n\
199 width: units.gu(6); \n\
200 width: units.gu(6); \n\
200 height: units.gu(5.625); \n\
201 height: units.gu(5.625); \n\
201− sourceSize { width: maxSize; height: maxSize } \n\
202− fillMode: Image.PreserveAspectCrop; \n\
203 horizontalAlignment: Image.AlignHCenter; \n\
202 horizontalAlignment: Image.AlignHCenter; \n\
204 verticalAlignment: Image.AlignVCenter; \n\
203 verticalAlignment: Image.AlignVCenter; \n\
205 visible: %2; \n\
204 visible: %2; \n\
@@ -220,13 +219,13 @@ var kTitleLabelCode = 'Label { \n\
220 color: %2; \n\
219 color: %2; \n\
221 visible: showHeader %3; \n\
220 visible: showHeader %3; \n\
222 text: root.title; \n\
221 text: root.title; \n\
223− font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal; \n\
222+ font.weight: cardData && cardData["subtitle"] ? Font.DemiBold : Font.Normal; \n\
224− horizontalAlignment: root.headerAlignment; \n\
223+ horizontalAlignment: root.titleAlignment; \n\
225 }\n';
224 }\n';
226
225
227// %1 is used as extra anchors of emblemIcon
226// %1 is used as extra anchors of emblemIcon
228// %2 is used as color of emblemIcon
227// %2 is used as color of emblemIcon
229−var kEmblemIconCode = 'Icon { \n\
228+var kEmblemIconCode = 'StatusIcon { \n\
230 id: emblemIcon; \n\
229 id: emblemIcon; \n\
231 objectName: "emblemIcon"; \n\
230 objectName: "emblemIcon"; \n\
232 anchors { \n\
231 anchors { \n\
@@ -236,7 +235,6 @@ var kEmblemIconCode = 'Icon { \n\
236 } \n\
235 } \n\
237 source: cardData && cardData["emblem"] || ""; \n\
236 source: cardData && cardData["emblem"] || ""; \n\
238 color: %2; \n\
237 color: %2; \n\
239− width: height; \n\
240 height: source != "" ? titleLabel.font.pixelSize : 0; \n\
238 height: source != "" ? titleLabel.font.pixelSize : 0; \n\
241 }\n';
239 }\n';
242
240
@@ -258,13 +256,12 @@ var kSubtitleLabelCode = 'Label { \n\
258 anchors { %1 } \n\
256 anchors { %1 } \n\
259 anchors.topMargin: units.dp(2); \n\
257 anchors.topMargin: units.dp(2); \n\
260 elide: Text.ElideRight; \n\
258 elide: Text.ElideRight; \n\
261− fontSize: "small"; \n\
259+ fontSize: "x-small"; \n\
262 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale); \n\
260 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale); \n\
263 color: %2; \n\
261 color: %2; \n\
264 visible: titleLabel.visible && titleLabel.text; \n\
262 visible: titleLabel.visible && titleLabel.text; \n\
265 text: cardData && cardData["subtitle"] || ""; \n\
263 text: cardData && cardData["subtitle"] || ""; \n\
266 font.weight: Font.Light; \n\
264 font.weight: Font.Light; \n\
267− horizontalAlignment: root.headerAlignment; \n\
268 }\n';
265 }\n';
269
266
270// %1 is used as anchors of attributesRow
267// %1 is used as anchors of attributesRow
@@ -310,7 +307,7 @@ function cardString(template, components) {
310 property var artShapeBorderSource: undefined; \n\
307 property var artShapeBorderSource: undefined; \n\
311 property real fontScale: 1.0; \n\
308 property real fontScale: 1.0; \n\
312 property var scopeStyle: null; \n\
309 property var scopeStyle: null; \n\
313− property int headerAlignment: Text.AlignLeft; \n\
310+ property int titleAlignment: Text.AlignLeft; \n\
314 property int fixedHeaderHeight: -1; \n\
311 property int fixedHeaderHeight: -1; \n\
315 property size fixedArtShapeSize: Qt.size(-1, -1); \n\
312 property size fixedArtShapeSize: Qt.size(-1, -1); \n\
316 readonly property string title: cardData && cardData["title"] || ""; \n\
313 readonly property string title: cardData && cardData["title"] || ""; \n\
@@ -436,7 +433,7 @@ function cardString(template, components) {
436 mascotShapeCode = kMascotShapeLoaderCode.arg(mascotAnchors);
433 mascotShapeCode = kMascotShapeLoaderCode.arg(mascotAnchors);
437 }
434 }
438
435
439− var mascotImageVisible = useMascotShape ? 'false' : 'showHeader';
436+ var mascotImageVisible = useMascotShape ? 'false' : 'showHeader && resized';
440 mascotCode = kMascotImageCode.arg(mascotAnchors).arg(mascotImageVisible);
437 mascotCode = kMascotImageCode.arg(mascotAnchors).arg(mascotImageVisible);
441 }
438 }
442
439
@@ -655,6 +652,8 @@ function cardString(template, components) {
655function createCardComponent(parent, template, components) {
652function createCardComponent(parent, template, components) {
656 var imports = 'import QtQuick 2.2; \n\
653 var imports = 'import QtQuick 2.2; \n\
657 import Ubuntu.Components 1.1; \n\
654 import Ubuntu.Components 1.1; \n\
655+ import Ubuntu.Settings.Components 0.1; \n\
656+ import Ubuntu.Connectivity 1.0; \n\
658 import Dash 0.1;\n\
657 import Dash 0.1;\n\
659 import Utils 0.1;\n';
658 import Utils 0.1;\n';
660 var card = cardString(template, components);
659 var card = cardString(template, components);
plugins/Dash/CroppedImageMinimumSourceSize.qmlnew file+38
@@ -0,0 +1,38 @@
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+
19+Image {
20+ property bool resized: false
21+ property bool resizing: false
22+ fillMode: Image.PreserveAspectCrop
23+ visible: resized
24+ onSourceSizeChanged: {
25+ if (!resized && !resizing) {
26+ resizing = true;
27+ var ar = width / height;
28+ var ssar = sourceSize.width / sourceSize.height;
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+ }
38+}
plugins/Dash/listviewwithpageheader.cpp+18−7
@@ -104,8 +104,6 @@
104#pragma GCC diagnostic pop
104#pragma GCC diagnostic pop
105// #include <private/qquickrectangle_p.h>
105// #include <private/qquickrectangle_p.h>
106
106
107−static const qreal bufferRatio = 0.5;
108−
109qreal ListViewWithPageHeader::ListItem::height() const
107qreal ListViewWithPageHeader::ListItem::height() const
110{
108{
111 return m_item->height() + (m_sectionItem ? m_sectionItem->height() : 0);
109 return m_item->height() + (m_sectionItem ? m_sectionItem->height() : 0);
@@ -158,6 +156,7 @@ ListViewWithPageHeader::ListViewWithPageHeader()
158 , m_forceNoClip(false)
156 , m_forceNoClip(false)
159 , m_inLayout(false)
157 , m_inLayout(false)
160 , m_inContentHeightKeepHeaderShown(false)
158 , m_inContentHeightKeepHeaderShown(false)
159+ , m_cacheBuffer(0)
161{
160{
162 m_clipItem = new QQuickItem(contentItem());
161 m_clipItem = new QQuickItem(contentItem());
163// m_clipItem = new QQuickRectangle(contentItem());
162// m_clipItem = new QQuickRectangle(contentItem());
@@ -335,6 +334,20 @@ qreal ListViewWithPageHeader::headerItemShownHeight() const
335 return m_headerItemShownHeight;
334 return m_headerItemShownHeight;
336}
335}
337
336
337+qreal ListViewWithPageHeader::cacheBuffer() const
338+{
339+ return m_cacheBuffer;
340+}
341+
342+void ListViewWithPageHeader::setCacheBuffer(qreal cacheBuffer)
343+{
344+ if (cacheBuffer != m_cacheBuffer) {
345+ m_cacheBuffer = cacheBuffer;
346+ Q_EMIT cacheBufferChanged();
347+ polish();
348+ }
349+}
350+
338void ListViewWithPageHeader::positionAtBeginning()
351void ListViewWithPageHeader::positionAtBeginning()
339{
352{
340 if (m_delegateModel->count() <= 0)
353 if (m_delegateModel->count() <= 0)
@@ -356,8 +369,7 @@ void ListViewWithPageHeader::positionAtBeginning()
356 // Create the subsequent items
369 // Create the subsequent items
357 int modelIndex = 1;
370 int modelIndex = 1;
358 qreal pos = item->y() + item->height();
371 qreal pos = item->y() + item->height();
359− const qreal buffer = height() * bufferRatio;
360− const qreal bufferTo = height() + buffer;
372+ const qreal bufferTo = height() + m_cacheBuffer;
361 while (modelIndex < m_delegateModel->count() && pos <= bufferTo) {
373 while (modelIndex < m_delegateModel->count() && pos <= bufferTo) {
362 if (!(item = createItem(modelIndex, false)))
374 if (!(item = createItem(modelIndex, false)))
363 break;
375 break;
@@ -603,11 +615,10 @@ void ListViewWithPageHeader::refill()
603 return;
615 return;
604 }
616 }
605
617
606− const qreal buffer = height() * bufferRatio;
607 const qreal from = contentY();
618 const qreal from = contentY();
608 const qreal to = from + height();
619 const qreal to = from + height();
609− const qreal bufferFrom = from - buffer;
620+ const qreal bufferFrom = from - m_cacheBuffer;
610− const qreal bufferTo = to + buffer;
621+ const qreal bufferTo = to + m_cacheBuffer;
611
622
612 bool added = addVisibleItems(from, to, false);
623 bool added = addVisibleItems(from, to, false);
613 bool removed = removeNonVisibleItems(bufferFrom, bufferTo);
624 bool removed = removeNonVisibleItems(bufferFrom, bufferTo);
plugins/Dash/listviewwithpageheader.h+6
@@ -54,6 +54,7 @@ class ListViewWithPageHeader : public QQuickFlickable, public QQuickItemChangeLi
54 Q_PROPERTY(bool forceNoClip READ forceNoClip WRITE setForceNoClip NOTIFY forceNoClipChanged)
54 Q_PROPERTY(bool forceNoClip READ forceNoClip WRITE setForceNoClip NOTIFY forceNoClipChanged)
55 Q_PROPERTY(int stickyHeaderHeight READ stickyHeaderHeight NOTIFY stickyHeaderHeightChanged)
55 Q_PROPERTY(int stickyHeaderHeight READ stickyHeaderHeight NOTIFY stickyHeaderHeightChanged)
56 Q_PROPERTY(qreal headerItemShownHeight READ headerItemShownHeight NOTIFY headerItemShownHeightChanged)
56 Q_PROPERTY(qreal headerItemShownHeight READ headerItemShownHeight NOTIFY headerItemShownHeightChanged)
57+ Q_PROPERTY(qreal cacheBuffer READ cacheBuffer WRITE setCacheBuffer NOTIFY cacheBufferChanged)
57
58
58 friend class ListViewWithPageHeaderTest;
59 friend class ListViewWithPageHeaderTest;
59 friend class ListViewWithPageHeaderTestSection;
60 friend class ListViewWithPageHeaderTestSection;
@@ -84,6 +85,9 @@ public:
84 int stickyHeaderHeight() const;
85 int stickyHeaderHeight() const;
85 qreal headerItemShownHeight() const;
86 qreal headerItemShownHeight() const;
86
87
88+ qreal cacheBuffer() const;
89+ void setCacheBuffer(qreal cacheBuffer);
90+
87 Q_INVOKABLE void positionAtBeginning();
91 Q_INVOKABLE void positionAtBeginning();
88 Q_INVOKABLE void showHeader();
92 Q_INVOKABLE void showHeader();
89 Q_INVOKABLE int firstCreatedIndex() const;
93 Q_INVOKABLE int firstCreatedIndex() const;
@@ -105,6 +109,7 @@ Q_SIGNALS:
105 void forceNoClipChanged();
109 void forceNoClipChanged();
106 void stickyHeaderHeightChanged();
110 void stickyHeaderHeightChanged();
107 void headerItemShownHeightChanged();
111 void headerItemShownHeightChanged();
112+ void cacheBufferChanged();
108
113
109protected:
114protected:
110 void componentComplete() override;
115 void componentComplete() override;
@@ -199,6 +204,7 @@ private:
199 bool m_forceNoClip;
204 bool m_forceNoClip;
200 bool m_inLayout;
205 bool m_inLayout;
201 bool m_inContentHeightKeepHeaderShown;
206 bool m_inContentHeightKeepHeaderShown;
207+ qreal m_cacheBuffer;
202
208
203 // Qt 5.0 doesn't like releasing the items just after itemCreated
209 // Qt 5.0 doesn't like releasing the items just after itemCreated
204 // so we delay the releasing until the next updatePolish
210 // so we delay the releasing until the next updatePolish
plugins/Dash/qmldir+2−1
@@ -3,4 +3,5 @@ plugin Dash-qml
3typeinfo Dash.qmltypes
3typeinfo Dash.qmltypes
4singleton CardCreatorCache 0.1 CardCreatorCache.qml
4singleton CardCreatorCache 0.1 CardCreatorCache.qml
5ScopeStyle 0.1 ScopeStyle.qml
5ScopeStyle 0.1 ScopeStyle.qml
6−CardAttributes 0.1 CardAttributes.qml
6+CardAttributes 0.1 CardAttributes.qml
7+CroppedImageMinimumSourceSize 0.1 CroppedImageMinimumSourceSize.qml
plugins/LightDM/DBusGreeter.h−1
@@ -21,7 +21,6 @@
21#include <QObject>
21#include <QObject>
22
22
23class Greeter;
23class Greeter;
24−class QDBusInterface;
25
24
26/** This is an internal class used to talk with the indicators.
25/** This is an internal class used to talk with the indicators.
27 */
26 */
plugins/LightDM/DBusGreeterList.h−1
@@ -21,7 +21,6 @@
21#include <QObject>
21#include <QObject>
22
22
23class Greeter;
23class Greeter;
24−class QDBusInterface;
25
24
26/** This is an internal class used to talk with the indicators.
25/** This is an internal class used to talk with the indicators.
27 */
26 */
plugins/Unity/DashCommunicator/CMakeLists.txt+8−2
@@ -1,5 +1,6 @@
1include_directories(
1include_directories(
2 ${CMAKE_CURRENT_SOURCE_DIR}
2 ${CMAKE_CURRENT_SOURCE_DIR}
3+ ${libunity8-private_SOURCE_DIR}
3)
4)
4
5
5set(QMLDASHCOMMUNICATORPLUGIN_SRC
6set(QMLDASHCOMMUNICATORPLUGIN_SRC
@@ -7,13 +8,18 @@ set(QMLDASHCOMMUNICATORPLUGIN_SRC
7 dbusdashcommunicatorservice.cpp
8 dbusdashcommunicatorservice.cpp
8 dashcommunicatorservice.cpp
9 dashcommunicatorservice.cpp
9 dashcommunicator.cpp
10 dashcommunicator.cpp
11+ dashconnection.cpp
10− )
12+)
11
13
12add_library(DashCommunicator-qml MODULE
14add_library(DashCommunicator-qml MODULE
13 ${QMLDASHCOMMUNICATORPLUGIN_SRC}
15 ${QMLDASHCOMMUNICATORPLUGIN_SRC}
14− )
16+)
15
17
16qt5_use_modules(DashCommunicator-qml DBus Qml)
18qt5_use_modules(DashCommunicator-qml DBus Qml)
17
19
20+target_link_libraries(DashCommunicator-qml
21+ unity8-private
22+)
23+
18# export the qmldir and qmltypes files
24# export the qmldir and qmltypes files
19add_unity8_plugin(Unity.DashCommunicator 0.1 Unity/DashCommunicator TARGETS DashCommunicator-qml)
25add_unity8_plugin(Unity.DashCommunicator 0.1 Unity/DashCommunicator TARGETS DashCommunicator-qml)
plugins/Unity/DashCommunicator/dashcommunicator.cpp+22−13
@@ -15,29 +15,38 @@
15 */
15 */
16
16
17#include "dashcommunicator.h"
17#include "dashcommunicator.h"
18+#include "dashconnection.h"
18
19
19#include <QObject>
20#include <QObject>
20−#include <QDBusConnection>
21−#include <QDBusInterface>
22−#include <QDebug>
23
21
24DashCommunicator::DashCommunicator(QObject *parent):
22DashCommunicator::DashCommunicator(QObject *parent):
25− QObject(parent)
23+ QThread(parent),
24+ m_dashConnection(nullptr),
25+ m_created(false)
26{
26{
27+ start();
27}
28}
28
29
29−DashCommunicator::~DashCommunicator()
30+void DashCommunicator::setCurrentScope(const QString &scopeId, bool animate, bool isSwipe)
30{
31{
32+ m_mutex.lock();
33+ if (m_created) {
34+ QMetaObject::invokeMethod(m_dashConnection, "setCurrentScope",
35+ Q_ARG(QString, scopeId),
36+ Q_ARG(bool, animate),
37+ Q_ARG(bool, isSwipe));
38+ }
39+ m_mutex.unlock();
31}
40}
32
41
33−
34−void DashCommunicator::setCurrentScope(const QString &scopeId, bool animate, bool isSwipe)
42+void DashCommunicator::run()
35{
43{
36− QDBusConnection connection = QDBusConnection::sessionBus();
37− QDBusInterface dashIface ("com.canonical.UnityDash",
44+ m_dashConnection = new DashConnection("com.canonical.UnityDash",
38− "/com/canonical/UnityDash",
45+ "/com/canonical/UnityDash",
39− "",
46+ "", this);
40− connection);
47+ m_mutex.lock();
48+ m_created = true;
49+ m_mutex.unlock();
41
50
42− dashIface.call("SetCurrentScope", scopeId, animate, isSwipe);
51+ exec();
43}
52}
plugins/Unity/DashCommunicator/dashcommunicator.h+16−6
@@ -17,19 +17,29 @@
17#ifndef DASHCOMMUNICATOR_H
17#ifndef DASHCOMMUNICATOR_H
18#define DASHCOMMUNICATOR_H
18#define DASHCOMMUNICATOR_H
19
19
20+// Qt
20−#include <QObject>
21+#include <QThread>
22+#include <QMutex>
21
23
24+class DashConnection;
25+
22−class DashCommunicator: public QObject
26+class DashCommunicator: public QThread
23{
27{
24 Q_OBJECT
28 Q_OBJECT
25− Q_CLASSINFO("D-Bus Interface", "com.canonical.Unity.DashCommunicator")
26−
27public:
29public:
28− DashCommunicator(QObject *parent = 0);
30+ explicit DashCommunicator(QObject *parent = 0);
29− ~DashCommunicator();
31+ ~DashCommunicator() = default;
30
32
31public Q_SLOTS:
33public Q_SLOTS:
32 void setCurrentScope(const QString &scopeId, bool animate, bool isSwipe);
34 void setCurrentScope(const QString &scopeId, bool animate, bool isSwipe);
35+
36+protected:
37+ void run() override;
38+
39+private:
40+ DashConnection *m_dashConnection;
41+ bool m_created;
42+ QMutex m_mutex;
33};
43};
34
44
35#endif
45#endif
plugins/Unity/DashCommunicator/dashconnection.cppnew file+33
@@ -0,0 +1,33 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it under
5+ * the terms of the GNU Lesser General Public License version 3, as published by
6+ * the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but WITHOUT
9+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11+ * Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "dashconnection.h"
18+
19+#include <QDBusInterface>
20+#include <QDBusPendingCall>
21+
22+DashConnection::DashConnection(const QString &service, const QString &path, const QString &interface, QObject *parent):
23+ AbstractDBusServiceMonitor(service, path, interface, SessionBus, parent)
24+{
25+
26+}
27+
28+void DashConnection::setCurrentScope(const QString &scopeId, bool animate, bool isSwipe)
29+{
30+ if (dbusInterface()) {
31+ dbusInterface()->asyncCall("SetCurrentScope", scopeId, animate, isSwipe);
32+ }
33+}
plugins/Unity/DashCommunicator/dashconnection.hnew file+34
@@ -0,0 +1,34 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it under
5+ * the terms of the GNU Lesser General Public License version 3, as published by
6+ * the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but WITHOUT
9+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11+ * Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef THREADEDDASHCONNECTION_H
18+#define THREADEDDASHCONNECTION_H
19+
20+// local
21+#include "abstractdbusservicemonitor.h"
22+
23+
24+class DashConnection: public AbstractDBusServiceMonitor
25+{
26+ Q_OBJECT
27+public:
28+ DashConnection(const QString &service, const QString &path, const QString &interface, QObject *parent = 0);
29+
30+public Q_SLOTS:
31+ void setCurrentScope(const QString &scopeId, bool animate, bool isSwipe);
32+};
33+
34+#endif
plugins/Unity/Launcher/CMakeLists.txt+1−1
@@ -1,6 +1,6 @@
1include(FindPkgConfig)
1include(FindPkgConfig)
2pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=4)
2pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=4)
3−pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=4)
3+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=5)
4pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
4pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
5
5
6add_definitions(-DSM_BUSNAME=systemBus)
6add_definitions(-DSM_BUSNAME=systemBus)
po/unity8.pot+10−2
@@ -8,7 +8,7 @@ msgid ""
8msgstr ""
8msgstr ""
9"Project-Id-Version: unity8\n"
9"Project-Id-Version: unity8\n"
10"Report-Msgid-Bugs-To: \n"
10"Report-Msgid-Bugs-To: \n"
11−"POT-Creation-Date: 2014-10-02 10:35+0200\n"
11+"POT-Creation-Date: 2014-10-03 08:13-0400\n"
12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14"Language-Team: LANGUAGE <LL@li.org>\n"
14"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -160,7 +160,7 @@ msgstr ""
160msgid "OK"
160msgid "OK"
161msgstr ""
161msgstr ""
162
162
163−#: qml/Dash/GenericScopeView.qml:411 qml/Dash/GenericScopeView.qml:533
163+#: qml/Dash/GenericScopeView.qml:411 qml/Dash/GenericScopeView.qml:568
164msgid "See less"
164msgid "See less"
165msgstr ""
165msgstr ""
166
166
@@ -205,6 +205,14 @@ msgstr ""
205msgid "Send"
205msgid "Send"
206msgstr ""
206msgstr ""
207
207
208+#: qml/Dash/PullToRefreshScopeStyle.qml:55
209+msgid "Pull to refresh…"
210+msgstr ""
211+
212+#: qml/Dash/PullToRefreshScopeStyle.qml:60
213+msgid "Release to refresh…"
214+msgstr ""
215+
208#: qml/Dash/ScopesOverview.qml:206
216#: qml/Dash/ScopesOverview.qml:206
209msgid "Manage Scopes"
217msgid "Manage Scopes"
210msgstr ""
218msgstr ""
qml/Components/LazyImage.qml+1
@@ -37,6 +37,7 @@ Item {
37 property alias horizontalAlignment: image.horizontalAlignment
37 property alias horizontalAlignment: image.horizontalAlignment
38 property alias verticalAlignment: image.verticalAlignment
38 property alias verticalAlignment: image.verticalAlignment
39 property alias sourceImage: image
39 property alias sourceImage: image
40+ property alias borderSource: shape.borderSource
40
41
41 state: "default"
42 state: "default"
42
43
qml/Components/ResponsiveGridView.qml+5−1
@@ -34,7 +34,11 @@ Item {
34 readonly property int cellWidth: gridView.cellWidth
34 readonly property int cellWidth: gridView.cellWidth
35 readonly property int cellHeight: gridView.cellHeight
35 readonly property int cellHeight: gridView.cellHeight
36 readonly property int totalContentHeight: {
36 readonly property int totalContentHeight: {
37+ if (gridView.model) {
37− return contentHeightForRows(Math.ceil(gridView.model.count / columns), cellHeight)
38+ return contentHeightForRows(Math.ceil(gridView.model.count / columns), cellHeight)
39+ } else {
40+ return 0;
41+ }
38 }
42 }
39 property alias interactive: gridView.interactive
43 property alias interactive: gridView.interactive
40 readonly property alias flicking: gridView.flicking
44 readonly property alias flicking: gridView.flicking
qml/Dash/CardGrid.qml+2−1
@@ -55,6 +55,7 @@ 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
58 Loader {
59 Loader {
59 id: loader
60 id: loader
60 sourceComponent: cardTool.cardComponent
61 sourceComponent: cardTool.cardComponent
@@ -68,7 +69,7 @@ DashRenderer {
68 item.cardData = Qt.binding(function() { return model; });
69 item.cardData = Qt.binding(function() { return model; });
69 item.template = Qt.binding(function() { return cardTool.template; });
70 item.template = Qt.binding(function() { return cardTool.template; });
70 item.components = Qt.binding(function() { return cardTool.components; });
71 item.components = Qt.binding(function() { return cardTool.components; });
71− item.headerAlignment = Qt.binding(function() { return cardTool.headerAlignment; });
72+ item.titleAlignment = Qt.binding(function() { return cardTool.titleAlignment; });
72 item.scopeStyle = root.scopeStyle;
73 item.scopeStyle = root.scopeStyle;
73 }
74 }
74 Connections {
75 Connections {
qml/Dash/CardHorizontalList.qml+1−1
@@ -49,7 +49,7 @@ DashRenderer {
49 item.cardData = Qt.binding(function() { return model; });
49 item.cardData = Qt.binding(function() { return model; });
50 item.template = Qt.binding(function() { return cardTool.template; });
50 item.template = Qt.binding(function() { return cardTool.template; });
51 item.components = Qt.binding(function() { return cardTool.components; });
51 item.components = Qt.binding(function() { return cardTool.components; });
52− item.headerAlignment = Qt.binding(function() { return cardTool.headerAlignment; });
52+ item.titleAlignment = Qt.binding(function() { return cardTool.titleAlignment; });
53 item.scopeStyle = root.scopeStyle;
53 item.scopeStyle = root.scopeStyle;
54 }
54 }
55 Connections {
55 Connections {
qml/Dash/CardTool.qml+26−18
@@ -131,21 +131,26 @@ Item {
131 property size artShapeSize: cardLoader.item ? cardLoader.item.artShapeSize : 0
131 property size artShapeSize: cardLoader.item ? cardLoader.item.artShapeSize : 0
132
132
133 /*!
133 /*!
134− \brief Desired alignment of header components.
134+ \brief Desired alignment of title
135 */
135 */
136− readonly property int headerAlignment: {
136+ readonly property int titleAlignment: {
137− var subtitle = components["subtitle"];
137+ if (template["card-layout"] === "horizontal"
138− var price = components["price"];
138+ || (typeof components["title"] !== "object" &&
139− var summary = components["summary"];
139+ components["title"]["align"] !== "center")) return Text.AlignLeft;
140−
140+
141− var hasSubtitle = subtitle && (typeof subtitle === "string" || subtitle["field"])
141+ var keys = ["mascot", "emblem", "subtitle", "attributes", "summary"];
142− var hasPrice = price && (typeof price === "string" || subtitle["field"]);
142+
143− var hasSummary = summary && (typeof summary === "string" || summary["field"])
143+ for (var key in keys) {
144−
144+ key = keys[key];
145− var isOnlyTextComponent = !hasSubtitle && !hasPrice && !hasSummary;
145+ try {
146− if (!isOnlyTextComponent) return Text.AlignLeft;
146+ if (typeof components[key] === "string"
147+ || typeof components[key]["field"] === "string") return Text.AlignLeft;
148+ } catch (e) {
149+ continue;
150+ }
151+ }
147
152
148− return (template["card-layout"] === "horizontal") ? Text.AlignLeft : Text.AlignHCenter;
153+ return Text.AlignHCenter;
149 }
154 }
150
155
151 QtObject {
156 QtObject {
@@ -169,16 +174,19 @@ Item {
169
174
170 Item {
175 Item {
171 id: attributesModel
176 id: attributesModel
172− property int numOfAttributes: {
177+ property int numOfAttributes: 0
178+ property var model: []
179+ property bool hasAttributes: {
173 var attributes = components["attributes"];
180 var attributes = components["attributes"];
174− if ((attributes != undefined) && attributes["field"]) {
181+ var hasAttributesFlag = (attributes != undefined) && attributes["field"];
182+
183+ if (hasAttributesFlag) {
175 if (attributes["max-count"]) {
184 if (attributes["max-count"]) {
176− return attributes["max-count"];
185+ numOfAttributes = attributes["max-count"];
177 }
186 }
178 }
187 }
179− return 0;
188+ return hasAttributesFlag
180 }
189 }
181− property var model: []
182
190
183 onNumOfAttributesChanged: {
191 onNumOfAttributesChanged: {
184 model = []
192 model = []
qml/Dash/CardVerticalJournal.qml+2−1
@@ -49,6 +49,7 @@ DashRenderer {
49 id: loader
49 id: loader
50 sourceComponent: cardTool.cardComponent
50 sourceComponent: cardTool.cardComponent
51 width: cardTool.cardWidth
51 width: cardTool.cardWidth
52+ visible: y + height >= root.visibleRangeBegin && y <= root.visibleRangeEnd
52 onLoaded: {
53 onLoaded: {
53 item.objectName = "delegate" + index;
54 item.objectName = "delegate" + index;
54 item.fixedArtShapeSize = Qt.binding(function() { return cardTool.artShapeSize; });
55 item.fixedArtShapeSize = Qt.binding(function() { return cardTool.artShapeSize; });
@@ -56,7 +57,7 @@ DashRenderer {
56 item.cardData = Qt.binding(function() { return model; });
57 item.cardData = Qt.binding(function() { return model; });
57 item.template = Qt.binding(function() { return cardTool.template; });
58 item.template = Qt.binding(function() { return cardTool.template; });
58 item.components = Qt.binding(function() { return cardTool.components; });
59 item.components = Qt.binding(function() { return cardTool.components; });
59− item.headerAlignment = Qt.binding(function() { return cardTool.headerAlignment; });
60+ item.titleAlignment = Qt.binding(function() { return cardTool.titleAlignment; });
60 item.scopeStyle = root.scopeStyle;
61 item.scopeStyle = root.scopeStyle;
61 }
62 }
62 Connections {
63 Connections {
qml/Dash/DashContent.qml+3
@@ -111,6 +111,8 @@ Item {
111 highlightMoveDuration: 250
111 highlightMoveDuration: 250
112 highlightRangeMode: ListView.StrictlyEnforceRange
112 highlightRangeMode: ListView.StrictlyEnforceRange
113 // TODO Investigate if we can switch to a smaller cache buffer when/if UbuntuShape gets more performant
113 // TODO Investigate if we can switch to a smaller cache buffer when/if UbuntuShape gets more performant
114+ // 1073741823 is s^30 -1. A quite big number so that you have "infinite" cache, but not so
115+ // big so that if you add if with itself you're outside the 2^31 int range
114 cacheBuffer: 1073741823
116 cacheBuffer: 1073741823
115 onMovementStarted: currentItem.item.showHeader();
117 onMovementStarted: currentItem.item.showHeader();
116 clip: parent.x != 0
118 clip: parent.x != 0
@@ -177,6 +179,7 @@ Item {
177 dashContent.scopeLoaded(item.scope.id)
179 dashContent.scopeLoaded(item.scope.id)
178 item.paginationCount = Qt.binding(function() { return dashContentList.count } )
180 item.paginationCount = Qt.binding(function() { return dashContentList.count } )
179 item.paginationIndex = Qt.binding(function() { return dashContentList.currentIndex } )
181 item.paginationIndex = Qt.binding(function() { return dashContentList.currentIndex } )
182+ item.holdingList = dashContentList;
180 }
183 }
181 Connections {
184 Connections {
182 target: isCurrent ? scope : null
185 target: isCurrent ? scope : null
qml/Dash/DashNavigation.qml+1
@@ -52,6 +52,7 @@ Item {
52
52
53 Rectangle {
53 Rectangle {
54 id: blackRect
54 id: blackRect
55+ objectName: "blackRect"
55 color: "black"
56 color: "black"
56 opacity: openList && openList.currentItem && openList.currentItem.visible ? 0.5 : 0
57 opacity: openList && openList.currentItem && openList.currentItem.visible ? 0.5 : 0
57 Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration } }
58 Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration } }
qml/Dash/DashRenderer.qml+4
@@ -27,6 +27,10 @@ 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+
30 property real originY: 0
34 property real originY: 0
31
35
32 // The model to renderer
36 // The model to renderer
qml/Dash/GenericScopeView.qml+71−13
@@ -15,7 +15,7 @@
15 */
15 */
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18−import Ubuntu.Components 0.1
18+import Ubuntu.Components 1.1
19import Utils 0.1
19import Utils 0.1
20import Unity 0.2
20import Unity 0.2
21import Dash 0.1
21import Dash 0.1
@@ -38,6 +38,7 @@ FocusScope {
38 property int paginationCount: 0
38 property int paginationCount: 0
39 property int paginationIndex: 0
39 property int paginationIndex: 0
40 property alias pageHeaderTotallyVisible: categoryView.pageHeaderTotallyVisible
40 property alias pageHeaderTotallyVisible: categoryView.pageHeaderTotallyVisible
41+ property var holdingList: null
41
42
42 property var scopeStyle: ScopeStyle {
43 property var scopeStyle: ScopeStyle {
43 style: scope ? scope.customizations : {}
44 style: scope ? scope.customizations : {}
@@ -251,8 +252,8 @@ FocusScope {
251 }
252 }
252 }
253 }
253
254
254− onHeightChanged: rendererLoader.updateDelegateCreationRange();
255+ onHeightChanged: rendererLoader.updateRanges();
255− onYChanged: rendererLoader.updateDelegateCreationRange();
256+ onYChanged: rendererLoader.updateRanges();
256
257
257 Loader {
258 Loader {
258 id: rendererLoader
259 id: rendererLoader
@@ -314,7 +315,7 @@ FocusScope {
314 var shouldExpand = baseItem.category === categoryView.expandedCategoryId;
315 var shouldExpand = baseItem.category === categoryView.expandedCategoryId;
315 baseItem.expand(shouldExpand, false /*animate*/);
316 baseItem.expand(shouldExpand, false /*animate*/);
316 }
317 }
317− updateDelegateCreationRange();
318+ updateRanges();
318 if (scope && scope.id === "clickscope" && (categoryId === "predefined" || categoryId === "local")) {
319 if (scope && scope.id === "clickscope" && (categoryId === "predefined" || categoryId === "local")) {
319 // Yeah, hackish :/
320 // Yeah, hackish :/
320 cardTool.artShapeSize = Qt.size(units.gu(8), units.gu(7.5));
321 cardTool.artShapeSize = Qt.size(units.gu(8), units.gu(7.5));
@@ -348,13 +349,25 @@ FocusScope {
348 }
349 }
349 Connections {
350 Connections {
350 target: categoryView
351 target: categoryView
351− onOriginYChanged: rendererLoader.updateDelegateCreationRange();
352+ onOriginYChanged: rendererLoader.updateRanges();
352− onContentYChanged: rendererLoader.updateDelegateCreationRange();
353+ onContentYChanged: rendererLoader.updateRanges();
353− onHeightChanged: rendererLoader.updateDelegateCreationRange();
354+ onHeightChanged: rendererLoader.updateRanges();
354− onContentHeightChanged: rendererLoader.updateDelegateCreationRange();
355+ onContentHeightChanged: rendererLoader.updateRanges();
356+ }
357+ Connections {
358+ target: scopeView
359+ onIsCurrentChanged: rendererLoader.updateRanges();
360+ }
361+ Connections {
362+ target: holdingList
363+ onMovingChanged: if (!moving) rendererLoader.updateRanges();
355 }
364 }
356
365
357− function updateDelegateCreationRange() {
366+ function updateRanges() {
367+ if (holdingList && holdingList.moving) {
368+ return;
369+ }
370+
358 if (categoryView.moving) {
371 if (categoryView.moving) {
359 // Do not update the range if we are overshooting up or down, since we'll come back
372 // Do not update the range if we are overshooting up or down, since we'll come back
360 // to the stable position and delete/create items without any reason
373 // to the stable position and delete/create items without any reason
@@ -366,10 +379,20 @@ FocusScope {
366 }
379 }
367 }
380 }
368
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+
369 if (item && item.hasOwnProperty("displayMarginBeginning")) {
387 if (item && item.hasOwnProperty("displayMarginBeginning")) {
370 // TODO do we need item.originY here, test 1300302 once we have a silo
388 // TODO do we need item.originY here, test 1300302 once we have a silo
371 // and we can run it on the phone
389 // and we can run it on the phone
390+ if (scopeView.isCurrent) {
391+ // 1073741823 is s^30 -1. A quite big number so that you have "infinite" display margin, but not so
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;
372− if (baseItem.y + baseItem.height <= 0) {
395+ } else if (baseItem.y + baseItem.height <= 0) {
373 // Not visible (item at top of the list viewport)
396 // Not visible (item at top of the list viewport)
374 item.displayMarginBeginning = -baseItem.height;
397 item.displayMarginBeginning = -baseItem.height;
375 item.displayMarginEnd = 0;
398 item.displayMarginEnd = 0;
@@ -378,9 +401,9 @@ FocusScope {
378 item.displayMarginBeginning = 0;
401 item.displayMarginBeginning = 0;
379 item.displayMarginEnd = -baseItem.height;
402 item.displayMarginEnd = -baseItem.height;
380 } else {
403 } else {
381− item.displayMarginBeginning = -Math.max(-baseItem.y, 0);
404+ item.displayMarginBeginning = Math.round(-Math.max(-baseItem.y, 0));
382− item.displayMarginEnd = -Math.max(baseItem.height - seeAll.height
405+ item.displayMarginEnd = -Math.round(Math.max(baseItem.height - seeAll.height -
383− - categoryView.height + baseItem.y, 0)
406+ categoryView.height + baseItem.y, 0));
384 }
407 }
385 }
408 }
386 }
409 }
@@ -496,6 +519,41 @@ FocusScope {
496 }
519 }
497 }
520 }
498
521
522+ Item {
523+ id: pullToRefreshClippingItem
524+ anchors.left: parent.left
525+ anchors.right: parent.right
526+ anchors.bottom: parent.bottom
527+ height: parent.height - pullToRefresh.contentY + (pageHeaderLoader.item ? pageHeaderLoader.item.bottomItem[0].height - pageHeaderLoader.item.height : 0)
528+ clip: true
529+
530+ PullToRefresh {
531+ id: pullToRefresh
532+ objectName: "pullToRefresh"
533+ target: categoryView
534+
535+ readonly property real contentY: categoryView.contentY - categoryView.originY
536+ y: -contentY - units.gu(5)
537+
538+ onRefresh: {
539+ refreshing = true
540+ scopeView.scope.refresh()
541+ }
542+ anchors.left: parent.left
543+ anchors.right: parent.right
544+
545+ Connections {
546+ target: scopeView
547+ onProcessingChanged: if (!scopeView.processing) pullToRefresh.refreshing = false
548+ }
549+
550+ style: PullToRefreshScopeStyle {
551+ anchors.fill: parent
552+ activationThreshold: units.gu(14)
553+ }
554+ }
555+ }
556+
499 AbstractButton {
557 AbstractButton {
500 id: floatingSeeLess
558 id: floatingSeeLess
501 objectName: "floatingSeeLess"
559 objectName: "floatingSeeLess"
qml/Dash/Previews/PreviewHeader.qml+13
@@ -16,12 +16,14 @@
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19+import Dash 0.1
19import "../"
20import "../"
20
21
21/*! This preview widget shows a header
22/*! This preview widget shows a header
22 * The title comes in widgetData["title"]
23 * The title comes in widgetData["title"]
23 * The mascot comes in widgetData["mascot"]
24 * The mascot comes in widgetData["mascot"]
24 * The subtitle comes in widgetData["subtitle"]
25 * The subtitle comes in widgetData["subtitle"]
26+ * The attributes comes in widgetData["attributes"]
25 */
27 */
26
28
27PreviewWidget {
29PreviewWidget {
@@ -35,6 +37,7 @@ PreviewWidget {
35 readonly property url mascot: root.widgetData["mascot"] || ""
37 readonly property url mascot: root.widgetData["mascot"] || ""
36 readonly property string title: root.widgetData["title"] || ""
38 readonly property string title: root.widgetData["title"] || ""
37 readonly property string subtitle: root.widgetData["subtitle"] || ""
39 readonly property string subtitle: root.widgetData["subtitle"] || ""
40+ readonly property var attributes: root.widgetData["attributes"] || null
38 readonly property color fontColor: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText
41 readonly property color fontColor: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText
39
42
40 implicitHeight: row.height + row.margins * 2
43 implicitHeight: row.height + row.margins * 2
@@ -114,6 +117,16 @@ PreviewWidget {
114 text: headerRoot.subtitle
117 text: headerRoot.subtitle
115 }
118 }
116 }
119 }
120+
121+ Loader {
122+ active: titleLabel.text && headerRoot.attributes
123+ anchors { left: parent.left; right: parent.right }
124+ sourceComponent: CardAttributes {
125+ id: previewAttributes
126+ objectName: "previewAttributes"
127+ model: headerRoot.attributes
128+ }
129+ }
117 }
130 }
118 }
131 }
119 }
132 }
qml/Dash/Previews/PreviewImageGallery.qml+121−18
@@ -26,8 +26,11 @@ PreviewWidget {
26 id: root
26 id: root
27 implicitHeight: units.gu(22)
27 implicitHeight: units.gu(22)
28
28
29+ property Item rootItem: QuickUtils.rootItem(root)
30+
29 ListView {
31 ListView {
30 id: previewImageListView
32 id: previewImageListView
33+ objectName: "previewImageListView"
31 spacing: units.gu(1)
34 spacing: units.gu(1)
32 anchors.fill: parent
35 anchors.fill: parent
33 orientation: ListView.Horizontal
36 orientation: ListView.Horizontal
@@ -35,24 +38,6 @@ PreviewWidget {
35 model: root.widgetData["sources"]
38 model: root.widgetData["sources"]
36 clip: true
39 clip: true
37
40
38− // FIXME: Because of ListViews inside ListViews inside Flickables inside ListViews (and some more)
39− // we finally reached the point where this ListView doesn't correctly get swipe input any more but
40− // instead the parent ListView is the one that is swiped. This MouseArea sort of creates a blocking
41− // layer to make sure this ListView can be swiped, regardless of what's behind it.
42− MouseArea {
43− anchors.fill: parent
44− enabled: parent.contentWidth > parent.width
45− }
46−
47− // FIXME: Because of ListViews inside ListViews inside Flickables inside ListViews (and some more)
48− // we finally reached the point where this ListView doesn't correctly get swipe input any more but
49− // instead the parent ListView is the one that is swiped. This MouseArea sort of creates a blocking
50− // layer to make sure this ListView can be swiped, regardless of what's behind it.
51− MouseArea {
52− anchors.fill: parent
53− enabled: parent.contentWidth > parent.width
54− }
55−
56 LazyImage {
41 LazyImage {
57 objectName: "placeholderScreenshot"
42 objectName: "placeholderScreenshot"
58 anchors {
43 anchors {
@@ -66,6 +51,7 @@ PreviewWidget {
66 }
51 }
67
52
68 delegate: LazyImage {
53 delegate: LazyImage {
54+ objectName: "previewImage" + index
69 anchors {
55 anchors {
70 top: parent.top
56 top: parent.top
71 bottom: parent.bottom
57 bottom: parent.bottom
@@ -73,6 +59,123 @@ PreviewWidget {
73 source: modelData ? modelData : ""
59 source: modelData ? modelData : ""
74 scaleTo: "height"
60 scaleTo: "height"
75 initialWidth: units.gu(13)
61 initialWidth: units.gu(13)
62+ borderSource: mouseArea.pressed ? "radius_pressed.sci" : "radius_idle.sci"
63+
64+ MouseArea {
65+ id: mouseArea
66+ anchors.fill: parent
67+ onClicked: {
68+ slideShowListView.currentIndex = index;
69+ slideShow.initialX = rootItem.mapFromItem(parent, 0, 0).x
70+ slideShow.initialY = rootItem.mapFromItem(parent, 0, 0).y
71+ slideShow.visible = true;
72+ }
73+ }
74+ }
75+ }
76+
77+ Rectangle {
78+ id: slideShow
79+ objectName: "slideShow"
80+
81+ readonly property real initialScale: previewImageListView.height / rootItem.height
82+ readonly property real scaleProgress: (scale - initialScale) / (1.0 - initialScale)
83+ property real initialX: 0
84+ property real initialY: 0
85+
86+ parent: rootItem
87+ width: parent.width
88+ height: parent.height
89+ visible: false
90+ clip: visible && scale < 1.0
91+ scale: visible ? 1.0 : initialScale
92+ transformOrigin: Item.TopLeft
93+ transform: Translate {
94+ x: slideShow.initialX - slideShow.initialX * slideShow.scaleProgress
95+ y: slideShow.initialY - slideShow.initialY * slideShow.scaleProgress
96+ }
97+ color: "black"
98+ radius: units.gu(1) - units.gu(1) * slideShow.scaleProgress
99+
100+ Behavior on scale {
101+ enabled: !slideShow.visible
102+ UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration }
103+ }
104+
105+ ListView {
106+ id: slideShowListView
107+ objectName: "slideShowListView"
108+ anchors.fill: parent
109+ orientation: ListView.Horizontal
110+ highlightRangeMode: ListView.StrictlyEnforceRange
111+ highlightMoveDuration: 0
112+ snapMode: ListView.SnapOneItem
113+ boundsBehavior: Flickable.DragAndOvershootBounds
114+ model: root.widgetData["sources"]
115+
116+ delegate: Image {
117+ id: screenshot
118+ anchors {
119+ top: parent.top
120+ bottom: parent.bottom
121+ }
122+ width: slideShow.width
123+ source: modelData ? modelData : ""
124+ fillMode: Image.PreserveAspectFit
125+ sourceSize { width: screenshot.width; height: screenshot.height }
126+ }
127+
128+ MouseArea {
129+ anchors.fill: parent
130+ onClicked: slideShowHeader.shown = !slideShowHeader.shown
131+ }
132+ }
133+
134+ Rectangle {
135+ id: slideShowHeader
136+
137+ property bool shown: true
138+
139+ anchors {
140+ left: parent.left
141+ right: parent.right
142+ }
143+ height: units.gu(7)
144+ visible: opacity > 0
145+ opacity: slideShow.scaleProgress > 0.6 && shown ? 0.8 : 0
146+ color: "black"
147+
148+ Behavior on opacity {
149+ UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration }
150+ }
151+
152+ AbstractButton {
153+ id: slideShowCloseButton
154+ objectName: "slideShowCloseButton"
155+ anchors {
156+ top: parent.top
157+ bottom: parent.bottom
158+ }
159+ width: units.gu(8)
160+ height: width
161+
162+ onClicked: slideShow.visible = false
163+
164+ Rectangle {
165+ anchors.fill: parent
166+ color: Qt.rgba(1.0, 1.0, 1.0, 0.3)
167+ visible: slideShowCloseButton.pressed
168+ }
169+
170+ Icon {
171+ id: icon
172+ anchors.centerIn: parent
173+ width: units.gu(2.5)
174+ height: width
175+ color: Theme.palette.normal.foregroundText
176+ name: "close"
177+ }
178+ }
76 }
179 }
77 }
180 }
78}
181}
qml/Dash/PullToRefreshScopeStyle.qmlnew file+82
@@ -0,0 +1,82 @@
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 Ubuntu.Components 1.1
19+import Ubuntu.Components.Styles 1.1
20+
21+/**
22+ * TODO: Once the SDK version of PullToRefreshStyle doesn't have bug 1375799
23+ * (https://launchpad.net/bugs/1375799) anymore, we should switch to using a
24+ * a subclass of the Ambiance version with a hidden ActivityIndicator.
25+ */
26+PullToRefreshStyle {
27+ releaseToRefresh: styledItem.target.originY - styledItem.target.contentY > activationThreshold
28+
29+ Connections {
30+ property bool willRefresh: false
31+
32+ target: styledItem.target
33+ onDraggingChanged: {
34+ if (!styledItem.target.dragging && releaseToRefresh) {
35+ willRefresh = true
36+ }
37+ }
38+ onContentYChanged: {
39+ if (styledItem.target.originY - styledItem.target.contentY == 0 && willRefresh) {
40+ styledItem.refresh()
41+ willRefresh = false
42+ }
43+ }
44+ }
45+
46+ Label {
47+ id: pullLabel
48+ anchors.horizontalCenter: parent.horizontalCenter
49+ horizontalAlignment: Text.AlignHCenter
50+ verticalAlignment: Text.AlignVCenter
51+ states: [
52+ State {
53+ name: "pulling"
54+ when: styledItem.target.dragging && !releaseToRefresh
55+ PropertyChanges { target: pullLabel; text: i18n.tr("Pull to refresh…") }
56+ },
57+ State {
58+ name: "releasable"
59+ when: styledItem.target.dragging && releaseToRefresh
60+ PropertyChanges { target: pullLabel; text: i18n.tr("Release to refresh…") }
61+ }
62+ ]
63+ transitions: Transition {
64+ SequentialAnimation {
65+ UbuntuNumberAnimation {
66+ target: pullLabel
67+ property: "opacity"
68+ to: 0.0
69+ }
70+ PropertyAction {
71+ target: pullLabel
72+ property: "text"
73+ }
74+ UbuntuNumberAnimation {
75+ target: pullLabel
76+ property: "opacity"
77+ to: 1.0
78+ }
79+ }
80+ }
81+ }
82+}
qml/Dash/ScopeListView.qml+1
@@ -20,4 +20,5 @@ import Dash 0.1
20ListViewWithPageHeader {
20ListViewWithPageHeader {
21 maximumFlickVelocity: height * 10
21 maximumFlickVelocity: height * 10
22 flickDeceleration: height * 2
22 flickDeceleration: height * 2
23+ cacheBuffer: Number.MAX_VALUE
23}
24}
qml/Dash/ScopeSettingsPage.qml+1
@@ -49,6 +49,7 @@ Item {
49 right: parent.right
49 right: parent.right
50 }
50 }
51 model: root.scope ? root.scope.settings : null
51 model: root.scope ? root.scope.settings : null
52+ clip: true
52
53
53 delegate: ScopeSettingsWidgetFactory {
54 delegate: ScopeSettingsWidgetFactory {
54 objectName: "scopeSettingItem" + index
55 objectName: "scopeSettingItem" + index
qml/Launcher/Launcher.qml+1
@@ -92,6 +92,7 @@ Item {
92 // machine and switch to the final state in the next event loop run
92 // machine and switch to the final state in the next event loop run
93 Timer {
93 Timer {
94 id: animateTimer
94 id: animateTimer
95+ objectName: "animateTimer"
95 interval: 1
96 interval: 1
96 property string nextState: ""
97 property string nextState: ""
97 onTriggered: {
98 onTriggered: {
qml/Shell.qml+8−6
@@ -128,6 +128,13 @@ Item {
128 objectName: "dashCommunicator"
128 objectName: "dashCommunicator"
129 }
129 }
130
130
131+ Binding {
132+ target: ApplicationManager
133+ property: "forceDashActive"
134+ value: launcher.shown || launcher.dashSwipe
135+ }
136+
137+
131 WindowKeysFilter {
138 WindowKeysFilter {
132 // Handle but do not filter out volume keys
139 // Handle but do not filter out volume keys
133 Keys.onVolumeUpPressed: { volumeControl.volumeUp(); event.accepted = false; }
140 Keys.onVolumeUpPressed: { volumeControl.volumeUp(); event.accepted = false; }
@@ -170,7 +177,6 @@ Item {
170 lockscreen.show();
177 lockscreen.show();
171 }
178 }
172 greeter.hide();
179 greeter.hide();
173− launcher.hide();
174 }
180 }
175
181
176 onFocusedApplicationIdChanged: {
182 onFocusedApplicationIdChanged: {
@@ -597,11 +603,7 @@ Item {
597 available: edgeDemo.launcherEnabled && (!shell.locked || AccountsService.enableLauncherWhileLocked) && greeter.fakeActiveForApp === ""
603 available: edgeDemo.launcherEnabled && (!shell.locked || AccountsService.enableLauncherWhileLocked) && greeter.fakeActiveForApp === ""
598
604
599 onShowDashHome: showHome()
605 onShowDashHome: showHome()
600− onDash: {
601− if (ApplicationManager.focusedApplicationId != "unity8-dash") {
602− showDash()
606+ onDash: showDash()
603− }
604− }
605 onDashSwipeChanged: {
607 onDashSwipeChanged: {
606 if (dashSwipe) {
608 if (dashSwipe) {
607 dash.setCurrentScope("clickscope", false, true)
609 dash.setCurrentScope("clickscope", false, true)
src/CMakeLists.txt+1
@@ -36,3 +36,4 @@ install(TARGETS ${SHELL_APP}
36
36
37add_subdirectory(Panel)
37add_subdirectory(Panel)
38add_subdirectory(Dash)
38add_subdirectory(Dash)
39+add_subdirectory(libunity8-private)
src/Dash/CMakeLists.txt+2
@@ -9,6 +9,8 @@ add_executable(unity8-dash ${DASH_SRCS})
9
9
10qt5_use_modules(unity8-dash Gui Qml Quick Test)
10qt5_use_modules(unity8-dash Gui Qml Quick Test)
11
11
12+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden")
13+
12# install binaries
14# install binaries
13install(TARGETS ${DASH_APP}
15install(TARGETS ${DASH_APP}
14 RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
16 RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
src/Panel/CMakeLists.txt+2
@@ -1 +1,3 @@
1add_subdirectory(Indicators)
1add_subdirectory(Indicators)
2+
3+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden")
src/libunity8-private/CMakeLists.txtnew file+25
@@ -0,0 +1,25 @@
1+set(LIB_NAME "unity8-private")
2+set(SOVERSION 0)
3+set(VERSION ${SOVERSION}.0.0)
4+
5+project(lib${LIB_NAME})
6+
7+set(lib${LIB_NAME}_SRCS
8+ abstractdbusservicemonitor.cpp
9+ )
10+
11+add_library(${LIB_NAME} SHARED
12+ ${lib${LIB_NAME}_SRCS}
13+ )
14+
15+set_target_properties(${LIB_NAME} PROPERTIES
16+ VERSION ${VERSION}
17+ SOVERSION ${SOVERSION}
18+ )
19+
20+qt5_use_modules(${LIB_NAME} DBus)
21+
22+# install library
23+install(TARGETS ${LIB_NAME}
24+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
25+ )
src/libunity8-private/abstractdbusservicemonitor.cppnew file+87
@@ -0,0 +1,87 @@
1+/*
2+ * Copyright (C) 2011-2014 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Ugo Riboni <ugo.riboni@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+#include "abstractdbusservicemonitor.h"
21+
22+#include <QDBusInterface>
23+#include <QDBusServiceWatcher>
24+#include <QDBusConnection>
25+#include <QDBusConnectionInterface>
26+#include <QDBusReply>
27+
28+AbstractDBusServiceMonitor::AbstractDBusServiceMonitor(const QString &service, const QString &path,
29+ const QString &interface, const Bus bus,
30+ QObject *parent)
31+ : QObject(parent)
32+ , m_service(service)
33+ , m_path(path)
34+ , m_interface(interface)
35+ , m_watcher(new QDBusServiceWatcher(service,
36+ (bus == SystemBus) ? QDBusConnection::systemBus()
37+ : QDBusConnection::sessionBus()))
38+ , m_dbusInterface(nullptr)
39+{
40+ connect(m_watcher, &QDBusServiceWatcher::serviceRegistered, this, &AbstractDBusServiceMonitor::createInterface);
41+ connect(m_watcher, &QDBusServiceWatcher::serviceUnregistered, this, &AbstractDBusServiceMonitor::destroyInterface);
42+
43+ // Connect to the service if it's up already
44+ QDBusConnectionInterface* sessionBus = QDBusConnection::sessionBus().interface();
45+ QDBusReply<bool> reply = sessionBus->isServiceRegistered(m_service);
46+ if (reply.isValid() && reply.value()) {
47+ createInterface(m_service);
48+ }
49+}
50+
51+AbstractDBusServiceMonitor::~AbstractDBusServiceMonitor()
52+{
53+ delete m_watcher;
54+ delete m_dbusInterface;
55+}
56+
57+void AbstractDBusServiceMonitor::createInterface(const QString &)
58+{
59+ if (m_dbusInterface != nullptr) {
60+ delete m_dbusInterface;
61+ m_dbusInterface = nullptr;
62+ }
63+
64+ m_dbusInterface = new QDBusInterface(m_service, m_path, m_interface,
65+ QDBusConnection::sessionBus());
66+ Q_EMIT serviceAvailableChanged(true);
67+}
68+
69+void AbstractDBusServiceMonitor::destroyInterface(const QString &)
70+{
71+ if (m_dbusInterface != nullptr) {
72+ delete m_dbusInterface;
73+ m_dbusInterface = nullptr;
74+ }
75+
76+ Q_EMIT serviceAvailableChanged(false);
77+}
78+
79+QDBusInterface* AbstractDBusServiceMonitor::dbusInterface() const
80+{
81+ return m_dbusInterface;
82+}
83+
84+bool AbstractDBusServiceMonitor::serviceAvailable() const
85+{
86+ return m_dbusInterface != nullptr;
87+}
src/libunity8-private/abstractdbusservicemonitor.hnew file+66
@@ -0,0 +1,66 @@
1+/*
2+ * Copyright (C) 2011-2014 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Ugo Riboni <ugo.riboni@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+#ifndef ABSTRACTDBUSSERVICEMONITOR_H
21+#define ABSTRACTDBUSSERVICEMONITOR_H
22+
23+#include <QObject>
24+#include <QString>
25+#include <QDBusConnection>
26+
27+class QDBusInterface;
28+class QDBusServiceWatcher;
29+
30+class Q_DECL_EXPORT AbstractDBusServiceMonitor : public QObject
31+{
32+ Q_OBJECT
33+ Q_ENUMS(Bus)
34+ Q_PROPERTY(bool serviceAvailable READ serviceAvailable NOTIFY serviceAvailableChanged)
35+
36+public:
37+ enum Bus {
38+ SessionBus,
39+ SystemBus,
40+ };
41+
42+ explicit AbstractDBusServiceMonitor(const QString &service, const QString &path, const QString &interface,
43+ const Bus bus = SessionBus,
44+ QObject *parent = 0);
45+ ~AbstractDBusServiceMonitor();
46+
47+ QDBusInterface* dbusInterface() const;
48+
49+ bool serviceAvailable() const;
50+
51+Q_SIGNALS:
52+ void serviceAvailableChanged(bool available);
53+
54+private Q_SLOTS:
55+ void createInterface(const QString &service);
56+ void destroyInterface(const QString &service);
57+
58+protected:
59+ const QString m_service;
60+ const QString m_path;
61+ const QString m_interface;
62+ QDBusServiceWatcher* m_watcher;
63+ QDBusInterface* m_dbusInterface;
64+};
65+
66+#endif // ABSTRACTDBUSSERVICEMONITOR_H
tests/autopilot/unity8/shell/emulators/dash.py+4−4
@@ -66,7 +66,7 @@ class Dash(emulators.UnityEmulatorBase):
66 return resp_grid.select_single('Tile', text=text)
66 return resp_grid.select_single('Tile', text=text)
67
67
68 def get_scope(self, scope_name='clickscope'):
68 def get_scope(self, scope_name='clickscope'):
69− return self.dash_content_list.select_single(
69+ return self.dash_content_list.wait_select_single(
70 'QQuickLoader', scopeId=scope_name)
70 'QQuickLoader', scopeId=scope_name)
71
71
72 @autopilot_logging.log_action(logger.info)
72 @autopilot_logging.log_action(logger.info)
@@ -86,14 +86,14 @@ class Dash(emulators.UnityEmulatorBase):
86
86
87 def _get_scope_loader(self, scope_id):
87 def _get_scope_loader(self, scope_id):
88 try:
88 try:
89− return self.dash_content_list.select_single(
89+ return self.dash_content_list.wait_select_single(
90 'QQuickLoader', scopeId=scope_id)
90 'QQuickLoader', scopeId=scope_id)
91 except dbus.StateNotFoundError:
91 except dbus.StateNotFoundError:
92 raise emulators.UnityEmulatorException(
92 raise emulators.UnityEmulatorException(
93 'No scope found with id {0}'.format(scope_id))
93 'No scope found with id {0}'.format(scope_id))
94
94
95 def _get_scope_from_loader(self, loader):
95 def _get_scope_from_loader(self, loader):
96− return loader.get_children()[0]
96+ return loader.wait_select_single('GenericScopeView');
97
97
98 def _open_scope_scrolling(self, scope_loader):
98 def _open_scope_scrolling(self, scope_loader):
99 scroll = self._get_scroll_direction(scope_loader)
99 scroll = self._get_scroll_direction(scope_loader)
@@ -102,8 +102,8 @@ class Dash(emulators.UnityEmulatorBase):
102 scroll()
102 scroll()
103 self.dash_content_list.moving.wait_for(False)
103 self.dash_content_list.moving.wait_for(False)
104
104
105+ scope_loader.isCurrent.wait_for(True)
105 scope = self._get_scope_from_loader(scope_loader)
106 scope = self._get_scope_from_loader(scope_loader)
106− scope.isCurrent.wait_for(True)
107 return scope
107 return scope
108
108
109 def _get_scroll_direction(self, scope_loader):
109 def _get_scroll_direction(self, scope_loader):
tests/mocks/Ubuntu/CMakeLists.txt+1
@@ -1,3 +1,4 @@
1+add_subdirectory(Connectivity)
1add_subdirectory(DownloadDaemonListener)
2add_subdirectory(DownloadDaemonListener)
2add_subdirectory(Payments)
3add_subdirectory(Payments)
3add_subdirectory(SystemImage)
4add_subdirectory(SystemImage)
tests/mocks/Ubuntu/Connectivity/CMakeLists.txtnew file+10
@@ -0,0 +1,10 @@
1+add_library(connectivity-qml SHARED
2+ networking-status.cpp
3+ plugin.cpp
4+)
5+qt5_use_modules(connectivity-qml Qml)
6+
7+# copy qmldir file into build directory for shadow builds
8+file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/qmldir"
9+ DESTINATION ${CMAKE_CURRENT_BINARY_DIR}
10+ )
tests/mocks/Ubuntu/Connectivity/networking-status.cppnew file+64
@@ -0,0 +1,64 @@
1+/*
2+ * Copyright © 2014 Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU Lesser General Public License version 3,
6+ * as published by the Free Software Foundation.
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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Antti Kaijanmäki <antti.kaijanmaki@canonical.com>
18+ */
19+
20+#include "networking-status.h"
21+
22+NetworkingStatus::NetworkingStatus(QObject *parent)
23+ : QObject(parent)
24+{
25+ qRegisterMetaType<NetworkingStatus::Limitations>();
26+ qRegisterMetaType<QVector<NetworkingStatus::Limitations>>();
27+ qRegisterMetaType<NetworkingStatus::Status>();
28+}
29+
30+NetworkingStatus::~NetworkingStatus()
31+{}
32+
33+QVector<NetworkingStatus::Limitations>
34+NetworkingStatus::limitations() const
35+{
36+ return m_limitations;
37+}
38+
39+NetworkingStatus::Status
40+NetworkingStatus::status() const
41+{
42+ return Status::Online;
43+}
44+
45+bool
46+NetworkingStatus::online() const
47+{
48+ return status() == Status::Online;
49+}
50+
51+bool
52+NetworkingStatus::limitedBandwith() const
53+{
54+ return limitations().contains(Limitations::Bandwith);
55+}
56+
57+void NetworkingStatus::setLimitedBandwidth(bool limited)
58+{
59+ if (limited) {
60+ m_limitations << Limitations::Bandwith;
61+ } else if (limitedBandwith()) {
62+ m_limitations.remove(m_limitations.indexOf(Limitations::Bandwith));
63+ }
64+}
tests/mocks/Ubuntu/Connectivity/networking-status.hnew file+73
@@ -0,0 +1,73 @@
1+/*
2+ * Copyright © 2014 Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU Lesser General Public License version 3,
6+ * as published by the Free Software Foundation.
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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ */
17+
18+#ifndef FAKE_NETWORKING_STATUS_H
19+#define FAKE_NETWORKING_STATUS_H
20+
21+#include <QObject>
22+#include <QVector>
23+
24+class Q_DECL_EXPORT NetworkingStatus : public QObject
25+{
26+ Q_OBJECT
27+ Q_DISABLE_COPY(NetworkingStatus)
28+
29+ Q_ENUMS(Limitations)
30+ Q_ENUMS(Status)
31+
32+ Q_PROPERTY(QVector<Limitations> limitations READ limitations NOTIFY limitationsChanged)
33+ Q_PROPERTY(Status status READ status NOTIFY statusChanged)
34+ Q_PROPERTY(bool online READ online NOTIFY onlineChanged)
35+ Q_PROPERTY(bool limitedBandwith READ limitedBandwith WRITE setLimitedBandwidth NOTIFY limitedBandwithChanged)
36+
37+public:
38+ explicit NetworkingStatus(QObject *parent = 0);
39+ virtual ~NetworkingStatus();
40+
41+ enum Limitations {
42+ Bandwith
43+ };
44+
45+ enum Status {
46+ Offline,
47+ Connecting,
48+ Online
49+ };
50+
51+ QVector<Limitations> limitations() const;
52+ Status status() const;
53+ bool online() const;
54+ bool limitedBandwith() const;
55+
56+ // Only in the fake one
57+ void setLimitedBandwidth(bool limited);
58+
59+Q_SIGNALS:
60+ void limitationsChanged();
61+ void statusChanged(Status value);
62+ void onlineChanged(bool value);
63+ void limitedBandwithChanged(bool value);
64+
65+private:
66+ QVector<NetworkingStatus::Limitations> m_limitations;
67+};
68+
69+Q_DECLARE_METATYPE(NetworkingStatus::Limitations)
70+Q_DECLARE_METATYPE(QVector<NetworkingStatus::Limitations>)
71+Q_DECLARE_METATYPE(NetworkingStatus::Status)
72+
73+#endif // FAKE_NETWORKING_STATUS_H
tests/mocks/Ubuntu/Connectivity/plugin.cppnew file+46
@@ -0,0 +1,46 @@
1+/*
2+ * Copyright © 2014 Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU Lesser General Public License version 3,
6+ * as published by the Free Software Foundation.
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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Antti Kaijanmäki <antti.kaijanmaki@canonical.com>
18+ */
19+
20+#include "plugin.h"
21+
22+#include <QtQml>
23+
24+#include "networking-status.h"
25+
26+static QObject *
27+networkingStatusSingletonProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
28+{
29+ Q_UNUSED(scriptEngine)
30+
31+ return new NetworkingStatus(engine);
32+}
33+
34+void
35+QmlConnectivityNetworkingPlugin::registerTypes(const char *uri)
36+{
37+ // @uri Ubuntu.Connectivity
38+ qmlRegisterSingletonType<NetworkingStatus>(uri, 1, 0, "NetworkingStatus", networkingStatusSingletonProvider);
39+}
40+
41+void
42+QmlConnectivityNetworkingPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
43+{
44+ Q_UNUSED(uri);
45+ Q_UNUSED(engine);
46+}
tests/mocks/Ubuntu/Connectivity/plugin.hnew file+35
@@ -0,0 +1,35 @@
1+/*
2+ * Copyright © 2014 Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU Lesser General Public License version 3,
6+ * as published by the Free Software Foundation.
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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Antti Kaijanmäki <antti.kaijanmaki@canonical.com>
18+ */
19+
20+#ifndef QML_CONNECTIVITY_PLUGIN_H
21+#define QML_CONNECTIVITY_PLUGIN_H
22+
23+#include <QQmlExtensionPlugin>
24+
25+class QmlConnectivityNetworkingPlugin : public QQmlExtensionPlugin {
26+ Q_OBJECT
27+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
28+ Q_INTERFACES(QQmlExtensionInterface)
29+
30+public:
31+ void registerTypes(const char *uri);
32+ void initializeEngine(QQmlEngine *engine, const char *uri);
33+};
34+
35+#endif // QML_CONNECTIVITY_PLUGIN_H
tests/mocks/Ubuntu/Connectivity/qmldirnew file+2
@@ -0,0 +1,2 @@
1+module Ubuntu.Connectivity
2+plugin connectivity-qml
tests/mocks/Unity/Application/ApplicationManager.cpp+26
@@ -43,6 +43,7 @@ ApplicationManager *ApplicationManager::singleton()
43ApplicationManager::ApplicationManager(QObject *parent)
43ApplicationManager::ApplicationManager(QObject *parent)
44 : ApplicationManagerInterface(parent)
44 : ApplicationManagerInterface(parent)
45 , m_suspended(false)
45 , m_suspended(false)
46+ , m_forceDashActive(false)
46{
47{
47 m_roleNames.insert(RoleSession, "session");
48 m_roleNames.insert(RoleSession, "session");
48 m_roleNames.insert(RoleFullscreen, "fullscreen");
49 m_roleNames.insert(RoleFullscreen, "fullscreen");
@@ -292,6 +293,31 @@ void ApplicationManager::setSuspended(bool suspended)
292 Q_EMIT suspendedChanged();
293 Q_EMIT suspendedChanged();
293}
294}
294
295
296+bool ApplicationManager::forceDashActive() const
297+{
298+ return m_forceDashActive;
299+}
300+
301+void ApplicationManager::setForceDashActive(bool forceDashActive)
302+{
303+ if (m_forceDashActive == forceDashActive) {
304+ return;
305+ }
306+
307+ ApplicationInfo *dash = findApplication("unity8-dash");
308+ if (dash) {
309+ if (forceDashActive) {
310+ dash->setState(ApplicationInfo::Running);
311+ } else {
312+ if (!dash->focused()) {
313+ dash->setState(ApplicationInfo::Suspended);
314+ }
315+ }
316+ }
317+ m_forceDashActive = forceDashActive;
318+ Q_EMIT forceDashActiveChanged();
319+}
320+
295bool ApplicationManager::focusApplication(const QString &appId)
321bool ApplicationManager::focusApplication(const QString &appId)
296{
322{
297 ApplicationInfo *application = findApplication(appId);
323 ApplicationInfo *application = findApplication(appId);
tests/mocks/Unity/Application/ApplicationManager.h+6−2
@@ -99,8 +99,11 @@ class ApplicationManager : public ApplicationManagerInterface {
99 Q_INVOKABLE bool stopApplication(const QString &appId) override;
99 Q_INVOKABLE bool stopApplication(const QString &appId) override;
100
100
101 QString focusedApplicationId() const override;
101 QString focusedApplicationId() const override;
102− bool suspended() const;
102+ bool suspended() const override;
103− void setSuspended(bool suspended);
103+ void setSuspended(bool suspended) override;
104+
105+ bool forceDashActive() const override;
106+ void setForceDashActive(bool forceDashActive) override;
104
107
105 // Only for testing
108 // Only for testing
106 Q_INVOKABLE QStringList availableApplications();
109 Q_INVOKABLE QStringList availableApplications();
@@ -124,6 +127,7 @@ class ApplicationManager : public ApplicationManagerInterface {
124 void buildListOfAvailableApplications();
127 void buildListOfAvailableApplications();
125 void onWindowCreated();
128 void onWindowCreated();
126 bool m_suspended;
129 bool m_suspended;
130+ bool m_forceDashActive;
127 QList<ApplicationInfo*> m_runningApplications;
131 QList<ApplicationInfo*> m_runningApplications;
128 QList<ApplicationInfo*> m_availableApplications;
132 QList<ApplicationInfo*> m_availableApplications;
129 QTimer m_windowCreatedTimer;
133 QTimer m_windowCreatedTimer;
tests/mocks/Unity/Application/CMakeLists.txt+1−1
@@ -1,4 +1,4 @@
1−pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=4)
1+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=5)
2
2
3set(FakeUnityApplicationQml_SOURCES
3set(FakeUnityApplicationQml_SOURCES
4 plugin.cpp
4 plugin.cpp
tests/mocks/Unity/fake_scope.cpp+1−1
@@ -266,7 +266,7 @@ QVariantMap Scope::customizations() const
266
266
267void Scope::refresh()
267void Scope::refresh()
268{
268{
269− qDebug() << "Scope::refresh is currently not implmented in the fake scopes plugin";
269+ Q_EMIT refreshed();
270}
270}
271
271
272unity::shell::scopes::NavigationInterface* Scope::getNavigation(const QString& id)
272unity::shell::scopes::NavigationInterface* Scope::getNavigation(const QString& id)
tests/mocks/Unity/fake_scope.h+3
@@ -79,6 +79,9 @@ public:
79
79
80 Q_INVOKABLE void refresh() override;
80 Q_INVOKABLE void refresh() override;
81
81
82+Q_SIGNALS:
83+ void refreshed(); // This is not in the Interface, here for testing benefits
84+
82protected:
85protected:
83
86
84 QString m_id;
87 QString m_id;
tests/plugins/Dash/CMakeLists.txt+3−3
@@ -32,7 +32,7 @@ macro(add_lvwph_test FILENAME TESTNAME)
32 qt5_use_modules(${TESTNAME}TestExec Test Core Qml)
32 qt5_use_modules(${TESTNAME}TestExec Test Core Qml)
33 target_link_libraries(${TESTNAME}TestExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
33 target_link_libraries(${TESTNAME}TestExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
34
34
35− add_binary_qml_test(${TESTNAME} "" "Dash-qml")
35+ add_binary_qml_test(${TESTNAME} "" "Dash-qml" "")
36endmacro()
36endmacro()
37
37
38add_lvwph_test(listviewwithpageheader ListViewWithPageHeader)
38add_lvwph_test(listviewwithpageheader ListViewWithPageHeader)
@@ -48,7 +48,7 @@ macro(add_dashview_try_test FILENAME TESTNAME)
48 qt5_use_modules(${TESTNAME}TestExec Test Core Qml)
48 qt5_use_modules(${TESTNAME}TestExec Test Core Qml)
49 target_link_libraries(${TESTNAME}TestExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
49 target_link_libraries(${TESTNAME}TestExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
50
50
51− add_binary_qml_test(${TESTNAME} "" "Dash-qml")
51+ add_binary_qml_test(${TESTNAME} "" "Dash-qml" "")
52
52
53 add_executable(${TESTNAME}tryExec
53 add_executable(${TESTNAME}tryExec
54 ${FILENAME}try.cpp
54 ${FILENAME}try.cpp
@@ -71,7 +71,7 @@ add_dashview_try_test(organicgrid OrganicGrid)
71add_executable(CardCreatorTestExec cardcreatortest.cpp)
71add_executable(CardCreatorTestExec cardcreatortest.cpp)
72qt5_use_modules(CardCreatorTestExec Test Core Qml)
72qt5_use_modules(CardCreatorTestExec Test Core Qml)
73target_link_libraries(CardCreatorTestExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
73target_link_libraries(CardCreatorTestExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
74−add_binary_qml_test(CardCreator "" "Dash-qml")
74+add_binary_qml_test(CardCreator "" "Dash-qml" "QML2_IMPORT_PATH=${CMAKE_BINARY_DIR}/tests/mocks/")
75
75
76# plain qml test
76# plain qml test
77set(qmltest_DEFAULT_TARGETS qmluitests)
77set(qmltest_DEFAULT_TARGETS qmluitests)
tests/plugins/Dash/cardcreator/1.res+7−6
@@ -6,7 +6,7 @@ AbstractButton {
6 property var artShapeBorderSource: undefined;
6 property var artShapeBorderSource: undefined;
7 property real fontScale: 1.0;
7 property real fontScale: 1.0;
8 property var scopeStyle: null;
8 property var scopeStyle: null;
9− property int headerAlignment: Text.AlignLeft;
9+ property int titleAlignment: Text.AlignLeft;
10 property int fixedHeaderHeight: -1;
10 property int fixedHeaderHeight: -1;
11 property size fixedArtShapeSize: Qt.size(-1, -1);
11 property size fixedArtShapeSize: Qt.size(-1, -1);
12 readonly property string title: cardData && cardData["title"] || "";
12 readonly property string title: cardData && cardData["title"] || "";
@@ -44,12 +44,13 @@ Item {
44 height = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.height });
44 height = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.height });
45 }
45 }
46 }
46 }
47− image: Image {
47+ image: CroppedImageMinimumSourceSize {
48 objectName: "artImage";
48 objectName: "artImage";
49+ property bool doLoadSource: !NetworkingStatus.limitedBandwith;
49− source: cardData && cardData["art"] || "";
50+ source: { if (root.visible) doLoadSource = true; return doLoadSource && cardData && cardData["art"] || ""; }
50 cache: true;
51 cache: true;
51 asynchronous: root.asynchronous;
52 asynchronous: root.asynchronous;
52− fillMode: Image.PreserveAspectCrop;
53+ visible: false;
53 width: root.width;
54 width: root.width;
54 height: width / artShape.aspect;
55 height: width / artShape.aspect;
55 }
56 }
@@ -73,8 +74,8 @@ Label {
73 color: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText;
74 color: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText;
74 visible: showHeader ;
75 visible: showHeader ;
75 text: root.title;
76 text: root.title;
76− font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
77+ font.weight: cardData && cardData["subtitle"] ? Font.DemiBold : Font.Normal;
77− horizontalAlignment: root.headerAlignment;
78+ horizontalAlignment: root.titleAlignment;
78 }
79 }
79UbuntuShape {
80UbuntuShape {
80 id: touchdown;
81 id: touchdown;
tests/plugins/Dash/cardcreator/2.res+8−11
@@ -6,7 +6,7 @@ AbstractButton {
6 property var artShapeBorderSource: undefined;
6 property var artShapeBorderSource: undefined;
7 property real fontScale: 1.0;
7 property real fontScale: 1.0;
8 property var scopeStyle: null;
8 property var scopeStyle: null;
9− property int headerAlignment: Text.AlignLeft;
9+ property int titleAlignment: Text.AlignLeft;
10 property int fixedHeaderHeight: -1;
10 property int fixedHeaderHeight: -1;
11 property size fixedArtShapeSize: Qt.size(-1, -1);
11 property size fixedArtShapeSize: Qt.size(-1, -1);
12 readonly property string title: cardData && cardData["title"] || "";
12 readonly property string title: cardData && cardData["title"] || "";
@@ -62,19 +62,17 @@ Row {
62 anchors.margins: margins;
62 anchors.margins: margins;
63 anchors.rightMargin: 0;
63 anchors.rightMargin: 0;
64 data: [
64 data: [
65−Image {
65+CroppedImageMinimumSourceSize {
66 id: mascotImage;
66 id: mascotImage;
67 objectName: "mascotImage";
67 objectName: "mascotImage";
68 anchors { verticalCenter: parent.verticalCenter; }
68 anchors { verticalCenter: parent.verticalCenter; }
69− readonly property int maxSize: Math.max(width, height) * 4;
69+ property bool doLoadSource: !NetworkingStatus.limitedBandwith;
70− source: cardData && cardData["mascot"] || "";
70+ source: { if (root.visible) doLoadSource = true; return doLoadSource && cardData && cardData["mascot"] || ""; }
71 width: units.gu(6);
71 width: units.gu(6);
72 height: units.gu(5.625);
72 height: units.gu(5.625);
73− sourceSize { width: maxSize; height: maxSize }
74− fillMode: Image.PreserveAspectCrop;
75 horizontalAlignment: Image.AlignHCenter;
73 horizontalAlignment: Image.AlignHCenter;
76 verticalAlignment: Image.AlignVCenter;
74 verticalAlignment: Image.AlignVCenter;
77− visible: showHeader;
75+ visible: showHeader && resized;
78 }
76 }
79,Item {
77,Item {
80 id: headerTitleContainer;
78 id: headerTitleContainer;
@@ -97,8 +95,8 @@ Image {
97 color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item && backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
95 color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item && backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
98 visible: showHeader ;
96 visible: showHeader ;
99 text: root.title;
97 text: root.title;
100− font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
98+ font.weight: cardData && cardData["subtitle"] ? Font.DemiBold : Font.Normal;
101− horizontalAlignment: root.headerAlignment;
99+ horizontalAlignment: root.titleAlignment;
102 }
100 }
103 ,Label {
101 ,Label {
104 id: subtitleLabel;
102 id: subtitleLabel;
@@ -110,13 +108,12 @@ Image {
110 }
108 }
111 anchors.topMargin: units.dp(2);
109 anchors.topMargin: units.dp(2);
112 elide: Text.ElideRight;
110 elide: Text.ElideRight;
113− fontSize: "small";
111+ fontSize: "x-small";
114 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
112 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
115 color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item && backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
113 color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item && backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
116 visible: titleLabel.visible && titleLabel.text;
114 visible: titleLabel.visible && titleLabel.text;
117 text: cardData && cardData["subtitle"] || "";
115 text: cardData && cardData["subtitle"] || "";
118 font.weight: Font.Light;
116 font.weight: Font.Light;
119− horizontalAlignment: root.headerAlignment;
120 }
117 }
121
118
122 ]
119 ]
tests/plugins/Dash/cardcreator/3.res+9−9
@@ -6,7 +6,7 @@ AbstractButton {
6 property var artShapeBorderSource: undefined;
6 property var artShapeBorderSource: undefined;
7 property real fontScale: 1.0;
7 property real fontScale: 1.0;
8 property var scopeStyle: null;
8 property var scopeStyle: null;
9− property int headerAlignment: Text.AlignLeft;
9+ property int titleAlignment: Text.AlignLeft;
10 property int fixedHeaderHeight: -1;
10 property int fixedHeaderHeight: -1;
11 property size fixedArtShapeSize: Qt.size(-1, -1);
11 property size fixedArtShapeSize: Qt.size(-1, -1);
12 readonly property string title: cardData && cardData["title"] || "";
12 readonly property string title: cardData && cardData["title"] || "";
@@ -44,12 +44,13 @@ Item {
44 height = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.height });
44 height = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.height });
45 }
45 }
46 }
46 }
47− image: Image {
47+ image: CroppedImageMinimumSourceSize {
48 objectName: "artImage";
48 objectName: "artImage";
49+ property bool doLoadSource: !NetworkingStatus.limitedBandwith;
49− source: cardData && cardData["art"] || "";
50+ source: { if (root.visible) doLoadSource = true; return doLoadSource && cardData && cardData["art"] || ""; }
50− cache: true;
51+ cache: true;
51 asynchronous: root.asynchronous;
52 asynchronous: root.asynchronous;
52− fillMode: Image.PreserveAspectCrop;
53+ visible: false;
53 width: root.width;
54 width: root.width;
54 height: width / artShape.aspect;
55 height: width / artShape.aspect;
55 }
56 }
@@ -73,8 +74,8 @@ Label {
73 color: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText;
74 color: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText;
74 visible: showHeader ;
75 visible: showHeader ;
75 text: root.title;
76 text: root.title;
76− font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
77+ font.weight: cardData && cardData["subtitle"] ? Font.DemiBold : Font.Normal;
77− horizontalAlignment: root.headerAlignment;
78+ horizontalAlignment: root.titleAlignment;
78 }
79 }
79Label {
80Label {
80 id: subtitleLabel;
81 id: subtitleLabel;
@@ -86,13 +87,12 @@ Label {
86 }
87 }
87 anchors.topMargin: units.dp(2);
88 anchors.topMargin: units.dp(2);
88 elide: Text.ElideRight;
89 elide: Text.ElideRight;
89− fontSize: "small";
90+ fontSize: "x-small";
90 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
91 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
91 color: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText;
92 color: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText;
92 visible: titleLabel.visible && titleLabel.text;
93 visible: titleLabel.visible && titleLabel.text;
93 text: cardData && cardData["subtitle"] || "";
94 text: cardData && cardData["subtitle"] || "";
94 font.weight: Font.Light;
95 font.weight: Font.Light;
95− horizontalAlignment: root.headerAlignment;
96 }
96 }
97UbuntuShape {
97UbuntuShape {
98 id: touchdown;
98 id: touchdown;
tests/plugins/Dash/cardcreator/4.res+8−11
@@ -6,7 +6,7 @@ AbstractButton {
6 property var artShapeBorderSource: undefined;
6 property var artShapeBorderSource: undefined;
7 property real fontScale: 1.0;
7 property real fontScale: 1.0;
8 property var scopeStyle: null;
8 property var scopeStyle: null;
9− property int headerAlignment: Text.AlignLeft;
9+ property int titleAlignment: Text.AlignLeft;
10 property int fixedHeaderHeight: -1;
10 property int fixedHeaderHeight: -1;
11 property size fixedArtShapeSize: Qt.size(-1, -1);
11 property size fixedArtShapeSize: Qt.size(-1, -1);
12 readonly property string title: cardData && cardData["title"] || "";
12 readonly property string title: cardData && cardData["title"] || "";
@@ -41,16 +41,14 @@ Loader {
41 anchors { verticalCenter: parent.verticalCenter; }
41 anchors { verticalCenter: parent.verticalCenter; }
42 }
42 }
43
43
44−,Image {
44+,CroppedImageMinimumSourceSize {
45 id: mascotImage;
45 id: mascotImage;
46 objectName: "mascotImage";
46 objectName: "mascotImage";
47 anchors { verticalCenter: parent.verticalCenter; }
47 anchors { verticalCenter: parent.verticalCenter; }
48− readonly property int maxSize: Math.max(width, height) * 4;
48+ property bool doLoadSource: !NetworkingStatus.limitedBandwith;
49− source: cardData && cardData["mascot"] || "";
49+ source: { if (root.visible) doLoadSource = true; return doLoadSource && cardData && cardData["mascot"] || ""; }
50− width: units.gu(6);
50+ width: units.gu(6);
51 height: units.gu(5.625);
51 height: units.gu(5.625);
52− sourceSize { width: maxSize; height: maxSize }
53− fillMode: Image.PreserveAspectCrop;
54 horizontalAlignment: Image.AlignHCenter;
52 horizontalAlignment: Image.AlignHCenter;
55 verticalAlignment: Image.AlignVCenter;
53 verticalAlignment: Image.AlignVCenter;
56 visible: false;
54 visible: false;
@@ -77,8 +75,8 @@ Label {
77 color: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText;
75 color: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText;
78 visible: showHeader ;
76 visible: showHeader ;
79 text: root.title;
77 text: root.title;
80− font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
78+ font.weight: cardData && cardData["subtitle"] ? Font.DemiBold : Font.Normal;
81− horizontalAlignment: root.headerAlignment;
79+ horizontalAlignment: root.titleAlignment;
82 }
80 }
83,Label {
81,Label {
84 id: subtitleLabel;
82 id: subtitleLabel;
@@ -90,13 +88,12 @@ Label {
90 }
88 }
91 anchors.topMargin: units.dp(2);
89 anchors.topMargin: units.dp(2);
92 elide: Text.ElideRight;
90 elide: Text.ElideRight;
93− fontSize: "small";
91+ fontSize: "x-small";
94 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
92 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
95 color: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText;
93 color: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText;
96 visible: titleLabel.visible && titleLabel.text;
94 visible: titleLabel.visible && titleLabel.text;
97 text: cardData && cardData["subtitle"] || "";
95 text: cardData && cardData["subtitle"] || "";
98 font.weight: Font.Light;
96 font.weight: Font.Light;
99− horizontalAlignment: root.headerAlignment;
100 }
97 }
101]
98]
102}
99}
tests/plugins/Dash/cardcreator/5.res+9−9
@@ -6,7 +6,7 @@ AbstractButton {
6 property var artShapeBorderSource: undefined;
6 property var artShapeBorderSource: undefined;
7 property real fontScale: 1.0;
7 property real fontScale: 1.0;
8 property var scopeStyle: null;
8 property var scopeStyle: null;
9− property int headerAlignment: Text.AlignLeft;
9+ property int titleAlignment: Text.AlignLeft;
10 property int fixedHeaderHeight: -1;
10 property int fixedHeaderHeight: -1;
11 property size fixedArtShapeSize: Qt.size(-1, -1);
11 property size fixedArtShapeSize: Qt.size(-1, -1);
12 readonly property string title: cardData && cardData["title"] || "";
12 readonly property string title: cardData && cardData["title"] || "";
@@ -44,12 +44,13 @@ Item {
44 height = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.height });
44 height = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.height });
45 }
45 }
46 }
46 }
47− image: Image {
47+ image: CroppedImageMinimumSourceSize {
48 objectName: "artImage";
48 objectName: "artImage";
49+ property bool doLoadSource: !NetworkingStatus.limitedBandwith;
49− source: cardData && cardData["art"] || "";
50+ source: { if (root.visible) doLoadSource = true; return doLoadSource && cardData && cardData["art"] || ""; }
50− cache: true;
51+ cache: true;
51 asynchronous: root.asynchronous;
52 asynchronous: root.asynchronous;
52− fillMode: Image.PreserveAspectCrop;
53+ visible: false;
53 width: root.width;
54 width: root.width;
54 height: width / artShape.aspect;
55 height: width / artShape.aspect;
55 }
56 }
@@ -117,8 +118,8 @@ Label {
117 color: root.scopeStyle && overlayLoader.item ? root.scopeStyle.getTextColor(overlayLoader.item.luminance) : (overlayLoader.item && overlayLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
118 color: root.scopeStyle && overlayLoader.item ? root.scopeStyle.getTextColor(overlayLoader.item.luminance) : (overlayLoader.item && overlayLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
118 visible: showHeader && overlayLoader.active;
119 visible: showHeader && overlayLoader.active;
119 text: root.title;
120 text: root.title;
120− font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
121+ font.weight: cardData && cardData["subtitle"] ? Font.DemiBold : Font.Normal;
121− horizontalAlignment: root.headerAlignment;
122+ horizontalAlignment: root.titleAlignment;
122 }
123 }
123Label {
124Label {
124 id: subtitleLabel;
125 id: subtitleLabel;
@@ -131,13 +132,12 @@ Label {
131 }
132 }
132 anchors.topMargin: units.dp(2);
133 anchors.topMargin: units.dp(2);
133 elide: Text.ElideRight;
134 elide: Text.ElideRight;
134− fontSize: "small";
135+ fontSize: "x-small";
135 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
136 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
136 color: root.scopeStyle && overlayLoader.item ? root.scopeStyle.getTextColor(overlayLoader.item.luminance) : (overlayLoader.item && overlayLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
137 color: root.scopeStyle && overlayLoader.item ? root.scopeStyle.getTextColor(overlayLoader.item.luminance) : (overlayLoader.item && overlayLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
137 visible: titleLabel.visible && titleLabel.text;
138 visible: titleLabel.visible && titleLabel.text;
138 text: cardData && cardData["subtitle"] || "";
139 text: cardData && cardData["subtitle"] || "";
139 font.weight: Font.Light;
140 font.weight: Font.Light;
140− horizontalAlignment: root.headerAlignment;
141 }
141 }
142UbuntuShape {
142UbuntuShape {
143 id: touchdown;
143 id: touchdown;
tests/plugins/Dash/cardcreator/6.res+5−7
@@ -6,7 +6,7 @@ AbstractButton {
6 property var artShapeBorderSource: undefined;
6 property var artShapeBorderSource: undefined;
7 property real fontScale: 1.0;
7 property real fontScale: 1.0;
8 property var scopeStyle: null;
8 property var scopeStyle: null;
9− property int headerAlignment: Text.AlignLeft;
9+ property int titleAlignment: Text.AlignLeft;
10 property int fixedHeaderHeight: -1;
10 property int fixedHeaderHeight: -1;
11 property size fixedArtShapeSize: Qt.size(-1, -1);
11 property size fixedArtShapeSize: Qt.size(-1, -1);
12 readonly property string title: cardData && cardData["title"] || "";
12 readonly property string title: cardData && cardData["title"] || "";
@@ -73,8 +73,8 @@ Item {
73 color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item && backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
73 color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item && backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
74 visible: showHeader ;
74 visible: showHeader ;
75 text: root.title;
75 text: root.title;
76− font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
76+ font.weight: cardData && cardData["subtitle"] ? Font.DemiBold : Font.Normal;
77− horizontalAlignment: root.headerAlignment;
77+ horizontalAlignment: root.titleAlignment;
78 }
78 }
79,Label {
79,Label {
80 id: subtitleLabel;
80 id: subtitleLabel;
@@ -86,15 +86,14 @@ Item {
86 }
86 }
87 anchors.topMargin: units.dp(2);
87 anchors.topMargin: units.dp(2);
88 elide: Text.ElideRight;
88 elide: Text.ElideRight;
89− fontSize: "small";
89+ fontSize: "x-small";
90 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
90 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
91 color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item && backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
91 color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item && backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
92 visible: titleLabel.visible && titleLabel.text;
92 visible: titleLabel.visible && titleLabel.text;
93 text: cardData && cardData["subtitle"] || "";
93 text: cardData && cardData["subtitle"] || "";
94 font.weight: Font.Light;
94 font.weight: Font.Light;
95− horizontalAlignment: root.headerAlignment;
96 }
95 }
97−,Icon {
96+,StatusIcon {
98 id: emblemIcon;
97 id: emblemIcon;
99 objectName: "emblemIcon";
98 objectName: "emblemIcon";
100 anchors {
99 anchors {
@@ -104,7 +103,6 @@ Item {
104 }
103 }
105 source: cardData && cardData["emblem"] || "";
104 source: cardData && cardData["emblem"] || "";
106 color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item && backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
105 color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item && backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
107− width: height;
108 height: source != "" ? titleLabel.font.pixelSize : 0;
106 height: source != "" ? titleLabel.font.pixelSize : 0;
109 }
107 }
110
108
tests/plugins/Dash/cardcreator/7.res+9−12
@@ -6,7 +6,7 @@ AbstractButton {
6 property var artShapeBorderSource: undefined;
6 property var artShapeBorderSource: undefined;
7 property real fontScale: 1.0;
7 property real fontScale: 1.0;
8 property var scopeStyle: null;
8 property var scopeStyle: null;
9− property int headerAlignment: Text.AlignLeft;
9+ property int titleAlignment: Text.AlignLeft;
10 property int fixedHeaderHeight: -1;
10 property int fixedHeaderHeight: -1;
11 property size fixedArtShapeSize: Qt.size(-1, -1);
11 property size fixedArtShapeSize: Qt.size(-1, -1);
12 readonly property string title: cardData && cardData["title"] || "";
12 readonly property string title: cardData && cardData["title"] || "";
@@ -62,19 +62,17 @@ Row {
62 anchors.margins: margins;
62 anchors.margins: margins;
63 anchors.rightMargin: 0;
63 anchors.rightMargin: 0;
64 data: [
64 data: [
65−Image {
65+CroppedImageMinimumSourceSize {
66 id: mascotImage;
66 id: mascotImage;
67 objectName: "mascotImage";
67 objectName: "mascotImage";
68 anchors { verticalCenter: parent.verticalCenter; }
68 anchors { verticalCenter: parent.verticalCenter; }
69− readonly property int maxSize: Math.max(width, height) * 4;
69+ property bool doLoadSource: !NetworkingStatus.limitedBandwith;
70− source: cardData && cardData["mascot"] || "";
70+ source: { if (root.visible) doLoadSource = true; return doLoadSource && cardData && cardData["mascot"] || ""; }
71− width: units.gu(6);
71+ width: units.gu(6);
72 height: units.gu(5.625);
72 height: units.gu(5.625);
73− sourceSize { width: maxSize; height: maxSize }
74− fillMode: Image.PreserveAspectCrop;
75 horizontalAlignment: Image.AlignHCenter;
73 horizontalAlignment: Image.AlignHCenter;
76 verticalAlignment: Image.AlignVCenter;
74 verticalAlignment: Image.AlignVCenter;
77− visible: showHeader;
75+ visible: showHeader && resized;
78 }
76 }
79
77
80,Item {
78,Item {
@@ -98,8 +96,8 @@ Image {
98 color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item && backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
96 color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item && backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
99 visible: showHeader ;
97 visible: showHeader ;
100 text: root.title;
98 text: root.title;
101− font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
99+ font.weight: cardData && cardData["subtitle"] ? Font.DemiBold : Font.Normal;
102− horizontalAlignment: root.headerAlignment;
100+ horizontalAlignment: root.titleAlignment;
103 }
101 }
104,Label {
102,Label {
105 id: subtitleLabel;
103 id: subtitleLabel;
@@ -111,13 +109,12 @@ Image {
111 }
109 }
112 anchors.topMargin: units.dp(2);
110 anchors.topMargin: units.dp(2);
113 elide: Text.ElideRight;
111 elide: Text.ElideRight;
114− fontSize: "small";
112+ fontSize: "x-small";
115 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
113 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
116 color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item && backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
114 color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item && backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white");
117 visible: titleLabel.visible && titleLabel.text;
115 visible: titleLabel.visible && titleLabel.text;
118 text: cardData && cardData["subtitle"] || "";
116 text: cardData && cardData["subtitle"] || "";
119 font.weight: Font.Light;
117 font.weight: Font.Light;
120− horizontalAlignment: root.headerAlignment;
121 }
118 }
122,CardAttributes {
119,CardAttributes {
123 id: attributesRow;
120 id: attributesRow;
tests/plugins/Dash/listviewwithpageheadertest.cpp+39
@@ -28,6 +28,8 @@
28#include <private/qquickitem_p.h>
28#include <private/qquickitem_p.h>
29#pragma GCC diagnostic pop
29#pragma GCC diagnostic pop
30
30
31+#include <limits>
32+
31// TODO Think on how doing a test for lost items
33// TODO Think on how doing a test for lost items
32// particullary making sure that lost items are culled
34// particullary making sure that lost items are culled
33// and then removed in the next updatePolish cycle
35// and then removed in the next updatePolish cycle
@@ -1899,6 +1901,43 @@ private Q_SLOTS:
1899 QTRY_COMPARE(lvwph->m_firstVisibleIndex, 0);
1901 QTRY_COMPARE(lvwph->m_firstVisibleIndex, 0);
1900 }
1902 }
1901
1903
1904+ void testNoCacheBuffer()
1905+ {
1906+ lvwph->setCacheBuffer(0);
1907+ QTRY_COMPARE(lvwph->m_visibleItems.count(), 3);
1908+ QCOMPARE(lvwph->m_firstVisibleIndex, 0);
1909+ verifyItem(0, 50., 150., false);
1910+ verifyItem(1, 200., 200., false);
1911+ verifyItem(2, 400., 350., false);
1912+ QCOMPARE(lvwph->m_minYExtent, 0.);
1913+ QCOMPARE(lvwph->m_clipItem->y(), 0.);
1914+ QCOMPARE(lvwph->m_clipItem->clip(), false);
1915+ QCOMPARE(lvwph->m_headerItem->y(), 0.);
1916+ QCOMPARE(lvwph->m_headerItem->height(), 50.);
1917+ QCOMPARE(lvwph->contentY(), 0.);
1918+ QCOMPARE(lvwph->m_headerItemShownHeight, 0.);
1919+ }
1920+
1921+ void testAllCacheBuffer()
1922+ {
1923+ lvwph->setCacheBuffer(std::numeric_limits<qreal>::max());
1924+ QTRY_COMPARE(lvwph->m_visibleItems.count(), 6);
1925+ QCOMPARE(lvwph->m_firstVisibleIndex, 0);
1926+ verifyItem(0, 50., 150., false);
1927+ verifyItem(1, 200., 200., false);
1928+ verifyItem(2, 400., 350., false);
1929+ verifyItem(3, 750., 350., true);
1930+ verifyItem(4, 1100., 350., true);
1931+ verifyItem(5, 1450., 350., true);
1932+ QCOMPARE(lvwph->m_minYExtent, 0.);
1933+ QCOMPARE(lvwph->m_clipItem->y(), 0.);
1934+ QCOMPARE(lvwph->m_clipItem->clip(), false);
1935+ QCOMPARE(lvwph->m_headerItem->y(), 0.);
1936+ QCOMPARE(lvwph->m_headerItem->height(), 50.);
1937+ QCOMPARE(lvwph->contentY(), 0.);
1938+ QCOMPARE(lvwph->m_headerItemShownHeight, 0.);
1939+ }
1940+
1902private:
1941private:
1903 QQuickView *view;
1942 QQuickView *view;
1904 ListViewWithPageHeader *lvwph;
1943 ListViewWithPageHeader *lvwph;
tests/plugins/Dash/listviewwithpageheadertest.qml+1
@@ -60,6 +60,7 @@ Rectangle {
60 anchors.top: parent.top
60 anchors.top: parent.top
61 anchors.bottom: parent.bottom
61 anchors.bottom: parent.bottom
62 model: model
62 model: model
63+ cacheBuffer: height * 0.5
63 delegate: Rectangle {
64 delegate: Rectangle {
64 property bool timerDone: false
65 property bool timerDone: false
65 width: parent.width - 20
66 width: parent.width - 20
tests/plugins/Dash/listviewwithpageheadertestsection.qml+1
@@ -60,6 +60,7 @@ Rectangle {
60 anchors.top: parent.top
60 anchors.top: parent.top
61 anchors.bottom: parent.bottom
61 anchors.bottom: parent.bottom
62 model: model
62 model: model
63+ cacheBuffer: height * 0.5
63 delegate: Rectangle {
64 delegate: Rectangle {
64 property bool timerDone: false
65 property bool timerDone: false
65 width: parent.width - 20
66 width: parent.width - 20
tests/plugins/Dash/listviewwithpageheadertestsectionexternalmodel.qml+1
@@ -28,6 +28,7 @@ Rectangle {
28 anchors.top: parent.top
28 anchors.top: parent.top
29 anchors.bottom: parent.bottom
29 anchors.bottom: parent.bottom
30 model: model
30 model: model
31+ cacheBuffer: height * 0.5
31 delegate: Rectangle {
32 delegate: Rectangle {
32 property bool timerDone: false
33 property bool timerDone: false
33 width: parent.width - 20
34 width: parent.width - 20
tests/plugins/Ubuntu/Gestures/CMakeLists.txt+1−1
@@ -19,7 +19,7 @@ macro(add_gesture_ui_test CLASSNAME)
19 qt5_use_modules(${CLASSNAME}TestExec Test Core Qml Gui Quick)
19 qt5_use_modules(${CLASSNAME}TestExec Test Core Qml Gui Quick)
20 target_link_libraries(${CLASSNAME}TestExec UbuntuGestureQml)
20 target_link_libraries(${CLASSNAME}TestExec UbuntuGestureQml)
21
21
22− add_binary_qml_test(${CLASSNAME} ${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures UbuntuGesturesTestQmlFiles)
22+ add_binary_qml_test(${CLASSNAME} ${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures UbuntuGesturesTestQmlFiles "")
23 add_manual_qml_test(. ${CLASSNAME} IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
23 add_manual_qml_test(. ${CLASSNAME} IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
24endmacro(add_gesture_ui_test)
24endmacro(add_gesture_ui_test)
25
25
tests/plugins/Unity/Launcher/CMakeLists.txt+1−1
@@ -1,6 +1,6 @@
1pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
1pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
2pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=4)
2pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=4)
3−pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=4)
3+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=5)
4
4
5include_directories(
5include_directories(
6 ${CMAKE_CURRENT_BINARY_DIR}
6 ${CMAKE_CURRENT_BINARY_DIR}
tests/plugins/Unity/Launcher/launchermodeltest.cpp+2
@@ -105,6 +105,8 @@ public:
105 bool requestFocusApplication(const QString &appId) { Q_UNUSED(appId); return true; }
105 bool requestFocusApplication(const QString &appId) { Q_UNUSED(appId); return true; }
106 bool suspended() const { return false; }
106 bool suspended() const { return false; }
107 void setSuspended(bool) {}
107 void setSuspended(bool) {}
108+ bool forceDashActive() const { return false; }
109+ void setForceDashActive(bool) {}
108
110
109private:
111private:
110 QList<MockApp*> m_list;
112 QList<MockApp*> m_list;
tests/qmltests/Components/CMakeLists.txt+1−1
@@ -10,7 +10,7 @@ add_executable(DragHandleTestExec tst_DragHandle.cpp)
10qt5_use_modules(DragHandleTestExec Test Core Qml Gui Quick)
10qt5_use_modules(DragHandleTestExec Test Core Qml Gui Quick)
11target_link_libraries(DragHandleTestExec UbuntuGestureQml)
11target_link_libraries(DragHandleTestExec UbuntuGestureQml)
12
12
13−add_binary_qml_test(DragHandle ${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures "")
13+add_binary_qml_test(DragHandle ${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures "" "")
14add_manual_qml_test(. DragHandle IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
14add_manual_qml_test(. DragHandle IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
15
15
16add_definitions(-DUBUNTU_GESTURES_PLUGIN_DIR="${CMAKE_BINARY_DIR}/plugins")
16add_definitions(-DUBUNTU_GESTURES_PLUGIN_DIR="${CMAKE_BINARY_DIR}/plugins")
tests/qmltests/Dash/CardHelpers.js+1−12
@@ -16,7 +16,7 @@
16
16
17.pragma library
17.pragma library
18
18
19−var components = ["title", "art", "subtitle", "mascot", "emblem", "old-price", "price", "alt-price", "rating", "alt-rating", "summary", "attributes", "overlayColor"]
19+var components = ["title", "art", "subtitle", "mascot", "emblem", "summary", "attributes", "overlayColor"]
20
20
21var defaultLayout = ' \
21var defaultLayout = ' \
22{ \
22{ \
@@ -37,17 +37,6 @@ var defaultLayout = ' \
37 "overlayColor": null, \
37 "overlayColor": null, \
38 "mascot": null, \
38 "mascot": null, \
39 "emblem": null, \
39 "emblem": null, \
40− "old-price": null, \
41− "price": null, \
42− "alt-price": null, \
43− "rating": { \
44− "type": "stars", \
45− "range": [0, 5], \
46− "full": "image://theme/rating-star-full", \
47− "half": "image://theme/rating-star-half", \
48− "empty": "image://theme/rating-star-empty" \
49− }, \
50− "alt-rating": null, \
51 "summary": null, \
40 "summary": null, \
52 "attributes": { "max-count": 2 } \
41 "attributes": { "max-count": 2 } \
53 }, \
42 }, \
tests/qmltests/Dash/Previews/tst_PreviewHeader.qml+4−2
@@ -27,13 +27,15 @@ Rectangle {
27 property var origHeaderjson: {
27 property var origHeaderjson: {
28 "title": "THE TITLE",
28 "title": "THE TITLE",
29 "subtitle": "Something catchy",
29 "subtitle": "Something catchy",
30− "mascot": "../graphics/play_button.png"
30+ "mascot": "../graphics/play_button.png",
31+ "attributes": [{"value":"text1","icon":"image://theme/ok"},{"value":"text2","icon":"image://theme/cancel"}]
31 }
32 }
32
33
33 property var headerjson: {
34 property var headerjson: {
34 "title": "THE TITLE",
35 "title": "THE TITLE",
35 "subtitle": "Something catchy",
36 "subtitle": "Something catchy",
36− "mascot": "../graphics/play_button.png"
37+ "mascot": "../graphics/play_button.png",
38+ "attributes": [{"value":"text1","icon":"image://theme/ok"},{"value":"text2","icon":"image://theme/cancel"}]
37 }
39 }
38
40
39 PreviewHeader {
41 PreviewHeader {
tests/qmltests/Dash/Previews/tst_PreviewImageGallery.qml+51−1
@@ -21,7 +21,7 @@ import Unity.Test 0.1 as UT
21
21
22Rectangle {
22Rectangle {
23 id: root
23 id: root
24− width: units.gu(60)
24+ width: units.gu(80)
25 height: units.gu(80)
25 height: units.gu(80)
26 color: "lightgrey"
26 color: "lightgrey"
27
27
@@ -44,13 +44,63 @@ Rectangle {
44 }
44 }
45
45
46 UT.UnityTestCase {
46 UT.UnityTestCase {
47+ id: testCase
47 name: "PreviewImageGalleryTest"
48 name: "PreviewImageGalleryTest"
48 when: windowShown
49 when: windowShown
49
50
51+ property Item slideShow: findChild(imageGallery.rootItem, "slideShow")
52+ property Item slideShowCloseButton: findChild(slideShow, "slideShowCloseButton")
53+ property Item slideShowListView: findChild(slideShow, "slideShowListView")
54+
55+ function cleanup() {
56+ slideShow.visible = false;
57+ imageGallery.widgetData = sourcesModel1;
58+ waitForRendering(imageGallery);
59+ }
60+
50 function test_changeEmptyModel() {
61 function test_changeEmptyModel() {
51 imageGallery.widgetData = sourcesModel0;
62 imageGallery.widgetData = sourcesModel0;
52 var placeholderScreenshot = findChild(imageGallery, "placeholderScreenshot");
63 var placeholderScreenshot = findChild(imageGallery, "placeholderScreenshot");
53 compare(placeholderScreenshot.visible, true);
64 compare(placeholderScreenshot.visible, true);
54 }
65 }
66+
67+ function test_slideShowOpenClose() {
68+ var image0 = findChild(imageGallery, "previewImage0");
69+ mouseClick(image0, image0.width / 2, image0.height / 2);
70+ tryCompare(slideShow, "visible", true);
71+ tryCompare(slideShow, "scale", 1.0);
72+ tryCompare(slideShowCloseButton, "visible", true);
73+ mouseClick(slideShowCloseButton, slideShowCloseButton.width / 2, slideShowCloseButton.height / 2);
74+ tryCompare(slideShow, "visible", false);
75+ }
76+
77+ function test_slideShowShowHideHeader() {
78+ var image0 = findChild(imageGallery, "previewImage0");
79+ mouseClick(image0, image0.width / 2, image0.height / 2);
80+ tryCompare(slideShow, "visible", true);
81+ tryCompare(slideShow, "scale", 1.0);
82+ tryCompare(slideShowCloseButton, "visible", true);
83+ mouseClick(slideShow, slideShow.width / 2, slideShow.height / 2);
84+ tryCompare(slideShowCloseButton, "visible", false);
85+ mouseClick(slideShow, slideShow.width / 2, slideShow.height / 2);
86+ tryCompare(slideShowCloseButton, "visible", true);
87+ }
88+
89+ function test_slideShowOpenCorrectImage_data() {
90+ return [
91+ { tag: "Image 0", index: 0 },
92+ { tag: "Image 1", index: 1 },
93+ { tag: "Image 2", index: 2 },
94+ ];
95+ }
96+
97+ function test_slideShowOpenCorrectImage(data) {
98+ var image = findChild(imageGallery, "previewImage" + data.index);
99+ mouseClick(image, image.width / 2, image.height / 2);
100+ tryCompare(slideShow, "visible", true);
101+ tryCompare(slideShow, "scale", 1.0);
102+ tryCompare(slideShowListView, "currentIndex", data.index);
103+ verify(image.source === slideShowListView.currentItem.source);
104+ }
55 }
105 }
56}
106}
tests/qmltests/Dash/tst_Card.qml+26
@@ -17,6 +17,7 @@
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
20import Unity.Test 0.1 as UT
21import Unity.Test 0.1 as UT
21import "../../../qml/Dash"
22import "../../../qml/Dash"
22import "CardHelpers.js" as Helpers
23import "CardHelpers.js" as Helpers
@@ -205,6 +206,11 @@ Rectangle {
205 property Item backgroundImage: findChild(card, "backgroundImage")
206 property Item backgroundImage: findChild(card, "backgroundImage")
206 property Item mascotImage: findChild(card, "mascotImage");
207 property Item mascotImage: findChild(card, "mascotImage");
207
208
209+ function init() {
210+ loader.visible = true;
211+ NetworkingStatus.limitedBandwith = false;
212+ }
213+
208 function cleanup() {
214 function cleanup() {
209 selector.selectedIndex = -1;
215 selector.selectedIndex = -1;
210 }
216 }
@@ -566,5 +572,25 @@ Rectangle {
566 verify((card.width - titleToCard.x - titleToCard.width) === units.gu(1));
572 verify((card.width - titleToCard.x - titleToCard.width) === units.gu(1));
567 }
573 }
568 }
574 }
575+
576+ function test_load_images_visibility_network_data() {
577+ return [
578+ { tag: "Visible, network", visible: true, limitedBandwith: false },
579+ { tag: "Visible, no network", visible: true, limitedBandwith: true },
580+ { tag: "Not Visible, network", visible: false, limitedBandwith: false },
581+ { tag: "Not Visible, no network", visible: false, limitedBandwith: true }
582+ ];
583+ }
584+
585+ function test_load_images_visibility_network(data) {
586+ loader.visible = data.visible;
587+ NetworkingStatus.limitedBandwith = data.limitedBandwith;
588+
589+ selector.selectedIndex = 0;
590+ waitForRendering(selector);
591+ waitForRendering(card);
592+
593+ verify(data.visible || !data.limitedBandwith || artImage.source == "");
594+ }
569 }
595 }
570}
596}
tests/qmltests/Dash/tst_CardTool.qml+28−20
@@ -85,8 +85,8 @@ Rectangle {
85 "layout": { "template": { "card-layout": "vertical" }, "components": { "title": "title", "subtitle": "subtitle" } }
85 "layout": { "template": { "card-layout": "vertical" }, "components": { "title": "title", "subtitle": "subtitle" } }
86 },
86 },
87 {
87 {
88− "name": "Title, price - horizontal",
88+ "name": "Title, attributes - horizontal",
89− "layout": { "template": { "card-layout": "horizontal" }, "components": { "title": "title", "price": "price" } }
89+ "layout": { "template": { "card-layout": "horizontal" }, "components": { "title": "title", "attributes": "attributes" } }
90 },
90 },
91 ]
91 ]
92
92
@@ -100,6 +100,7 @@ Rectangle {
100 }
100 }
101
101
102 Column {
102 Column {
103+ id: column
103 width: units.gu(38)
104 width: units.gu(38)
104 anchors { left: parent.left; top: parent.top; margins: units.gu(1) }
105 anchors { left: parent.left; top: parent.top; margins: units.gu(1) }
105
106
@@ -111,7 +112,7 @@ Rectangle {
111 ]
112 ]
112
113
113 delegate: Row {
114 delegate: Row {
114− anchors { left: parent.left; right: parent.right; margins: units.gu(3) }
115+ anchors { left: column.left; right: column.right; margins: units.gu(3) }
115
116
116 Label {
117 Label {
117 height: units.gu(5)
118 height: units.gu(5)
@@ -315,28 +316,35 @@ Rectangle {
315 }
316 }
316 }
317 }
317
318
318− function test_card_header_component_alignment_data() {
319+ function test_card_title_alignment_data() {
319 return [
320 return [
320− { tag: "Title - vertical", component: "titleLabel", property: "headerAlignment",
321+ { tag: "Art, header, summary", value: Text.AlignLeft, index: 0 },
322+ { tag: "Header, summary", value: Text.AlignLeft, index: 1 },
323+ { tag: "Art, header", value: Text.AlignLeft, index: 2 },
324+ { tag: "Header only", value: Text.AlignLeft, index: 3 },
325+ { tag: "Header title only", value: Text.AlignHCenter, index: 4 },
326+ { tag: "Header title and subtitle", value: Text.AlignLeft, index: 5 },
327+ { tag: "Header title and mascot", value: Text.AlignLeft, index: 6 },
328+ { tag: "Art, header, summary - small", value: Text.AlignLeft, index: 7 },
329+ { tag: "Art, header, summary - large", value: Text.AlignLeft, index: 8 },
330+ { tag: "Art, header, summary - horizontal", value: Text.AlignLeft, index: 9 },
331+ { tag: "Art, header - portrait", value: Text.AlignLeft, index: 10 },
321− value: Text.AlignHCenter, index: 11, layout_index: 0 },
332+ { tag: "Title - vertical", value: Text.AlignHCenter, index: 11 },
322− { tag: "Title - horizontal", component: "titleLabel", property: "headerAlignment",
323− value: Text.AlignLeft, index: 12, layout_index: 0},
333+ { tag: "Title - horizontal", value: Text.AlignLeft, index: 12 },
324− { tag: "Title, subtitle - vertical", component: "titleLabel", property: "headerAlignment",
325− value: Text.AlignLeft, index: 13, layout_index: 0},
334+ { tag: "Title, subtitle - vertical", value: Text.AlignLeft, index: 13 },
326− { tag: "Title, price - horizontal", component: "titleLabel", property: "headerAlignment",
327− value: Text.AlignLeft, index: 14, layout_index: 0},
335+ { tag: "Title, attributes - horizontal", value: Text.AlignLeft, index: 14 },
328− ]
336+ ];
329 }
337 }
330
338
331− function test_card_header_component_alignment(data) {
339+ function test_card_title_alignment(data) {
332 selector.selectedIndex = data.index;
340 selector.selectedIndex = data.index;
333− if (data.hasOwnProperty("layout_index")) {
334− layoutSelector.selectedIndex = data.layout_index;
335− }
336
341
337− if (data.hasOwnProperty("property")) {
342+ tryCompare(cardTool, "titleAlignment", Text.AlignLeft);
338− tryCompare(cardTool, data.property, data.value);
343+
339− }
344+ cardTool.components['title'] = { "field": "title", "align": "center" };
345+ cardTool.componentsChanged();
346+
347+ tryCompare(cardTool, "titleAlignment", data.value);
340 }
348 }
341
349
342 function test_categoryLayout_data() {
350 function test_categoryLayout_data() {
tests/qmltests/Dash/tst_Dash.qml+16−39
@@ -55,6 +55,16 @@ Item {
55 scopes.load();
55 scopes.load();
56 tryCompare(dashContentList, "currentIndex", 0);
56 tryCompare(dashContentList, "currentIndex", 0);
57 tryCompare(dashContentList, "count", 6);
57 tryCompare(dashContentList, "count", 6);
58+ tryCompare(scopes, "loaded", true);
59+ tryCompareFunction(function() {
60+ var mockScope1Loader = findChild(dash, "MockScope1 loader");
61+ return mockScope1Loader && mockScope1Loader.item != null; },
62+ true, 15000);
63+ tryCompareFunction(function() {
64+ var mockScope1Loader = findChild(dash, "MockScope1 loader");
65+ return mockScope1Loader && mockScope1Loader.status === Loader.Ready; },
66+ true, 15000);
67+ waitForRendering(findChild(dash, "MockScope1 loader").item);
58 }
68 }
59
69
60 function get_scope_data() {
70 function get_scope_data() {
@@ -92,15 +102,6 @@ Item {
92 }
102 }
93
103
94 function test_dash_overview_show_select_same_favorite() {
104 function test_dash_overview_show_select_same_favorite() {
95− // Wait for stuff to be loaded
96− tryCompare(scopes, "loaded", true);
97− var dashContentList = findChild(dash, "dashContentList");
98− tryCompare(dashContentList, "count", 6);
99− tryCompareFunction(function() {
100− var mockScope1Loader = findChild(dash, "MockScope1 loader");
101− return mockScope1Loader && mockScope1Loader.item != null; },
102− true);
103−
104 // Show the overview
105 // Show the overview
105 touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, dash.height - units.gu(18));
106 touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, dash.height - units.gu(18));
106 var overviewController = findInvisibleChild(dash, "overviewController");
107 var overviewController = findInvisibleChild(dash, "overviewController");
@@ -121,18 +122,12 @@ Item {
121
122
122 // Make sure animation went back
123 // Make sure animation went back
123 tryCompare(overviewController, "progress", 0);
124 tryCompare(overviewController, "progress", 0);
125+
126+ var dashContentList = findChild(dash, "dashContentList");
124 compare(dashContentList.currentIndex, 0);
127 compare(dashContentList.currentIndex, 0);
125 }
128 }
126
129
127 function test_dash_overview_show_select_different_favorite() {
130 function test_dash_overview_show_select_different_favorite() {
128− // Wait for stuff to be loaded
129− tryCompare(scopes, "loaded", true);
130− var dashContentList = findChild(dash, "dashContentList");
131− tryCompare(dashContentList, "count", 6);
132− tryCompareFunction(function() { return findChild(dash, "MockScope1 loader") != null; }, true);
133− var mockScope1Loader = findChild(dash, "MockScope1 loader");
134− tryCompare(mockScope1Loader, "status", Loader.Ready);
135−
136 // Show the overview
131 // Show the overview
137 touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, dash.height - units.gu(18));
132 touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, dash.height - units.gu(18));
138 var overviewController = findInvisibleChild(dash, "overviewController");
133 var overviewController = findInvisibleChild(dash, "overviewController");
@@ -153,19 +148,11 @@ Item {
153
148
154 // Make sure animation went back
149 // Make sure animation went back
155 tryCompare(overviewController, "progress", 0);
150 tryCompare(overviewController, "progress", 0);
151+ var dashContentList = findChild(dash, "dashContentList");
156 compare(dashContentList.currentIndex, 1);
152 compare(dashContentList.currentIndex, 1);
157 }
153 }
158
154
159 function test_dash_overview_all_temp_scope_done_from_all() {
155 function test_dash_overview_all_temp_scope_done_from_all() {
160− // Wait for stuff to be loaded
161− tryCompare(scopes, "loaded", true);
162− var dashContentList = findChild(dash, "dashContentList");
163− tryCompare(dashContentList, "count", 6);
164− tryCompareFunction(function() {
165− var mockScope1Loader = findChild(dash, "MockScope1 loader");
166− return mockScope1Loader && mockScope1Loader.item != null; },
167− true);
168−
169 // Show the overview
156 // Show the overview
170 touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, dash.height - units.gu(18));
157 touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, dash.height - units.gu(18));
171 var overviewController = findInvisibleChild(dash, "overviewController");
158 var overviewController = findInvisibleChild(dash, "overviewController");
@@ -227,20 +214,13 @@ Item {
227 tryCompare(overviewController, "progress", 0);
214 tryCompare(overviewController, "progress", 0);
228
215
229 // Original list is still on 0
216 // Original list is still on 0
217+ var dashContentList = findChild(dash, "dashContentList");
230 compare(dashContentList.currentIndex, 0);
218 compare(dashContentList.currentIndex, 0);
231 }
219 }
232
220
233 function test_temp_scope_dash_overview_all_search_temp_scope_favorite_from_all() {
221 function test_temp_scope_dash_overview_all_search_temp_scope_favorite_from_all() {
234− // Wait for stuff to be loaded
235− tryCompare(scopes, "loaded", true);
236− var dashContentList = findChild(dash, "dashContentList");
237− tryCompare(dashContentList, "count", 6);
238− tryCompareFunction(function() {
239− var mockScope1Loader = findChild(dash, "MockScope1 loader");
240− return mockScope1Loader && mockScope1Loader.item != null; },
241− true);
242−
243 // Swipe right to Apps scope
222 // Swipe right to Apps scope
223+ var dashContentList = findChild(dash, "dashContentList");
244 touchFlick(dash, dash.width - 1, units.gu(1), dash.width - units.gu(10), units.gu(1));
224 touchFlick(dash, dash.width - 1, units.gu(1), dash.width - units.gu(10), units.gu(1));
245 tryCompare(dashContentList, "contentX", dashContentList.width);
225 tryCompare(dashContentList, "contentX", dashContentList.width);
246 tryCompare(dashContentList, "currentIndex", 1);
226 tryCompare(dashContentList, "currentIndex", 1);
@@ -249,8 +229,7 @@ Item {
249 var categoryListView = findChild(dashContentList.currentItem, "categoryListView");
229 var categoryListView = findChild(dashContentList.currentItem, "categoryListView");
250 var dashCategory2 = findChild(categoryListView, "dashCategory2");
230 var dashCategory2 = findChild(categoryListView, "dashCategory2");
251 tryCompareFunction(function() {
231 tryCompareFunction(function() {
252− var d = findChild(dashCategory2, "delegate2");
253− if (d) return true;
232+ if (dashCategory2.y < 200) return true;
254 categoryListView.contentY += 100;
233 categoryListView.contentY += 100;
255 return false;
234 return false;
256 }, true);
235 }, true);
@@ -340,8 +319,6 @@ Item {
340 }
319 }
341
320
342 function test_processing_indicator() {
321 function test_processing_indicator() {
343− tryCompare(scopes, "loaded", true);
344−
345 var processingIndicator = findChild(dash, "processingIndicator");
322 var processingIndicator = findChild(dash, "processingIndicator");
346 verify(processingIndicator, "Can't find the processing indicator.");
323 verify(processingIndicator, "Can't find the processing indicator.");
347
324
tests/qmltests/Dash/tst_DashContent.qml+14−1
@@ -70,7 +70,17 @@ Item {
70 function loadScopes() {
70 function loadScopes() {
71 scopeLoadedSpy.clear();
71 scopeLoadedSpy.clear();
72 scopesModel.load();
72 scopesModel.load();
73− tryCompare(scopeLoadedSpy, "count", 6);
73+ tryCompare(scopeLoadedSpy, "count", 6, 15000);
74+ tryCompare(scopesModel, "loaded", true);
75+ tryCompareFunction(function() {
76+ var mockScope1Loader = findChild(shell, "MockScope1 loader");
77+ return mockScope1Loader && mockScope1Loader.item != null; },
78+ true, 15000);
79+ tryCompareFunction(function() {
80+ var mockScope1Loader = findChild(shell, "MockScope1 loader");
81+ return mockScope1Loader && mockScope1Loader.status === Loader.Ready; },
82+ true, 15000);
83+ waitForRendering(findChild(shell, "MockScope1 loader").item);
74 }
84 }
75
85
76 function init() {
86 function init() {
@@ -467,6 +477,9 @@ Item {
467 var navigationListView = findChild(dashAltNavigationButton, "navigationListView");
477 var navigationListView = findChild(dashAltNavigationButton, "navigationListView");
468 tryCompare(navigationListView.currentItem.navigation, "loaded", true);
478 tryCompare(navigationListView.currentItem.navigation, "loaded", true);
469
479
480+ var blackRect = findChild(dashNavigation, "blackRect");
481+ tryCompare(blackRect, "opacity", 0.5);
482+
470 mouseClick(dashNavigation, dashNavigationButton.x, 0);
483 mouseClick(dashNavigation, dashNavigationButton.x, 0);
471 compare(dashNavigationButton.showList, false);
484 compare(dashNavigationButton.showList, false);
472 compare(dashAltNavigationButton.showList, false);
485 compare(dashAltNavigationButton.showList, false);
tests/qmltests/Dash/tst_GenericScopeView.qml+28−6
@@ -82,18 +82,19 @@ Item {
82 spy.signalName = "";
82 spy.signalName = "";
83 }
83 }
84
84
85− function scrollToCategory(category) {
85+ function scrollToCategory(categoryName) {
86 var categoryListView = findChild(genericScopeView, "categoryListView");
86 var categoryListView = findChild(genericScopeView, "categoryListView");
87 tryCompareFunction(function() {
87 tryCompareFunction(function() {
88− if (findChild(genericScopeView, category)) return true;
88+ var category = findChild(genericScopeView, categoryName);
89+ if (category && category.y > 0 && category.y < genericScopeView.height) return true;
89 mouseFlick(genericScopeView, genericScopeView.width/2, genericScopeView.height,
90 mouseFlick(genericScopeView, genericScopeView.width/2, genericScopeView.height,
90 genericScopeView.width/2, genericScopeView.y)
91 genericScopeView.width/2, genericScopeView.y)
91 tryCompare(categoryListView, "moving", false);
92 tryCompare(categoryListView, "moving", false);
92− return findChild(genericScopeView, category) !== null;
93+ return false;
93 }, true);
94 }, true);
94
95
95 tryCompareFunction(function() { return findChild(genericScopeView, "delegate0") !== null; }, true);
96 tryCompareFunction(function() { return findChild(genericScopeView, "delegate0") !== null; }, true);
96− return findChild(genericScopeView, category);
97+ return findChild(genericScopeView, categoryName);
97 }
98 }
98
99
99 function scrollToEnd()
100 function scrollToEnd()
@@ -283,7 +284,7 @@ Item {
283 if (category === undefined) category = 0;
284 if (category === undefined) category = 0;
284 if (delegate === undefined) delegate = 0;
285 if (delegate === undefined) delegate = 0;
285 tryCompareFunction(function() {
286 tryCompareFunction(function() {
286− var cardGrid = findChild(genericScopeView, category);
287+ var cardGrid = findChild(genericScopeView, "dashCategory"+category);
287 if (cardGrid != null) {
288 if (cardGrid != null) {
288 var tile = findChild(cardGrid, "delegate"+delegate);
289 var tile = findChild(cardGrid, "delegate"+delegate);
289 return tile != null;
290 return tile != null;
@@ -291,7 +292,7 @@ Item {
291 return false;
292 return false;
292 },
293 },
293 true);
294 true);
294− var tile = findChild(findChild(genericScopeView, category), "delegate"+delegate);
295+ var tile = findChild(findChild(genericScopeView, "dashCategory"+category), "delegate"+delegate);
295 mouseClick(tile, tile.width / 2, tile.height / 2);
296 mouseClick(tile, tile.width / 2, tile.height / 2);
296 tryCompare(testCase.subPageLoader, "open", true);
297 tryCompare(testCase.subPageLoader, "open", true);
297 tryCompare(testCase.subPageLoader, "x", 0);
298 tryCompare(testCase.subPageLoader, "x", 0);
@@ -581,6 +582,27 @@ Item {
581
582
582 genericScopeView.scope = !genericScopeView.scope;
583 genericScopeView.scope = !genericScopeView.scope;
583 }
584 }
585+
586+ function test_pullToRefresh() {
587+ waitForRendering(genericScopeView)
588+
589+ mouseFlick(genericScopeView,
590+ genericScopeView.width/2, units.gu(10),
591+ genericScopeView.width/2, units.gu(80),
592+ true, false)
593+
594+ var pullToRefresh = findChild(genericScopeView, "pullToRefresh")
595+ tryCompare(pullToRefresh, "releaseToRefresh", true)
596+
597+ spy.target = genericScopeView.scope
598+ spy.signalName = "refreshed"
599+
600+ mouseRelease(genericScopeView)
601+ tryCompare(pullToRefresh, "releaseToRefresh", false)
602+
603+ spy.wait()
604+ compare(spy.count, 1)
605+ }
584 }
606 }
585 }
607 }
586}
608}
tests/qmltests/tst_Shell.qml+4
@@ -198,6 +198,10 @@ Row {
198
198
199 var launcher = findChild(shell, "launcherPanel");
199 var launcher = findChild(shell, "launcherPanel");
200 tryCompare(launcher, "x", data.launcherHides ? -launcher.width : 0)
200 tryCompare(launcher, "x", data.launcherHides ? -launcher.width : 0)
201+
202+ // Make sure the helper for sliding out the launcher wasn't touched. We want to fade it out here.
203+ var animateTimer = findInvisibleChild(shell, "animateTimer");
204+ compare(animateTimer.nextState, "visible");
201 }
205 }
202
206
203 function test_suspend() {
207 function test_suspend() {
tests/utils/modules/Unity/Test/UnityTestCase.qml+3−2
@@ -178,9 +178,10 @@ TestCase {
178 // Keeps executing a given parameter-less function until it returns the given
178 // Keeps executing a given parameter-less function until it returns the given
179 // expected result or the timemout is reached (in which case a test failure
179 // expected result or the timemout is reached (in which case a test failure
180 // is generated)
180 // is generated)
181− function tryCompareFunction(func, expectedResult) {
181+ function tryCompareFunction(func, expectedResult, timeout) {
182 var timeSpent = 0
182 var timeSpent = 0
183+ if (timeout === undefined)
183− var timeout = 5000
184+ timeout = 5000;
184 var success = false
185 var success = false
185 var actualResult
186 var actualResult
186 while (timeSpent < timeout && !success) {
187 while (timeSpent < timeout && !success) {
tools/unlock-device+14−13
@@ -4,7 +4,7 @@
4# Our goal here is to get an Ubuntu Touch device into a testable unlocked state.
4# Our goal here is to get an Ubuntu Touch device into a testable unlocked state.
5# This will include a reboot.
5# This will include a reboot.
6
6
7−WAIT_COMMAND="adb reboot; sleep 5; adb wait-for-device"
7+WAIT_COMMAND="adb reboot; sleep 5; adb wait-for-device; sleep 20"
8
8
9while getopts s:w: opt; do
9while getopts s:w: opt; do
10 case $opt in
10 case $opt in
@@ -19,16 +19,17 @@ done
19
19
20eval "$WAIT_COMMAND"
20eval "$WAIT_COMMAND"
21
21
22−# Force a sleep no matter what, since we may need to wait for unity8
22+for i in `seq 1 5`; do
23−# to finish coming up (it's hard to tell when the greeter is actually rendered)
23+ # We need to go through sudo to get a proper environment for dbus set up
24+ UNLOCK_OUTPUT=$(adb shell "gdbus call --session --dest com.canonical.UnityGreeter --object-path / --method com.canonical.UnityGreeter.HideGreeter && echo Greeter unlocked" 2>&1)
25+ if echo "$UNLOCK_OUTPUT" | grep 'Greeter unlocked' >/dev/null; then
26+ echo "I: Unlock passed"
27+ exit 0
28+ else
29+ echo "I: Unlock attempt $i failed, script output: '$UNLOCK_OUTPUT'"
24−sleep 30
30+ sleep 20
31+ fi
32+done
25
33
26−# We need to go through sudo to get a proper environment for dbus set up
34+echo "I: Too many unlock failures, giving up"
27−UNLOCK_OUTPUT=$(adb shell "gdbus call --session --dest com.canonical.UnityGreeter --object-path / --method com.canonical.UnityGreeter.HideGreeter && echo Greeter unlocked" 2>&1)
28−if echo "$UNLOCK_OUTPUT" | grep 'Greeter unlocked' >/dev/null; then
29− echo "I: Unlock passed"
30− exit 0
31−else
32− echo "I: Unlock failed, script output: '$UNLOCK_OUTPUT'"
33− exit 1
35+exit 1
34−fi