on xenial help sign in
merge trunk, resolve conflicts
commitb4808776b394731a7f108c3244e5d451d81eb191 authorLukáš Tinkl <lukas.tinkl@canonical.com> dateTue 16 Feb 2016, 05:28 (11 years ago) parente59955d2733115
87 files changed+1878−225
CMakeLists.txt+1−1 data/unity8.conf+4−2 debian/changelog+52 debian/control+3−4 plugins/Dash/CardCreator.js+44−19 plugins/Unity/Indicators/modelactionrootstate.cpp+76−1 plugins/Unity/Indicators/modelactionrootstate.h+15−4 po/aa.po+664 po/en_AU.po+16−14 po/fr.po+6−4 po/unity8.pot+6−6 qml/Components/EdgeBarrierController.qml+2 qml/Components/VolumeControl.qml+1−6 qml/Components/ZoomableImage.qml+1−1 qml/Dash/CardCarousel.qml−1 qml/Dash/CardGrid.qml−1 qml/Dash/CardHorizontalList.qml−1 qml/Dash/CardTool.qml−1 qml/Dash/CardVerticalJournal.qml−1 qml/Dash/GenericScopeView.qml+1−1 qml/Dash/Previews/PreviewHeader.qml+1−1 qml/Dash/Previews/PreviewImageGallery.qml+2−2 qml/Dash/Previews/PreviewRatingEdit.qml+1 qml/Dash/Previews/PreviewZoomableImage.qml+2−2 qml/Greeter/LoginList.qml+3 qml/Greeter/WideView.qml+1−1 qml/Panel/IndicatorItem.qml+57−7 qml/Panel/graphics/Battery@18.png qml/Panel/graphics/Bluetooth@18.png qml/Panel/graphics/Clock@18.png qml/Panel/graphics/Location@18.png qml/Panel/graphics/Messages@18.png qml/Panel/graphics/Network@18.png qml/Panel/graphics/Sound@18.png qml/Panel/graphics/Sync@18.png qml/Panel/graphics/System@18.png qml/Panel/graphics/Time@18.png qml/Panel/graphics/VerticalDivider@18.png qml/Panel/graphics/handle@12.png qml/Panel/graphics/handle@12.sci−5 qml/Panel/graphics/search@12.png qml/Panel/graphics/time_and_date@18.png qml/Stages/ApplicationWindow.qml+14−1 qml/Stages/DecoratedWindow.qml+7 qml/Stages/DesktopStage.qml+6 qml/Stages/SessionContainer.qml+2 qml/Stages/Splash.qml+7−2 qml/Stages/WindowResizeArea.qml+95−16 tests/mocks/Unity/Application/ApplicationInfo.cpp+14−1 tests/mocks/Unity/Application/ApplicationInfo.h+4−1 tests/mocks/Unity/Application/ApplicationTestInterface.cpp+4−4 tests/mocks/Unity/Application/ApplicationTestInterface.h+2−2 tests/mocks/Unity/Application/MirSurface.cpp+48 tests/mocks/Unity/Application/MirSurface.h+21 tests/mocks/Unity/Application/MirSurfaceItem.cpp+6 tests/mocks/Unity/Application/MirSurfaceItem.h+14 tests/mocks/Unity/Application/SurfaceManager.cpp+56 tests/mocks/Unity/Application/SurfaceManager.h+40 tests/mocks/Unity/Indicators/ModelActionRootState.qml+11−1 tests/plugins/Dash/cardcreator/1.res+6−7 tests/plugins/Dash/cardcreator/10.res−1 tests/plugins/Dash/cardcreator/11.res+7−8 tests/plugins/Dash/cardcreator/2.res−1 tests/plugins/Dash/cardcreator/3.res+6−7 tests/plugins/Dash/cardcreator/4.res−1 tests/plugins/Dash/cardcreator/5.res+6−7 tests/plugins/Dash/cardcreator/5.tst+1−1 tests/plugins/Dash/cardcreator/6.res−1 tests/plugins/Dash/cardcreator/7.res−1 tests/plugins/Dash/cardcreator/8.res+1−2 tests/plugins/Dash/cardcreator/9.res+12−4 tests/plugins/Unity/Launcher/launchermodeltest.cpp+2 tests/qmltests/Components/tst_ZoomableImage.qml+8−5 tests/qmltests/Dash/Previews/tst_PreviewHeader.qml+15 tests/qmltests/Dash/Previews/tst_PreviewImageGallery.qml+19 tests/qmltests/Dash/Previews/tst_PreviewZoomableImage.qml+12 tests/qmltests/Dash/tst_Card.qml+18−1 tests/qmltests/Dash/tst_CardBenchmark.qml−2 tests/qmltests/Greeter/tst_WideView.qml+11 tests/qmltests/Stages/RecursingChildSessionControl.qml+3−3 tests/qmltests/Stages/SizeHintField.qml+33 tests/qmltests/Stages/SurfaceManagerControls.qml+33 tests/qmltests/Stages/SurfaceManagerField.qml+40 tests/qmltests/Stages/tst_DesktopStage.qml+30−24 tests/qmltests/Stages/tst_SessionContainer.qml+27−5 tests/qmltests/Stages/tst_WindowResizeArea.qml+225−30 tests/qmltests/tst_Shell.qml+53
CMakeLists.txt+1−1
@@ -57,7 +57,7 @@ find_package(Qt5DBus 5.4 REQUIRED)
5757find_package(Qt5Concurrent 5.4 REQUIRED)
5858find_package(Qt5Sql 5.4 REQUIRED)
5959
60−pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=12)
60+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=13)
6161pkg_check_modules(GIO REQUIRED gio-2.0>=2.32)
6262pkg_check_modules(GLIB REQUIRED glib-2.0>=2.32)
6363pkg_check_modules(QMENUMODEL REQUIRED qmenumodel)
data/unity8.conf+4−2
@@ -44,7 +44,11 @@ pre-start script
4444 fi
4545
4646 initctl set-env --global MIR_SERVER_PROMPT_FILE=1
47+end script
48+
49+exec ${BINARY:-unity8} $ARGS
4750
51+post-start script
4852 initctl emit --no-wait indicator-services-start
4953
5054 # Disable Qt's stuttering 'touch compression' to fix scrolling smoothness
@@ -58,8 +62,6 @@ pre-start script
5862 # initctl set-env MIR_SERVER_NBUFFERS=2
5963end script
6064
61−exec ${BINARY:-unity8} $ARGS
62−
6365post-stop script
6466 initctl set-env --global MIR_SOCKET=$UNITY_MIR_SOCKET
6567 initctl unset-env --global UNITY_MIR_SOCKET
debian/changelog+52
@@ -4,6 +4,58 @@ unity8 (8.13+15.04.20150914-0ubuntu1) UNRELEASED; urgency=medium
44
55 -- Lukas Tinkl <ltinkl@canonical.com> Mon, 14 Sep 2015 19:19:23 +0200
66
7+unity8 (8.11+16.04.20160212-0ubuntu1) xenial; urgency=medium
8+
9+ [ Albert Astals Cid ]
10+ * Do not use components in card creator generated code
11+ * Forward the makesurevisible signal from the inner preview widget
12+ * Make fallback also be fallback when the image is empty and not only
13+ on error (LP: #1531913)
14+ * Make the artShapeSize assignment be updated when using dynamic grid
15+ units
16+ * Visual improvements to the dash audio play cards
17+ * We don't use the unity-scope-scopes anymore (LP: #1439172)
18+
19+ [ Andrea Cimitan ]
20+ * only request a bigger image if zoomable widget data property is
21+ true, otherwise just leave the screen size (LP: #1536814)
22+
23+ [ CI Train Bot ]
24+ * Update translation template
25+
26+ [ Daniel d'Andrada ]
27+ * Set initial surface size (LP: #1532974)
28+
29+ [ Daniel d'Andrada, Michał Sawicz ]
30+ * Window Size Hints
31+
32+ [ Florian Boucault ]
33+ * Splash screen: delay showing the ActivityIndicator by 2 seconds.
34+
35+ [ Lukáš Tinkl ]
36+ * Replace the word "phone" with a more generic term "device" in the
37+ welcome wizard (LP: #1294732, #1543527)
38+
39+ [ Michael Terry ]
40+ * Have the tablet greeter set appropriate OSK hints if the user has a
41+ passcode instead of a passphrase. (LP: #1544173)
42+
43+ [ Michael Zanetti ]
44+ * Don't eat mouse events in the edgebarrer
45+
46+ [ Michał Sawicz ]
47+ * Bump unity-api dependencies
48+
49+ [ Nick Dedekind ]
50+ * Added support for secondary indicator actions (LP: #1398888)
51+ * Remove zombie if they're not animated. (LP: #1534541)
52+ * Removed unused panel resources
53+
54+ [ Ted Gould ]
55+ * Start the indicators after Unity8 starts instead of before
56+
57+ -- Michał Sawicz <michal.sawicz@canonical.com> Fri, 12 Feb 2016 00:12:41 +0000
58+
759unity8 (8.11+16.04.20160208-0ubuntu1) xenial; urgency=medium
860
961 [ Albert Astals Cid ]
debian/control+3−4
@@ -30,7 +30,7 @@ Build-Depends: cmake,
3030 libsystemsettings-dev,
3131 libtimezonemap1-dev,
3232 libudev-dev,
33− libunity-api-dev (>= 7.105),
33+ libunity-api-dev (>= 7.106),
3434 libusermetricsoutput1-dev,
3535# Need those X11 libs touch emulation from mouse events in manual QML tests on a X11 desktop
3636 libx11-dev[!armhf],
@@ -116,7 +116,6 @@ Depends: dmz-cursor-theme,
116116 ${shlibs:Depends},
117117Recommends: unity-scope-click,
118118 unity-scope-mediascanner2,
119− unity-scope-scopes,
120119Breaks: indicator-network (<< 0.5.1+14.10.20141014),
121120 ubuntu-touch-session (<< 0.107),
122121 unity8-greeter (<< 7.89),
@@ -134,7 +133,7 @@ Depends: qml-module-qtquick-layouts,
134133 qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.3.1627) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.3.1627),
135134 qtdeclarative5-unity-notifications-plugin (>= 0.1.2) | unity-notifications-impl,
136135 ubuntu-thumbnailer-impl-0,
137− unity-application-impl-12,
136+ unity-application-impl-13,
138137 unity-notifications-impl-3,
139138 unity-plugin-scopes | unity-scopes-impl,
140139 unity-scopes-impl-9,
@@ -180,7 +179,7 @@ Pre-Depends: ${misc:Pre-Depends},
180179Depends: ${misc:Depends},
181180 ${shlibs:Depends},
182181Provides: unity-application-impl,
183− unity-application-impl-12,
182+ unity-application-impl-13,
184183Replaces: unity8-autopilot (<< 8.02+15.04.20150422-0ubuntu1)
185184Description: Fake environment for running Unity 8 shell
186185 Provides fake implementations of some QML modules used by Unity 8 shell
plugins/Dash/CardCreator.js+44−19
@@ -61,7 +61,10 @@ var kBackgroundLoaderCode = 'Loader {\n\
6161// %1 is used as anchors of artShapeHolder
6262// %2 is used as image width
6363// %3 is used as image height
64−// %4 is injected as code to artImage
64+// %4 is used for artShapeSource.hideSource and inner Loader visible
65+// %5 is used as aspect ratio fallback
66+// %6 is injected as code to artImage
67+// %7 is used as image fallback
6568var kArtShapeHolderCode = 'Item { \n\
6669 id: artShapeHolder; \n\
6770 height: root.fixedArtShapeSize.height > 0 ? root.fixedArtShapeSize.height : artShapeLoader.height; \n\
@@ -70,13 +73,13 @@ var kArtShapeHolderCode = 'Item { \n\
7073 Loader { \n\
7174 id: artShapeLoader; \n\
7275 objectName: "artShapeLoader"; \n\
73− active: cardData && cardData["art"] || false; \n\
76+ readonly property string cardArt: cardData && cardData["art"] || %7; \n\
77+ active: cardArt != ""; \n\
7478 asynchronous: root.asynchronous; \n\
7579 visible: status == Loader.Ready; \n\
7680 sourceComponent: Item { \n\
7781 id: artShape; \n\
7882 objectName: "artShape"; \n\
79− readonly property bool doShapeItem: components["art"]["conciergeMode"] !== true; \n\
8083 visible: image.status == Image.Ready; \n\
8184 readonly property alias image: artImage; \n\
8285 ShaderEffectSource { \n\
@@ -85,11 +88,11 @@ var kArtShapeHolderCode = 'Item { \n\
8588 anchors.centerIn: parent; \n\
8689 width: 1; \n\
8790 height: 1; \n\
88− hideSource: doShapeItem; \n\
91+ hideSource: %4; \n\
8992 } \n\
9093 Loader { \n\
9194 anchors.fill: parent; \n\
92− visible: artShape.doShapeItem; \n\
95+ visible: %4; \n\
9396 sourceComponent: root.artShapeStyle === "icon" ? artShapeIconComponent : artShapeShapeComponent; \n\
9497 Component { \n\
9598 id: artShapeShapeComponent; \n\
@@ -113,7 +116,7 @@ var kArtShapeHolderCode = 'Item { \n\
113116 } \n\
114117 } \n\
115118 readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1; \n\
116− readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1; \n\
119+ readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : %5; \n\
117120 Component.onCompleted: { updateWidthHeightBindings(); } \n\
118121 Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); } \n\
119122 function updateWidthHeightBindings() { \n\
@@ -128,11 +131,11 @@ var kArtShapeHolderCode = 'Item { \n\
128131 CroppedImageMinimumSourceSize { \n\
129132 id: artImage; \n\
130133 objectName: "artImage"; \n\
131− source: cardData && cardData["art"] || ""; \n\
134+ source: artShapeLoader.cardArt; \n\
132135 asynchronous: root.asynchronous; \n\
133136 width: %2; \n\
134137 height: %3; \n\
135− %4 \n\
138+ %6 \n\
136139 } \n\
137140 } \n\
138141 } \n\
@@ -152,11 +155,20 @@ var kAudioButtonCode = 'AbstractButton { \n\
152155 visible: parent.pressed; \n\
153156 radius: "medium"; \n\
154157 } \n\
158+ Rectangle { \n\
159+ color: Qt.rgba(0, 0, 0, 0.5); \n\
160+ anchors.centerIn: parent; \n\
161+ width: parent.width * 0.5; \n\
162+ height: width; \n\
163+ radius: width / 2; \n\
164+ } \n\
155165 Icon { \n\
156− anchors.fill: parent; \n\
157− anchors.margins: parent.height > units.gu(5) ? units.gu(2) : 0; \n\
166+ anchors.centerIn: parent; \n\
167+ width: parent.width * 0.3; \n\
168+ height: width; \n\
158169 opacity: 0.9; \n\
159170 name: DashAudioPlayer.playing && AudioUrlComparer.compare(parent.source, DashAudioPlayer.currentSource) ? "media-playback-pause" : "media-playback-start"; \n\
171+ color: "white"; \n\
160172 } \n\
161173 onClicked: { \n\
162174 if (AudioUrlComparer.compare(source, DashAudioPlayer.currentSource)) { \n\
@@ -245,11 +257,12 @@ var kMascotShapeLoaderCode = 'Loader { \n\
245257// %1 is used as anchors of mascotImage
246258// %2 is used as visible of mascotImage
247259// %3 is injected as code to mascotImage
260+// %4 is used as fallback image
248261var kMascotImageCode = 'CroppedImageMinimumSourceSize { \n\
249262 id: mascotImage; \n\
250263 objectName: "mascotImage"; \n\
251264 anchors { %1 } \n\
252− source: cardData && cardData["mascot"] || ""; \n\
265+ source: cardData && cardData["mascot"] || %4; \n\
253266 width: units.gu(6); \n\
254267 height: units.gu(5.625); \n\
255268 horizontalAlignment: Image.AlignHCenter; \n\
@@ -392,7 +405,6 @@ function cardString(template, components) {
392405
393406 code = 'AbstractButton { \n\
394407 id: root; \n\
395− property var components; \n\
396408 property var cardData; \n\
397409 property string artShapeStyle: "inset"; \n\
398410 property string backgroundShapeStyle: "inset"; \n\
@@ -410,7 +422,8 @@ function cardString(template, components) {
410422
411423 var hasArt = components["art"] && components["art"]["field"] || false;
412424 var hasSummary = components["summary"] || false;
413− var artAndSummary = hasArt && hasSummary && components["art"]["conciergeMode"] !== true;
425+ var isConciergeMode = components["art"] && components["art"]["conciergeMode"] || false;
426+ var artAndSummary = hasArt && hasSummary && !isConciergeMode;
414427 var isHorizontal = template["card-layout"] === "horizontal";
415428 var hasBackground = (!isHorizontal && (template["card-background"] || components["background"] || artAndSummary)) ||
416429 (hasSummary && (template["card-background"] || components["background"]));
@@ -480,13 +493,21 @@ function cardString(template, components) {
480493 heightCode = 'width / artShape.aspect';
481494 }
482495
496+ var aspectRatio = components["art"] && components["art"]["aspect-ratio"] || 1;
497+ if (isNaN(aspectRatio)) {
498+ aspectRatio = 1;
499+ }
483500 var fallback = components["art"] && components["art"]["fallback"] || "";
484501 fallback = encodeURI(fallback);
485− var fallbackCode = "";
502+ var fallbackStatusCode = "";
503+ var fallbackURICode = '""';
486504 if (fallback !== "") {
487− fallbackCode += 'onStatusChanged: if (status === Image.Error) source = decodeURI("%1");'.arg(fallback);
505+ // fallbackStatusCode has %6 in it because we want to substitute it for fallbackURICode
506+ // which in kArtShapeHolderCode is %7
507+ fallbackStatusCode += 'onStatusChanged: if (status === Image.Error) source = %7;';
508+ fallbackURICode = 'decodeURI("%1")'.arg(fallback);
488509 }
489− code += kArtShapeHolderCode.arg(artAnchors).arg(widthCode).arg(heightCode).arg(fallbackCode);
510+ code += kArtShapeHolderCode.arg(artAnchors).arg(widthCode).arg(heightCode).arg(isConciergeMode ? "false" : "true").arg(aspectRatio).arg(fallbackStatusCode).arg(fallbackURICode);
490511 } else {
491512 code += 'readonly property size artShapeSize: Qt.size(-1, -1);\n'
492513 }
@@ -580,11 +601,15 @@ function cardString(template, components) {
580601 var mascotImageVisible = useMascotShape ? 'false' : 'showHeader';
581602 var fallback = components["mascot"] && components["mascot"]["fallback"] || "";
582603 fallback = encodeURI(fallback);
583− var fallbackCode = "";
604+ var fallbackStatusCode = "";
605+ var fallbackURICode = '""';
584606 if (fallback !== "") {
585− fallbackCode += 'onStatusChanged: if (status === Image.Error) source = decodeURI("%1");'.arg(fallback);
607+ // fallbackStatusCode has %4 in it because we want to substitute it for fallbackURICode
608+ // which in kMascotImageCode is %4
609+ fallbackStatusCode += 'onStatusChanged: if (status === Image.Error) source = %4;';
610+ fallbackURICode = 'decodeURI("%1")'.arg(fallback);
586611 }
587− mascotCode = kMascotImageCode.arg(mascotAnchors).arg(mascotImageVisible).arg(fallbackCode);
612+ mascotCode = kMascotImageCode.arg(mascotAnchors).arg(mascotImageVisible).arg(fallbackStatusCode).arg(fallbackURICode);
588613 }
589614
590615 var summaryColorWithBackground = 'backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item && backgroundLoader.item.luminance > 0.7 ? theme.palette.normal.baseText : "white")';
plugins/Unity/Indicators/modelactionrootstate.cpp+76−1
@@ -1,5 +1,5 @@
11/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013-2016 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU Lesser General Public License as published by
@@ -32,6 +32,7 @@ extern "C" {
3232ModelActionRootState::ModelActionRootState(QObject *parent)
3333 : RootStateObject(parent),
3434 m_menu(nullptr)
35+ , m_reentryGuard(false)
3536{
3637}
3738
@@ -62,6 +63,7 @@ void ModelActionRootState::setMenu(UnityMenuModel* menu)
6263 connect(m_menu, &UnityMenuModel::destroyed, this, &ModelActionRootState::reset);
6364 }
6465 updateActionState();
66+ updateOtherActions();
6567 Q_EMIT menuChanged();
6668
6769 if (wasValid != valid())
@@ -69,6 +71,21 @@ void ModelActionRootState::setMenu(UnityMenuModel* menu)
6971 }
7072}
7173
74+QString ModelActionRootState::secondaryAction() const
75+{
76+ return m_secondaryAction;
77+}
78+
79+QString ModelActionRootState::scrollAction() const
80+{
81+ return m_scrollAction;
82+}
83+
84+QString ModelActionRootState::submenuAction() const
85+{
86+ return m_submenuAction;
87+}
88+
7289bool ModelActionRootState::valid() const
7390{
7491 return !currentState().empty();
@@ -79,6 +96,7 @@ void ModelActionRootState::onModelRowsAdded(const QModelIndex& parent, int start
7996 Q_UNUSED(parent);
8097 if (start == 0 && end >= 0) {
8198 updateActionState();
99+ updateOtherActions();
82100 }
83101}
84102
@@ -87,6 +105,7 @@ void ModelActionRootState::onModelRowsRemoved(const QModelIndex& parent, int sta
87105 Q_UNUSED(parent);
88106 if (start == 0 && end >= 0) {
89107 updateActionState();
108+ updateOtherActions();
90109 }
91110}
92111
@@ -99,6 +118,7 @@ void ModelActionRootState::onModelDataChanged(const QModelIndex& topLeft, const
99118
100119 if (topLeft.row() <= 0 && bottomRight.row() >= 0) {
101120 updateActionState();
121+ updateOtherActions();
102122 }
103123}
104124
@@ -108,10 +128,15 @@ void ModelActionRootState::reset()
108128
109129 Q_EMIT menuChanged();
110130 setCurrentState(QVariantMap());
131+
132+ updateOtherActions();
111133}
112134
113135void ModelActionRootState::updateActionState()
114136{
137+ if (m_reentryGuard) return;
138+ m_reentryGuard = true;
139+
115140 if (m_menu && m_menu->rowCount() > 0) {
116141 ActionStateParser* oldParser = m_menu->actionStateParser();
117142 m_menu->setActionStateParser(&m_parser);
@@ -126,4 +151,54 @@ void ModelActionRootState::updateActionState()
126151 }
127152 // else if m_menu->rowCount() == 0, let's leave existing cache in place
128153 // until the new menu comes in, to avoid flashing the UI empty for a moment
154+
155+ m_reentryGuard = false;
156+}
157+
158+void ModelActionRootState::updateOtherActions()
159+{
160+ if (m_reentryGuard) return;
161+ m_reentryGuard = true;
162+
163+ if (m_menu && m_menu->rowCount() > 0) {
164+ QVariantMap map;
165+ map[QStringLiteral("submenu-action")] = QStringLiteral("string");
166+ map[QStringLiteral("x-canonical-scroll-action")] = QStringLiteral("string");
167+ map[QStringLiteral("x-canonical-secondary-action")] = QStringLiteral("string");
168+ m_menu->loadExtendedAttributes(0, map);
169+ QVariantMap extMap = m_menu->get(0, "ext").toMap();
170+
171+ QString secondaryAction = extMap.value(QStringLiteral("xCanonicalSecondaryAction")).toString();
172+ if (m_secondaryAction != secondaryAction) {
173+ m_secondaryAction = secondaryAction;
174+ Q_EMIT secondaryActionChanged();
175+ }
176+
177+ QString scrollAction = extMap.value(QStringLiteral("xCanonicalScrollAction")).toString();
178+ if (m_scrollAction != scrollAction) {
179+ m_scrollAction = scrollAction;
180+ Q_EMIT scrollActionChanged();
181+ }
182+
183+ QString submenuAction = extMap.value(QStringLiteral("submenuAction")).toString();
184+ if (m_submenuAction != submenuAction) {
185+ m_submenuAction = submenuAction;
186+ Q_EMIT submenuActionChanged();
187+ }
188+ } else {
189+ if (!m_secondaryAction.isEmpty()) {
190+ m_secondaryAction.clear();
191+ Q_EMIT secondaryActionChanged();
192+ }
193+ if (!m_scrollAction.isEmpty()) {
194+ m_scrollAction.clear();
195+ Q_EMIT scrollActionChanged();
196+ }
197+ if (!m_submenuAction.isEmpty()) {
198+ m_submenuAction.clear();
199+ Q_EMIT submenuActionChanged();
200+ }
201+ }
202+
203+ m_reentryGuard = false;
129204}
plugins/Unity/Indicators/modelactionrootstate.h+15−4
@@ -1,5 +1,5 @@
11/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013-2016 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU Lesser General Public License as published by
@@ -30,6 +30,9 @@ class UNITYINDICATORS_EXPORT ModelActionRootState : public RootStateObject
3030{
3131 Q_OBJECT
3232 Q_PROPERTY(UnityMenuModel* menu READ menu WRITE setMenu NOTIFY menuChanged)
33+ Q_PROPERTY(QString secondaryAction READ secondaryAction NOTIFY secondaryActionChanged)
34+ Q_PROPERTY(QString scrollAction READ scrollAction NOTIFY scrollActionChanged)
35+ Q_PROPERTY(QString submenuAction READ submenuAction NOTIFY submenuActionChanged)
3336public:
3437 ModelActionRootState(QObject *parent = 0);
3538 virtual ~ModelActionRootState();
@@ -37,14 +40,17 @@ public:
3740 UnityMenuModel* menu() const;
3841 void setMenu(UnityMenuModel* menu);
3942
40− int index() const;
41− void setIndex(int index);
43+ QString secondaryAction() const;
44+ QString scrollAction() const;
45+ QString submenuAction() const;
4246
4347 bool valid() const override;
4448
4549Q_SIGNALS:
4650 void menuChanged();
47− void indexChanged();
51+ void secondaryActionChanged();
52+ void scrollActionChanged();
53+ void submenuActionChanged();
4854
4955private Q_SLOTS:
5056 void onModelRowsAdded(const QModelIndex& parent, int start, int end);
@@ -54,8 +60,13 @@ private Q_SLOTS:
5460
5561private:
5662 void updateActionState();
63+ void updateOtherActions();
5764
5865 UnityMenuModel* m_menu;
66+ QString m_secondaryAction;
67+ QString m_scrollAction;
68+ QString m_submenuAction;
69+ bool m_reentryGuard;
5970};
6071
6172#endif // MODELACTIONROOTSTATE_H
po/aa.ponew file+664
@@ -0,0 +1,664 @@
1+# Afar translation for unity8
2+# Copyright (c) 2016 Rosetta Contributors and Canonical Ltd 2016
3+# This file is distributed under the same license as the unity8 package.
4+# FIRST AUTHOR <EMAIL@ADDRESS>, 2016.
5+#
6+msgid ""
7+msgstr ""
8+"Project-Id-Version: unity8\n"
9+"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10+"POT-Creation-Date: 2016-02-08 09:50+0000\n"
11+"PO-Revision-Date: 2016-02-12 10:18+0000\n"
12+"Last-Translator: Charif AYFARAH <ayfarah@ymail.com>\n"
13+"Language-Team: Afar <aa@li.org>\n"
14+"MIME-Version: 1.0\n"
15+"Content-Type: text/plain; charset=UTF-8\n"
16+"Content-Transfer-Encoding: 8bit\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2016-02-13 05:37+0000\n"
19+"X-Generator: Launchpad (build 17925)\n"
20+
21+#: plugins/IntegratedLightDM/Greeter.cpp:112
22+msgid "Password: "
23+msgstr "Cuumita "
24+
25+#: plugins/Unity/Launcher/launcheritem.cpp:47
26+#: plugins/Unity/Launcher/launcheritem.cpp:106
27+msgid "Pin shortcut"
28+msgstr ""
29+
30+#: plugins/Unity/Launcher/launcheritem.cpp:52
31+msgid "Quit"
32+msgstr "Kat"
33+
34+#: plugins/Unity/Launcher/launcheritem.cpp:106
35+msgid "Unpin shortcut"
36+msgstr ""
37+
38+#: qml/Components/DelayedLockscreen.qml:49
39+msgid "Device Locked"
40+msgstr ""
41+
42+#: qml/Components/DelayedLockscreen.qml:64
43+msgid "You have been locked out due to too many failed passphrase attempts."
44+msgstr ""
45+
46+#: qml/Components/DelayedLockscreen.qml:65
47+msgid "You have been locked out due to too many failed passcode attempts."
48+msgstr ""
49+
50+#: qml/Components/DelayedLockscreen.qml:74
51+#, qt-format
52+msgid "Please wait %1 minute and then try again…"
53+msgid_plural "Please wait %1 minutes and then try again…"
54+msgstr[0] "Maganak %1 minit qambaalaay, kaadu gabbat"
55+msgstr[1] "Maganak %1 minit qambaalaay, kaadu gabbat"
56+
57+#: qml/Components/Dialogs.qml:144
58+msgctxt "Title: Lock/Log out dialog"
59+msgid "Log out"
60+msgstr "Eweq"
61+
62+#: qml/Components/Dialogs.qml:145
63+msgid "Are you sure you want to log out?"
64+msgstr ""
65+
66+#: qml/Components/Dialogs.qml:147
67+msgctxt "Button: Lock the system"
68+msgid "Lock"
69+msgstr "Koflis"
70+
71+#: qml/Components/Dialogs.qml:154
72+msgctxt "Button: Log out from the system"
73+msgid "Log Out"
74+msgstr "Eweq"
75+
76+#: qml/Components/Dialogs.qml:161 qml/Components/Dialogs.qml:194
77+#: qml/Components/Dialogs.qml:253
78+msgid "Cancel"
79+msgstr "Bayis"
80+
81+#: qml/Components/Dialogs.qml:173
82+msgctxt "Title: Reboot/Shut down dialog"
83+msgid "Shut down"
84+msgstr "Bargus"
85+
86+#: qml/Components/Dialogs.qml:174
87+msgid "Are you sure you want to shut down?"
88+msgstr "Bargussuh digga maay litoo?"
89+
90+#: qml/Components/Dialogs.qml:176
91+msgctxt "Button: Reboot the system"
92+msgid "Reboot"
93+msgstr "Wadir-ugus"
94+
95+#: qml/Components/Dialogs.qml:185
96+msgctxt "Button: Shut down the system"
97+msgid "Shut down"
98+msgstr "Bargus"
99+
100+#: qml/Components/Dialogs.qml:207
101+msgctxt "Title: Reboot dialog"
102+msgid "Reboot"
103+msgstr "Wadir-ugus"
104+
105+#: qml/Components/Dialogs.qml:208
106+msgid "Are you sure you want to reboot?"
107+msgstr "Wadir-ugussuh digga maay litoo?"
108+
109+#: qml/Components/Dialogs.qml:210
110+msgid "No"
111+msgstr "Maleey"
112+
113+#: qml/Components/Dialogs.qml:217
114+msgid "Yes"
115+msgstr "Yeey"
116+
117+#: qml/Components/Dialogs.qml:232
118+msgctxt "Title: Power off/Restart dialog"
119+msgid "Power"
120+msgstr "Cayla"
121+
122+#: qml/Components/Dialogs.qml:233
123+msgid ""
124+"Are you sure you would like\n"
125+"to power off?"
126+msgstr "Timmi haytuh digga maay litoo?"
127+
128+#: qml/Components/Dialogs.qml:235
129+msgctxt "Button: Power off the system"
130+msgid "Power off"
131+msgstr "Timmi hayis"
132+
133+#: qml/Components/Dialogs.qml:244
134+msgctxt "Button: Restart the system"
135+msgid "Restart"
136+msgstr "Wadir-qimbis"
137+
138+#: qml/Components/Lockscreen.qml:245
139+msgid "Return to Call"
140+msgstr "Seecol gacis"
141+
142+#: qml/Components/Lockscreen.qml:245
143+msgid "Emergency Call"
144+msgstr ""
145+
146+#: qml/Components/Lockscreen.qml:277
147+msgid "OK"
148+msgstr "Hayya"
149+
150+#: qml/Components/ModeSwitchWarningDialog.qml:32
151+msgid "Apps may have unsaved data:"
152+msgstr ""
153+
154+#: qml/Components/ModeSwitchWarningDialog.qml:57
155+msgid "Re-dock, save your work and close these apps to continue."
156+msgstr ""
157+
158+#: qml/Components/ModeSwitchWarningDialog.qml:63
159+msgid "Or force close now (unsaved data will be lost)."
160+msgstr ""
161+
162+#: qml/Components/ModeSwitchWarningDialog.qml:75
163+msgid "OK, I will reconnect"
164+msgstr ""
165+
166+#: qml/Components/ModeSwitchWarningDialog.qml:76
167+msgid "Reconnect now!"
168+msgstr ""
169+
170+#: qml/Components/ModeSwitchWarningDialog.qml:88
171+msgid "Close all"
172+msgstr "Inkih alif"
173+
174+#: qml/Dash/DashPageHeader.qml:266
175+msgctxt "Button: Open the Ubuntu Store"
176+msgid "Store"
177+msgstr ""
178+
179+#: qml/Dash/DashPageHeader.qml:273
180+msgctxt "Button: Start a search in the current dash scope"
181+msgid "Search"
182+msgstr "Goranto"
183+
184+#: qml/Dash/DashPageHeader.qml:283
185+msgctxt "Button: Show the current dash scope settings"
186+msgid "Settings"
187+msgstr "Massosu"
188+
189+#: qml/Dash/DashPageHeader.qml:290
190+msgid "Remove from Favorites"
191+msgstr ""
192+
193+#: qml/Dash/DashPageHeader.qml:290
194+msgid "Add to Favorites"
195+msgstr ""
196+
197+#: qml/Dash/GenericScopeView.qml:587 qml/Dash/GenericScopeView.qml:745
198+msgid "See less"
199+msgstr ""
200+
201+#: qml/Dash/GenericScopeView.qml:587
202+msgid "See all"
203+msgstr ""
204+
205+#: qml/Dash/GenericScopeView.qml:649
206+msgctxt "Label: Hint for dash search line edit"
207+msgid "Search"
208+msgstr "Goranto"
209+
210+#: qml/Dash/Previews/PreviewActionCombo.qml:35
211+msgid "More..."
212+msgstr ""
213+
214+#: qml/Dash/Previews/PreviewActionCombo.qml:35
215+msgid "Less..."
216+msgstr ""
217+
218+#: qml/Dash/Previews/PreviewCommentInput.qml:68
219+#: qml/Dash/Previews/PreviewRatingInput.qml:174
220+msgid "Send"
221+msgstr "Rub"
222+
223+#: qml/Dash/Previews/PreviewRatingInput.qml:84
224+msgid "Rate this"
225+msgstr ""
226+
227+#: qml/Dash/Previews/PreviewRatingInput.qml:129
228+msgid "Add a review"
229+msgstr ""
230+
231+#: qml/Dash/Previews/PreviewSharing.qml:50
232+msgid "Preview Share Item"
233+msgstr ""
234+
235+#: qml/Dash/PullToRefreshScopeStyle.qml:55
236+msgid "Pull to refresh…"
237+msgstr ""
238+
239+#: qml/Dash/PullToRefreshScopeStyle.qml:60
240+msgid "Release to refresh…"
241+msgstr ""
242+
243+#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
244+msgid "Enable location data"
245+msgstr ""
246+
247+#: qml/Dash/ScopesList.qml:67
248+msgid "Manage"
249+msgstr ""
250+
251+#: qml/Dash/ScopesList.qml:113
252+msgid "Home"
253+msgstr "Dabqi"
254+
255+#: qml/Dash/ScopesList.qml:114
256+msgid "Also installed"
257+msgstr ""
258+
259+#: qml/DisabledScreenNotice.qml:87
260+msgid ""
261+"Your device is now connected to an external display. Use this screen as a "
262+"touch pad to interact with the mouse."
263+msgstr ""
264+
265+#: qml/Greeter/CoverPage.qml:107
266+msgid "Unlock"
267+msgstr ""
268+
269+#: qml/Greeter/LoginList.qml:236
270+msgid "Retry"
271+msgstr "Qagis"
272+
273+#: qml/Greeter/LoginList.qml:237
274+msgid "Tap to unlock"
275+msgstr ""
276+
277+#: qml/Greeter/NarrowView.qml:54 qml/Wizard/Pages/passwd-confirm.qml:53
278+msgid "Sorry, incorrect passphrase."
279+msgstr ""
280+
281+#: qml/Greeter/NarrowView.qml:55 qml/Wizard/Pages/passwd-confirm.qml:54
282+msgid "Sorry, incorrect passcode."
283+msgstr ""
284+
285+#: qml/Greeter/NarrowView.qml:56
286+msgid "This will be your last attempt."
287+msgstr ""
288+
289+#: qml/Greeter/NarrowView.qml:58
290+msgid ""
291+"If passphrase is entered incorrectly, your phone will conduct a factory "
292+"reset and all personal data will be deleted."
293+msgstr ""
294+
295+#: qml/Greeter/NarrowView.qml:59
296+msgid ""
297+"If passcode is entered incorrectly, your phone will conduct a factory reset "
298+"and all personal data will be deleted."
299+msgstr ""
300+
301+#: qml/Greeter/NarrowView.qml:116
302+#, qt-format
303+msgid "Enter %1"
304+msgstr "%1 culus"
305+
306+#: qml/Greeter/NarrowView.qml:117 qml/Wizard/Pages/passwd-set.qml:60
307+msgid "Enter passphrase"
308+msgstr "Tatur-maxco culus"
309+
310+#: qml/Greeter/NarrowView.qml:118
311+msgid "Enter passcode"
312+msgstr "Tatur-mudenta culus"
313+
314+#: qml/Greeter/NarrowView.qml:119
315+#, qt-format
316+msgid "Sorry, incorrect %1"
317+msgstr ""
318+
319+#: qml/Greeter/NarrowView.qml:120
320+msgid "Sorry, incorrect passphrase"
321+msgstr ""
322+
323+#: qml/Greeter/NarrowView.qml:121
324+msgctxt "passphrase"
325+msgid "Please re-enter"
326+msgstr ""
327+
328+#: qml/Greeter/NarrowView.qml:122
329+msgid "Sorry, incorrect passcode"
330+msgstr ""
331+
332+#: qml/Notifications/NotificationMenuItemFactory.qml:129
333+msgid "Show password"
334+msgstr "Cuumita uybulluy"
335+
336+#: qml/Panel/ActiveCallHint.qml:79
337+msgid "Tap to return to call..."
338+msgstr ""
339+
340+#: qml/Panel/ActiveCallHint.qml:92
341+msgid "Conference"
342+msgstr "Koboxu"
343+
344+#: qml/Panel/Indicators/MenuItemFactory.qml:753
345+msgid "Nothing is playing"
346+msgstr ""
347+
348+#: qml/Panel/Indicators/MenuItemFactory.qml:882
349+#, qt-format
350+msgid "%1 hour"
351+msgid_plural "%1 hours"
352+msgstr[0] "%1 saaqata"
353+msgstr[1] "%1 saaqata"
354+
355+#: qml/Panel/Indicators/MenuItemFactory.qml:886
356+#, qt-format
357+msgid "%1 minute"
358+msgid_plural "%1 minutes"
359+msgstr[0] "%1 miniti"
360+msgstr[1] "%1 miniti"
361+
362+#: qml/Panel/Indicators/MenuItemFactory.qml:891
363+#, qt-format
364+msgid "%1 second"
365+msgid_plural "%1 seconds"
366+msgstr[0] "%1 nammayye"
367+msgstr[1] "%1 nammayye"
368+
369+#: qml/Panel/Indicators/MenuItemFactory.qml:894
370+msgid "0 seconds"
371+msgstr "0 nammayye"
372+
373+#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
374+#: qml/Panel/Indicators/MenuItemFactory.qml:896
375+#, qt-format
376+msgid "%1 remaining"
377+msgstr "%1 raaqe"
378+
379+#: qml/Panel/Indicators/MenuItemFactory.qml:902
380+msgid "In queue…"
381+msgstr ""
382+
383+#: qml/Panel/Indicators/MenuItemFactory.qml:906
384+msgid "Downloading"
385+msgstr "Qulluumah"
386+
387+#: qml/Panel/Indicators/MenuItemFactory.qml:908
388+msgid "Paused, tap to resume"
389+msgstr ""
390+
391+#: qml/Panel/Indicators/MenuItemFactory.qml:910
392+msgid "Canceled"
393+msgstr "Bayteh"
394+
395+#: qml/Panel/Indicators/MenuItemFactory.qml:912
396+msgid "Finished"
397+msgstr "Duudumteh"
398+
399+#: qml/Panel/Indicators/MenuItemFactory.qml:914
400+msgid "Failed, tap to retry"
401+msgstr ""
402+
403+#: qml/Panel/Indicators/MessageMenuItemFactory.qml:155
404+#: qml/Panel/Indicators/MessageMenuItemFactory.qml:214
405+msgctxt "Button: Send a reply message"
406+msgid "Send"
407+msgstr "Rub"
408+
409+#: qml/Panel/Indicators/MessageMenuItemFactory.qml:156
410+msgctxt "Label: Hint in message indicator line edit"
411+msgid "Reply"
412+msgstr ""
413+
414+#: qml/Panel/Indicators/MessageMenuItemFactory.qml:213
415+msgctxt "Button: Call back on phone"
416+msgid "Call back"
417+msgstr ""
418+
419+#: qml/Panel/Indicators/ModemInfoItem.qml:105
420+msgid "Unlock SIM"
421+msgstr ""
422+
423+#: qml/Panel/Indicators/RoamingIndication.qml:27
424+msgid "Roaming"
425+msgstr ""
426+
427+#: qml/Tutorial/TutorialBottom.qml:28
428+msgid "Open special menus"
429+msgstr ""
430+
431+#: qml/Tutorial/TutorialBottom.qml:29
432+msgid "Swipe up from the bottom edge."
433+msgstr ""
434+
435+#: qml/Tutorial/TutorialBottomFinish.qml:24
436+msgid "This action does different things for different apps"
437+msgstr ""
438+
439+#: qml/Tutorial/TutorialBottomFinish.qml:25
440+msgid "Tap here to finish."
441+msgstr ""
442+
443+#: qml/Tutorial/TutorialContent.qml:80 qml/Tutorial/TutorialLeftFinish.qml:25
444+#: qml/Tutorial/TutorialRight.qml:144
445+msgid "Tap here to continue."
446+msgstr ""
447+
448+#: qml/Tutorial/TutorialLeft.qml:26
449+msgid "Open the launcher"
450+msgstr ""
451+
452+#: qml/Tutorial/TutorialLeft.qml:27
453+msgid "Short swipe from the left edge."
454+msgstr ""
455+
456+#: qml/Tutorial/TutorialLeftFinish.qml:24
457+msgid "These are the shortcuts to favorite apps"
458+msgstr ""
459+
460+#: qml/Tutorial/TutorialPage.qml:178
461+msgid "You almost got it!"
462+msgstr ""
463+
464+#: qml/Tutorial/TutorialPage.qml:195
465+msgid "Try again."
466+msgstr ""
467+
468+#: qml/Tutorial/TutorialRight.qml:31
469+msgid "To view open apps"
470+msgstr ""
471+
472+#: qml/Tutorial/TutorialRight.qml:32
473+msgid "Long swipe from the right edge."
474+msgstr ""
475+
476+#: qml/Tutorial/TutorialRight.qml:122
477+msgid "View all your running tasks."
478+msgstr ""
479+
480+#: qml/Wizard/Page.qml:89
481+msgctxt "Button: Go back one page in the Wizard"
482+msgid "Back"
483+msgstr "Derre"
484+
485+#: qml/Wizard/Pages/10-welcome.qml:27
486+msgid "Hi!"
487+msgstr "Nagaale!"
488+
489+#: qml/Wizard/Pages/10-welcome.qml:44
490+msgid "Welcome to your Ubuntu phone."
491+msgstr "Ubuntu Telefoonul arcib"
492+
493+#: qml/Wizard/Pages/10-welcome.qml:52
494+msgid "Let’s get started."
495+msgstr "Hayya qimbisnay."
496+
497+#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:133
498+#: qml/Wizard/Pages/40-wifi.qml:215 qml/Wizard/Pages/50-location.qml:131
499+#: qml/Wizard/Pages/60-reporting.qml:60 qml/Wizard/Pages/passwd-confirm.qml:82
500+#: qml/Wizard/Pages/passwd-set.qml:90
501+msgid "Continue"
502+msgstr "Tatur"
503+
504+#: qml/Wizard/Pages/20-sim.qml:25
505+msgid "Add a SIM card and restart your device"
506+msgstr ""
507+
508+#: qml/Wizard/Pages/20-sim.qml:59
509+msgid "Without it, you won’t be able to make calls or use text messaging."
510+msgstr ""
511+
512+#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
513+msgid "Skip"
514+msgstr "Kaqit"
515+
516+#: qml/Wizard/Pages/30-passwd-type.qml:39
517+msgid "Lock security"
518+msgstr ""
519+
520+#: qml/Wizard/Pages/30-passwd-type.qml:79
521+msgid "Please select how you’d like to unlock your phone."
522+msgstr ""
523+
524+#: qml/Wizard/Pages/30-passwd-type.qml:102
525+msgctxt "Label: Type of security method"
526+msgid "Swipe"
527+msgstr "Uysuttuc"
528+
529+#: qml/Wizard/Pages/30-passwd-type.qml:103
530+msgctxt "Label: Description of security method"
531+msgid "No security"
532+msgstr ""
533+
534+#: qml/Wizard/Pages/30-passwd-type.qml:105
535+msgctxt "Label: Type of security method"
536+msgid "Passcode"
537+msgstr ""
538+
539+#: qml/Wizard/Pages/30-passwd-type.qml:106
540+msgctxt "Label: Description of security method"
541+msgid "4 digits only"
542+msgstr ""
543+
544+#: qml/Wizard/Pages/30-passwd-type.qml:108
545+msgctxt "Label: Type of security method"
546+msgid "Passphrase"
547+msgstr ""
548+
549+#: qml/Wizard/Pages/30-passwd-type.qml:109
550+msgctxt "Label: Description of security method"
551+msgid "Numbers and letters"
552+msgstr ""
553+
554+#: qml/Wizard/Pages/40-wifi.qml:29
555+msgid "Connect to Wi‑Fi"
556+msgstr ""
557+
558+#: qml/Wizard/Pages/40-wifi.qml:168
559+msgid "Available networks…"
560+msgstr ""
561+
562+#: qml/Wizard/Pages/40-wifi.qml:169
563+msgid "No available networks."
564+msgstr ""
565+
566+#: qml/Wizard/Pages/50-location.qml:27
567+msgid "Location"
568+msgstr ""
569+
570+#: qml/Wizard/Pages/50-location.qml:62
571+msgid "Let the phone detect your location:"
572+msgstr ""
573+
574+#: qml/Wizard/Pages/50-location.qml:69
575+msgid "Using GPS only (less accurate)"
576+msgstr ""
577+
578+#: qml/Wizard/Pages/50-location.qml:86
579+msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
580+msgstr ""
581+
582+#. Translators: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
583+#: qml/Wizard/Pages/50-location.qml:103
584+msgid ""
585+"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
586+"conditions</a>."
587+msgstr ""
588+
589+#: qml/Wizard/Pages/50-location.qml:112
590+msgid "Not at all"
591+msgstr ""
592+
593+#: qml/Wizard/Pages/50-location.qml:124
594+msgid "You can change your mind later in <b>System Settings</b>."
595+msgstr ""
596+
597+#: qml/Wizard/Pages/60-reporting.qml:26
598+msgid "Improving your experience"
599+msgstr ""
600+
601+#: qml/Wizard/Pages/60-reporting.qml:46
602+msgid ""
603+"Your phone is set up to automatically report errors to Canonical and its "
604+"partners, the makers of the operating system."
605+msgstr ""
606+
607+#: qml/Wizard/Pages/60-reporting.qml:53
608+msgid ""
609+"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
610+"Privacy</b>"
611+msgstr ""
612+
613+#: qml/Wizard/Pages/80-finished.qml:24
614+msgid "All done"
615+msgstr ""
616+
617+#: qml/Wizard/Pages/80-finished.qml:39
618+msgid "Nice work!"
619+msgstr ""
620+
621+#: qml/Wizard/Pages/80-finished.qml:46
622+msgid "Your phone is now ready to use."
623+msgstr ""
624+
625+#: qml/Wizard/Pages/80-finished.qml:53
626+msgid "Finish"
627+msgstr ""
628+
629+#: qml/Wizard/Pages/here-terms.qml:27
630+msgid "Terms & Conditions"
631+msgstr ""
632+
633+#: qml/Wizard/Pages/passwd-confirm.qml:49
634+msgid "Confirm passphrase"
635+msgstr ""
636+
637+#: qml/Wizard/Pages/passwd-confirm.qml:50
638+msgid "Confirm passcode"
639+msgstr ""
640+
641+#: qml/Wizard/Pages/passwd-confirm.qml:53
642+#: qml/Wizard/Pages/passwd-confirm.qml:54
643+msgid "Please try again."
644+msgstr ""
645+
646+#: qml/Wizard/Pages/passwd-set.qml:61
647+msgid "Choose your passcode"
648+msgstr ""
649+
650+#: qml/Wizard/Pages/passwd-set.qml:68
651+msgid "Passphrase must be 4 characters long"
652+msgstr ""
653+
654+#. Translators: This is the arrow for "Back" buttons
655+#: qml/Wizard/StackButton.qml:39
656+#, qt-format
657+msgid "〈 %1"
658+msgstr ""
659+
660+#. Translators: This is the arrow for "Forward" buttons
661+#: qml/Wizard/StackButton.qml:42
662+#, qt-format
663+msgid "%1 〉"
664+msgstr ""
po/en_AU.po+16−14
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2016-02-08 09:50+0000\n"
11−"PO-Revision-Date: 2015-07-12 07:17+0000\n"
11+"PO-Revision-Date: 2016-02-12 23:22+0000\n"
1212"Last-Translator: Jared Norris <jarednorris@ubuntu.com>\n"
1313"Language-Team: English (Australia) <en_AU@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-02-11 06:15+0000\n"
19−"X-Generator: Launchpad (build 17923)\n"
18+"X-Launchpad-Export-Date: 2016-02-13 05:37+0000\n"
19+"X-Generator: Launchpad (build 17925)\n"
2020
2121#: plugins/IntegratedLightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -29,7 +29,7 @@ msgstr "Pin shortcut"
2929
3030#: plugins/Unity/Launcher/launcheritem.cpp:52
3131msgid "Quit"
32−msgstr ""
32+msgstr "Quit"
3333
3434#: plugins/Unity/Launcher/launcheritem.cpp:106
3535msgid "Unpin shortcut"
@@ -151,27 +151,27 @@ msgstr "OK"
151151
152152#: qml/Components/ModeSwitchWarningDialog.qml:32
153153msgid "Apps may have unsaved data:"
154−msgstr ""
154+msgstr "Apps may have unsaved data:"
155155
156156#: qml/Components/ModeSwitchWarningDialog.qml:57
157157msgid "Re-dock, save your work and close these apps to continue."
158−msgstr ""
158+msgstr "Re-dock, save your work and close these apps to continue."
159159
160160#: qml/Components/ModeSwitchWarningDialog.qml:63
161161msgid "Or force close now (unsaved data will be lost)."
162−msgstr ""
162+msgstr "Or force close now (unsaved data will be lost)."
163163
164164#: qml/Components/ModeSwitchWarningDialog.qml:75
165165msgid "OK, I will reconnect"
166−msgstr ""
166+msgstr "OK, I will reconnect"
167167
168168#: qml/Components/ModeSwitchWarningDialog.qml:76
169169msgid "Reconnect now!"
170−msgstr ""
170+msgstr "Reconnect now!"
171171
172172#: qml/Components/ModeSwitchWarningDialog.qml:88
173173msgid "Close all"
174−msgstr ""
174+msgstr "Close all"
175175
176176#: qml/Dash/DashPageHeader.qml:266
177177msgctxt "Button: Open the Ubuntu Store"
@@ -232,7 +232,7 @@ msgstr "Add a review"
232232
233233#: qml/Dash/Previews/PreviewSharing.qml:50
234234msgid "Preview Share Item"
235−msgstr ""
235+msgstr "Preview Share Item"
236236
237237#: qml/Dash/PullToRefreshScopeStyle.qml:55
238238msgid "Pull to refresh…"
@@ -263,6 +263,8 @@ msgid ""
263263"Your device is now connected to an external display. Use this screen as a "
264264"touch pad to interact with the mouse."
265265msgstr ""
266+"Your device is now connected to an external display. Use this screen as a "
267+"touch pad to interact with the mouse."
266268
267269#: qml/Greeter/CoverPage.qml:107
268270msgid "Unlock"
@@ -329,7 +331,7 @@ msgstr "Sorry, incorrect passphrase"
329331#: qml/Greeter/NarrowView.qml:121
330332msgctxt "passphrase"
331333msgid "Please re-enter"
332−msgstr ""
334+msgstr "Please re-enter"
333335
334336#: qml/Greeter/NarrowView.qml:122
335337msgid "Sorry, incorrect passcode"
@@ -410,7 +412,7 @@ msgstr "Failed, tap to retry"
410412#: qml/Panel/Indicators/MessageMenuItemFactory.qml:214
411413msgctxt "Button: Send a reply message"
412414msgid "Send"
413−msgstr ""
415+msgstr "Send"
414416
415417#: qml/Panel/Indicators/MessageMenuItemFactory.qml:156
416418msgctxt "Label: Hint in message indicator line edit"
@@ -420,7 +422,7 @@ msgstr "Reply"
420422#: qml/Panel/Indicators/MessageMenuItemFactory.qml:213
421423msgctxt "Button: Call back on phone"
422424msgid "Call back"
423−msgstr ""
425+msgstr "Call back"
424426
425427#: qml/Panel/Indicators/ModemInfoItem.qml:105
426428msgid "Unlock SIM"
po/fr.po+6−4
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2016-02-08 09:50+0000\n"
11−"PO-Revision-Date: 2015-11-07 19:43+0000\n"
12−"Last-Translator: Jean-Marc <Unknown>\n"
11+"PO-Revision-Date: 2016-02-13 09:30+0000\n"
12+"Last-Translator: Anne <anneonyme017@gmail.com>\n"
1313"Language-Team: French <fr@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2016-02-11 06:14+0000\n"
19−"X-Generator: Launchpad (build 17923)\n"
18+"X-Launchpad-Export-Date: 2016-02-14 06:10+0000\n"
19+"X-Generator: Launchpad (build 17925)\n"
2020
2121#: plugins/IntegratedLightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -271,6 +271,8 @@ msgid ""
271271"Your device is now connected to an external display. Use this screen as a "
272272"touch pad to interact with the mouse."
273273msgstr ""
274+"Votre appareil est maintenant connecté à un affichage externe. Utilisez cet "
275+"écran comme un pavé tactile pour interagir avec la souris."
274276
275277#: qml/Greeter/CoverPage.qml:107
276278msgid "Unlock"
po/unity8.pot+6−6
@@ -8,7 +8,7 @@ msgid ""
88msgstr ""
99"Project-Id-Version: unity8\n"
1010"Report-Msgid-Bugs-To: \n"
11−"POT-Creation-Date: 2016-02-08 09:50+0000\n"
11+"POT-Creation-Date: 2016-02-12 00:28+0000\n"
1212"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
1313"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1414"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -266,11 +266,11 @@ msgstr ""
266266msgid "Unlock"
267267msgstr ""
268268
269−#: qml/Greeter/LoginList.qml:236
269+#: qml/Greeter/LoginList.qml:239
270270msgid "Retry"
271271msgstr ""
272272
273−#: qml/Greeter/LoginList.qml:237
273+#: qml/Greeter/LoginList.qml:240
274274msgid "Tap to unlock"
275275msgstr ""
276276
@@ -487,7 +487,7 @@ msgid "Hi!"
487487msgstr ""
488488
489489#: qml/Wizard/Pages/10-welcome.qml:44
490−msgid "Welcome to your Ubuntu phone."
490+msgid "Welcome to your Ubuntu device."
491491msgstr ""
492492
493493#: qml/Wizard/Pages/10-welcome.qml:52
@@ -518,7 +518,7 @@ msgid "Lock security"
518518msgstr ""
519519
520520#: qml/Wizard/Pages/30-passwd-type.qml:79
521−msgid "Please select how you’d like to unlock your phone."
521+msgid "Please select how you’d like to unlock your device."
522522msgstr ""
523523
524524#: qml/Wizard/Pages/30-passwd-type.qml:102
@@ -619,7 +619,7 @@ msgid "Nice work!"
619619msgstr ""
620620
621621#: qml/Wizard/Pages/80-finished.qml:46
622−msgid "Your phone is now ready to use."
622+msgid "Your device is now ready to use."
623623msgstr ""
624624
625625#: qml/Wizard/Pages/80-finished.qml:53
qml/Components/EdgeBarrierController.qml+2
@@ -30,6 +30,8 @@ MouseArea {
3030 // Should trigger the action associated with this edge
3131 signal passed()
3232
33+ onPressed: mouse.accepted = false;
34+
3335 function push(amount) {
3436 if (!root._containsMouse) {
3537 return;
qml/Components/VolumeControl.qml+1−6
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013-2015 Canonical, Ltd.
2+ * Copyright (C) 2013-2016 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -28,10 +28,6 @@ Item {
2828 readonly property int stepDown: -1
2929
3030 property var indicators // passed from Shell.qml
31− readonly property bool showNotification: indicators && indicators.fullyOpened && indicators.currentIndicator === "indicator-sound"
32− onShowNotificationChanged: { // disallow the volume notification when using the slider, lpbug#1484126
33− actionGroup.indicatorsAction.updateState(root.showNotification);
34− }
3531
3632 GlobalShortcut {
3733 id: muteShortcut
@@ -47,7 +43,6 @@ Item {
4743
4844 property variant actionObject: action("volume")
4945 property variant muteActionObject: indicators.indicatorsModel.profile === "desktop" ? action("mute") : action("silent-mode")
50− property variant indicatorsAction: action("indicator-shown")
5146 }
5247
5348 function volumeUp() {
qml/Components/ZoomableImage.qml+1−1
@@ -65,7 +65,7 @@ Item {
6565 anchors.fill: parent
6666 fillMode: Image.PreserveAspectFit
6767
68− readonly property int sourceSizeMultiplier: 3
68+ readonly property int sourceSizeMultiplier: root.zoomable ? 3 : 1
6969
7070 sourceSize.width: root.width * sourceSizeMultiplier <= root.height * sourceSizeMultiplier ? root.width * sourceSizeMultiplier : 0
7171 sourceSize.height: root.height * sourceSizeMultiplier <= root.width * sourceSizeMultiplier ? root.height * sourceSizeMultiplier : 0
qml/Dash/CardCarousel.qml−1
@@ -64,7 +64,6 @@ DashRenderer {
6464 item.fixedHeaderHeight = Qt.binding(function() { return carousel.headerHeight; });
6565 item.height = Qt.binding(function() { return cardTool.cardHeight; });
6666 item.cardData = Qt.binding(function() { return model; });
67− item.components = Qt.binding(function() { return cardTool.components; });
6867 item.fontScale = Qt.binding(function() { return carousel.fontScale; });
6968 item.showHeader = Qt.binding(function() { return loader.explicitlyScaled; });
7069 item.titleAlignment = Qt.binding(function() { return cardTool.titleAlignment; });
qml/Dash/CardGrid.qml−1
@@ -70,7 +70,6 @@ DashRenderer {
7070 item.fixedHeaderHeight = Qt.binding(function() { return cardTool.headerHeight; });
7171 item.fixedArtShapeSize = Qt.binding(function() { return cardTool.artShapeSize; });
7272 item.cardData = Qt.binding(function() { return model; });
73− item.components = Qt.binding(function() { return cardTool.components; });
7473 item.titleAlignment = Qt.binding(function() { return cardTool.titleAlignment; });
7574 item.scopeStyle = root.scopeStyle;
7675 item.artShapeStyle = root.artShapeStyle;
qml/Dash/CardHorizontalList.qml−1
@@ -53,7 +53,6 @@ DashRenderer {
5353 item.fixedArtShapeSize = Qt.binding(function() { return cardTool.artShapeSize; });
5454 item.fixedHeaderHeight = Qt.binding(function() { return cardTool.headerHeight; });
5555 item.cardData = Qt.binding(function() { return model; });
56− item.components = Qt.binding(function() { return cardTool.components; });
5756 item.titleAlignment = Qt.binding(function() { return cardTool.titleAlignment; });
5857 item.scopeStyle = root.scopeStyle;
5958 }
qml/Dash/CardTool.qml−1
@@ -220,7 +220,6 @@ Item {
220220 onLoaded: {
221221 item.objectName = "cardToolCard";
222222 item.asynchronous = false;
223− item.components = Qt.binding(function() { return cardTool.components; });
224223 item.width = Qt.binding(function() { return cardTool.cardWidth !== -1 ? cardTool.cardWidth : item.implicitWidth; });
225224 item.height = Qt.binding(function() { return cardTool.cardHeight !== -1 ? cardTool.cardHeight : item.implicitHeight; });
226225 }
qml/Dash/CardVerticalJournal.qml−1
@@ -72,7 +72,6 @@ DashRenderer {
7272 item.fixedArtShapeSize = Qt.binding(function() { return cardTool.artShapeSize; });
7373 item.fixedHeaderHeight = Qt.binding(function() { return cardTool.headerHeight; });
7474 item.cardData = Qt.binding(function() { return model; });
75− item.components = Qt.binding(function() { return cardTool.components; });
7675 item.titleAlignment = Qt.binding(function() { return cardTool.titleAlignment; });
7776 item.scopeStyle = root.scopeStyle;
7877 }
qml/Dash/GenericScopeView.qml+1−1
@@ -363,7 +363,7 @@ FocusScope {
363363 clickScopeSizingHacks();
364364 if (scope && scope.id === "clickscope") {
365365 if (categoryId === "predefined" || categoryId === "local") {
366− cardTool.artShapeSize = Qt.size(units.gu(8), units.gu(7.5));
366+ cardTool.artShapeSize = Qt.binding(function() { return Qt.size(units.gu(8), units.gu(7.5)) });
367367 item.artShapeStyle = "icon";
368368 } else {
369369 // Should be ubuntu store icon
qml/Dash/Previews/PreviewHeader.qml+1−1
@@ -35,7 +35,7 @@ PreviewWidget {
3535 Item {
3636 id: headerRoot
3737 objectName: "innerPreviewHeader"
38− readonly property url mascot: root.widgetData["mascot"] || ""
38+ readonly property url mascot: root.widgetData["mascot"] || fallback
3939 readonly property url fallback: root.widgetData["fallback"] || ""
4040 readonly property string title: root.widgetData["title"] || ""
4141 readonly property string subtitle: root.widgetData["subtitle"] || ""
qml/Dash/Previews/PreviewImageGallery.qml+2−2
@@ -59,7 +59,7 @@ PreviewWidget {
5959 top: parent.top
6060 bottom: parent.bottom
6161 }
62− source: modelData ? modelData : ""
62+ source: modelData || root.widgetData["fallback"] || ""
6363 scaleTo: "height"
6464 initialWidth: units.gu(13)
6565 pressed: mouseArea.pressed
@@ -128,7 +128,7 @@ PreviewWidget {
128128 bottom: parent.bottom
129129 }
130130 width: overlay.width
131− source: modelData ? modelData : ""
131+ source: modelData || root.widgetData["fallback"] || ""
132132 fillMode: Image.PreserveAspectFit
133133 sourceSize { width: screenshot.width; height: screenshot.height }
134134
qml/Dash/Previews/PreviewRatingEdit.qml+1
@@ -75,6 +75,7 @@ PreviewWidget {
7575 reviewText: widgetData["review"]
7676
7777 onTriggered: root.triggered(widgetId, actionId, data);
78+ onMakeSureVisible: root.makeSureVisible(item);
7879 }
7980
8081}
qml/Dash/Previews/PreviewZoomableImage.qml+2−2
@@ -42,7 +42,7 @@ PreviewWidget {
4242 right: parent.right
4343 }
4444 scaleTo: "width"
45− source: widgetData["source"]
45+ source: widgetData["source"] || widgetData["fallback"] || ""
4646 asynchronous: true
4747 useUbuntuShape: false
4848 pressed: mouseArea.pressed
@@ -86,7 +86,7 @@ PreviewWidget {
8686
8787 delegate: ZoomableImage {
8888 anchors.fill: parent
89− source: widgetData["source"]
89+ source: widgetData["source"] || widgetData["fallback"] || ""
9090 zoomable: widgetData["zoomable"] ? widgetData["zoomable"] : false
9191 // If modelData would change after failing to load it would not be
9292 // reloaded since the source binding is destroyed by the source = fallback
qml/Greeter/LoginList.qml+3
@@ -22,6 +22,7 @@ Item {
2222 id: root
2323
2424 property alias model: userList.model
25+ property bool alphanumeric: true
2526 property int currentIndex
2627 property bool locked
2728
@@ -231,6 +232,8 @@ Item {
231232 width: parent.width - anchors.margins * 2
232233 opacity: userList.movingInternally ? 0 : 1
233234
235+ inputMethodHints: root.alphanumeric ? Qt.ImhNone : Qt.ImhDigitsOnly
236+
234237 property string promptText
235238 placeholderText: root.wasPrompted ? promptText
236239 : (root.locked ? i18n.tr("Retry")
qml/Greeter/WideView.qml+1−1
@@ -27,7 +27,7 @@ FocusScope {
2727 property alias backgroundTopMargin: coverPage.backgroundTopMargin
2828 property alias background: coverPage.background
2929 property bool locked
30− property bool alphanumeric // unused
30+ property alias alphanumeric: loginList.alphanumeric
3131 property alias userModel: loginList.model
3232 property alias infographicModel: coverPage.infographicModel
3333 readonly property bool fullyShown: coverPage.showProgress === 1
qml/Panel/IndicatorItem.qml+57−7
@@ -1,5 +1,5 @@
11/*
2− * Copyright 2013-2014 Canonical Ltd.
2+ * Copyright 2013-2016 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU Lesser General Public License as published by
@@ -17,6 +17,7 @@
1717import QtQuick 2.4
1818import Ubuntu.Components 1.3
1919import Ubuntu.Settings.Components 0.1
20+import QMenuModel 0.1
2021import "Indicators"
2122
2223IndicatorDelegate {
@@ -36,13 +37,24 @@ IndicatorDelegate {
3637 return "#ffffff";
3738 }
3839
39− signal clicked()
40−
4140 implicitWidth: mainItems.width
4241
4342 MouseArea {
43+ readonly property int stepUp: 1
44+ readonly property int stepDown: -1
45+
4446 anchors.fill: parent
45− onClicked: parent.clicked()
47+ acceptedButtons: Qt.MiddleButton
48+ onClicked: {
49+ if ((!expanded || selected) && secondaryAction.valid) {
50+ secondaryAction.activate();
51+ }
52+ }
53+ onWheel: {
54+ if ((!expanded || selected) && scrollAction.valid) {
55+ scrollAction.activate(wheel.angleDelta.y > 0 ? stepUp : stepDown);
56+ }
57+ }
4658 }
4759
4860 Item {
@@ -152,9 +164,6 @@ IndicatorDelegate {
152164 }
153165
154166 StateGroup {
155− id: d
156− property bool useFallbackIcon: false
157−
158167 states: [
159168 State {
160169 name: "minimised"
@@ -251,4 +260,45 @@ IndicatorDelegate {
251260 rightLabel = rootActionState.rightLabel ? rootActionState.rightLabel : "";
252261 icons = rootActionState.icons;
253262 }
263+
264+ QtObject {
265+ id: d
266+
267+ property bool useFallbackIcon: false
268+ property var shouldIndicatorBeShown: undefined
269+
270+ onShouldIndicatorBeShownChanged: {
271+ if (shouldIndicatorBeShown !== undefined) {
272+ submenuAction.changeState(shouldIndicatorBeShown);
273+ }
274+ }
275+ }
276+
277+ UnityMenuAction {
278+ id: secondaryAction
279+ model: menuModel
280+ index: 0
281+ name: rootActionState.secondaryAction
282+ }
283+
284+ UnityMenuAction {
285+ id: scrollAction
286+ model: menuModel
287+ index: 0
288+ name: rootActionState.scrollAction
289+ }
290+
291+ UnityMenuAction {
292+ id: submenuAction
293+ model: menuModel
294+ index: 0
295+ name: rootActionState.submenuAction
296+ }
297+
298+ Binding {
299+ target: d
300+ property: "shouldIndicatorBeShown"
301+ when: submenuAction.valid
302+ value: root.selected && root.expanded
303+ }
254304}
qml/Panel/graphics/Battery@18.pngdeleted

Binary file not shown.

qml/Panel/graphics/Bluetooth@18.pngdeleted

Binary file not shown.

qml/Panel/graphics/Clock@18.pngdeleted

Binary file not shown.

qml/Panel/graphics/Location@18.pngdeleted

Binary file not shown.

qml/Panel/graphics/Messages@18.pngdeleted

Binary file not shown.

qml/Panel/graphics/Network@18.pngdeleted

Binary file not shown.

qml/Panel/graphics/Sound@18.pngdeleted

Binary file not shown.

qml/Panel/graphics/Sync@18.pngdeleted

Binary file not shown.

qml/Panel/graphics/System@18.pngdeleted

Binary file not shown.

qml/Panel/graphics/Time@18.pngdeleted

Binary file not shown.

qml/Panel/graphics/VerticalDivider@18.pngdeleted

Binary file not shown.

qml/Panel/graphics/handle@12.pngdeleted

Binary file not shown.

qml/Panel/graphics/handle@12.scideleted−5
@@ -1,5 +0,0 @@
1−border.left: 0
2−border.top: 1
3−border.bottom: 0
4−border.right: 0
5−source: handle@12.png
qml/Panel/graphics/search@12.pngdeleted

Binary file not shown.

qml/Panel/graphics/time_and_date@18.pngdeleted

Binary file not shown.

qml/Stages/ApplicationWindow.qml+14−1
@@ -1,5 +1,5 @@
11/*
2− * Copyright 2014-2015 Canonical Ltd.
2+ * Copyright 2014-2016 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU Lesser General Public License as published by
@@ -37,6 +37,13 @@ FocusScope {
3737 property int requestedWidth: -1
3838 property int requestedHeight: -1
3939
40+ readonly property int minimumWidth: sessionContainer.surface ? sessionContainer.surface.minimumWidth : 0
41+ readonly property int minimumHeight: sessionContainer.surface ? sessionContainer.surface.minimumHeight : 0
42+ readonly property int maximumWidth: sessionContainer.surface ? sessionContainer.surface.maximumWidth : 0
43+ readonly property int maximumHeight: sessionContainer.surface ? sessionContainer.surface.maximumHeight : 0
44+ readonly property int widthIncrement: sessionContainer.surface ? sessionContainer.surface.widthIncrement : 0
45+ readonly property int heightIncrement: sessionContainer.surface ? sessionContainer.surface.heightIncrement : 0
46+
4047 QtObject {
4148 id: d
4249
@@ -82,6 +89,12 @@ FocusScope {
8289 property bool surfaceOldEnoughToBeResized: false
8390 }
8491
92+ Binding {
93+ target: root.application
94+ property: "initialSurfaceSize"
95+ value: Qt.size(root.requestedWidth, root.requestedHeight)
96+ }
97+
8598 Timer {
8699 id: surfaceInitTimer
87100 interval: 100
qml/Stages/DecoratedWindow.qml+7
@@ -39,6 +39,13 @@ FocusScope {
3939 property alias requestedWidth: applicationWindow.requestedWidth
4040 property real requestedHeight
4141
42+ property alias minimumWidth: applicationWindow.minimumWidth
43+ readonly property int minimumHeight: (root.decorationShown ? decoration.height : 0) + applicationWindow.minimumHeight
44+ property alias maximumWidth: applicationWindow.maximumWidth
45+ readonly property int maximumHeight: (root.decorationShown ? decoration.height : 0) + applicationWindow.maximumHeight
46+ property alias widthIncrement: applicationWindow.widthIncrement
47+ property alias heightIncrement: applicationWindow.heightIncrement
48+
4249 signal close()
4350 signal maximize()
4451 signal minimize()
qml/Stages/DesktopStage.qml+6
@@ -256,6 +256,12 @@ AbstractStage {
256256 height: decoratedWindow.height
257257 property alias requestedWidth: decoratedWindow.requestedWidth
258258 property alias requestedHeight: decoratedWindow.requestedHeight
259+ property alias minimumWidth: decoratedWindow.minimumWidth
260+ property alias minimumHeight: decoratedWindow.minimumHeight
261+ property alias maximumWidth: decoratedWindow.maximumWidth
262+ property alias maximumHeight: decoratedWindow.maximumHeight
263+ property alias widthIncrement: decoratedWindow.widthIncrement
264+ property alias heightIncrement: decoratedWindow.heightIncrement
259265
260266 QtObject {
261267 id: appDelegatePrivate
qml/Stages/SessionContainer.qml+2
@@ -152,6 +152,8 @@ FocusScope {
152152 popped.completed.connect(function() { root.session.release(); } );
153153 popped.end();
154154 d.animations = tmp;
155+ } else {
156+ root.session.release();
155157 }
156158 }
157159
qml/Stages/Splash.qml+7−2
@@ -138,15 +138,20 @@ Item {
138138 visible: d.showIcon
139139 }
140140
141+ Timer {
142+ interval: 2000
143+ onTriggered: spinner.running = true
144+ running: true
145+ }
146+
141147 ActivityIndicator {
148+ id: spinner
142149 anchors.centerIn: header.visible ? parent : undefined
143150 anchors.verticalCenterOffset: header.visible ? header.height / 2 : 0
144151
145152 anchors.horizontalCenter: header.visible ? undefined : parent.horizontalCenter
146153 anchors.bottom: header.visible ? undefined : parent.bottom
147154 anchors.bottomMargin: header.visible ? 0 : units.gu(12)
148−
149− running: true
150155 }
151156
152157 MouseArea {
qml/Stages/WindowResizeArea.qml+95−16
@@ -72,8 +72,8 @@ MouseArea {
7272 var windowGeometry = windowStateStorage.getGeometry(root.windowId,
7373 Qt.rect(target.x, target.y, defaultWidth, defaultHeight));
7474
75− target.requestedWidth = Math.min(Math.max(windowGeometry.width, minWidth), screenWidth);
76− target.requestedHeight = Math.min(Math.max(windowGeometry.height, minHeight), root.screenHeight - PanelState.panelHeight);
75+ target.requestedWidth = Math.min(Math.max(windowGeometry.width, d.minimumWidth), screenWidth);
76+ target.requestedHeight = Math.min(Math.max(windowGeometry.height, d.minimumHeight), root.screenHeight - PanelState.panelHeight);
7777 target.x = Math.max(Math.min(windowGeometry.x, root.screenWidth - target.requestedWidth), 0)
7878 target.y = Math.max(Math.min(windowGeometry.y, root.screenHeight - target.requestedHeight), PanelState.panelHeight)
7979
@@ -91,6 +91,65 @@ MouseArea {
9191
9292 QtObject {
9393 id: d
94+
95+ readonly property int maxSafeInt: 2147483647
96+ readonly property int maxSizeIncrement: units.gu(40)
97+
98+ readonly property int minimumWidth: root.target ? Math.max(root.minWidth, root.target.minimumWidth) : root.minWidth
99+ onMinimumWidthChanged: {
100+ if (target.requestedWidth < minimumWidth) {
101+ target.requestedWidth = minimumWidth;
102+ }
103+ }
104+ readonly property int minimumHeight: root.target ? Math.max(root.minHeight, root.target.minimumHeight) : root.minHeight
105+ onMinimumHeightChanged: {
106+ if (target.requestedHeight < minimumHeight) {
107+ target.requestedHeight = minimumHeight;
108+ }
109+ }
110+ readonly property int maximumWidth: root.target && root.target.maximumWidth >= minimumWidth && root.target.maximumWidth > 0
111+ ? root.target.maximumWidth : maxSafeInt
112+ onMaximumWidthChanged: {
113+ if (target.requestedWidth > maximumWidth) {
114+ target.requestedWidth = maximumWidth;
115+ }
116+ }
117+ readonly property int maximumHeight: root.target && root.target.maximumHeight >= minimumHeight && root.target.maximumHeight > 0
118+ ? root.target.maximumHeight : maxSafeInt
119+ onMaximumHeightChanged: {
120+ if (target.requestedHeight > maximumHeight) {
121+ target.requestedHeight = maximumHeight;
122+ }
123+ }
124+ readonly property int widthIncrement: {
125+ if (!root.target) {
126+ return 1;
127+ }
128+ if (root.target.widthIncrement > 0) {
129+ if (root.target.widthIncrement < maxSizeIncrement) {
130+ return root.target.widthIncrement;
131+ } else {
132+ return maxSizeIncrement;
133+ }
134+ } else {
135+ return 1;
136+ }
137+ }
138+ readonly property int heightIncrement: {
139+ if (!root.target) {
140+ return 1;
141+ }
142+ if (root.target.heightIncrement > 0) {
143+ if (root.target.heightIncrement < maxSizeIncrement) {
144+ return root.target.heightIncrement;
145+ } else {
146+ return maxSizeIncrement;
147+ }
148+ } else {
149+ return 1;
150+ }
151+ }
152+
94153 property bool leftBorder: false
95154 property bool rightBorder: false
96155 property bool topBorder: false
@@ -209,38 +268,58 @@ MouseArea {
209268
210269 var pos = mapToItem(target.parent, mouse.x, mouse.y);
211270
212− var deltaX = pos.x - d.startMousePosX;
213− var deltaY = pos.y - d.startMousePosY;
271+ var deltaX = Math.floor((pos.x - d.startMousePosX) / d.widthIncrement) * d.widthIncrement;
272+ var deltaY = Math.floor((pos.y - d.startMousePosY) / d.heightIncrement) * d.heightIncrement;
214273
215274 if (d.leftBorder) {
216275 var newTargetX = d.startX + deltaX;
217− if (target.x + target.width > newTargetX + minWidth) {
218− target.requestedWidth = target.x + target.width - newTargetX;
276+ var rightBorderX = target.x + target.width;
277+ if (rightBorderX > newTargetX + d.minimumWidth) {
278+ if (rightBorderX < newTargetX + d.maximumWidth) {
279+ target.requestedWidth = rightBorderX - newTargetX;
280+ } else {
281+ target.requestedWidth = d.maximumWidth;
282+ }
219283 } else {
220− target.requestedWidth = minWidth;
284+ target.requestedWidth = d.minimumWidth;
221285 }
222286
223287 } else if (d.rightBorder) {
224− if (d.startWidth + deltaX >= minWidth) {
225− target.requestedWidth = d.startWidth + deltaX;
288+ var newWidth = d.startWidth + deltaX;
289+ if (newWidth > d.minimumWidth) {
290+ if (newWidth < d.maximumWidth) {
291+ target.requestedWidth = newWidth;
292+ } else {
293+ target.requestedWidth = d.maximumWidth;
294+ }
226295 } else {
227− target.requestedWidth = minWidth;
296+ target.requestedWidth = d.minimumWidth;
228297 }
229298 }
230299
231300 if (d.topBorder) {
232301 var newTargetY = d.startY + deltaY;
233− if (target.y + target.height > newTargetY + minHeight) {
234− target.requestedHeight = target.y + target.height - newTargetY;
302+ var bottomBorderY = target.y + target.height;
303+ if (bottomBorderY > newTargetY + d.minimumHeight) {
304+ if (bottomBorderY < newTargetY + d.maximumHeight) {
305+ target.requestedHeight = bottomBorderY - newTargetY;
306+ } else {
307+ target.requestedHeight = d.maximumHeight;
308+ }
235309 } else {
236− target.requestedHeight = minHeight;
310+ target.requestedHeight = d.minimumHeight;
237311 }
238312
239313 } else if (d.bottomBorder) {
240− if (d.startHeight + deltaY >= minHeight) {
241− target.requestedHeight = d.startHeight + deltaY;
314+ var newHeight = d.startHeight + deltaY;
315+ if (newHeight > d.minimumHeight) {
316+ if (newHeight < d.maximumHeight) {
317+ target.requestedHeight = newHeight;
318+ } else {
319+ target.requestedHeight = d.maximumHeight;
320+ }
242321 } else {
243− target.requestedHeight = minHeight;
322+ target.requestedHeight = d.minimumHeight;
244323 }
245324 }
246325 }
tests/mocks/Unity/Application/ApplicationInfo.cpp+14−1
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013-2015 Canonical, Ltd.
2+ * Copyright (C) 2013-2016 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -278,3 +278,16 @@ void ApplicationInfo::setExemptFromLifecycle(bool exemptFromLifecycle)
278278 Q_EMIT exemptFromLifecycleChanged(m_exemptFromLifecycle);
279279 }
280280}
281+
282+QSize ApplicationInfo::initialSurfaceSize() const
283+{
284+ return m_initialSurfaceSize;
285+}
286+
287+void ApplicationInfo::setInitialSurfaceSize(const QSize &size)
288+{
289+ if (size != m_initialSurfaceSize) {
290+ m_initialSurfaceSize = size;
291+ Q_EMIT initialSurfaceSizeChanged(m_initialSurfaceSize);
292+ }
293+}
tests/mocks/Unity/Application/ApplicationInfo.h+4−1
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013-2015 Canonical, Ltd.
2+ * Copyright (C) 2013-2016 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -95,6 +95,8 @@ public:
9595 bool exemptFromLifecycle() const override;
9696 void setExemptFromLifecycle(bool) override;
9797
98+ QSize initialSurfaceSize() const override;
99+ void setInitialSurfaceSize(const QSize &size) override;
98100public:
99101 void setSession(Session* session);
100102 Session* session() const { return m_session; }
@@ -129,6 +131,7 @@ private:
129131 RequestedState m_requestedState;
130132 bool m_isTouchApp;
131133 bool m_exemptFromLifecycle;
134+ QSize m_initialSurfaceSize;
132135
133136 bool m_manualSurfaceCreation;
134137};
tests/mocks/Unity/Application/ApplicationTestInterface.cpp+4−4
@@ -36,7 +36,7 @@ ApplicationTestInterface::ApplicationTestInterface(QObject* parent)
3636 connection.registerObject("/com/canonical/Unity8/Mocks", parent);
3737}
3838
39−Session* ApplicationTestInterface::addChildSession(Session* existingSession, const QString& surfaceImage)
39+Session* ApplicationTestInterface::addChildSession(Session* existingSession, const QString& surfaceImage, bool addSurface)
4040{
4141 if (!existingSession) return nullptr;
4242
@@ -49,7 +49,7 @@ Session* ApplicationTestInterface::addChildSession(Session* existingSession, con
4949 .arg(existingSession->childSessions()->count()),
5050 screenshotUrl);
5151 existingSession->addChildSession(session);
52− session->createSurface();
52+ if (addSurface) session->createSurface();
5353
5454 return session;
5555}
@@ -70,7 +70,7 @@ void ApplicationTestInterface::removeSurface(MirSurface* existingSurface)
7070 existingSurface->setLive(false);
7171}
7272
73−quint32 ApplicationTestInterface::addChildSession(const QString& appId, quint32 existingSessionId, const QString& surfaceImage)
73+quint32 ApplicationTestInterface::addChildSession(const QString& appId, quint32 existingSessionId, const QString& surfaceImage, bool addSurface)
7474{
7575 qDebug() << "ApplicationTestInterface::addChildSession to " << appId;
7676
@@ -100,7 +100,7 @@ quint32 ApplicationTestInterface::addChildSession(const QString& appId, quint32
100100 .arg(parentSession->childSessions()->count()),
101101 screenshotUrl);
102102 parentSession->addChildSession(session);
103− session->createSurface();
103+ if (addSurface) { session->createSurface(); }
104104 m_childSessions[sessionId] = session;
105105
106106 return sessionId;
tests/mocks/Unity/Application/ApplicationTestInterface.h+2−2
@@ -30,12 +30,12 @@ class ApplicationTestInterface : public QDBusAbstractAdaptor
3030public:
3131 ApplicationTestInterface(QObject* parent = 0);
3232
33− Q_INVOKABLE Session* addChildSession(Session* existingSession, const QString& surfaceImage);
33+ Q_INVOKABLE Session* addChildSession(Session* existingSession, const QString& surfaceImage, bool addSurface);
3434 Q_INVOKABLE void removeSession(Session* existingSession);
3535 Q_INVOKABLE void removeSurface(MirSurface* existingSurface);
3636
3737public Q_SLOTS:
38− quint32 addChildSession(const QString& appId, quint32 existingSessionId, const QString& surfaceImage);
38+ quint32 addChildSession(const QString& appId, quint32 existingSessionId, const QString& surfaceImage, bool addSurface);
3939 void removeSession(quint32 sessionId);
4040
4141private:
tests/mocks/Unity/Application/MirSurface.cpp+48
@@ -266,3 +266,51 @@ void MirSurface::setSlowToResize(bool value)
266266 }
267267 }
268268}
269+
270+void MirSurface::setMinimumWidth(int value)
271+{
272+ if (value != m_minimumWidth) {
273+ m_minimumWidth = value;
274+ Q_EMIT minimumWidthChanged(m_minimumWidth);
275+ }
276+}
277+
278+void MirSurface::setMaximumWidth(int value)
279+{
280+ if (value != m_maximumWidth) {
281+ m_maximumWidth = value;
282+ Q_EMIT maximumWidthChanged(m_maximumWidth);
283+ }
284+}
285+
286+void MirSurface::setMinimumHeight(int value)
287+{
288+ if (value != m_minimumHeight) {
289+ m_minimumHeight = value;
290+ Q_EMIT minimumHeightChanged(m_minimumHeight);
291+ }
292+}
293+
294+void MirSurface::setMaximumHeight(int value)
295+{
296+ if (value != m_maximumHeight) {
297+ m_maximumHeight = value;
298+ Q_EMIT maximumHeightChanged(m_maximumHeight);
299+ }
300+}
301+
302+void MirSurface::setWidthIncrement(int value)
303+{
304+ if (value != m_widthIncrement) {
305+ m_widthIncrement = value;
306+ Q_EMIT widthIncrementChanged(m_widthIncrement);
307+ }
308+}
309+
310+void MirSurface::setHeightIncrement(int value)
311+{
312+ if (value != m_heightIncrement) {
313+ m_heightIncrement = value;
314+ Q_EMIT heightIncrementChanged(m_heightIncrement);
315+ }
316+}
tests/mocks/Unity/Application/MirSurface.h+21
@@ -66,6 +66,13 @@ public:
6666 Mir::OrientationAngle orientationAngle() const override;
6767 void setOrientationAngle(Mir::OrientationAngle) override;
6868
69+ int minimumWidth() const override { return m_minimumWidth; }
70+ int minimumHeight() const override { return m_minimumHeight; }
71+ int maximumWidth() const override { return m_maximumWidth; }
72+ int maximumHeight() const override { return m_maximumHeight; }
73+ int widthIncrement() const override { return m_widthIncrement; }
74+ int heightIncrement() const override { return m_heightIncrement; }
75+
6976 ////
7077 // API for tests
7178
@@ -82,6 +89,13 @@ public:
8289 bool isSlowToResize() const;
8390 void setSlowToResize(bool value);
8491
92+ Q_INVOKABLE void setMinimumWidth(int);
93+ Q_INVOKABLE void setMaximumWidth(int);
94+ Q_INVOKABLE void setMinimumHeight(int);
95+ Q_INVOKABLE void setMaximumHeight(int);
96+ Q_INVOKABLE void setWidthIncrement(int);
97+ Q_INVOKABLE void setHeightIncrement(int);
98+
8599 /////
86100 // internal mock stuff
87101
@@ -125,6 +139,13 @@ private:
125139 int m_width;
126140 int m_height;
127141
142+ int m_minimumWidth{0};
143+ int m_minimumHeight{0};
144+ int m_maximumWidth{0};
145+ int m_maximumHeight{0};
146+ int m_widthIncrement{0};
147+ int m_heightIncrement{0};
148+
128149 bool m_slowToResize;
129150 QTimer m_delayedResizeTimer;
130151 QSize m_delayedResize;
tests/mocks/Unity/Application/MirSurfaceItem.cpp+6
@@ -37,6 +37,8 @@ MirSurfaceItem::MirSurfaceItem(QQuickItem *parent)
3737 , m_surfaceHeight(0)
3838 , m_touchPressCount(0)
3939 , m_touchReleaseCount(0)
40+ , m_mousePressCount(0)
41+ , m_mouseReleaseCount(0)
4042{
4143// qDebug() << "MirSurfaceItem::MirSurfaceItem() " << (void*)(this) << name();
4244 setAcceptedMouseButtons(Qt::LeftButton | Qt::MiddleButton | Qt::RightButton |
@@ -165,6 +167,8 @@ void MirSurfaceItem::touchEvent(QTouchEvent * event)
165167
166168void MirSurfaceItem::mousePressEvent(QMouseEvent * event)
167169{
170+ m_mousePressCount++;
171+ Q_EMIT mousePressCountChanged(m_mousePressCount);
168172 event->accept();
169173}
170174
@@ -175,6 +179,8 @@ void MirSurfaceItem::mouseMoveEvent(QMouseEvent * event)
175179
176180void MirSurfaceItem::mouseReleaseEvent(QMouseEvent * event)
177181{
182+ m_mouseReleaseCount++;
183+ Q_EMIT mouseReleaseCountChanged(m_mouseReleaseCount);
178184 event->accept();
179185}
180186
tests/mocks/Unity/Application/MirSurfaceItem.h+14
@@ -36,6 +36,10 @@ class MirSurfaceItem : public unity::shell::application::MirSurfaceItemInterface
3636 Q_PROPERTY(int touchReleaseCount READ touchReleaseCount WRITE setTouchReleaseCount
3737 NOTIFY touchReleaseCountChanged DESIGNABLE false)
3838
39+ Q_PROPERTY(int mousePressCount READ mousePressCount WRITE setMousePressCount
40+ NOTIFY mousePressCountChanged)
41+ Q_PROPERTY(int mouseReleaseCount READ mouseReleaseCount WRITE setMouseReleaseCount
42+ NOTIFY mouseReleaseCountChanged)
3943public:
4044 explicit MirSurfaceItem(QQuickItem *parent = 0);
4145 ~MirSurfaceItem();
@@ -76,9 +80,17 @@ public:
7680 int touchReleaseCount() const { return m_touchReleaseCount; }
7781 void setTouchReleaseCount(int count) { m_touchReleaseCount = count; Q_EMIT touchReleaseCountChanged(count); }
7882
83+ int mousePressCount() const { return m_mousePressCount; }
84+ void setMousePressCount(int count) { m_mousePressCount = count; Q_EMIT mousePressCountChanged(count); }
85+
86+ int mouseReleaseCount() const { return m_mouseReleaseCount; }
87+ void setMouseReleaseCount(int count) { m_mouseReleaseCount = count; Q_EMIT mouseReleaseCountChanged(count); }
88+
7989Q_SIGNALS:
8090 void touchPressCountChanged(int count);
8191 void touchReleaseCountChanged(int count);
92+ void mousePressCountChanged(int count);
93+ void mouseReleaseCountChanged(int count);
8294
8395protected:
8496 void touchEvent(QTouchEvent * event) override;
@@ -109,6 +121,8 @@ private:
109121
110122 int m_touchPressCount;
111123 int m_touchReleaseCount;
124+ int m_mousePressCount;
125+ int m_mouseReleaseCount;
112126
113127 FillMode m_fillMode{Stretch};
114128
tests/mocks/Unity/Application/SurfaceManager.cpp+56
@@ -46,6 +46,14 @@ MirSurface *SurfaceManager::createSurface(const QString& name,
4646 MirSurface* surface = qobject_cast<MirSurface*>(obj);
4747 Q_EMIT surfaceDestroyed(surface);
4848 });
49+
50+ surface->setMinimumWidth(m_newSurfaceMinimumWidth);
51+ surface->setMaximumWidth(m_newSurfaceMaximumWidth);
52+ surface->setMinimumHeight(m_newSurfaceMinimumHeight);
53+ surface->setMaximumHeight(m_newSurfaceMaximumHeight);
54+ surface->setWidthIncrement(m_newSurfaceWidthIncrement);
55+ surface->setHeightIncrement(m_newSurfaceHeightIncrement);
56+
4957 Q_EMIT surfaceCreated(surface);
5058 return surface;
5159}
@@ -62,3 +70,51 @@ MirSurface *SurfaceManager::inputMethodSurface()
6270 }
6371 return m_virtualKeyboard;
6472}
73+
74+void SurfaceManager::setNewSurfaceMinimumWidth(int value)
75+{
76+ if (m_newSurfaceMinimumWidth != value) {
77+ m_newSurfaceMinimumWidth = value;
78+ Q_EMIT newSurfaceMinimumWidthChanged(m_newSurfaceMinimumWidth);
79+ }
80+}
81+
82+void SurfaceManager::setNewSurfaceMaximumWidth(int value)
83+{
84+ if (m_newSurfaceMaximumWidth != value) {
85+ m_newSurfaceMaximumWidth = value;
86+ Q_EMIT newSurfaceMaximumWidthChanged(m_newSurfaceMaximumWidth);
87+ }
88+}
89+
90+void SurfaceManager::setNewSurfaceMinimumHeight(int value)
91+{
92+ if (m_newSurfaceMinimumHeight != value) {
93+ m_newSurfaceMinimumHeight = value;
94+ Q_EMIT newSurfaceMinimumHeightChanged(m_newSurfaceMinimumHeight);
95+ }
96+}
97+
98+void SurfaceManager::setNewSurfaceMaximumHeight(int value)
99+{
100+ if (m_newSurfaceMaximumHeight != value) {
101+ m_newSurfaceMaximumHeight = value;
102+ Q_EMIT newSurfaceMaximumHeightChanged(m_newSurfaceMaximumHeight);
103+ }
104+}
105+
106+void SurfaceManager::setNewSurfaceWidthIncrement(int value)
107+{
108+ if (m_newSurfaceWidthIncrement != value) {
109+ m_newSurfaceWidthIncrement = value;
110+ Q_EMIT newSurfaceWidthIncrementChanged(m_newSurfaceWidthIncrement);
111+ }
112+}
113+
114+void SurfaceManager::setNewSurfaceHeightIncrement(int value)
115+{
116+ if (m_newSurfaceHeightIncrement != value) {
117+ m_newSurfaceHeightIncrement = value;
118+ Q_EMIT newSurfaceHeightIncrementChanged(m_newSurfaceHeightIncrement);
119+ }
120+}
tests/mocks/Unity/Application/SurfaceManager.h+40
@@ -25,6 +25,14 @@
2525class SurfaceManager : public QObject
2626{
2727 Q_OBJECT
28+
29+ Q_PROPERTY(int newSurfaceMinimumWidth READ newSurfaceMinimumWidth WRITE setNewSurfaceMinimumWidth NOTIFY newSurfaceMinimumWidthChanged)
30+ Q_PROPERTY(int newSurfaceMaximumWidth READ newSurfaceMaximumWidth WRITE setNewSurfaceMaximumWidth NOTIFY newSurfaceMaximumWidthChanged)
31+ Q_PROPERTY(int newSurfaceMinimumHeight READ newSurfaceMinimumHeight WRITE setNewSurfaceMinimumHeight NOTIFY newSurfaceMinimumHeightChanged)
32+ Q_PROPERTY(int newSurfaceMaximumHeight READ newSurfaceMaximumHeight WRITE setNewSurfaceMaximumHeight NOTIFY newSurfaceMaximumHeightChanged)
33+ Q_PROPERTY(int newSurfaceWidthIncrement READ newSurfaceWidthIncrement WRITE setNewSurfaceWidthIncrement NOTIFY newSurfaceWidthIncrementChanged)
34+ Q_PROPERTY(int newSurfaceHeightIncrement READ newSurfaceHeightIncrement WRITE setNewSurfaceHeightIncrement NOTIFY newSurfaceHeightIncrementChanged)
35+
2836public:
2937 explicit SurfaceManager(QObject *parent = 0);
3038
@@ -38,14 +46,46 @@ public:
3846 // To be used in the tests
3947 Q_INVOKABLE MirSurface* inputMethodSurface();
4048
49+ int newSurfaceMinimumWidth() const { return m_newSurfaceMinimumWidth; }
50+ void setNewSurfaceMinimumWidth(int value);
51+
52+ int newSurfaceMaximumWidth() const { return m_newSurfaceMaximumWidth; }
53+ void setNewSurfaceMaximumWidth(int value);
54+
55+ int newSurfaceMinimumHeight() const { return m_newSurfaceMinimumHeight; }
56+ void setNewSurfaceMinimumHeight(int value);
57+
58+ int newSurfaceMaximumHeight() const { return m_newSurfaceMaximumHeight; }
59+ void setNewSurfaceMaximumHeight(int value);
60+
61+ int newSurfaceWidthIncrement() const { return m_newSurfaceWidthIncrement; }
62+ void setNewSurfaceWidthIncrement(int);
63+
64+ int newSurfaceHeightIncrement() const { return m_newSurfaceHeightIncrement; }
65+ void setNewSurfaceHeightIncrement(int);
66+
4167Q_SIGNALS:
4268 void countChanged();
4369 void surfaceCreated(MirSurface *surface);
4470 void surfaceDestroyed(MirSurface*surface);
4571
72+ void newSurfaceMinimumWidthChanged(int value);
73+ void newSurfaceMaximumWidthChanged(int value);
74+ void newSurfaceMinimumHeightChanged(int value);
75+ void newSurfaceMaximumHeightChanged(int value);
76+ void newSurfaceWidthIncrementChanged(int value);
77+ void newSurfaceHeightIncrementChanged(int value);
78+
4679private:
4780 static SurfaceManager *the_surface_manager;
4881 VirtualKeyboard *m_virtualKeyboard;
82+
83+ int m_newSurfaceMinimumWidth{0};
84+ int m_newSurfaceMaximumWidth{0};
85+ int m_newSurfaceMinimumHeight{0};
86+ int m_newSurfaceMaximumHeight{0};
87+ int m_newSurfaceWidthIncrement{1};
88+ int m_newSurfaceHeightIncrement{1};
4989};
5090
5191#endif // SURFACEMANAGER_H
tests/mocks/Unity/Indicators/ModelActionRootState.qml+11−1
@@ -1,5 +1,5 @@
11/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013-2016 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU Lesser General Public License as published by
@@ -30,10 +30,20 @@ Item {
3030 property bool indicatorVisible: cachedState && cachedState.hasOwnProperty("visible") ? cachedState["visible"] : true
3131
3232 property var cachedState: menu ? menu.get(0, "actionState") : undefined
33+ property string submenuAction: {
34+ if (!menu) return "";
35+ var ext = menu.get(0, "ext");
36+ var submenuVar = ext ? ext["submenu-action"] : undefined;
37+ return submenuVar ? submenuVar : ""
38+ }
3339 Connections {
3440 target: menu
3541 onModelDataChanged: {
3642 cachedState = menu.get(0, "actionState");
43+
44+ var ext = menu.get(0, "ext");
45+ var submenuVar = ext ? ext["submenu-action"] : undefined;
46+ submenuAction = submenuVar ? submenuVar : ""
3747 }
3848 }
3949
tests/plugins/Dash/cardcreator/1.res+6−7
@@ -1,6 +1,5 @@
11AbstractButton {
22 id: root;
3− property var components;
43 property var cardData;
54 property string artShapeStyle: "inset";
65 property string backgroundShapeStyle: "inset";
@@ -24,13 +23,13 @@ Item {
2423 Loader {
2524 id: artShapeLoader;
2625 objectName: "artShapeLoader";
27− active: cardData && cardData["art"] || false;
26+ readonly property string cardArt: cardData && cardData["art"] || "";
27+ active: cardArt != "";
2828 asynchronous: root.asynchronous;
2929 visible: status == Loader.Ready;
3030 sourceComponent: Item {
3131 id: artShape;
3232 objectName: "artShape";
33− readonly property bool doShapeItem: components["art"]["conciergeMode"] !== true;
3433 visible: image.status == Image.Ready;
3534 readonly property alias image: artImage;
3635 ShaderEffectSource {
@@ -39,11 +38,11 @@ Item {
3938 anchors.centerIn: parent;
4039 width: 1;
4140 height: 1;
42− hideSource: doShapeItem;
41+ hideSource: true;
4342 }
4443 Loader {
4544 anchors.fill: parent;
46− visible: artShape.doShapeItem;
45+ visible: true;
4746 sourceComponent: root.artShapeStyle === "icon" ? artShapeIconComponent : artShapeShapeComponent;
4847 Component {
4948 id: artShapeShapeComponent;
@@ -67,7 +66,7 @@ Item {
6766 }
6867 }
6968 readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1;
70− readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1;
69+ readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : 1.6;
7170 Component.onCompleted: { updateWidthHeightBindings(); }
7271 Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
7372 function updateWidthHeightBindings() {
@@ -82,7 +81,7 @@ Item {
8281 CroppedImageMinimumSourceSize {
8382 id: artImage;
8483 objectName: "artImage";
85− source: cardData && cardData["art"] || "";
84+ source: artShapeLoader.cardArt;
8685 asynchronous: root.asynchronous;
8786 width: root.width;
8887 height: width / artShape.aspect;
tests/plugins/Dash/cardcreator/10.res−1
@@ -1,6 +1,5 @@
11AbstractButton {
22 id: root;
3− property var components;
43 property var cardData;
54 property string artShapeStyle: "inset";
65 property string backgroundShapeStyle: "inset";
tests/plugins/Dash/cardcreator/11.res+7−8
@@ -1,6 +1,5 @@
11AbstractButton {
22 id: root;
3− property var components;
43 property var cardData;
54 property string artShapeStyle: "inset";
65 property string backgroundShapeStyle: "inset";
@@ -62,13 +61,13 @@ Item {
6261 Loader {
6362 id: artShapeLoader;
6463 objectName: "artShapeLoader";
65− active: cardData && cardData["art"] || false;
64+ readonly property string cardArt: cardData && cardData["art"] || decodeURI("%5C");
65+ active: cardArt != "";
6666 asynchronous: root.asynchronous;
6767 visible: status == Loader.Ready;
6868 sourceComponent: Item {
6969 id: artShape;
7070 objectName: "artShape";
71− readonly property bool doShapeItem: components["art"]["conciergeMode"] !== true;
7271 visible: image.status == Image.Ready;
7372 readonly property alias image: artImage;
7473 ShaderEffectSource {
@@ -77,11 +76,11 @@ Item {
7776 anchors.centerIn: parent;
7877 width: 1;
7978 height: 1;
80− hideSource: doShapeItem;
79+ hideSource: true;
8180 }
8281 Loader {
8382 anchors.fill: parent;
84− visible: artShape.doShapeItem;
83+ visible: true;
8584 sourceComponent: root.artShapeStyle === "icon" ? artShapeIconComponent : artShapeShapeComponent;
8685 Component {
8786 id: artShapeShapeComponent;
@@ -105,7 +104,7 @@ Item {
105104 }
106105 }
107106 readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1;
108− readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1;
107+ readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : 1;
109108 Component.onCompleted: { updateWidthHeightBindings(); }
110109 Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
111110 function updateWidthHeightBindings() {
@@ -120,7 +119,7 @@ Item {
120119 CroppedImageMinimumSourceSize {
121120 id: artImage;
122121 objectName: "artImage";
123− source: cardData && cardData["art"] || "";
122+ source: artShapeLoader.cardArt;
124123 asynchronous: root.asynchronous;
125124 width: root.width;
126125 height: width / artShape.aspect;
@@ -148,7 +147,7 @@ left: parent.left;
148147 id: mascotImage;
149148 objectName: "mascotImage";
150149 anchors { verticalCenter: parent.verticalCenter; }
151− source: cardData && cardData["mascot"] || "";
150+ source: cardData && cardData["mascot"] || decodeURI("%22");
152151 width: units.gu(6);
153152 height: units.gu(5.625);
154153 horizontalAlignment: Image.AlignHCenter;
tests/plugins/Dash/cardcreator/2.res−1
@@ -1,6 +1,5 @@
11AbstractButton {
22 id: root;
3− property var components;
43 property var cardData;
54 property string artShapeStyle: "inset";
65 property string backgroundShapeStyle: "inset";
tests/plugins/Dash/cardcreator/3.res+6−7
@@ -1,6 +1,5 @@
11AbstractButton {
22 id: root;
3− property var components;
43 property var cardData;
54 property string artShapeStyle: "inset";
65 property string backgroundShapeStyle: "inset";
@@ -24,13 +23,13 @@ Item {
2423 Loader {
2524 id: artShapeLoader;
2625 objectName: "artShapeLoader";
27− active: cardData && cardData["art"] || false;
26+ readonly property string cardArt: cardData && cardData["art"] || decodeURI("IHAVE%5C%22ESCAPED%5C%22QUOTES%5C%22");
27+ active: cardArt != "";
2828 asynchronous: root.asynchronous;
2929 visible: status == Loader.Ready;
3030 sourceComponent: Item {
3131 id: artShape;
3232 objectName: "artShape";
33− readonly property bool doShapeItem: components["art"]["conciergeMode"] !== true;
3433 visible: image.status == Image.Ready;
3534 readonly property alias image: artImage;
3635 ShaderEffectSource {
@@ -39,11 +38,11 @@ Item {
3938 anchors.centerIn: parent;
4039 width: 1;
4140 height: 1;
42− hideSource: doShapeItem;
41+ hideSource: true;
4342 }
4443 Loader {
4544 anchors.fill: parent;
46− visible: artShape.doShapeItem;
45+ visible: true;
4746 sourceComponent: root.artShapeStyle === "icon" ? artShapeIconComponent : artShapeShapeComponent;
4847 Component {
4948 id: artShapeShapeComponent;
@@ -67,7 +66,7 @@ Item {
6766 }
6867 }
6968 readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1;
70− readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1;
69+ readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : 0.75;
7170 Component.onCompleted: { updateWidthHeightBindings(); }
7271 Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
7372 function updateWidthHeightBindings() {
@@ -82,7 +81,7 @@ Item {
8281 CroppedImageMinimumSourceSize {
8382 id: artImage;
8483 objectName: "artImage";
85− source: cardData && cardData["art"] || "";
84+ source: artShapeLoader.cardArt;
8685 asynchronous: root.asynchronous;
8786 width: root.width;
8887 height: width / artShape.aspect;
tests/plugins/Dash/cardcreator/4.res−1
@@ -1,6 +1,5 @@
11AbstractButton {
22 id: root;
3− property var components;
43 property var cardData;
54 property string artShapeStyle: "inset";
65 property string backgroundShapeStyle: "inset";
tests/plugins/Dash/cardcreator/5.res+6−7
@@ -1,6 +1,5 @@
11AbstractButton {
22 id: root;
3− property var components;
43 property var cardData;
54 property string artShapeStyle: "inset";
65 property string backgroundShapeStyle: "inset";
@@ -24,13 +23,13 @@ Item {
2423 Loader {
2524 id: artShapeLoader;
2625 objectName: "artShapeLoader";
27− active: cardData && cardData["art"] || false;
26+ readonly property string cardArt: cardData && cardData["art"] || "";
27+ active: cardArt != "";
2828 asynchronous: root.asynchronous;
2929 visible: status == Loader.Ready;
3030 sourceComponent: Item {
3131 id: artShape;
3232 objectName: "artShape";
33− readonly property bool doShapeItem: components["art"]["conciergeMode"] !== true;
3433 visible: image.status == Image.Ready;
3534 readonly property alias image: artImage;
3635 ShaderEffectSource {
@@ -39,11 +38,11 @@ Item {
3938 anchors.centerIn: parent;
4039 width: 1;
4140 height: 1;
42− hideSource: doShapeItem;
41+ hideSource: false;
4342 }
4443 Loader {
4544 anchors.fill: parent;
46− visible: artShape.doShapeItem;
45+ visible: false;
4746 sourceComponent: root.artShapeStyle === "icon" ? artShapeIconComponent : artShapeShapeComponent;
4847 Component {
4948 id: artShapeShapeComponent;
@@ -67,7 +66,7 @@ Item {
6766 }
6867 }
6968 readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1;
70− readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1;
69+ readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : 1;
7170 Component.onCompleted: { updateWidthHeightBindings(); }
7271 Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
7372 function updateWidthHeightBindings() {
@@ -82,7 +81,7 @@ Item {
8281 CroppedImageMinimumSourceSize {
8382 id: artImage;
8483 objectName: "artImage";
85− source: cardData && cardData["art"] || "";
84+ source: artShapeLoader.cardArt;
8685 asynchronous: root.asynchronous;
8786 width: root.width;
8887 height: width / artShape.aspect;
tests/plugins/Dash/cardcreator/5.tst+1−1
@@ -1,3 +1,3 @@
11template: {"non-interactive":true,"card-layout":"vertical","card-size":"medium","category-layout":"carousel","collapsed-rows":2,"overlay":true}
2−components: {"art":{"aspect-ratio":1,"field":"art"},"subtitle":{"field":"artist"},"title":{"field":"title"},"attributes":{}}
2+components: {"art":{"aspect-ratio":1,"field":"art","conciergeMode":true},"subtitle":{"field":"artist"},"title":{"field":"title"},"attributes":{}}
33result: 5.res
tests/plugins/Dash/cardcreator/6.res−1
@@ -1,6 +1,5 @@
11AbstractButton {
22 id: root;
3− property var components;
43 property var cardData;
54 property string artShapeStyle: "inset";
65 property string backgroundShapeStyle: "inset";
tests/plugins/Dash/cardcreator/7.res−1
@@ -1,6 +1,5 @@
11AbstractButton {
22 id: root;
3− property var components;
43 property var cardData;
54 property string artShapeStyle: "inset";
65 property string backgroundShapeStyle: "inset";
tests/plugins/Dash/cardcreator/8.res+1−2
@@ -1,7 +1,6 @@
11AbstractButton {
22 id: root;
3− property var components;
4− property var cardData;
3+ property var cardData;
54 property string artShapeStyle: "inset";
65 property string backgroundShapeStyle: "inset";
76 property real fontScale: 1.0;
tests/plugins/Dash/cardcreator/9.res+12−4
@@ -1,9 +1,8 @@
11AbstractButton {
22 id: root;
3− property var components;
43 property var cardData;
54 property string artShapeStyle: "inset";
6− property string backgroundShapeStyle: "inset";
5+ property string backgroundShapeStyle: "inset";
76 property real fontScale: 1.0;
87 property var scopeStyle: null;
98 property int titleAlignment: Text.AlignLeft;
@@ -80,11 +79,20 @@ CardAudioProgress {
8079 visible: parent.pressed;
8180 radius: "medium";
8281 }
82+ Rectangle {
83+ color: Qt.rgba(0, 0, 0, 0.5);
84+ anchors.centerIn: parent;
85+ width: parent.width * 0.5;
86+ height: width;
87+ radius: width / 2;
88+ }
8389 Icon {
84− anchors.fill: parent;
85− anchors.margins: parent.height > units.gu(5) ? units.gu(2) : 0;
90+ anchors.centerIn: parent;
91+ width: parent.width * 0.3;
92+ height: width;
8693 opacity: 0.9;
8794 name: DashAudioPlayer.playing && AudioUrlComparer.compare(parent.source, DashAudioPlayer.currentSource) ? "media-playback-pause" : "media-playback-start";
95+ color: "white";
8896 }
8997 onClicked: {
9098 if (AudioUrlComparer.compare(source, DashAudioPlayer.currentSource)) {
tests/plugins/Unity/Launcher/launchermodeltest.cpp+2
@@ -61,6 +61,8 @@ public:
6161 bool isTouchApp() const override { return true; }
6262 bool exemptFromLifecycle() const override { return false; }
6363 void setExemptFromLifecycle(bool) override {}
64+ QSize initialSurfaceSize() const override { return QSize(); }
65+ void setInitialSurfaceSize(const QSize &) override {}
6466
6567 // Methods used for mocking (not in the interface)
6668 void setFocused(bool focused) { m_focused = focused; Q_EMIT focusedChanged(focused); }
tests/qmltests/Components/tst_ZoomableImage.qml+8−5
@@ -73,7 +73,10 @@ Rectangle {
7373 tryCompareFunction(function() { return get_filename(imageRenderer.source.toString()) === get_filename(widgetData1["source"]); }, true);
7474 waitForRendering(zoomableImage);
7575 tryCompare(zoomableImage, "imageStatus", Image.Ready);
76− compare (signalSpy.count, 1);
76+ compare(signalSpy.count, 1);
77+ compare(imageRenderer.sourceSize.width, root.width);
78+ zoomableImage.zoomable = true;
79+ compare(imageRenderer.sourceSize.width, root.width*3);
7780 }
7881
7982 function get_filename(a) {
@@ -138,14 +141,14 @@ Rectangle {
138141 var imageRenderer = findChild(zoomableImage, "imageRenderer");
139142 var flickable = findChild(zoomableImage, "flickable");
140143
141− signalSpy.signalName = "onScaleChanged";
142− signalSpy.target = image;
143− signalSpy.clear();
144−
145144 zoomableImage.source = data.source;
146145 zoomableImage.zoomable = data.zoomable;
147146 waitForRendering(zoomableImage);
148147
148+ signalSpy.signalName = "onScaleChanged";
149+ signalSpy.target = image;
150+ signalSpy.clear();
151+
149152 tryCompare(zoomableImage, "imageStatus", Image.Ready);
150153 tryCompareFunction(function() { return get_filename(imageRenderer.source.toString()) === get_filename(data.source); }, true);
151154 waitForRendering(image);
tests/qmltests/Dash/Previews/tst_PreviewHeader.qml+15
@@ -54,6 +54,14 @@ Rectangle {
5454 "attributes": [{"value":"text1","icon":"image://theme/ok"},{"value":"text2","icon":"image://theme/cancel"}]
5555 }
5656
57+ property var emptyfallbackheaderjson: {
58+ "title": "THE TITLE",
59+ "subtitle": "Something catchy",
60+ "mascot": "",
61+ "fallback": "../graphics/play_button.png",
62+ "attributes": [{"value":"text1","icon":"image://theme/ok"},{"value":"text2","icon":"image://theme/cancel"}]
63+ }
64+
5765 PreviewHeader {
5866 id: previewHeader
5967 widgetData: headerjson
@@ -150,6 +158,13 @@ Rectangle {
150158 var mascot = findChild(previewHeader, "mascotShape");
151159 tryCompare(mascot, "visible", true);
152160 tryCompare(mascot.source, "status", Image.Ready);
161+
162+ previewHeader.widgetData = {};
163+ previewHeader.widgetData = emptyfallbackheaderjson;
164+ tryCompareFunction(function() { return findChild(previewHeader, "mascotShape") != null }, true);
165+ var mascot = findChild(previewHeader, "mascotShape");
166+ tryCompare(mascot, "visible", true);
167+ tryCompare(mascot.source, "status", Image.Ready);
153168 }
154169 }
155170}
tests/qmltests/Dash/Previews/tst_PreviewImageGallery.qml+19
@@ -48,6 +48,16 @@ Rectangle {
4848 , "fallback": "../../../tests/graphics/clock@18.png"
4949 }
5050
51+ property var sourcesModelEmptyWithFallback: {
52+ "sources": [
53+ "../../graphics/phone_background.jpg",
54+ "../../graphics/tablet_background.jpg",
55+ "../../../tests/graphics/clock@18.png",
56+ ""
57+ ]
58+ , "fallback": "../../../tests/graphics/clock@18.png"
59+ }
60+
5161 PreviewImageGallery {
5262 id: imageGallery
5363 width: parent.width
@@ -123,5 +133,14 @@ Rectangle {
123133 image3 = findChild(imageGallery, "previewImage3");
124134 tryCompare(image3, "state", "ready");
125135 }
136+
137+ function test_empty_fallback() {
138+ var image3 = findChild(imageGallery, "previewImage3");
139+ tryCompare(image3, "state", "error");
140+ imageGallery.widgetData = sourcesModel0;
141+ imageGallery.widgetData = sourcesModelEmptyWithFallback;
142+ image3 = findChild(imageGallery, "previewImage3");
143+ tryCompare(image3, "state", "ready");
144+ }
126145 }
127146}
tests/qmltests/Dash/Previews/tst_PreviewZoomableImage.qml+12
@@ -39,6 +39,11 @@ Rectangle {
3939 "fallback": "../../graphics/phone_background.jpg"
4040 }
4141
42+ property var widgetData3: {
43+ "source": "",
44+ "fallback": "../../graphics/phone_background.jpg"
45+ }
46+
4247 Loader {
4348 id: loader
4449 width: parent.width
@@ -100,5 +105,12 @@ Rectangle {
100105 waitForRendering(lazyImage);
101106 tryCompare(lazyImage, "state", "ready");
102107 }
108+
109+ function test_emptyfallback() {
110+ zoomableImage.widgetData = widgetData3;
111+ waitForRendering(zoomableImage);
112+ waitForRendering(lazyImage);
113+ tryCompare(lazyImage, "state", "ready");
114+ }
103115 }
104116}
tests/qmltests/Dash/tst_Card.qml+18−1
@@ -130,7 +130,6 @@ Rectangle {
130130 sourceComponent: cardTool.cardComponent
131131 clip: true
132132 onLoaded: {
133− item.components = Qt.binding(function() { return cardTool.components; });
134133 item.cardData = Qt.binding(function() { return Helpers.mapData(dataArea.text, cardTool.components, dataError); });
135134 item.width = Qt.binding(function() { return cardTool.cardWidth || item.implicitWidth; });
136135 item.height = Qt.binding(function() { return cardTool.cardHeight || item.implicitHeight; });
@@ -476,6 +475,15 @@ Rectangle {
476475 card.cardDataChanged();
477476 waitForRendering(card);
478477 tryCompare(art, "visible", true);
478+ compare(artImage.source, Qt.resolvedUrl("artwork/emblem.png"));
479+
480+ cardTool.components["art"]["fallback"] = Qt.resolvedUrl("artwork/checkers.png");
481+ cardTool.componentsChanged();
482+ card.cardData["art"] = "";
483+ card.cardDataChanged();
484+ waitForRendering(card);
485+ tryCompare(art, "visible", true);
486+ compare(artImage.source, Qt.resolvedUrl("artwork/checkers.png"));
479487
480488 card.cardData["mascot"] = "somethingbroken2";
481489 card.cardDataChanged();
@@ -487,6 +495,15 @@ Rectangle {
487495 card.cardDataChanged();
488496 waitForRendering(card);
489497 tryCompare(mascotImage, "status", Image.Ready);
498+ compare(mascotImage.source, Qt.resolvedUrl("artwork/emblem.png"));
499+
500+ cardTool.components["mascot"] = {"fallback": Qt.resolvedUrl("artwork/checkers.png")};
501+ cardTool.componentsChanged();
502+ card.cardData["mascot"] = "";
503+ card.cardDataChanged();
504+ waitForRendering(card);
505+ tryCompare(mascotImage, "status", Image.Ready);
506+ compare(mascotImage.source, Qt.resolvedUrl("artwork/checkers.png"));
490507 }
491508
492509 function test_font_weights_data() {
tests/qmltests/Dash/tst_CardBenchmark.qml−2
@@ -61,8 +61,6 @@ Rectangle {
6161 item.width = Qt.binding(function() { return cardTool.cardWidth || implicitWidth; });
6262 item.height = Qt.binding(function() { return cardTool.cardHeight || implicitHeight; });
6363 item.cardData = Qt.binding(function() { return Helpers.mapData(root.cardData, cardTool.components); });
64− item.template = Qt.binding(function() { return cardTool.template; });
65− item.components = Qt.binding(function() { return cardTool.components; });
6664 }
6765 }
6866 }
tests/qmltests/Greeter/tst_WideView.qml+11
@@ -522,5 +522,16 @@ Item {
522522
523523 tryCompare(loginList, "height", view.height);
524524 }
525+
526+ function test_alphanumeric() {
527+ var passwordInput = findChild(view, "passwordInput");
528+
529+ verify(view.alphanumeric);
530+ compare(passwordInput.inputMethodHints, Qt.ImhNone);
531+ view.alphanumeric = false;
532+ compare(passwordInput.inputMethodHints, Qt.ImhDigitsOnly);
533+ view.alphanumeric = true;
534+ compare(passwordInput.inputMethodHints, Qt.ImhNone);
535+ }
525536 }
526537}
tests/qmltests/Stages/RecursingChildSessionControl.qml+3−3
@@ -88,7 +88,7 @@ ColumnLayout {
8888
8989 CheckBox {
9090 id: _surfaceCheckbox;
91− checked: false;
91+ checked: root.session.lastSurface ? true : false
9292 activeFocusOnPress: false
9393 enabled: root.session
9494 onCheckedChanged: {
@@ -101,7 +101,7 @@ ColumnLayout {
101101
102102 Connections {
103103 target: root.session ? root.session : null
104− onSurfaceChanged: {
104+ onLastSurfaceChanged: {
105105 surfaceCheckbox.checked = root.session.lastSurface !== null
106106 }
107107 }
@@ -137,7 +137,7 @@ ColumnLayout {
137137 text: "Add Child"
138138 onClicked: {
139139 var screenshot = Math.round(Math.random()*100 % (screenshotIds.length-1));
140− var session = ApplicationTest.addChildSession(root.session, root.screenshotIds[screenshot]);
140+ var session = ApplicationTest.addChildSession(root.session, root.screenshotIds[screenshot], true);
141141 }
142142 }
143143 }
tests/qmltests/Stages/SizeHintField.qmlnew file+33
@@ -0,0 +1,33 @@
1+/*
2+ * Copyright 2015 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+import QtQuick.Layouts 1.1
19+import Ubuntu.Components 1.3
20+
21+Row {
22+ property alias text: label.text
23+ property alias value: field.text
24+ spacing: units.gu(1)
25+ Label {
26+ id: label
27+ color: "black"; anchors.verticalCenter: parent.verticalCenter
28+ }
29+ TextField {
30+ id: field
31+ text: ""; width: units.gu(8); hasClearButton: false; maximumLength: 6; inputMask: "d00000"
32+ }
33+}
tests/qmltests/Stages/SurfaceManagerControls.qmlnew file+33
@@ -0,0 +1,33 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+import Ubuntu.Components 1.3
19+import Unity.Application 0.1
20+
21+Column {
22+ id: root
23+ property color textColor: "black"
24+
25+ Label {text: "Size hints for new surface:"; color: root.textColor; font.bold: true}
26+
27+ SurfaceManagerField { text: "min width"; propertyName: "newSurfaceMinimumWidth"; textColor: root.textColor }
28+ SurfaceManagerField { text: "max width"; propertyName: "newSurfaceMaximumWidth"; textColor: root.textColor }
29+ SurfaceManagerField { text: "min height"; propertyName: "newSurfaceMinimumHeight"; textColor: root.textColor }
30+ SurfaceManagerField { text: "max height"; propertyName: "newSurfaceMaximumHeight"; textColor: root.textColor }
31+ SurfaceManagerField { text: "width increment"; propertyName: "newSurfaceWidthIncrement"; textColor: root.textColor }
32+ SurfaceManagerField { text: "height increment"; propertyName: "newSurfaceHeightIncrement"; textColor: root.textColor }
33+}
tests/qmltests/Stages/SurfaceManagerField.qmlnew file+40
@@ -0,0 +1,40 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+import Ubuntu.Components 1.3
19+import Unity.Application 0.1
20+
21+Row {
22+ id: root
23+ property alias textColor: label.color
24+ property alias text: label.text
25+ property string propertyName
26+
27+ Label {id: label; anchors.verticalCenter: parent.verticalCenter}
28+ TextField {
29+ width: units.gu(10); hasClearButton: false; maximumLength: 6
30+ inputMask: "d00000"
31+ text: ""
32+ onTextChanged: {
33+ if (text.length > 0) {
34+ SurfaceManager[root.propertyName] = parseInt(text);
35+ } else {
36+ SurfaceManager[root.propertyName] = 0;
37+ }
38+ }
39+ }
40+}
tests/qmltests/Stages/tst_DesktopStage.qml+30−24
@@ -89,36 +89,42 @@ Item {
8989 right: parent.right
9090 }
9191
92− Column {
93− anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
94− spacing: units.gu(1)
95−
96− Button {
97− color: "white"
98− text: "Make surface slow to resize"
99− activeFocusOnPress: false
100− onClicked: {
101− if (ApplicationManager.focusedApplicationId) {
102− var surface = ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).session.lastSurface;
103− surface.slowToResize = true;
92+ Flickable {
93+ anchors.fill: parent
94+ contentHeight: controlsColumn.height
95+ Column {
96+ id: controlsColumn
97+ spacing: units.gu(1)
98+
99+ Button {
100+ color: "white"
101+ text: "Make surface slow to resize"
102+ activeFocusOnPress: false
103+ onClicked: {
104+ if (ApplicationManager.focusedApplicationId) {
105+ var surface = ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).session.lastSurface;
106+ surface.slowToResize = true;
107+ }
104108 }
105109 }
106− }
107110
108− EdgeBarrierControls {
109− id: edgeBarrierControls
110− text: "Drag here to pull out spread"
111− backgroundColor: "blue"
112− onDragged: { desktopStageLoader.item.pushRightEdge(amount); }
113− }
111+ EdgeBarrierControls {
112+ id: edgeBarrierControls
113+ text: "Drag here to pull out spread"
114+ backgroundColor: "blue"
115+ onDragged: { desktopStageLoader.item.pushRightEdge(amount); }
116+ }
114117
115− Divider {}
118+ Divider {}
116119
117− Repeater {
118− model: ApplicationManager.availableApplications
119− ApplicationCheckBox {
120− appId: modelData
120+ Repeater {
121+ model: ApplicationManager.availableApplications
122+ ApplicationCheckBox {
123+ appId: modelData
124+ }
121125 }
126+
127+ SurfaceManagerControls { textColor: "white" }
122128 }
123129 }
124130 }
tests/qmltests/Stages/tst_SessionContainer.qml+27−5
@@ -155,7 +155,8 @@ Rectangle {
155155 var sessions = [];
156156 for (i = 0; i < data.count; i++) {
157157 var session = ApplicationTest.addChildSession(sessionContainer.session,
158− "gallery");
158+ "gallery",
159+ true);
159160 session.createSurface();
160161 sessionContainer.session.addChildSession(session);
161162 compare(sessionContainer.childSessions.count(), i+1);
@@ -180,7 +181,7 @@ Rectangle {
180181 // 3 sessions should cover all edge cases
181182 for(i = 0; i < 3; i++) {
182183 var a_session = ApplicationTest.addChildSession(
183− sessionContainer.session, "gallery"
184+ sessionContainer.session, "gallery", true
184185 )
185186
186187 a_session.createSurface();
@@ -226,7 +227,8 @@ Rectangle {
226227 var container = sessionContainer;
227228 for (i = 0; i < data.depth; i++) {
228229 var session = ApplicationTest.addChildSession(lastSession,
229− "gallery");
230+ "gallery",
231+ true);
230232 session.createSurface();
231233 lastSession.addChildSession(session);
232234 compare(container.childSessions.count(), 1);
@@ -250,7 +252,8 @@ Rectangle {
250252 var sessionContainer = sessionContainerLoader.item;
251253
252254 var session = ApplicationTest.addChildSession(sessionContainer.session,
253− "gallery");
255+ "gallery",
256+ true);
254257 session.createSurface();
255258 sessionContainer.session.addChildSession(session);
256259
@@ -288,7 +291,8 @@ Rectangle {
288291 var sessionContainer = sessionContainerLoader.item;
289292
290293 var session = ApplicationTest.addChildSession(sessionContainer.session,
291− "gallery");
294+ "gallery",
295+ true);
292296
293297 var delegate = findChild(sessionContainer, "childDelegate0");
294298 var childContainer = findChild(delegate, "sessionContainer");
@@ -304,6 +308,24 @@ Rectangle {
304308 tryCompareFunction(function() { return isContainerAnimating(childContainer); }, true);
305309 // wait for animation to end
306310 tryCompareFunction(function() { return isContainerAnimating(childContainer); }, false);
311+
312+ tryCompareFunction(function() { return sessionContainer.childSessions.count() }, 0);
313+ }
314+
315+ function test_childSessionsRemovedOnStopWhenNotLive() {
316+ sessionCheckbox.checked = true;
317+ var sessionContainer = sessionContainerLoader.item;
318+
319+ var session = ApplicationTest.addChildSession(sessionContainer.session,
320+ "gallery",
321+ false);
322+
323+ var delegate = findChild(sessionContainer, "childDelegate0");
324+ var childContainer = findChild(delegate, "sessionContainer");
325+
326+ ApplicationTest.removeSession(session);
327+
328+ tryCompareFunction(function() { return sessionContainer.childSessions.count() }, 0);
307329 }
308330 }
309331}
tests/qmltests/Stages/tst_WindowResizeArea.qml+225−30
@@ -28,7 +28,7 @@ import Unity.Application 0.1
2828Item {
2929 id: root
3030 height: units.gu(60)
31− width: units.gu(60)
31+ width: units.gu(85)
3232
3333 Binding {
3434 target: PanelState
@@ -41,14 +41,20 @@ Item {
4141
4242 Item {
4343 id: fakeWindow
44− property alias minWidth: windowResizeArea.minWidth
45− property alias minHeight: windowResizeArea.minHeight
44+ property alias resizeAreaMinWidth: windowResizeArea.minWidth
45+ property alias resizeAreaMinHeight: windowResizeArea.minHeight
4646 x: units.gu(20)
4747 y: units.gu(20)
4848 width: requestedWidth
4949 height: requestedHeight
5050 property real requestedWidth
5151 property real requestedHeight
52+ property real minimumWidth: 0
53+ property real minimumHeight: 0
54+ property real maximumWidth: 0
55+ property real maximumHeight: 0
56+ property real widthIncrement: 0
57+ property real heightIncrement: 0
5258 state: "normal"
5359
5460 function maximize() {
@@ -80,6 +86,10 @@ Item {
8086 anchors.fill: parent
8187 hoverEnabled: true
8288 }
89+ Text {
90+ text: parent.width + "x" + parent.height
91+ color: "white"
92+ }
8393 }
8494 }
8595 }
@@ -90,27 +100,107 @@ Item {
90100 active: windowLoaderCheckbox.checked
91101 }
92102
93− Column {
94− MouseTouchEmulationCheckbox {
95− checked: false
96− color: "black"
97− }
98− RowLayout {
99− Layout.fillWidth: true
100− CheckBox {
101− id: windowLoaderCheckbox
102− checked: true
103− activeFocusOnPress: false
103+ Rectangle {
104+ anchors.right: parent.right
105+ anchors.top: parent.top
106+ anchors.bottom: parent.bottom
107+ width: units.gu(25)
108+ color: "lightgrey"
109+ Column {
110+ width: parent.width
111+ spacing: units.gu(1)
112+ MouseTouchEmulationCheckbox {
113+ checked: false
114+ color: "black"
115+ }
116+ RowLayout {
117+ Layout.fillWidth: true
118+ CheckBox {
119+ id: windowLoaderCheckbox
120+ checked: true
121+ activeFocusOnPress: false
122+ }
123+ Label {
124+ color: "black"
125+ text: "Window loader active"
126+ anchors.verticalCenter: parent.verticalCenter
127+ }
104128 }
105129 Label {
106− id: label
130+ color: "black"; font.bold: true
131+ text: "Size Hints:"
132+ }
133+ SizeHintField { id: minWidthText; text: "min width" }
134+ SizeHintField { id: maxWidthText; text: "max width" }
135+ SizeHintField { id: minHeightText; text: "min height" }
136+ SizeHintField { id: maxHeightText; text: "max height" }
137+ SizeHintField { id: widthIncrText; text: "width incr" }
138+ SizeHintField { id: heightIncrText; text: "height incr" }
139+ Button {
107140 color: "black"
108− text: "Window loader active"
109− anchors.verticalCenter: parent.verticalCenter
141+ anchors.horizontalCenter: parent.horizontalCenter
142+ text: "Apply"
143+ onClicked: {
144+ var value = parseInt(minWidthText.value);
145+ if (isNaN(value)) {
146+ windowLoader.item.minimumWidth = 0;
147+ } else {
148+ windowLoader.item.resizeAreaMinWidth = 1; // get it out of the way
149+ windowLoader.item.minimumWidth = value;
150+ }
151+
152+ value = parseInt(maxWidthText.value);
153+ if (isNaN(value)) {
154+ windowLoader.item.maximumWidth = 0;
155+ } else {
156+ windowLoader.item.maximumWidth = value;
157+ }
158+
159+ value = parseInt(minHeightText.value);
160+ if (isNaN(value)) {
161+ windowLoader.item.minimumHeight = 0;
162+ } else {
163+ windowLoader.item.resizeAreaMinHeight = 1; // get it out of the way
164+ windowLoader.item.minimumHeight = value;
165+ }
166+
167+ value = parseInt(maxHeightText.value);
168+ if (isNaN(value)) {
169+ windowLoader.item.maximumHeight = 0;
170+ } else {
171+ windowLoader.item.maximumHeight = value;
172+ }
173+
174+ value = parseInt(widthIncrText.value);
175+ if (isNaN(value)) {
176+ windowLoader.item.widthIncrement = 0;
177+ } else {
178+ windowLoader.item.widthIncrement = value;
179+ }
180+
181+ value = parseInt(heightIncrText.value);
182+ if (isNaN(value)) {
183+ windowLoader.item.heightIncrement = 0;
184+ } else {
185+ windowLoader.item.heightIncrement = value;
186+ }
187+ }
110188 }
111189 }
112190 }
113191
192+ SignalSpy {
193+ id: windowRequestedWidthSpy
194+ target: windowLoader.item
195+ signalName: "onRequestedWidthChanged"
196+ }
197+
198+ SignalSpy {
199+ id: windowRequestedHeightSpy
200+ target: windowLoader.item
201+ signalName: "onRequestedHeightChanged"
202+ }
203+
114204 UnityTestCase {
115205 name: "WindowResizeArea"
116206 when: windowShown
@@ -122,6 +212,18 @@ Item {
122212 fakeWindow.y = units.gu(20)
123213 fakeWindow.requestedWidth = units.gu(20)
124214 fakeWindow.requestedHeight = units.gu(20)
215+ fakeWindow.resizeAreaMinWidth = units.gu(15);
216+ fakeWindow.resizeAreaMinHeight = units.gu(10);
217+ fakeWindow.minimumWidth = 0;
218+ fakeWindow.minimumHeight = 0;
219+ fakeWindow.maximumWidth = 0;
220+ fakeWindow.maximumHeight = 0;
221+ fakeWindow.widthIncrement = 0;
222+ fakeWindow.heightIncrement = 0;
223+
224+ // Our test window resizes instantly
225+ compare(fakeWindow.width, fakeWindow.requestedWidth);
226+ compare(fakeWindow.height, fakeWindow.requestedHeight);
125227 }
126228
127229 function test_resizeWindowRightBottom_data() {
@@ -143,8 +245,8 @@ Item {
143245 var startDragY = initialWindowY + initialWindowHeight + 1
144246 mouseFlick(root, startDragX, startDragY, startDragX + data.dx, startDragY + data.dy, true, true, units.gu(.5), 10);
145247
146− tryCompare(fakeWindow, "width", Math.max(initialWindowWidth + data.dx, fakeWindow.minWidth));
147− tryCompare(fakeWindow, "height", Math.max(initialWindowHeight + data.dy, fakeWindow.minHeight));
248+ tryCompare(fakeWindow, "width", Math.max(initialWindowWidth + data.dx, fakeWindow.resizeAreaMinWidth));
249+ tryCompare(fakeWindow, "height", Math.max(initialWindowHeight + data.dy, fakeWindow.resizeAreaMinHeight));
148250
149251 compare(fakeWindow.x, initialWindowX);
150252 compare(fakeWindow.y, initialWindowY);
@@ -169,11 +271,11 @@ Item {
169271 var startDragY = initialWindowY - 1
170272 mouseFlick(root, startDragX, startDragY, startDragX + data.dx, startDragY + data.dy, true, true, units.gu(.5), 10);
171273
172− tryCompare(fakeWindow, "width", Math.max(initialWindowWidth - data.dx, fakeWindow.minWidth));
173− tryCompare(fakeWindow, "height", Math.max(initialWindowHeight - data.dy, fakeWindow.minHeight));
274+ tryCompare(fakeWindow, "width", Math.max(initialWindowWidth - data.dx, fakeWindow.resizeAreaMinWidth));
275+ tryCompare(fakeWindow, "height", Math.max(initialWindowHeight - data.dy, fakeWindow.resizeAreaMinHeight));
174276
175− var maxMoveX = initialWindowWidth - fakeWindow.minWidth;
176− var maxMoveY = initialWindowHeight - fakeWindow.minHeight;
277+ var maxMoveX = initialWindowWidth - fakeWindow.resizeAreaMinWidth;
278+ var maxMoveY = initialWindowHeight - fakeWindow.resizeAreaMinHeight;
177279 compare(fakeWindow.x, Math.min(initialWindowX + data.dx, initialWindowX + maxMoveX));
178280 compare(fakeWindow.y, Math.min(initialWindowY + data.dy, initialWindowY + maxMoveY));
179281 }
@@ -189,8 +291,8 @@ Item {
189291 var startDragY = initialWindowY + initialWindowHeight + 1
190292 mouseFlick(root, startDragX, startDragY, startDragX + resizeDelta, startDragY + resizeDelta, true, true, units.gu(.5), 10);
191293
192− tryCompare(fakeWindow, "width", Math.max(initialWindowWidth + resizeDelta, fakeWindow.minWidth));
193− tryCompare(fakeWindow, "height", Math.max(initialWindowHeight + resizeDelta, fakeWindow.minHeight));
294+ tryCompare(fakeWindow, "width", Math.max(initialWindowWidth + resizeDelta, fakeWindow.resizeAreaMinWidth));
295+ tryCompare(fakeWindow, "height", Math.max(initialWindowHeight + resizeDelta, fakeWindow.resizeAreaMinHeight));
194296
195297 // This will destroy the window and recreate it
196298 windowLoader.active = false;
@@ -198,8 +300,8 @@ Item {
198300 windowLoader.active = true;
199301
200302 // Make sure its size is again the same as before
201− tryCompare(fakeWindow, "width", Math.max(initialWindowWidth + resizeDelta, fakeWindow.minWidth));
202− tryCompare(fakeWindow, "height", Math.max(initialWindowHeight + resizeDelta, fakeWindow.minHeight));
303+ tryCompare(fakeWindow, "width", Math.max(initialWindowWidth + resizeDelta, fakeWindow.resizeAreaMinWidth));
304+ tryCompare(fakeWindow, "height", Math.max(initialWindowHeight + resizeDelta, fakeWindow.resizeAreaMinHeight));
203305 }
204306
205307 // This tests if dragging smaller than minSize and then larger again, will keep the edge sticking
@@ -220,8 +322,8 @@ Item {
220322 var startDragX = initialWindowX + data.startX
221323 var startDragY = initialWindowY + data.startY
222324 mouseFlick(root, startDragX, startDragY, startDragX + data.dx, startDragY + data.dy, true, false, units.gu(.05), 10);
223− tryCompare(fakeWindow, "width", Math.max(initialWindowWidth - Math.abs(data.dx), fakeWindow.minWidth));
224− tryCompare(fakeWindow, "height", Math.max(initialWindowHeight - Math.abs(data.dy), fakeWindow.minHeight));
325+ tryCompare(fakeWindow, "width", Math.max(initialWindowWidth - Math.abs(data.dx), fakeWindow.resizeAreaMinWidth));
326+ tryCompare(fakeWindow, "height", Math.max(initialWindowHeight - Math.abs(data.dy), fakeWindow.resizeAreaMinHeight));
225327 mouseFlick(root, startDragX + data.dx, startDragY + data.dy, startDragX, startDragY, false, true, units.gu(.05), 10);
226328 tryCompare(fakeWindow, "width", initialWindowWidth);
227329 tryCompare(fakeWindow, "height", initialWindowHeight);
@@ -257,7 +359,6 @@ Item {
257359 fakeWindow.state = "normal"
258360 }
259361
260−
261362 function test_restoreMovesIntoBounds_data() {
262363 return [
263364 {tag: "left off", x: -units.gu(5), y: units.gu(5), w: units.gu(10), h: units.gu(10)},
@@ -290,5 +391,99 @@ Item {
290391
291392 waitForRendering(root)
292393 }
394+
395+ /*
396+ Tests that even though you dragged the window bottom right corner continously (ie,
397+ through multiple intermediate steps), its (requested) width and height changed only
398+ once. And, furthermore, its dimensions increased by a multiple of its size (width
399+ and height) increment value.
400+ */
401+ function test_sizeIncrement() {
402+ var initialWindowX = fakeWindow.x;
403+ var initialWindowY = fakeWindow.y;
404+ var initialWindowWidth = fakeWindow.width
405+ var initialWindowHeight = fakeWindow.height
406+
407+ fakeWindow.widthIncrement = 70;
408+ fakeWindow.heightIncrement = 70;
409+
410+ var startDragX = initialWindowX + initialWindowWidth + 1
411+ var startDragY = initialWindowY + initialWindowHeight + 1
412+ var deltaX = 100;
413+ var deltaY = 100;
414+
415+ windowRequestedWidthSpy.clear();
416+ verify(windowRequestedWidthSpy.valid);
417+
418+ windowRequestedHeightSpy.clear();
419+ verify(windowRequestedHeightSpy.valid);
420+
421+ mouseFlick(root, startDragX, startDragY, startDragX + deltaX, startDragY + deltaY, true, true, units.gu(.5), 10);
422+
423+ compare(windowRequestedWidthSpy.count, 1);
424+ compare(fakeWindow.width, initialWindowWidth + fakeWindow.widthIncrement);
425+ compare(windowRequestedHeightSpy.count, 1);
426+ compare(fakeWindow.height, initialWindowHeight + fakeWindow.heightIncrement);
427+ }
428+
429+ /*
430+ Tests that when dragging a window border you cannot make it bigger than its maximum size
431+ */
432+ function test_maximumSize() {
433+ fakeWindow.x = units.gu(1);
434+ fakeWindow.y = units.gu(1);
435+ fakeWindow.resizeAreaMinWidth = 1; // so it does not interfere with anything
436+ fakeWindow.resizeAreaMinHeight = 1; // so it does not interfere with anything
437+ fakeWindow.requestedWidth = units.gu(10);
438+ fakeWindow.requestedHeight = units.gu(10);
439+
440+ fakeWindow.maximumWidth = units.gu(20);
441+ fakeWindow.maximumHeight = units.gu(20);
442+
443+ // Our test window resizes instantly
444+ compare(fakeWindow.width, fakeWindow.requestedWidth);
445+ compare(fakeWindow.height, fakeWindow.requestedHeight);
446+
447+ var startDragX = fakeWindow.width + 1
448+ var startDragY = fakeWindow.height + 1
449+ var endDragX = (fakeWindow.maximumWidth * 2) + 1;
450+ var endDragY = (fakeWindow.maximumHeight * 2) + 1;
451+
452+ mouseFlick(fakeWindow, startDragX, startDragY, endDragX, endDragY,
453+ true/*pressMouse*/, true/*releaseMouse*/, units.gu(.5)/*speed*/, 20/*iterations*/);
454+
455+ compare(fakeWindow.requestedWidth, fakeWindow.maximumWidth);
456+ compare(fakeWindow.requestedHeight, fakeWindow.maximumHeight);
457+ }
458+
459+ /*
460+ Tests that when dragging a window border you cannot make it smaller than its minimum size
461+ */
462+ function test_minimumSize() {
463+ fakeWindow.x = units.gu(1);
464+ fakeWindow.y = units.gu(1);
465+ fakeWindow.resizeAreaMinWidth = 1; // so it does not interfere with anything
466+ fakeWindow.resizeAreaMinHeight = 1; // so it does not interfere with anything
467+ fakeWindow.requestedWidth = units.gu(20);
468+ fakeWindow.requestedHeight = units.gu(20);
469+
470+ fakeWindow.minimumWidth = units.gu(10);
471+ fakeWindow.minimumHeight = units.gu(10);
472+
473+ // Our test window resizes instantly
474+ compare(fakeWindow.width, fakeWindow.requestedWidth);
475+ compare(fakeWindow.height, fakeWindow.requestedHeight);
476+
477+ var startDragX = fakeWindow.width + 1
478+ var startDragY = fakeWindow.height + 1
479+ var endDragX = (fakeWindow.minimumWidth * 0.5) + 1;
480+ var endDragY = (fakeWindow.minimumHeight * 0.5) + 1;
481+
482+ mouseFlick(fakeWindow, startDragX, startDragY, endDragX, endDragY,
483+ true/*pressMouse*/, true/*releaseMouse*/, units.gu(.5)/*speed*/, 20/*iterations*/);
484+
485+ compare(fakeWindow.requestedWidth, fakeWindow.minimumWidth);
486+ compare(fakeWindow.requestedHeight, fakeWindow.minimumHeight);
487+ }
293488 }
294489}
tests/qmltests/tst_Shell.qml+53
@@ -49,6 +49,11 @@ Rectangle {
4949 shellLoader.active = true;
5050 }
5151
52+ MouseArea {
53+ id: clickThroughCatcher
54+ anchors.fill: shellContainer
55+ }
56+
5257 Item {
5358 id: shellContainer
5459 anchors.left: root.left
@@ -291,6 +296,12 @@ Rectangle {
291296 signalName: "applicationRemoved"
292297 }
293298
299+ SignalSpy {
300+ id: clickThroughSpy
301+ target: clickThroughCatcher
302+ signalName: "clicked"
303+ }
304+
294305 Telephony.CallEntry {
295306 id: phoneCall
296307 phoneNumber: "+447812221111"
@@ -1977,5 +1988,47 @@ Rectangle {
19771988 compare(ApplicationManager.findApplication("libreoffice") === null, true);
19781989 }
19791990 }
1991+
1992+ function test_inputEventsOnEdgesEndUpInAppSurface_data() {
1993+ return [
1994+ { tag: "phone", repeaterName: "spreadRepeater" },
1995+ { tag: "tablet", repeaterName: "spreadRepeater" },
1996+ { tag: "desktop", repeaterName: "appRepeater" },
1997+ ]
1998+ }
1999+
2000+ function test_inputEventsOnEdgesEndUpInAppSurface(data) {
2001+ loadShell(data.tag);
2002+ shell.usageScenario = data.tag;
2003+ waitForRendering(shell);
2004+ swipeAwayGreeter();
2005+
2006+ // Let's open a fullscreen app
2007+ var app = ApplicationManager.startApplication("camera-app");
2008+ waitUntilAppWindowIsFullyLoaded(app);
2009+
2010+ var appRepeater = findChild(shell, data.repeaterName);
2011+ var topmostAppDelegate = appRepeater.itemAt(0);
2012+ verify(topmostAppDelegate);
2013+
2014+ var topmostSurfaceItem = findChild(topmostAppDelegate, "surfaceItem");
2015+ verify(topmostSurfaceItem);
2016+
2017+ mouseClick(shell, 1, shell.height / 2);
2018+ compare(topmostSurfaceItem.mousePressCount, 1);
2019+ compare(topmostSurfaceItem.mouseReleaseCount, 1);
2020+
2021+ mouseClick(shell, shell.width - 1, shell.height / 2);
2022+ compare(topmostSurfaceItem.mousePressCount, 2);
2023+ compare(topmostSurfaceItem.mouseReleaseCount, 2);
2024+
2025+ tap(shell, 1, shell.height / 2);
2026+ compare(topmostSurfaceItem.touchPressCount, 1);
2027+ compare(topmostSurfaceItem.touchReleaseCount, 1);
2028+
2029+ tap(shell, shell.width - 1, shell.height / 2);
2030+ compare(topmostSurfaceItem.touchPressCount, 2);
2031+ compare(topmostSurfaceItem.touchReleaseCount, 2);
2032+ }
19802033 }
19812034}