on xenial help sign in
Merge trunk
commitb7eefec5ff827297c244c402881b024050c6de79 authorMichał Sawicz <michal.sawicz@canonical.com> dateTue 29 Apr 2014, 14:26 (12 years ago) parent9e24f67ba71822
62 files changed+1072−897
cmake/modules/QmlTest.cmake+16−1 data/unity8.conf+2−1 debian/changelog+132 debian/control−3 debian/unity8-autopilot.install+1 plugins/Unity/Indicators/CMakeLists.txt−3 plugins/Unity/Indicators/indicatorsmanager.cpp−33 plugins/Unity/Indicators/indicatorsmanager.h−2 po/br.po+164 po/ug.po+8−24 qml/Components/AnimationControllerWithSignals.qml−64 qml/Components/Carousel.qml+13−19 qml/Components/FilterGrid.qml+1 qml/Components/ResponsiveGridView.qml+1 qml/Dash/Card.qml+99−79 qml/Dash/CardCarousel.qml+1−1 qml/Dash/CardFilterGrid.qml+4−2 qml/Dash/CardHeader.qml+41−31 qml/Dash/DashContent.qml+15−28 qml/Dash/DashRenderer.qml+2 qml/Dash/GenericScopeView.qml+17−16 qml/Dash/Previews/PreviewRatingDisplay.qml+4−2 qml/Dash/Previews/PreviewRatingInput.qml+4−2 qml/Dash/ScopeDelegateMapper.qml−30 qml/Notifications/NotificationMenuItemFactory.qml+2−1 qml/Shell.qml+5−12 qml/graphics/phone_background.jpg qml/graphics/tablet_background.jpg run+15−1 tests/autopilot/unity8/application_lifecycle/tests/__init__.py+49 tests/autopilot/unity8/application_lifecycle/tests/test_application_lifecycle.py+56−72 tests/autopilot/unity8/application_lifecycle/tests/test_url_dispatcher.py+8−39 tests/autopilot/unity8/indicators/tests/__init__.py+17 tests/autopilot/unity8/indicators/tests/test_indicators.py+69−13 tests/autopilot/unity8/process_helpers.py+16 tests/autopilot/unity8/shell/emulators/__init__.py+2−6 tests/autopilot/unity8/shell/emulators/dash.py+25−5 tests/autopilot/unity8/shell/emulators/main_window.py+32−2 tests/autopilot/unity8/shell/tests/test_emulators.py+19−11 tests/autopilot/unity8/shell/tests/test_notifications.py+3−4 tests/autopilot/unity8/shell/tests/test_system_integration.py+1−1 tests/qmltests/CMakeLists.txt+1−1 tests/qmltests/Components/tst_AnimationControllerWithSignals.qml−142 tests/qmltests/Components/tst_Carousel.qml+34−7 tests/qmltests/Dash/qml/FakeScopeView.qml−45 tests/qmltests/Dash/qml/fake_generic_scopeView.qml−24 tests/qmltests/Dash/qml/fake_scopeView1.qml−24 tests/qmltests/Dash/qml/fake_scopeView2.qml−24 tests/qmltests/Dash/qml/fake_scopeView3.qml−24 tests/qmltests/Dash/qml/fake_scopeView4.qml−24 tests/qmltests/Dash/tst_Card.qml+12−10 tests/qmltests/Dash/tst_CardBenchmark.qml+94 tests/qmltests/Dash/tst_CardHeader.qml+1−1 tests/qmltests/Dash/tst_Dash.qml−11 tests/qmltests/Dash/tst_DashContent.qml+27−51 tests/qmltests/Dash/tst_GenericScopeView.qml+1−1 tests/qmltests/Launcher/tst_Launcher.qml+1 tests/qmltests/Panel/Indicators/tst_MenuItemFactory.qml+1 tests/qmltests/Panel/Indicators/tst_MessageMenuItemFactory.qml+1 tests/qmltests/tst_Shell.qml+11 tools/CMakeLists.txt+2 tools/unlock-device+42
cmake/modules/QmlTest.cmake+16−1
@@ -58,7 +58,15 @@ macro(add_manual_qml_test SUBPATH COMPONENT_NAME)
5858
5959endmacro(add_manual_qml_test)
6060
61+macro(add_qml_benchmark SUBPATH COMPONENT_NAME ITERATIONS)
62+ add_qml_test_internal(${SUBPATH} ${COMPONENT_NAME} ${ITERATIONS})
63+endmacro(add_qml_benchmark)
64+
6165macro(add_qml_test SUBPATH COMPONENT_NAME)
66+ add_qml_test_internal(${SUBPATH} ${COMPONENT_NAME} 0)
67+endmacro(add_qml_test)
68+
69+macro(add_qml_test_internal SUBPATH COMPONENT_NAME ITERATIONS)
6270 set(options NO_ADD_TEST NO_TARGETS)
6371 set(multi_value_keywords IMPORT_PATHS TARGETS PROPERTIES ENVIRONMENT)
6472
@@ -88,10 +96,17 @@ macro(add_qml_test SUBPATH COMPONENT_NAME)
8896 set(function_ARGS "")
8997 endif()
9098
99+ if (${ITERATIONS} GREATER 0)
100+ set(ITERATIONS_STRING "-iterations" ${ITERATIONS})
101+ else()
102+ set(ITERATIONS_STRING "")
103+ endif()
104+
91105 set(qmltest_command
92106 env ${qmltest_ENVIRONMENT}
93107 ${qmltestrunner_exe} -input ${CMAKE_CURRENT_SOURCE_DIR}/${qmltest_FILE}.qml
94108 ${qmltestrunner_imports}
109+ ${ITERATIONS_STRING}
95110 -o ${CMAKE_BINARY_DIR}/${qmltest_TARGET}.xml,xunitxml
96111 -o -,txt
97112 ${function_ARGS}
@@ -114,7 +129,7 @@ macro(add_qml_test SUBPATH COMPONENT_NAME)
114129 add_qmltest_target(${qmltest_TARGET} "${qmltest_command}" TRUE ${qmltest_NO_ADD_TEST})
115130 add_qmltest_target(${qmltest_xvfb_TARGET} "${qmltest_xvfb_command}" ${qmltest_NO_TARGETS} TRUE)
116131 add_manual_qml_test(${SUBPATH} ${COMPONENT_NAME} ${ARGN})
117−endmacro(add_qml_test)
132+endmacro(add_qml_test_internal)
118133
119134macro(add_binary_qml_test CLASS_NAME LD_PATH DEPS)
120135 set(testCommand
data/unity8.conf+2−1
@@ -1,7 +1,7 @@
11description "Unity Shell v8"
22author "Ricardo Mendoza <ricmm@ubuntu.com>"
33
4−emits scope-ui-starting
4+emits scope-ui-starting indicator-services-start
55
66start on ((xsession SESSION=ubuntu-touch) or (xsession SESSION=ubuntu-touch-surfaceflinger)) and started dbus
77stop on desktop-end
@@ -42,6 +42,7 @@ pre-start script
4242 fi
4343
4444 initctl emit scope-ui-starting
45+ initctl emit indicator-services-start
4546end script
4647
4748exec ${BINARY:-unity8} $ARGS
debian/changelog+132
@@ -1,3 +1,135 @@
1+unity8 (7.85+14.04.20140416-0ubuntu1) trusty; urgency=low
2+
3+ [ Albert Astals ]
4+ * Fix last item X position Fixes clicking on the last item sometimes
5+ not working (LP: #1301871)
6+ * Use upstart in ./run Makes it so that you can use the lock button on
7+ the device without getting that nasty hwc crash
8+ * Remove AnimationControllerWithSignals.
9+ * Use the correct delegate base item for the Carousel test
10+ * Some simplification in DashContent Kill the ScopeDelegateMapper in
11+ favour of a simple if (that will eventually go away). Removal of all
12+ the fake scopes in the tests that added nothing of value to the
13+ tests. Removal of movementEnded signal that was unused. Removal of
14+ movementStarted and positionedAtBeginning signals that were being
15+ used as function calls. Rework DashContent tests so they what the
16+ function does what it is supposed to do instead of just making sure
17+ QML signals work .
18+ * Improve Card creation time by adding loaders that make sure only
19+ what's needed is loaded In my computer it goes from RESULT :
20+ qmltestrunner::benchmark_time:"cardTitleArtSubtitleMascotSummaryMode
21+ l": 3.217 msecs per iteration (total: 3,218, iterations: 1000)
22+ RESULT :
23+ qmltestrunner::benchmark_time:"cardTitleArtSubtitleMascotModel":
24+ 1.647 msecs per iteration (total: 1,648, iterations: 1000) RESULT :
25+ qmltestrunner::benchmark_time:"cardTitleArtSubtitleModel": 1.514
26+ msecs per iteration (total: 1,515, iterations: 1000) RESULT :
27+ qmltestrunner::benchmark_time:"cardTitleArtModel": 1.471 msecs per
28+ iteration (total: 1,471, iterations: 1000) RESULT :
29+ qmltestrunner::benchmark_time:"cardArtModel": 1.447 msecs per
30+ iteration (total: 1,448, iterations: 1000) RESULT :
31+ qmltestrunner::benchmark_time:"cardTitleModel": 1.276 msecs per
32+ iteration (total: 1,276, iterations: 1000) to RESULT :
33+ qmltestrunner::benchmark_time:"cardTitleArtSubtitleMascotSummaryMode
34+ l": 2.916 msecs per iteration (total: 2,917, iterations: 1000)
35+ RESULT :
36+ qmltestrunner::benchmark_time:"cardTitleArtSubtitleMascotModel":
37+ 1.504 msecs per iteration (total: 1,504, iterations: 1000) RESULT :
38+ qmltestrunner::benchmark_time:"cardTitleArtSubtitleModel": 1.060
39+ msecs per iteration (total: 1,061, iterations: 1000) RESULT :
40+ qmltestrunner::benchmark_time:"cardTitleArtModel": 1.052 msecs per
41+ iteration (total: 1,053, iterations: 1000) RESULT :
42+ qmltestrunner::benchmark_time:"cardArtModel": 0.727 msecs per
43+ iteration (total: 728, iterations: 1000) RESULT :
44+ qmltestrunner::benchmark_time:"cardTitleModel": 0.817 msecs per
45+ iteration (total: 818, iterations: 1000) (LP: #1297197)
46+
47+ [ Allan LeSage ]
48+ * DashApps emulator get_applications should return a list ordered by
49+ visible y, x.
50+
51+ [ Andrea Cimitan ]
52+ * Workaround for lp1301309 until fixes for palette in ui toolkit (LP:
53+ #1301309)
54+
55+ [ Leo Arias ]
56+ * Reverted the change that returns application cards instead of
57+ titles.
58+
59+ [ Nick Dedekind ]
60+ * Indicator services started by unity8 upstart configuration rather
61+ than manual emmision from indicator manager.
62+
63+ [ Mirco Müller ]
64+ * Fix notification ap-test assertions.
65+
66+ [ Michael Terry ]
67+ * Use new tablet and phone backgrounds from Design.
68+
69+ [ Michael Zanetti ]
70+ * workaround the QTestLogger assertion issue with make tryXyz and our
71+ custom uqmlscene
72+
73+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 16 Apr 2014 13:45:01 +0000
74+
75+unity8 (7.85+14.04.20140415.2-0ubuntu1) trusty; urgency=low
76+
77+ [ Michael Terry ]
78+ * When an application requests focus, handle it in Shell.qml by hiding
79+ the greeter and stopping any edge demo. (LP: #1227753)
80+
81+ [ Leo Arias ]
82+ * Use subprocess.check_call when caling url-dispatcher, so an error
83+ will be raised if it fails.
84+ * Test application life cycle with fake apps, instead of messaging and
85+ address book.
86+
87+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 15 Apr 2014 12:47:11 +0000
88+
89+unity8 (7.85+14.04.20140410.1-0ubuntu1) trusty; urgency=medium
90+
91+ [ Didier Roche ]
92+ * Resync trunk with previous revert upload
93+
94+ [ Michał Sawicz ]
95+ * Set the Qt.ImhNoPredictiveText flag on wifi password field, fixes
96+ lp:1291575 (LP: #1291575)
97+
98+ [ Albert Astals ]
99+ * Take into account the originY when specifying the delegate ranges
100+ Fixes bug #1300302 (LP: #1300302)
101+
102+ [ CI bot ]
103+ * Resync trunk
104+
105+ [ Allan LeSage ]
106+ * Swiping open an indicator must show its correct title--protect
107+ against lp:1253804 . (LP: #1253804)
108+
109+ [ Alexander Sack ]
110+ * Fix TypeError: issue seen in system_integration autopilot test on
111+ image 279. (LP: #1303685)
112+
113+ [ Bill Filler ]
114+ * Set the Qt.ImhNoPredictiveText flag on wifi password field, fixes
115+ lp:1291575 (LP: #1291575)
116+
117+ [ Leo Arias ]
118+ * Added a search autopilot helper.
119+
120+ [ Michael Terry ]
121+ * Provide a all-in-one script for getting a device to an unlocked
122+ state.
123+
124+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Thu, 10 Apr 2014 10:03:31 +0000
125+
126+unity8 (7.85+14.04.20140404.is.7.85+14.04.20140403.1-0ubuntu1) trusty; urgency=medium
127+
128+ * Revert to previous version as it's linked to latest sdk change which
129+ is making gallery-app AP tests failing on the CI dashboard
130+
131+ -- Didier Roche <didrocks@ubuntu.com> Tue, 08 Apr 2014 13:53:47 +0200
132+
1133unity8 (7.85+14.04.20140404-0ubuntu1) trusty; urgency=low
2134
3135 [ Albert Astals ]
debian/control−3
@@ -14,14 +14,11 @@ Build-Depends: cmake,
1414 libglib2.0-dev,
1515 libgsettings-qt-dev,
1616 libhud-client2-dev,
17− libnih-dbus-dev,
18− libnih-dev,
1917 libpulse-dev,
2018 libqmenumodel-dev (>= 0.2.7),
2119 libqt5xmlpatterns5-dev,
2220 libunity-api-dev (>= 7.80.6),
2321 libunity-mir-dev,
24− libupstart-dev,
2522 libusermetricsoutput1-dev,
2623 libxcb1-dev,
2724 pkg-config,
debian/unity8-autopilot.install+1
@@ -1 +1,2 @@
11usr/lib/python*/*/unity*
2+usr/share/unity8/unlock-device
plugins/Unity/Indicators/CMakeLists.txt−3
@@ -6,7 +6,6 @@ project(IndicatorsQml)
66pkg_check_modules(GLIB REQUIRED glib-2.0>=2.32)
77pkg_check_modules(GIO REQUIRED gio-2.0>=2.32)
88pkg_check_modules(QMENUMODEL REQUIRED qmenumodel)
9−pkg_check_modules(LIBUPSTART REQUIRED libupstart libnih libnih-dbus dbus-1)
109
1110find_package(Qt5Core REQUIRED)
1211find_package(Qt5Quick REQUIRED)
@@ -19,7 +18,6 @@ include_directories(
1918 ${GLIB_INCLUDE_DIRS}
2019 ${GIO_INCLUDE_DIRS}
2120 ${QMENUMODEL_INCLUDE_DIRS}
22− ${LIBUPSTART_INCLUDE_DIRS}
2321)
2422
2523set(IndicatorsQML_SOURCES
@@ -54,7 +52,6 @@ target_link_libraries(IndicatorsQml
5452 ${GLIB_LIBRARIES}
5553 ${GIO_LIBRARIES}
5654 ${QMENUMODEL_LDFLAGS}
57− ${LIBUPSTART_LIBRARIES}
5855)
5956
6057qt5_use_modules(IndicatorsQml Core Qml Quick)
plugins/Unity/Indicators/indicatorsmanager.cpp−33
@@ -21,9 +21,6 @@
2121#include <QSettings>
2222#include <QDebug>
2323
24−#include <upstart.h>
25−#include <nih/alloc.h>
26−
2724#include <paths.h>
2825
2926
@@ -216,36 +213,6 @@ void IndicatorsManager::setLoaded(bool loaded)
216213 {
217214 m_loaded = loaded;
218215 Q_EMIT loadedChanged(m_loaded);
219−
220− auto upstartsession = qgetenv("UPSTART_SESSION");
221− NihDBusProxy * upstart = NULL;
222−
223− if (!upstartsession.isNull()) {
224− DBusConnection * conn = NULL;
225− conn = dbus_connection_open(upstartsession.constData(), NULL);
226− if (conn != NULL) {
227− upstart = nih_dbus_proxy_new(NULL, conn,
228− NULL,
229− DBUS_PATH_UPSTART,
230− NULL, NULL);
231− dbus_connection_unref(conn);
232− }
233− }
234−
235− if (upstart != NULL) {
236− int event_sent = 0;
237− if (m_loaded) {
238− event_sent = upstart_emit_event_sync(NULL, upstart, "indicator-services-start", NULL, 0);
239− } else {
240− event_sent = upstart_emit_event_sync(NULL, upstart, "indicator-services-end", NULL, 0);
241− }
242−
243− if (event_sent != 0) {
244− qWarning() << "Unable to send indicator event to Upstart";
245− }
246− nih_unref(upstart, NULL);
247− upstart = NULL;
248− }
249216 }
250217}
251218
plugins/Unity/Indicators/indicatorsmanager.h−2
@@ -28,8 +28,6 @@
2828#include <QHash>
2929#include <QSharedPointer>
3030
31−typedef struct nih_dbus_proxy NihDBusProxy;
32−
3331class UNITYINDICATORS_EXPORT IndicatorsManager : public QObject
3432{
3533 Q_OBJECT
po/br.ponew file+164
@@ -0,0 +1,164 @@
1+# Breton translation for unity8
2+# Copyright (c) 2014 Rosetta Contributors and Canonical Ltd 2014
3+# This file is distributed under the same license as the unity8 package.
4+# FIRST AUTHOR <EMAIL@ADDRESS>, 2014.
5+#
6+msgid ""
7+msgstr ""
8+"Project-Id-Version: unity8\n"
9+"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10+"POT-Creation-Date: 2014-03-25 16:48+0100\n"
11+"PO-Revision-Date: 2014-04-15 18:15+0000\n"
12+"Last-Translator: Rouxel <vincent.rouxel@mailoo.org>\n"
13+"Language-Team: Breton <br@li.org>\n"
14+"MIME-Version: 1.0\n"
15+"Content-Type: text/plain; charset=UTF-8\n"
16+"Content-Transfer-Encoding: 8bit\n"
17+"X-Launchpad-Export-Date: 2014-04-16 07:12+0000\n"
18+"X-Generator: Launchpad (build 16976)\n"
19+
20+#: plugins/Unity/Launcher/launcheritem.cpp:43
21+#: plugins/Unity/Launcher/launcheritem.cpp:73
22+msgid "Lock to Launcher"
23+msgstr ""
24+
25+#: plugins/Unity/Launcher/launcheritem.cpp:73
26+msgid "Unlock from Launcher"
27+msgstr ""
28+
29+#: qml/Components/EdgeDemoOverlay.qml:151
30+msgid "Skip intro"
31+msgstr ""
32+
33+#: qml/Components/EdgeDemo.qml:113
34+msgid "Right edge"
35+msgstr ""
36+
37+#: qml/Components/EdgeDemo.qml:114
38+msgid "Try swiping from the right edge to unlock the phone"
39+msgstr ""
40+
41+#: qml/Components/EdgeDemo.qml:145
42+msgid "Top edge"
43+msgstr ""
44+
45+#: qml/Components/EdgeDemo.qml:146
46+msgid "Try swiping from the top edge to access the indicators"
47+msgstr ""
48+
49+#: qml/Components/EdgeDemo.qml:171
50+msgid "Close"
51+msgstr "Serriñ"
52+
53+#: qml/Components/EdgeDemo.qml:172
54+msgid "Swipe up again to close the settings screen"
55+msgstr ""
56+
57+#: qml/Components/EdgeDemo.qml:199
58+msgid "Left edge"
59+msgstr ""
60+
61+#: qml/Components/EdgeDemo.qml:200
62+msgid "Swipe from the left to reveal the launcher for quick access to apps"
63+msgstr ""
64+
65+#: qml/Components/EdgeDemo.qml:227
66+msgid "Well done"
67+msgstr ""
68+
69+#: qml/Components/EdgeDemo.qml:228
70+msgid ""
71+"You have now mastered the edge gestures and can start using the "
72+"phone<br><br>Tap on the screen to start"
73+msgstr ""
74+
75+#: qml/Components/PageHeader.qml:365
76+msgid "Recent searches"
77+msgstr ""
78+
79+#: qml/Components/SeeMore.qml:35
80+msgid "See more"
81+msgstr ""
82+
83+#: qml/Components/SeeMore.qml:58
84+msgid "See less"
85+msgstr ""
86+
87+#: qml/Dash/DashApps.qml:38
88+msgid "Recent"
89+msgstr ""
90+
91+#: qml/Dash/PreviewListView.qml:52
92+#, qt-format
93+msgid "%1 Preview"
94+msgstr ""
95+
96+#: qml/Dash/Previews/PreviewActionCombo.qml:34
97+msgid "More..."
98+msgstr ""
99+
100+#: qml/Dash/Previews/PreviewActionCombo.qml:34
101+msgid "Less..."
102+msgstr ""
103+
104+#: qml/Dash/Previews/PreviewRatingInput.qml:81
105+msgid "Rate this"
106+msgstr ""
107+
108+#: qml/Dash/Previews/PreviewRatingInput.qml:126
109+msgid "Add a review"
110+msgstr ""
111+
112+#: qml/Dash/Previews/PreviewRatingInput.qml:166
113+msgid "Send"
114+msgstr ""
115+
116+#: qml/Greeter/Lockscreen.qml:149
117+msgid "Emergency Call"
118+msgstr ""
119+
120+#: qml/Greeter/PassphraseLockscreen.qml:59
121+#, qt-format
122+msgid "Hello %1"
123+msgstr ""
124+
125+#: qml/Greeter/PassphraseLockscreen.qml:59
126+msgid "Hello"
127+msgstr ""
128+
129+#: qml/Hud/HudParametrizedActionsPage.qml:132
130+msgid "Confirm"
131+msgstr ""
132+
133+#: qml/Hud/Hud.qml:61 qml/Hud/Hud.qml:99 qml/Hud/Hud.qml:372
134+msgid "Type or say a command"
135+msgstr ""
136+
137+#: qml/Hud/Hud.qml:84
138+msgid "Loading. Please Wait..."
139+msgstr ""
140+
141+#: qml/Hud/Hud.qml:89
142+msgid "Speak Now..."
143+msgstr ""
144+
145+#: qml/Hud/Hud.qml:94
146+msgid "Speaking..."
147+msgstr ""
148+
149+#: qml/Notifications/NotificationMenuItemFactory.qml:91
150+msgid "Show password"
151+msgstr ""
152+
153+#: qml/Notifications/NotificationMenuItemFactory.qml:103
154+msgid "Please enter SIM PIN"
155+msgstr ""
156+
157+#: qml/Panel/SearchIndicator.qml:27
158+msgid "Search"
159+msgstr ""
160+
161+#: qml/Shell.qml:417
162+#, qt-format
163+msgid "Please enter %1"
164+msgstr ""
po/ug.po+8−24
@@ -8,15 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2014-03-25 16:48+0100\n"
11−"PO-Revision-Date: 2014-02-15 12:09+0000\n"
12−"Last-Translator: Gheyret T.Kenji <Unknown>\n"
11+"PO-Revision-Date: 2014-04-22 05:46+0000\n"
12+"Last-Translator: abdurahman <Unknown>\n"
1313"Language-Team: Uyghur <ug@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−"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2014-03-28 06:49+0000\n"
19−"X-Generator: Launchpad (build 16967)\n"
17+"X-Launchpad-Export-Date: 2014-04-23 07:07+0000\n"
18+"X-Generator: Launchpad (build 16985)\n"
2019
2120#: plugins/Unity/Launcher/launcheritem.cpp:43
2221#: plugins/Unity/Launcher/launcheritem.cpp:73
@@ -89,20 +88,20 @@ msgstr "ئازراق كۆرۈش"
8988
9089#: qml/Dash/DashApps.qml:38
9190msgid "Recent"
92−msgstr ""
91+msgstr "يېقىنقى"
9392
9493#: qml/Dash/PreviewListView.qml:52
9594#, qt-format
9695msgid "%1 Preview"
97−msgstr ""
96+msgstr "%1 ئالدىن كۆرۈپ بىقىش"
9897
9998#: qml/Dash/Previews/PreviewActionCombo.qml:34
10099msgid "More..."
101−msgstr ""
100+msgstr "تېخىمۇ كۆپ…"
102101
103102#: qml/Dash/Previews/PreviewActionCombo.qml:34
104103msgid "Less..."
105−msgstr ""
104+msgstr "ئازراق..."
106105
107106#: qml/Dash/Previews/PreviewRatingInput.qml:81
108107msgid "Rate this"
@@ -165,18 +164,3 @@ msgstr "ئىزدە"
165164#, qt-format
166165msgid "Please enter %1"
167166msgstr "%1 نى كىرگۈزۈڭ"
168−
169−#, qt-format
170−#~ msgid "%1 review"
171−#~ msgid_plural "%1 reviews"
172−#~ msgstr[0] "%1 باھا"
173−
174−#, qt-format
175−#~ msgid "(%1)"
176−#~ msgstr "(%1)"
177−
178−#~ msgid "Comments:"
179−#~ msgstr "ئىنكاس:"
180−
181−#~ msgid "Review"
182−#~ msgstr "تەكشۈر"
qml/Components/AnimationControllerWithSignals.qmldeleted−64
@@ -1,64 +0,0 @@
1−/*
2− * Copyright (C) 2013 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−
19−AnimationController {
20− signal animationCompletedAtBeginning()
21− signal animationCompletedAtEnd()
22− readonly property bool completed: progress == 1.0 || progress == 0.0
23− property bool completing: false
24−
25− function __completeAtBeginning() {
26− completing = false;
27− animationCompletedAtBeginning();
28− }
29−
30− function __completeAtEnd() {
31− completing = false;
32− animationCompletedAtEnd();
33− }
34−
35− function completeToBeginningWithSignal() {
36− if (progress == 0.0) {
37− __completeAtBeginning()
38− } else {
39− completing = true;
40− completeToBeginning();
41− }
42− }
43−
44− function completeToEndWithSignal() {
45− if (progress == 1.0) {
46− __completeAtEnd()
47− } else {
48− completing = true;
49− completeToEnd();
50− }
51− }
52−
53− onProgressChanged: {
54− if (!completing) {
55− return;
56− }
57−
58− if (progress == 0.0) {
59− __completeAtBeginning()
60− } else if (progress == 1.0) {
61− __completeAtEnd()
62− }
63− }
64−}
qml/Components/Carousel.qml+13−19
@@ -75,6 +75,7 @@ Item {
7575 The scaling of the items is controlled by the variable continuousIndex, described below. */
7676 ListView {
7777 id: listView
78+ objectName: "listView"
7879
7980 property int highlightIndex: -1
8081 property real minimumTileWidth: 0
@@ -148,15 +149,19 @@ Item {
148149 maximumFlickVelocity: Math.max(2500 * Math.pow(realWidth / referenceWidth, 1.5), 2500) // 2500 is platform default
149150 orientation: ListView.Horizontal
150151
151− function itemClicked(index, delegateItem) {
152− listView.currentIndex = index
153− var x = CarouselJS.getXFromContinuousIndex(index,
152+ function getXFromContinuousIndex(index) {
153+ return CarouselJS.getXFromContinuousIndex(index,
154154 realWidth,
155− realContentWidth,
155+ footerItem.x,
156156 tileWidth,
157157 gapToMiddlePhase,
158158 gapToEndPhase,
159159 carousel.drawBuffer)
160+ }
161+
162+ function itemClicked(index, delegateItem) {
163+ listView.currentIndex = index
164+ var x = getXFromContinuousIndex(index);
160165
161166 if (Math.abs(x - contentX) < 1 && delegateItem !== undefined) {
162167 /* We're clicking the selected item and
@@ -174,13 +179,7 @@ Item {
174179 }
175180
176181 function itemPressAndHold(index, delegateItem) {
177− var x = CarouselJS.getXFromContinuousIndex(index,
178− realWidth,
179− realContentWidth,
180− tileWidth,
181− gapToMiddlePhase,
182− gapToEndPhase,
183− carousel.drawBuffer);
182+ var x = getXFromContinuousIndex(index);
184183
185184 if (Math.abs(x - contentX) < 1 && delegateItem !== undefined) {
186185 /* We're pressAndHold the selected item and
@@ -209,22 +208,17 @@ Item {
209208 newContentX = disabledNewContentX
210209 }
211210 onMovementEnded: {
212− if (realContentX > 0 && realContentX < realContentWidth - realWidth)
211+ if (realContentX > 0)
213212 stepAnimation.start()
214213 }
215214
216215 SmoothedAnimation {
217216 id: stepAnimation
217+ objectName: "stepAnimation"
218218
219219 target: listView
220220 property: "contentX"
221− to: CarouselJS.getXFromContinuousIndex(listView.selectedIndex,
222− listView.realWidth,
223− listView.realContentWidth,
224− listView.tileWidth,
225− listView.gapToMiddlePhase,
226− listView.gapToEndPhase,
227− carousel.drawBuffer)
221+ to: listView.getXFromContinuousIndex(listView.selectedIndex)
228222 duration: 450
229223 velocity: 200
230224 easing.type: Easing.InOutQuad
qml/Components/FilterGrid.qml+1
@@ -59,6 +59,7 @@ Item {
5959 property alias cellHeight: iconTileGrid.cellHeight
6060 property alias delegateCreationBegin: iconTileGrid.delegateCreationBegin
6161 property alias delegateCreationEnd: iconTileGrid.delegateCreationEnd
62+ readonly property alias originY: iconTileGrid.originY
6263 readonly property alias flicking: iconTileGrid.flicking
6364 readonly property alias moving: iconTileGrid.moving
6465 readonly property alias pressDelay: iconTileGrid.pressDelay
qml/Components/ResponsiveGridView.qml+1
@@ -40,6 +40,7 @@ Item {
4040 readonly property alias flicking: gridView.flicking
4141 readonly property alias moving: gridView.moving
4242 readonly property alias pressDelay: gridView.pressDelay
43+ readonly property alias originY: gridView.originY
4344 property alias delegateCreationBegin: gridView.delegateCreationBegin
4445 property alias delegateCreationEnd: gridView.delegateCreationEnd
4546 property alias highlightIndex: gridView.highlightIndex
qml/Dash/Card.qml+99−79
@@ -25,47 +25,53 @@ AbstractButton {
2525 property var components
2626 property var cardData
2727
28− property alias fontScale: header.fontScale
29− property alias headerAlignment: header.headerAlignment
30− property alias headerHeight: header.height
31− readonly property alias title: header.title
28+ property real fontScale: 1.0
29+ property int headerAlignment: Text.AlignLeft
30+ readonly property int headerHeight: headerLoader.item ? headerLoader.item.height : 0
31+ property int fixedHeaderHeight: -1
32+ readonly property string title: cardData && cardData["title"] || ""
3233
3334 property bool showHeader: true
3435
3536 implicitWidth: childrenRect.width
36− implicitHeight: summary.y + summary.height + (summary.text && background.visible ? units.gu(1) : 0)
37+ implicitHeight: summary.y + summary.height + (summary.text && backgroundLoader.active ? units.gu(1) : 0)
3738
38− UbuntuShape {
39− id: background
40− objectName: "background"
41− radius: "medium"
42− visible: template["card-layout"] !== "horizontal" && (template["card-background"] || components["background"]
43− || artAndSummary)
44− property bool artAndSummary: components["art"]["field"] && components["summary"] || false
45− color: getColor(0) || "white"
46− gradientColor: getColor(1) || color
47− anchors.fill: parent
48− image: backgroundImage.source ? backgroundImage : null
39+ Loader {
40+ id: backgroundLoader
41+ objectName: "backgroundLoader"
4942
50− property real luminance: 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b
43+ readonly property bool artAndSummary: components["art"]["field"] && components["summary"] || false
44+ active: template["card-layout"] !== "horizontal" && (template["card-background"] || components["background"] || artAndSummary)
45+ anchors.fill: parent
5146
52− property Image backgroundImage: Image {
53− objectName: "backgroundImage"
54− source: {
55− if (cardData && typeof cardData["background"] === "string") return cardData["background"]
56− else if (template && typeof template["card-background"] === "string") return template["card-background"]
57− else return ""
47+ sourceComponent: UbuntuShape {
48+ objectName: "background"
49+ radius: "medium"
50+ color: getColor(0) || "white"
51+ gradientColor: getColor(1) || color
52+ anchors.fill: parent
53+ image: backgroundImage.source ? backgroundImage : null
54+
55+ property real luminance: 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b
56+
57+ property Image backgroundImage: Image {
58+ objectName: "backgroundImage"
59+ source: {
60+ if (cardData && typeof cardData["background"] === "string") return cardData["background"]
61+ else if (template && typeof template["card-background"] === "string") return template["card-background"]
62+ else return ""
63+ }
5864 }
59− }
6065
61− function getColor(index) {
62− if (cardData && typeof cardData["background"] === "object"
63− && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) {
64− return cardData["background"]["elements"][index];
65− } else if (template && typeof template["card-background"] === "object"
66− && (template["card-background"]["type"] === "color" || template["card-background"]["type"] === "gradient")) {
67− return template["card-background"]["elements"][index];
68− } else return undefined;
66+ function getColor(index) {
67+ if (cardData && typeof cardData["background"] === "object"
68+ && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) {
69+ return cardData["background"]["elements"][index];
70+ } else if (template && typeof template["card-background"] === "object"
71+ && (template["card-background"]["type"] === "color" || template["card-background"]["type"] === "gradient")) {
72+ return template["card-background"]["elements"][index];
73+ } else return undefined;
74+ }
6975 }
7076 }
7177
@@ -111,7 +117,7 @@ AbstractButton {
111117 function updateWidthHeightBindings() {
112118 if (isHorizontal) {
113119 width = Qt.binding(function() { return height * artShape.aspect });
114− height = Qt.binding(function() { return header.height });
120+ height = Qt.binding(function() { return root.headerHeight });
115121 } else {
116122 width = Qt.binding(function() { return root.width });
117123 height = Qt.binding(function() { return width / artShape.aspect });
@@ -120,53 +126,58 @@ AbstractButton {
120126 }
121127 }
122128
123− ShaderEffect {
124− id: overlay
129+ Loader {
130+ id: overlayLoader
125131 anchors {
126132 left: artShape.left
127133 right: artShape.right
128134 bottom: artShape.bottom
129135 }
136+ active: template && template["overlay"] && artShape.visible && artShape.image.status === Image.Ready || false
130137
131− height: header.height
132− opacity: header.opacity * 0.6
133− visible: template && template["overlay"] && artShape.visible && artShape.image.status === Image.Ready || false
138+ sourceComponent: ShaderEffect {
139+ id: overlay
134140
135− property var source: ShaderEffectSource {
136− id: shaderSource
137− sourceItem: artShape
138− onVisibleChanged: if (visible) scheduleUpdate()
139− live: false
140− sourceRect: Qt.rect(0, artShape.height - overlay.height, artShape.width, overlay.height)
141− }
141+ height: headerLoader.item ? headerLoader.item.height : 0
142+ opacity: headerLoader.item ? headerLoader.item.opacity * 0.6 : 0
143+
144+ property var source: ShaderEffectSource {
145+ id: shaderSource
146+ sourceItem: artShape
147+ onVisibleChanged: if (visible) scheduleUpdate()
148+ live: false
149+ sourceRect: Qt.rect(0, artShape.height - overlay.height, artShape.width, overlay.height)
150+ }
142151
143− vertexShader: "
144− uniform highp mat4 qt_Matrix;
145− attribute highp vec4 qt_Vertex;
146− attribute highp vec2 qt_MultiTexCoord0;
147− varying highp vec2 coord;
148− void main() {
149− coord = qt_MultiTexCoord0;
150− gl_Position = qt_Matrix * qt_Vertex;
151− }"
152−
153− fragmentShader: "
154− varying highp vec2 coord;
155− uniform sampler2D source;
156− uniform lowp float qt_Opacity;
157− void main() {
158− lowp vec4 tex = texture2D(source, coord);
159− gl_FragColor = vec4(0, 0, 0, tex.a) * qt_Opacity;
160− }"
152+ vertexShader: "
153+ uniform highp mat4 qt_Matrix;
154+ attribute highp vec4 qt_Vertex;
155+ attribute highp vec2 qt_MultiTexCoord0;
156+ varying highp vec2 coord;
157+ void main() {
158+ coord = qt_MultiTexCoord0;
159+ gl_Position = qt_Matrix * qt_Vertex;
160+ }"
161+
162+ fragmentShader: "
163+ varying highp vec2 coord;
164+ uniform sampler2D source;
165+ uniform lowp float qt_Opacity;
166+ void main() {
167+ lowp vec4 tex = texture2D(source, coord);
168+ gl_FragColor = vec4(0, 0, 0, tex.a) * qt_Opacity;
169+ }"
170+ }
161171 }
162172
163− CardHeader {
164− id: header
165− objectName: "cardHeader"
173+ Loader {
174+ id: headerLoader
175+ objectName: "cardHeaderLoader"
176+
166177 anchors {
167178 top: {
168179 if (template) {
169− if (template["overlay"]) return overlay.top;
180+ if (template["overlay"]) return overlayLoader.top;
170181 if (template["card-layout"] === "horizontal") return artShape.top;
171182 }
172183 return artShape.bottom;
@@ -179,29 +190,38 @@ AbstractButton {
179190 }
180191 right: parent.right
181192 }
193+ active: cardData && cardData["title"] || cardData && cardData["mascot"] || false
194+
195+ sourceComponent: CardHeader {
196+ id: header
197+ objectName: "cardHeader"
182198
183− mascot: cardData && cardData["mascot"] || ""
184− title: cardData && cardData["title"] || ""
185− subtitle: cardData && cardData["subtitle"] || ""
199+ mascot: cardData && cardData["mascot"] || ""
200+ title: root.title
201+ subtitle: cardData && cardData["subtitle"] || ""
186202
187− titleWeight: components && components["subtitle"] ? Font.DemiBold : Font.Normal
203+ titleWeight: components && components["subtitle"] ? Font.DemiBold : Font.Normal
188204
189− opacity: showHeader ? 1 : 0
190− inOverlay: root.template && root.template["overlay"] === true
191− fontColor: inOverlay ? "white" : summary.color
192− useMascotShape: !background.visible && !inOverlay
205+ opacity: showHeader ? 1 : 0
206+ inOverlay: root.template && root.template["overlay"] === true
207+ fontColor: inOverlay ? "white" : summary.color
208+ useMascotShape: !backgroundLoader.active && !inOverlay
209+ headerAlignment: root.headerAlignment
210+ height: root.fixedHeaderHeight != -1 ? root.fixedHeaderHeight : implicitHeight
211+ fontScale: root.fontScale
193212
194− Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.SnapDuration } }
213+ Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.SnapDuration } }
214+ }
195215 }
196216
197217 Label {
198218 id: summary
199219 objectName: "summaryLabel"
200220 anchors {
201− top: header.visible ? header.bottom : artShape.bottom
221+ top: headerLoader.active ? headerLoader.bottom : artShape.bottom
202222 left: parent.left
203223 right: parent.right
204− margins: background.visible ? units.gu(1) : 0
224+ margins: backgroundLoader.active ? units.gu(1) : 0
205225 topMargin: 0
206226 }
207227 wrapMode: Text.Wrap
@@ -211,6 +231,6 @@ AbstractButton {
211231 height: text ? implicitHeight : 0
212232 fontSize: "small"
213233 // TODO karni: Change "grey" to Ubuntu.Components.Palette color once updated.
214− color: background.visible && background.luminance < 0.7 ? "white" : "grey"
234+ color: backgroundLoader.active && backgroundLoader.item.luminance < 0.7 ? "white" : "grey"
215235 }
216236}
qml/Dash/CardCarousel.qml+1−1
@@ -52,7 +52,7 @@ DashRenderer {
5252 itemComponent: Card {
5353 id: card
5454 objectName: "carouselDelegate" + index
55− headerHeight: carousel.headerHeight
55+ fixedHeaderHeight: carousel.headerHeight
5656 cardData: model
5757 template: cardTool.template
5858 components: cardTool.components
qml/Dash/CardFilterGrid.qml+4−2
@@ -24,6 +24,7 @@ DashRenderer {
2424 collapsedHeight: filterGrid.collapsedHeight
2525 margins: filterGrid.margins
2626 uncollapsedHeight: filterGrid.uncollapsedHeight
27+ originY: filterGrid.originY
2728 verticalSpacing: units.gu(1)
2829 currentItem: filterGrid.currentItem
2930 height: filterGrid.height
@@ -44,14 +45,15 @@ DashRenderer {
4445 collapsedRowCount: Math.min(2, cardTool && cardTool.template && cardTool.template["collapsed-rows"] || 2)
4546 delegateCreationBegin: genericFilterGrid.delegateCreationBegin
4647 delegateCreationEnd: genericFilterGrid.delegateCreationEnd
47− delegate: Item {
48+ delegate: Loader {
49+ asynchronous: true
4850 width: filterGrid.cellWidth
4951 height: filterGrid.cellHeight
5052 Card {
5153 id: card
5254 width: cardTool.cardWidth
5355 height: cardTool.cardHeight
54− headerHeight: cardTool.headerHeight
56+ fixedHeaderHeight: cardTool.headerHeight
5557 anchors.horizontalCenter: parent.horizontalCenter
5658 objectName: "delegate" + index
5759 cardData: model
qml/Dash/CardHeader.qml+41−31
@@ -19,9 +19,9 @@ import Ubuntu.Components 0.1
1919
2020Item {
2121 id: root
22− property alias mascot: mascotImage.source
22+ property url mascot: ""
2323 property alias title: titleLabel.text
24− property alias subtitle: subtitleLabel.text
24+ property var subtitle
2525
2626 property alias titleWeight: titleLabel.font.weight
2727 property alias titleSize: titleLabel.fontSize
@@ -35,8 +35,8 @@ Item {
3535 property bool useMascotShape: true
3636 property color fontColor: Theme.palette.selected.backgroundText
3737
38− visible: mascotImage.status === Image.Ready || title
39− height: row.height > 0 ? row.height + row.margins * 2 : 0
38+ visible: mascot != "" || title
39+ implicitHeight: row.height > 0 ? row.height + row.margins * 2 : 0
4040
4141 Row {
4242 id: row
@@ -44,7 +44,7 @@ Item {
4444
4545 property real margins: units.gu(1)
4646
47− spacing: mascotShape.visible || mascotImage.visible || inOverlay ? margins : 0
47+ spacing: mascotShapeLoader.active || mascotImageLoader.active || inOverlay ? margins : 0
4848 anchors {
4949 top: parent.top; left: parent.left; right: parent.right
5050 margins: margins
@@ -52,33 +52,40 @@ Item {
5252 rightMargin: spacing
5353 }
5454
55− UbuntuShape {
56− id: mascotShape
57− objectName: "mascotShape"
55+ Loader {
56+ id: mascotShapeLoader
57+ objectName: "mascotShapeLoader"
5858
59+ active: useMascotShape && mascotImageLoader.item && mascotImageLoader.item.status === Image.Ready
60+ visible: active
61+ anchors.verticalCenter: parent.verticalCenter
5962 // TODO karni: Icon aspect-ratio is 8:7.5. Revisit these values to avoid fraction of pixels.
6063 width: units.gu(6)
6164 height: units.gu(5.625)
62− anchors.verticalCenter: parent.verticalCenter
63− visible: useMascotShape && image && image.status === Image.Ready
6465 readonly property int maxSize: Math.max(width, height) * 4
6566
66− image: useMascotShape ? mascotImage : null
67+ sourceComponent: UbuntuShape {
68+ image: mascotImageLoader.item
69+ }
6770 }
6871
69− Image {
70− id: mascotImage
71− objectName: "mascotImage"
72−
73− width: source ? mascotShape.width : 0
74− height: mascotShape.height
72+ Loader {
73+ id: mascotImageLoader
74+ active: root.mascot != ""
75+ visible: active && !useMascotShape && item.status === Image.Ready
7576 anchors.verticalCenter: parent.verticalCenter
76− visible: !useMascotShape && status === Image.Ready
77+ sourceComponent: Image {
78+ objectName: "mascotImage"
79+
80+ source: root.mascot
81+ width: source ? mascotShapeLoader.width : 0
82+ height: mascotShapeLoader.height
7783
78− sourceSize { width: mascotShape.maxSize; height: mascotShape.maxSize }
79− fillMode: Image.PreserveAspectCrop
80− horizontalAlignment: Image.AlignHCenter
81− verticalAlignment: Image.AlignVCenter
84+ sourceSize { width: mascotShapeLoader.maxSize; height: mascotShapeLoader.maxSize }
85+ fillMode: Image.PreserveAspectCrop
86+ horizontalAlignment: Image.AlignHCenter
87+ verticalAlignment: Image.AlignVCenter
88+ }
8289 }
8390
8491 Column {
@@ -100,16 +107,19 @@ Item {
100107 color: fontColor
101108 }
102109
103− Label {
104− id: subtitleLabel
105− objectName: "subtitleLabel"
110+ Loader {
111+ active: titleLabel.text && root.subtitle
106112 anchors { left: parent.left; right: parent.right }
107− elide: Text.ElideRight
108− fontSize: "small"
109− font.weight: Font.Light
110− visible: titleLabel.text && text
111− font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale)
112− color: fontColor
113+ sourceComponent: Label {
114+ id: subtitleLabel
115+ objectName: "subtitleLabel"
116+ elide: Text.ElideRight
117+ fontSize: "small"
118+ font.weight: Font.Light
119+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale)
120+ color: fontColor
121+ text: root.subtitle
122+ }
113123 }
114124 }
115125 }
qml/Dash/DashContent.qml+15−28
@@ -17,7 +17,6 @@
1717import QtQuick 2.0
1818import Ubuntu.Components 0.1
1919import Unity 0.2
20−import Utils 0.1
2120import "../Components"
2221
2322Item {
@@ -28,13 +27,9 @@ Item {
2827 readonly property alias currentIndex: dashContentList.currentIndex
2928 property alias previewOpen: previewListView.open
3029
31− property ScopeDelegateMapper scopeMapper : ScopeDelegateMapper {}
3230 property ListModel searchHistory
3331
34− signal movementStarted()
35− signal movementEnded()
3632 signal scopeLoaded(string scopeId)
37− signal positionedAtBeginning()
3833 signal gotoScope(string scopeId)
3934 signal openScope(var scope)
4035
@@ -72,7 +67,7 @@ Item {
7267 dashContentList.currentIndex = index
7368
7469 if (reset) {
75− dashContent.positionedAtBeginning()
70+ dashContentList.currentItem.item.positionAtBeginning()
7671 }
7772 }
7873
@@ -115,8 +110,7 @@ Item {
115110 highlightRangeMode: ListView.StrictlyEnforceRange
116111 // TODO Investigate if we can switch to a smaller cache buffer when/if UbuntuShape gets more performant
117112 cacheBuffer: 1073741823
118− onMovementStarted: dashContent.movementStarted()
119− onMovementEnded: dashContent.movementEnded()
113+ onMovementStarted: currentItem.item.showHeader();
120114 clip: parent.x != 0
121115
122116 // If the number of items is less than the current index, then need to reset to another item.
@@ -136,7 +130,9 @@ Item {
136130 width: ListView.view.width
137131 height: ListView.view.height
138132 asynchronous: true
139− source: scopeMapper.map(scope.id)
133+ // TODO This if will eventually go away since we're killing DashApps.qml
134+ // once we move app closing to the spread
135+ source: (scope.id == "clickscope") ? "DashApps.qml" : "GenericScopeView.qml"
140136 objectName: scope.id + " loader"
141137
142138 readonly property bool moving: item ? item.moving : false
@@ -155,8 +151,6 @@ Item {
155151 item.scope = Qt.binding(function() { return scope })
156152 item.isCurrent = Qt.binding(function() { return visible && ListView.isCurrentItem })
157153 item.tabBarHeight = dashPageHeader.implicitHeight;
158− dashContentList.movementStarted.connect(item.movementStarted)
159− dashContent.positionedAtBeginning.connect(item.positionedAtBeginning)
160154 dashContent.scopeLoaded(item.scope.id)
161155 }
162156 Connections {
@@ -189,30 +183,23 @@ Item {
189183 width: parent.width
190184 style: DashContentTabBarStyle {}
191185
192− SortFilterProxyModel {
193− id: tabBarModel
186+ model: dashContentList.model
194187
195− model: dashContentList.model
196−
197− property int selectedIndex: -1
198− onSelectedIndexChanged: {
199− if (dashContentList.currentIndex == -1 && tabBar.selectedIndex != -1) {
200− // TODO This together with the Timer below
201− // are a workaround for the first tab sometimes not showing the text.
202− // But Tabs are going away in the future so not sure if makes
203− // sense invetigating what's the problem at this stage
204− selectionModeTimer.restart();
205− }
206− dashContentList.currentIndex = selectedIndex;
188+ onSelectedIndexChanged: {
189+ if (dashContentList.currentIndex == -1 && tabBar.selectedIndex != -1) {
190+ // TODO This together with the Timer below
191+ // are a workaround for the first tab sometimes not showing the text.
192+ // But Tabs are going away in the future so not sure if makes
193+ // sense invetigating what's the problem at this stage
194+ selectionModeTimer.restart();
207195 }
196+ dashContentList.currentIndex = selectedIndex;
208197 }
209198
210− model: tabBarModel.count > 0 ? tabBarModel : null
211−
212199 Connections {
213200 target: dashContentList
214201 onCurrentIndexChanged: {
215− tabBarModel.selectedIndex = dashContentList.currentIndex
202+ tabBar.selectedIndex = dashContentList.currentIndex
216203 }
217204 }
218205
qml/Dash/DashRenderer.qml+2
@@ -33,6 +33,8 @@ Item {
3333
3434 property var delegateCreationEnd: undefined
3535
36+ property real originY: 0
37+
3638 property real verticalSpacing: 0
3739
3840 // The current item of the renderer
qml/Dash/GenericScopeView.qml+17−16
@@ -33,9 +33,6 @@ FocusScope {
3333 property PageHeader pageHeader: null
3434 property Item previewListView: null
3535
36− signal movementStarted
37− signal positionedAtBeginning
38−
3936 property bool enableHeightBehaviorOnNextCreation: false
4037 property var categoryView: categoryView
4138
@@ -49,6 +46,14 @@ FocusScope {
4946 shell.activateApplication(appId);
5047 }
5148
49+ function positionAtBeginning() {
50+ categoryView.positionAtBeginning()
51+ }
52+
53+ function showHeader() {
54+ categoryView.showHeader()
55+ }
56+
5257 Binding {
5358 target: scope
5459 property: "isActive"
@@ -69,10 +74,6 @@ FocusScope {
6974 previewListView.open = false;
7075 }
7176
72− onMovementStarted: categoryView.showHeader()
73−
74− onPositionedAtBeginning: categoryView.positionAtBeginning()
75−
7677 Binding {
7778 target: scopeView.scope
7879 property: "searchQuery"
@@ -241,22 +242,22 @@ FocusScope {
241242 // to the stable position and delete/create items without any reason
242243 if (categoryView.contentY < categoryView.originY) {
243244 return;
244− } else if (categoryView.contentY + categoryView.height > categoryView.contentHeight) {
245+ } else if (categoryView.contentHeight > categoryView.height && categoryView.contentY + categoryView.height > categoryView.contentHeight) {
245246 return;
246247 }
247248
248249 if (item && item.hasOwnProperty("delegateCreationBegin")) {
249250 if (baseItem.y + baseItem.height <= 0) {
250− // Not visible (item at top of the list)
251− item.delegateCreationBegin = baseItem.height
252− item.delegateCreationEnd = baseItem.height
251+ // Not visible (item at top of the list viewport)
252+ item.delegateCreationBegin = item.originY + baseItem.height
253+ item.delegateCreationEnd = item.originY + baseItem.height
253254 } else if (baseItem.y >= categoryView.height) {
254− // Not visible (item at bottom of the list)
255− item.delegateCreationBegin = 0
256− item.delegateCreationEnd = 0
255+ // Not visible (item at bottom of the list viewport)
256+ item.delegateCreationBegin = item.originY
257+ item.delegateCreationEnd = item.originY
257258 } else {
258− item.delegateCreationBegin = Math.max(-baseItem.y, 0)
259− item.delegateCreationEnd = Math.min(categoryView.height + item.delegateCreationBegin, baseItem.height)
259+ item.delegateCreationBegin = item.originY + Math.max(-baseItem.y, 0)
260+ item.delegateCreationEnd = item.originY + Math.min(categoryView.height + item.delegateCreationBegin, baseItem.height)
260261 }
261262 }
262263 }
qml/Dash/Previews/PreviewRatingDisplay.qml+4−2
@@ -71,7 +71,8 @@ PreviewWidget {
7171 id: authorLabel
7272 objectName: "authorLabel"
7373 anchors { left: parent.left; right: parent.right }
74− color: Theme.palette.selected.backgroundText
74+ // TODO cimi: Yet another fix requiring Palette update.
75+ color: "grey" //Theme.palette.selected.backgroundText
7576 opacity: .8
7677 text: modelData["author"] || ""
7778 visible: text !== ""
@@ -82,7 +83,8 @@ PreviewWidget {
8283 id: reviewLabel
8384 objectName: "reviewLabel"
8485 anchors { left: parent.left; right: parent.right }
85− color: Theme.palette.selected.backgroundText
86+ // TODO cimi: Yet another fix requiring Palette update.
87+ color: "grey" //Theme.palette.selected.backgroundText
8688 opacity: .8
8789 text: modelData["review"] || ""
8890 visible: text !== ""
qml/Dash/Previews/PreviewRatingInput.qml+4−2
@@ -76,7 +76,8 @@ PreviewWidget {
7676 verticalCenter: parent.verticalCenter
7777 left: parent.left
7878 }
79− color: Theme.palette.selected.backgroundText
79+ // TODO cimi: Yet another fix requiring Palette update.
80+ color: "grey" //Theme.palette.selected.backgroundText
8081 opacity: .8
8182 text: widgetData["rating-label"] || i18n.tr("Rate this")
8283 }
@@ -121,7 +122,8 @@ PreviewWidget {
121122 left: parent.left
122123 right: parent.right
123124 }
124− color: Theme.palette.selected.backgroundText
125+ // TODO cimi: Yet another fix requiring Palette update.
126+ color: "grey" //Theme.palette.selected.backgroundText
125127 opacity: .8
126128 text: widgetData["review-label"] || i18n.tr("Add a review")
127129 }
qml/Dash/ScopeDelegateMapper.qmldeleted−30
@@ -1,30 +0,0 @@
1−/*
2− * Copyright (C) 2013 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−
19−QtObject {
20− property var scopeDelegateMapping: {"clickscope": "DashApps.qml"}
21− property string genericScope: "GenericScopeView.qml"
22−
23− function map(scopeId) {
24− var customScope = scopeDelegateMapping[scopeId]
25− if (customScope != undefined) {
26− return customScope
27− }
28− return genericScope
29− }
30−}
qml/Notifications/NotificationMenuItemFactory.qml+2−1
@@ -66,7 +66,8 @@ Loader {
6666 TextField {
6767 id: textfield
6868
69− inputMethodHints: Qt.ImhNoAutoUppercase
69+ // TODO using Qt.ImhNoPredictiveText here until lp #1291575 is fixed for ubuntu-ui-toolkit
70+ inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhNoPredictiveText
7071 anchors.left: parent.left; anchors.right: parent.right
7172 echoMode: checkBox.checked ? TextInput.Normal : TextInput.Password
7273 height: units.gu(5)
qml/Shell.qml+5−12
@@ -240,6 +240,8 @@ FocusScope {
240240 function show(focusApp) {
241241 shown = true;
242242 panel.indicators.hide();
243+ edgeDemo.stopDemo();
244+ greeter.hide();
243245 if (!ApplicationManager.focusedApplicationId && ApplicationManager.count > 0 && focusApp) {
244246 ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
245247 }
@@ -267,12 +269,6 @@ FocusScope {
267269 stages.hide();
268270 }
269271 }
270−
271− // If any app is focused when greeter is open, it's due to a user action
272− // like a snap decision (say, an incoming call).
273− // TODO: these should be protected to only unlock for certain applications / certain usecases
274− // potentially only in connection with a notification.
275− greeter.hide()
276272 }
277273
278274 onApplicationAdded: {
@@ -559,12 +555,9 @@ FocusScope {
559555 }
560556 }
561557 onDashSwipeChanged: if (dashSwipe && stages.shown) dash.setCurrentScope("clickscope", false, true)
562− onLauncherApplicationSelected:{
563− if (edgeDemo.running)
564− return;
565−
566− greeter.hide()
567− shell.activateApplication(appId)
558+ onLauncherApplicationSelected: {
559+ if (!edgeDemo.running)
560+ shell.activateApplication(appId)
568561 }
569562 onShownChanged: {
570563 if (shown) {
qml/graphics/phone_background.jpg

Binary file not shown.

qml/graphics/tablet_background.jpg

Binary file not shown.

run+15−1
@@ -68,8 +68,22 @@ if $MOUSE_TOUCH; then
6868 QML_PHONE_SHELL_ARGS="$QML_PHONE_SHELL_ARGS -mousetouch"
6969fi
7070
71+control_c()
72+{
73+ /sbin/initctl stop unity8
74+ exit $?
75+}
76+
77+trap control_c INT
78+
7179if $GDB; then
7280 gdb -ex run --args $QML_PHONE_SHELL_PATH $QML_PHONE_SHELL_ARGS $@
7381else
74− $QML_PHONE_SHELL_PATH $QML_PHONE_SHELL_ARGS $@
82+ status=`/sbin/initctl status unity8`
83+ if [ "$status" != "unity8 stop/waiting" ]; then
84+ echo "Unity8 is already running, please stop it first"
85+ exit 1
86+ fi
87+ /sbin/initctl start unity8 BINARY="`readlink -f $QML_PHONE_SHELL_PATH` $QML_PHONE_SHELL_ARGS $@"
88+ tailf -n 0 ~/.cache/upstart/unity8.log
7589fi
tests/autopilot/unity8/application_lifecycle/tests/__init__.py+49
@@ -0,0 +1,49 @@
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+from autopilot.matchers import Eventually
21+from testtools.matchers import Equals
22+from ubuntuuitoolkit import fixture_setup
23+
24+from unity8 import process_helpers
25+from unity8.shell import tests
26+
27+
28+class ApplicationLifeCycleTestCase(tests.UnityTestCase):
29+
30+ def setUp(self):
31+ super(ApplicationLifeCycleTestCase, self).setUp()
32+ self._qml_mock_enabled = False
33+ self._data_dirs_mock_enabled = False
34+ unity_proxy = self.launch_unity()
35+ process_helpers.unlock_unity(unity_proxy)
36+
37+ def create_test_application(self):
38+ desktop_file_dict = fixture_setup.DEFAULT_DESKTOP_FILE_DICT
39+ desktop_file_dict.update({'X-Ubuntu-Single-Instance': 'true'})
40+ fake_application = fixture_setup.FakeApplication(
41+ desktop_file_dict=desktop_file_dict)
42+ self.useFixture(fake_application)
43+ return (
44+ fake_application.qml_file_path, fake_application.desktop_file_path)
45+
46+ def assert_current_focused_application(self, application_name):
47+ self.assertThat(
48+ self.main_window.get_current_focused_app_id,
49+ Eventually(Equals(application_name)))
tests/autopilot/unity8/application_lifecycle/tests/test_application_lifecycle.py+56−72
@@ -21,27 +21,26 @@
2121
2222from __future__ import absolute_import
2323
24−from unity8.process_helpers import unlock_unity
25−from unity8.shell import disable_qml_mocking
26−from unity8.shell.tests import UnityTestCase, _get_device_emulation_scenarios
24+import logging
25+import os
2726
2827from autopilot.matchers import Eventually
2928from autopilot.platform import model
30−from testtools.matchers import Equals, NotEquals
29+from testtools.matchers import Equals
3130
32−import logging
31+from unity8 import process_helpers
32+from unity8.application_lifecycle import tests
3333
34−logger = logging.getLogger(__name__)
3534
35+logger = logging.getLogger(__name__)
3636
37−class ApplicationLifecycleTests(UnityTestCase):
3837
39− scenarios = _get_device_emulation_scenarios()
38+class ApplicationLifecycleTests(tests.ApplicationLifeCycleTestCase):
4039
4140 def setUp(self):
41+ if model() == 'Desktop':
42+ self.skipTest('Test cannot be run on the desktop.')
4243 super(ApplicationLifecycleTests, self).setUp()
43− if model() == "Desktop":
44− self.skipTest("Test cannot be run on the desktop.")
4544
4645 def swipe_screen_from_right(self):
4746 width = self.main_window.width
@@ -54,80 +53,65 @@ class ApplicationLifecycleTests(UnityTestCase):
5453 logger.info("Swiping screen from the right edge")
5554 self.touch.drag(start_x, start_y, end_x, end_y)
5655
57− def _launch_app(self, app_name):
58− """Launches the application *app_name*
59−
60− Assumes that the desktop file resides at:
61− /usr/share/applications/{app_name}.desktop
56+ def launch_fake_app(self):
57+ _, desktop_file_path = self.create_test_application()
58+ desktop_file_name = os.path.basename(desktop_file_path)
59+ application_name, _ = os.path.splitext(desktop_file_name)
60+ self.launch_upstart_application(application_name)
61+ return application_name
6262
63− """
64− desktop_file = "--desktop_file_hint="\
65− "/usr/share/applications/{app_name}.desktop".format(
66− app_name=app_name
67− )
68− return self.launch_test_application(
69− app_name,
70− desktop_file,
71− "--stage_hint=main_stage",
72− app_type='qt'
73− )
74−
75− @disable_qml_mocking
7663 def test_can_launch_application(self):
7764 """Must be able to launch an application."""
78− unity_proxy = self.launch_unity()
79− unlock_unity(unity_proxy)
80−
81− app = self._launch_app("messaging-app")
65+ application_name = self.launch_fake_app()
66+ self.assert_current_focused_application(application_name)
8267
83− self.assertThat(app, NotEquals(None))
84− self.assertThat(
85− self.main_window.get_current_focused_app_id(),
86− Eventually(Equals("messaging-app"))
87− )
88−
89− @disable_qml_mocking
9068 def test_can_launch_multiple_applications(self):
9169 """A second application launched must be focused."""
92− unity_proxy = self.launch_unity()
93− unlock_unity(unity_proxy)
94−
95− self._launch_app("messaging-app")
96− self.assertThat(
97− self.main_window.get_current_focused_app_id(),
98− Eventually(Equals("messaging-app"))
99− )
100−
101− self._launch_app("address-book-app")
102− self.assertThat(
103− self.main_window.get_current_focused_app_id(),
104− Eventually(Equals("address-book-app"))
105− )
106−
107− @disable_qml_mocking
70+ application1_name = self.launch_fake_app()
71+ self.assert_current_focused_application(application1_name)
72+
73+ application2_name = self.launch_fake_app()
74+ self.assertFalse(application1_name == application2_name)
75+ self.assert_current_focused_application(application2_name)
76+
10877 def test_app_moves_from_unfocused_to_focused(self):
10978 """An application that is in the unfocused state must be able to be
11079 brought back to the focused state.
11180
11281 """
113− unity_proxy = self.launch_unity()
114− unlock_unity(unity_proxy)
115−
116− self._launch_app("messaging-app")
117− self.assertThat(
118− self.main_window.get_current_focused_app_id(),
119− Eventually(Equals("messaging-app"))
120− )
82+ application1_name = self.launch_fake_app()
83+ self.assert_current_focused_application(application1_name)
12184
122− self._launch_app("address-book-app")
123− self.assertThat(
124− self.main_window.get_current_focused_app_id(),
125− Eventually(Equals("address-book-app"))
126− )
85+ application2_name = self.launch_fake_app()
86+ self.assertFalse(application1_name == application2_name)
87+ self.assert_current_focused_application(application2_name)
12788
12889 self.swipe_screen_from_right()
12990
130− self.assertThat(
131− self.main_window.get_current_focused_app_id(),
132− Eventually(Equals("messaging-app"))
133− )
91+ self.assert_current_focused_application(application1_name)
92+
93+ def test_greeter_hides_on_app_open(self):
94+ """Greeter should hide when an app is opened"""
95+ process_helpers.lock_unity()
96+ greeter = self.main_window.get_greeter()
97+ self.assertThat(greeter.created, Eventually(Equals(True)))
98+
99+ application_name = self.launch_fake_app()
100+ self.assertThat(greeter.created, Eventually(Equals(False)))
101+ self.assert_current_focused_application(application_name)
102+
103+ def test_greeter_hides_on_app_focus(self):
104+ """Greeter should hide when an app is re-focused"""
105+ application_name = self.launch_fake_app()
106+ self.assert_current_focused_application(application_name)
107+
108+ self.main_window.show_dash_swiping()
109+ self.assert_current_focused_application('')
110+
111+ process_helpers.lock_unity()
112+ greeter = self.main_window.get_greeter()
113+ self.assertThat(greeter.created, Eventually(Equals(True)))
114+
115+ self.launch_upstart_application(application_name)
116+ self.assertThat(greeter.created, Eventually(Equals(False)))
117+ self.assert_current_focused_application(application_name)
tests/autopilot/unity8/application_lifecycle/tests/test_url_dispatcher.py+8−39
@@ -20,48 +20,19 @@
2020"""Test the integration with the URL dispatcher service."""
2121
2222import os
23+import subprocess
2324
2425from autopilot import platform
25−from autopilot.matchers import Eventually
26−from testtools.matchers import Equals
27−from ubuntuuitoolkit import fixture_setup
2826
29−from unity8 import process_helpers
30−from unity8.shell import tests
27+from unity8.application_lifecycle import tests
3128
3229
33−class URLDispatcherTestCase(tests.UnityTestCase):
34−
35− scenarios = tests._get_device_emulation_scenarios()
36−
37− test_qml = ("""
38−import QtQuick 2.0
39−import Ubuntu.Components 0.1
40−
41−MainView {
42− width: units.gu(48)
43− height: units.gu(60)
44−
45− Label {
46− text: 'Test application.'
47− }
48−}
49−""")
30+class URLDispatcherTestCase(tests.ApplicationLifeCycleTestCase):
5031
5132 def setUp(self):
5233 if platform.model() == 'Desktop':
5334 self.skipTest("URL dispatcher doesn't work on the desktop.")
5435 super(URLDispatcherTestCase, self).setUp()
55− self._qml_mock_enabled = False
56− self._data_dirs_mock_enabled = False
57− unity_proxy = self.launch_unity()
58− process_helpers.unlock_unity(unity_proxy)
59−
60− def create_test_application(self):
61− fake_application = fixture_setup.FakeApplication(self.test_qml)
62− self.useFixture(fake_application)
63− return (
64− fake_application.qml_file_path, fake_application.desktop_file_path)
6536
6637 def test_swipe_out_application_started_by_url_dispatcher(self):
6738 _, desktop_file_path = self.create_test_application()
@@ -70,12 +41,10 @@ MainView {
7041
7142 self.assertEqual('', self.main_window.get_current_focused_app_id())
7243 self.addCleanup(os.system, 'pkill qmlscene')
73− os.system('url-dispatcher application:///{}'.format(desktop_file_name))
74− self.assertThat(
75− self.main_window.get_current_focused_app_id,
76− Eventually(Equals(application_name)))
44+
45+ subprocess.check_call(
46+ ['url-dispatcher', 'application:///{}'.format(desktop_file_name)])
47+ self.assert_current_focused_application(application_name)
7748
7849 self.main_window.show_dash_swiping()
79− self.assertThat(
80− self.main_window.get_current_focused_app_id,
81− Eventually(Equals('')))
50+ self.assert_current_focused_application('')
tests/autopilot/unity8/indicators/tests/__init__.py+17
@@ -0,0 +1,17 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity - Indicators Autopilot Test Suite
4+# Copyright (C) 2013, 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/>.
tests/autopilot/unity8/indicators/tests/test_indicators.py+69−13
@@ -1,7 +1,7 @@
11# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
22#
3−# Unity - Indicators Autopilot Test Suite
4−# Copyright (C) 2013 Canonical
3+# Unity Indicators Autopilot Test Suite
4+# Copyright (C) 2013, 2014 Canonical
55#
66# This program is free software: you can redistribute it and/or modify
77# it under the terms of the GNU General Public License as published by
@@ -15,20 +15,32 @@
1515#
1616# You should have received a copy of the GNU General Public License
1717# along with this program. If not, see <http://www.gnu.org/licenses/>.
18−#
1918
2019from __future__ import absolute_import
2120
21+from testscenarios import multiply_scenarios
22+
2223from autopilot import platform
23−from testtools import skipIf
2424
2525from unity8.process_helpers import unlock_unity
26−from unity8.shell.tests import UnityTestCase
26+from unity8.shell.tests import UnityTestCase, _get_device_emulation_scenarios
2727
2828
2929class IndicatorTestCase(UnityTestCase):
3030
31− scenarios = [
31+ device_emulation_scenarios = _get_device_emulation_scenarios()
32+
33+ def setUp(self):
34+ if platform.model() == 'Desktop':
35+ self.skipTest('Test cannot be run on the desktop.')
36+ super(IndicatorTestCase, self).setUp()
37+ self.unity_proxy = self.launch_unity()
38+ unlock_unity(self.unity_proxy)
39+
40+
41+class IndicatorExistsTestCase(IndicatorTestCase):
42+
43+ indicator_scenarios = [
3244 ('Bluetooth', dict(indicator_name='indicator-bluetooth')),
3345 ('Datetime', dict(indicator_name='indicator-datetime')),
3446 ('Location', dict(indicator_name='indicator-location')),
@@ -37,14 +49,58 @@ class IndicatorTestCase(UnityTestCase):
3749 ('Power', dict(indicator_name='indicator-power')),
3850 ('Sound', dict(indicator_name='indicator-sound')),
3951 ]
52+ scenarios = multiply_scenarios(
53+ indicator_scenarios,
54+ IndicatorTestCase.device_emulation_scenarios
55+ )
4056
41− @skipIf(platform.image_codename() == 'Desktop',
42− "Unity8/phablet-only test!")
4357 def setUp(self):
44− super(IndicatorTestCase, self).setUp()
58+ super(IndicatorExistsTestCase, self).setUp()
59+ if (platform.model() == 'Nexus 10' and
60+ self.indicator_name == 'indicator-bluetooth'):
61+ self.skipTest('Nexus 10 does not have bluetooth at the moment.')
4562
4663 def test_indicator_exists(self):
47− unity_proxy = self.launch_unity()
48− unlock_unity(unity_proxy)
49− indicator = self.main_window.get_indicator(self.indicator_name)
50− self.assertIsNotNone(indicator)
64+ self.main_window._get_indicator_widget(
65+ self.indicator_name
66+ )
67+
68+
69+class IndicatorPageTitleMatchesWidgetTestCase(IndicatorTestCase):
70+
71+ indicator_scenarios = [
72+ ('Bluetooth', dict(indicator_name='indicator-bluetooth',
73+ title='Bluetooth')),
74+ ('Datetime', dict(indicator_name='indicator-datetime',
75+ title='Upcoming')),
76+ ('Location', dict(indicator_name='indicator-location',
77+ title='Location')),
78+ ('Messaging', dict(indicator_name='indicator-messages',
79+ title='Incoming')),
80+ ('Network', dict(indicator_name='indicator-network',
81+ title='Network')),
82+ ('Power', dict(indicator_name='indicator-power',
83+ title='Battery')),
84+ ('Sound', dict(indicator_name='indicator-sound',
85+ title='Sound')),
86+ ]
87+ scenarios = multiply_scenarios(
88+ indicator_scenarios,
89+ IndicatorTestCase.device_emulation_scenarios
90+ )
91+
92+ def setUp(self):
93+ super(IndicatorPageTitleMatchesWidgetTestCase, self).setUp()
94+ if (platform.model() == 'Nexus 10' and
95+ self.indicator_name == 'indicator-bluetooth'):
96+ self.skipTest('Nexus 10 does not have bluetooth at the moment.')
97+
98+ def test_indicator_page_title_matches_widget(self):
99+ """Swiping open an indicator must show its correct title.
100+
101+ See https://bugs.launchpad.net/ubuntu-ux/+bug/1253804 .
102+ """
103+ indicator_page = self.main_window.open_indicator_page(
104+ self.indicator_name)
105+ self.assertTrue(indicator_page.visible)
106+ self.assertEqual(indicator_page.title, self.title)
tests/autopilot/unity8/process_helpers.py+16
@@ -89,6 +89,22 @@ def unlock_unity(unity_proxy_obj=None):
8989 break
9090
9191
92+def lock_unity(unity_proxy_obj=None):
93+ """Helper function that attempts to lock the unity greeter."""
94+ import evdev, time
95+ uinput = evdev.UInput(name='unity8-autopilot-power-button',
96+ devnode='/dev/autopilot-uinput')
97+ # One press and release to turn screen off (locking unity)
98+ uinput.write(evdev.ecodes.EV_KEY, evdev.ecodes.KEY_POWER, 1)
99+ uinput.write(evdev.ecodes.EV_KEY, evdev.ecodes.KEY_POWER, 0)
100+ uinput.syn()
101+ time.sleep(1)
102+ # And another press and release to turn screen back on
103+ uinput.write(evdev.ecodes.EV_KEY, evdev.ecodes.KEY_POWER, 1)
104+ uinput.write(evdev.ecodes.EV_KEY, evdev.ecodes.KEY_POWER, 0)
105+ uinput.syn()
106+
107+
92108def restart_unity_with_testability(*args):
93109 """Restarts (or starts) unity with testability enabled.
94110
tests/autopilot/unity8/shell/emulators/__init__.py+2−6
@@ -17,16 +17,12 @@
1717# along with this program. If not, see <http://www.gnu.org/licenses/>.
1818#
1919
20−from autopilot import input, introspection
20+from ubuntuuitoolkit import emulators as toolkit_emulators
2121
2222
23−class UnityEmulatorBase(introspection.CustomEmulatorBase):
23+class UnityEmulatorBase(toolkit_emulators.UbuntuUIToolkitEmulatorBase):
2424 """A base class for all unity emulators."""
2525
26− def __init__(self, *args):
27− super(UnityEmulatorBase, self).__init__(*args)
28− self.pointing_device = input.Pointer(device=input.Touch.create())
29−
3026
3127class UnityEmulatorException(Exception):
3228 """Exception raised when there is an error with the Unity emulators."""
tests/autopilot/unity8/shell/emulators/dash.py+25−5
@@ -23,6 +23,8 @@ from unity8.shell import emulators
2323
2424from autopilot import logging as autopilot_logging
2525from autopilot.introspection import dbus
26+from testtools.matchers import MatchesAny, Equals
27+from ubuntuuitoolkit import emulators as toolkit_emulators
2628
2729
2830logger = logging.getLogger(__name__)
@@ -132,6 +134,19 @@ class Dash(emulators.UnityEmulatorBase):
132134 self.pointing_device.drag(start_x, start_y, stop_x, stop_y)
133135 self.dash_content_list.currentIndex.wait_for(original_index + 1)
134136
137+ def enter_search_query(self, query):
138+ search_text_field = self._get_search_text_field()
139+ search_text_field.write(query)
140+ search_text_field.state.wait_for('idle')
141+
142+ def _get_search_text_field(self):
143+ page_header = self._get_page_header()
144+ search_container = page_header.select_single(
145+ 'QQuickItem', objectName='searchContainer')
146+ search_container.state.wait_for(
147+ MatchesAny(Equals('narrowActive'), Equals('active')))
148+ return search_container.select_single(toolkit_emulators.TextField)
149+
135150
136151class GenericScopeView(emulators.UnityEmulatorBase):
137152 """Autopilot emulator for generic scopes."""
@@ -176,13 +191,18 @@ class DashApps(GenericScopeView):
176191
177192 """
178193 category_element = self._get_category_element(category)
179− application_tiles = category_element.select_many('Card')
180− # TODO return them on the same order they are displayed.
181− # --elopio - 2014-1-15
194+ application_cards = category_element.select_many('Card')
195+
196+ # sort by y, x
197+ application_cards = sorted(
198+ application_cards,
199+ key=lambda card: (card.globalRect.y, card.globalRect.x))
200+
182201 result = []
183− for card in application_tiles:
202+ for card in application_cards:
184203 if card.objectName != 'cardToolCard':
185− result.append(card)
204+ card_header = card.select_single('CardHeader')
205+ result.append(card_header.title)
186206 return result
187207
188208
tests/autopilot/unity8/shell/emulators/main_window.py+32−2
@@ -20,6 +20,7 @@
2020import logging
2121
2222from autopilot import logging as autopilot_logging
23+from autopilot import input
2324
2425from unity8.shell import emulators
2526from unity8.shell.emulators.greeter import Greeter
@@ -87,12 +88,32 @@ class QQuickView(emulators.UnityEmulatorBase):
8788 def get_pinentryField(self):
8889 return self.select_single(objectName="pinentryField")
8990
90− def get_indicator(self, indicator_name):
91− return self.wait_select_single(
91+ def _get_indicator_widget(self, indicator_name):
92+ return self.select_single(
9293 'DefaultIndicatorWidget',
9394 objectName=indicator_name+'-widget'
9495 )
9596
97+ def _get_indicator_page(self, indicator_name):
98+ return self.select_single(
99+ 'DefaultIndicatorPage',
100+ objectName=indicator_name+'-page'
101+ )
102+
103+ @autopilot_logging.log_action(logger.info)
104+ def open_indicator_page(self, indicator_name):
105+ """Swipe to open the indicator, wait until it's open.
106+
107+ :returns: The indicator page.
108+ """
109+ widget = self._get_indicator_widget(indicator_name)
110+ start_x, start_y = input.get_center_point(widget)
111+ end_x = start_x
112+ end_y = self.height
113+ self.pointing_device.drag(start_x, start_y, end_x, end_y)
114+ self.wait_select_single('Indicators', fullyOpened=True)
115+ return self._get_indicator_page(indicator_name)
116+
96117 def get_shell_background(self):
97118 return self.select_single(
98119 "CrossFadeImage", objectName="backgroundImage")
@@ -113,3 +134,12 @@ class QQuickView(emulators.UnityEmulatorBase):
113134 def get_current_focused_app_id(self):
114135 """Return the id of the focused application."""
115136 return self.select_single('Shell').focusedApplicationId
137+
138+ @autopilot_logging.log_action(logger.info)
139+ def search(self, query):
140+ search_indicator = self._get_search_indicator()
141+ self.pointing_device.click_object(search_indicator)
142+ self.get_dash().enter_search_query(query)
143+
144+ def _get_search_indicator(self):
145+ return self.select_single('SearchIndicator', objectName='search')
tests/autopilot/unity8/shell/tests/test_emulators.py+19−11
@@ -39,6 +39,22 @@ from unity8.shell import emulators, fixture_setup, tests
3939from unity8.shell.emulators import dash as dash_emulators
4040
4141
42+class MainWindowTestCase(tests.UnityTestCase):
43+
44+ scenarios = tests._get_device_emulation_scenarios()
45+
46+ def setUp(self):
47+ super(MainWindowTestCase, self).setUp()
48+ unity_proxy = self.launch_unity()
49+ process_helpers.unlock_unity(unity_proxy)
50+
51+ def test_search(self):
52+ self.main_window.search('Test')
53+ text_field = self.main_window.get_dash()._get_search_text_field()
54+ self.assertEqual(text_field.text, 'Test')
55+ self.assertEqual(text_field.state, 'idle')
56+
57+
4258class DashBaseTestCase(tests.UnityTestCase):
4359
4460 scenarios = tests._get_device_emulation_scenarios()
@@ -150,7 +166,7 @@ class DashAppsEmulatorTestCase(DashBaseTestCase):
150166 available_applications = [
151167 'Title.2.0', 'Title.2.1', 'Title.2.2', 'Title.2.3', 'Title.2.4',
152168 'Title.2.5', 'Title.2.6', 'Title.2.7', 'Title.2.8', 'Title.2.9',
153− 'Title.2.9', 'Title.2.10', 'Title.2.11']
169+ 'Title.2.10', 'Title.2.11', 'Title.2.12']
154170
155171 def setUp(self):
156172 # Set up the fake scopes before launching unity.
@@ -167,21 +183,13 @@ class DashAppsEmulatorTestCase(DashBaseTestCase):
167183 self.assertEqual(
168184 'No category found with name unexisting category', str(exception))
169185
170− def test_get_applications_should_return_list_with_names(self):
186+ def test_get_applications_should_return_correct_applications(self):
171187 category = '2'
172188 expected_apps_count = self._get_number_of_application_slots(category)
173189 expected_applications = self.available_applications[
174190 :expected_apps_count]
175−
176191 applications = self.applications_scope.get_applications(category)
177− applications_titles = []
178− for application in applications:
179− cardHeader = application.select_single('CardHeader')
180− applications_titles.append(cardHeader.title)
181−
182− self.assertThat(applications, HasLength(expected_apps_count))
183− for expected in expected_applications:
184− self.assertThat(applications_titles, Contains(expected))
192+ self.assertEqual(expected_applications, applications)
185193
186194 def _get_number_of_application_slots(self, category):
187195 category_element = self.applications_scope._get_category_element(
tests/autopilot/unity8/shell/tests/test_notifications.py+3−4
@@ -196,7 +196,7 @@ class InteractiveNotificationBase(NotificationsBase):
196196 get_notification = lambda: notify_list.wait_select_single(
197197 'Notification', objectName='notification1')
198198 notification = get_notification()
199− self._assert_notification(notification, None, None, True, True, 1.0)
199+ self._assert_notification(notification, summary, body, True, True, 1.0)
200200 initial_height = notification.height
201201 self.touch.tap_object(notification.select_single(objectName="button1"))
202202 self.assertThat(
@@ -403,7 +403,6 @@ class EphemeralNotificationsTests(NotificationsBase):
403403
404404 notification = lambda: notify_list.wait_select_single(
405405 'Notification', objectName='notification1')
406−
407406 self._assert_notification(
408407 notification(),
409408 summary,
@@ -579,7 +578,7 @@ class EphemeralNotificationsTests(NotificationsBase):
579578 icon_path = self._get_icon_path('avatars/amanda.png')
580579 notification.update(summary, body, icon_path)
581580 notification.show()
582− self._assert_notification(get_notification(), summary, body)
581+ self._assert_notification(get_notification(), summary, body, True, False, 1.0)
583582
584583 def test_update_notification_layout_change(self):
585584 """Notification must allow updating its contents and layout while
@@ -626,7 +625,7 @@ class EphemeralNotificationsTests(NotificationsBase):
626625 notification.show()
627626
628627 self.assertThat(get_notification, Eventually(NotEquals(None)))
629− self._assert_notification(get_notification(), summary, body, False)
628+ self._assert_notification(get_notification(), summary, body, False, False, 1.0)
630629
631630 def test_append_hint(self):
632631 """Notification has to accumulate body-text using append-hint."""
tests/autopilot/unity8/shell/tests/test_system_integration.py+1−1
@@ -37,5 +37,5 @@ class SystemIntegrationTests(UnityTestCase):
3737 self.launch_unity()
3838
3939 # invoke policykit to check permissions
40− pid = subprocess.check_output("pidof -s unity8", shell=True)
40+ pid = subprocess.check_output(["pidof", "-s", "unity8"], universal_newlines=True)
4141 subprocess.check_call("pkcheck --action-id org.freedesktop.NetworkManager.enable-disable-network --process " + pid, shell=True)
tests/qmltests/CMakeLists.txt+1−1
@@ -11,7 +11,6 @@ set(qmltest_DEFAULT_IMPORT_PATHS
1111set(qmltest_DEFAULT_TARGETS qmlunittests)
1212set(qmltest_DEFAULT_NO_ADD_TEST FALSE)
1313set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=minimal")
14−add_qml_test(Components AnimationControllerWithSignals)
1514add_qml_test(Panel IndicatorItem)
1615add_qml_test(utils/Unity/Test UnityTest)
1716
@@ -36,6 +35,7 @@ add_qml_test(Components PageHeaderLabel)
3635add_qml_test(Dash Dash IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
3736add_qml_test(Dash DashContent IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
3837add_qml_test(Dash Card)
38+add_qml_benchmark(Dash CardBenchmark 1000)
3939add_qml_test(Dash CardHeader)
4040add_qml_test(Dash CardTool)
4141add_qml_test(Dash GenericScopeView IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
tests/qmltests/Components/tst_AnimationControllerWithSignals.qmldeleted−142
@@ -1,142 +0,0 @@
1−/*
2− * Copyright 2013 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 "../../../qml/Components"
20−
21−TestCase {
22− id: testCase
23− name: "AnimationControllerWithSignals"
24−
25− property real someNumber: 0.0
26−
27− NumberAnimation {
28− id: someNumberAnimation
29− target: testCase
30− properties: "someNumber"
31− from: 1.0
32− to: 100.0
33− }
34−
35− Component {
36− id: testSubjectComponent
37− AnimationControllerWithSignals {}
38− }
39−
40− Loader {
41− id: loader
42− asynchronous: false
43− }
44−
45− SignalSpy {
46− id: signalSpy
47− }
48−
49− function test_completeToEndWithSignal() {
50− loader.sourceComponent = testSubjectComponent;
51− var testSubject = loader.item;
52−
53− testSubject.animation = someNumberAnimation;
54− testSubject.progress = 0;
55−
56− signalSpy.signalName = "animationCompletedAtEnd";
57− signalSpy.target = testSubject;
58− signalSpy.clear();
59−
60− compare(testSubject.completing, false)
61− compare(testSubject.completed, true)
62−
63− testSubject.completeToEndWithSignal();
64− compare(testSubject.completing, true)
65− compare(testSubject.completed, true)
66− compare(signalSpy.count, 0);
67−
68− testSubject.progress = 0.5;
69− compare(testSubject.completing, true)
70− compare(testSubject.completed, false)
71− compare(signalSpy.count, 0);
72−
73− testSubject.progress = 1.0;
74− compare(testSubject.completing, false)
75− compare(testSubject.completed, true)
76− compare(signalSpy.count, 1);
77−
78− testSubject.animation = null
79− loader.sourceComponent = undefined;
80− }
81−
82− function test_completeToBeginningWithSignal() {
83− loader.sourceComponent = testSubjectComponent;
84− var testSubject = loader.item;
85−
86− testSubject.animation = someNumberAnimation;
87− testSubject.progress = 1;
88−
89− signalSpy.signalName = "animationCompletedAtBeginning";
90− signalSpy.target = testSubject;
91− signalSpy.clear();
92−
93− compare(testSubject.completing, false)
94− compare(testSubject.completed, true)
95−
96− testSubject.completeToBeginningWithSignal();
97− compare(testSubject.completing, true)
98− compare(testSubject.completed, true)
99− compare(signalSpy.count, 0);
100−
101− testSubject.progress = 0.5;
102− compare(testSubject.completing, true)
103− compare(testSubject.completed, false)
104− compare(signalSpy.count, 0);
105−
106− testSubject.progress = 0;
107− compare(testSubject.completing, false)
108− compare(testSubject.completed, true)
109− compare(signalSpy.count, 1);
110−
111− testSubject.animation = null
112− loader.sourceComponent = undefined;
113− }
114−
115− function test_settingProgressWithoutCallingCompleteWithSignal() {
116− loader.sourceComponent = testSubjectComponent;
117− var testSubject = loader.item;
118−
119− testSubject.animation = someNumberAnimation;
120− testSubject.progress = 0;
121−
122− signalSpy.signalName = "animationCompletedAtEnd";
123− signalSpy.target = testSubject;
124− signalSpy.clear();
125−
126− compare(testSubject.completing, false)
127− compare(testSubject.completed, true)
128−
129− testSubject.progress = 0.5;
130− compare(testSubject.completing, false)
131− compare(testSubject.completed, false)
132− compare(signalSpy.count, 0);
133−
134− testSubject.progress = 1;
135− compare(testSubject.completing, false)
136− compare(testSubject.completed, true)
137− compare(signalSpy.count, 0);
138−
139− testSubject.animation = null
140− loader.sourceComponent = undefined;
141− }
142−}
tests/qmltests/Components/tst_Carousel.qml+34−7
@@ -18,6 +18,7 @@ import QtQuick 2.0
1818import QtTest 1.0
1919import "../../../qml/Components/"
2020import "../../../qml/Components/carousel.js" as Carousel
21+import Unity.Test 0.1 as UT
2122
2223Item {
2324 width: 700
@@ -31,13 +32,17 @@ Item {
3132 property int createdDelegates: 0
3233 property int destroyedDelegates: 0
3334
34− itemComponent: Rectangle {
35− color: "red"
36− Component.onCompleted: carousel.createdDelegates++
37− Component.onDestruction: carousel.destroyedDelegates++
35+ itemComponent: BaseCarouselDelegate {
36+ Rectangle {
37+ anchors.fill: parent
38+ border.color: "black"
39+ color: "red"
40+ Component.onCompleted: carousel.createdDelegates++
41+ Component.onDestruction: carousel.destroyedDelegates++
42+ }
3843 }
3944 }
40− TestCase {
45+ UT.UnityTestCase {
4146 id: root
4247 name: "Carousel"
4348 when: windowShown
@@ -55,8 +60,11 @@ Item {
5560 property real kGapEnd: kMiddleIndex * (1 - gapToEndPhase / gapToMiddlePhase)
5661 property real kXBeginningEnd: 1 / tileWidth + kMiddleIndex / gapToMiddlePhase
5762
58− function test_onlyNeededItemsCreated() {
59− compare(carousel.createdDelegates, 10);
63+ // This test needs to run first since it tests a corner case condition
64+ // to where the carousel is just being created
65+ // That is why it has a 1 in the name
66+ function test_1onlyNeededItemsCreated() {
67+ tryCompare(carousel, "createdDelegates", 10);
6068 compare(carousel.destroyedDelegates, 0);
6169 }
6270
@@ -216,5 +224,24 @@ Item {
216224 data.maxTranslation)
217225 compare(scale, data.result)
218226 }
227+
228+ function test_lastItemXPosition() {
229+ var carouselList = findChild(carousel, "listView");
230+ var stepAnimation = findInvisibleChild(carouselList, "stepAnimation");
231+ var stepDiff = 0;
232+ stepAnimation.onRunningChanged.connect(function() { if (stepAnimation.running) stepDiff = Math.abs(carouselList.contentX - stepAnimation.to); })
233+ var overshootOnce = false;
234+ while (!overshootOnce) {
235+ overshootOnce = carouselList.realContentX > carouselList.realContentWidth - carouselList.realWidth;
236+ mouseFlick(carousel, carousel.width - units.gu(1),
237+ carousel.height / 2,
238+ units.gu(2),
239+ carousel.height / 2);
240+ tryCompare(carouselList, "moving", false);
241+ }
242+ verify(stepDiff < 1);
243+ var x = carouselList.getXFromContinuousIndex(carousel.model - 1);
244+ verify(Math.abs(x - carouselList.contentX) < 1);
245+ }
219246 }
220247}
tests/qmltests/Dash/qml/FakeScopeView.qmldeleted−45
@@ -1,45 +0,0 @@
1−/*
2− * Copyright 2013 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 Unity 0.2
19−
20−import "../../../../qml/Dash"
21−
22−GenericScopeView {
23− id: fakeScopeView
24−
25− property alias backColor : back.color
26− property var categoryView: null
27−
28− onMovementStarted: {
29− if (shell != undefined && shell.scopeStatus != undefined) {
30− shell.scopeStatus[scope.id].movementStarted++;
31− }
32− }
33− onPositionedAtBeginning: {
34− if (shell != undefined && shell.scopeStatus != undefined) {
35− shell.scopeStatus[scope.id].positionedAtBeginning++;
36− }
37− }
38−
39− Rectangle {
40− id: back
41− anchors.fill: parent
42− color: "grey"
43− z: -1
44− }
45−}
tests/qmltests/Dash/qml/fake_generic_scopeView.qmldeleted−24
@@ -1,24 +0,0 @@
1−/*
2− * Copyright 2013 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−
19−FakeScopeView {
20− id: fake_generic_scopeView
21− objectName: "fake_generic_scopeView"
22−
23− backColor: "#D0D0D0"
24−}
tests/qmltests/Dash/qml/fake_scopeView1.qmldeleted−24
@@ -1,24 +0,0 @@
1−/*
2− * Copyright 2013 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−
19−FakeScopeView {
20− id: fake_scopeView1
21− objectName: "fake_scopeView1"
22−
23− backColor: "#90E090"
24−}
tests/qmltests/Dash/qml/fake_scopeView2.qmldeleted−24
@@ -1,24 +0,0 @@
1−/*
2− * Copyright 2013 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−
19−FakeScopeView {
20− id: fake_scopeView2
21− objectName: "fake_scopeView2"
22−
23− backColor: "#A0D0A0"
24−}
tests/qmltests/Dash/qml/fake_scopeView3.qmldeleted−24
@@ -1,24 +0,0 @@
1−/*
2− * Copyright 2013 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−
19−FakeScopeView {
20− id: fake_scopeView3
21− objectName: "fake_scopeView3"
22−
23− backColor: "#B0C0B0"
24−}
tests/qmltests/Dash/qml/fake_scopeView4.qmldeleted−24
@@ -1,24 +0,0 @@
1−/*
2− * Copyright 2013 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−
19−FakeScopeView {
20− id: fake_scopeView4
21− objectName: "fake_scopeView4"
22−
23− backColor: "#C0C0C0"
24−}
tests/qmltests/Dash/tst_Card.qml+12−10
@@ -182,11 +182,13 @@ Rectangle {
182182 when: windowShown
183183
184184 property Item header: findChild(card, "cardHeader")
185+ property Item headerLoader: findChild(card, "cardHeaderLoader")
185186 property Item title: findChild(header, "titleLabel")
186187 property Item art: findChild(card, "artShape")
187188 property Item artImage: findChild(card, "artImage")
188189 property Item summary: findChild(card, "summaryLabel")
189190 property Item background: findChild(card, "background")
191+ property Item backgroundLoader: findChild(card, "backgroundLoader")
190192 property Item backgroundImage: findChild(card, "backgroundImage")
191193
192194 function initTestCase() {
@@ -244,8 +246,8 @@ Rectangle {
244246 { tag: "Wide", width: units.gu(18.5), aspect: 0.5, index: 0 },
245247 { tag: "Horizontal", width: units.gu(38), index: 5 },
246248 // Make sure card ends with header when there's no summary
247− { tag: "NoSummary", height: function() { return header.y + header.height }, index: 6 },
248− { tag: "HorizontalNoSummary", height: function() { return header.height }, card_layout: "horizontal", index: 6 },
249+ { tag: "NoSummary", height: function() { return headerLoader.y + headerLoader.height }, index: 6 },
250+ { tag: "HorizontalNoSummary", height: function() { return headerLoader.height }, card_layout: "horizontal", index: 6 },
249251 ]
250252 }
251253
@@ -288,7 +290,7 @@ Rectangle {
288290 { tag: "Fit", height: units.gu(38), size: "large", width: units.gu(19), index: 4 },
289291 { tag: "VerticalWidth", width: function() { return header.width }, index: 0 },
290292 { tag: "HorizontalHeight", height: function() { return header.height }, index: 5 },
291− { tag: "HorizontalWidth", width: function() { return header.x }, index: 5 },
293+ { tag: "HorizontalWidth", width: function() { return headerLoader.x }, index: 5 },
292294 ]
293295 }
294296
@@ -332,7 +334,7 @@ Rectangle {
332334 function test_art_layout(data) {
333335 selector.selectedIndex = data.index;
334336
335− tryCompare(testCase.header, "x", data.left());
337+ tryCompare(testCase.headerLoader, "x", data.left());
336338 }
337339
338340 function test_header_layout_data() {
@@ -349,13 +351,13 @@ Rectangle {
349351 function test_header_layout(data) {
350352 selector.selectedIndex = data.index;
351353
352− tryCompareFunction(function() { return testCase.header.y === data.top() }, true);
353− tryCompareFunction(function() { return testCase.header.x === data.left() }, true);
354+ tryCompareFunction(function() { return testCase.headerLoader.y === data.top() }, true);
355+ tryCompareFunction(function() { return testCase.headerLoader.x === data.left() }, true);
354356 }
355357
356358 function test_summary_layout_data() {
357359 return [
358− { tag: "With header", top: function() { return header.y + header.height }, index: 0 },
360+ { tag: "With header", top: function() { return headerLoader.y + headerLoader.height }, index: 0 },
359361 { tag: "Without header", top: function() { return art.y + art.height }, index: 7 },
360362 ]
361363 }
@@ -400,7 +402,7 @@ Rectangle {
400402
401403 waitForRendering(card);
402404
403− tryCompare(background, "visible", data.visible);
405+ tryCompare(backgroundLoader, "active", data.visible);
404406
405407 if (data.hasOwnProperty("color")) {
406408 tryCompare(background, "color", data.color);
@@ -467,13 +469,13 @@ Rectangle {
467469 function test_mascotShape(data) {
468470 selector.selectedIndex = data.index;
469471
470− var shape = findChild(card, "mascotShape");
472+ var shape = findChild(card, "mascotShapeLoader");
471473 var image = findChild(card, "mascotImage");
472474
473475 verify(shape, "Could not find shape.");
474476 verify(image, "Could not find image.");
475477
476− tryCompare(shape, "visible", data.shape);
478+ tryCompare(shape, "active", data.shape);
477479 tryCompare(image, "visible", !data.shape);
478480 }
479481 }
tests/qmltests/Dash/tst_CardBenchmark.qmlnew file+94
@@ -0,0 +1,94 @@
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 QtTest 1.0
19+import Ubuntu.Components 0.1
20+import Unity.Test 0.1 as UT
21+import "../../../qml/Dash"
22+import "CardHelpers.js" as Helpers
23+
24+Rectangle {
25+ id: root
26+ width: units.gu(80)
27+ height: units.gu(72)
28+ color: "#88FFFFFF"
29+
30+ property string cardData: '
31+ {
32+ "art": "../../tests/qmltests/Dash/artwork/music-player-design.png",
33+ "mascot": "../../tests/qmltests/Dash/artwork/avatar.png",
34+ "title": "foo",
35+ "subtitle": "bar",
36+ "summary": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
37+ }'
38+
39+ property string currentModel: '{}'
40+ property string cardTitleArtSubtitleMascotSummaryModel: '{ "components": { "title": "title", "art": "art", "subtitle": "subtitle", "mascot": "mascot", "summary": "summary" } }'
41+ property string cardTitleArtSubtitleMascotModel: '{ "components": { "title": "title", "art": "art", "subtitle": "subtitle", "mascot": "mascot" } }'
42+ property string cardTitleArtSubtitleModel: '{ "components": { "title": "title", "art": "art", "subtitle": "subtitle" } }'
43+ property string cardTitleArtModel: '{ "components": { "title": "title", "art": "art" } }'
44+ property string cardArtModel: '{ "components": { "art": "art" } }'
45+ property string cardTitleModel: '{ "components": { "title": "title" } }'
46+
47+ CardTool {
48+ id: cardTool
49+ template: Helpers.update(JSON.parse(Helpers.defaultLayout), JSON.parse(currentModel))['template'];
50+ components: Helpers.update(JSON.parse(Helpers.defaultLayout), JSON.parse(currentModel))['components'];
51+ viewWidth: units.gu(48)
52+ }
53+
54+ Repeater {
55+ id: cardRepeater
56+ model: 0
57+ Card {
58+ width: cardTool.cardWidth || implicitWidth
59+ height: cardTool.cardHeight || implicitHeight
60+
61+ template: cardTool.template
62+ components: cardTool.components
63+ cardData: Helpers.mapData(root.cardData, components)
64+ }
65+ }
66+
67+ UT.UnityTestCase {
68+ id: testCase
69+ name: "CardBenchmark"
70+
71+ when: windowShown
72+
73+ function init() {
74+ wait(1);
75+ }
76+
77+ function benchmark_time_data() {
78+ return [
79+ { tag: "cardTitleArtSubtitleMascotSummaryModel", model: cardTitleArtSubtitleMascotSummaryModel },
80+ { tag: "cardTitleArtSubtitleMascotModel", model: cardTitleArtSubtitleMascotModel },
81+ { tag: "cardTitleArtSubtitleModel", model: cardTitleArtSubtitleModel },
82+ { tag: "cardTitleArtModel", model: cardTitleArtModel },
83+ { tag: "cardArtModel", model: cardArtModel },
84+ { tag: "cardTitleModel", model: cardTitleModel },
85+ ];
86+ }
87+
88+ function benchmark_time(data) {
89+ currentModel = data.model;
90+ cardRepeater.model = 1;
91+ cardRepeater.model = 0;
92+ }
93+ }
94+}
tests/qmltests/Dash/tst_CardHeader.qml+1−1
@@ -43,7 +43,7 @@ Rectangle {
4343
4444 when: windowShown
4545
46− property Item mascot: findChild(cardHeader, "mascotShape")
46+ property Item mascot: findChild(cardHeader, "mascotShapeLoader")
4747 property Item titleLabel: findChild(cardHeader, "titleLabel")
4848 property Item subtitleLabel: findChild(cardHeader, "subtitleLabel")
4949 property Item oldPriceLabel: findChild(cardHeader, "oldPriceLabel")
tests/qmltests/Dash/tst_Dash.qml−11
@@ -41,16 +41,6 @@ Item {
4141 showScopeOnLoaded: "MockScope2"
4242 }
4343
44− ScopeDelegateMapper {
45− id: scopeDelegateMapper
46− scopeDelegateMapping: {
47− "MockScope1": Qt.resolvedUrl("qml/fake_scopeView1.qml"),
48− "MockScope2": Qt.resolvedUrl("qml/fake_scopeView2.qml"),
49− "clickscope": Qt.resolvedUrl("qml/fake_scopeView3.qml")
50− }
51− genericScope: Qt.resolvedUrl("qml/fake_generic_scopeView.qml")
52− }
53−
5444 UT.UnityTestCase {
5545 name: "Dash"
5646 when: windowShown
@@ -59,7 +49,6 @@ Item {
5949
6050 Component.onCompleted: {
6151 var dashContent = findChild(dash, "dashContent");
62− dashContent.scopeMapper = scopeDelegateMapper;
6352 scopes = dashContent.scopes;
6453 }
6554
tests/qmltests/Dash/tst_DashContent.qml+27−51
@@ -35,13 +35,6 @@ Item {
3535 property var panel: null
3636 // BEGIN To reduce warnings
3737
38− property var scopeStatus: {
39− 'MockScope1': { 'movementStarted': 0, 'positionedAtBeginning': 0 },
40− 'MockScope2': { 'movementStarted': 0, 'positionedAtBeginning': 0 },
41− 'clickscope': { 'movementStarted': 0, 'positionedAtBeginning': 0 },
42− 'MockScope5': { 'movementStarted': 0, 'positionedAtBeginning': 0 }
43− }
44−
4538 Scopes {
4639 id: scopesModel
4740 }
@@ -55,34 +48,9 @@ Item {
5548 }
5649 scopes : scopesModel
5750
58− scopeMapper : scopeDelegateMapper
5951 searchHistory: SearchHistoryModel {}
6052 }
6153
62− ScopeDelegateMapper {
63− id: scopeDelegateMapper
64− scopeDelegateMapping: {
65− "MockScope1": Qt.resolvedUrl("qml/fake_scopeView1.qml"),
66− "MockScope2": Qt.resolvedUrl("qml/fake_scopeView2.qml"),
67− "clickscope": Qt.resolvedUrl("qml/fake_scopeView3.qml"),
68− }
69− genericScope: Qt.resolvedUrl("qml/fake_generic_scopeView.qml")
70− }
71−
72− function clear_scope_status() {
73− scopeStatus["MockScope1"].movementStarted = 0;
74− scopeStatus["MockScope1"].positionedAtBeginning = 0;
75−
76− scopeStatus["MockScope2"].movementStarted = 0;
77− scopeStatus["MockScope2"].positionedAtBeginning = 0;
78−
79− scopeStatus["clickscope"].movementStarted = 0;
80− scopeStatus["clickscope"].positionedAtBeginning = 0;
81−
82− scopeStatus["MockScope5"].movementStarted = 0;
83− scopeStatus["MockScope5"].positionedAtBeginning = 0;
84− }
85−
8654 SignalSpy {
8755 id: scopeLoadedSpy
8856 target: dashContent
@@ -97,7 +65,7 @@ Item {
9765
9866 UT.UnityTestCase {
9967 name: "DashContent"
100− when: scopesModel.loaded
68+ when: scopesModel.loaded && windowShown
10169
10270 function init() {
10371 scopesModel.clear();
@@ -108,7 +76,6 @@ Item {
10876
10977 function cleanup() {
11078 movementStartedSpy.clear();
111− clear_scope_status();
11279 dashContent.visible = true;
11380
11481 var dashContentList = findChild(dashContent, "dashContentList");
@@ -149,29 +116,38 @@ Item {
149116 verify(dashContentList.currentIndex >= 0 && dashContentList.currentIndex < 5);
150117 }
151118
152− function test_movement_started_signal() {
153− dashContent.setCurrentScopeAtIndex(3, true, false);
154−
119+ function test_show_header_on_list_movement() {
155120 var dashContentList = findChild(dashContent, "dashContentList");
156− verify(dashContentList != undefined)
121+ verify(dashContentList != undefined);
122+ var categoryListView = findChild(dashContentList, "categoryListView");
123+ verify(categoryListView != undefined);
124+
125+ waitForRendering(categoryListView);
157126
158− dashContentList.movementStarted();
127+ categoryListView.contentY = units.gu(11);
128+ console.log("contentY", categoryListView.contentY);
159129
160− compare(movementStartedSpy.count, 1, "DashContent should have emitted movementStarted signal when content list did.");
161− compare(scopeStatus["MockScope1"].movementStarted, 1, "MockScope1 should have emitted movementStarted signal when content list did.");
162− compare(scopeStatus["MockScope2"].movementStarted, 1, "MockScope2 should have emitted movementStarted signal when content list did.");
163− compare(scopeStatus["clickscope"].movementStarted, 1, "clickscope should have emitted movementStarted signal when content list did.");
164− compare(scopeStatus["MockScope5"].movementStarted, 1, "MockScope5 should have emitted movementStarted signal when content list did.");
130+ var startX = dashContentList.width/2;
131+ var startY = dashContentList.height/2;
132+ touchFlick(dashContentList, startX - units.gu(2), startY, startX, startY);
133+ tryCompare(categoryListView, "contentY", units.gu(11) - categoryListView.pageHeader.height);
165134 }
166135
167− function test_positioned_at_beginning_signal() {
168− dashContent.setCurrentScopeAtIndex(3, true, false);
136+ function test_set_current_scope_reset() {
137+ var dashContentList = findChild(dashContent, "dashContentList");
138+ verify(dashContentList != undefined);
139+ var categoryListView = findChild(dashContentList, "categoryListView");
140+ verify(categoryListView != undefined);
141+
142+ categoryListView.contentY = units.gu(10);
143+
144+ compare(dashContentList.currentItem.item.objectName, "MockScope1")
145+ compare(categoryListView.contentY, units.gu(10));
146+
147+ dashContent.setCurrentScopeAtIndex(0, false, true);
169148
170− dashContent.positionedAtBeginning();
171− compare(scopeStatus["MockScope1"].positionedAtBeginning, 1, "MockScope1 should have emitted positionedAtBeginning signal when DashContent did.");
172− compare(scopeStatus["MockScope2"].positionedAtBeginning, 1, "MockScope2 should have emitted positionedAtBeginning signal when DashContent did.");
173− compare(scopeStatus["clickscope"].positionedAtBeginning, 1, "clickscope should have emitted positionedAtBeginning signal when DashContent did.");
174− compare(scopeStatus["MockScope5"].positionedAtBeginning, 1, "MockScope5 should have emitted positionedAtBeginning signal when DashContent did.");
149+ compare(dashContentList.currentItem.item.objectName, "MockScope1")
150+ compare(categoryListView.contentY, 0);
175151 }
176152
177153 // This tests that setting the current scope index will end up at the correct index even if
tests/qmltests/Dash/tst_GenericScopeView.qml+1−1
@@ -64,7 +64,7 @@ Item {
6464
6565 UT.UnityTestCase {
6666 name: "GenericScopeView"
67− when: scopes.loaded
67+ when: scopes.loaded && windowShown
6868
6969 function init() {
7070 genericScopeView.scope = scopes.get(2)
tests/qmltests/Launcher/tst_Launcher.qml+1
@@ -66,6 +66,7 @@ Item {
6666
6767 UT.UnityTestCase {
6868 id: revealer
69+ when: windowShown
6970
7071 function dragLauncherIntoView() {
7172 var startX = launcher.dragAreaWidth/2;
tests/qmltests/Panel/Indicators/tst_MenuItemFactory.qml+1
@@ -44,6 +44,7 @@ Item {
4444
4545 UT.UnityTestCase {
4646 name: "MenuItemFactory"
47+ when: windowShown
4748
4849 property QtObject menuData: QtObject {
4950 property string label: "root"
tests/qmltests/Panel/Indicators/tst_MessageMenuItemFactory.qml+1
@@ -39,6 +39,7 @@ Item {
3939
4040 UT.UnityTestCase {
4141 name: "MessageMenuItemFactory"
42+ when: windowShown
4243
4344 property QtObject menuData: QtObject {
4445 property string label: "root"
tests/qmltests/tst_Shell.qml+11
@@ -554,5 +554,16 @@ Item {
554554
555555 tryCompare(searchIndicator, "opacity", 0);
556556 }
557+
558+ function test_focusRequestedHidesGreeter() {
559+ var greeter = findChild(shell, "greeter")
560+
561+ greeter.show()
562+ tryCompare(greeter, "showProgress", 1)
563+
564+ ApplicationManager.focusRequested("notes-app")
565+ tryCompare(greeter, "showProgress", 0)
566+ waitUntilApplicationWindowIsFullyVisible()
567+ }
557568 }
558569}
tools/CMakeLists.txt+2
@@ -9,3 +9,5 @@ qt5_use_modules(${SCOPE_TOOL} Qml Quick)
99install(TARGETS ${SCOPE_TOOL}
1010 RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
1111 )
12+
13+install(FILES unlock-device DESTINATION ${SHELL_APP_DIR})
tools/unlock-devicenew file+42
@@ -0,0 +1,42 @@
1+#!/bin/bash
2+# -*- Mode: sh; indent-tabs-mode: nil; tab-width: 4 -*-
3+
4+# Our goal here is to get an Ubuntu Touch device into a testable unlocked state.
5+# This will include a reboot.
6+
7+WAIT_COMMAND="sleep 20"
8+
9+while getopts s:w: opt; do
10+ case $opt in
11+ s)
12+ export ANDROID_SERIAL=$OPTARG
13+ ;;
14+ w)
15+ WAIT_COMMAND=$OPTARG
16+ ;;
17+ esac
18+done
19+
20+UNLOCK_SCRIPT='
21+import dbus, logging;
22+from unity8 import process_helpers as helpers;
23+logging.basicConfig(level=logging.INFO);
24+bus = dbus.SystemBus().get_object("com.canonical.powerd", "/com/canonical/powerd");
25+cookie = bus.requestSysState("unlock-device-hold", 1, dbus_interface="com.canonical.powerd");
26+helpers.restart_unity_with_testability();
27+bus.clearSysState(cookie, dbus_interface="com.canonical.powerd");
28+helpers.unlock_unity()
29+'
30+
31+adb reboot
32+sleep 5
33+adb "wait-for-device"
34+eval "$WAIT_COMMAND"
35+UNLOCK_OUTPUT=$(adb shell "sudo -u phablet -i python3 -c '$UNLOCK_SCRIPT'" 2>&1)
36+if echo "$UNLOCK_OUTPUT" | grep 'Greeter unlocked' >/dev/null; then
37+ echo "I: Unlock passed"
38+ exit 0
39+else
40+ echo "I: Unlock failed, script output: '$UNLOCK_OUTPUT'"
41+ exit 1
42+fi