on xenial help sign in
Merge from trunk
commit5acb4e1d81d7cd628684e1a511f4d2e74df1f474 authorMichael Terry <michael.terry@canonical.com> dateThu 7 Aug 2014, 11:30 (12 years ago) parent1ce88f8d816a46
133 files changed+2839−1680
CMakeLists.txt+1 cmake/modules/QmlTest.cmake+2−2 data/CMakeLists.txt+5−2 data/unity8-dash.conf+22 data/unity8-dash.desktop.in+9 data/unity8.conf+1−7 debian/changelog+71 debian/control+2−2 debian/unity8.install+3 plugins/Dash/CardCreator.js+26−12 plugins/Dash/ScopeStyle.qml+11−8 plugins/Unity/CMakeLists.txt+1 plugins/Unity/DashCommunicator/CMakeLists.txt+19 plugins/Unity/DashCommunicator/dashcommunicator.cpp+43 plugins/Unity/DashCommunicator/dashcommunicator.h+35 plugins/Unity/DashCommunicator/dashcommunicatorservice.cpp+30 plugins/Unity/DashCommunicator/dashcommunicatorservice.h+38 plugins/Unity/DashCommunicator/dbusdashcommunicatorservice.cpp+39 plugins/Unity/DashCommunicator/dbusdashcommunicatorservice.h+39 plugins/Unity/DashCommunicator/plugin.cpp+31 plugins/Unity/DashCommunicator/plugin.h+34 plugins/Unity/DashCommunicator/qmldir+3 plugins/Unity/Launcher/launchermodel.cpp+5 po/am.po+10−10 po/el.po+6−6 po/es.po+5−5 po/eu.po+7−6 po/hu.po+4−4 po/ja.po+11−11 po/pt_BR.po+11−11 po/sl.po+4−4 qml/Components/EdgeDemo.qml+15−16 qml/Components/PageHeader.qml+25−16 qml/Components/SeeMore.qml−70 qml/Components/graphics/pagination_dot_off@30.png qml/Components/graphics/pagination_dot_on@30.png qml/Dash/CardTool.qml+1−1 qml/Dash/Dash.qml+92−11 qml/Dash/DashApplication.qml+36 qml/Dash/DashContent.qml+7−4 qml/Dash/DashDepartments.qml → qml/Dash/DashNavigation.qml+50−43 qml/Dash/DashDepartmentsList.qml → qml/Dash/DashNavigationList.qml+14−18 qml/Dash/GenericScopeView.qml+12−12 qml/Dash/PreviewListView.qml+23−27 qml/Dash/Previews/PreviewExpandable.qml+101 qml/Dash/Previews/PreviewImageGallery.qml+1 qml/Dash/Previews/PreviewTextSummary.qml+3−16 qml/Dash/Previews/PreviewWidget.qml+3 qml/Dash/Previews/PreviewWidgetFactory.qml+5 qml/Dash/ScopeListView.qml−5 qml/Dash/graphics/phone/screenshots/unity8-dash@12.png qml/Greeter/Greeter.qml+1−1 qml/Launcher/Launcher.qml+2−3 qml/Notifications/NotificationMenuItemFactory.qml+3 qml/Panel/Indicators/MenuItemFactory.qml+74 qml/Panel/Indicators/ModemInfoItem.qml+111 qml/Panel/Indicators/RoamingIndication.qml+40 qml/ScopeTool.qml+2−11 qml/Shell.qml+28−153 qml/Stages/PhoneStage.qml+59−26 qml/Stages/SpreadDelegate.qml+6 qml/Stages/TabletStage.qml+85−41 qml/Stages/TransformedSpreadDelegate.qml+6−5 qml/Stages/TransformedTabletSpreadDelegate.qml+28 run_on_device.sh+1−3 src/CMakeLists.txt+1 src/Dash/CMakeLists.txt+13 src/Dash/main.cpp+96 tests/autopilot/unity8/application_lifecycle/tests/test_application_lifecycle.py+1−1 tests/autopilot/unity8/application_lifecycle/tests/test_url_dispatcher.py+3−2 tests/autopilot/unity8/fixture_setup.py+64 tests/autopilot/unity8/process_helpers.py+89−20 tests/autopilot/unity8/shell/emulators/dash.py+22−9 tests/autopilot/unity8/shell/emulators/greeter.py+4−1 tests/autopilot/unity8/shell/emulators/main_window.py+34−20 tests/autopilot/unity8/shell/tests/__init__.py+141−48 tests/autopilot/unity8/shell/tests/test_emulators.py+7−18 tests/autopilot/unity8/shell/tests/test_lock_screen.py+7−47 tests/autopilot/unity8/shell/tests/test_notifications.py+12−7 tests/autopilot/unity8/shell/tests/test_upstart.py+2−2 tests/mocks/Unity/Application/ApplicationInfo.cpp−62 tests/mocks/Unity/Application/ApplicationInfo.h−10 tests/mocks/Unity/Application/ApplicationManager.cpp+17−147 tests/mocks/Unity/Application/ApplicationManager.h−16 tests/mocks/Unity/Application/ApplicationScreenshotProvider.cpp+1−1 tests/mocks/Unity/CMakeLists.txt+4−3 tests/mocks/Unity/DashCommunicator/CMakeLists.txt+18 tests/mocks/Unity/DashCommunicator/dashcommunicator.cpp+37 tests/mocks/Unity/DashCommunicator/dashcommunicator.h+39 tests/mocks/Unity/DashCommunicator/dashcommunicatorservice.cpp+33 tests/mocks/Unity/DashCommunicator/dashcommunicatorservice.h+36 tests/mocks/Unity/DashCommunicator/plugin.cpp+31 tests/mocks/Unity/DashCommunicator/plugin.h+34 tests/mocks/Unity/DashCommunicator/qmldir+3 tests/mocks/Unity/Unity.qmltypes+79−27 tests/mocks/Unity/fake_department.cpp → tests/mocks/Unity/fake_navigation.cpp+32−40 tests/mocks/Unity/fake_department.h → tests/mocks/Unity/fake_navigation.h+13−13 tests/mocks/Unity/fake_scope.cpp+65−17 tests/mocks/Unity/fake_scope.h+16−8 tests/mocks/Unity/fake_scopes.cpp+10−2 tests/mocks/Unity/fake_scopes.h+1 tests/mocks/Unity/fake_unity_plugin.cpp+2−2 tests/plugins/Dash/cardcreator/1.res+12−10 tests/plugins/Dash/cardcreator/1.tst+1−1 tests/plugins/Dash/cardcreator/2.res+8 tests/plugins/Dash/cardcreator/2.tst+1−1 tests/plugins/Dash/cardcreator/3.res+12−10 tests/plugins/Dash/cardcreator/3.tst+1−1 tests/plugins/Dash/cardcreator/4.res+8 tests/plugins/Dash/cardcreator/4.tst+1−1 tests/plugins/Dash/cardcreator/5.res+14−12 tests/plugins/Dash/cardcreator/5.tst+1−1 tests/plugins/Dash/cardcreator/6.res+10−8 tests/plugins/Dash/cardcreator/6.tst+1−1 tests/plugins/Dash/cardcreator/7.res+9−1 tests/plugins/Dash/cardcreator/7.tst+1−1 tests/plugins/Dash/cardcreatortest.cpp+13−4 tests/plugins/Dash/tst_ScopeStyle.qml+14−7 tests/qmltests/CMakeLists.txt+1−1 tests/qmltests/Components/tst_PageHeader.qml+21−23 tests/qmltests/Components/tst_SeeMore.qml−52 tests/qmltests/Dash/CardHelpers.js+4−3 tests/qmltests/Dash/Previews/tst_PreviewExpandable.qml+97 tests/qmltests/Dash/Previews/tst_PreviewTextSummary.qml+4−18 tests/qmltests/Dash/Previews/tst_PreviewWidgetFactory.qml+1 tests/qmltests/Dash/tst_Card.qml+15−3 tests/qmltests/Dash/tst_Dash.qml+47−15 tests/qmltests/Dash/tst_DashContent.qml+94−95 tests/qmltests/Dash/tst_GenericScopeView.qml+3−2 tests/qmltests/Panel/Indicators/tst_MenuItemFactory.qml+47 tests/qmltests/Stages/tst_PhoneStage.qml+3−2 tests/qmltests/tst_Shell.qml+62−275 tests/qmltests/tst_ShellWithPin.qml+4−37
CMakeLists.txt+1
@@ -96,6 +96,7 @@ include(autopilot)
9696declare_autopilot_test(shell unity8.shell ${CMAKE_SOURCE_DIR}/tests/autopilot/)
9797
9898set(SHELL_APP unity8)
99+set(DASH_APP unity8-dash)
99100set(SCOPE_TOOL unity-scope-tool)
100101
101102include_directories(
cmake/modules/QmlTest.cmake+2−2
@@ -135,7 +135,7 @@ macro(add_binary_qml_test CLASS_NAME LD_PATH DEPS)
135135 set(testCommand
136136 LD_LIBRARY_PATH=${LD_PATH}
137137 ${CMAKE_CURRENT_BINARY_DIR}/${CLASS_NAME}TestExec
138− -o ${CMAKE_BINARY_DIR}/${CLASS_NAME}Test.xml,xunitxml
138+ -o ${CMAKE_BINARY_DIR}/test${CLASS_NAME}.xml,xunitxml
139139 -o -,txt)
140140
141141 add_qmltest_target(test${CLASS_NAME} "${testCommand}" FALSE TRUE)
@@ -151,7 +151,7 @@ macro(add_binary_qml_test CLASS_NAME LD_PATH DEPS)
151151 LD_LIBRARY_PATH=${LD_PATH}
152152 xvfb-run --server-args "-screen 0 1024x768x24" --auto-servernum
153153 ${CMAKE_CURRENT_BINARY_DIR}/${CLASS_NAME}TestExec
154− -o ${CMAKE_BINARY_DIR}/${CLASS_NAME}Test.xml,xunitxml
154+ -o ${CMAKE_BINARY_DIR}/test${CLASS_NAME}.xml,xunitxml
155155 -o -,txt)
156156
157157 add_qmltest_target(xvfbtest${CLASS_NAME} "${xvfbtestCommand}" FALSE TRUE)
data/CMakeLists.txt+5−2
@@ -1,7 +1,10 @@
1−# generate desktop file
1+# generate desktop files
22configure_file(${SHELL_APP}.desktop.in ${CMAKE_CURRENT_BINARY_DIR}/${SHELL_APP}.desktop @ONLY)
3+configure_file(${DASH_APP}.desktop.in ${CMAKE_CURRENT_BINARY_DIR}/${DASH_APP}.desktop @ONLY)
34
45# install desktop files
5−install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${SHELL_APP}.desktop
6+install(FILES
7+ ${CMAKE_CURRENT_BINARY_DIR}/${SHELL_APP}.desktop
8+ ${CMAKE_CURRENT_BINARY_DIR}/${DASH_APP}.desktop
69 DESTINATION ${CMAKE_INSTALL_DATADIR}/applications
710 )
data/unity8-dash.confnew file+22
@@ -0,0 +1,22 @@
1+description "Unity Shell v8 Dash"
2+author "Michael Zanetti <michael.zanetti@canonical.com>"
3+
4+emits scope-ui-starting
5+
6+start on started unity8
7+stop on stopping unity8
8+
9+kill timeout 120
10+
11+respawn
12+
13+pre-start script
14+ if [ -z "$UNITY_SCOPES_LIST" ]; then
15+ # FIXME: remove once we have this in dconf
16+ initctl set-env UNITY_SCOPES_LIST="scopes;clickscope;musicaggregator;videoaggregator"
17+ fi
18+
19+ initctl emit scope-ui-starting
20+end script
21+
22+exec ${BINARY:-unity8-dash} $ARGS --desktop_file_hint=/usr/share/applications/unity8-dash.desktop
data/unity8-dash.desktop.innew file+9
@@ -0,0 +1,9 @@
1+[Desktop Entry]
2+Type=Application
3+Name=Unity 8 Dash
4+Comment=The converged Unity shell dash
5+Exec=@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_BINDIR@/unity8-dash
6+Terminal=false
7+Icon=unity8
8+NoDisplay=true
9+X-Ubuntu-Touch=true
data/unity8.conf+1−7
@@ -1,7 +1,7 @@
11description "Unity Shell v8"
22author "Ricardo Mendoza <ricmm@ubuntu.com>"
33
4−emits scope-ui-starting indicator-services-start
4+emits indicator-services-start
55
66start on ((xsession SESSION=ubuntu-touch) or (xsession SESSION=ubuntu-touch-surfaceflinger)) and started dbus
77stop on desktop-end
@@ -34,11 +34,6 @@ pre-start script
3434 gdbus call --session --dest org.freedesktop.DBus --object-path /org/freedesktop/DBus --method org.freedesktop.DBus.UpdateActivationEnvironment "@a{ss} {'MIR_SOCKET': '$MIR_SERVER_FILE'}"
3535 fi
3636
37− if [ -z "$UNITY_SCOPES_LIST" ]; then
38− # FIXME: remove once we have this in dconf
39− initctl set-env UNITY_SCOPES_LIST="scopes;clickscope;musicaggregator;videoaggregator"
40− fi
41−
4237 # Remove the socket if still there
4338 if [ -S "$MIR_SERVER_FILE" ]; then
4439 rm "$MIR_SERVER_FILE"
@@ -46,7 +41,6 @@ pre-start script
4641
4742 initctl set-env --global MIR_SERVER_PROMPT_FILE=1
4843
49− initctl emit scope-ui-starting
5044 initctl emit --no-wait indicator-services-start
5145end script
5246
debian/changelog+71
@@ -1,3 +1,74 @@
1+unity8 (8.00+14.10.20140806.1-0ubuntu1) utopic; urgency=low
2+
3+ [ Michal Hruby ]
4+ * Work with the scopes-v4 branch + departments->navigation renaming
5+
6+ [ Michał Sawicz ]
7+ * Hardcode art shape size for click scope local and predefined
8+ categories While at it, drop the fillmode of cards
9+ * Use the correct API in PageHeader. (LP: #1353048)
10+ * Refactor dash activity indicator. (LP: #1351539)
11+
12+ [ Albert Astals ]
13+ * Dash Departments fixes Update maxHeight manually since it depends on
14+ the position of the item and its parents and it can't know when the
15+ binding has to be updated Make parent stuff non interactive when the
16+ department list is shown
17+ * PageHeader: when on search clip y-coordinates otherwise the
18+ background spills out when on search (LP: #1350398)
19+ * Dash: Implement OverlayColor support in Cards
20+ * Hardcode art shape size for click scope local and predefined
21+ categories While at it, drop the fillmode of cards
22+ * Make test_departments test more stable There's various
23+ DashDepartments on the dash, make sure we're working over the one
24+ that is on screen, otherwise clicks don't end up where they should
25+ * Work with the scopes-v4 branch + departments->navigation renaming
26+ * Fixes for dash as app Load i18n catalog Process command line options
27+ Add the posibility to have a mouse touch adaptor (LP: #1353351)
28+ * Implement the Expandable Preview Widget Now TextSummary is not
29+ expandable by itself anymore, you have to use it inside an
30+ Expandable to get the behaviour
31+ * Add test prefix to xml output, seems CI needs it
32+
33+ [ Antti Kaijanmäki ]
34+ * Indicators: Adds new ModemInfoItem to be used with indicator-network
35+ (LP: #1329204)
36+
37+ [ Michael Terry ]
38+ * When the edge demo is running, don't show the greeter if the screen
39+ is turned off. This avoids an odd interaction where parts of the
40+ greeter are disabled but the edge demo isn't visible until you slide
41+ the greeter away. (LP: #1283425)
42+ * Don't hardcode the phablet password in our testing script.
43+
44+ [ Ying-Chun Liu ]
45+ * Add divider dots.
46+
47+ [ Mirco Müller ]
48+ * Make sure the TextField of a snap-decision notification has the
49+ active focus upon creation, thus the osk shows up right away. (LP:
50+ #1346867)
51+
52+ [ Andrea Cimitan ]
53+ * Add touchdown effect to dash cards.
54+ * Import Ubuntu.Components for preview image gallery to pick up
55+ default flicking speeds.
56+
57+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 06 Aug 2014 19:40:05 +0000
58+
59+unity8 (8.00+14.10.20140805-0ubuntu1) utopic; urgency=low
60+
61+ [ Michael Zanetti ]
62+ * Split the dash from the shell into a separate app (LP: #1232687)
63+
64+ [ Leo Arias ]
65+ * Update the autopilot tests to work with the new dash app.
66+
67+ [ Daniel d'Andrada ]
68+ * Split the dash from the shell into a separate app (LP: #1232687)
69+
70+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 05 Aug 2014 12:06:31 +0000
71+
172unity8 (8.00+14.10.20140731.1-0ubuntu1) utopic; urgency=low
273
374 [ Gerry Boland ]
debian/control+2−2
@@ -24,7 +24,7 @@ Build-Depends: cmake,
2424 libpulse-dev,
2525 libqmenumodel-dev (>= 0.2.8),
2626 libqt5xmlpatterns5-dev,
27− libunity-api-dev (>= 7.87),
27+ libunity-api-dev (>= 7.88),
2828 libusermetricsoutput1-dev,
2929 libxcb1-dev,
3030 pkg-config,
@@ -112,7 +112,7 @@ Depends: qtdeclarative5-ubuntu-thumbnailer0.1 | ubuntu-thumbnailer-impl,
112112 unity-application-impl-2,
113113 unity-notifications-impl-3,
114114 unity-plugin-scopes | unity-scopes-impl,
115− unity-scopes-impl-2,
115+ unity-scopes-impl-4,
116116 unity8-fake-env | unity-application-impl,
117117 ${misc:Depends},
118118Breaks: unity8 (<< 7.86),
debian/unity8.install+3
@@ -1,6 +1,9 @@
11data/unity8.conf usr/share/upstart/sessions/
2+data/unity8-dash.conf usr/share/upstart/sessions/
23usr/bin/unity8
4+usr/bin/unity8-dash
35usr/share/applications/unity8.desktop
6+usr/share/applications/unity8-dash.desktop
47usr/share/unity8/Greeter
58usr/share/unity8/Hud
69usr/share/unity8/Launcher
plugins/Dash/CardCreator.js+26−12
@@ -71,19 +71,14 @@ var kArtShapeHolderCode = 'Item { \n\
7171 visible: image.status == Image.Ready; \n\
7272 readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1; \n\
7373 readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1; \n\
74− readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect; \n\
7574 Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; } \n\
76− onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings(); \n\
7775 Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); } \n\
7876 function updateWidthHeightBindings() { \n\
7977 if (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) { \n\
8078 width = root.fixedArtShapeSize.width; \n\
8179 height = root.fixedArtShapeSize.height; \n\
82− } else if (aspectSmallerThanImageAspect) { \n\
83− width = Qt.binding(function() { return !visible ? 0 : image.width }); \n\
84− height = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.height : width / image.aspect }); \n\
8580 } else { \n\
86− width = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.width : height * image.aspect }); \n\
81+ width = Qt.binding(function() { return !visible ? 0 : image.width }); \n\
8782 height = Qt.binding(function() { return !visible ? 0 : image.height }); \n\
8883 } \n\
8984 } \n\
@@ -92,8 +87,7 @@ var kArtShapeHolderCode = 'Item { \n\
9287 source: cardData && cardData["art"] || ""; \n\
9388 cache: true; \n\
9489 asynchronous: root.asynchronous; \n\
95− fillMode: components && components["art"]["fill-mode"] === "fit" ? Image.PreserveAspectFit: Image.PreserveAspectCrop; \n\
96− readonly property real aspect: implicitWidth / implicitHeight; \n\
90+ fillMode: Image.PreserveAspectCrop; \n\
9791 width: %2; \n\
9892 height: %3; \n\
9993 } \n\
@@ -114,7 +108,7 @@ var kOverlayLoaderCode = 'Loader { \n\
114108 sourceComponent: ShaderEffect { \n\
115109 id: overlay; \n\
116110 height: (fixedHeaderHeight > 0 ? fixedHeaderHeight : headerHeight) + units.gu(2); \n\
117− opacity: 0.6; \n\
111+ property color overlayColor: cardData && cardData["overlayColor"] || "#99000000"; \n\
118112 property var source: ShaderEffectSource { \n\
119113 id: shaderSource; \n\
120114 sourceItem: artShapeLoader.item; \n\
@@ -135,9 +129,10 @@ var kOverlayLoaderCode = 'Loader { \n\
135129 varying highp vec2 coord; \n\
136130 uniform sampler2D source; \n\
137131 uniform lowp float qt_Opacity; \n\
132+ uniform highp vec4 overlayColor; \n\
138133 void main() { \n\
139134 lowp vec4 tex = texture2D(source, coord); \n\
140− gl_FragColor = vec4(0, 0, 0, tex.a) * qt_Opacity; \n\
135+ gl_FragColor = vec4(overlayColor.r, overlayColor.g, overlayColor.b, 1) * qt_Opacity * overlayColor.a * tex.a; \n\
141136 }"; \n\
142137 } \n\
143138 }\n';
@@ -257,6 +252,16 @@ var kTitleLabelCode = 'Label { \n\
257252 horizontalAlignment: root.headerAlignment; \n\
258253 }\n';
259254
255+// %1 is used as anchors of touchdown effect
256+var kTouchdownCode = 'UbuntuShape { \n\
257+ id: touchdown; \n\
258+ objectName: "touchdown"; \n\
259+ anchors { %1 } \n\
260+ visible: root.pressed; \n\
261+ radius: "medium"; \n\
262+ borderSource: "radius_pressed.sci" \n\
263+ }\n';
264+
260265// %1 is used as anchors of subtitleLabel
261266// %2 is used as color of subtitleLabel
262267var kSubtitleLabelCode = 'Label { \n\
@@ -279,7 +284,7 @@ var kAttributesRowCode = 'CardAttributes { \n\
279284 objectName: "attributesRow"; \n\
280285 anchors { %1 } \n\
281286 color: %2; \n\
282− model: cardData && cardData["attributes"] || undefined; \n\
287+ model: cardData["attributes"]; \n\
283288 }\n';
284289
285290// %1 is used as top anchor of summary
@@ -569,6 +574,16 @@ function cardString(template, components) {
569574 code += kSummaryLabelCode.arg(summaryTopAnchor).arg(summaryTopMargin).arg(color);
570575 }
571576
577+ var touchdownAnchors;
578+ if (hasBackground) {
579+ touchdownAnchors = 'fill: backgroundLoader';
580+ } else if (hasArt && !hasMascot && !hasSummary) {
581+ touchdownAnchors = 'fill: artShapeHolder';
582+ } else {
583+ touchdownAnchors = 'fill: root'
584+ }
585+ code += kTouchdownCode.arg(touchdownAnchors);
586+
572587 if (hasSummary) {
573588 code += 'implicitHeight: summary.y + summary.height + (summary.text ? units.gu(1) : 0);\n';
574589 } else if (hasHeaderRow) {
@@ -593,7 +608,6 @@ function cardString(template, components) {
593608function createCardComponent(parent, template, components) {
594609 var imports = 'import QtQuick 2.2; \n\
595610 import Ubuntu.Components 0.1; \n\
596− import Ubuntu.Thumbnailer 0.1;\n\
597611 import Dash 0.1;\n';
598612 var card = cardString(template, components);
599613 var code = imports + 'Component {\n' + card + '}\n';
plugins/Dash/ScopeStyle.qml+11−8
@@ -38,14 +38,20 @@ QtObject {
3838 /// Color used for text and symbolic icons
3939 readonly property color foreground: "foreground-color" in style ? style["foreground-color"] : d.defaultDark
4040
41+ /// Luminance of the foreground color
42+ readonly property real foregroundLuminance: foreground ? luminance(foreground) : d.defaultForegroundLuminance
43+
4144 /// Color used for the overall background
4245 readonly property color background: "background-color" in style ? style["background-color"] : "transparent"
4346
47+ /// Luminance of the foreground color
48+ readonly property real backgroundLuminance: background ? luminance(background) : d.defaultBackgroundLuminance
49+
4450 /*! \brief Luminance threshold for switching between fore and background color
4551
4652 \note If background colour is not fully opaque, it's not taken into account.
4753 */
48− readonly property real threshold: background.a !== 1.0 ? d.foregroundLuminance : (d.foregroundLuminance + d.backgroundLuminance) / 2
54+ readonly property real threshold: background.a !== 1.0 ? foregroundLuminance : (foregroundLuminance + backgroundLuminance) / 2
4955
5056 /*! \brief The lighter of foreground and background colors
5157
@@ -53,8 +59,8 @@ QtObject {
5359 and defaults to the theme-provided light color.
5460 */
5561 readonly property color light: {
56− if (background.a !== 1.0) return d.foregroundLuminance > d.defaultLightLuminance ? foreground : d.defaultLight;
57− return d.foregroundLuminance > d.backgroundLuminance ? foreground : background;
62+ if (background.a !== 1.0) return foregroundLuminance > d.defaultLightLuminance ? foreground : d.defaultLight;
63+ return foregroundLuminance > backgroundLuminance ? foreground : background;
5864 }
5965
6066 /*! \brief The darker of foreground and background colors
@@ -63,8 +69,8 @@ QtObject {
6369 and defaults to the theme-provided dark color.
6470 */
6571 readonly property color dark: {
66− if (background.a !== 1.0) return d.foregroundLuminance < d.defaultDarkLuminance ? foreground : d.defaultDark;
67− return d.foregroundLuminance < d.backgroundLuminance ? foreground : background;
72+ if (background.a !== 1.0) return foregroundLuminance < d.defaultDarkLuminance ? foreground : d.defaultDark;
73+ return foregroundLuminance < backgroundLuminance ? foreground : background;
6874 }
6975
7076 /// Source of the logo image for the header
@@ -78,9 +84,6 @@ QtObject {
7884
7985 //! @cond
8086 property var d: QtObject {
81− readonly property real foregroundLuminance: luminance(foreground)
82− readonly property real backgroundLuminance: luminance(background)
83−
8487 // FIXME: should be taken from the theme
8588 readonly property color defaultLight: "white"
8689 readonly property color defaultDark: "grey"
plugins/Unity/CMakeLists.txt+1
@@ -1,3 +1,4 @@
11add_subdirectory(Indicators)
22add_subdirectory(Launcher)
33add_subdirectory(Session)
4+add_subdirectory(DashCommunicator)
plugins/Unity/DashCommunicator/CMakeLists.txtnew file+19
@@ -0,0 +1,19 @@
1+include_directories(
2+ ${CMAKE_CURRENT_SOURCE_DIR}
3+)
4+
5+set(QMLDASHCOMMUNICATORPLUGIN_SRC
6+ plugin.cpp
7+ dbusdashcommunicatorservice.cpp
8+ dashcommunicatorservice.cpp
9+ dashcommunicator.cpp
10+ )
11+
12+add_library(DashCommunicator-qml MODULE
13+ ${QMLDASHCOMMUNICATORPLUGIN_SRC}
14+ )
15+
16+qt5_use_modules(DashCommunicator-qml DBus Qml)
17+
18+# export the qmldir and qmltypes files
19+add_unity8_plugin(Unity.DashCommunicator 0.1 Unity/DashCommunicator TARGETS DashCommunicator-qml)
plugins/Unity/DashCommunicator/dashcommunicator.cppnew file+43
@@ -0,0 +1,43 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it under
5+ * the terms of the GNU Lesser General Public License version 3, as published by
6+ * the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but WITHOUT
9+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11+ * Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "dashcommunicator.h"
18+
19+#include <QObject>
20+#include <QDBusConnection>
21+#include <QDBusInterface>
22+#include <QDebug>
23+
24+DashCommunicator::DashCommunicator(QObject *parent):
25+ QObject(parent)
26+{
27+}
28+
29+DashCommunicator::~DashCommunicator()
30+{
31+}
32+
33+
34+void DashCommunicator::setCurrentScope(const QString &scopeId, bool animate, bool reset)
35+{
36+ QDBusConnection connection = QDBusConnection::sessionBus();
37+ QDBusInterface dashIface ("com.canonical.UnityDash",
38+ "/com/canonical/UnityDash",
39+ "",
40+ connection);
41+
42+ dashIface.call("SetCurrentScope", scopeId, animate, reset);
43+}
plugins/Unity/DashCommunicator/dashcommunicator.hnew file+35
@@ -0,0 +1,35 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it under
5+ * the terms of the GNU Lesser General Public License version 3, as published by
6+ * the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but WITHOUT
9+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11+ * Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef DASHCOMMUNICATOR_H
18+#define DASHCOMMUNICATOR_H
19+
20+#include <QObject>
21+
22+class DashCommunicator: public QObject
23+{
24+ Q_OBJECT
25+ Q_CLASSINFO("D-Bus Interface", "com.canonical.Unity.DashCommunicator")
26+
27+public:
28+ DashCommunicator(QObject *parent = 0);
29+ ~DashCommunicator();
30+
31+public Q_SLOTS:
32+ void setCurrentScope(const QString &scopeId, bool animate, bool reset);
33+};
34+
35+#endif
plugins/Unity/DashCommunicator/dashcommunicatorservice.cppnew file+30
@@ -0,0 +1,30 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it under
5+ * the terms of the GNU Lesser General Public License version 3, as published by
6+ * the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but WITHOUT
9+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11+ * Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "dashcommunicatorservice.h"
18+
19+DashCommunicatorService::DashCommunicatorService(QObject *parent):
20+ QObject(parent),
21+ m_dbusService(new DBusDashCommunicatorService(this))
22+{
23+ connect(m_dbusService, &DBusDashCommunicatorService::setCurrentScopeRequested, this, &DashCommunicatorService::setCurrentScopeRequested);
24+}
25+
26+
27+DashCommunicatorService::~DashCommunicatorService()
28+{
29+
30+}
plugins/Unity/DashCommunicator/dashcommunicatorservice.hnew file+38
@@ -0,0 +1,38 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it under
5+ * the terms of the GNU Lesser General Public License version 3, as published by
6+ * the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but WITHOUT
9+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11+ * Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef DASHCOMMUNICATORSERVICE_H
18+#define DASHCOMMUNICATORSERVICE_H
19+
20+#include "dbusdashcommunicatorservice.h"
21+
22+#include <QObject>
23+
24+class DashCommunicatorService: public QObject
25+{
26+ Q_OBJECT
27+public:
28+ DashCommunicatorService(QObject *parent = 0);
29+ ~DashCommunicatorService();
30+
31+Q_SIGNALS:
32+ void setCurrentScopeRequested(const QString &scopeId, bool animate, bool reset);
33+
34+private:
35+ DBusDashCommunicatorService *m_dbusService;
36+};
37+
38+#endif // DBUSUNITYSESSIONSERVICE_H
plugins/Unity/DashCommunicator/dbusdashcommunicatorservice.cppnew file+39
@@ -0,0 +1,39 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it under
5+ * the terms of the GNU Lesser General Public License version 3, as published by
6+ * the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but WITHOUT
9+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11+ * Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "dbusdashcommunicatorservice.h"
18+
19+#include <QDBusConnection>
20+#include <QDBusInterface>
21+#include <QDebug>
22+
23+DBusDashCommunicatorService::DBusDashCommunicatorService(QObject *parent):
24+ QObject(parent)
25+{
26+ QDBusConnection connection = QDBusConnection::sessionBus();
27+
28+ connection.registerService("com.canonical.UnityDash");
29+ connection.registerObject("/com/canonical/UnityDash", this, QDBusConnection::ExportScriptableSlots);
30+}
31+
32+DBusDashCommunicatorService::~DBusDashCommunicatorService()
33+{
34+}
35+
36+void DBusDashCommunicatorService::SetCurrentScope(const QString &scopeId, bool animate, bool reset)
37+{
38+ Q_EMIT setCurrentScopeRequested(scopeId, animate, reset);
39+}
plugins/Unity/DashCommunicator/dbusdashcommunicatorservice.hnew file+39
@@ -0,0 +1,39 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it under
5+ * the terms of the GNU Lesser General Public License version 3, as published by
6+ * the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but WITHOUT
9+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11+ * Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef DBUSDASHCOMMUNICATORSERVICE_H
18+#define DBUSDASHCOMMUNICATORSERVICE_H
19+
20+#include <QObject>
21+
22+class DBusDashCommunicatorService: public QObject
23+{
24+ Q_OBJECT
25+ Q_CLASSINFO("D-Bus Interface", "com.canonical.Unity.DashCommunicator")
26+
27+public:
28+ DBusDashCommunicatorService(QObject *parent = 0);
29+ ~DBusDashCommunicatorService();
30+
31+Q_SIGNALS:
32+ void setCurrentScopeRequested(const QString &scopeId, bool animate, bool reset);
33+
34+public Q_SLOTS:
35+ Q_SCRIPTABLE void SetCurrentScope(const QString &scopeId, bool animate, bool reset);
36+
37+};
38+
39+#endif // DBUSUNITYSESSIONSERVICE_H
plugins/Unity/DashCommunicator/plugin.cppnew file+31
@@ -0,0 +1,31 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors: Michael Zanetti <michael.zanetti@canonical.com>
17+ */
18+
19+#include "plugin.h"
20+#include "dashcommunicator.h"
21+#include "dashcommunicatorservice.h"
22+
23+#include <QDBusConnection>
24+#include <QtQml/qqml.h>
25+
26+void DashCommunicatorPlugin::registerTypes(const char *uri)
27+{
28+ Q_ASSERT(uri == QStringLiteral("Unity.DashCommunicator"));
29+ qmlRegisterType<DashCommunicatorService>(uri, 0, 1, "DashCommunicatorService");
30+ qmlRegisterType<DashCommunicator>(uri, 0, 1, "DashCommunicator");
31+}
plugins/Unity/DashCommunicator/plugin.hnew file+34
@@ -0,0 +1,34 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
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+ * Authors: Michael Zanetti <michael.zanetti@canonical.com>
17+ */
18+
19+#ifndef DASHCOMMUNICATOR_PLUGIN_H
20+#define DASHCOMMUNICATOR_PLUGIN_H
21+
22+#include <QtQml/QQmlEngine>
23+#include <QtQml/QQmlExtensionPlugin>
24+
25+class DashCommunicatorPlugin : public QQmlExtensionPlugin
26+{
27+ Q_OBJECT
28+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
29+
30+public:
31+ void registerTypes(const char *uri);
32+};
33+
34+#endif
plugins/Unity/DashCommunicator/qmldirnew file+3
@@ -0,0 +1,3 @@
1+module Unity.DashCommunicator
2+plugin DashCommunicator-qml
3+typeinfo DashCommunicator.qmltypes
plugins/Unity/Launcher/launchermodel.cpp+5
@@ -317,6 +317,11 @@ void LauncherModel::applicationAdded(const QModelIndex &parent, int row)
317317 return;
318318 }
319319
320+ if (app->appId() == "unity8-dash") {
321+ // Not adding the dash app
322+ return;
323+ }
324+
320325 bool found = false;
321326 Q_FOREACH(LauncherItem *item, m_list) {
322327 if (app->appId() == item->appId()) {
po/am.po+10−10
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2014-07-11 14:59+0200\n"
11−"PO-Revision-Date: 2014-07-27 01:25+0000\n"
11+"PO-Revision-Date: 2014-08-04 16:30+0000\n"
1212"Last-Translator: samson <Unknown>\n"
1313"Language-Team: Amharic <am@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-07-28 06:44+0000\n"
18−"X-Generator: Launchpad (build 17131)\n"
17+"X-Launchpad-Export-Date: 2014-08-05 06:45+0000\n"
18+"X-Generator: Launchpad (build 17147)\n"
1919
2020#: plugins/Unity/Launcher/launcheritem.cpp:43
2121#: plugins/Unity/Launcher/launcheritem.cpp:73
@@ -36,7 +36,7 @@ msgstr "የቀኝ ጠርዝ"
3636
3737#: qml/Components/EdgeDemo.qml:114
3838msgid "Try swiping from the right edge to unlock the phone"
39−msgstr ""
39+msgstr "ስልኩን ለመክፈት ከ ቀኝ ጠርዝ በኩል ወደ ግራ ይጥረጉ"
4040
4141#: qml/Components/EdgeDemo.qml:145
4242msgid "Top edge"
@@ -44,7 +44,7 @@ msgstr "የላይ ጠርዝ"
4444
4545#: qml/Components/EdgeDemo.qml:146
4646msgid "Try swiping from the top edge to access the indicators"
47−msgstr ""
47+msgstr "ጠቋሚዎች ጋር ለመድረስ ከ ላይ ጠርዝ በኩል ወደ ታች ይጥረጉ"
4848
4949#: qml/Components/EdgeDemo.qml:171
5050msgid "Close"
@@ -52,7 +52,7 @@ msgstr "መዝጊያ"
5252
5353#: qml/Components/EdgeDemo.qml:172
5454msgid "Swipe up again to close the settings screen"
55−msgstr ""
55+msgstr "የማሰናጃውን መመልከቻ ለመዝጋት እንደገና ወደ ላይ ይጥረጉ"
5656
5757#: qml/Components/EdgeDemo.qml:199
5858msgid "Left edge"
@@ -60,11 +60,11 @@ msgstr "የግራ ጠርዝ"
6060
6161#: qml/Components/EdgeDemo.qml:200
6262msgid "Swipe from the left to reveal the launcher for quick access to apps"
63−msgstr ""
63+msgstr "ከ ግራ በኩል ወደ ቀኝ ይጥረጉ በፍጥነት መተግበሪያዎች ጋር ለመድረስ እና ለማስጀመር"
6464
6565#: qml/Components/EdgeDemo.qml:227
6666msgid "Well done"
67−msgstr ""
67+msgstr "ጥሩ አድረገዋል"
6868
6969#: qml/Components/EdgeDemo.qml:228
7070msgid ""
@@ -123,7 +123,7 @@ msgstr "አነስተኛ..."
123123
124124#: qml/Dash/Previews/PreviewRatingInput.qml:81
125125msgid "Rate this"
126−msgstr ""
126+msgstr "ለዚህ ደረጃ ይስጡ"
127127
128128#: qml/Dash/Previews/PreviewRatingInput.qml:126
129129msgid "Add a review"
@@ -135,7 +135,7 @@ msgstr "መላኪያ"
135135
136136#: qml/Greeter/Greeter.qml:157
137137msgid "Swipe to unlock"
138−msgstr ""
138+msgstr "ለመክፈት ይጥረጉ"
139139
140140#: qml/Hud/HudParametrizedActionsPage.qml:132
141141msgid "Confirm"
po/el.po+6−6
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2014-07-11 14:59+0200\n"
11−"PO-Revision-Date: 2014-07-28 16:06+0000\n"
12−"Last-Translator: Chris Taklis (aka Kailor) <ctaklis@gmail.com>\n"
11+"PO-Revision-Date: 2014-08-04 18:44+0000\n"
12+"Last-Translator: madvinegar <madvinegar@hotmail.com>\n"
1313"Language-Team: Greek <el@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-07-29 06:54+0000\n"
18−"X-Generator: Launchpad (build 17131)\n"
17+"X-Launchpad-Export-Date: 2014-08-05 06:45+0000\n"
18+"X-Generator: Launchpad (build 17147)\n"
1919
2020#: plugins/Unity/Launcher/launcheritem.cpp:43
2121#: plugins/Unity/Launcher/launcheritem.cpp:73
@@ -171,7 +171,7 @@ msgstr "Παρακαλώ εισάγετε το PIN της κάρτας SIM"
171171
172172#: qml/Panel/ActiveCallHint.qml:77
173173msgid "Tap to return to call..."
174−msgstr ""
174+msgstr "Πατήστε για να γυρίσετε στην κλήση..."
175175
176176#: qml/Panel/ActiveCallHint.qml:90
177177msgid "Conference"
@@ -199,7 +199,7 @@ msgstr "Ολοκληρώθηκε"
199199
200200#: qml/Panel/Indicators/MenuItemFactory.qml:595
201201msgid "Failed, tap to retry"
202−msgstr ""
202+msgstr "Αποτυχία, πατήστε για να ξαναπροσπαθήσετε"
203203
204204#: qml/Panel/SearchIndicator.qml:27
205205msgid "Search"
po/es.po+5−5
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2014-07-11 14:59+0200\n"
11−"PO-Revision-Date: 2014-07-26 06:22+0000\n"
12−"Last-Translator: Paco Molinero <paco@byasl.com>\n"
11+"PO-Revision-Date: 2014-08-06 15:24+0000\n"
12+"Last-Translator: Leo Arias <leo.arias@canonical.com>\n"
1313"Language-Team: Spanish <es@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-07-27 06:35+0000\n"
18−"X-Generator: Launchpad (build 17131)\n"
17+"X-Launchpad-Export-Date: 2014-08-07 06:27+0000\n"
18+"X-Generator: Launchpad (build 17147)\n"
1919
2020#: plugins/Unity/Launcher/launcheritem.cpp:43
2121#: plugins/Unity/Launcher/launcheritem.cpp:73
@@ -116,7 +116,7 @@ msgstr "Recientes"
116116
117117#: qml/Dash/GenericScopeView.qml:330
118118msgid "See all"
119−msgstr "Ver todo"
119+msgstr "Ver todas"
120120
121121#: qml/Dash/Previews/PreviewActionCombo.qml:34
122122msgid "More..."
po/eu.po+7−6
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2014-07-11 14:59+0200\n"
11−"PO-Revision-Date: 2014-07-25 12:38+0000\n"
11+"PO-Revision-Date: 2014-08-06 22:51+0000\n"
1212"Last-Translator: Ibai Oihanguren Sala <Unknown>\n"
1313"Language-Team: Basque <eu@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-07-26 07:16+0000\n"
18−"X-Generator: Launchpad (build 17131)\n"
17+"X-Launchpad-Export-Date: 2014-08-07 06:27+0000\n"
18+"X-Generator: Launchpad (build 17147)\n"
1919
2020#: plugins/Unity/Launcher/launcheritem.cpp:43
2121#: plugins/Unity/Launcher/launcheritem.cpp:73
@@ -36,7 +36,8 @@ msgstr "Eskuineko ertza"
3636
3737#: qml/Components/EdgeDemo.qml:114
3838msgid "Try swiping from the right edge to unlock the phone"
39−msgstr "Probatu eskuineko ertzetik arrastatzen telefonoa desblokeatzeko"
39+msgstr ""
40+"Pasatu hatza eskuineko ertzetik ezkerrerantz telefonoa desblokeatzeko"
4041
4142#: qml/Components/EdgeDemo.qml:145
4243msgid "Top edge"
@@ -44,7 +45,7 @@ msgstr "Goiko ertza"
4445
4546#: qml/Components/EdgeDemo.qml:146
4647msgid "Try swiping from the top edge to access the indicators"
47−msgstr "Probatu goiko ertzetik arrastatzen adierazleak ikusteko"
48+msgstr "Pasatu hatza goiko ertzetik beherantz adierazleak ikusteko"
4849
4950#: qml/Components/EdgeDemo.qml:171
5051msgid "Close"
@@ -52,7 +53,7 @@ msgstr "Itxi"
5253
5354#: qml/Components/EdgeDemo.qml:172
5455msgid "Swipe up again to close the settings screen"
55−msgstr "Pasatu hatza gora berriro ezarpenen leihoa ixteko"
56+msgstr "Pasatu hatza berriro gora ezarpenen leihoa ixteko"
5657
5758#: qml/Components/EdgeDemo.qml:199
5859msgid "Left edge"
po/hu.po+4−4
@@ -8,13 +8,13 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2014-07-11 14:59+0200\n"
11−"PO-Revision-Date: 2014-08-03 17:15+0000\n"
11+"PO-Revision-Date: 2014-08-06 07:41+0000\n"
1212"Last-Translator: Richard Somlói <ricsipontaz@gmail.com>\n"
1313"Language-Team: Hungarian <hu@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-08-04 06:32+0000\n"
17+"X-Launchpad-Export-Date: 2014-08-07 06:27+0000\n"
1818"X-Generator: Launchpad (build 17147)\n"
1919
2020#: plugins/Unity/Launcher/launcheritem.cpp:43
@@ -82,7 +82,7 @@ msgstr "Vészhívás"
8282
8383#: qml/Components/Lockscreen.qml:231
8484msgid "OK"
85−msgstr "Oké"
85+msgstr "Ok"
8686
8787#: qml/Components/PassphraseLockscreen.qml:60
8888#, qt-format
@@ -91,7 +91,7 @@ msgstr "Üdv %1"
9191
9292#: qml/Components/PassphraseLockscreen.qml:60
9393msgid "Hello"
94−msgstr "Üdv"
94+msgstr "Üdvözöljük"
9595
9696#: qml/Components/PinLockscreen.qml:198
9797msgid "CANCEL"
po/ja.po+11−11
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2014-07-11 14:59+0200\n"
11−"PO-Revision-Date: 2014-01-18 07:01+0000\n"
11+"PO-Revision-Date: 2014-08-06 15:19+0000\n"
1212"Last-Translator: Shushi Kurose <md81bird@hitaki.net>\n"
1313"Language-Team: Japanese <ja@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-07-26 07:16+0000\n"
18−"X-Generator: Launchpad (build 17131)\n"
17+"X-Launchpad-Export-Date: 2014-08-07 06:27+0000\n"
18+"X-Generator: Launchpad (build 17147)\n"
1919
2020#: plugins/Unity/Launcher/launcheritem.cpp:43
2121#: plugins/Unity/Launcher/launcheritem.cpp:73
@@ -28,7 +28,7 @@ msgstr ""
2828
2929#: qml/Components/EdgeDemoOverlay.qml:151
3030msgid "Skip intro"
31−msgstr ""
31+msgstr "導入ガイドをスキップ"
3232
3333#: qml/Components/EdgeDemo.qml:113
3434msgid "Right edge"
@@ -44,7 +44,7 @@ msgstr "上端"
4444
4545#: qml/Components/EdgeDemo.qml:146
4646msgid "Try swiping from the top edge to access the indicators"
47−msgstr ""
47+msgstr "インジケーターにアクセスするには上端からスワイプしてみてください"
4848
4949#: qml/Components/EdgeDemo.qml:171
5050msgid "Close"
@@ -52,7 +52,7 @@ msgstr "閉じる"
5252
5353#: qml/Components/EdgeDemo.qml:172
5454msgid "Swipe up again to close the settings screen"
55−msgstr ""
55+msgstr "設定画面を閉じるにはもう一度上にスワイプしてください"
5656
5757#: qml/Components/EdgeDemo.qml:199
5858msgid "Left edge"
@@ -60,11 +60,11 @@ msgstr "左端"
6060
6161#: qml/Components/EdgeDemo.qml:200
6262msgid "Swipe from the left to reveal the launcher for quick access to apps"
63−msgstr ""
63+msgstr "アプリに素早くアクセスできるLauncherを表示するには左からスワイプしてください"
6464
6565#: qml/Components/EdgeDemo.qml:227
6666msgid "Well done"
67−msgstr ""
67+msgstr "よくできました!"
6868
6969#: qml/Components/EdgeDemo.qml:228
7070msgid ""
@@ -74,7 +74,7 @@ msgstr ""
7474
7575#: qml/Components/Lockscreen.qml:214
7676msgid "Emergency Call"
77−msgstr ""
77+msgstr "緊急通報"
7878
7979#: qml/Components/Lockscreen.qml:231
8080msgid "OK"
@@ -83,7 +83,7 @@ msgstr ""
8383#: qml/Components/PassphraseLockscreen.qml:60
8484#, qt-format
8585msgid "Hello %1"
86−msgstr ""
86+msgstr "こんにちは %1 さん"
8787
8888#: qml/Components/PassphraseLockscreen.qml:60
8989msgid "Hello"
@@ -127,7 +127,7 @@ msgstr ""
127127
128128#: qml/Dash/Previews/PreviewRatingInput.qml:126
129129msgid "Add a review"
130−msgstr ""
130+msgstr "レビューを追加"
131131
132132#: qml/Dash/Previews/PreviewRatingInput.qml:166
133133msgid "Send"
po/pt_BR.po+11−11
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2014-07-11 14:59+0200\n"
11−"PO-Revision-Date: 2014-07-17 05:46+0000\n"
12−"Last-Translator: Tiago Hillebrandt <tiagohillebrandt@gmail.com>\n"
11+"PO-Revision-Date: 2014-08-04 18:33+0000\n"
12+"Last-Translator: Rafael Neri <Unknown>\n"
1313"Language-Team: Brazilian Portuguese <pt_BR@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-07-26 07:17+0000\n"
18−"X-Generator: Launchpad (build 17131)\n"
17+"X-Launchpad-Export-Date: 2014-08-05 06:45+0000\n"
18+"X-Generator: Launchpad (build 17147)\n"
1919
2020#: plugins/Unity/Launcher/launcheritem.cpp:43
2121#: plugins/Unity/Launcher/launcheritem.cpp:73
@@ -99,7 +99,7 @@ msgstr "CANCELAR"
9999
100100#: qml/Components/PinLockscreen.qml:216
101101msgid "DONE"
102−msgstr ""
102+msgstr "FEITO"
103103
104104#: qml/Components/SeeMore.qml:35
105105msgid "See more"
@@ -115,7 +115,7 @@ msgstr "Recentes"
115115
116116#: qml/Dash/GenericScopeView.qml:330
117117msgid "See all"
118−msgstr ""
118+msgstr "Ver todos"
119119
120120#: qml/Dash/Previews/PreviewActionCombo.qml:34
121121msgid "More..."
@@ -171,11 +171,11 @@ msgstr "Por favor, digite o PIN do cartão SIM"
171171
172172#: qml/Panel/ActiveCallHint.qml:77
173173msgid "Tap to return to call..."
174−msgstr ""
174+msgstr "Toque para retornar para a chamada..."
175175
176176#: qml/Panel/ActiveCallHint.qml:90
177177msgid "Conference"
178−msgstr ""
178+msgstr "Conferência"
179179
180180#: qml/Panel/Indicators/MenuItemFactory.qml:583
181181msgid "In queue…"
@@ -187,7 +187,7 @@ msgstr "Baixando"
187187
188188#: qml/Panel/Indicators/MenuItemFactory.qml:589
189189msgid "Paused, tap to resume"
190−msgstr ""
190+msgstr "Em pausa, toque para continuar"
191191
192192#: qml/Panel/Indicators/MenuItemFactory.qml:591
193193msgid "Canceled"
@@ -195,11 +195,11 @@ msgstr "Cancelado"
195195
196196#: qml/Panel/Indicators/MenuItemFactory.qml:593
197197msgid "Finished"
198−msgstr ""
198+msgstr "Finalizado"
199199
200200#: qml/Panel/Indicators/MenuItemFactory.qml:595
201201msgid "Failed, tap to retry"
202−msgstr ""
202+msgstr "Falhou, toque para tentar novamente"
203203
204204#: qml/Panel/SearchIndicator.qml:27
205205msgid "Search"
po/sl.po+4−4
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2014-07-11 14:59+0200\n"
11−"PO-Revision-Date: 2014-07-28 12:32+0000\n"
11+"PO-Revision-Date: 2014-08-06 06:11+0000\n"
1212"Last-Translator: Damir Jerovšek <Unknown>\n"
1313"Language-Team: Slovenian <sl@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-07-29 06:54+0000\n"
18−"X-Generator: Launchpad (build 17131)\n"
17+"X-Launchpad-Export-Date: 2014-08-07 06:27+0000\n"
18+"X-Generator: Launchpad (build 17147)\n"
1919
2020#: plugins/Unity/Launcher/launcheritem.cpp:43
2121#: plugins/Unity/Launcher/launcheritem.cpp:73
@@ -115,7 +115,7 @@ msgstr "Nedavno"
115115
116116#: qml/Dash/GenericScopeView.qml:330
117117msgid "See all"
118−msgstr ""
118+msgstr "Ogled vsega"
119119
120120#: qml/Dash/Previews/PreviewActionCombo.qml:34
121121msgid "More..."
qml/Components/EdgeDemo.qml+15−16
@@ -22,16 +22,15 @@ Item {
2222 id: demo
2323
2424 property Item greeter
25− property Item dash
2625 property Item launcher
2726 property Item indicators
28− property Item underlay
27+ property Item stages
2928
3029 property bool launcherEnabled: true
31− property bool dashEnabled: true
30+ property bool stagesEnabled: true
3231 property bool panelEnabled: true
3332 property bool panelContentEnabled: true
34− property bool running: !launcherEnabled || !dashEnabled || !panelEnabled || !panelContentEnabled
33+ property bool running: !launcherEnabled || !stagesEnabled || !panelEnabled || !panelContentEnabled
3534
3635 property bool paused: false
3736
@@ -59,7 +58,7 @@ Item {
5958
6059 function stopDemo() {
6160 launcherEnabled = true
62− dashEnabled = true
61+ stagesEnabled = true
6362 panelEnabled = true
6463 panelContentEnabled = true
6564 if (d.rightEdgeDemo) d.rightEdgeDemo.destroy()
@@ -75,11 +74,11 @@ Item {
7574 }
7675
7776 launcherEnabled = false;
78− dashEnabled = false;
77+ stagesEnabled = false;
7978 panelEnabled = false;
8079 panelContentEnabled = false;
8180
82− // Begin with either greeter or dash, depending on which is visible
81+ // Begin with either greeter or top, depending on which is visible
8382 if (greeter && greeter.shown) {
8483 startRightEdgeDemo()
8584 } else {
@@ -139,12 +138,12 @@ Item {
139138
140139 function startTopEdgeDemo() {
141140 demo.panelEnabled = true;
142− if (demo.dash && demo.underlay) {
143− d.topEdgeDemo = d.overlay.createObject(demo.underlay, {
141+ if (demo.stages) {
142+ d.topEdgeDemo = d.overlay.createObject(demo.stages, {
144143 "edge": "top",
145144 "title": i18n.tr("Top edge"),
146145 "text": i18n.tr("Try swiping from the top edge to access the indicators"),
147− "anchors.fill": demo.dash,
146+ "anchors.fill": demo.stages,
148147 });
149148 }
150149 if (d.topEdgeDemo) {
@@ -193,12 +192,12 @@ Item {
193192 function startLeftEdgeDemo() {
194193 demo.panelEnabled = false;
195194 demo.launcherEnabled = true;
196− if (demo.dash && demo.underlay) {
197− d.leftEdgeDemo = d.overlay.createObject(demo.underlay, {
195+ if (demo.stages) {
196+ d.leftEdgeDemo = d.overlay.createObject(demo.stages, {
198197 "edge": "left",
199198 "title": i18n.tr("Left edge"),
200199 "text": i18n.tr("Swipe from the left to reveal the launcher for quick access to apps"),
201− "anchors.fill": demo.dash,
200+ "anchors.fill": demo.stages,
202201 });
203202 }
204203 if (d.leftEdgeDemo) {
@@ -221,12 +220,12 @@ Item {
221220
222221 function startFinalEdgeDemo() {
223222 demo.launcherEnabled = false;
224− if (demo.dash && demo.underlay) {
225− d.finalEdgeDemo = d.overlay.createObject(demo.underlay, {
223+ if (demo.stages) {
224+ d.finalEdgeDemo = d.overlay.createObject(demo.stages, {
226225 "edge": "none",
227226 "title": i18n.tr("Well done"),
228227 "text": i18n.tr("You have now mastered the edge gestures and can start using the phone<br><br>Tap on the screen to start"),
229− "anchors.fill": demo.dash,
228+ "anchors.fill": demo.stages,
230229 "showSkip": false,
231230 });
232231 }
qml/Components/PageHeader.qml+25−16
@@ -32,9 +32,10 @@ Item {
3232 property bool searchEntryEnabled: false
3333 property ListModel searchHistory: SearchHistoryModel
3434 property alias searchQuery: searchTextField.text
35− property bool searchInProgress: false
3635
3736 property alias bottomItem: bottomContainer.children
37+ property int paginationCount: 0
38+ property int paginationIndex: -1
3839
3940 // TODO We should use foreground for the icons
4041 // of the toolbar but unfortunately Action does not have
@@ -125,7 +126,7 @@ Item {
125126 Flickable {
126127 id: headerContainer
127128 objectName: "headerContainer"
128− clip: openSearchAnimation.running
129+ clip: contentY < height
129130 anchors { left: parent.left; top: parent.top; right: parent.right }
130131 height: units.gu(6.5)
131132 contentHeight: headersColumn.height
@@ -169,6 +170,7 @@ Item {
169170 property var styledItem: searchHeader
170171 property string title
171172 property var config: PageHeadConfiguration {
173+ foregroundColor: root.scopeStyle ? root.scopeStyle.headerForeground : "grey"
172174 backAction: Action {
173175 iconName: "back"
174176 onTriggered: {
@@ -200,25 +202,13 @@ Item {
200202 anchors.fill: parent
201203 anchors.margins: units.gu(.75)
202204 source: "image://theme/clear"
203− opacity: searchTextField.text.length > 0 && !searchActivityIndicator.running
205+ opacity: searchTextField.text.length > 0
204206 visible: opacity > 0
205207 Behavior on opacity {
206208 UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration }
207209 }
208210 }
209211
210− ActivityIndicator {
211− id: searchActivityIndicator
212− objectName: "searchIndicator"
213− anchors.fill: parent
214− anchors.margins: units.gu(.75)
215− running: root.searchInProgress
216− opacity: running ? 1 : 0
217− Behavior on opacity {
218− UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration }
219− }
220− }
221−
222212 onClicked: {
223213 root.resetSearch(true);
224214 root.openSearchHistory();
@@ -247,10 +237,10 @@ Item {
247237 contentHeight: height
248238 opacity: headerContainer.clip || !headerContainer.showSearch ? 1 : 0 // setting visible false cause column to relayout
249239 separatorSource: ""
250− textColor: root.scopeStyle ? root.scopeStyle.headerForeground : "grey"
251240 property var styledItem: header
252241 property string title: root.title
253242 property var config: PageHeadConfiguration {
243+ foregroundColor: root.scopeStyle ? root.scopeStyle.headerForeground : "grey"
254244 backAction: Action {
255245 iconName: "back"
256246 visible: root.showBackButton
@@ -294,6 +284,25 @@ Item {
294284 }
295285 }
296286
287+ Row {
288+ spacing: units.gu(.5)
289+ Repeater {
290+ objectName: "paginationRepeater"
291+ model: root.paginationCount
292+ Image {
293+ objectName: "paginationDots_" + index
294+ height: units.gu(1)
295+ width: height
296+ source: (index == root.paginationIndex) ? "graphics/pagination_dot_on.png" : "graphics/pagination_dot_off.png"
297+ }
298+ }
299+ anchors {
300+ top: headerContainer.bottom
301+ horizontalCenter: headerContainer.horizontalCenter
302+ topMargin: units.gu(.5)
303+ }
304+ }
305+
297306 Component {
298307 id: popoverComponent
299308 Popover {
qml/Components/SeeMore.qmldeleted−70
@@ -1,70 +0,0 @@
1−/*
2− * Copyright (C) 2014 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−import QtQuick 2.0
18−import Ubuntu.Components 0.1
19−
20−/*! Widget that can be used in combination with a text area to allow easy 'expand/collapse' functionality. */
21−
22−Item {
23− //! Boolean set to true when it suggests expanded functionality
24− property bool more: false
25−
26− implicitHeight: seeMoreLabel.height + units.gu(2)
27−
28− Row {
29− anchors.centerIn: parent
30− spacing: units.gu(2)
31−
32− Label {
33− id: seeMoreLabel
34− objectName: "seeMoreLabel"
35− text: i18n.tr("See more")
36− opacity: !more ? 0.8 : 0.4
37− // TODO: Fix requiring Palette update
38− color: "grey" //Theme.palette.selected.backgroundText
39− font.weight: Font.Bold
40−
41− MouseArea {
42− anchors.fill: parent
43− onClicked: more = true
44− }
45− }
46−
47− Image {
48− anchors {
49− top: parent.top
50− bottom: parent.bottom
51− }
52− width: units.dp(2)
53− source: "ListItems/graphics/ListItemDividerVertical.png"
54− }
55−
56− Label {
57− objectName: "seeLessLabel"
58− text: i18n.tr("See less")
59− opacity: more ? 0.8 : 0.4
60− // TODO: Fix requiring Palette update
61− color: "grey" //Theme.palette.selected.backgroundText
62− font.weight: Font.Bold
63−
64− MouseArea {
65− anchors.fill: parent
66− onClicked: more = false
67− }
68− }
69− }
70−}
qml/Components/graphics/pagination_dot_off@30.pngnew file

Binary file not shown.

qml/Components/graphics/pagination_dot_on@30.pngnew file

Binary file not shown.

qml/Dash/CardTool.qml+1−1
@@ -126,7 +126,7 @@ Item {
126126 type:real \brief Height of the card's header.
127127 */
128128 readonly property int headerHeight: cardLoader.item ? cardLoader.item.headerHeight : 0
129− readonly property size artShapeSize: cardLoader.item ? cardLoader.item.artShapeSize : 0
129+ property size artShapeSize: cardLoader.item ? cardLoader.item.artShapeSize : 0
130130
131131 /*!
132132 \brief Desired alignment of header components.
qml/Dash/Dash.qml+92−11
@@ -14,10 +14,11 @@
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 */
1616
17−import QtQuick 2.0
17+import QtQuick 2.2
1818import Ubuntu.Components 0.1
1919import Unity 0.2
2020import Utils 0.1
21+import Unity.DashCommunicator 0.1
2122import "../Components"
2223
2324Showable {
@@ -29,8 +30,21 @@ Showable {
2930 property string showScopeOnLoaded: "clickscope"
3031 property real contentScale: 1.0
3132
33+ DashCommunicatorService {
34+ objectName: "dashCommunicatorService"
35+ onSetCurrentScopeRequested: {
36+ dash.setCurrentScope(scopeId, animate, reset)
37+ }
38+ }
39+
3240 function setCurrentScope(scopeId, animate, reset) {
33− var scopeIndex = filteredScopes.findFirst(Scopes.RoleId, scopeId)
41+ var scopeIndex = -1;
42+ for (var i = 0; i < scopes.count; ++i) {
43+ if (scopes.getScope(i).id == scopeId) {
44+ scopeIndex = i;
45+ break;
46+ }
47+ }
3448
3549 if (scopeIndex == -1) {
3650 console.warn("No match for scope with id: %1".arg(scopeId))
@@ -55,15 +69,16 @@ Showable {
5569 }
5670 }
5771
58− SortFilterProxyModel {
59− id: filteredScopes
60− model: Scopes {
61− id: scopes
62− }
63− dynamicSortFilter: true
72+ Scopes {
73+ id: scopes
74+ }
6475
65− filterRole: Scopes.RoleVisible
66− filterRegExp: RegExp("^true$")
76+ Image {
77+ anchors.fill: parent
78+ source: parent.width > parent.height ? "graphics/paper_landscape.png" : "graphics/paper_portrait.png"
79+ fillMode: Image.PreserveAspectCrop
80+ horizontalAlignment: Image.AlignRight
81+ verticalAlignment: Image.AlignTop
6782 }
6883
6984 DashContent {
@@ -71,7 +86,6 @@ Showable {
7186 objectName: "dashContent"
7287 width: parent.width
7388 height: parent.height
74− model: filteredScopes
7589 scopes: scopes
7690 visible: x != -width
7791 onGotoScope: {
@@ -126,4 +140,71 @@ Showable {
126140 }
127141 }
128142 }
143+
144+ Rectangle {
145+ id: indicator
146+ objectName: "processingIndicator"
147+ anchors {
148+ left: parent.left
149+ right: parent.right
150+ bottom: parent.bottom
151+ }
152+ height: units.dp(3)
153+ color: scopeStyle.backgroundLuminance > 0.7 ? "#50000000" : "#50ffffff"
154+ opacity: 0
155+ visible: opacity > 0
156+
157+ readonly property bool processing: dashContent.processing || scopeItem.processing
158+
159+ Behavior on opacity {
160+ UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration }
161+ }
162+
163+ onProcessingChanged: {
164+ if (processing) delay.start();
165+ else if (!persist.running) indicator.opacity = 0;
166+ }
167+
168+ Timer {
169+ id: delay
170+ interval: 200
171+ onTriggered: if (indicator.processing) {
172+ persist.restart();
173+ indicator.opacity = 1;
174+ }
175+ }
176+
177+ Timer {
178+ id: persist
179+ interval: 2 * UbuntuAnimation.SleepyDuration - UbuntuAnimation.FastDuration
180+ onTriggered: if (!indicator.processing) indicator.opacity = 0
181+ }
182+
183+ Rectangle {
184+ id: orange
185+ anchors { top: parent.top; bottom: parent.bottom }
186+ width: parent.width / 4
187+ color: Theme.palette.selected.foreground
188+
189+ SequentialAnimation {
190+ running: indicator.visible
191+ loops: Animation.Infinite
192+ XAnimator {
193+ from: -orange.width / 2
194+ to: indicator.width - orange.width / 2
195+ duration: UbuntuAnimation.SleepyDuration
196+ easing.type: Easing.InOutSine
197+ target: orange
198+ }
199+ XAnimator {
200+ from: indicator.width - orange.width / 2
201+ to: -orange.width / 2
202+ duration: UbuntuAnimation.SleepyDuration
203+ easing.type: Easing.InOutSine
204+ target: orange
205+ }
206+ }
207+ }
208+ }
209+
129210}
qml/Dash/DashApplication.qmlnew file+36
@@ -0,0 +1,36 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.2
18+import Ubuntu.Components 1.0
19+import Ubuntu.Thumbnailer 0.1 // Register support for image://thumbnailer/ and image://albumart/
20+
21+MainView {
22+ width: units.gu(40)
23+ height: units.gu(71)
24+
25+ useDeprecatedToolbar: false
26+
27+ Binding {
28+ target: i18n
29+ property: "domain"
30+ value: "unity8"
31+ }
32+
33+ Dash {
34+ anchors.fill: parent
35+ }
36+}
qml/Dash/DashContent.qml+7−4
@@ -23,9 +23,10 @@ import "../Components"
2323Item {
2424 id: dashContent
2525
26− property var model: null
27− property var scopes: null
26+ property alias scopes: dashContentList.model
2827 readonly property alias currentIndex: dashContentList.currentIndex
28+ readonly property bool processing: dashContentList.currentItem && dashContentList.currentItem.item
29+ && dashContentList.currentItem.item.processing || false
2930
3031 signal scopeLoaded(string scopeId)
3132 signal gotoScope(string scopeId)
@@ -89,10 +90,9 @@ Item {
8990 id: dashContentList
9091 objectName: "dashContentList"
9192
92− interactive: dashContent.scopes.loaded && currentItem && !currentItem.moving
93+ interactive: dashContent.scopes.loaded && currentItem && !currentItem.moving && !currentItem.navigationShown
9394
9495 anchors.fill: parent
95− model: dashContent.model
9696 orientation: ListView.Horizontal
9797 boundsBehavior: Flickable.DragAndOvershootBounds
9898 flickDeceleration: units.gu(625)
@@ -130,6 +130,7 @@ Item {
130130 objectName: scope.id + " loader"
131131
132132 readonly property bool moving: item ? item.moving : false
133+ readonly property bool navigationShown: item ? item.navigationShown : false
133134 readonly property var categoryView: item ? item.categoryView : null
134135 readonly property var theScope: scope
135136
@@ -143,6 +144,8 @@ Item {
143144 item.scope = Qt.binding(function() { return scope })
144145 item.isCurrent = Qt.binding(function() { return visible && ListView.isCurrentItem })
145146 dashContent.scopeLoaded(item.scope.id)
147+ item.paginationCount = Qt.binding(function() { return dashContentList.count } )
148+ item.paginationIndex = Qt.binding(function() { return dashContentList.currentIndex } )
146149 }
147150 Connections {
148151 target: isCurrent ? scope : null
qml/Dash/DashDepartments.qml → qml/Dash/DashNavigation.qmlrenamed+50−43
@@ -15,17 +15,17 @@
1515 */
1616
1717import QtQuick 2.2
18−import Ubuntu.Components 0.1
18+import Ubuntu.Components 1.1
1919
2020AbstractButton {
2121 id: root
22− objectName: "dashDepartments"
22+ objectName: "dashNavigation"
2323
2424 property var scope: null
2525
2626 property bool showList: false
2727
28− readonly property var currentDepartment: scope && scope.hasDepartments ? scope.getDepartment(scope.currentDepartmentId) : null
28+ readonly property var currentNavigation: scope && scope.hasNavigation ? scope.getNavigation(scope.currentNavigationId) : null
2929
3030 property alias windowWidth: blackRect.width
3131 property alias windowHeight: blackRect.height
@@ -34,20 +34,21 @@ AbstractButton {
3434 // Are we drilling down the tree or up?
3535 property bool isGoingBack: false
3636
37− visible: root.currentDepartment != null
37+ visible: root.currentNavigation != null
3838
3939 height: visible ? units.gu(5) : 0
4040
4141 onClicked: {
42+ navigationListView.updateMaxHeight();
4243 root.showList = !root.showList;
4344 }
4445
4546 Rectangle {
4647 id: blackRect
4748 color: "black"
48− opacity: departmentListView.currentItem && departmentListView.currentItem.visible ? 0.3 : 0
49+ opacity: navigationListView.currentItem && navigationListView.currentItem.visible ? 0.3 : 0
4950 Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration } }
50− anchors.top: departmentListView.top
51+ anchors.top: navigationListView.top
5152 anchors.right: parent.right
5253 visible: opacity != 0
5354 }
@@ -78,7 +79,7 @@ AbstractButton {
7879 anchors.fill: parent
7980 anchors.margins: units.gu(2)
8081 verticalAlignment: Text.AlignVCenter
81− text: root.currentDepartment ? root.currentDepartment.label : ""
82+ text: root.currentNavigation ? root.currentNavigation.label : ""
8283 color: root.scopeStyle ? root.scopeStyle.foreground : "grey"
8384 }
8485
@@ -92,18 +93,18 @@ AbstractButton {
9293 color: root.scopeStyle ? root.scopeStyle.foreground : "grey"
9394 }
9495
95− // departmentListView is outside root
96+ // navigationListView is outside root
9697 ListView {
97− id: departmentListView
98− objectName: "departmentListView"
98+ id: navigationListView
99+ objectName: "navigationListView"
99100 orientation: ListView.Horizontal
100101 interactive: false
101102 clip: root.width != windowWidth
102103 model: ListModel {
103− id: departmentModel
104+ id: navigationModel
104105 // We have two roles
105− // departmentId: the department id of the department the list represents
106− // nullifyDepartment: overrides departmentId to be null
106+ // navigationId: the navigation id of the navigation the list represents
107+ // nullifyNavigation: overrides navigationId to be null
107108 // This is used to "clear" the delegate when going "right" on the tree
108109 }
109110 anchors {
@@ -111,7 +112,12 @@ AbstractButton {
111112 right: parent.right
112113 top: root.bottom
113114 }
114− readonly property int maxHeight: (windowHeight - mapToItem(null, root.x, root.y).y) - units.gu(8)
115+ property int maxHeight: -1
116+ Component.onCompleted: updateMaxHeight();
117+ function updateMaxHeight()
118+ {
119+ maxHeight = (windowHeight - mapToItem(null, 0, 0).y) - units.gu(8);
120+ }
115121 property int prevHeight: maxHeight
116122 height: currentItem ? currentItem.height : maxHeight
117123 onHeightChanged: {
@@ -120,83 +126,84 @@ AbstractButton {
120126 }
121127 }
122128 highlightMoveDuration: UbuntuAnimation.FastDuration
123− delegate: DashDepartmentsList {
124− objectName: "department" + index
129+ delegate: DashNavigationList {
130+ objectName: "navigation" + index
125131 visible: height != 0
126− width: departmentListView.width
132+ width: navigationListView.width
127133 scopeStyle: root.scopeStyle
128134 property real desiredHeight: {
129135 if (root.showList) {
130− if (department && department.loaded && x == departmentListView.contentX)
136+ if (navigation && navigation.loaded && x == navigationListView.contentX)
131137 {
132− return Math.min(implicitHeight, departmentListView.maxHeight);
138+ navigationListView.updateMaxHeight();
139+ return Math.min(implicitHeight, navigationListView.maxHeight);
133140 } else {
134− return departmentListView.prevHeight;
141+ return navigationListView.prevHeight;
135142 }
136143 } else {
137144 return 0;
138145 }
139146 }
140147 height: desiredHeight
141− department: (nullifyDepartment || !scope) ? null : scope.getDepartment(departmentId)
142− currentDepartment: root.currentDepartment
143− onEnterDepartment: {
144− scope.loadDepartment(newDepartmentId);
148+ navigation: (nullifyNavigation || !scope) ? null : scope.getNavigation(navigationId)
149+ currentNavigation: root.currentNavigation
150+ onEnterNavigation: {
151+ scope.setNavigationState(newNavigationId, false);
145152 // We only need to add a new item to the model
146153 // if we have children, otherwise just load it
147154 if (hasChildren) {
148155 isGoingBack = false;
149− departmentModel.append({"departmentId": newDepartmentId, "nullifyDepartment": false});
150− departmentListView.currentIndex++;
156+ navigationModel.append({"navigationId": newNavigationId, "nullifyNavigation": false});
157+ navigationListView.currentIndex++;
151158 } else {
152159 showList = false;
153160 }
154161 }
155162 onGoBackToParentClicked: {
156− scope.loadDepartment(department.parentDepartmentId);
163+ scope.setNavigationState(navigation.parentNavigationId, false);
157164 isGoingBack = true;
158− departmentModel.setProperty(departmentListView.currentIndex - 1, "nullifyDepartment", false);
159− departmentListView.currentIndex--;
165+ navigationModel.setProperty(navigationListView.currentIndex - 1, "nullifyNavigation", false);
166+ navigationListView.currentIndex--;
160167 }
161− onAllDepartmentClicked: {
168+ onAllNavigationClicked: {
162169 showList = false;
163− if (root.currentDepartment.parentDepartmentId == department.departmentId) {
170+ if (root.currentNavigation.parentNavigationId == navigation.navigationId) {
164171 // For leaves we have to go to the parent too
165− scope.loadDepartment(root.currentDepartment.parentDepartmentId);
172+ scope.setNavigationState(root.currentNavigation.parentNavigationId, false);
166173 }
167174 }
168175 }
169176 onContentXChanged: {
170− if (contentX == width * departmentListView.currentIndex) {
177+ if (contentX == width * navigationListView.currentIndex) {
171178 if (isGoingBack) {
172− departmentModel.remove(departmentListView.currentIndex + 1);
179+ navigationModel.remove(navigationListView.currentIndex + 1);
173180 } else {
174− departmentModel.setProperty(departmentListView.currentIndex - 1, "nullifyDepartment", true);
181+ navigationModel.setProperty(navigationListView.currentIndex - 1, "nullifyNavigation", true);
175182 }
176183 }
177184 }
178185 }
179186
180187 InverseMouseArea {
181− anchors.fill: departmentListView
188+ anchors.fill: navigationListView
182189 enabled: root.showList
183190 onClicked: root.showList = false
184191 }
185192
186193 onScopeChanged: {
187− departmentModel.clear();
188− if (scope && scope.hasDepartments) {
189− departmentModel.append({"departmentId": scope.currentDepartmentId, "nullifyDepartment": false});
194+ navigationModel.clear();
195+ if (scope && scope.hasNavigation) {
196+ navigationModel.append({"navigationId": scope.currentNavigationId, "nullifyNavigation": false});
190197 }
191198 }
192199
193200 Connections {
194201 target: scope
195− onHasDepartmentsChanged: {
196− if (scope.hasDepartments) {
197− departmentModel.append({"departmentId": scope.currentDepartmentId, "nullifyDepartment": false});
202+ onHasNavigationChanged: {
203+ if (scope.hasNavigation) {
204+ navigationModel.append({"navigationId": scope.currentNavigationId, "nullifyNavigation": false});
198205 } else {
199− departmentModel.clear();
206+ navigationModel.clear();
200207 }
201208 }
202209 }
qml/Dash/DashDepartmentsList.qml → qml/Dash/DashNavigationList.qmlrenamed+14−18
@@ -15,16 +15,16 @@
1515 */
1616
1717import QtQuick 2.2
18−import Ubuntu.Components 0.1
18+import Ubuntu.Components 1.1
1919
2020Item {
2121 id: root
22− property var department: null
23− property var currentDepartment: null
22+ property var navigation: null
23+ property var currentNavigation: null
2424 property var scopeStyle: null
25− signal enterDepartment(var newDepartmentId, bool hasChildren)
25+ signal enterNavigation(var newNavigationId, bool hasChildren)
2626 signal goBackToParentClicked()
27− signal allDepartmentClicked()
27+ signal allNavigationClicked()
2828
2929 readonly property int itemHeight: units.gu(5)
3030 readonly property color foregroundColor: root.scopeStyle ? root.scopeStyle.foreground : "grey"
@@ -35,11 +35,6 @@ Item {
3535 anchors.fill: parent
3636 }
3737
38− ActivityIndicator {
39− id: loadingIndicator
40− anchors.centerIn: parent
41− running: !(department && department.loaded)
42− }
4338 clip: true
4439
4540 Behavior on height {
@@ -54,6 +49,7 @@ Item {
5449
5550 anchors.fill: parent
5651
52+ flickableDirection: Flickable.VerticalFlick
5753 contentHeight: column.height
5854 contentWidth: width
5955
@@ -68,7 +64,7 @@ Item {
6864 id: backButton
6965 objectName: "backButton"
7066 width: parent.width
71− visible: department && !department.isRoot || false
67+ visible: navigation && !navigation.isRoot || false
7268 height: itemHeight
7369
7470 onClicked: root.goBackToParentClicked();
@@ -92,7 +88,7 @@ Item {
9288 left: backImage.right
9389 leftMargin: units.gu(0.5)
9490 }
95− text: department ? department.parentLabel : ""
91+ text: navigation ? navigation.parentLabel : ""
9692 color: root.foregroundColor
9793 }
9894
@@ -114,7 +110,7 @@ Item {
114110 id: allButton
115111 objectName: "allButton"
116112 width: parent.width
117− visible: department && (!department.isRoot || (root.currentDepartment && !root.currentDepartment.isRoot && root.currentDepartment.parentDepartmentId == department.departmentId)) || false
113+ visible: navigation && (!navigation.isRoot || (root.currentNavigation && !root.currentNavigation.isRoot && root.currentNavigation.parentNavigationId == navigation.navigationId)) || false
118114 height: itemHeight
119115
120116 Label {
@@ -123,7 +119,7 @@ Item {
123119 left: parent.left
124120 leftMargin: units.gu(2)
125121 }
126− text: department ? (department.allLabel != "" ? department.allLabel : department.label) : ""
122+ text: navigation ? (navigation.allLabel != "" ? navigation.allLabel : navigation.label) : ""
127123 font.bold: true
128124 color: root.foregroundColor
129125 }
@@ -141,18 +137,18 @@ Item {
141137 height: units.dp(1)
142138 }
143139
144− onClicked: root.allDepartmentClicked();
140+ onClicked: root.allNavigationClicked();
145141 }
146142
147143 Repeater {
148− model: department && department.loaded ? department : null
144+ model: navigation && navigation.loaded ? navigation : null
149145 clip: true
150146 delegate: AbstractButton {
151147 objectName: root.objectName + "child" + index
152148 height: root.itemHeight
153149 width: root.width
154150
155− onClicked: root.enterDepartment(departmentId, hasChildren)
151+ onClicked: root.enterNavigation(navigationId, hasChildren)
156152
157153 Label {
158154 anchors {
@@ -188,7 +184,7 @@ Item {
188184 color: root.foregroundColor
189185 opacity: 0.1
190186 height: units.dp(1)
191− visible: index != department.count - 1
187+ visible: index != navigation.count - 1
192188 }
193189 }
194190 }
qml/Dash/GenericScopeView.qml+12−12
@@ -25,6 +25,7 @@ import "../Components/ListItems" as ListItems
2525FocusScope {
2626 id: scopeView
2727
28+ readonly property alias navigationShown: dashNavigation.showList
2829 property var scope: null
2930 property SortFilterProxyModel categories: categoryFilter
3031 property bool isCurrent: false
@@ -32,22 +33,16 @@ FocusScope {
3233 property bool hasBackAction: false
3334 property bool enableHeightBehaviorOnNextCreation: false
3435 property var categoryView: categoryView
36+ property alias paginationCount: pageHeader.paginationCount
37+ property alias paginationIndex: pageHeader.paginationIndex
3538
3639 property var scopeStyle: ScopeStyle {
3740 style: scope ? scope.customizations : {}
3841 }
3942
40− signal backClicked()
41−
42− onScopeChanged: {
43− if (scope) {
44− scope.activateApplication.connect(activateApp);
45− }
46− }
43+ readonly property bool processing: scope ? scope.searchInProgress || previewListView.processing : false
4744
48− function activateApp(appId) {
49− shell.activateApplication(appId);
50− }
45+ signal backClicked()
5146
5247 function positionAtBeginning() {
5348 categoryView.positionAtBeginning()
@@ -119,6 +114,7 @@ FocusScope {
119114 model: scopeView.categories
120115 forceNoClip: previewListView.open
121116 pixelAligned: true
117+ interactive: !dashNavigation.showList
122118
123119 property string expandedCategoryId: ""
124120
@@ -211,6 +207,10 @@ FocusScope {
211207 baseItem.expand(shouldExpand, false /*animate*/);
212208 }
213209 updateDelegateCreationRange();
210+ if (scope && scope.id === "clickscope" && (categoryId === "predefined" || categoryId === "local")) {
211+ // Yeah, hackish :/
212+ cardTool.artShapeSize = Qt.size(units.gu(8), units.gu(7.5));
213+ }
214214 item.cardTool = cardTool;
215215 }
216216
@@ -387,10 +387,10 @@ FocusScope {
387387 title: scopeView.scope ? scopeView.scope.name : ""
388388 showBackButton: scopeView.hasBackAction
389389 searchEntryEnabled: true
390− searchInProgress: scopeView.scope ? scopeView.scope.searchInProgress : false
391390 scopeStyle: scopeView.scopeStyle
392391
393− bottomItem: DashDepartments {
392+ bottomItem: DashNavigation {
393+ id: dashNavigation
394394 scope: scopeView.scope
395395 width: parent.width <= units.gu(60) ? parent.width : units.gu(40)
396396 anchors.right: parent.right
qml/Dash/PreviewListView.qml+23−27
@@ -32,6 +32,9 @@ Item {
3232 property alias currentItem: previewListView.currentItem
3333 property alias count: previewListView.count
3434
35+ readonly property bool processing: currentItem && (!currentItem.previewModel.loaded
36+ || currentItem.previewModel.processingAction)
37+
3538 PageHeader {
3639 id: header
3740 objectName: "pageHeader"
@@ -83,39 +86,32 @@ Item {
8386 }
8487 }
8588
86− delegate: Item {
87− objectName: "previewItem" + index
89+ delegate: Previews.Preview {
90+ id: preview
91+ objectName: "preview" + index
8892 height: previewListView.height
8993 width: previewListView.width
9094
91− readonly property bool ready: preview.previewModel.loaded
92−
93− Previews.Preview {
94− id: preview
95− objectName: "preview" + index
96− anchors.fill: parent
95+ isCurrent: ListView.isCurrentItem
9796
98− isCurrent: parent.ListView.isCurrentItem
99−
100− previewModel: {
101− var previewStack = root.scope.preview(result);
102− return previewStack.getPreviewModel(0);
103− }
104− scopeStyle: root.scopeStyle
97+ previewModel: {
98+ var previewStack = root.scope.preview(result);
99+ return previewStack.getPreviewModel(0);
105100 }
101+ scopeStyle: root.scopeStyle
102+ }
103+ }
106104
107− MouseArea {
108− id: processingMouseArea
109− objectName: "processingMouseArea"
110− anchors.fill: parent
111− enabled: !preview.previewModel.loaded || preview.previewModel.processingAction
112−
113− ActivityIndicator {
114− anchors.centerIn: parent
115− visible: root.open && parent.enabled
116− running: visible
117− }
118− }
105+ MouseArea {
106+ id: processingMouseArea
107+ objectName: "processingMouseArea"
108+ anchors {
109+ left: parent.left
110+ right: parent.right
111+ top: pageHeader.bottom
112+ bottom: parent.bottom
119113 }
114+
115+ enabled: root.processing
120116 }
121117}
qml/Dash/Previews/PreviewExpandable.qmlnew file+101
@@ -0,0 +1,101 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.3
18+import Ubuntu.Components 1.1
19+import "../../Components"
20+
21+/*! \brief Preview widget for expandable widgets.
22+
23+ This widget shows a list of widgets defined in widgetData["widgets"]
24+ Those widgets can be collapsed or uncollapsed. When uncollapsed
25+ all the widgets are shown, when collapsed only the first
26+ widgetData["collapsed-widgets"] are shown. It has a title that comes
27+ in via widgetData["title"]
28+ */
29+
30+PreviewWidget {
31+ id: root
32+ implicitHeight: childrenRect.height
33+
34+ expanded: false
35+
36+ Label {
37+ id: titleLabel
38+ objectName: "titleLabel"
39+ anchors {
40+ left: parent.left
41+ right: expandButton.left
42+ }
43+ fontSize: "large"
44+ color: root.scopeStyle ? root.scopeStyle.foreground : "grey"
45+ visible: text !== ""
46+ opacity: .8
47+ text: widgetData["title"] || ""
48+ wrapMode: Text.Wrap
49+ }
50+
51+ AbstractButton {
52+ id: expandButton
53+ objectName: "expandButton"
54+ width: titleLabel.height
55+ height: titleLabel.height
56+ anchors.right: parent.right
57+ onClicked: {
58+ root.expanded = !root.expanded;
59+ }
60+ Icon {
61+ anchors.fill: parent
62+ width: 64
63+ height: 64
64+ name: root.expanded ? "view-collapse" : "view-expand"
65+ }
66+ }
67+
68+ Column {
69+ anchors {
70+ top: titleLabel.bottom
71+ topMargin: units.gu(1)
72+ left: parent.left
73+ right: parent.right
74+ }
75+ spacing: units.gu(1)
76+ Repeater {
77+ id: repeater
78+ objectName: "repeater"
79+ model: widgetData["widgets"]
80+ delegate: PreviewWidgetFactory {
81+ height: visible ? implicitHeight : 0
82+ width: parent.width
83+ widgetId: modelData.widgetId
84+ widgetType: modelData.type
85+ widgetData: modelData.properties
86+ isCurrentPreview: root.isCurrentPreview
87+ scopeStyle: root.scopeStyle
88+ anchors {
89+ left: parent.left
90+ right: parent.right
91+ }
92+ expanded: root.expanded
93+ visible: root.expanded || index < root.widgetData["collapsed-widgets"]
94+
95+ onTriggered: {
96+ root.triggered(widgetId, actionId, data);
97+ }
98+ }
99+ }
100+ }
101+}
qml/Dash/Previews/PreviewImageGallery.qml+1
@@ -15,6 +15,7 @@
1515 */
1616
1717import QtQuick 2.0
18+import Ubuntu.Components 1.1
1819import "../../Components"
1920
2021/*! This preview widget shows a horizontal list of images.
qml/Dash/Previews/PreviewTextSummary.qml+3−16
@@ -23,7 +23,7 @@ import "../../Components"
2323 This widget shows text contained in widgetData["text"]
2424 along with a title that comes from widgetData["title"].
2525
26− In case the text does not fit in 7 lines a See More / Less widget is also shown.
26+ In case the widget is collapsed it only shows 3 lines of text.
2727 */
2828
2929PreviewWidget {
@@ -50,14 +50,14 @@ PreviewWidget {
5050 id: textLabel
5151 objectName: "textLabel"
5252
53− readonly property int maximumCollapsedLineCount: 7
53+ readonly property int maximumCollapsedLineCount: 3
5454
5555 anchors {
5656 left: parent.left
5757 right: parent.right
5858 top: titleLabel.visible ? titleLabel.bottom : parent.top
5959 }
60− height: (!seeMore.visible || seeMore.more) ? contentHeight : contentHeight / lineCount * (maximumCollapsedLineCount - 2)
60+ height: (lineCount <= maximumCollapsedLineCount || root.expanded) ? contentHeight : contentHeight / lineCount * maximumCollapsedLineCount
6161 clip: true
6262 fontSize: "small"
6363 lineHeight: 1.2
@@ -70,17 +70,4 @@ PreviewWidget {
7070 UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration }
7171 }
7272 }
73−
74− SeeMore {
75− id: seeMore
76− objectName: "seeMore"
77− anchors {
78− left: parent.left
79− right: parent.right
80− top: textLabel.bottom
81− topMargin: visible ? units.gu(1) : 0
82− }
83− height: visible ? implicitHeight : 0
84− visible: textLabel.lineCount > textLabel.maximumCollapsedLineCount
85− }
8673}
qml/Dash/Previews/PreviewWidget.qml+3
@@ -31,6 +31,9 @@ Item {
3131 //! The ScopeStyle component.
3232 property var scopeStyle: null
3333
34+ //! Should the widget show in expanded mode (For those that support it)
35+ property bool expanded: true
36+
3437 /*! \brief This signal should be emitted when a preview action was triggered.
3538 *
3639 * \param widgetId, actionId Respective identifiers from widgetData.
qml/Dash/Previews/PreviewWidgetFactory.qml+5
@@ -34,6 +34,9 @@ Loader {
3434 //! The ScopeStyle component.
3535 property var scopeStyle: null
3636
37+ //! Should the widget show in expanded mode (For those that support it)
38+ property bool expanded: true
39+
3740 //! Set to true if the parent preview is displayed.
3841 property bool isCurrentPreview: false
3942
@@ -47,6 +50,7 @@ Loader {
4750 switch (widgetType) {
4851 case "actions": return "PreviewActions.qml";
4952 case "audio": return "PreviewAudioPlayback.qml";
53+ case "expandable": return "PreviewExpandable.qml";
5054 case "gallery": return "PreviewImageGallery.qml";
5155 case "header": return "PreviewHeader.qml";
5256 case "image": return "PreviewZoomableImage.qml";
@@ -65,6 +69,7 @@ Loader {
6569 item.widgetId = Qt.binding(function() { return root.widgetId } )
6670 item.widgetData = Qt.binding(function() { return root.widgetData } )
6771 item.isCurrentPreview = Qt.binding(function() { return root.isCurrentPreview } )
72+ item.expanded = Qt.binding(function() { return root.expanded } )
6873 if (item.hasOwnProperty("scopeStyle")) item.scopeStyle = root.scopeStyle;
6974 }
7075
qml/Dash/ScopeListView.qml−5
@@ -20,9 +20,4 @@ import Dash 0.1
2020ListViewWithPageHeader {
2121 maximumFlickVelocity: height * 10
2222 flickDeceleration: height * 2
23−
24− Connections {
25− target: greeter
26− onShownChanged: if (greeter.shown) showHeader()
27− }
2823}
qml/Dash/graphics/phone/screenshots/unity8-dash@12.pngnew file

Binary file not shown.

qml/Greeter/Greeter.qml+1−1
@@ -70,7 +70,7 @@ Showable {
7070 DraggingArea {
7171 id: dragHandle
7272 anchors.fill: parent
73− enabled: (greeter.narrowMode || !greeter.locked) && greeter.enabled
73+ enabled: (greeter.narrowMode || !greeter.locked) && greeter.enabled && greeter.shown
7474 orientation: Qt.Horizontal
7575 propagateComposedEvents: true
7676
qml/Launcher/Launcher.qml+2−3
@@ -29,8 +29,7 @@ Item {
2929 property int dragAreaWidth: units.gu(1)
3030 property int minimizeDistance: units.gu(26)
3131 property real progress: dragArea.dragging && dragArea.touchX > panelWidth ?
32− (width * (dragArea.touchX-panelWidth) / (width - panelWidth)) :
33− (dragArea.dragging ? 0.001 : 0)
32+ (width * (dragArea.touchX-panelWidth) / (width - panelWidth)) : 0
3433
3534 readonly property bool shown: panel.x > -panel.width
3635
@@ -245,7 +244,7 @@ Item {
245244 if (!dragging) {
246245 if (distance > panel.width / 2) {
247246 if (distance > minimizeDistance) {
248− root.dash()
247+ root.dash();
249248 } else {
250249 root.switchToNextState("visible")
251250 }
qml/Notifications/NotificationMenuItemFactory.qml+3
@@ -76,6 +76,9 @@ Loader {
7676 }
7777 echoMode: checkBox.checked ? TextInput.Normal : TextInput.Password
7878 height: units.gu(5)
79+ Component.onCompleted: {
80+ forceActiveFocus();
81+ }
7982 onTextChanged: {
8083 menuModel.changeState(menuIndex, text);
8184 }
qml/Panel/Indicators/MenuItemFactory.qml+74
@@ -57,6 +57,7 @@ Item {
5757
5858 "unity.widgets.systemsettings.tablet.wifisection" : wifiSection,
5959 "unity.widgets.systemsettings.tablet.accesspoint" : accessPoint,
60+ "com.canonical.indicator.network.modeminfoitem" : modeminfoitem,
6061 }
6162
6263 function getExtendedProperty(object, propertyName, defaultValue) {
@@ -374,6 +375,79 @@ Item {
374375 }
375376 }
376377
378+ Component {
379+ id: modeminfoitem;
380+ ModemInfoItem {
381+ objectName: "modemInfoItem"
382+ property QtObject menuData: null
383+ property var menuModel: menuFactory.menuModel
384+ property int menuIndex: -1
385+ property var extendedData: menuData && menuData.ext || undefined
386+
387+ property var statusLabelAction: UnityMenuAction {
388+ model: menuModel
389+ index: menuIndex
390+ name: getExtendedProperty(extendedData, "xCanonicalModemStatusLabelAction", "")
391+ }
392+ statusText: statusLabelAction.valid ? statusLabelAction.state : ""
393+
394+ property var statusIconAction: UnityMenuAction {
395+ model: menuModel
396+ index: menuIndex
397+ name: getExtendedProperty(extendedData, "xCanonicalModemStatusIconAction", "")
398+ }
399+ statusIcon: statusIconAction.valid ? statusIconAction.state : ""
400+
401+ property var connectivityIconAction: UnityMenuAction {
402+ model: menuModel
403+ index: menuIndex
404+ name: getExtendedProperty(extendedData, "xCanonicalModemConnectivityIconAction", "")
405+ }
406+ connectivityIcon: connectivityIconAction.valid ? connectivityIconAction.state : ""
407+
408+ property var simIdentifierLabelAction: UnityMenuAction {
409+ model: menuModel
410+ index: menuIndex
411+ name: getExtendedProperty(extendedData, "xCanonicalModemSimIdentifierLabelAction", "")
412+ }
413+ simIdentifierText: simIdentifierLabelAction.valid ? simIdentifierLabelAction.state : ""
414+
415+ property var roamingAction: UnityMenuAction {
416+ model: menuModel
417+ index: menuIndex
418+ name: getExtendedProperty(extendedData, "xCanonicalModemRoamingAction", "")
419+ }
420+ roaming: roamingAction.valid ? roamingAction.state : false
421+
422+ property var unlockAction: UnityMenuAction {
423+ model: menuModel
424+ index: menuIndex
425+ name: getExtendedProperty(extendedData, "xCanonicalModemLockedAction", "")
426+ }
427+ onUnlock: {
428+ unlockAction.activate();
429+ }
430+ locked: unlockAction.valid ? unlockAction.state : false
431+
432+ onMenuModelChanged: {
433+ loadAttributes();
434+ }
435+ onMenuIndexChanged: {
436+ loadAttributes();
437+ }
438+
439+ function loadAttributes() {
440+ if (!menuModel || menuIndex == -1) return;
441+ menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-modem-status-label-action': 'string',
442+ 'x-canonical-modem-status-icon-action': 'string',
443+ 'x-canonical-modem-connectivity-icon-action': 'string',
444+ 'x-canonical-modem-sim-identifier-label-action': 'string',
445+ 'x-canonical-modem-roaming-action': 'string',
446+ 'x-canonical-modem-locked-action': 'string'});
447+ }
448+ }
449+ }
450+
377451 Component {
378452 id: messageItem
379453
qml/Panel/Indicators/ModemInfoItem.qmlnew file+111
@@ -0,0 +1,111 @@
1+/*
2+ * Copyright 2014 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import QtQuick.Layouts 1.1
19+import Ubuntu.Components 0.1
20+import Ubuntu.Components.ListItems 0.1 as ListItem
21+
22+ListItem.Empty {
23+ id: menu
24+ implicitHeight: mainColumn.implicitHeight
25+ + mainColumn.anchors.topMargin
26+ + mainColumn.anchors.bottomMargin
27+
28+ property alias statusIcon: statusIcon.name
29+ property alias statusText: labelStatus.text
30+ property alias connectivityIcon: iconConnectivity.name
31+ property alias simIdentifierText: labelSimIdentifier.text
32+ property bool locked : false
33+ property bool roaming: false
34+ signal unlock
35+
36+ ColumnLayout {
37+ id: mainColumn
38+ anchors.fill: parent
39+ anchors.margins: menu.__contentsMargins
40+ spacing: units.gu(0.5)
41+
42+ Label {
43+ id: labelSimIdentifier
44+ elide: Text.ElideRight
45+ visible: text !== ""
46+ font.bold: true
47+ opacity: menu.locked ? 0.6 : 1.0
48+ }
49+
50+ RowLayout {
51+ id: statusRow
52+ spacing: units.gu(1)
53+
54+ height: labelStatus.height
55+ width: parent.width
56+
57+ Label {
58+ id: labelStatus
59+ elide: Text.ElideRight
60+ opacity: 0.6
61+ }
62+
63+ RowLayout {
64+ spacing: 0.5
65+ height: parent.height
66+ Icon {
67+ id: statusIcon
68+ color: Theme.palette.selected.backgroundText
69+
70+ Layout.preferredHeight: parent.height
71+ Layout.preferredWidth: Layout.preferredHeight
72+
73+ visible: name !== ""
74+ }
75+
76+ Icon {
77+ id: iconConnectivity
78+ color: Theme.palette.selected.backgroundText
79+
80+ Layout.preferredHeight: parent.height
81+ Layout.preferredWidth: Layout.preferredHeight
82+
83+ visible: name !== ""
84+ }
85+ }
86+
87+ Item {
88+ // eat up all the excess space so that RoamingIndication
89+ // ends up to the far right
90+ Layout.fillWidth: true
91+ }
92+
93+ RoamingIndication {
94+ id: roamingIndication
95+ visible: menu.roaming
96+ Layout.fillHeight: true
97+ }
98+ }
99+
100+ Button {
101+ id: buttonUnlock
102+ objectName: "buttonUnlockSim"
103+ visible: menu.locked
104+
105+ text: i18n.tr("Unlock SIM")
106+ Layout.preferredWidth: implicitWidth + units.gu(5)
107+
108+ onTriggered: menu.unlock()
109+ }
110+ }
111+}
qml/Panel/Indicators/RoamingIndication.qmlnew file+40
@@ -0,0 +1,40 @@
1+/*
2+ * Copyright 2014 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import QtQuick.Layouts 1.1
19+import Ubuntu.Components 0.1
20+
21+RowLayout {
22+ spacing: units.gu(0.5)
23+ Label {
24+ id: labelRoaming
25+ elide: Text.ElideRight
26+ fontSize: "x-small"
27+ text: i18n.tr("Roaming")
28+ opacity: 0.6
29+ }
30+
31+ Icon {
32+ id: iconRoaming
33+ color: Theme.palette.selected.backgroundText
34+
35+ Layout.preferredHeight: parent.height
36+ Layout.preferredWidth: Layout.preferredHeight
37+
38+ name: "network-cellular-roaming"
39+ }
40+}
qml/ScopeTool.qml+2−11
@@ -17,6 +17,7 @@
1717import QtQuick 2.0
1818import Ubuntu.Components 0.1
1919import Ubuntu.Components.Popups 0.1
20+import Ubuntu.Thumbnailer 0.1 // Register support for image://thumbnailer/ and image://albumart/
2021import Utils 0.1
2122import Unity 0.2
2223import "Components"
@@ -45,16 +46,6 @@ Rectangle {
4546 signal searchClicked
4647 }
4748
48− SortFilterProxyModel {
49− id: filteredScopes
50− model: Scopes {
51− id: scopes
52− }
53− dynamicSortFilter: true
54−
55− filterRole: Scopes.RoleVisible
56− filterRegExp: RegExp("^true$")
57− }
5849
5950 Rectangle {
6051 anchors.fill: dashContent
@@ -72,8 +63,8 @@ Rectangle {
7263 DashContent {
7364 id: dashContent
7465
75− model: filteredScopes
7666 property var scope: scopes.getScope(currentIndex)
67+ scopes: Scopes { }
7768
7869 anchors {
7970 top: parent.top
qml/Shell.qml+28−153
@@ -25,7 +25,6 @@ import Utils 0.1
2525import LightDM 0.1 as LightDM
2626import Powerd 0.1
2727import SessionBroadcast 0.1
28−import "Dash"
2928import "Greeter"
3029import "Launcher"
3130import "Panel"
@@ -34,6 +33,7 @@ import "Notifications"
3433import "Stages"
3534import Unity.Notifications 1.0 as NotificationBackend
3635import Unity.Session 0.1
36+import Unity.DashCommunicator 0.1
3737
3838Item {
3939 id: shell
@@ -48,8 +48,6 @@ Item {
4848 property url background
4949 readonly property real panelHeight: panel.panelHeight
5050
51− property bool dashShown: dash.shown && dash.available && underlay.visible
52−
5351 property bool sideStageEnabled: shell.width >= units.gu(100)
5452 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
5553
@@ -59,7 +57,6 @@ Item {
5957 } else {
6058 var execFlags = shell.sideStageEnabled ? ApplicationManager.NoFlag : ApplicationManager.ForceMainStage;
6159 ApplicationManager.startApplication(appId, execFlags);
62− stages.show();
6360 }
6461 }
6562
@@ -86,6 +83,11 @@ Item {
8683 id: volumeControl
8784 }
8885
86+ DashCommunicator {
87+ id: dash
88+ objectName: "dashCommunicator"
89+ }
90+
8991 WindowKeysFilter {
9092 // Handle but do not filter out volume keys
9193 Keys.onVolumeUpPressed: { volumeControl.volumeUp(); event.accepted = false; }
@@ -110,131 +112,12 @@ Item {
110112 }
111113 }
112114
113− Item {
114− id: underlay
115− objectName: "underlay"
116− anchors.fill: parent
117−
118− // Whether the underlay is fully covered by opaque UI elements.
119− property bool fullyCovered: (panel.indicators.fullyOpened && shell.width <= panel.indicatorsMenuWidth)
120− || stages.fullyShown || greeterWrapper.fullyShown
121− visible: !fullyCovered
122−
123− Image {
124− anchors.fill: dash
125− source: shell.width > shell.height ? "Dash/graphics/paper_landscape.png" : "Dash/graphics/paper_portrait.png"
126− fillMode: Image.PreserveAspectCrop
127− horizontalAlignment: Image.AlignRight
128− verticalAlignment: Image.AlignTop
129− }
130−
131− Dash {
132− id: dash
133− objectName: "dash"
134−
135− available: !LightDM.Greeter.active
136− hides: [stages, launcher, panel.indicators]
137− shown: disappearingAnimationProgress !== 1.0 && greeterWrapper.showProgress !== 1.0 &&
138− !(panel.indicators.fullyOpened && !sideStageEnabled)
139− enabled: disappearingAnimationProgress === 0.0 && greeterWrapper.showProgress === 0.0 && edgeDemo.dashEnabled
140−
141− anchors {
142− fill: parent
143− topMargin: panel.panelHeight
144− }
145−
146− contentScale: 1.0 - 0.2 * disappearingAnimationProgress
147− opacity: 1.0 - disappearingAnimationProgress
148− property real disappearingAnimationProgress: stages.showProgress
149−
150− // FIXME: only necessary because stages.showProgress is not animated
151− Behavior on disappearingAnimationProgress { SmoothedAnimation { velocity: 5 }}
152− }
153− }
154−
155− EdgeDragArea {
156− id: stagesDragArea
157− direction: Direction.Leftwards
158−
159− anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
160− width: shell.edgeSize
161−
162− property real progress: stages.width
163−
164− onTouchXChanged: {
165− if (status == DirectionalDragArea.Recognized) {
166− if (ApplicationManager.empty) {
167− progress = Math.max(stages.width - stagesDragArea.width + touchX, stages.width * .3);
168− } else {
169− progress = stages.width - stagesDragArea.width + touchX;
170− }
171− }
172− }
173−
174− onDraggingChanged: {
175− if (!dragging) {
176− if (!ApplicationManager.empty && progress < stages.width - units.gu(10)) {
177− stages.show();
178− }
179− stagesDragArea.progress = Qt.binding(function () { return stages.width; });
180− }
181− }
182− }
183−
184115 Item {
185116 id: stages
186117 objectName: "stages"
187118 width: parent.width
188119 height: parent.height
189−
190− visible: !fullyHidden && !ApplicationManager.empty
191−
192− x: {
193− if (shown) {
194− if (locked || greeter.fakeActiveForApp !== "") {
195− return 0;
196− }
197− return launcher.progress;
198− } else {
199− return stagesDragArea.progress
200− }
201− }
202− Behavior on x { SmoothedAnimation { velocity: 600; duration: UbuntuAnimation.FastDuration } }
203−
204− property bool shown: false
205− onShownChanged: {
206− if (shown) {
207− if (ApplicationManager.count > 0) {
208− ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
209− }
210− } else {
211− if (ApplicationManager.focusedApplicationId) {
212− ApplicationManager.updateScreenshot(ApplicationManager.focusedApplicationId);
213− ApplicationManager.unfocusCurrentApplication();
214− }
215− }
216− }
217−
218− // Avoid a silent "divide by zero -> NaN" situation during init as shell.width will be
219− // zero. That breaks the property binding and the function won't be reevaluated once
220− // shell.width is set, with the NaN result staying there for good.
221− property real showProgress: shell.width ? MathUtils.clamp(1 - x / shell.width, 0, 1) : 0
222−
223− property bool fullyShown: x == 0
224− property bool fullyHidden: x == width
225−
226− property bool locked: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.locked : false
227−
228− // It might technically not be fullyShown but visually it just looks so.
229− property bool roughlyFullyShown: x >= 0 && x <= units.gu(1)
230−
231− function show() {
232− shown = true;
233− }
234−
235− function hide() {
236− shown = false;
237− }
120+ visible: !ApplicationManager.empty
238121
239122 Connections {
240123 target: ApplicationManager
@@ -243,7 +126,6 @@ Item {
243126 lockscreen.show();
244127 }
245128 greeter.hide();
246− stages.show();
247129 }
248130
249131 onFocusedApplicationIdChanged: {
@@ -254,18 +136,9 @@ Item {
254136 }
255137
256138 onApplicationAdded: {
257− if (greeter.shown) {
139+ if (greeter.shown && appId != "unity8-dash") {
258140 greeter.hide();
259141 }
260− if (!stages.shown) {
261− stages.show();
262− }
263− }
264−
265− onEmptyChanged: {
266− if (ApplicationManager.empty) {
267− stages.hide();
268− }
269142 }
270143 }
271144
@@ -294,13 +167,17 @@ Item {
294167 Binding {
295168 target: applicationsDisplayLoader.item
296169 property: "interactive"
297− value: stages.roughlyFullyShown && !greeter.shown && !lockscreen.shown
298− && panel.indicators.fullyClosed
170+ value: edgeDemo.stagesEnabled && !greeter.shown && !lockscreen.shown && panel.indicators.fullyClosed && launcher.progress == 0
299171 }
300172 Binding {
301173 target: applicationsDisplayLoader.item
302174 property: "spreadEnabled"
303− value: greeter.fakeActiveForApp === "" // to support emergency dialer hack
175+ value: edgeDemo.stagesEnabled && greeter.fakeActiveForApp === "" // to support emergency dialer hack
176+ }
177+ Binding {
178+ target: applicationsDisplayLoader.item
179+ property: "inverseProgress"
180+ value: launcher.progress
304181 }
305182 }
306183 }
@@ -504,7 +381,7 @@ Item {
504381 onDisplayPowerStateChange: {
505382 // We ignore any display-off signals when the proximity sensor
506383 // is active. This usually indicates something like a phone call.
507− if (status == Powerd.Off && reason != Powerd.Proximity) {
384+ if (status == Powerd.Off && reason != Powerd.Proximity && !edgeDemo.running) {
508385 greeter.showNow();
509386 }
510387
@@ -531,25 +408,20 @@ Item {
531408
532409 var animate = !LightDM.Greeter.active && !stages.shown
533410 dash.setCurrentScope("clickscope", animate, false)
534− stages.hide()
411+ ApplicationManager.requestFocusApplication("unity8-dash")
535412 }
536413
537414 function showDash() {
538415 if (LightDM.Greeter.active && !LightDM.Greeter.authenticated) {
539416 return;
540417 }
541−
542− if (!stages.locked) {
543− stages.hide();
544− launcher.fadeOut();
545− } else {
546− launcher.switchToNextState("visible");
547− }
548−
549418 if (greeter.shown) {
550419 greeter.hideRight();
551420 launcher.fadeOut();
552421 }
422+
423+ ApplicationManager.requestFocusApplication("unity8-dash")
424+ launcher.fadeOut();
553425 }
554426
555427 Item {
@@ -574,8 +446,8 @@ Item {
574446 ApplicationManager.focusedApplicationId &&
575447 ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen
576448
577− fullscreenMode: (stages.roughlyFullyShown && topmostApplicationIsFullscreen
578− && !LightDM.Greeter.active) || greeter.fakeActiveForApp !== ""
449+ fullscreenMode: (topmostApplicationIsFullscreen && !LightDM.Greeter.active && launcher.progress == 0)
450+ || greeter.fakeActiveForApp !== ""
579451 }
580452
581453 Launcher {
@@ -592,7 +464,11 @@ Item {
592464
593465 onShowDashHome: showHome()
594466 onDash: showDash()
595− onDashSwipeChanged: if (dashSwipe && stages.shown) dash.setCurrentScope("clickscope", false, true)
467+ onDashSwipeChanged: {
468+ if (dashSwipe && ApplicationManager.focusedApplicationId !== "unity8-dash") {
469+ dash.setCurrentScope("clickscope", false, true)
470+ }
471+ }
596472 onLauncherApplicationSelected: {
597473 if (greeter.fakeActiveForApp !== "") {
598474 lockscreen.show()
@@ -684,9 +560,8 @@ Item {
684560 z: alphaDisclaimerLabel.z + 10
685561 greeter: greeter
686562 launcher: launcher
687− dash: dash
688563 indicators: panel.indicators
689− underlay: underlay
564+ stages: stages
690565 }
691566
692567 Connections {
qml/Stages/PhoneStage.qml+59−26
@@ -21,7 +21,7 @@ import Unity.Application 0.1
2121import Utils 0.1
2222import "../Components"
2323
24−Item {
24+Rectangle {
2525 id: root
2626
2727 // Controls to be set from outside
@@ -29,9 +29,9 @@ Item {
2929 property real maximizedAppTopMargin
3030 property bool interactive
3131 property bool spreadEnabled: true // If false, animations and right edge will be disabled
32+ property real inverseProgress: 0 // This is the progress for left edge drags, in pixels.
3233
33− // State information propagated to the outside
34− readonly property bool locked: spreadView.phase == 2
34+ color: "black"
3535
3636 function select(appId) {
3737 spreadView.snapTo(priv.indexOf(appId));
@@ -43,6 +43,16 @@ Item {
4343 spreadView.contentX = -spreadView.shift;
4444 }
4545
46+ onInverseProgressChanged: {
47+ if (inverseProgress == 0 && priv.oldInverseProgress > 0) {
48+ // left edge drag released. Minimum distance is given by design.
49+ if (priv.oldInverseProgress > units.gu(22)) {
50+ ApplicationManager.focusApplication("unity8-dash");
51+ }
52+ }
53+ priv.oldInverseProgress = inverseProgress;
54+ }
55+
4656 Connections {
4757 target: ApplicationManager
4858
@@ -50,7 +60,7 @@ Item {
5060 if (spreadView.phase > 0) {
5161 spreadView.snapTo(priv.indexOf(appId));
5262 } else {
53− priv.switchToApp(appId);
63+ ApplicationManager.focusApplication(appId);
5464 }
5565 }
5666
@@ -60,7 +70,7 @@ Item {
6070 } else {
6171 spreadView.phase = 0;
6272 spreadView.contentX = -spreadView.shift;
63− priv.switchToApp(appId);
73+ ApplicationManager.focusApplication(appId);
6474 }
6575 }
6676
@@ -79,15 +89,11 @@ Item {
7989
8090 property string focusedAppId: ApplicationManager.focusedApplicationId
8191 property var focusedApplication: ApplicationManager.findApplication(focusedAppId)
92+ property var focusedAppDelegate: null
8293
83− function switchToApp(appId) {
84− if (priv.focusedAppId) {
85− spreadView.focusChanging = true;
86− ApplicationManager.focusApplication(appId);
87− } else {
88− ApplicationManager.focusApplication(appId);
89− }
90− }
94+ property real oldInverseProgress: 0
95+
96+ onFocusedAppIdChanged: focusedAppDelegate = spreadRepeater.itemAt(0);
9197
9298 function indexOf(appId) {
9399 for (var i = 0; i < ApplicationManager.count; i++) {
@@ -100,14 +106,6 @@ Item {
100106
101107 }
102108
103− Rectangle {
104− id: coverFlipBackground
105− anchors.fill: parent
106− color: "black"
107− visible: spreadView.visible
108− }
109−
110−
111109 Flickable {
112110 id: spreadView
113111 objectName: "spreadView"
@@ -116,6 +114,10 @@ Item {
116114 contentWidth: spreadRow.width - shift
117115 contentX: -shift
118116
117+ // This indicates when the spreadView is active. That means, all the animations
118+ // are activated and tiles need to line up for the spread.
119+ readonly property bool active: shiftedContentX > 0 || spreadDragArea.dragging
120+
119121 // The flickable needs to fill the screen in order to get touch events all over.
120122 // However, we don't want to the user to be able to scroll back all the way. For
121123 // that, the beginning of the gesture starts with a negative value for contentX
@@ -198,6 +200,12 @@ Item {
198200 snapAnimation.start();
199201 }
200202
203+ // In case the ApplicationManager already holds an app when starting up we're missing animations
204+ // Make sure we end up in the same state
205+ Component.onCompleted: {
206+ spreadView.contentX = -spreadView.shift
207+ }
208+
201209 SequentialAnimation {
202210 id: snapAnimation
203211 property int targetContentX: -spreadView.shift
@@ -260,14 +268,33 @@ Item {
260268 && spreadView.shiftedContentX === 0 && root.interactive && index === 0
261269 swipeToCloseEnabled: spreadView.interactive
262270 maximizedAppTopMargin: root.maximizedAppTopMargin
263− dropShadow: spreadView.shiftedContentX > 0 || spreadDragArea.status == DirectionalDragArea.Undecided
271+ dropShadow: spreadView.active ||
272+ priv.focusedAppDelegate.x !== 0
273+
274+ readonly property bool isDash: model.appId == "unity8-dash"
275+
276+ z: isDash && !spreadView.active ? -1 : behavioredIndex
264277
265− z: behavioredIndex
266− x: index == 0 ? 0 : spreadView.width + (index - 1) * spreadView.tileDistance
278+ x: {
279+ // focused app is always positioned at 0 except when following left edge drag
280+ if (index == 0) {
281+ if (!isDash && root.inverseProgress > 0) {
282+ return root.inverseProgress;
283+ }
284+ return 0;
285+ }
286+ if (isDash && !spreadView.active && !spreadDragArea.dragging) {
287+ return 0;
288+ }
289+
290+ // Otherwise line up for the spread
291+ return spreadView.width + (index - 1) * spreadView.tileDistance;
292+ }
267293 property real behavioredIndex: index
268294 Behavior on behavioredIndex {
269295 enabled: spreadView.closingIndex >= 0
270296 UbuntuNumberAnimation {
297+ id: appXAnimation
271298 onRunningChanged: {
272299 if (!running) {
273300 spreadView.closingIndex = -1;
@@ -277,11 +304,13 @@ Item {
277304 }
278305
279306 Behavior on x {
280− enabled: spreadView.focusChanging && index == 0 && root.spreadEnabled
307+ enabled: root.spreadEnabled &&
308+ !spreadView.active &&
309+ !snapAnimation.running
281310 UbuntuNumberAnimation {
282311 duration: UbuntuAnimation.FastDuration
283312 onRunningChanged: {
284− if (!running) {
313+ if (!running && root.inverseProgress == 0) {
285314 spreadView.focusChanging = false;
286315 }
287316 }
@@ -322,6 +351,10 @@ Item {
322351 return progress;
323352 }
324353
354+ // Hiding tiles when their progress is negative or reached the maximum
355+ visible: (progress >= 0 && progress < 1.7) ||
356+ (isDash && priv.focusedAppDelegate.x !== 0)
357+
325358 EasingCurve {
326359 id: snappingCurve
327360 type: EasingCurve.Linear
qml/Stages/SpreadDelegate.qml+6
@@ -37,6 +37,7 @@ Item {
3737
3838 SurfaceContainer {
3939 id: surfaceContainer
40+ objectName: "surfaceContainer"
4041 anchors.fill: parent
4142 surface: model.surface
4243 property bool appHasCreatedASurface: false
@@ -172,6 +173,11 @@ Item {
172173 }
173174
174175 onDragEnd: {
176+ if (model.appId == "unity8-dash") {
177+ animation.animate("center")
178+ return;
179+ }
180+
175181 // velocity and distance values specified by design prototype
176182 if ((dragVelocity < -units.gu(40) && distance < -units.gu(8)) || distance < -root.height / 2) {
177183 animation.animate("up")
qml/Stages/TabletStage.qml+85−41
@@ -21,10 +21,11 @@ import Unity.Application 0.1
2121import Utils 0.1
2222import "../Components"
2323
24−Item {
24+Rectangle {
2525 id: root
2626 objectName: "stages"
2727 anchors.fill: parent
28+ color: "black"
2829
2930 // Controls to be set from outside
3031 property bool shown: false
@@ -32,9 +33,17 @@ Item {
3233 property int dragAreaWidth
3334 property real maximizedAppTopMargin
3435 property bool interactive
36+ property real inverseProgress: 0 // This is the progress for left edge drags, in pixels.
3537
36− // State information propagated to the outside
37− readonly property bool locked: spreadView.phase == 2
38+ onInverseProgressChanged: {
39+ if (inverseProgress == 0 && priv.oldInverseProgress > 0) {
40+ // left edge drag released. Minimum distance is given by design.
41+ if (priv.oldInverseProgress > units.gu(22)) {
42+ ApplicationManager.focusApplication("unity8-dash");
43+ }
44+ }
45+ priv.oldInverseProgress = inverseProgress;
46+ }
3847
3948 QtObject {
4049 id: priv
@@ -49,6 +58,8 @@ Item {
4958 property string appId0
5059 property string appId1
5160
61+ property int oldInverseProgress: 0
62+
5263 onFocusedAppIdChanged: {
5364 if (priv.focusedAppId.length > 0) {
5465 var focusedApp = ApplicationManager.findApplication(focusedAppId);
@@ -107,16 +118,26 @@ Item {
107118 }
108119 }
109120
121+ onApplicationAdded: {
122+ if (spreadView.phase == 2) {
123+ spreadView.snapTo(ApplicationManager.count - 1);
124+ } else {
125+ spreadView.phase = 0;
126+ spreadView.contentX = -spreadView.shift;
127+ ApplicationManager.focusApplication(appId);
128+ }
129+ }
130+
110131 onApplicationRemoved: {
111132 if (priv.mainStageAppId == appId) {
112− priv.mainStageAppId = "";
133+ ApplicationManager.focusApplication("unity8-dash")
113134 }
114135 if (priv.sideStageAppId == appId) {
115136 priv.sideStageAppId = "";
116137 }
117138 if (ApplicationManager.count == 0) {
118139 spreadView.phase = 0;
119− spreadView.contentX = 0;
140+ spreadView.contentX = -spreadView.shift;
120141 }
121142 }
122143 }
@@ -124,13 +145,26 @@ Item {
124145 Flickable {
125146 id: spreadView
126147 anchors.fill: parent
127− contentWidth: spreadRow.width
128148 interactive: (spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1) && draggedIndex == -1
149+ contentWidth: spreadRow.width - shift
150+ contentX: -shift
129151
130152 property int tileDistance: units.gu(20)
131153 property int sideStageWidth: units.gu(40)
132154 property bool sideStageVisible: priv.sideStageAppId
133155
156+ // This indicates when the spreadView is active. That means, all the animations
157+ // are activated and tiles need to line up for the spread.
158+ readonly property bool active: shiftedContentX > 0 || spreadDragArea.dragging
159+
160+ // The flickable needs to fill the screen in order to get touch events all over.
161+ // However, we don't want to the user to be able to scroll back all the way. For
162+ // that, the beginning of the gesture starts with a negative value for contentX
163+ // so the flickable wants to pull it into the view already. "shift" tunes the
164+ // distance where to "lock" the content.
165+ readonly property real shift: width / 2
166+ readonly property real shiftedContentX: contentX + shift
167+
134168 // Phase of the animation:
135169 // 0: Starting from right edge, a new app (index 1) comes in from the right
136170 // 1: The app has reached the first snap position.
@@ -168,6 +202,12 @@ Item {
168202 }
169203 }
170204
205+ // In case the ApplicationManager already holds an app when starting up we're missing animations
206+ // Make sure we end up in the same state
207+ Component.onCompleted: {
208+ spreadView.contentX = -spreadView.shift
209+ }
210+
171211 property int nextInStack: {
172212 switch (state) {
173213 case "main":
@@ -224,31 +264,31 @@ Item {
224264 return "empty";
225265 }
226266
227− onContentXChanged: {
228− if (spreadView.phase == 0 && spreadView.contentX > spreadView.width * spreadView.positionMarker2) {
267+ onShiftedContentXChanged: {
268+ if (spreadView.phase == 0 && spreadView.shiftedContentX > spreadView.width * spreadView.positionMarker2) {
229269 spreadView.phase = 1;
230− } else if (spreadView.phase == 1 && spreadView.contentX > spreadView.width * spreadView.positionMarker4) {
270+ } else if (spreadView.phase == 1 && spreadView.shiftedContentX > spreadView.width * spreadView.positionMarker4) {
231271 spreadView.phase = 2;
232− } else if (spreadView.phase == 1 && spreadView.contentX < spreadView.width * spreadView.positionMarker2) {
272+ } else if (spreadView.phase == 1 && spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker2) {
233273 spreadView.phase = 0;
234274 }
235275 }
236276
237277 function snap() {
238− if (contentX < phase0Width) {
239− snapAnimation.targetContentX = 0;
278+ if (shiftedContentX < phase0Width) {
279+ snapAnimation.targetContentX = -shift;
240280 snapAnimation.start();
241− } else if (contentX < phase1Width) {
281+ } else if (shiftedContentX < phase1Width) {
242282 snapTo(1);
243283 } else {
244284 // Add 1 pixel to make sure we definitely hit positionMarker4 even with rounding errors of the animation.
245− snapAnimation.targetContentX = spreadView.width * spreadView.positionMarker4 + 1;
285+ snapAnimation.targetContentX = spreadView.width * spreadView.positionMarker4 + 1 - shift;
246286 snapAnimation.start();
247287 }
248288 }
249289 function snapTo(index) {
250290 spreadView.selectedIndex = index;
251− snapAnimation.targetContentX = 0;
291+ snapAnimation.targetContentX = -shift;
252292 snapAnimation.start();
253293 }
254294
@@ -261,12 +301,12 @@ Item {
261301 return index;
262302 }
263303
264− var isActive = app.appId == priv.mainStageAppId || app.appId == priv.sideStageAppId;
265− if (isActive && app.stage == ApplicationInfoInterface.MainStage) {
304+ var active = app.appId == priv.mainStageAppId || app.appId == priv.sideStageAppId;
305+ if (active && app.stage == ApplicationInfoInterface.MainStage) {
266306 // if this app is active, and its the MainStage, always put it to index 0
267307 return 0;
268308 }
269− if (isActive && app.stage == ApplicationInfoInterface.SideStage) {
309+ if (active && app.stage == ApplicationInfoInterface.SideStage) {
270310 if (!priv.mainStageAppId) {
271311 // Only have SS apps running. Put the active one at 0
272312 return 0;
@@ -309,7 +349,7 @@ Item {
309349
310350 SequentialAnimation {
311351 id: snapAnimation
312− property int targetContentX: 0
352+ property int targetContentX: -spreadView.shift
313353
314354 UbuntuNumberAnimation {
315355 target: spreadView
@@ -325,15 +365,14 @@ Item {
325365 spreadView.selectedIndex = -1;
326366 ApplicationManager.focusApplication(ApplicationManager.get(newIndex).appId);
327367 spreadView.phase = 0;
328− spreadView.contentX = 0;
368+ spreadView.contentX = -spreadView.shift;
329369 }
330370 }
331371 }
332372 }
333373
334− Rectangle {
374+ Item {
335375 id: spreadRow
336− color: "black"
337376 x: spreadView.contentX
338377 height: root.height
339378 width: spreadView.width + Math.max(spreadView.width, ApplicationManager.count * spreadView.tileDistance)
@@ -380,7 +419,7 @@ Item {
380419 MouseArea {
381420 id: sideStageDragHandleMouseArea
382421 anchors.fill: parent
383− enabled: spreadView.contentX == 0
422+ enabled: spreadView.shiftedContentX == 0
384423 property int startX
385424 property var gesturePoints: new Array()
386425
@@ -413,7 +452,7 @@ Item {
413452
414453 onRunningChanged: {
415454 if (!running) {
416− sideStageDragHandle.dragging = false;;
455+ sideStageDragHandle.dragging = false;
417456 }
418457 }
419458 }
@@ -427,17 +466,26 @@ Item {
427466 id: spreadTile
428467 height: spreadView.height
429468 width: model.stage == ApplicationInfoInterface.MainStage ? spreadView.width : spreadView.sideStageWidth
430− x: spreadView.width
431− z: spreadView.indexToZIndex(index)
432469 active: model.appId == priv.mainStageAppId || model.appId == priv.sideStageAppId
433− zIndex: z
470+ zIndex: spreadView.indexToZIndex(index)
434471 selected: spreadView.selectedIndex == index
435472 otherSelected: spreadView.selectedIndex >= 0 && !selected
436473 isInSideStage: priv.sideStageAppId == model.appId
437474 interactive: !spreadView.interactive && spreadView.phase === 0 && root.interactive
438475 swipeToCloseEnabled: spreadView.interactive
439476 maximizedAppTopMargin: root.maximizedAppTopMargin
440− dropShadow: spreadView.contentX > 0 || spreadDragArea.status == DirectionalDragArea.Undecided
477+ dragOffset: !isDash && model.appId == priv.mainStageAppId && root.inverseProgress > 0 ? root.inverseProgress : 0
478+
479+ readonly property bool isDash: model.appId == "unity8-dash"
480+
481+ // FIXME: A regular binding doesn't update any more after closing an app.
482+ // Using a Binding for now.
483+ Binding {
484+ target: spreadTile
485+ property: "z"
486+ value: (!spreadView.active && isDash && !active) ? -1 : spreadTile.zIndex
487+ }
488+ x: spreadView.width
441489
442490 property real behavioredZIndex: zIndex
443491 Behavior on behavioredZIndex {
@@ -458,7 +506,7 @@ Item {
458506 }
459507
460508 progress: {
461− var tileProgress = (spreadView.contentX - behavioredZIndex * spreadView.tileDistance) / spreadView.width;
509+ var tileProgress = (spreadView.shiftedContentX - behavioredZIndex * spreadView.tileDistance) / spreadView.width;
462510 // Some tiles (nextInStack, active) need to move directly from the beginning, normalize progress to immediately start at 0
463511 if ((index == spreadView.nextInStack && spreadView.phase < 2) || (active && spreadView.phase < 1)) {
464512 tileProgress += behavioredZIndex * spreadView.tileDistance / spreadView.width;
@@ -481,11 +529,7 @@ Item {
481529
482530 onClicked: {
483531 if (spreadView.phase == 2) {
484− if (ApplicationManager.focusedApplicationId == ApplicationManager.get(index).appId) {
485− spreadView.snapTo(index);
486− } else {
487− ApplicationManager.requestFocusApplication(ApplicationManager.get(index).appId);
488− }
532+ spreadView.snapTo(index);
489533 }
490534 }
491535
@@ -518,12 +562,12 @@ Item {
518562 onTouchXChanged: {
519563 if (!dragging) {
520564 spreadView.phase = 0;
521− spreadView.contentX = 0;
565+ spreadView.contentX = -spreadView.shift;
522566 }
523567
524− if (attachedToView) {
525− spreadView.contentX = -touchX + spreadDragArea.width;
526− if (spreadView.contentX > spreadView.phase0Width + spreadView.phase1Width / 2) {
568+ if (dragging && attachedToView) {
569+ spreadView.contentX = -touchX + spreadDragArea.width - spreadView.shift;
570+ if (spreadView.shiftedContentX > spreadView.phase0Width + spreadView.phase1Width / 2) {
527571 attachedToView = false;
528572 spreadView.snap();
529573 }
@@ -548,14 +592,14 @@ Item {
548592 var oneWayFlick = priv.evaluateOneWayFlick(gesturePoints);
549593 gesturePoints = [];
550594
551− if (oneWayFlick && spreadView.contentX < spreadView.positionMarker1 * spreadView.width) {
595+ if (oneWayFlick && spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
552596 // If it was a short one-way movement, do the Alt+Tab switch
553597 // no matter if we didn't cross positionMarker1 yet.
554598 spreadView.snapTo(spreadView.nextInStack);
555599 } else if (!dragging && attachedToView) {
556− if (spreadView.contentX < spreadView.width * spreadView.positionMarker1) {
600+ if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker1) {
557601 spreadView.snap();
558− } else if (spreadView.contentX < spreadView.width * spreadView.positionMarker2) {
602+ } else if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker2) {
559603 spreadView.snapTo(spreadView.nextInStack);
560604 } else {
561605 // otherwise snap to the closest snap position we can find
qml/Stages/TransformedSpreadDelegate.qml+6−5
@@ -46,9 +46,6 @@ SpreadDelegate {
4646 property real startDistance: units.gu(5)
4747 property real endDistance: units.gu(.5)
4848
49− // Hiding tiles when their progress is negative or reached the maximum
50− visible: progress >= 0 && progress < 1.7
51−
5249 onSelectedChanged: {
5350 if (selected) {
5451 priv.snapshot();
@@ -132,6 +129,10 @@ SpreadDelegate {
132129 // the selected tile, which is animated from the snapshotted position to be fullscreen.
133130
134131 readonly property real xTranslate: {
132+ if (!spreadView.active) {
133+ return 0;
134+ }
135+
135136 if (otherSelected) {
136137 if (spreadView.phase < 2 && index == 0) {
137138 return linearAnimation(selectedProgress, 0, selectedXTranslate,
@@ -183,7 +184,7 @@ SpreadDelegate {
183184 }
184185
185186 readonly property real angle: {
186− if (spreadView.focusChanging) {
187+ if (!spreadView.active) {
187188 return 0;
188189 }
189190
@@ -216,7 +217,7 @@ SpreadDelegate {
216217 }
217218
218219 readonly property real scale: {
219− if (spreadView.focusChanging) {
220+ if (!spreadView.active) {
220221 return 1;
221222 }
222223 if (priv.otherSelected) {
qml/Stages/TransformedTabletSpreadDelegate.qml+28
@@ -47,6 +47,11 @@ SpreadDelegate {
4747
4848 property bool isInSideStage: false
4949
50+ property int dragOffset: 0
51+
52+ dropShadow: spreadView.active ||
53+ (active && model.stage == ApplicationInfoInterface.MainStage && priv.xTranslate != 0)
54+
5055 onSelectedChanged: {
5156 if (selected) {
5257 priv.snapshot();
@@ -129,6 +134,16 @@ SpreadDelegate {
129134 return helperEasingCurve.value * (endValue - startValue) + startValue;
130135 }
131136
137+ Behavior on xTranslate {
138+ enabled: !spreadView.active &&
139+ !snapAnimation.running &&
140+ model.appId !== "unity8-dash" &&
141+ !spreadView.sideStageDragging
142+ UbuntuNumberAnimation {
143+ duration: UbuntuAnimation.FastDuration
144+ }
145+ }
146+
132147 property real xTranslate: {
133148 var newTranslate = 0;
134149
@@ -155,7 +170,12 @@ SpreadDelegate {
155170 if (spreadView.phase == 0 && shouldMoveAway) {
156171 newTranslate += linearAnimation(0, spreadView.positionMarker2, 0, -units.gu(4), root.animatedProgress);
157172 }
173+ newTranslate += root.dragOffset;
174+ }
175+ if (!spreadView.active && model.appId == "unity8-dash" && !root.active) {
176+ newTranslate -= root.width;
158177 }
178+
159179 if (nextInStack && spreadView.phase == 0) {
160180 if (model.stage == ApplicationInfoInterface.MainStage) {
161181 if (spreadView.sideStageVisible && root.progress > 0) {
@@ -205,6 +225,10 @@ SpreadDelegate {
205225 }
206226
207227 property real scale: {
228+ if (!spreadView.active) {
229+ return 1;
230+ }
231+
208232 if (otherSelected) {
209233 return selectedScale;
210234 }
@@ -249,6 +273,10 @@ SpreadDelegate {
249273 }
250274
251275 property real angle: {
276+ if (!spreadView.active) {
277+ return 0;
278+ }
279+
252280 if (otherSelected) {
253281 return selectedAngle;
254282 }
run_on_device.sh+1−3
@@ -2,7 +2,6 @@
22CODE_DIR=shell
33USER=phablet
44USER_ID=32011
5−PASSWORD=phablet
65PACKAGE=unity8
76BINARY=unity8
87TARGET_IP=${TARGET_IP-127.0.0.1}
@@ -13,7 +12,6 @@ SETUP=false
1312GDB=false
1413PINLOCK=false
1514KEYLOCK=false
16−SUDO="echo $PASSWORD | sudo -S"
1715NUM_JOBS='$(( `grep -c ^processor /proc/cpuinfo` + 1 ))'
1816FLIPPED=false
1917CHROOT_PREFIX="/data/ubuntu"
@@ -76,7 +74,7 @@ setup_adb_forwarding() {
7674
7775install_dependencies() {
7876 exec_with_adb apt-get update
79− exec_with_ssh $SUDO apt-get -y install build-essential rsync bzr ccache gdb ninja-build devscripts equivs unity-plugin-scopes
77+ exec_with_adb apt-get -y --force-yes install build-essential rsync bzr ccache gdb ninja-build devscripts equivs unity-plugin-scopes
8078}
8179
8280sync_code() {
src/CMakeLists.txt+1
@@ -35,3 +35,4 @@ install(TARGETS ${SHELL_APP}
3535 )
3636
3737add_subdirectory(Panel)
38+add_subdirectory(Dash)
src/Dash/CMakeLists.txtnew file+13
@@ -0,0 +1,13 @@
1+set(DASH_SRCS
2+ main.cpp
3+ ../MouseTouchAdaptor.cpp
4+)
5+
6+add_executable(unity8-dash ${DASH_SRCS})
7+
8+qt5_use_modules(unity8-dash Gui Qml Quick Test)
9+
10+# install binaries
11+install(TARGETS ${DASH_APP}
12+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
13+)
src/Dash/main.cppnew file+96
@@ -0,0 +1,96 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * Authors:
5+ * Michael Zanetti <michael.zanetti@canonical.com>
6+ *
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as published by
9+ * the Free Software Foundation; version 3.
10+ *
11+ * This program is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ * GNU General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public License
17+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18+ */
19+
20+#include <QtQuick/QQuickView>
21+#include <QtGui/QGuiApplication>
22+#include <QtQml/QQmlEngine>
23+#include <QtQml/QQmlContext>
24+#include <QDebug>
25+#include <QCommandLineParser>
26+#include <QLibrary>
27+#include <libintl.h>
28+
29+#include <paths.h>
30+#include "../MouseTouchAdaptor.h"
31+
32+int main(int argc, const char *argv[])
33+{
34+ QGuiApplication *application = new QGuiApplication(argc, (char**)argv);
35+
36+ QCommandLineParser parser;
37+ parser.setApplicationDescription("Description: Unity 8 Shell Dash");
38+ parser.addHelpOption();
39+
40+ QCommandLineOption mousetouchOption("mousetouch",
41+ "Allow the mouse to provide touch input");
42+ parser.addOption(mousetouchOption);
43+
44+ // FIXME Remove once we drop the need of the hint
45+ QCommandLineOption desktopFileHintOption("desktop_file_hint",
46+ "The desktop_file_hint option for QtMir", "hint_file");
47+ parser.addOption(desktopFileHintOption);
48+
49+ // Treat args with single dashes the same as arguments with two dashes
50+ // Ex: -fullscreen == --fullscreen
51+ parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
52+ parser.process(*application);
53+
54+ if (getenv("QT_LOAD_TESTABILITY")) {
55+ QLibrary testLib(QLatin1String("qttestability"));
56+ if (testLib.load()) {
57+ typedef void (*TasInitialize)(void);
58+ TasInitialize initFunction = (TasInitialize)testLib.resolve("qt_testability_init");
59+ if (initFunction) {
60+ initFunction();
61+ } else {
62+ qCritical("Library qttestability resolve failed!");
63+ }
64+ } else {
65+ qCritical("Library qttestability load failed!");
66+ }
67+ }
68+
69+ bindtextdomain("unity8", translationDirectory().toUtf8().data());
70+
71+ QQuickView* view = new QQuickView();
72+ view->setResizeMode(QQuickView::SizeRootObjectToView);
73+ view->setTitle("Unity Dash");
74+
75+ // You will need this if you want to interact with touch-only components using a mouse
76+ // Needed only when manually testing on a desktop.
77+ MouseTouchAdaptor *mouseTouchAdaptor = 0;
78+ if (parser.isSet(mousetouchOption)) {
79+ mouseTouchAdaptor = new MouseTouchAdaptor;
80+ application->installNativeEventFilter(mouseTouchAdaptor);
81+ }
82+
83+ QUrl source(::qmlDirectory()+"Dash/DashApplication.qml");
84+ prependImportPaths(view->engine(), ::overrideImportPaths());
85+ appendImportPaths(view->engine(), ::fallbackImportPaths());
86+
87+ view->setSource(source);
88+ view->show();
89+
90+ int result = application->exec();
91+
92+ delete view;
93+ delete application;
94+
95+ return result;
96+}
tests/autopilot/unity8/application_lifecycle/tests/test_application_lifecycle.py+1−1
@@ -106,7 +106,7 @@ class ApplicationLifecycleTests(tests.ApplicationLifeCycleTestCase):
106106 self.assert_current_focused_application(application_name)
107107
108108 self.main_window.show_dash_swiping()
109− self.assert_current_focused_application('')
109+ self.assert_current_focused_application('unity8-dash')
110110
111111 process_helpers.lock_unity()
112112 greeter = self.main_window.get_greeter()
tests/autopilot/unity8/application_lifecycle/tests/test_url_dispatcher.py+3−2
@@ -39,7 +39,8 @@ class URLDispatcherTestCase(tests.ApplicationLifeCycleTestCase):
3939 desktop_file_name = os.path.basename(desktop_file_path)
4040 application_name, _ = os.path.splitext(desktop_file_name)
4141
42− self.assertEqual('', self.main_window.get_current_focused_app_id())
42+ self.assertEqual(
43+ 'unity8-dash', self.main_window.get_current_focused_app_id())
4344 self.addCleanup(os.system, 'pkill qmlscene')
4445
4546 subprocess.check_call(
@@ -47,4 +48,4 @@ class URLDispatcherTestCase(tests.ApplicationLifeCycleTestCase):
4748 self.assert_current_focused_application(application_name)
4849
4950 self.main_window.show_dash_swiping()
50− self.assert_current_focused_application('')
51+ self.assert_current_focused_application('unity8-dash')
tests/autopilot/unity8/fixture_setup.pynew file+64
@@ -0,0 +1,64 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2014 Canonical
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License as published by
8+# the Free Software Foundation, either version 3 of the License, or
9+# (at your option) any later version.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+#
19+
20+import fixtures
21+from autopilot import introspection
22+
23+from unity8 import process_helpers
24+from unity8.shell import emulators
25+
26+
27+class LaunchDashApp(fixtures.Fixture):
28+
29+ """Fixture to launch the Dash app."""
30+
31+ def __init__(self, binary_path, variables):
32+ """Initialize an instance.
33+
34+ :param str binary_path: The path to the Dash app binary.
35+ :param variables: The variables to use when launching the app.
36+ :type variables: A dictionary.
37+
38+ """
39+ super(LaunchDashApp, self).__init__()
40+ self.binary_path = binary_path
41+ self.variables = variables
42+
43+ def setUp(self):
44+ """Launch the dash app when the fixture is used."""
45+ super(LaunchDashApp, self).setUp()
46+ self.addCleanup(self.stop_application)
47+ self.application_proxy = self.launch_application()
48+
49+ def launch_application(self):
50+ binary_arg = 'BINARY={}'.format(self.binary_path)
51+ testability_arg = 'QT_LOAD_TESTABILITY={}'.format(1)
52+ env_args = [
53+ '{}={}'.format(key, value) for key, value in self.variables.items()
54+ ]
55+ all_args = [binary_arg, testability_arg] + env_args
56+
57+ pid = process_helpers.start_job('unity8-dash', *all_args)
58+ return introspection.get_proxy_object_for_existing_process(
59+ pid=pid,
60+ emulator_base=emulators.UnityEmulatorBase,
61+ )
62+
63+ def stop_application(self):
64+ process_helpers.stop_job('unity8-dash')
tests/autopilot/unity8/process_helpers.py+89−20
@@ -36,6 +36,10 @@ from unity8.shell.emulators import main_window as main_window_emulator
3636logger = logging.getLogger(__name__)
3737
3838
39+class JobError(Exception):
40+ pass
41+
42+
3943class CannotAccessUnity(Exception):
4044 pass
4145
@@ -134,48 +138,113 @@ def restart_unity(*args):
134138 """
135139 status = _get_unity_status()
136140 if "start/" in status:
137− try:
138− output = subprocess.check_output(
139− ['/sbin/initctl', 'stop', 'unity8'])
140− logger.info(output)
141− except subprocess.CalledProcessError as e:
142− e.args += (
143− "Failed to stop running instance of unity8: %s" % e.output,
144− )
145− raise
141+ stop_job('unity8')
142+
143+ pid = start_job('unity8', *args)
144+ return _get_unity_proxy_object(pid)
146145
146+
147+def start_job(name, *args):
148+ """Start a job.
149+
150+ :param str name: The name of the job.
151+ :param args: The arguments to be used when starting the job.
152+ :return: The process id of the started job.
153+ :raises CalledProcessError: if the job failed to start.
154+
155+ """
156+ logger.info('Starting job {} with arguments {}.'.format(name, args))
157+ command = ['/sbin/initctl', 'start', name] + list(args)
147158 try:
148− command = ['/sbin/initctl', 'start', 'unity8'] + list(args)
149159 output = subprocess.check_output(
150160 command,
151161 stderr=subprocess.STDOUT,
152162 universal_newlines=True,
153163 )
154164 logger.info(output)
155− pid = _get_unity_pid()
165+ pid = get_job_pid(name)
156166 except subprocess.CalledProcessError as e:
157− e.args += ("Failed to start unity8: %s" % e.output,)
167+ e.args += ('Failed to start {}: {}.'.format(name, e.output),)
158168 raise
159169 else:
160− return _get_unity_proxy_object(pid)
170+ return pid
161171
162172
163−def _get_unity_status():
173+def get_job_pid(name):
174+ """Return the process id of a running job.
175+
176+ :param str name: The name of the job.
177+ :raises JobError: if the job is not running.
178+
179+ """
180+ status = get_job_status(name)
181+ if "start/" not in status:
182+ raise JobError('{} is not in the running state.'.format(name))
183+ return int(status.split()[-1])
184+
185+
186+def get_job_status(name):
187+ """Return the status of a job.
188+
189+ :param str name: The name of the job.
190+ :raises JobError: if it's not possible to get the status of the job.
191+
192+ """
164193 try:
165194 return subprocess.check_output([
166195 '/sbin/initctl',
167196 'status',
168− 'unity8'
197+ name
169198 ], universal_newlines=True)
199+ except subprocess.CalledProcessError as error:
200+ raise JobError(
201+ "Unable to get {}'s status: {}".format(name, error)
202+ )
203+
204+
205+def stop_job(name):
206+ """Stop a job.
207+
208+ :param str name: The name of the job.
209+ :raises CalledProcessError: if the job failed to stop.
210+
211+ """
212+ logger.info('Stoping job {}.'.format(name))
213+ command = ['/sbin/initctl', 'stop', name]
214+ try:
215+ output = subprocess.check_output(
216+ command,
217+ stderr=subprocess.STDOUT,
218+ universal_newlines=True,
219+ )
220+ logger.info(output)
170221 except subprocess.CalledProcessError as e:
171− raise CannotAccessUnity("Unable to get unity's status: %s" % str(e))
222+ e.args += ('Failed to stop {}: {}.'.format(name, e.output),)
223+ raise
224+
225+
226+def is_job_running(name):
227+ """Return True if the job is running. Otherwise, False.
228+
229+ :param str name: The name of the job.
230+ :raises JobError: if it's not possible to get the status of the job.
231+
232+ """
233+ return 'start/' in get_job_status(name)
234+
235+
236+def _get_unity_status():
237+ try:
238+ return get_job_status('unity8')
239+ except JobError as error:
240+ raise CannotAccessUnity(str(error))
172241
173242
174243def _get_unity_pid():
175− status = _get_unity_status()
176− if "start/" not in status:
177− raise CannotAccessUnity("Unity is not in the running state.")
178− return int(status.split()[-1])
244+ try:
245+ return get_job_pid('unity8')
246+ except JobError as error:
247+ raise CannotAccessUnity(str(error))
179248
180249
181250def _get_unity_proxy_object(pid):
tests/autopilot/unity8/shell/emulators/dash.py+22−9
@@ -31,6 +31,17 @@ from ubuntuuitoolkit import emulators as toolkit_emulators
3131logger = logging.getLogger(__name__)
3232
3333
34+class DashApp(object):
35+
36+ """Autopilot helper for the Dash app."""
37+
38+ def __init__(self, app_proxy):
39+ self.app_proxy = app_proxy
40+ self.main_view = self.app_proxy.select_single(
41+ toolkit_emulators.MainView)
42+ self.dash = self.main_view.select_single(Dash)
43+
44+
3445class Dash(emulators.UnityEmulatorBase):
3546 """An emulator that understands the Dash."""
3647
@@ -109,20 +120,22 @@ class Dash(emulators.UnityEmulatorBase):
109120 @autopilot_logging.log_action(logger.info)
110121 def _scroll_to_left_scope(self):
111122 original_index = self.dash_content_list.currentIndex
112− dashContent = self.select_single(objectName="dashContent")
113− start_x = dashContent.width / 3
114− stop_x = dashContent.width / 3 * 2
115− start_y = stop_y = dashContent.globalRect.y + 1
123+ dash_content = self.select_single(objectName="dashContent")
124+ x, y, width, height = dash_content.globalRect
125+ start_x = x + width / 3
126+ stop_x = x + width / 3 * 2
127+ start_y = stop_y = y + 1
116128 self.pointing_device.drag(start_x, start_y, stop_x, stop_y)
117129 self.dash_content_list.currentIndex.wait_for(original_index - 1)
118130
119131 @autopilot_logging.log_action(logger.info)
120132 def _scroll_to_right_scope(self):
121133 original_index = self.dash_content_list.currentIndex
122− dashContent = self.select_single(objectName="dashContent")
123− start_x = dashContent.width / 3 * 2
124− stop_x = dashContent.width / 3
125− start_y = stop_y = dashContent.globalRect.y + 1
134+ dash_content = self.select_single(objectName="dashContent")
135+ x, y, width, height = dash_content.globalRect
136+ start_x = x + width / 3 * 2
137+ stop_x = x + width / 3
138+ start_y = stop_y = y + 1
126139 self.pointing_device.drag(start_x, start_y, stop_x, stop_y)
127140 self.dash_content_list.currentIndex.wait_for(original_index + 1)
128141
@@ -137,7 +150,7 @@ class Dash(emulators.UnityEmulatorBase):
137150 headerContainer.contentY.wait_for(0)
138151 search_text_field = self._get_search_text_field()
139152 search_text_field.write(query)
140− current_header.select_single(objectName="searchIndicator").running.wait_for(False)
153+ self.select_single(objectName="processingIndicator").visible.wait_for(False)
141154
142155 def _get_search_text_field(self):
143156 page_header = self._get_current_page_header()
tests/autopilot/unity8/shell/emulators/greeter.py+4−1
@@ -17,6 +17,8 @@
1717# along with this program. If not, see <http://www.gnu.org/licenses/>.
1818#
1919
20+import ubuntuuitoolkit
21+
2022from unity8.shell.emulators import UnityEmulatorBase
2123
2224
@@ -37,4 +39,5 @@ class Greeter(UnityEmulatorBase):
3739 self.created.wait_for(False)
3840
3941 def get_prompt(self):
40− return self.select_single("TextField", objectName="passwordInput")
42+ return self.select_single(
43+ ubuntuuitoolkit.TextField, objectName='passwordInput')
tests/autopilot/unity8/shell/emulators/main_window.py+34−20
@@ -25,7 +25,6 @@ from autopilot import input
2525from unity8.shell import emulators
2626from unity8.shell.emulators.greeter import Greeter
2727from unity8.shell.emulators.hud import Hud
28−from unity8.shell.emulators.dash import Dash
2928from unity8.shell.emulators.launcher import Launcher
3029
3130logger = logging.getLogger(__name__)
@@ -61,9 +60,6 @@ class QQuickView(emulators.UnityEmulatorBase):
6160 def get_hud_edge_drag_area(self):
6261 return self.select_single(objectName="hudDragArea")
6362
64− def get_dash(self):
65− return self.select_single(Dash)
66−
6763 def get_bottombar(self):
6864 return self.select_single("Bottombar")
6965
@@ -76,12 +72,6 @@ class QQuickView(emulators.UnityEmulatorBase):
7672 objectName="pinPadLoader"
7773 )
7874
79− def get_pinPadButton(self, buttonId):
80− return self.select_single(
81− "PinPadButton",
82− objectName="pinPadButton%i" % buttonId
83− )
84−
8575 def get_lockscreen(self):
8676 return self.select_single("Lockscreen")
8777
@@ -117,20 +107,44 @@ class QQuickView(emulators.UnityEmulatorBase):
117107 @autopilot_logging.log_action(logger.info)
118108 def show_dash_swiping(self):
119109 """Show the dash swiping from the left."""
120− width = self.width
121− height = self.height
122− start_x = 0
123− start_y = height // 2
124− end_x = width
125− end_y = start_y
110+ x, y, width, height = self._get_shell().globalRect
111+ start_x = x
112+ end_x = x + width
113+ start_y = end_y = y + height // 2
126114
127115 self.pointing_device.drag(start_x, start_y, end_x, end_y)
128− return self.get_dash()
116+
117+ def _get_shell(self):
118+ return self.select_single('Shell')
129119
130120 def get_current_focused_app_id(self):
131121 """Return the id of the focused application."""
132− return self.select_single('Shell').focusedApplicationId
122+ return self._get_shell().focusedApplicationId
133123
134124 @autopilot_logging.log_action(logger.info)
135− def search(self, query):
136− self.get_dash().enter_search_query(query)
125+ def enter_pin_code(self, code):
126+ """Enter code 'code' into the single-pin lightdm pincode entry screen.
127+
128+ :param code: must be a string of numeric characters.
129+ :raises: TypeError if code is not a string.
130+ :raises: ValueError if code contains non-numeric characters.
131+
132+ """
133+ if not isinstance(code, str):
134+ raise TypeError(
135+ "'code' parameter must be a string, not %r."
136+ % type(code)
137+ )
138+ for num in code:
139+ if not num.isdigit():
140+ raise ValueError(
141+ "'code' parameter contains non-numeric characters."
142+ )
143+ self.pointing_device.click_object(
144+ self._get_pinpad_button(int(num)))
145+
146+ def _get_pinpad_button(self, button_id):
147+ return self.select_single(
148+ 'PinPadButton',
149+ objectName='pinPadButton{}'.format(button_id)
150+ )
tests/autopilot/unity8/shell/tests/__init__.py+141−48
@@ -24,6 +24,7 @@ try:
2424except ImportError:
2525 Gio = None
2626
27+from autopilot import introspection
2728from autopilot.platform import model
2829from autopilot.testcase import AutopilotTestCase
2930from autopilot.matchers import Eventually
@@ -34,6 +35,10 @@ import os.path
3435import subprocess
3536import sys
3637from testtools.matchers import Equals
38+from ubuntuuitoolkit import (
39+ fixture_setup as toolkit_fixtures,
40+ ubuntu_scenarios
41+)
3742
3843from unity8 import (
3944 get_lib_path,
@@ -42,9 +47,15 @@ from unity8 import (
4247 get_default_extra_mock_libraries,
4348 get_data_dirs
4449)
45−from unity8.process_helpers import restart_unity_with_testability
46−from unity8.shell.emulators import main_window as main_window_emulator
47−from unity8.shell.emulators.dash import Dash
50+from unity8 import (
51+ fixture_setup,
52+ process_helpers
53+)
54+from unity8.shell import emulators
55+from unity8.shell.emulators import (
56+ dash as dash_helpers,
57+ main_window as main_window_emulator,
58+)
4859
4960
5061logger = logging.getLogger(__name__)
@@ -78,6 +89,26 @@ def _get_device_emulation_scenarios(devices='All'):
7889 return [native]
7990
8091
92+def is_unity7_running():
93+ """Return True if Unity7 is running. Otherwise, return False."""
94+ return (
95+ Gio is not None and
96+ UNITYSHELL_GSETTINGS_SCHEMA in
97+ Gio.Settings.list_relocatable_schemas()
98+ )
99+
100+
101+def get_qml_import_path_with_mock():
102+ """Return the QML2_IMPORT_PATH value with the mock path prepended."""
103+ qml_import_path = [get_mocks_library_path()]
104+ if os.getenv('QML2_IMPORT_PATH') is not None:
105+ qml_import_path.append(os.getenv('QML2_IMPORT_PATH'))
106+
107+ qml_import_path = ':'.join(qml_import_path)
108+ logger.info("New QML2 import path: %s", qml_import_path)
109+ return qml_import_path
110+
111+
81112class UnityTestCase(AutopilotTestCase):
82113
83114 """A test case base class for the Unity shell tests."""
@@ -118,23 +149,8 @@ class UnityTestCase(AutopilotTestCase):
118149
119150 def setUp(self):
120151 super(UnityTestCase, self).setUp()
121− if (Gio is not None and
122− UNITYSHELL_GSETTINGS_SCHEMA in
123− Gio.Settings.list_relocatable_schemas()):
124−
125− # Hide Unity launcher
126− self._unityshell_schema = Gio.Settings.new_with_path(
127− UNITYSHELL_GSETTINGS_SCHEMA,
128− UNITYSHELL_GSETTINGS_PATH,
129− )
130− self._launcher_hide_mode = self._unityshell_schema.get_int(
131− UNITYSHELL_LAUNCHER_KEY,
132− )
133− self._unityshell_schema.set_int(
134− UNITYSHELL_LAUNCHER_KEY,
135− UNITYSHELL_LAUNCHER_MODE,
136− )
137− self.addCleanup(self._reset_launcher)
152+ if is_unity7_running():
153+ self.useFixture(toolkit_fixtures.HideUnity7Launcher())
138154
139155 self._proxy = None
140156 self._lightdm_mock_type = None
@@ -151,13 +167,6 @@ class UnityTestCase(AutopilotTestCase):
151167 self.touch = Touch.create()
152168 self._setup_display_details()
153169
154− def _reset_launcher(self):
155− """Reset Unity launcher hide mode"""
156− self._unityshell_schema.set_int(
157− UNITYSHELL_LAUNCHER_KEY,
158− self._launcher_hide_mode,
159− )
160−
161170 def _setup_display_details(self):
162171 scale_divisor = self._determine_geometry()
163172 self._setup_grid_size(scale_divisor)
@@ -263,7 +272,9 @@ class UnityTestCase(AutopilotTestCase):
263272 self.patch_lightdm_mock()
264273
265274 if self._qml_mock_enabled:
266− self._setup_extra_mock_environment_patch()
275+ self._environment['QML2_IMPORT_PATH'] = (
276+ get_qml_import_path_with_mock()
277+ )
267278
268279 if self._data_dirs_mock_enabled:
269280 self._patch_data_dirs()
@@ -292,9 +303,14 @@ class UnityTestCase(AutopilotTestCase):
292303
293304 # Ensure that the dash is visible before we return:
294305 logger.debug("Unity started, waiting for it to be ready.")
295− self.assertUnityReady()
306+ self.wait_for_unity()
296307 logger.debug("Unity loaded and ready.")
297308
309+ if model() == 'Desktop':
310+ # On desktop, close the dash because it's opened in a separate
311+ # window and it gets in the way.
312+ process_helpers.stop_job('unity8-dash')
313+
298314 return app_proxy
299315
300316 def _launch_unity_with_upstart(self, binary_path, args):
@@ -308,7 +324,7 @@ class UnityTestCase(AutopilotTestCase):
308324
309325 self.addCleanup(self._cleanup_launching_upstart_unity)
310326
311− return restart_unity_with_testability(*all_args)
327+ return process_helpers.restart_unity_with_testability(*all_args)
312328
313329 def _cleanup_launching_upstart_unity(self):
314330 logger.info("Stopping unity")
@@ -353,15 +369,6 @@ class UnityTestCase(AutopilotTestCase):
353369 )
354370 return lightdm_mock_path
355371
356− def _setup_extra_mock_environment_patch(self):
357− qml_import_path = [get_mocks_library_path()]
358− if os.getenv('QML2_IMPORT_PATH') is not None:
359− qml_import_path.append(os.getenv('QML2_IMPORT_PATH'))
360−
361− qml_import_path = ':'.join(qml_import_path)
362− logger.info("New QML2 import path: %s", qml_import_path)
363− self._environment['QML2_IMPORT_PATH'] = qml_import_path
364−
365372 def _set_proxy(self, proxy):
366373 """Keep a copy of the proxy object, so we can use it to get common
367374 parts of the shell later on.
@@ -373,10 +380,80 @@ class UnityTestCase(AutopilotTestCase):
373380 def _clear_proxy(self):
374381 self._proxy = None
375382
376− def assertUnityReady(self):
377− dash = self.get_dash()
378− home_scope = dash.get_scope('clickscope')
383+ def wait_for_unity(self):
384+ greeter_content_loader = self.main_window.wait_select_single(
385+ objectName='greeterContentLoader')
386+ greeter_content_loader.progress.wait_for(1)
387+
388+ def get_dash(self):
389+ pid = process_helpers.get_job_pid('unity8-dash')
390+ dash_proxy = introspection.get_proxy_object_for_existing_process(
391+ pid=pid,
392+ emulator_base=emulators.UnityEmulatorBase,
393+ )
394+ dash_app = dash_helpers.DashApp(dash_proxy)
395+ return dash_app.dash
396+
397+ @property
398+ def main_window(self):
399+ return self._proxy.select_single(main_window_emulator.QQuickView)
379400
401+
402+class DashBaseTestCase(AutopilotTestCase):
403+
404+ scenarios = ubuntu_scenarios.get_device_simulation_scenarios()
405+ qml_mock_enabled = True
406+ environment = {}
407+
408+ def setUp(self):
409+ super(DashBaseTestCase, self).setUp()
410+
411+ if is_unity7_running():
412+ self.useFixture(toolkit_fixtures.HideUnity7Launcher())
413+
414+ if model() != 'Desktop':
415+ # On the phone, we need unity to be running and unlocked.
416+ self.addCleanup(process_helpers.stop_job, 'unity8')
417+ process_helpers.restart_unity_with_testability()
418+ process_helpers.unlock_unity()
419+
420+ self.ensure_dash_not_running()
421+
422+ if self.qml_mock_enabled:
423+ self.environment['QML2_IMPORT_PATH'] = (
424+ get_qml_import_path_with_mock()
425+ )
426+
427+ if self.should_simulate_device():
428+ # This sets the grid units, so it should be called before launching
429+ # the app.
430+ self.simulate_device()
431+
432+ binary_path = get_binary_path('unity8-dash')
433+ dash_proxy = self.launch_dash(binary_path, self.environment)
434+
435+ if self.should_simulate_device():
436+ # XXX Currently we have no way to launch the application with a
437+ # specific size, so we must resize it after it's launched.
438+ # --elopio - 2014-06-25
439+ self.resize_window()
440+
441+ self.dash_app = dash_helpers.DashApp(dash_proxy)
442+ self.dash = self.dash_app.dash
443+ self.wait_for_dash()
444+
445+ def ensure_dash_not_running(self):
446+ if process_helpers.is_job_running('unity8-dash'):
447+ process_helpers.stop_job('unity8-dash')
448+
449+ def launch_dash(self, binary_path, variables):
450+ launch_dash_app_fixture = fixture_setup.LaunchDashApp(
451+ binary_path, variables)
452+ self.useFixture(launch_dash_app_fixture)
453+ return launch_dash_app_fixture.application_proxy
454+
455+ def wait_for_dash(self):
456+ home_scope = self.dash.get_scope('clickscope')
380457 # FIXME! There is a huge timeout here for when we're doing CI on
381458 # VMs. See lp:1203715
382459 self.assertThat(
@@ -385,10 +462,26 @@ class UnityTestCase(AutopilotTestCase):
385462 )
386463 self.assertThat(home_scope.isCurrent, Eventually(Equals(True)))
387464
388− def get_dash(self):
389− dash = self._proxy.wait_select_single(Dash)
390− return dash
465+ def should_simulate_device(self):
466+ return (hasattr(self, 'app_width') and hasattr(self, 'app_height') and
467+ hasattr(self, 'grid_unit_px'))
391468
392− @property
393− def main_window(self):
394− return self._proxy.select_single(main_window_emulator.QQuickView)
469+ def simulate_device(self):
470+ simulate_device_fixture = self.useFixture(
471+ toolkit_fixtures.SimulateDevice(
472+ self.app_width, self.app_height, self.grid_unit_px))
473+ self.app_width = simulate_device_fixture.app_width
474+ self.app_height = simulate_device_fixture.app_height
475+
476+ def resize_window(self):
477+ application = self.process_manager.get_running_applications()[0]
478+ window = application.get_windows()[0]
479+ window.resize(self.app_width, self.app_height)
480+
481+ def get_window_size():
482+ _, _, window_width, window_height = window.geometry
483+ return window_width, window_height
484+
485+ self.assertThat(
486+ get_window_size,
487+ Eventually(Equals((self.app_width, self.app_height))))
tests/autopilot/unity8/shell/tests/test_emulators.py+7−18
@@ -47,24 +47,13 @@ class MainWindowTestCase(tests.UnityTestCase):
4747 unity_proxy = self.launch_unity()
4848 process_helpers.unlock_unity(unity_proxy)
4949
50− def test_search(self):
51− self.main_window.search('Test')
52− text_field = self.main_window.get_dash()._get_search_text_field()
53− self.assertEqual(text_field.text, 'Test')
54−
55−
56−class DashBaseTestCase(tests.UnityTestCase):
57−
58− scenarios = tests._get_device_emulation_scenarios()
5950
60− def setUp(self):
61− super(DashBaseTestCase, self).setUp()
62− unity_proxy = self.launch_unity()
63− process_helpers.unlock_unity(unity_proxy)
64− self.dash = self.main_window.get_dash()
51+class DashEmulatorTestCase(tests.DashBaseTestCase):
6552
66−
67−class DashEmulatorTestCase(DashBaseTestCase):
53+ def test_search(self):
54+ self.dash.enter_search_query('Test')
55+ text_field = self.dash._get_search_text_field()
56+ self.assertEqual(text_field.text, 'Test')
6857
6958 def test_open_unexisting_scope(self):
7059 scope_name = 'unexisting'
@@ -145,7 +134,7 @@ class DashEmulatorTestCase(DashBaseTestCase):
145134 self.assertIsInstance(scope, dash_emulators.GenericScopeView)
146135
147136
148−class GenericScopeViewEmulatorTestCase(DashBaseTestCase):
137+class GenericScopeViewEmulatorTestCase(tests.DashBaseTestCase):
149138
150139 def setUp(self):
151140 # Set up the fake scopes before launching unity.
@@ -159,7 +148,7 @@ class GenericScopeViewEmulatorTestCase(DashBaseTestCase):
159148 self.assertTrue(preview.isCurrent)
160149
161150
162−class DashAppsEmulatorTestCase(DashBaseTestCase):
151+class DashAppsEmulatorTestCase(tests.DashBaseTestCase):
163152
164153 available_applications = [
165154 'Title.2.0', 'Title.2.1', 'Title.2.2', 'Title.2.3', 'Title.2.4',
tests/autopilot/unity8/shell/tests/test_lock_screen.py+7−47
@@ -51,7 +51,7 @@ class TestLockscreen(UnityTestCase):
5151 if greeter.narrowMode:
5252 unlock_unity(unity_proxy)
5353 lockscreen = self._wait_for_lockscreen()
54− self._enter_pincode("1234")
54+ self.main_window.enter_pin_code("1234")
5555 self.assertThat(lockscreen.shown, Eventually(Equals(False)))
5656 else:
5757 self._enter_prompt_passphrase("1234")
@@ -81,7 +81,7 @@ class TestLockscreen(UnityTestCase):
8181 if greeter.narrowMode:
8282 unlock_unity(unity_proxy)
8383 lockscreen = self._wait_for_lockscreen()
84− self._enter_pincode("4321")
84+ self.main_window.enter_pin_code("4321")
8585 pinentryField = self.main_window.get_pinentryField()
8686 self.assertThat(pinentryField.text, Eventually(Equals("")))
8787 self.assertThat(lockscreen.shown, Eventually(Equals(True)))
@@ -118,28 +118,6 @@ class TestLockscreen(UnityTestCase):
118118 self.assertThat(lockscreen.shown, Eventually(Equals(True)))
119119 return lockscreen
120120
121− def _enter_pincode(self, code):
122− """Enter code 'code' into the single-pin lightdm pincode entry
123− screen.
124−
125− :param code: must be a string of numeric characters.
126− :raises: TypeError if code is not a string.
127− :raises: ValueError if code contains non-numeric characters.
128−
129− """
130−
131− if not isinstance(code, basestring):
132− raise TypeError(
133− "'code' parameter must be a string, not %r."
134− % type(code)
135− )
136− for num in code:
137− if not num.isdigit():
138− raise ValueError(
139− "'code' parameter contains non-numeric characters."
140− )
141− self.touch.tap_object(self.main_window.get_pinPadButton(int(num)))
142−
143121 def _enter_pin_passphrase(self, passphrase):
144122 """Enter the password specified in 'passphrase' into the password entry
145123 field of the pin lock screen.
@@ -148,18 +126,15 @@ class TestLockscreen(UnityTestCase):
148126 :raises: TypeError if passphrase is not a string.
149127
150128 """
151− if not isinstance(passphrase, basestring):
129+ if not isinstance(passphrase, str):
152130 raise TypeError(
153131 "'passphrase' parameter must be a string, not %r."
154132 % type(passphrase)
155133 )
156134
157− pinentryField = self.main_window.get_pinentryField()
158− self.touch.tap_object(pinentryField)
159− self.assertThat(pinentryField.activeFocus, Eventually(Equals(True)))
160− for character in passphrase:
161− self._type_character(character, pinentryField)
162− logger.debug("Typed passphrase: %s", pinentryField.text)
135+ pin_entry_field = self.main_window.get_pinentryField()
136+ pin_entry_field.write(passphrase)
137+ logger.debug("Typed passphrase: %s", pin_entry_field.text)
163138 self.keyboard.type("\n")
164139
165140 def _enter_prompt_passphrase(self, passphrase):
@@ -177,21 +152,6 @@ class TestLockscreen(UnityTestCase):
177152 )
178153
179154 prompt = self.main_window.get_greeter().get_prompt()
180− self.touch.tap_object(prompt)
181− self.assertThat(prompt.activeFocus, Eventually(Equals(True)))
182− for character in passphrase:
183− self._type_character(character, prompt)
155+ prompt.write(passphrase)
184156 logger.debug("Typed passphrase: %s", prompt.text)
185157 self.keyboard.type("\n")
186−
187− def _type_character(self, character, prompt, retries=5):
188− current_text = prompt.text
189− self.keyboard.type(character)
190− try:
191− self.assertThat(
192− prompt.text, Eventually(Equals(current_text + character)))
193− except AssertionError:
194− if retries > 0:
195− self._type_character(character, prompt, retries-1)
196− else:
197− raise
tests/autopilot/unity8/shell/tests/test_notifications.py+12−7
@@ -154,7 +154,7 @@ class InteractiveNotificationBase(NotificationsBase):
154154 'Notification', objectName='notification1')
155155 notification = get_notification()
156156
157− self.touch.tap_object(
157+ notification.pointing_device.click_object(
158158 notification.select_single(objectName="interactiveArea")
159159 )
160160
@@ -201,11 +201,14 @@ class InteractiveNotificationBase(NotificationsBase):
201201 'Notification', objectName='notification1')
202202 notification = get_notification()
203203 self._assert_notification(notification, summary, body, True, True, 1.0)
204− initial_height = notification.height
205− self.touch.tap_object(notification.select_single(objectName="combobutton_dropdown"))
206− self.assertThat(notification.select_single(objectName="button2").expanded, Eventually(Equals(True)))
204+ notification.pointing_device.click_object(
205+ notification.select_single(objectName="combobutton_dropdown"))
206+ self.assertThat(
207+ notification.select_single(objectName="button2").expanded,
208+ Eventually(Equals(True)))
207209 time.sleep(2)
208− self.touch.tap_object(notification.select_single(objectName="button4"))
210+ notification.pointing_device.click_object(
211+ notification.select_single(objectName="button4"))
209212 self.assert_notification_action_id_was_called("action_decline_4")
210213
211214 def test_modal_sd_without_greeter(self):
@@ -249,7 +252,8 @@ class InteractiveNotificationBase(NotificationsBase):
249252 notification = get_notification()
250253 self._assert_notification(
251254 notification, summary, body, True, False, 1.0)
252− self.touch.tap_object(notification.select_single(objectName="button0"))
255+ notification.pointing_device.click_object(
256+ notification.select_single(objectName="button0"))
253257 self.assert_notification_action_id_was_called("action_accept")
254258
255259 def test_modal_sd_with_greeter(self):
@@ -291,7 +295,8 @@ class InteractiveNotificationBase(NotificationsBase):
291295 notification = get_notification()
292296 self._assert_notification(
293297 notification, summary, body, True, False, 1.0)
294− self.touch.tap_object(notification.select_single(objectName="button0"))
298+ notification.pointing_device.click_object(
299+ notification.select_single(objectName="button0"))
295300 self.assert_notification_action_id_was_called("action_accept")
296301
297302 def _create_interactive_notification(
tests/autopilot/unity8/shell/tests/test_upstart.py+2−2
@@ -119,7 +119,7 @@ class UpstartIntegrationTests(UnityTestCase):
119119 self._set_proxy()
120120
121121 logger.debug("Unity started, waiting for it to be ready.")
122− self.assertUnityReady()
122+ self.wait_for_unity()
123123 logger.debug("Unity loaded and ready.")
124124
125125 def test_expect_sigstop(self):
@@ -137,5 +137,5 @@ class UpstartIntegrationTests(UnityTestCase):
137137
138138 logger.debug("Unity started, waiting for it to be ready.")
139139 self._set_proxy()
140− self.assertUnityReady()
140+ self.wait_for_unity()
141141 logger.debug("Unity loaded and ready.")
tests/mocks/Unity/Application/ApplicationInfo.cpp−62
@@ -31,8 +31,6 @@ ApplicationInfo::ApplicationInfo(const QString &appId, QObject *parent)
3131 , m_state(Starting)
3232 , m_focused(false)
3333 , m_fullscreen(false)
34− , m_windowItem(0)
35− , m_windowComponent(0)
3634 , m_parentItem(0)
3735 , m_surface(0)
3836{
@@ -45,8 +43,6 @@ ApplicationInfo::ApplicationInfo(QObject *parent)
4543 , m_state(Starting)
4644 , m_focused(false)
4745 , m_fullscreen(false)
48− , m_windowItem(0)
49− , m_windowComponent(0)
5046 , m_parentItem(0)
5147 , m_surface(0)
5248{
@@ -61,12 +57,6 @@ ApplicationInfo::~ApplicationInfo()
6157 }
6258}
6359
64−void ApplicationInfo::onWindowComponentStatusChanged(QQmlComponent::Status status)
65−{
66− if (status == QQmlComponent::Ready && !m_windowItem)
67− doCreateWindowItem();
68−}
69−
7060void ApplicationInfo::onStateChanged(State state)
7161{
7262 if (state == ApplicationInfo::Running) {
@@ -108,55 +98,3 @@ void ApplicationInfo::setSurface(MirSurfaceItem* surface)
10898 Q_EMIT surfaceChanged(m_surface);
10999 SurfaceManager::singleton()->registerSurface(m_surface);
110100}
111−
112−void ApplicationInfo::createWindowComponent()
113−{
114− // The assumptions I make here really should hold.
115− QQuickView *quickView =
116− qobject_cast<QQuickView*>(QGuiApplication::topLevelWindows()[0]);
117−
118− QQmlEngine *engine = quickView->engine();
119−
120− m_windowComponent = new QQmlComponent(engine, this);
121− m_windowComponent->setData(m_windowQml.toLatin1(), QUrl());
122−}
123−
124−void ApplicationInfo::doCreateWindowItem()
125−{
126− m_windowItem = qobject_cast<QQuickItem *>(m_windowComponent->create());
127− m_windowItem->setParentItem(m_parentItem);
128−}
129−
130−void ApplicationInfo::createWindowItem()
131−{
132− if (!m_windowComponent)
133− createWindowComponent();
134−
135− // only create the windowItem once the component is ready
136− if (!m_windowComponent->isReady()) {
137− connect(m_windowComponent, &QQmlComponent::statusChanged,
138− this, &ApplicationInfo::onWindowComponentStatusChanged);
139− } else {
140− doCreateWindowItem();
141− }
142−}
143−
144−void ApplicationInfo::showWindow(QQuickItem *parent)
145−{
146− m_parentItem = parent;
147−
148− if (!m_windowItem)
149− createWindowItem();
150−
151− if (m_windowItem) {
152− m_windowItem->setVisible(true);
153− }
154−}
155−
156−void ApplicationInfo::hideWindow()
157−{
158− if (!m_windowItem)
159− return;
160−
161− m_windowItem->setVisible(false);
162−}
tests/mocks/Unity/Application/ApplicationInfo.h−10
@@ -88,22 +88,12 @@ public:
8888Q_SIGNALS:
8989 void surfaceChanged(MirSurfaceItem*);
9090
91−public:
92− void showWindow(QQuickItem *parent);
93− void hideWindow();
94−
9591private Q_SLOTS:
96− void onWindowComponentStatusChanged(QQmlComponent::Status status);
9792 void onStateChanged(State state);
9893
9994 void createSurface();
10095
10196private:
102− void createWindowItem();
103− void doCreateWindowItem();
104− void createWindowComponent();
105− QQuickItem *m_windowItem;
106− QQmlComponent *m_windowComponent;
10797 QQuickItem *m_parentItem;
10898 MirSurfaceItem* m_surface;
10999};
tests/mocks/Unity/Application/ApplicationManager.cpp+17−147
@@ -48,16 +48,14 @@ ApplicationManager *ApplicationManager::singleton()
4848ApplicationManager::ApplicationManager(QObject *parent)
4949 : ApplicationManagerInterface(parent)
5050 , m_suspended(false)
51− , m_mainStageComponent(0)
52− , m_mainStage(0)
53− , m_sideStageComponent(0)
54− , m_sideStage(0)
55− , m_rightMargin(0)
5651{
5752 m_roleNames.insert(RoleSurface, "surface");
5853 m_roleNames.insert(RoleFullscreen, "fullscreen");
5954
6055 buildListOfAvailableApplications();
56+
57+ startApplication("unity8-dash");
58+ focusApplication("unity8-dash");
6159}
6260
6361ApplicationManager::~ApplicationManager()
@@ -136,7 +134,6 @@ void ApplicationManager::add(ApplicationInfo *application) {
136134 Q_EMIT applicationAdded(application->appId());
137135 Q_EMIT countChanged();
138136 if (count() == 1) Q_EMIT emptyChanged(isEmpty()); // was empty but not anymore
139− Q_EMIT focusRequested(application->appId());
140137
141138 connect(application, &ApplicationInfo::surfaceChanged, this, [application, this]() {
142139 QModelIndex appIndex = findIndex(application);
@@ -222,6 +219,10 @@ ApplicationInfo* ApplicationManager::startApplication(const QString &appId,
222219
223220bool ApplicationManager::stopApplication(const QString &appId)
224221{
222+ if (appId == "unity8-dash") {
223+ return false;
224+ }
225+
225226 ApplicationInfo *application = findApplication(appId);
226227 if (application == nullptr)
227228 return false;
@@ -294,39 +295,25 @@ bool ApplicationManager::focusApplication(const QString &appId)
294295 for (ApplicationInfo *app : m_runningApplications) {
295296 if (app->focused() && app->stage() == ApplicationInfo::MainStage) {
296297 app->setFocused(false);
297− app->hideWindow();
298298 app->setState(ApplicationInfo::Suspended);
299299 }
300300 }
301301
302302 // focus this app
303303 application->setFocused(true);
304− if (!m_mainStage)
305− createMainStage();
306304 application->setState(ApplicationInfo::Running);
307− application->showWindow(m_mainStage);
308− m_mainStage->setZ(-1000);
309− if (m_sideStage)
310− m_sideStage->setZ(-2000);
311305 } else if (application->stage() == ApplicationInfo::SideStage) {
312306 // unfocus currently focused sidestage app
313307 for (ApplicationInfo *app : m_runningApplications) {
314308 if (app->focused() && app->stage() == ApplicationInfo::SideStage) {
315309 app->setFocused(false);
316− app->hideWindow();
317310 app->setState(ApplicationInfo::Suspended);
318311 }
319312 }
320313
321314 // focus this app
322315 application->setFocused(true);
323− if (!m_sideStage)
324− createSideStage();
325316 application->setState(ApplicationInfo::Running);
326− application->showWindow(m_sideStage);
327− m_sideStage->setZ(-1000);
328− if (m_mainStage)
329− m_mainStage->setZ(-2000);
330317 }
331318
332319 // move app to top of stack
@@ -337,18 +324,14 @@ bool ApplicationManager::focusApplication(const QString &appId)
337324
338325bool ApplicationManager::requestFocusApplication(const QString &appId)
339326{
340− if (appId != focusedApplicationId()) {
341− QMetaObject::invokeMethod(this, "focusRequested", Qt::QueuedConnection, Q_ARG(QString, appId));
342− return true;
343− }
344− return false;
327+ QMetaObject::invokeMethod(this, "focusRequested", Qt::QueuedConnection, Q_ARG(QString, appId));
328+ return true;
345329}
346330
347331void ApplicationManager::unfocusCurrentApplication()
348332{
349333 for (ApplicationInfo *app : m_runningApplications) {
350334 if (app->focused()) {
351− app->hideWindow();
352335 app->setFocused(false);
353336 }
354337 }
@@ -357,42 +340,6 @@ void ApplicationManager::unfocusCurrentApplication()
357340
358341void ApplicationManager::generateQmlStrings(ApplicationInfo *application)
359342{
360− // TODO: Is there a better way of solving this fullscreen vs. regular
361− // application height?
362− QString topMargin;
363− if (application->fullscreen()) {
364− topMargin.append("0");
365− } else {
366− // Taken from Panel.panelHeight
367− topMargin.append("units.gu(3) + units.dp(2)");
368− }
369−
370− QString windowQml = QString(
371− "import QtQuick 2.0\n"
372− "Image {\n"
373− " anchors.fill: parent\n"
374− " anchors.topMargin: %1\n"
375− " anchors.rightMargin: %2\n"
376− " source: \"file://%3/Dash/graphics/phone/screenshots/%4.png\"\n"
377− " smooth: true\n"
378− " fillMode: Image.PreserveAspectCrop\n"
379− "}").arg(topMargin)
380− .arg(m_rightMargin)
381− .arg(qmlDirectory())
382− .arg(application->icon().toString());
383− application->setWindowQml(windowQml);
384−
385− QString imageQml = QString(
386− "import QtQuick 2.0\n"
387− "Image {\n"
388− " anchors.fill: parent\n"
389− " source: \"file://%1/Dash/graphics/phone/screenshots/%2.png\"\n"
390− " smooth: true\n"
391− " fillMode: Image.PreserveAspectCrop\n"
392− "}").arg(qmlDirectory())
393− .arg(application->icon().toString());
394− application->setImageQml(imageQml);
395−
396343 application->setScreenshot(QString("file://%1/Dash/graphics/phone/screenshots/%2@12.png").arg(qmlDirectory())
397344 .arg(application->icon().toString()));
398345}
@@ -401,6 +348,14 @@ void ApplicationManager::buildListOfAvailableApplications()
401348{
402349 ApplicationInfo *application;
403350
351+ application = new ApplicationInfo(this);
352+ application->setAppId("unity8-dash");
353+ application->setName("Unity 8 Mock Dash");
354+ application->setIcon(QUrl("unity8-dash"));
355+ application->setStage(ApplicationInfo::MainStage);
356+ generateQmlStrings(application);
357+ m_availableApplications.append(application);
358+
404359 application = new ApplicationInfo(this);
405360 application->setAppId("dialer-app");
406361 application->setName("Dialer");
@@ -521,78 +476,6 @@ void ApplicationManager::buildListOfAvailableApplications()
521476 m_availableApplications.append(application);
522477}
523478
524−void ApplicationManager::createMainStageComponent()
525−{
526− // The assumptions I make here really should hold.
527− QQuickView *quickView =
528− qobject_cast<QQuickView*>(QGuiApplication::topLevelWindows()[0]);
529−
530− QQmlEngine *engine = quickView->engine();
531−
532− m_mainStageComponent = new QQmlComponent(engine, this);
533− QString mainStageQml =
534− "import QtQuick 2.0\n"
535− "Rectangle {\n"
536− " anchors.fill: parent\n"
537− " color: 'black'\n"
538− " z: -2000\n"
539− "}\n";
540− m_mainStageComponent->setData(mainStageQml.toLatin1(), QUrl());
541−}
542−
543−void ApplicationManager::createMainStage()
544−{
545− if (!m_mainStageComponent)
546− createMainStageComponent();
547−
548− // The assumptions I make here really should hold.
549− QQuickView *quickView =
550− qobject_cast<QQuickView*>(QGuiApplication::topLevelWindows()[0]);
551−
552− QQuickItem *shell = quickView->rootObject();
553−
554− m_mainStage = qobject_cast<QQuickItem *>(m_mainStageComponent->create());
555− m_mainStage->setParentItem(shell);
556−}
557−
558−void ApplicationManager::createSideStageComponent()
559−{
560− // The assumptions I make here really should hold.
561− QQuickView *quickView =
562− qobject_cast<QQuickView*>(QGuiApplication::topLevelWindows()[0]);
563−
564− QQmlEngine *engine = quickView->engine();
565−
566− m_sideStageComponent = new QQmlComponent(engine, this);
567− QString sideStageQml =
568− "import QtQuick 2.0\n"
569− "import Ubuntu.Components 0.1\n"
570− "Item {\n"
571− " width: units.gu(40)\n" // from SideStage in Shell.qml
572− " anchors.top: parent.top\n"
573− " anchors.bottom: parent.bottom\n"
574− " anchors.right: parent.right\n"
575− " z: -1000\n"
576− "}\n";
577− m_sideStageComponent->setData(sideStageQml.toLatin1(), QUrl());
578−}
579−
580−void ApplicationManager::createSideStage()
581−{
582− if (!m_sideStageComponent)
583− createSideStageComponent();
584−
585− // The assumptions I make here really should hold.
586− QQuickView *quickView =
587− qobject_cast<QQuickView*>(QGuiApplication::topLevelWindows()[0]);
588−
589− QQuickItem *shell = quickView->rootObject();
590−
591− m_sideStage = qobject_cast<QQuickItem *>(m_sideStageComponent->create());
592− m_sideStage->setParentItem(shell);
593− m_sideStage->setFlag(QQuickItem::ItemHasContents, false);
594−}
595−
596479QStringList ApplicationManager::availableApplications()
597480{
598481 QStringList appIds;
@@ -602,19 +485,6 @@ QStringList ApplicationManager::availableApplications()
602485 return appIds;
603486}
604487
605−int ApplicationManager::rightMargin() const
606−{
607− return m_rightMargin;
608−}
609−
610−void ApplicationManager::setRightMargin(int rightMargin)
611−{
612− m_rightMargin = rightMargin;
613− Q_FOREACH(ApplicationInfo *app, m_availableApplications) {
614− generateQmlStrings(app);
615− }
616−}
617−
618488bool ApplicationManager::isEmpty() const
619489{
620490 return m_runningApplications.isEmpty();
tests/mocks/Unity/Application/ApplicationManager.h−16
@@ -44,10 +44,6 @@ class ApplicationManager : public ApplicationManagerInterface {
4444
4545 Q_PROPERTY(bool fake READ fake CONSTANT)
4646
47− // Only for testing
48− // This can be used to place some controls to right, like make tryPhoneStage for example
49− Q_PROPERTY(int rightMargin READ rightMargin WRITE setRightMargin)
50−
5147 public:
5248 ApplicationManager(QObject *parent = NULL);
5349 virtual ~ApplicationManager();
@@ -108,8 +104,6 @@ class ApplicationManager : public ApplicationManagerInterface {
108104
109105 // Only for testing
110106 Q_INVOKABLE QStringList availableApplications();
111− int rightMargin() const;
112− void setRightMargin(int rightMargin);
113107
114108 QModelIndex findIndex(ApplicationInfo* application);
115109
@@ -126,19 +120,9 @@ class ApplicationManager : public ApplicationManagerInterface {
126120 void showApplicationWindow(ApplicationInfo *application);
127121 void buildListOfAvailableApplications();
128122 void generateQmlStrings(ApplicationInfo *application);
129− void createMainStageComponent();
130− void createMainStage();
131− void createSideStageComponent();
132− void createSideStage();
133123 bool m_suspended;
134124 QList<ApplicationInfo*> m_runningApplications;
135125 QList<ApplicationInfo*> m_availableApplications;
136− QQmlComponent *m_mainStageComponent;
137− QQuickItem *m_mainStage;
138− QQmlComponent *m_sideStageComponent;
139− QQuickItem *m_sideStage;
140−
141− int m_rightMargin;
142126
143127 static ApplicationManager *the_application_manager;
144128};
tests/mocks/Unity/Application/ApplicationScreenshotProvider.cpp+1−1
@@ -64,7 +64,7 @@ QImage ApplicationScreenshotProvider::requestImage(const QString &imageId, QSize
6464 Q_FOREACH (QWindow *win, unity->allWindows()) {
6565 QQuickWindow *quickWin = qobject_cast<QQuickWindow*>(win);
6666 if (quickWin) {
67− image = image.scaledToWidth(quickWin->width() - m_appManager->rightMargin());
67+ image = image.scaledToWidth(quickWin->width());
6868 break;
6969 }
7070 }
tests/mocks/Unity/CMakeLists.txt+4−3
@@ -2,11 +2,12 @@ add_subdirectory(Application)
22add_subdirectory(Indicators)
33add_subdirectory(Launcher)
44add_subdirectory(Notifications)
5+add_subdirectory(DashCommunicator)
56
67pkg_search_module(DEE dee-1.0 REQUIRED)
78pkg_search_module(GOBJECT gobject-2.0 REQUIRED)
89pkg_search_module(DEEQT libdee-qt5 REQUIRED)
9−pkg_check_modules(SCOPES_API REQUIRED unity-shell-scopes=3)
10+pkg_check_modules(SCOPES_API REQUIRED unity-shell-scopes=4)
1011
1112include_directories(
1213 ${CMAKE_CURRENT_BINARY_DIR}
@@ -22,14 +23,14 @@ set(UnityQML_SOURCES
2223 fake_scope.cpp
2324 fake_scopes.cpp
2425 fake_categories.cpp
25− fake_department.cpp
26+ fake_navigation.cpp
2627 fake_resultsmodel.cpp
2728 fake_previewmodel.cpp
2829 fake_previewstack.cpp
2930 fake_previewwidgetmodel.cpp
3031 fake_unity_plugin.cpp
3132 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/CategoriesInterface.h
32− ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/DepartmentInterface.h
33+ ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/NavigationInterface.h
3334 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/PreviewModelInterface.h
3435 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/PreviewStackInterface.h
3536 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/PreviewWidgetModelInterface.h
tests/mocks/Unity/DashCommunicator/CMakeLists.txtnew file+18
@@ -0,0 +1,18 @@
1+include_directories(
2+ ${CMAKE_CURRENT_SOURCE_DIR}
3+)
4+
5+set(QMLDASHCOMMUNICATORPLUGIN_SRC
6+ plugin.cpp
7+ dashcommunicatorservice.cpp
8+ dashcommunicator.cpp
9+ )
10+
11+add_library(MockDashCommunicator-qml MODULE
12+ ${QMLDASHCOMMUNICATORPLUGIN_SRC}
13+ )
14+
15+qt5_use_modules(MockDashCommunicator-qml DBus Qml)
16+
17+# export the qmldir and qmltypes files
18+add_unity8_mock(Unity.DashCommunicator 0.1 Unity/DashCommunicator TARGETS MockDashCommunicator-qml)
tests/mocks/Unity/DashCommunicator/dashcommunicator.cppnew file+37
@@ -0,0 +1,37 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it under
5+ * the terms of the GNU Lesser General Public License version 3, as published by
6+ * the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but WITHOUT
9+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11+ * Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "dashcommunicator.h"
18+
19+#include <QObject>
20+#include <QDBusConnection>
21+#include <QDBusInterface>
22+#include <QDebug>
23+
24+DashCommunicator::DashCommunicator(QObject *parent):
25+ QObject(parent)
26+{
27+}
28+
29+DashCommunicator::~DashCommunicator()
30+{
31+}
32+
33+
34+void DashCommunicator::setCurrentScope(const QString &scopeId, bool animate, bool reset)
35+{
36+ Q_EMIT setCurrentScopeCalled(scopeId, animate, reset);
37+}
tests/mocks/Unity/DashCommunicator/dashcommunicator.hnew file+39
@@ -0,0 +1,39 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it under
5+ * the terms of the GNU Lesser General Public License version 3, as published by
6+ * the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but WITHOUT
9+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11+ * Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef DASHCOMMUNICATOR_H
18+#define DASHCOMMUNICATOR_H
19+
20+#include <QObject>
21+
22+class DashCommunicator: public QObject
23+{
24+ Q_OBJECT
25+ Q_CLASSINFO("D-Bus Interface", "com.canonical.Unity.DashCommunicator")
26+
27+public:
28+ DashCommunicator(QObject *parent = 0);
29+ ~DashCommunicator();
30+
31+public Q_SLOTS:
32+ void setCurrentScope(const QString &scopeId, bool animate, bool reset);
33+
34+Q_SIGNALS:
35+ // This mock just emits calls back to the QML api for the plugin to verify calls
36+ void setCurrentScopeCalled(const QString &scopeId, bool animate, bool reset);
37+};
38+
39+#endif
tests/mocks/Unity/DashCommunicator/dashcommunicatorservice.cppnew file+33
@@ -0,0 +1,33 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it under
5+ * the terms of the GNU Lesser General Public License version 3, as published by
6+ * the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but WITHOUT
9+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11+ * Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "dashcommunicatorservice.h"
18+
19+DashCommunicatorService::DashCommunicatorService(QObject *parent):
20+ QObject(parent)
21+{
22+}
23+
24+
25+DashCommunicatorService::~DashCommunicatorService()
26+{
27+
28+}
29+
30+void DashCommunicatorService::mockSetCurrentScope(const QString &scopeId, bool animate, bool reset)
31+{
32+ Q_EMIT setCurrentScopeRequested(scopeId, animate, reset);
33+}
tests/mocks/Unity/DashCommunicator/dashcommunicatorservice.hnew file+36
@@ -0,0 +1,36 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it under
5+ * the terms of the GNU Lesser General Public License version 3, as published by
6+ * the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but WITHOUT
9+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11+ * Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef DASHCOMMUNICATORSERVICE_H
18+#define DASHCOMMUNICATORSERVICE_H
19+
20+#include <QObject>
21+
22+class DashCommunicatorService: public QObject
23+{
24+ Q_OBJECT
25+public:
26+ DashCommunicatorService(QObject *parent = 0);
27+ ~DashCommunicatorService();
28+
29+Q_SIGNALS:
30+ void setCurrentScopeRequested(const QString &scopeId, bool animate, bool reset);
31+
32+public Q_SLOTS:
33+ void mockSetCurrentScope(const QString &scopeId, bool animate, bool reset);
34+};
35+
36+#endif // DBUSUNITYSESSIONSERVICE_H
tests/mocks/Unity/DashCommunicator/plugin.cppnew file+31
@@ -0,0 +1,31 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors: Michael Zanetti <michael.zanetti@canonical.com>
17+ */
18+
19+#include "plugin.h"
20+#include "dashcommunicator.h"
21+#include "dashcommunicatorservice.h"
22+
23+#include <QDBusConnection>
24+#include <QtQml/qqml.h>
25+
26+void DashCommunicatorPlugin::registerTypes(const char *uri)
27+{
28+ Q_ASSERT(uri == QStringLiteral("Unity.DashCommunicator"));
29+ qmlRegisterType<DashCommunicatorService>(uri, 0, 1, "DashCommunicatorService");
30+ qmlRegisterType<DashCommunicator>(uri, 0, 1, "DashCommunicator");
31+}
tests/mocks/Unity/DashCommunicator/plugin.hnew file+34
@@ -0,0 +1,34 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
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+ * Authors: Michael Zanetti <michael.zanetti@canonical.com>
17+ */
18+
19+#ifndef DASHCOMMUNICATOR_PLUGIN_H
20+#define DASHCOMMUNICATOR_PLUGIN_H
21+
22+#include <QtQml/QQmlEngine>
23+#include <QtQml/QQmlExtensionPlugin>
24+
25+class DashCommunicatorPlugin : public QQmlExtensionPlugin
26+{
27+ Q_OBJECT
28+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
29+
30+public:
31+ void registerTypes(const char *uri);
32+};
33+
34+#endif
tests/mocks/Unity/DashCommunicator/qmldirnew file+3
@@ -0,0 +1,3 @@
1+module Unity.DashCommunicator
2+plugin MockDashCommunicator-qml
3+typeinfo DashCommunicator.qmltypes
tests/mocks/Unity/Unity.qmltypes+79−27
@@ -24,6 +24,12 @@ Module {
2424 Parameter { name: "categoryId"; type: "string" }
2525 Parameter { name: "json"; type: "string" }
2626 }
27+ Method {
28+ name: "data"
29+ type: "QVariant"
30+ Parameter { name: "row"; type: "int" }
31+ Parameter { name: "role"; type: "int" }
32+ }
2733 }
2834 Component {
2935 name: "PreviewModel"
@@ -62,6 +68,10 @@ Module {
6268 prototype: "unity::shell::scopes::ScopeInterface"
6369 exports: ["Unity/MockScope 0.2"]
6470 exportMetaObjectRevisions: [0]
71+ Signal {
72+ name: "performQuery"
73+ Parameter { name: "query"; type: "string" }
74+ }
6575 Method {
6676 name: "setSearchInProgress"
6777 Parameter { name: "inProg"; type: "bool" }
@@ -81,18 +91,21 @@ Module {
8191 Parameter { name: "scope"; type: "unity::shell::scopes::ScopeInterface"; isPointer: true }
8292 }
8393 Method {
84− name: "getDepartment"
85− type: "unity::shell::scopes::DepartmentInterface*"
86− Parameter { name: "id"; type: "string" }
94+ name: "getNavigation"
95+ type: "unity::shell::scopes::NavigationInterface*"
96+ Parameter { name: "navigationId"; type: "string" }
8797 }
8898 Method {
89− name: "loadDepartment"
90− Parameter { name: "id"; type: "string" }
99+ name: "getAltNavigation"
100+ type: "unity::shell::scopes::NavigationInterface*"
101+ Parameter { name: "altNavigationId"; type: "string" }
91102 }
92103 Method {
93− name: "performQuery"
94− Parameter { name: "query"; type: "string" }
104+ name: "setNavigationState"
105+ Parameter { name: "navigationId"; type: "string" }
106+ Parameter { name: "isAltNavigation"; type: "bool" }
95107 }
108+ Method { name: "refresh" }
96109 }
97110 Component {
98111 name: "Scopes"
@@ -137,6 +150,7 @@ Module {
137150 "RoleRawRendererTemplate": 3,
138151 "RoleRenderer": 4,
139152 "RoleComponents": 5,
153+ "RoleHeaderLink": 6,
140154 "RoleResults": 7,
141155 "RoleCount": 8
142156 }
@@ -157,27 +171,28 @@ Module {
157171 }
158172 }
159173 Component {
160− name: "unity::shell::scopes::DepartmentInterface"
174+ name: "unity::shell::scopes::NavigationInterface"
161175 prototype: "QAbstractListModel"
162− exports: ["Unity/Department 0.2"]
176+ exports: ["Unity/Navigation 0.2"]
163177 isCreatable: false
164178 exportMetaObjectRevisions: [0]
165179 Enum {
166180 name: "Roles"
167181 values: {
168− "RoleDepartmentId": 0,
182+ "RoleNavigationId": 0,
169183 "RoleLabel": 1,
170184 "RoleHasChildren": 2,
171185 "RoleIsActive": 3
172186 }
173187 }
174− Property { name: "departmentId"; type: "string"; isReadonly: true }
188+ Property { name: "navigationId"; type: "string"; isReadonly: true }
175189 Property { name: "label"; type: "string"; isReadonly: true }
176190 Property { name: "allLabel"; type: "string"; isReadonly: true }
177− Property { name: "parentDepartmentId"; type: "string"; isReadonly: true }
191+ Property { name: "parentNavigationId"; type: "string"; isReadonly: true }
178192 Property { name: "parentLabel"; type: "string"; isReadonly: true }
179193 Property { name: "loaded"; type: "bool"; isReadonly: true }
180194 Property { name: "isRoot"; type: "bool"; isReadonly: true }
195+ Property { name: "hidden"; type: "bool"; isReadonly: true }
181196 Property { name: "count"; type: "int"; isReadonly: true }
182197 }
183198 Component {
@@ -247,7 +262,8 @@ Module {
247262 "RoleEmblem": 8,
248263 "RoleSummary": 9,
249264 "RoleAttributes": 10,
250− "RoleBackground": 11
265+ "RoleBackground": 11,
266+ "RoleOverlayColor": 12
251267 }
252268 }
253269 Property { name: "categoryId"; type: "string" }
@@ -259,13 +275,22 @@ Module {
259275 exports: ["Unity/Scope 0.2"]
260276 isCreatable: false
261277 exportMetaObjectRevisions: [0]
278+ Enum {
279+ name: "Status"
280+ values: {
281+ "Okay": 0,
282+ "NoInternet": 1,
283+ "NoLocationData": 2,
284+ "Unknown": 3
285+ }
286+ }
262287 Property { name: "id"; type: "string"; isReadonly: true }
263288 Property { name: "name"; type: "string"; isReadonly: true }
264289 Property { name: "iconHint"; type: "string"; isReadonly: true }
265290 Property { name: "description"; type: "string"; isReadonly: true }
266291 Property { name: "searchHint"; type: "string"; isReadonly: true }
267292 Property { name: "searchInProgress"; type: "bool"; isReadonly: true }
268− Property { name: "visible"; type: "bool"; isReadonly: true }
293+ Property { name: "favorite"; type: "bool" }
269294 Property { name: "shortcut"; type: "string"; isReadonly: true }
270295 Property {
271296 name: "categories"
@@ -273,13 +298,27 @@ Module {
273298 isReadonly: true
274299 isPointer: true
275300 }
301+ Property {
302+ name: "settings"
303+ type: "unity::shell::scopes::SettingsModelInterface"
304+ isReadonly: true
305+ isPointer: true
306+ }
276307 Property { name: "searchQuery"; type: "string" }
277308 Property { name: "noResultsHint"; type: "string" }
278309 Property { name: "formFactor"; type: "string" }
279310 Property { name: "isActive"; type: "bool" }
280− Property { name: "currentDepartmentId"; type: "string"; isReadonly: true }
281− Property { name: "hasDepartments"; type: "bool"; isReadonly: true }
311+ Property { name: "currentNavigationId"; type: "string"; isReadonly: true }
312+ Property { name: "hasNavigation"; type: "bool"; isReadonly: true }
313+ Property { name: "currentAltNavigationId"; type: "string"; isReadonly: true }
314+ Property { name: "hasAltNavigation"; type: "bool"; isReadonly: true }
282315 Property { name: "customizations"; type: "QVariantMap"; isReadonly: true }
316+ Property {
317+ name: "status"
318+ type: "unity::shell::scopes::ScopeInterface::Status"
319+ isReadonly: true
320+ }
321+ Signal { name: "detailsChanged" }
283322 Signal { name: "showDash" }
284323 Signal { name: "hideDash" }
285324 Signal {
@@ -298,10 +337,6 @@ Module {
298337 name: "openScope"
299338 Parameter { name: "scope"; type: "unity::shell::scopes::ScopeInterface"; isPointer: true }
300339 }
301− Signal {
302− name: "activateApplication"
303− Parameter { name: "desktop"; type: "string" }
304− }
305340 Method {
306341 name: "activate"
307342 Parameter { name: "result"; type: "QVariant" }
@@ -317,14 +352,25 @@ Module {
317352 Parameter { name: "scope"; type: "unity::shell::scopes::ScopeInterface"; isPointer: true }
318353 }
319354 Method {
320− name: "getDepartment"
321− type: "unity::shell::scopes::DepartmentInterface*"
322− Parameter { name: "departmentId"; type: "string" }
355+ name: "getNavigation"
356+ type: "unity::shell::scopes::NavigationInterface*"
357+ Parameter { name: "navigationId"; type: "string" }
358+ }
359+ Method {
360+ name: "getAltNavigation"
361+ type: "unity::shell::scopes::NavigationInterface*"
362+ Parameter { name: "altNavigationId"; type: "string" }
323363 }
324364 Method {
325− name: "loadDepartment"
326− Parameter { name: "departmentId"; type: "string" }
365+ name: "setNavigationState"
366+ Parameter { name: "navId"; type: "string" }
367+ Parameter { name: "altNavigation"; type: "bool" }
327368 }
369+ Method {
370+ name: "performQuery"
371+ Parameter { name: "cannedQuery"; type: "string" }
372+ }
373+ Method { name: "refresh" }
328374 }
329375 Component {
330376 name: "unity::shell::scopes::ScopesInterface"
@@ -334,11 +380,17 @@ Module {
334380 values: {
335381 "RoleScope": 0,
336382 "RoleId": 1,
337− "RoleVisible": 2,
338− "RoleTitle": 3
383+ "RoleTitle": 2
339384 }
340385 }
341386 Property { name: "loaded"; type: "bool"; isReadonly: true }
387+ Property { name: "count"; type: "int"; isReadonly: true }
388+ Property {
389+ name: "overviewScope"
390+ type: "unity::shell::scopes::ScopeInterface"
391+ isReadonly: true
392+ isPointer: true
393+ }
342394 Method {
343395 name: "getScope"
344396 type: "unity::shell::scopes::ScopeInterface*"
tests/mocks/Unity/fake_department.cpp → tests/mocks/Unity/fake_navigation.cpprenamed+32−40
@@ -14,14 +14,15 @@
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 */
1616
17−#include "fake_department.h"
17+#include "fake_navigation.h"
1818
1919#include "fake_scope.h"
2020
21+#include <QDebug>
2122#include <QTimer>
2223
23−Department::Department(const QString& departmentId, const QString& label, const QString& allLabel, const QString& parentId, const QString& parentLabel, Scope* scope)
24− : m_departmentId(departmentId)
24+Navigation::Navigation(const QString& navigationId, const QString& label, const QString& allLabel, const QString& parentId, const QString& parentLabel, Scope* scope)
25+ : m_navigationId(navigationId)
2526 , m_label(label)
2627 , m_allLabel(allLabel)
2728 , m_parentId(parentId)
@@ -30,96 +31,87 @@ Department::Department(const QString& departmentId, const QString& label, const
3031 , m_scope(scope)
3132{
3233 QTimer::singleShot(1500, this, SLOT(slotLoaded()));
33− connect(scope, SIGNAL(currentDepartmentIdChanged()), this, SLOT(slotCurrentDepartmentChanged()));
34+ connect(scope, SIGNAL(currentNavigationIdChanged()), this, SLOT(slotCurrentNavigationChanged()));
3435}
3536
36−QString Department::departmentId() const
37+QString Navigation::navigationId() const
3738{
38− return m_departmentId;
39+ return m_navigationId;
3940}
4041
41−QString Department::label() const
42+QString Navigation::label() const
4243{
4344 return m_label;
4445}
4546
46−QString Department::allLabel() const
47+QString Navigation::allLabel() const
4748{
4849 return m_allLabel;
4950}
5051
51−QString Department::parentDepartmentId() const
52+QString Navigation::parentNavigationId() const
5253{
5354 return m_parentId;
5455}
5556
56−QString Department::parentLabel() const
57+QString Navigation::parentLabel() const
5758{
5859 return m_parentLabel;
5960}
6061
61−void Department::slotLoaded()
62+void Navigation::slotLoaded()
6263{
6364 m_loaded = true;
6465 Q_EMIT loadedChanged();
6566}
6667
67−bool Department::loaded() const
68+bool Navigation::loaded() const
6869{
6970 return m_loaded;
7071}
7172
72−int Department::count() const
73+int Navigation::count() const
7374{
7475 return rowCount();
7576}
7677
77−bool Department::isRoot() const
78+bool Navigation::isRoot() const
7879{
79− return m_departmentId == "root";
80+ return m_navigationId == "root" || m_navigationId == "altroot";
8081}
8182
82−int Department::rowCount(const QModelIndex & /*parent*/) const
83+bool Navigation::hidden() const
8384{
84− if (!m_loaded || m_departmentId.startsWith("child") || m_departmentId == "middle3")
85− return 0;
86− else
87− return 8;
85+ return m_navigationId == "altroot";
8886}
8987
90−QHash<int, QByteArray> Department::roleNames() const
88+int Navigation::rowCount(const QModelIndex & /*parent*/) const
9189{
92− QHash<int, QByteArray> res;
93− res[RoleDepartmentId] = "departmentId";
94− res[RoleLabel] = "label";
95− res[RoleHasChildren] = "hasChildren";
96− res[RoleIsActive] = "isActive";
97− return res;
90+ if (!m_loaded || m_navigationId.startsWith("child") || m_navigationId == "middle3")
91+ return 0;
92+ else
93+ return 8;
9894}
9995
100−QVariant Department::data(const QModelIndex &index, int role) const
96+QVariant Navigation::data(const QModelIndex &index, int role) const
10197{
10298 switch (role) {
103− case RoleDepartmentId:
104− if (m_departmentId == "root")
99+ case RoleNavigationId:
100+ if (m_navigationId == "root")
105101 return QString("middle%1").arg(index.row());
106− else if (m_departmentId.startsWith("middle"))
107− return QString("child%1%2").arg(m_departmentId).arg(index.row());
108− break;
102+ else if (m_navigationId.startsWith("middle"))
103+ return QString("child%1%2").arg(m_navigationId).arg(index.row());
109104 case RoleLabel:
110− return QString("%1Child%2").arg(m_departmentId).arg(index.row());
111− break;
105+ return QString("%1Child%2").arg(m_navigationId).arg(index.row());
112106 case RoleHasChildren:
113− return m_departmentId == "root" && index.row() != 3;
114− break;
107+ return m_navigationId == "root" && index.row() != 3;
115108 case RoleIsActive:
116− return m_scope->currentDepartmentId() == data(index, RoleDepartmentId);
117− break;
109+ return m_scope->currentNavigationId() == data(index, RoleNavigationId);
118110 }
119111 return QVariant();
120112}
121113
122−void Department::slotCurrentDepartmentChanged()
114+void Navigation::slotCurrentNavigationChanged()
123115{
124116 // This is wasteful, should only emit it if really something changed in this
125117 // deparment, but this is a mock, so no need to optimize
tests/mocks/Unity/fake_department.h → tests/mocks/Unity/fake_navigation.hrenamed+13−13
@@ -14,48 +14,48 @@
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 */
1616
17−#ifndef FAKE_DEPARTMENT_H
18−#define FAKE_DEPARTMENT_H
17+#ifndef FAKE_NAVIGATION_H
18+#define FAKE_NAVIGATION_H
1919
20−#include <unity/shell/scopes/DepartmentInterface.h>
20+#include <unity/shell/scopes/NavigationInterface.h>
2121
2222class Scope;
2323
24−class Department : public unity::shell::scopes::DepartmentInterface
24+class Navigation : public unity::shell::scopes::NavigationInterface
2525{
2626 Q_OBJECT
2727
2828public:
29− Department(const QString& departmentId, const QString& label, const QString& allLabel, const QString& parentId, const QString& parentLabel, Scope* scope);
29+ Navigation(const QString& navigationId, const QString& label, const QString& allLabel, const QString& parentId, const QString& parentLabel, Scope* scope);
3030
31− QString departmentId() const override;
31+ QString navigationId() const override;
3232 QString label() const override;
3333 QString allLabel() const override;
34− QString parentDepartmentId() const override;
34+ QString parentNavigationId() const override;
3535 QString parentLabel() const override;
3636 bool loaded() const override;
37− int count() const override;
3837 bool isRoot() const override;
38+ bool hidden() const override;
39+ int count() const override;
3940
4041 int rowCount(const QModelIndex &parent = QModelIndex()) const override;
41− QHash<int, QByteArray> roleNames() const;
4242 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
4343
4444public Q_SLOTS:
45− void slotCurrentDepartmentChanged();
45+ void slotCurrentNavigationChanged();
4646
4747private Q_SLOTS:
4848 void slotLoaded();
4949
5050private:
51− QString m_departmentId;
51+ QString m_navigationId;
5252 QString m_label;
5353 QString m_allLabel;
5454 QString m_parentId;
5555 QString m_parentLabel;
5656 bool m_loaded;
57− QString m_currentDepartment;
57+ QString m_currentNavigation;
5858 Scope *m_scope;
5959};
6060
61−#endif // FAKE_DEPARTMENT_H
61+#endif // FAKE_NAVIGATION_H
tests/mocks/Unity/fake_scope.cpp+65−17
@@ -14,24 +14,26 @@
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 */
1616
17+#include <QDebug>
1718#include <QUrl>
1819
1920#include "fake_scope.h"
20−#include "fake_department.h"
21+#include "fake_navigation.h"
2122#include "fake_resultsmodel.h"
2223
2324Scope::Scope(QObject* parent) : Scope(QString(), QString(), false, parent)
2425{
2526}
2627
27−Scope::Scope(QString const& id, QString const& name, bool visible, QObject* parent, int categories)
28+Scope::Scope(QString const& id, QString const& name, bool favorite, QObject* parent, int categories)
2829 : unity::shell::scopes::ScopeInterface(parent)
2930 , m_id(id)
3031 , m_name(name)
31− , m_visible(visible)
3232 , m_searching(false)
33+ , m_favorite(favorite)
3334 , m_isActive(false)
34− , m_currentDeparment("root")
35+ , m_currentNavigationId("root")
36+ , m_currentAltNavigationId("altroot")
3537 , m_previewRendererName("preview-generic")
3638 , m_categories(new Categories(categories, this))
3739{
@@ -77,6 +79,11 @@ bool Scope::searchInProgress() const
7779 return m_searching;
7880}
7981
82+bool Scope::favorite() const
83+{
84+ return m_favorite;
85+}
86+
8087unity::shell::scopes::CategoriesInterface* Scope::categories() const
8188{
8289 return m_categories;
@@ -97,11 +104,6 @@ QString Scope::formFactor() const
97104 return m_formFactor;
98105}
99106
100−bool Scope::visible() const
101−{
102− return m_visible;
103−}
104−
105107bool Scope::isActive() const
106108{
107109 return m_isActive;
@@ -131,6 +133,13 @@ void Scope::setActive(const bool active)
131133 }
132134}
133135
136+void Scope::setFavorite(const bool favorite)
137+{
138+ if (favorite != m_favorite) {
139+ m_favorite = favorite;
140+ Q_EMIT favoriteChanged();
141+ }
142+}
134143void Scope::setSearchInProgress(const bool inProg)
135144{
136145 if (inProg != m_searching) {
@@ -170,16 +179,31 @@ void Scope::closeScope(unity::shell::scopes::ScopeInterface* /*scope*/)
170179 qFatal("Scope::closeScope is not implemented");
171180}
172181
173−QString Scope::currentDepartmentId() const
182+QString Scope::currentNavigationId() const
183+{
184+ return m_currentNavigationId;
185+}
186+
187+bool Scope::hasNavigation() const
188+{
189+ return true;
190+}
191+
192+QString Scope::currentAltNavigationId() const
174193{
175− return m_currentDeparment;
194+ return m_currentAltNavigationId;
176195}
177196
178−bool Scope::hasDepartments() const
197+bool Scope::hasAltNavigation() const
179198{
180199 return true;
181200}
182201
202+Scope::Status Scope::status() const
203+{
204+ return Status::Okay;
205+}
206+
183207QVariantMap Scope::customizations() const
184208{
185209 QVariantMap m, h;
@@ -196,7 +220,12 @@ QVariantMap Scope::customizations() const
196220 return m;
197221}
198222
199−unity::shell::scopes::DepartmentInterface* Scope::getDepartment(const QString& id)
223+void Scope::refresh()
224+{
225+ qDebug() << "Scope::refresh is currently not implmented in the fake scopes plugin";
226+}
227+
228+unity::shell::scopes::NavigationInterface* Scope::getNavigation(const QString& id)
200229{
201230 if (id.isEmpty())
202231 return nullptr;
@@ -210,11 +239,30 @@ unity::shell::scopes::DepartmentInterface* Scope::getDepartment(const QString& i
210239 parentId = id.mid(5, 7);
211240 parentLabel = parentId;
212241 }
213− return new Department(id, id, "all"+id, parentId, parentLabel, this);
242+ return new Navigation(id, id, "all"+id, parentId, parentLabel, this);
243+}
244+
245+unity::shell::scopes::NavigationInterface* Scope::getAltNavigation(QString const& id)
246+{
247+ if (id.isEmpty())
248+ return nullptr;
249+
250+ QString parentId;
251+ QString parentLabel;
252+ if (id.startsWith("altmiddle")) {
253+ parentId = "altroot";
254+ parentLabel = "altroot";
255+ }
256+ return new Navigation(id, id, "all"+id, parentId, parentLabel, this);
214257}
215258
216−void Scope::loadDepartment(const QString& id)
259+void Scope::setNavigationState(const QString &navigationId, bool isAltNavigation)
217260{
218− m_currentDeparment = id;
219− Q_EMIT currentDepartmentIdChanged();
261+ if (isAltNavigation) {
262+ m_currentAltNavigationId = navigationId;
263+ Q_EMIT currentAltNavigationIdChanged();
264+ } else {
265+ m_currentNavigationId = navigationId;
266+ Q_EMIT currentNavigationIdChanged();
267+ }
220268}
tests/mocks/Unity/fake_scope.h+16−8
@@ -30,7 +30,7 @@ class Scope : public unity::shell::scopes::ScopeInterface
3030
3131public:
3232 Scope(QObject* parent = 0);
33− Scope(QString const& id, QString const& name, bool visible, QObject* parent = 0, int categories = 20);
33+ Scope(QString const& id, QString const& name, bool favorite, QObject* parent = 0, int categories = 20);
3434
3535 /* getters */
3636 QString id() const override;
@@ -38,9 +38,9 @@ public:
3838 QString iconHint() const override;
3939 QString description() const override;
4040 QString searchHint() const override;
41− bool visible() const override;
4241 QString shortcut() const override;
4342 bool searchInProgress() const override;
43+ bool favorite() const override;
4444 unity::shell::scopes::CategoriesInterface* categories() const override;
4545 QString searchQuery() const override;
4646 QString noResultsHint() const override;
@@ -53,6 +53,7 @@ public:
5353 void setNoResultsHint(const QString& hint) override;
5454 void setFormFactor(const QString& form_factor) override;
5555 void setActive(const bool) override;
56+ void setFavorite(const bool) override;
5657 Q_INVOKABLE void setSearchInProgress(const bool inProg); // This is not invokable in the Interface, here for testing benefits
5758
5859 Q_INVOKABLE void activate(QVariant const& result) override;
@@ -60,14 +61,20 @@ public:
6061 Q_INVOKABLE void cancelActivation() override;
6162 Q_INVOKABLE void closeScope(unity::shell::scopes::ScopeInterface* scope) override;
6263
63− QString currentDepartmentId() const override;
64− bool hasDepartments() const override;
65− Q_INVOKABLE unity::shell::scopes::DepartmentInterface* getDepartment(const QString& id) override;
66− Q_INVOKABLE void loadDepartment(const QString& id) override;
64+ QString currentNavigationId() const override;
65+ bool hasNavigation() const override;
66+ QString currentAltNavigationId() const override;
67+ bool hasAltNavigation() const override;
68+ Q_INVOKABLE unity::shell::scopes::NavigationInterface* getNavigation(QString const& navigationId) override;
69+ Q_INVOKABLE unity::shell::scopes::NavigationInterface* getAltNavigation(QString const& altNavigationId) override;
70+ Q_INVOKABLE void setNavigationState(const QString &navigationId, bool isAltNavigation) override;
6771 Q_SIGNAL void performQuery(const QString& query) override;
6872
73+ Status status() const override;
6974 QVariantMap customizations() const override;
7075
76+ Q_INVOKABLE void refresh() override;
77+
7178protected:
7279
7380 QString m_id;
@@ -77,10 +84,11 @@ protected:
7784 QString m_searchQuery;
7885 QString m_noResultsHint;
7986 QString m_formFactor;
80− bool m_visible;
8187 bool m_searching;
88+ bool m_favorite;
8289 bool m_isActive;
83− QString m_currentDeparment;
90+ QString m_currentNavigationId;
91+ QString m_currentAltNavigationId;
8492
8593 QString m_previewRendererName;
8694
tests/mocks/Unity/fake_scopes.cpp+10−2
@@ -32,6 +32,11 @@ Scopes::Scopes(QObject *parent)
3232 timer.setSingleShot(true);
3333 timer.setInterval(100);
3434 QObject::connect(&timer, SIGNAL(timeout()), this, SLOT(updateScopes()));
35+
36+ QObject::connect(this, SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SIGNAL(countChanged()));
37+ QObject::connect(this, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), this, SIGNAL(countChanged()));
38+ QObject::connect(this, SIGNAL(modelReset()), this, SIGNAL(countChanged()));
39+
3540 load();
3641}
3742
@@ -92,8 +97,6 @@ QVariant Scopes::data(const QModelIndex& index, int role) const
9297 return QVariant::fromValue(scope);
9398 } else if (role == Scopes::RoleId) {
9499 return QVariant::fromValue(scope->id());
95− } else if (role == Scopes::RoleVisible) {
96− return QVariant::fromValue(scope->visible());
97100 } else if (role == Scopes::RoleTitle) {
98101 return QVariant::fromValue(scope->name());
99102 } else {
@@ -125,6 +128,11 @@ bool Scopes::loaded() const
125128 return m_loaded;
126129}
127130
131+int Scopes::count() const
132+{
133+ return rowCount();
134+}
135+
128136unity::shell::scopes::ScopeInterface* Scopes::overviewScope() const
129137{
130138 return nullptr;
tests/mocks/Unity/fake_scopes.h+1
@@ -50,6 +50,7 @@ public:
5050 QModelIndex parent ( const QModelIndex & index ) const;
5151
5252 bool loaded() const override;
53+ int count() const override;
5354 unity::shell::scopes::ScopeInterface* overviewScope() const override;
5455
5556private Q_SLOTS:
tests/mocks/Unity/fake_unity_plugin.cpp+2−2
@@ -22,7 +22,7 @@
2222// local
2323#include "fake_scopes.h"
2424#include "fake_categories.h"
25−#include "fake_department.h"
25+#include "fake_navigation.h"
2626#include "fake_previewmodel.h"
2727#include "fake_previewwidgetmodel.h"
2828#include "fake_resultsmodel.h"
@@ -43,7 +43,7 @@ void FakeUnityPlugin::registerTypes(const char *uri)
4343 qmlRegisterType<Scopes>(uri, 0, 2, "Scopes");
4444 qmlRegisterType<Scope>(uri, 0, 2, "MockScope");
4545 qmlRegisterUncreatableType<unity::shell::scopes::ScopeInterface>(uri, 0, 2, "Scope", "Can't create Scope object in QML.");
46− qmlRegisterUncreatableType<unity::shell::scopes::DepartmentInterface>(uri, 0, 2, "Department", "Can't create Department object in QML.");
46+ qmlRegisterUncreatableType<unity::shell::scopes::NavigationInterface>(uri, 0, 2, "Navigation", "Can't create Navigation object in QML.");
4747 qmlRegisterUncreatableType<unity::shell::scopes::CategoriesInterface>(uri, 0, 2, "Categories", "Can't create Categories object in QML.");
4848 qmlRegisterUncreatableType<unity::shell::scopes::PreviewModelInterface>(uri, 0, 2, "PreviewModel", "Can't create new PreviewModel in QML. Get them from PreviewStack instance.");
4949 qmlRegisterUncreatableType<ResultsModel>(uri, 0, 2, "ResultsModel", "Can't create ResultsModel object in QML.");
tests/plugins/Dash/cardcreator/1.res+12−10
@@ -33,20 +33,15 @@ Item {
3333 visible: image.status == Image.Ready;
3434 readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1;
3535 readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1;
36− readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect;
37− Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
38− onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings();
36+ Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
3937 Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
4038 function updateWidthHeightBindings() {
4139 if (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) {
4240 width = root.fixedArtShapeSize.width;
4341 height = root.fixedArtShapeSize.height;
44− } else if (aspectSmallerThanImageAspect) {
45− width = Qt.binding(function() { return !visible ? 0 : image.width });
46− height = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.height : width / image.aspect });
4742 } else {
48− width = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.width : height * image.aspect });
49− height = Qt.binding(function() { return !visible ? 0 : image.height });
43+ width = Qt.binding(function() { return !visible ? 0 : image.width });
44+ height = Qt.binding(function() { return !visible ? 0 : image.height });
5045 }
5146 }
5247 image: Image {
@@ -54,8 +49,7 @@ Item {
5449 source: cardData && cardData["art"] || "";
5550 cache: true;
5651 asynchronous: root.asynchronous;
57− fillMode: components && components["art"]["fill-mode"] === "fit" ? Image.PreserveAspectFit: Image.PreserveAspectCrop;
58− readonly property real aspect: implicitWidth / implicitHeight;
52+ fillMode: Image.PreserveAspectCrop;
5953 width: root.width;
6054 height: width / artShape.aspect;
6155 }
@@ -84,5 +78,13 @@ leftMargin: units.gu(1);
8478 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
8579 horizontalAlignment: root.headerAlignment;
8680 }
81+UbuntuShape {
82+ id: touchdown;
83+ objectName: "touchdown";
84+ anchors { fill: artShapeHolder }
85+ visible: root.pressed;
86+ radius: "medium";
87+ borderSource: "radius_pressed.sci"
88+}
8789implicitHeight: titleLabel.y + titleLabel.height + units.gu(1);
8890}
tests/plugins/Dash/cardcreator/1.tst+1−1
@@ -1,3 +1,3 @@
11template: {"card-layout":"vertical","card-size":"small","category-layout":"grid","collapsed-rows":2}
2−components: {"art":{"aspect-ratio":1.6,"field":"art","fill-mode":"fit"},"title":{"field":"title"}}
2+components: {"art":{"aspect-ratio":1.6,"field":"art"},"title":{"field":"title"}}
33result: 1.res
tests/plugins/Dash/cardcreator/2.res+8
@@ -116,5 +116,13 @@ left: parent.left;
116116
117117 ]
118118 }
119+UbuntuShape {
120+ id: touchdown;
121+ objectName: "touchdown";
122+ anchors { fill: backgroundLoader }
123+ visible: root.pressed;
124+ radius: "medium";
125+ borderSource: "radius_pressed.sci"
126+}
119127implicitHeight: row.y + row.height + units.gu(1);
120128}
tests/plugins/Dash/cardcreator/2.tst+1−1
@@ -1,3 +1,3 @@
11template: {"card-background":{"elements":["#E9E9E9"],"type":"color"},"card-layout":"vertical","card-size":"medium","category-layout":"grid","collapsed-rows":2}
2−components: {"art":{"aspect-ratio":1,"fill-mode":"crop"},"background":{"field":"background"},"mascot":{"field":"icon"},"subtitle":{"field":"author"},"title":{"field":"title"}}
2+components: {"art":{"aspect-ratio":1},"background":{"field":"background"},"mascot":{"field":"icon"},"subtitle":{"field":"author"},"title":{"field":"title"}}
33result: 2.res
tests/plugins/Dash/cardcreator/3.res+12−10
@@ -25,7 +25,7 @@ Item {
2525 objectName: "artShapeLoader";
2626 active: cardData && cardData["art"] || false;
2727 asynchronous: root.asynchronous;
28− visible: status == Loader.Ready;
28+ visible: status == Loader.Ready;
2929 sourceComponent: UbuntuShape {
3030 id: artShape;
3131 objectName: "artShape";
@@ -33,20 +33,15 @@ Item {
3333 visible: image.status == Image.Ready;
3434 readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1;
3535 readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1;
36− readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect;
3736 Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
38− onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings();
3937 Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
4038 function updateWidthHeightBindings() {
4139 if (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) {
4240 width = root.fixedArtShapeSize.width;
4341 height = root.fixedArtShapeSize.height;
44− } else if (aspectSmallerThanImageAspect) {
45− width = Qt.binding(function() { return !visible ? 0 : image.width });
46− height = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.height : width / image.aspect });
4742 } else {
48− width = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.width : height * image.aspect });
49− height = Qt.binding(function() { return !visible ? 0 : image.height });
43+ width = Qt.binding(function() { return !visible ? 0 : image.width });
44+ height = Qt.binding(function() { return !visible ? 0 : image.height });
5045 }
5146 }
5247 image: Image {
@@ -54,8 +49,7 @@ Item {
5449 source: cardData && cardData["art"] || "";
5550 cache: true;
5651 asynchronous: root.asynchronous;
57− fillMode: components && components["art"]["fill-mode"] === "fit" ? Image.PreserveAspectFit: Image.PreserveAspectCrop;
58− readonly property real aspect: implicitWidth / implicitHeight;
52+ fillMode: Image.PreserveAspectCrop;
5953 width: root.width;
6054 height: width / artShape.aspect;
6155 }
@@ -103,5 +97,13 @@ Label {
10397 font.weight: Font.Light;
10498 horizontalAlignment: root.headerAlignment;
10599 }
100+UbuntuShape {
101+ id: touchdown;
102+ objectName: "touchdown";
103+ anchors { fill: artShapeHolder }
104+ visible: root.pressed;
105+ radius: "medium";
106+ borderSource: "radius_pressed.sci"
107+}
106108implicitHeight: subtitleLabel.y + subtitleLabel.height + units.gu(1);
107109}
tests/plugins/Dash/cardcreator/3.tst+1−1
@@ -1,3 +1,3 @@
11template: {"card-layout":"vertical","card-size":"small","category-layout":"grid","collapsed-rows":2}
2−components: {"art":{"aspect-ratio":0.75,"field":"art","fill-mode":"crop"},"subtitle":{"field":"price"},"title":{"field":"title"}}
2+components: {"art":{"aspect-ratio":0.75,"field":"art"},"subtitle":{"field":"price"},"title":{"field":"title"}}
33result: 3.res
tests/plugins/Dash/cardcreator/4.res+8
@@ -94,5 +94,13 @@ Label {
9494}
9595]
9696}
97+UbuntuShape {
98+ id: touchdown;
99+ objectName: "touchdown";
100+ anchors { fill: root }
101+ visible: root.pressed;
102+ radius: "medium";
103+ borderSource: "radius_pressed.sci"
104+}
97105implicitHeight: row.y + row.height + units.gu(1);
98106}
tests/plugins/Dash/cardcreator/4.tst+1−1
@@ -1,3 +1,3 @@
11template: {"card-layout":"horizontal","card-size":"large","category-layout":"grid","collapsed-rows":2}
2−components: {"art":{"aspect-ratio":1,"fill-mode":"crop"},"mascot":{"field":"mascot"},"subtitle":{"field":"domain"},"title":{"field":"title"}}
2+components: {"art":{"aspect-ratio":1},"mascot":{"field":"mascot"},"subtitle":{"field":"domain"},"title":{"field":"title"}}
33result: 4.res
tests/plugins/Dash/cardcreator/5.res+14−12
@@ -25,7 +25,7 @@ Item {
2525 objectName: "artShapeLoader";
2626 active: cardData && cardData["art"] || false;
2727 asynchronous: root.asynchronous;
28− visible: status == Loader.Ready;
28+ visible: status == Loader.Ready;
2929 sourceComponent: UbuntuShape {
3030 id: artShape;
3131 objectName: "artShape";
@@ -33,20 +33,15 @@ Item {
3333 visible: image.status == Image.Ready;
3434 readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1;
3535 readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1;
36− readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect;
3736 Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
38− onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings();
3937 Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
4038 function updateWidthHeightBindings() {
4139 if (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) {
4240 width = root.fixedArtShapeSize.width;
4341 height = root.fixedArtShapeSize.height;
44− } else if (aspectSmallerThanImageAspect) {
45− width = Qt.binding(function() { return !visible ? 0 : image.width });
46− height = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.height : width / image.aspect });
4742 } else {
48− width = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.width : height * image.aspect });
49− height = Qt.binding(function() { return !visible ? 0 : image.height });
43+ width = Qt.binding(function() { return !visible ? 0 : image.width });
44+ height = Qt.binding(function() { return !visible ? 0 : image.height });
5045 }
5146 }
5247 image: Image {
@@ -54,13 +49,12 @@ Item {
5449 source: cardData && cardData["art"] || "";
5550 cache: true;
5651 asynchronous: root.asynchronous;
57− fillMode: components && components["art"]["fill-mode"] === "fit" ? Image.PreserveAspectFit: Image.PreserveAspectCrop;
58− readonly property real aspect: implicitWidth / implicitHeight;
52+ fillMode: Image.PreserveAspectCrop;
5953 width: root.width;
6054 height: width / artShape.aspect;
6155 }
6256 }
63− }
57+ }
6458 }
6559Loader {
6660 id: overlayLoader;
@@ -69,7 +63,7 @@ Loader {
6963 right: artShapeHolder.right;
7064 bottom: artShapeHolder.bottom;
7165 }
72− active: artShapeLoader.active && artShapeLoader.item && artShapeLoader.item.image.status === Image.Ready || false;
66+ active: artShapeLoader.active && artShapeLoader.item && artShapeLoader.item.image.status === Image.Ready || false;
7367 asynchronous: root.asynchronous;
7468 visible: showHeader && status == Loader.Ready;
7569 sourceComponent: ShaderEffect {
@@ -143,5 +137,13 @@ Label {
143137 font.weight: Font.Light;
144138 horizontalAlignment: root.headerAlignment;
145139 }
140+UbuntuShape {
141+ id: touchdown;
142+ objectName: "touchdown";
143+ anchors { fill: artShapeHolder }
144+ visible: root.pressed;
145+ radius: "medium";
146+ borderSource: "radius_pressed.sci"
147+}
146148implicitHeight: subtitleLabel.y + subtitleLabel.height + units.gu(1);
147149}
tests/plugins/Dash/cardcreator/5.tst+1−1
@@ -1,3 +1,3 @@
11template: {"card-layout":"vertical","card-size":"medium","category-layout":"carousel","collapsed-rows":2,"overlay":true}
2−components: {"art":{"aspect-ratio":1,"field":"art","fill-mode":"crop"},"subtitle":{"field":"artist"},"title":{"field":"title"}}
2+components: {"art":{"aspect-ratio":1,"field":"art"},"subtitle":{"field":"artist"},"title":{"field":"title"}}
33result: 5.res
tests/plugins/Dash/cardcreator/6.res+10−8
@@ -33,19 +33,14 @@ Item {
3333 visible: image.status == Image.Ready;
3434 readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1;
3535 readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1;
36− readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect;
3736 Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
38− onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings();
3937 Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
4038 function updateWidthHeightBindings() {
4139 if (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) {
4240 width = root.fixedArtShapeSize.width;
4341 height = root.fixedArtShapeSize.height;
44− } else if (aspectSmallerThanImageAspect) {
45− width = Qt.binding(function() { return !visible ? 0 : image.width });
46− height = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.height : width / image.aspect });
4742 } else {
48− width = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.width : height * image.aspect });
43+ width = Qt.binding(function() { return !visible ? 0 : image.width });
4944 height = Qt.binding(function() { return !visible ? 0 : image.height });
5045 }
5146 }
@@ -54,8 +49,7 @@ Item {
5449 source: cardData && cardData["art"] || "";
5550 cache: true;
5651 asynchronous: root.asynchronous;
57− fillMode: components && components["art"]["fill-mode"] === "fit" ? Image.PreserveAspectFit: Image.PreserveAspectCrop;
58− readonly property real aspect: implicitWidth / implicitHeight;
52+ fillMode: Image.PreserveAspectCrop;
5953 width: root.width;
6054 height: width / artShape.aspect;
6155 }
@@ -63,5 +57,13 @@ Item {
6357 }
6458 }
6559readonly property int headerHeight: 0;
60+UbuntuShape {
61+ id: touchdown;
62+ objectName: "touchdown";
63+ anchors { fill: artShapeHolder }
64+ visible: root.pressed;
65+ radius: "medium";
66+ borderSource: "radius_pressed.sci"
67+}
6668implicitHeight: artShapeHolder.height;
6769}
tests/plugins/Dash/cardcreator/6.tst+1−1
@@ -1,3 +1,3 @@
11template: {"card-layout":"vertical","card-size":"small","category-layout":"grid","collapsed-rows":2}
2−components: {"art":{"aspect-ratio":1.6,"field":"art","fill-mode":"fit"}}
2+components: {"art":{"aspect-ratio":1.6,"field":"art"}}
33result: 6.res
tests/plugins/Dash/cardcreator/7.res+9−1
@@ -116,7 +116,7 @@ CardAttributes {
116116 objectName: "attributesRow";
117117 anchors { left: parent.left; right: parent.right }
118118 color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : (root.scopeStyle ? root.scopeStyle.foreground : "grey");
119− model: cardData["attributes"]
119+ model: cardData["attributes"];
120120 }
121121
122122 ]
@@ -124,5 +124,13 @@ CardAttributes {
124124
125125 ]
126126 }
127+UbuntuShape {
128+ id: touchdown;
129+ objectName: "touchdown";
130+ anchors { fill: backgroundLoader }
131+ visible: root.pressed;
132+ radius: "medium";
133+ borderSource: "radius_pressed.sci"
134+}
127135implicitHeight: row.y + row.height + units.gu(1);
128136}
tests/plugins/Dash/cardcreator/7.tst+1−1
@@ -1,3 +1,3 @@
11template: {"card-background":{"elements":["#E9E9E9"],"type":"color"},"card-layout":"vertical","card-size":"medium","category-layout":"grid","collapsed-rows":2}
2−components: {"art":{"aspect-ratio":1,"fill-mode":"crop"},"background":{"field":"background"},"mascot":{"field":"icon"},"subtitle":{"field":"author"},"title":{"field":"title"},"attributes":{"field":"attributes","max-count":2}}
2+components: {"art":{"aspect-ratio":1},"background":{"field":"background"},"mascot":{"field":"icon"},"subtitle":{"field":"author"},"title":{"field":"title"},"attributes":{"field":"attributes","max-count":2}}
33result: 7.res
tests/plugins/Dash/cardcreatortest.cpp+13−4
@@ -19,6 +19,7 @@
1919#include <QQuickItem>
2020#include <QQuickView>
2121#include <QtTestGui>
22+#include <QDebug>
2223#include <QTemporaryFile>
2324
2425class CardCreatorTest : public QObject
@@ -74,15 +75,23 @@ private Q_SLOTS:
7475 QFile testResultFile(testDirPath + resultFileName);
7576 QVERIFY(testResultFile.open(QIODevice::ReadOnly));
7677 QTextStream ts2(&testResultFile);
77− const QString expectedResult = ts2.readAll();
78− const QString executedResult = cardStringResult.toString();
78+
7979 // Record failed results to /tmp
80+ const QString executedResult = cardStringResult.toString();
8081 QTemporaryFile tmpFile(QDir::tempPath() + QDir::separator() + "testCardCreatorFailedResultXXXXXX");
8182 tmpFile.open();
8283 tmpFile.setAutoRemove(false);
8384 tmpFile.write(executedResult.toUtf8().constData());
84− QCOMPARE(executedResult.simplified(), expectedResult.simplified());
85− tmpFile.setAutoRemove(true); // Remove the result if it passed
85+
86+ // Line by line comparison
87+ const QStringList expectedLines = ts2.readAll().trimmed().replace(QRegExp("\n\\s*\n"),"\n").split("\n");
88+ const QStringList cardStringResultLines = cardStringResult.toString().trimmed().replace(QRegExp("\n\\s*\n"),"\n").split("\n");
89+ for (int i = 0; i < expectedLines.size(); ++i) {
90+ QCOMPARE(cardStringResultLines[i].simplified(), expectedLines[i].simplified());
91+ }
92+
93+ // Remove the result if it passed
94+ tmpFile.setAutoRemove(true);
8695
8796 QVariant createCardComponentResult;
8897 QMetaObject::invokeMethod(view->rootObject(), "createCardComponent", Q_RETURN_ARG(QVariant, createCardComponentResult), Q_ARG(QVariant, templateJSON), Q_ARG(QVariant, componentsJSON));
tests/plugins/Dash/tst_ScopeStyle.qml+14−7
@@ -54,6 +54,9 @@ Rectangle {
5454 { tag: "lightgrey", luminance: 0.8275 },
5555 { tag: "grey", luminance: 0.5020 },
5656 { tag: "darkgrey", luminance: 0.6627 },
57+ { tag: "red", luminance: 0.2126 },
58+ { tag: "green", luminance: 0.3590 },
59+ { tag: "blue", luminance: 0.0722 },
5760 ];
5861 }
5962
@@ -64,10 +67,10 @@ Rectangle {
6467
6568 function test_foreground_data() {
6669 return [
67− { tag: "default", index: 0, foreground: "grey" },
68− { tag: "red on black", index: 1, foreground: "red" },
69− { tag: "green on white", index: 2, foreground: "green" },
70− { tag: "blue on darkgrey", index: 3, foreground: "blue" },
70+ { tag: "default", index: 0, foreground: "grey", luminance: 0.5020 },
71+ { tag: "red on black", index: 1, foreground: "red", luminance: 0.2126 },
72+ { tag: "green on white", index: 2, foreground: "green", luminance: 0.3590 },
73+ { tag: "blue on darkgrey", index: 3, foreground: "blue", luminance: 0.0722 },
7174 ];
7275 }
7376
@@ -75,14 +78,15 @@ Rectangle {
7578 tool.style = testCase.styles[data.index];
7679 verify(Qt.colorEqual(tool.foreground, data.foreground),
7780 "Foreground color not equal: %1 != %2".arg(tool.foreground).arg(data.foreground));
81+ compare(tool.foregroundLuminance.toFixed(4), data.luminance.toFixed(4));
7882 }
7983
8084 function test_background_data() {
8185 return [
8286 { tag: "default", index: 0, background: "transparent" },
83− { tag: "red on black", index: 1, background: "black" },
84− { tag: "green on white", index: 2, background: "white" },
85− { tag: "blue on darkgrey", index: 3, background: "darkgrey" },
87+ { tag: "red on black", index: 1, background: "black", luminance: 0 },
88+ { tag: "green on white", index: 2, background: "white", luminance: 1 },
89+ { tag: "blue on darkgrey", index: 3, background: "darkgrey", luminance: 0.6627 },
8690 ];
8791 }
8892
@@ -90,6 +94,9 @@ Rectangle {
9094 tool.style = testCase.styles[data.index];
9195 verify(Qt.colorEqual(tool.background, data.background),
9296 "Background color not equal: %1 != %2".arg(tool.background).arg(data.background));
97+ if (data.hasOwnProperty("luminance")) {
98+ compare(tool.backgroundLuminance.toFixed(4), data.luminance.toFixed(4));
99+ }
93100 }
94101
95102 function test_threshold_data() {
tests/qmltests/CMakeLists.txt+1−1
@@ -30,7 +30,6 @@ add_qml_test(Components Rating)
3030add_qml_test(Components ResponsiveFlowView)
3131add_qml_test(Components ResponsiveGridView)
3232add_qml_test(Components ResponsiveVerticalJournal IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins)
33−add_qml_test(Components SeeMore)
3433add_qml_test(Components Showable)
3534add_qml_test(Components PageHeader)
3635add_qml_test(Components ZoomableImage)
@@ -43,6 +42,7 @@ add_qml_test(Dash GenericScopeView)
4342add_qml_test(Dash/Previews Preview)
4443add_qml_test(Dash/Previews PreviewActions)
4544add_qml_test(Dash/Previews PreviewAudioPlayback)
45+add_qml_test(Dash/Previews PreviewExpandable)
4646add_qml_test(Dash/Previews PreviewHeader)
4747add_qml_test(Dash/Previews PreviewImageGallery)
4848add_qml_test(Dash/Previews PreviewPayments)
tests/qmltests/Components/tst_PageHeader.qml+21−23
@@ -119,29 +119,6 @@ Item {
119119 compare(pageHeader.searchHistory.get(0).query, "humppa4")
120120 }
121121
122− function test_search_indicator() {
123− var searchIndicator = findChild(pageHeader, "searchIndicator")
124− var clearIcon = findChild(pageHeader, "clearIcon")
125−
126− pageHeader.triggerSearch()
127−
128− tryCompare(clearIcon, "visible", false)
129− pageHeader.searchQuery = "ubuntu"
130− tryCompare(clearIcon, "visible", true)
131−
132− pageHeader.searchInProgress = false
133− compare(searchIndicator.running, false, "Search indicator is running.")
134− tryCompare(clearIcon, "visible", true)
135−
136− pageHeader.searchInProgress = true
137− compare(searchIndicator.running, true, "Search indicator isn't running.")
138− tryCompare(clearIcon, "visible", false)
139−
140− pageHeader.searchInProgress = false;
141− compare(searchIndicator.running, false, "Search indicator is running.")
142− tryCompare(clearIcon, "visible", true)
143− }
144−
145122 function test_titleImage() {
146123
147124 var titleImage = findChild(pageHeader, "titleImage");
@@ -192,6 +169,27 @@ Item {
192169 ];
193170 }
194171
172+ function test_pagination() {
173+ var paginationRepeater = findChild(pageHeader, "paginationRepeater");
174+ tryCompare(paginationRepeater, "count", 0);
175+ pageHeader.paginationCount = 5;
176+ tryCompare(paginationRepeater, "count", 5);
177+ for (var i=0; i<pageHeader.paginationCount; i++) {
178+ pageHeader.paginationIndex = i;
179+ for (var j=0; j<paginationRepeater.count; j++) {
180+ var paginationDot = findChild(pageHeader, "paginationDots_"+j);
181+ if (i==j) {
182+ compare(paginationDot.source.toString().indexOf("pagination_dot_on") > -1, true);
183+ } else {
184+ compare(paginationDot.source.toString().indexOf("pagination_dot_off") > -1, true);
185+ }
186+ }
187+ }
188+ pageHeader.paginationIndex = -1;
189+ pageHeader.paginationCount = 0;
190+ tryCompare(paginationRepeater, "count", 0);
191+ }
192+
195193 function test_popup_closing(data) {
196194 searchEnabled = true;
197195 pageHeader.searchHistory.clear();
tests/qmltests/Components/tst_SeeMore.qmldeleted−52
@@ -1,52 +0,0 @@
1−/*
2− * Copyright 2014 Canonical Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−import QtQuick 2.0
18−import QtTest 1.0
19−import Ubuntu.Components 0.1
20−import "../../../qml/Components"
21−import Unity.Test 0.1 as UT
22−
23−Rectangle {
24− id: root
25− width: units.gu(40)
26− height: units.gu(10)
27− color: Theme.palette.selected.background
28−
29− SeeMore {
30− id: seeMore
31− anchors.fill: parent
32− more: false
33− }
34−
35− UT.UnityTestCase {
36− name: "SeeMoreTest"
37− when: windowShown
38−
39− function test_interaction() {
40− var seeMoreLabel = findChild(seeMore, "seeMoreLabel")
41− var seeLessLabel = findChild(seeMore, "seeLessLabel")
42−
43− compare(seeMore.more, false)
44−
45− mouseClick(seeMoreLabel, seeMoreLabel.width / 2, seeMoreLabel.height / 2)
46− compare(seeMore.more, true)
47−
48− mouseClick(seeLessLabel, seeLessLabel.width / 2, seeLessLabel.height / 2)
49− compare(seeMore.more, false)
50− }
51− }
52−}
tests/qmltests/Dash/CardHelpers.js+4−3
@@ -16,7 +16,7 @@
1616
1717.pragma library
1818
19−var components = ["title", "art", "subtitle", "mascot", "emblem", "old-price", "price", "alt-price", "rating", "alt-rating", "summary", "attributes"]
19+var components = ["title", "art", "subtitle", "mascot", "emblem", "old-price", "price", "alt-price", "rating", "alt-rating", "summary", "attributes", "overlayColor"]
2020
2121var defaultLayout = ' \
2222{ \
@@ -31,10 +31,10 @@ var defaultLayout = ' \
3131 "components": { \
3232 "title": null, \
3333 "art": { \
34− "aspect-ratio": 1.0, \
35− "fill-mode": "crop" \
34+ "aspect-ratio": 1.0 \
3635 }, \
3736 "subtitle": null, \
37+ "overlayColor": null, \
3838 "mascot": null, \
3939 "emblem": null, \
4040 "old-price": null, \
@@ -60,6 +60,7 @@ var fullMapping = ' \
6060 "art": "art", \
6161 "subtitle": "subtitle", \
6262 "mascot": "mascot", \
63+ "overlayColor": "overlayColor", \
6364 "summary": "summary", \
6465 "attributes": "attributes" \
6566}'
tests/qmltests/Dash/Previews/tst_PreviewExpandable.qmlnew file+97
@@ -0,0 +1,97 @@
1+/*
2+ * Copyright 2014 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import QtTest 1.0
19+import Ubuntu.Components 0.1
20+import "../../../../qml/Dash/Previews"
21+import Unity.Test 0.1 as UT
22+
23+Rectangle {
24+ id: root
25+ width: units.gu(40)
26+ height: units.gu(80)
27+ color: Theme.palette.selected.background
28+
29+ property string longText: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.\nPhasellus a mi vitae augue rhoncus lobortis ut rutrum metus.\nCurabitur tortor leo, tristique sed mollis quis, condimentum venenatis nibh.\nLorem ipsum dolor sit amet, consectetur adipiscing elit.\nPhasellus a mi vitae augue rhoncus lobortis ut rutrum metus.\nCurabitur tortor leo, tristique sed mollis quis, condimentum venenatis nibh."
30+ property string longText2: "This is a very very very long text. 1 This is a very very very long text. 2 This is a very very very long text. 3 This is a very very very long text. 4 This is a very very very long text. 5 This is a very very very long text. 6 This is a very very very long text. 7 This is a very very very long text. 8 This is a very very very long text. 9 This is a very very very long text. 10 This is a very very very long text. 11 This is a very very very long text."
31+ property string shortText: "This is a short text :)"
32+
33+ property var widgetData: {
34+ "title": "Title here",
35+ "collapsed-widgets": 2,
36+ "widgets": [
37+ { "type": "text", "widgetId" : "text1", "properties" : { "text": longText } },
38+ { "type": "text", "widgetId" : "text2", "properties" : { "text": longText2 } },
39+ { "type": "text", "widgetId" : "text3", "properties" : { "text": shortText } },
40+ { "type": "text", "widgetId" : "text4", "properties" : { "text": longText } }
41+ ]
42+ }
43+
44+ PreviewExpandable {
45+ id: previewExpandable
46+ anchors { left: parent.left; right: parent.right }
47+ widgetData: root.widgetData
48+ }
49+
50+ UT.UnityTestCase {
51+ name: "PreviewExpandableTest"
52+ when: windowShown
53+
54+ function checkInitialState()
55+ {
56+ compare(previewExpandable.expanded, false);
57+
58+ var repeater = findChild(previewExpandable, "repeater")
59+ compare(repeater.count, 4)
60+
61+ compare (repeater.itemAt(0).visible, true);
62+ compare (repeater.itemAt(1).visible, true);
63+ compare (repeater.itemAt(2).visible, false);
64+ compare (repeater.itemAt(3).visible, false);
65+ compare (repeater.itemAt(0).expanded, false);
66+ compare (repeater.itemAt(1).expanded, false);
67+ }
68+
69+ function init() {
70+ }
71+
72+ function test_collapsed_by_default() {
73+ // Nothing init does this already
74+ }
75+
76+ function test_expand_collapse() {
77+ var expandButton = findChild(previewExpandable, "expandButton")
78+ mouseClick(expandButton, expandButton.width / 2, expandButton.height / 2);
79+
80+ var repeater = findChild(previewExpandable, "repeater")
81+ compare(repeater.count, 4)
82+
83+ compare (repeater.itemAt(0).visible, true);
84+ compare (repeater.itemAt(1).visible, true);
85+ compare (repeater.itemAt(2).visible, true);
86+ compare (repeater.itemAt(3).visible, true);
87+ compare (repeater.itemAt(0).expanded, true);
88+ compare (repeater.itemAt(1).expanded, true);
89+ compare (repeater.itemAt(2).expanded, true);
90+ compare (repeater.itemAt(3).expanded, true);
91+
92+ mouseClick(expandButton, expandButton.width / 2, expandButton.height / 2);
93+
94+ checkInitialState();
95+ }
96+ }
97+}
tests/qmltests/Dash/Previews/tst_PreviewTextSummary.qml+4−18
@@ -37,7 +37,7 @@ Rectangle {
3737
3838 property var widgetDataShortText: {
3939 "title": "Title here",
40− "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.\nPhasellus a mi vitae augue rhoncus lobortis ut rutrum metus.\nCurabitur tortor leo, tristique sed mollis quis, condimentum venenatis nibh."
40+ "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus a mi vitae augue rhoncus lobortis ut rutrum metus."
4141 }
4242
4343 PreviewTextSummary {
@@ -72,28 +72,14 @@ Rectangle {
7272 tryCompare(textLabel, "y", 0)
7373 }
7474
75− function test_see_more() {
76− var seeMore = findChild(previewTextSummary, "seeMore")
77−
78− // when it's more than textLabel.maximumCollapsedLineCount lines of text, show SeeMore component
75+ function test_show_collapsed() {
7976 verify(textLabel.lineCount > textLabel.maximumCollapsedLineCount)
80− compare(seeMore.visible, true)
81− verify(seeMore.more === false)
82− verify(textLabel.height < textLabel.contentHeight)
8377
84− // test interactions with SeeMore
85− var seeMoreLabel = findChild(seeMore, "seeMoreLabel")
86− var seeLessLabel = findChild(seeMore, "seeLessLabel")
87− var initialTextLabelHeight = textLabel.height
88− mouseClick(seeMoreLabel, seeMoreLabel.width / 2, seeMoreLabel.height / 2)
89− tryCompare(textLabel, "height", textLabel.contentHeight)
90− mouseClick(seeLessLabel, seeLessLabel.width / 2, seeLessLabel.height / 2)
91− tryCompare(textLabel, "height", initialTextLabelHeight)
78+ previewTextSummary.expanded = false;
79+ tryCompareFunction(function() { return textLabel.height < textLabel.contentHeight; }, true)
9280
93− // text SeeMore automatic hiding
9481 previewTextSummary.widgetData = widgetDataShortText
9582 verify(textLabel.lineCount <= textLabel.maximumCollapsedLineCount)
96− compare(seeMore.visible, false)
9783 tryCompare(textLabel, "height", textLabel.contentHeight)
9884 tryCompare(previewTextSummary, "height", titleLabel.height + textLabel.height)
9985 }
tests/qmltests/Dash/Previews/tst_PreviewWidgetFactory.qml+1
@@ -77,6 +77,7 @@ Rectangle {
7777 return [
7878 { tag: "Actions", type: "actions", source: "PreviewActions.qml" },
7979 { tag: "Audio", type: "audio", source: "PreviewAudioPlayback.qml" },
80+ { tag: "Expandable", type: "expandable", source: "PreviewExpandable.qml" },
8081 { tag: "Gallery", type: "gallery", source: "PreviewImageGallery.qml" },
8182 { tag: "Header", type: "header", source: "PreviewHeader.qml" },
8283 { tag: "Image", type: "image", source: "PreviewZoomableImage.qml" },
tests/qmltests/Dash/tst_Card.qml+15−3
@@ -55,8 +55,8 @@ Rectangle {
5555 "layout": { "components": Helpers.update(JSON.parse(Helpers.fullMapping), { "art": { "aspect-ratio": 2 } }) }
5656 },
5757 {
58− "name": "Art, title - fitted",
59− "layout": { "components": Helpers.update(JSON.parse(Helpers.fullMapping), { "art": { "fill-mode": "fit" } }) }
58+ "name": "Art, title - tall",
59+ "layout": { "components": Helpers.update(JSON.parse(Helpers.fullMapping), { "art": { "aspect-ratio": 0.7 } }) }
6060 },
6161 {
6262 "name": "Art, header, summary - horizontal",
@@ -269,7 +269,7 @@ Rectangle {
269269 { tag: "Small", width: units.gu(12), index: 1 },
270270 { tag: "Large", width: units.gu(38), index: 2 },
271271 { tag: "Wide", height: units.gu(19), size: "large", index: 3 },
272− { tag: "Fit", height: units.gu(38), size: "large", width: units.gu(19), index: 4 },
272+ { tag: "Tall", height: units.gu(38) / 0.7, size: "large", width: units.gu(38), index: 4 },
273273 { tag: "VerticalWidth", width: function() { return headerRow.width + units.gu(1) * 2 }, index: 0 },
274274 { tag: "HorizontalHeight", height: function() { return headerRow.height + units.gu(1) * 2 }, index: 5 },
275275 { tag: "HorizontalWidth", width: function() { return headerRow.x - units.gu(1) }, index: 5 },
@@ -475,5 +475,17 @@ Rectangle {
475475 compare(shape !== null, data.shape);
476476 tryCompare(mascotImage, "visible", !data.shape);
477477 }
478+
479+ function test_touchdown_visibility() {
480+ selector.selectedIndex = 0;
481+
482+ var touchdown = findChild(card, "touchdown");
483+
484+ compare(touchdown.visible, false);
485+ mousePress(card, card.width/2, card.height/2);
486+ compare(touchdown.visible, true);
487+ mouseRelease(card, card.width/2, card.height/2);
488+ compare(touchdown.visible, false);
489+ }
478490 }
479491}
tests/qmltests/Dash/tst_Dash.qml+47−15
@@ -45,12 +45,8 @@ Item {
4545 name: "Dash"
4646 when: windowShown
4747
48− property var scopes
49−
50− Component.onCompleted: {
51− var dashContent = findChild(dash, "dashContent");
52− scopes = dashContent.scopes;
53− }
48+ readonly property Item dashContent: findChild(dash, "dashContent");
49+ readonly property var scopes: dashContent.scopes
5450
5551 function init() {
5652 // clear and reload the scopes.
@@ -59,14 +55,15 @@ Item {
5955 verify(dashContentList != undefined);
6056 tryCompare(dashContentList, "count", 0);
6157 scopes.load();
58+ tryCompare(dashContentList, "currentIndex", 0);
6259 }
6360
6461 function get_scope_data() {
6562 return [
66− { tag: "MockScope1", visualIndex: 0, shouldBeVisible: true },
67− { tag: "MockScope2", visualIndex: -1, shouldBeVisible: false },
68− { tag: "clickscope", visualIndex: 1, shouldBeVisible: true },
69− { tag: "MockScope5", visualIndex: 2, shouldBeVisible: true },
63+ { tag: "MockScope1", visualIndex: 0 },
64+ { tag: "MockScope2", visualIndex: 1 },
65+ { tag: "clickscope", visualIndex: 2 },
66+ { tag: "MockScope5", visualIndex: 3 },
7067 ]
7168 }
7269
@@ -75,10 +72,6 @@ Item {
7572 }
7673
7774 function test_show_scope_on_load(data) {
78− if (data.shouldBeVisible == false) {
79− console.log("Not testing " + data.tag + ": not visible");
80− return;
81− }
8275 var dashContentList = findChild(dash, "dashContentList");
8376
8477 dash.showScopeOnLoaded = data.tag
@@ -86,10 +79,49 @@ Item {
8679 tryCompare(dashContentList, "count", 0);
8780 scopes.load();
8881 tryCompare(scopes, "loaded", true);
89− tryCompare(dashContentList, "count", 4);
82+ tryCompare(dashContentList, "count", 5);
9083
9184 verify(dashContentList != undefined);
9285 tryCompare(dashContentList, "currentIndex", data.visualIndex);
9386 }
87+
88+ function test_setCurrentScope() {
89+ var dashContentList = findChild(dash, "dashContentList");
90+ var startX = dash.width - units.gu(1);
91+ var startY = dash.height / 2;
92+ var stopX = units.gu(1)
93+ var stopY = startY;
94+ var retry = 0;
95+ while (dashContentList.currentIndex != 2 && retry <= 5) {
96+ mouseFlick(dash, startX, startY, stopX, stopY)
97+ waitForRendering(dashContentList)
98+ retry++;
99+ }
100+ compare(dashContentList.currentIndex, 2);
101+ var dashCommunicatorService = findInvisibleChild(dash, "dashCommunicatorService");
102+ dashCommunicatorService.mockSetCurrentScope("clickscope", true, true);
103+ tryCompare(dashContentList, "currentIndex", 1)
104+ }
105+
106+ function test_processing_indicator() {
107+ tryCompare(scopes, "loaded", true);
108+
109+ var processingIndicator = findChild(dash, "processingIndicator");
110+ verify(processingIndicator, "Can't find the processing indicator.");
111+
112+ verify(!processingIndicator.visible, "Processing indicator should be visible.");
113+
114+ tryCompareFunction(function() {
115+ return scopes.getScope(dashContent.currentIndex) != null;
116+ }, true);
117+ var currentScope = scopes.getScope(dashContent.currentIndex);
118+ verify(currentScope, "Can't find the current scope.");
119+
120+ currentScope.setSearchInProgress(true);
121+ tryCompare(processingIndicator, "visible", true);
122+
123+ currentScope.setSearchInProgress(false);
124+ tryCompare(processingIndicator, "visible", false);
125+ }
94126 }
95127}
tests/qmltests/Dash/tst_DashContent.qml+94−95
@@ -43,9 +43,6 @@ Item {
4343 id: dashContent
4444 anchors.fill: parent
4545
46− model: SortFilterProxyModel {
47− model: scopesModel
48− }
4946 scopes : scopesModel
5047 }
5148
@@ -268,117 +265,119 @@ Item {
268265 verify(carouselLV.tileWidth / carouselLV.tileHeight == cardTool.components["art"]["aspect-ratio"]);
269266 }
270267
271− function test_departments() {
272− var dashDepartments = findChild(dashContent, "dashDepartments");
273− compare(dashDepartments.visible, true);
274− compare(dashDepartments.showList, false);
275− waitForRendering(dashDepartments);
276− mouseClick(dashDepartments, 0, 0);
277− compare(dashDepartments.showList, true);
278−
279− var departmentListView = findChild(dashDepartments, "departmentListView");
280− compare(departmentListView.count, 1);
281− tryCompare(departmentListView.currentItem.department, "loaded", true);
282−
283− waitForRendering(departmentListView);
284− waitForRendering(departmentListView.currentItem);
285−
286− var allButton = findChild(dashDepartments, "allButton");
268+ function test_navigations() {
269+ var dashContentList = findChild(dashContent, "dashContentList");
270+ tryCompareFunction(function() { return findChild(dashContentList.currentItem, "dashNavigation") != null; }, true);
271+ var dashNavigation = findChild(dashContentList.currentItem, "dashNavigation");
272+ tryCompare(dashNavigation, "visible", true);
273+ compare(dashNavigation.showList, false);
274+ waitForRendering(dashNavigation);
275+ mouseClick(dashNavigation, 0, 0);
276+ compare(dashNavigation.showList, true);
277+
278+ var navigationListView = findChild(dashNavigation, "navigationListView");
279+ compare(navigationListView.count, 1);
280+ tryCompare(navigationListView.currentItem.navigation, "loaded", true);
281+
282+ waitForRendering(navigationListView);
283+ waitForRendering(navigationListView.currentItem);
284+
285+ var allButton = findChild(dashNavigation, "allButton");
287286 compare(allButton.visible, false);
288287
289− var department = findChild(dashDepartments, "department0child3");
290− mouseClick(department, 0, 0);
291− compare(dashDepartments.showList, false);
292− tryCompare(dashDepartments.currentDepartment, "departmentId", "middle3");
293− tryCompare(departmentListView.currentItem.department, "departmentId", "root");
288+ var navigation = findChild(dashNavigation, "navigation0child3");
289+ mouseClick(navigation, 0, 0);
290+ compare(dashNavigation.showList, false);
291+ tryCompare(dashNavigation.currentNavigation, "navigationId", "middle3");
292+ tryCompare(navigationListView.currentItem.navigation, "navigationId", "root");
294293
295− mouseClick(dashDepartments, 0, 0);
296− compare(dashDepartments.showList, true);
297− waitForRendering(departmentListView);
298− waitForRendering(departmentListView.currentItem);
294+ mouseClick(dashNavigation, 0, 0);
295+ compare(dashNavigation.showList, true);
296+ waitForRendering(navigationListView);
297+ waitForRendering(navigationListView.currentItem);
299298 compare(allButton.visible, true);
300299
301300 mouseClick(allButton, 0, 0);
302− compare(dashDepartments.showList, false);
303− tryCompare(dashDepartments.currentDepartment, "departmentId", "root");
304− tryCompare(departmentListView.currentItem.department, "departmentId", "root");
305−
306− mouseClick(dashDepartments, 0, 0);
307− compare(dashDepartments.showList, true);
308− waitForRendering(departmentListView);
309− waitForRendering(departmentListView.currentItem);
301+ compare(dashNavigation.showList, false);
302+ tryCompare(dashNavigation.currentNavigation, "navigationId", "root");
303+ tryCompare(navigationListView.currentItem.navigation, "navigationId", "root");
304+
305+ mouseClick(dashNavigation, 0, 0);
306+ compare(dashNavigation.showList, true);
307+ waitForRendering(navigationListView);
308+ waitForRendering(navigationListView.currentItem);
310309 compare(allButton.visible, false);
311310
312− department = findChild(dashDepartments, "department0child2");
313− mouseClick(department, 0, 0);
314− compare(dashDepartments.showList, true);
315− tryCompare(dashDepartments.currentDepartment, "departmentId", "middle2");
316− tryCompare(departmentListView.currentItem.department, "departmentId", "middle2");
311+ navigation = findChild(dashNavigation, "navigation0child2");
312+ mouseClick(navigation, 0, 0);
313+ compare(dashNavigation.showList, true);
314+ tryCompare(dashNavigation.currentNavigation, "navigationId", "middle2");
315+ tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle2");
317316
318− var departmentList1 = findChild(dashDepartments, "department1");
319− allButton = findChild(departmentList1, "allButton");
320− var backButton = findChild(findChild(departmentList1, "department1"), "backButton");
317+ var navigationList1 = findChild(dashNavigation, "navigation1");
318+ allButton = findChild(navigationList1, "allButton");
319+ var backButton = findChild(findChild(navigationList1, "navigation1"), "backButton");
321320 compare(allButton.visible, true);
322321 compare(backButton.visible, true);
323322
324− tryCompare(departmentListView, "contentX", departmentList1.x);
325− waitForRendering(departmentListView);
323+ tryCompare(navigationListView, "contentX", navigationList1.x);
324+ waitForRendering(navigationListView);
326325 mouseClick(allButton, 0, 0);
327− compare(dashDepartments.showList, false);
328− tryCompare(dashDepartments.currentDepartment, "departmentId", "middle2");
329− tryCompare(departmentListView.currentItem.department, "departmentId", "middle2");
330−
331− mouseClick(dashDepartments, 0, 0);
332− compare(dashDepartments.showList, true);
333− waitForRendering(departmentListView);
334− waitForRendering(departmentListView.currentItem);
326+ compare(dashNavigation.showList, false);
327+ tryCompare(dashNavigation.currentNavigation, "navigationId", "middle2");
328+ tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle2");
329+
330+ mouseClick(dashNavigation, 0, 0);
331+ compare(dashNavigation.showList, true);
332+ waitForRendering(navigationListView);
333+ waitForRendering(navigationListView.currentItem);
335334 compare(allButton.visible, true);
336335 compare(backButton.visible, true);
337336
338− tryCompare(departmentList1.department, "loaded", true);
339− department = findChild(dashDepartments, "department1child2");
340− mouseClick(department, 0, 0);
341− compare(dashDepartments.showList, false);
342− tryCompare(dashDepartments.currentDepartment, "departmentId", "childmiddle22");
343− tryCompare(departmentListView.currentItem.department, "departmentId", "middle2");
344−
345− mouseClick(dashDepartments, 0, 0);
346− compare(dashDepartments.showList, true);
347− waitForRendering(departmentListView);
348− waitForRendering(departmentListView.currentItem);
349−
350− tryCompare(departmentList1.department, "loaded", true);
351− department = findChild(dashDepartments, "department1child3");
352− mouseClick(department, 0, 0);
353− compare(dashDepartments.showList, false);
354− tryCompare(dashDepartments.currentDepartment, "departmentId", "childmiddle23");
355− tryCompare(departmentListView.currentItem.department, "departmentId", "middle2");
356−
357− mouseClick(dashDepartments, 0, 0);
358− compare(dashDepartments.showList, true);
359− waitForRendering(departmentListView);
360− waitForRendering(departmentListView.currentItem);
337+ tryCompare(navigationList1.navigation, "loaded", true);
338+ navigation = findChild(dashNavigation, "navigation1child2");
339+ mouseClick(navigation, 0, 0);
340+ compare(dashNavigation.showList, false);
341+ tryCompare(dashNavigation.currentNavigation, "navigationId", "childmiddle22");
342+ tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle2");
343+
344+ mouseClick(dashNavigation, 0, 0);
345+ compare(dashNavigation.showList, true);
346+ waitForRendering(navigationListView);
347+ waitForRendering(navigationListView.currentItem);
348+
349+ tryCompare(navigationList1.navigation, "loaded", true);
350+ navigation = findChild(dashNavigation, "navigation1child3");
351+ mouseClick(navigation, 0, 0);
352+ compare(dashNavigation.showList, false);
353+ tryCompare(dashNavigation.currentNavigation, "navigationId", "childmiddle23");
354+ tryCompare(navigationListView.currentItem.navigation, "navigationId", "middle2");
355+
356+ mouseClick(dashNavigation, 0, 0);
357+ compare(dashNavigation.showList, true);
358+ waitForRendering(navigationListView);
359+ waitForRendering(navigationListView.currentItem);
361360 mouseClick(backButton, 0, 0);
362361
363− tryCompare(dashDepartments.currentDepartment, "departmentId", "root");
364− tryCompare(departmentListView.currentItem.department, "departmentId", "root");
365− compare(dashDepartments.showList, true);
366− mouseClick(dashDepartments, 0, 0);
367− compare(dashDepartments.showList, false);
368−
369− mouseClick(dashDepartments, 0, 0);
370− compare(dashDepartments.showList, true);
371− tryCompare(departmentListView.currentItem.department, "loaded", true);
372− department = findChild(dashDepartments, "department0child2");
373− mouseClick(department, 0, 0);
374− compare(dashDepartments.showList, true);
375− departmentList1 = findChild(dashDepartments, "department1");
376− compare(departmentList1.department.loaded, false);
377− tryCompare(dashDepartments.currentDepartment, "departmentId", "middle2");
378− allButton = findChild(departmentList1, "allButton");
379− tryCompare(dashDepartments.currentDepartment, "departmentId", "middle2");
362+ tryCompare(dashNavigation.currentNavigation, "navigationId", "root");
363+ tryCompare(navigationListView.currentItem.navigation, "navigationId", "root");
364+ compare(dashNavigation.showList, true);
365+ mouseClick(dashNavigation, 0, 0);
366+ compare(dashNavigation.showList, false);
367+
368+ mouseClick(dashNavigation, 0, 0);
369+ compare(dashNavigation.showList, true);
370+ tryCompare(navigationListView.currentItem.navigation, "loaded", true);
371+ navigation = findChild(dashNavigation, "navigation0child2");
372+ mouseClick(navigation, 0, 0);
373+ compare(dashNavigation.showList, true);
374+ navigationList1 = findChild(dashNavigation, "navigation1");
375+ compare(navigationList1.navigation.loaded, false);
376+ tryCompare(dashNavigation.currentNavigation, "navigationId", "middle2");
377+ allButton = findChild(navigationList1, "allButton");
378+ tryCompare(dashNavigation.currentNavigation, "navigationId", "middle2");
380379 mouseClick(allButton, 0, 0);
381− tryCompare(dashDepartments.currentDepartment, "departmentId", "middle2");
380+ tryCompare(dashNavigation.currentNavigation, "navigationId", "middle2");
382381 }
383382 }
384383}
tests/qmltests/Dash/tst_GenericScopeView.qml+3−2
@@ -129,8 +129,9 @@ Item {
129129 var initialHeight = category.height;
130130 mouseClick(seeAll, seeAll.width / 2, seeAll.height / 2);
131131 verify(category.expanded);
132− tryCompareFunction(function() { return category.height > initialHeight; }, true);
132+ tryCompare(category, "height", category.item.expandedHeight + seeAll.height);
133133
134+ waitForRendering(seeAll);
134135 mouseClick(seeAll, seeAll.width / 2, seeAll.height / 2);
135136 verify(!category.expanded);
136137 }
@@ -319,7 +320,7 @@ Item {
319320 units.gu(2),
320321 testCase.previewListView.height / 2);
321322 tryCompare(previewListViewList, "moving", false);
322− tryCompare(testCase.previewListView.currentItem, "objectName", "previewItem" + i);
323+ tryCompare(testCase.previewListView.currentItem, "objectName", "preview" + i);
323324
324325 }
325326 closePreview();
tests/qmltests/Panel/Indicators/tst_MenuItemFactory.qml+47
@@ -112,6 +112,7 @@ Item {
112112
113113 { tag: 'wifisection', type: "unity.widgets.systemsettings.tablet.wifisection", objectName: "wifiSection" },
114114 { tag: 'accesspoint', type: "unity.widgets.systemsettings.tablet.accesspoint", objectName: "accessPoint" },
115+ { tag: 'modeminfoitem', type: "com.canonical.indicator.network.modeminfoitem", objectName: "modemInfoItem" },
115116
116117 { tag: 'unknown', type: "", objectName: "standardMenu"}
117118 ];
@@ -542,6 +543,52 @@ Item {
542543 compare(loader.item.enabled, data.enabled, "Enabled does not match data");
543544 }
544545
546+ function test_create_modemInfoItem_data() {
547+ // ModemInfoItem gets all it's data through the actions.
548+ return [
549+ {
550+ label: "",
551+ enabled: true,
552+ checked: false,
553+ statusLabelAction: "action::statusLabel",
554+ statusIconAction: "action::statusIcon",
555+ connectivityIconAction: "action::connectivityIcon",
556+ simIdentifierLabelAction: "action::simIdentifierLabel",
557+ roamingAction: "action::roaming",
558+ unlockAction: "action::unlock"
559+ }
560+ ];
561+ }
562+
563+ function test_create_modemInfoItem(data) {
564+ menuData.type = "com.canonical.indicator.network.modeminfoitem";
565+ menuData.label = data.label;
566+ menuData.sensitive = data.enabled;
567+ menuData.isToggled = data.checked;
568+ menuData.ext = {
569+ 'xCanonicalModemStatusLabelAction': data.statusLabelAction,
570+ 'xCanonicalModemStatusIconAction': data.statusIconAction,
571+ 'xCanonicalModemConnectivityIconAction': data.connectivityIconAction,
572+ 'xCanonicalModemSimIdentifierLabelAction': data.simIdentifierLabelAction,
573+ 'xCanonicalModemRoamingAction': data.roamingAction,
574+ 'xCanonicalModemLockedAction': data.unlockAction,
575+ };
576+
577+ loader.data = menuData;
578+ loader.sourceComponent = factory.load(menuData);
579+ tryCompareFunction(function() { return loader.item != undefined; }, true);
580+ compare(loader.item.objectName, "modemInfoItem", "Should have created a modem info item.");
581+ compare(loader.item.text, data.label, "Label does not match data");
582+
583+ compare(loader.item.statusLabelAction.name, data.statusLabelAction, "StatusLabel action incorrect");
584+ compare(loader.item.statusIconAction.name, data.statusIconAction, "StatusIcon action incorrect");
585+ compare(loader.item.connectivityIconAction.name, data.connectivityIconAction, "ConnectivityIcon action incorrect");
586+ compare(loader.item.simIdentifierLabelAction.name, data.simIdentifierLabelAction, " action incorrect");
587+ compare(loader.item.roamingAction.name, data.roamingAction, "Roaming action incorrect");
588+ compare(loader.item.unlockAction.name, data.unlockAction, "Unlock action incorrect");
589+ compare(loader.item.enabled, data.enabled, "Enabled does not match data");
590+ }
591+
545592 function test_create_messageItem() {
546593 menuData.type = "com.canonical.indicator.messages.messageitem";
547594
tests/qmltests/Stages/tst_PhoneStage.qml+3−2
@@ -231,9 +231,10 @@ Item {
231231 }
232232
233233 function cleanup() {
234− while (ApplicationManager.count > 0) {
234+ while (ApplicationManager.count > 1) {
235235 var oldCount = ApplicationManager.count;
236− ApplicationManager.stopApplication(ApplicationManager.get(0).appId)
236+ var closingIndex = ApplicationManager.focusedApplicationId == "unity8-dash" ? 1 : 0
237+ ApplicationManager.stopApplication(ApplicationManager.get(closingIndex).appId)
237238 tryCompare(ApplicationManager, "count", oldCount - 1)
238239 }
239240 }
tests/qmltests/tst_Shell.qml+62−275
@@ -56,49 +56,20 @@ Item {
5656 signalName: "sessionStarted"
5757 }
5858
59+ SignalSpy {
60+ id: dashCommunicatorSpy
61+ signalName: "setCurrentScopeCalled"
62+ }
63+
5964 UT.UnityTestCase {
6065 name: "Shell"
6166 when: windowShown
6267
6368 function initTestCase() {
64− var ok = false;
65− var attempts = 0;
66− var maxAttempts = 1000;
67−
68− // Qt loads a qml scene asynchronously. So early on, some findChild() calls made in
69− // tests may fail because the desired child item wasn't loaded yet.
70− // Thus here we try to ensure the scene has been fully loaded before proceeding with the tests.
71− // As I couldn't find an API in QQuickView & friends to tell me that the scene is 100% loaded
72− // (all items instantiated, etc), I resort to checking the existence of some key items until
73− // repeatedly until they're all there.
74− do {
75− var dashContentList = findChild(shell, "dashContentList");
76− waitForRendering(dashContentList);
77− var homeLoader = findChild(dashContentList, "clickscope loader");
78− ok = homeLoader !== null
79− && homeLoader.item !== undefined;
80−
81− var greeter = findChild(shell, "greeter");
82− ok &= greeter !== null;
83−
84− var launcherPanel = findChild(shell, "launcherPanel");
85− ok &= launcherPanel !== null;
86−
87− attempts++;
88− if (!ok) {
89− console.log("Attempt " + attempts + " failed. Waiting a bit before trying again.");
90− // wait a bit before retrying
91− wait(100);
92− } else {
93− console.log("All seem fine after " + attempts + " attempts.");
94− }
95− } while (!ok && attempts <= maxAttempts);
96−
97− verify(ok);
98−
9969 swipeAwayGreeter();
10070
10171 sessionSpy.target = findChild(shell, "greeter")
72+ dashCommunicatorSpy.target = findInvisibleChild(shell, "dashCommunicator");
10273 }
10374
10475 function cleanup() {
@@ -111,56 +82,16 @@ Item {
11182 // kill all (fake) running apps
11283 killApps(ApplicationManager);
11384
114− var dashContent = findChild(shell, "dashContent");
115− dashContent.closePreview();
116−
117− var dashHome = findChild(shell, "clickscope loader");
118− swipeUntilScopeViewIsReached(dashHome);
119−
85+ waitForUIToSettle();
12086 hideIndicators();
12187 }
12288
123− function killApps(apps) {
124− if (!apps) return;
125− while (apps.count > 0) {
126− ApplicationManager.stopApplication(apps.get(0).appId);
89+ function killApps() {
90+ while (ApplicationManager.count > 1) {
91+ var appIndex = ApplicationManager.get(0).appId == "unity8-dash" ? 1 : 0
92+ ApplicationManager.stopApplication(ApplicationManager.get(appIndex).appId);
12793 }
128− compare(ApplicationManager.count, 0)
129− }
130−
131− /*
132− Test the effect of a right-edge drag on the dash in 3 situations:
133− 1 - when no application has been launched yet
134− 2 - when there's a minimized application
135− 3 - after the last running application has been closed/stopped
136−
137− The behavior of Dash on 3 should be the same as on 1.
138− */
139− function test_rightEdgeDrag() {
140− checkRightEdgeDragWithNoRunningApps();
141−
142− dragLauncherIntoView();
143−
144− // Launch an app from the launcher
145− tapOnAppIconInLauncher();
146− waitUntilApplicationWindowIsFullyVisible();
147−
148− // Minimize the application we just launched
149− swipeFromLeftEdge(units.gu(27));
150−
151− waitForUIToSettle();
152−
153− checkRightEdgeDragWithMinimizedApp();
154−
155− // Minimize that application once again
156− swipeFromLeftEdge(units.gu(27));
157−
158− // Right edge behavior should now be the same as before that app,
159− // was launched. Manually cleanup beforehand to get to initial
160− // state.
161− cleanup();
162− waitForUIToSettle();
163− checkRightEdgeDragWithNoRunningApps();
94+ compare(ApplicationManager.count, 1)
16495 }
16596
16697 function test_leftEdgeDrag_data() {
@@ -183,7 +114,7 @@ Item {
183114
184115 swipeFromLeftEdge(data.swipeLength);
185116 if (data.appHides)
186− waitUntilApplicationWindowIsFullyHidden();
117+ waitUntilDashIsFocused();
187118 else
188119 waitUntilApplicationWindowIsFullyVisible();
189120 }
@@ -239,100 +170,18 @@ Item {
239170 waitForRendering(greeter);
240171 }
241172
242− /*
243− Perform a right-edge drag when the Dash is being show and there are
244− no running/minimized apps to be restored.
245−
246− The expected behavior is that an animation should be played to hint the
247− user that his right-edge drag gesture has been successfully recognized
248− but there is no application to be brought to foreground.
249− */
250− function checkRightEdgeDragWithNoRunningApps() {
251− var touchX = shell.width - (shell.edgeSize / 2);
252− var touchY = shell.height / 2;
253−
254− var dash = findChild(shell, "dash");
255− // check that dash has normal scale and opacity
256− tryCompare(dash, "contentScale", 1.0);
257− tryCompare(dash, "opacity", 1.0);
258−
259− touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY,
260− true /* beginTouch */, false /* endTouch */, units.gu(10), 50);
261−
262− // check that Dash has been scaled down and had its opacity reduced
263− tryCompareFunction(function() { return dash.contentScale <= 0.9; }, true);
264− tryCompareFunction(function() { return dash.opacity <= 0.5; }, true);
265−
266− touchRelease(shell, shell.width * 0.1, touchY);
267−
268− // and now everything should have gone back to normal
269− tryCompare(dash, "contentScale", 1.0);
270− tryCompare(dash, "opacity", 1.0);
271− }
272−
273− /*
274− Perform a right-edge drag when the Dash is being show and there is
275− a running/minimized app to be restored.
276−
277− The expected behavior is that the dash should fade away and ultimately be
278− made invisible once the gesture is finished as the restored app will now
279− be on foreground.
280− */
281− function checkRightEdgeDragWithMinimizedApp() {
282− var touchX = shell.width - (shell.edgeSize / 2);
283− var touchY = shell.height / 2;
284−
285− var dash = findChild(shell, "dash");
286− // check that dash has normal scale and opacity
287− tryCompare(dash, "contentScale", 1.0);
288− tryCompare(dash, "opacity", 1.0);
289−
290− touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY,
291− true /* beginTouch */, false /* endTouch */, units.gu(10), 50);
292−
293− // check that Dash has been scaled down and had its opacity reduced
294− tryCompareFunction(function() { return dash.contentScale <= 0.9; }, true);
295− tryCompareFunction(function() { return dash.opacity <= 0.5; }, true);
296−
297− touchRelease(shell, shell.width * 0.1, touchY);
298−
299− // dash should have gone away, now that the app is on foreground
300− tryCompare(dash, "visible", false);
301− }
302−
303173 /*
304174 Regression test for bug https://bugs.launchpad.net/touch-preview-images/+bug/1193419
305175
306− When the user minimizes an application (left-edge swipe) he should always end up in the "Running Apps"
307− category of the "Applications" scope view.
176+ When the user minimizes an application (left-edge swipe) he should always end up in the
177+ "Applications" scope view.
308178
309179 Steps:
310− - go to apps lens
311− - scroll to the bottom
312− - reveal launcher and launch an app
180+ - reveal launcher and launch an app that covers the dash
313181 - perform long left edge swipe to go minimize the app and go back to the dash.
314−
315− Expected Results
316− - apps lens shown
182+ - verify the setCurrentScope() D-Bus call to the dash has been called for the correct scope id.
317183 */
318184 function test_minimizingAppTakesToDashApps() {
319− var dashApps = findChild(shell, "clickscope");
320− swipeUntilScopeViewIsReached(dashApps);
321−
322− // swipe finger up until the running/recent apps section (which we assume
323− // it's the first one) is as far from view as possible.
324− // We also assume that DashApps is tall enough that it's scrollable
325− var appsCategoryListView = findChild(dashApps, "categoryListView");
326− while (!appsCategoryListView.atYEnd) {
327− swipeUpFromCenter();
328− tryCompare(appsCategoryListView, "moving", false);
329− }
330−
331− // Switch away from the Applications scope.
332− swipeRightFromCenter();
333− waitUntilItemStopsMoving(dashApps);
334− verify(!itemIsOnScreen(dashApps));
335−
336185 dragLauncherIntoView();
337186
338187 // Launch an app from the launcher
@@ -340,19 +189,16 @@ Item {
340189
341190 waitUntilApplicationWindowIsFullyVisible();
342191
343− // Dragging launcher into view with a little bit of gap (units.gu(1)) should switch to Apps scope
344− dragLauncherIntoView();
345− verify(itemIsOnScreen(dashApps));
192+ verify(ApplicationManager.focusedApplicationId !== "unity8-dash")
346193
194+ dashCommunicatorSpy.clear();
347195 // Minimize the application we just launched
348196 swipeFromLeftEdge(units.gu(27));
349197
350− // Wait for the whole UI to settle down
351− waitUntilApplicationWindowIsFullyHidden();
352− waitUntilItemStopsMoving(dashApps);
353− tryCompare(appsCategoryListView, "moving", false);
198+ tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
354199
355− verify(itemIsOnScreen(dashApps));
200+ compare(dashCommunicatorSpy.count, 1);
201+ compare(dashCommunicatorSpy.signalArguments[0][0], "clickscope");
356202 }
357203
358204 function test_showInputMethod() {
@@ -409,9 +255,14 @@ Item {
409255
410256 // Wait for the whole UI to settle down
411257 function waitForUIToSettle() {
412− waitUntilApplicationWindowIsFullyHidden();
413− var dashContentList = findChild(shell, "dashContentList");
414− tryCompare(dashContentList, "moving", false);
258+ var launcher = findChild(shell, "launcherPanel")
259+ tryCompareFunction(function() {return launcher.x === 0 || launcher.x === -launcher.width;}, true);
260+ if (launcher.x === 0) {
261+ mouseClick(shell, shell.width / 2, shell.height / 2)
262+ }
263+ tryCompare(launcher, "x", -launcher.width)
264+
265+ waitForRendering(shell)
415266 }
416267
417268 function dragToCloseIndicatorsPanel() {
@@ -468,24 +319,13 @@ Item {
468319 }
469320
470321 function waitUntilApplicationWindowIsFullyVisible() {
471− var underlay = findChild(shell, "underlay");
472− tryCompare(underlay, "visible", false);
322+ var appDelegate = findChild(shell, "appDelegate0")
323+ var surfaceContainer = findChild(appDelegate, "surfaceContainer");
324+ tryCompareFunction(function() { return surfaceContainer.surface !== null; }, true);
473325 }
474326
475− function waitUntilApplicationWindowIsFullyHidden() {
476− var stages = findChild(shell, "stages");
477− tryCompare(stages, "fullyHidden", true);
478− }
479−
480− function swipeUntilScopeViewIsReached(scopeView) {
481− while (!itemIsOnScreen(scopeView)) {
482− if (itemIsToLeftOfScreen(scopeView)) {
483− swipeRightFromCenter();
484− } else {
485− swipeLeftFromCenter();
486− }
487− waitUntilItemStopsMoving(scopeView);
488− }
327+ function waitUntilDashIsFocused() {
328+ tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
489329 }
490330
491331 function swipeFromLeftEdge(swipeLength) {
@@ -494,24 +334,6 @@ Item {
494334 touchFlick(shell, touchStartX, touchStartY, swipeLength, touchStartY);
495335 }
496336
497− function swipeLeftFromCenter() {
498− var touchStartX = shell.width * 3 / 4;
499− var touchStartY = shell.height / 2;
500− touchFlick(shell, touchStartX, touchStartY, 0, touchStartY);
501− }
502−
503− function swipeRightFromCenter() {
504− var touchStartX = shell.width * 3 / 4;
505− var touchStartY = shell.height / 2;
506− touchFlick(shell, touchStartX, touchStartY, shell.width, touchStartY);
507− }
508−
509− function swipeUpFromCenter() {
510− var touchStartX = shell.width / 2;
511− var touchStartY = shell.height / 2;
512− touchFlick(shell, touchStartX, touchStartY, touchStartX, 0);
513− }
514−
515337 function itemIsOnScreen(item) {
516338 var itemRectInShell = item.mapToItem(shell, 0, 0, item.width, item.height);
517339
@@ -521,64 +343,6 @@ Item {
521343 && itemRectInShell.y + itemRectInShell.height <= shell.height;
522344 }
523345
524− function itemIsToLeftOfScreen(item) {
525− var itemRectInShell = item.mapToItem(shell, 0, 0, item.width, item.height);
526− return itemRectInShell.x < 0;
527− }
528−
529− function waitUntilItemStopsMoving(item) {
530− var itemRectInShell = item.mapToItem(shell, 0, 0, item.width, item.height);
531− var previousX = itemRectInShell.x;
532− var previousY = itemRectInShell.y;
533− var isStill = false;
534−
535− do {
536− wait(100);
537− itemRectInShell = item.mapToItem(shell, 0, 0, item.width, item.height);
538− if (itemRectInShell.x == previousX && itemRectInShell.y == previousY) {
539− isStill = true;
540− } else {
541− previousX = itemRectInShell.x;
542− previousY = itemRectInShell.y;
543− }
544− } while (!isStill);
545− }
546−
547− function test_DashShown_data() {
548− return [
549− {tag: "in focus", greeter: false, app: false, launcher: false, indicators: false, expectedShown: true},
550− {tag: "under greeter", greeter: true, app: false, launcher: false, indicators: false, expectedShown: false},
551− {tag: "under app", greeter: false, app: true, launcher: false, indicators: false, expectedShown: false},
552− {tag: "under launcher", greeter: false, app: false, launcher: true, indicators: false, expectedShown: true},
553− {tag: "under indicators", greeter: false, app: false, launcher: false, indicators: true, expectedShown: false},
554− ]
555− }
556−
557− function test_DashShown(data) {
558− if (data.greeter) {
559− // Swipe the greeter in
560− var greeter = findChild(shell, "greeter");
561− Powerd.displayPowerStateChange(Powerd.Off, 0);
562− tryCompare(greeter, "showProgress", 1);
563− }
564−
565− if (data.app) {
566− dragLauncherIntoView();
567− tapOnAppIconInLauncher();
568− }
569−
570− if (data.launcher) {
571− dragLauncherIntoView();
572− }
573−
574− if (data.indicators) {
575− showIndicators();
576− }
577−
578− var dash = findChild(shell, "dash");
579− tryCompare(dash, "shown", data.expectedShown);
580− }
581−
582346 function test_focusRequestedHidesGreeter() {
583347 var greeter = findChild(shell, "greeter");
584348
@@ -587,10 +351,9 @@ Item {
587351 tryCompareFunction(function() { return app.surface != null }, true);
588352
589353 // Minimize the application we just launched
590− swipeFromLeftEdge(units.gu(27));
354+ swipeFromLeftEdge(units.gu(26) + 1);
591355
592− // Wait for the whole UI to settle down
593− waitUntilApplicationWindowIsFullyHidden();
356+ waitUntilDashIsFocused();
594357
595358 greeter.show();
596359 tryCompare(greeter, "showProgress", 1);
@@ -606,8 +369,9 @@ Item {
606369
607370 showIndicators();
608371
372+ var oldCount = ApplicationManager.count;
609373 ApplicationManager.startApplication("camera-app");
610− tryCompare(ApplicationManager, "count", 1);
374+ tryCompare(ApplicationManager, "count", oldCount + 1);
611375
612376 tryCompare(indicators, "fullyClosed", true);
613377 }
@@ -616,6 +380,7 @@ Item {
616380 function test_showGreeterDBusCall() {
617381 var greeter = findChild(shell, "greeter")
618382 tryCompare(greeter, "showProgress", 0)
383+ waitForRendering(greeter);
619384 LightDM.Greeter.showGreeter()
620385 tryCompare(greeter, "showProgress", 1)
621386 }
@@ -624,6 +389,7 @@ Item {
624389 sessionSpy.clear()
625390
626391 var greeter = findChild(shell, "greeter")
392+ waitForRendering(greeter)
627393 greeter.show()
628394 tryCompare(greeter, "showProgress", 1)
629395
@@ -644,5 +410,26 @@ Item {
644410 ApplicationManager.requestFocusApplication("gallery-app");
645411 tryCompare(panel, "fullscreenMode", false);
646412 }
413+
414+ function test_leftEdgeDragFullscreen() {
415+ var panel = findChild(shell, "panel");
416+ tryCompare(panel, "fullscreenMode", false)
417+
418+ ApplicationManager.startApplication("camera-app");
419+ tryCompare(panel, "fullscreenMode", true)
420+
421+ var touchStartX = 2;
422+ var touchStartY = shell.height / 2;
423+
424+ touchFlick(shell, touchStartX, touchStartY, units.gu(2), touchStartY, true, false);
425+
426+ compare(panel.fullscreenMode, true);
427+
428+ touchFlick(shell, units.gu(2), touchStartY, units.gu(10), touchStartY, false, false);
429+
430+ tryCompare(panel, "fullscreenMode", false);
431+
432+ touchRelease(shell);
433+ }
647434 }
648435}
tests/qmltests/tst_ShellWithPin.qml+4−37
@@ -58,40 +58,6 @@ Item {
5858 when: windowShown
5959
6060 function initTestCase() {
61− var ok = false;
62− var attempts = 0;
63− var maxAttempts = 1000;
64−
65− // Qt loads a qml scene asynchronously. So early on, some findChild() calls made in
66− // tests may fail because the desired child item wasn't loaded yet.
67− // Thus here we try to ensure the scene has been fully loaded before proceeding with the tests.
68− // As I couldn't find an API in QQuickView & friends to tell me that the scene is 100% loaded
69− // (all items instantiated, etc), I resort to checking the existence of some key items until
70− // repeatedly until they're all there.
71− do {
72− var dashContentList = findChild(shell, "dashContentList");
73− waitForRendering(dashContentList);
74− var homeLoader = findChild(dashContentList, "clickscope loader");
75− ok = homeLoader !== null
76− && homeLoader.item !== undefined;
77−
78− var greeter = findChild(shell, "greeter");
79− ok &= greeter !== null;
80−
81− var launcherPanel = findChild(shell, "launcherPanel");
82− ok &= launcherPanel !== null;
83−
84− attempts++;
85− if (!ok) {
86− console.log("Attempt " + attempts + " failed. Waiting a bit before trying again.");
87− // wait a bit before retrying
88− wait(100);
89− } else {
90− console.log("All seem fine after " + attempts + " attempts.");
91− }
92− } while (!ok && attempts <= maxAttempts);
93−
94− verify(ok);
9561
9662 sessionSpy.target = findChild(shell, "greeter")
9763 }
@@ -110,10 +76,11 @@ Item {
11076 }
11177
11278 function killApps() {
113− while (ApplicationManager.count > 0) {
114− ApplicationManager.stopApplication(ApplicationManager.get(0).appId)
79+ while (ApplicationManager.count > 1) {
80+ var appIndex = ApplicationManager.get(0).appId == "unity8-dash" ? 1 : 0
81+ ApplicationManager.stopApplication(ApplicationManager.get(appIndex).appId);
11582 }
116− compare(ApplicationManager.count, 0)
83+ compare(ApplicationManager.count, 1)
11784 }
11885
11986 function swipeAwayGreeter() {