on xenial help sign in
Merged with trunk and resolved merge-conflicts.
commit743e7334c794effffebb7d57c6ad0df819f004ce authorMirco Müller <mirco.mueller@canonical.com> dateWed 30 Apr 2014, 10:42 (12 years ago) parentde6ed96e1ea44b
112 files changed+1775−2406
.bazaar/plugins/makecheck_unity_phablet.py+1−1 CODING−4 build+1 cmake/modules/QmlTest.cmake+75−17 data/CMakeLists.txt+1−2 data/unity-scope-tool.desktop.in−8 data/unity8.conf+10−1 debian/changelog+200 debian/control+34−25 debian/unity-scope-tool.install−1 debian/unity8-autopilot.install+1 debian/unity8-common.install+5 debian/unity8-fake-env.install+1 debian/unity8-private.install+1−1 debian/unity8.install+1−6 include/paths.h.in+10 plugins/Unity/Indicators/CMakeLists.txt−3 plugins/Unity/Indicators/indicatorsmanager.cpp−33 plugins/Unity/Indicators/indicatorsmanager.h−2 plugins/Unity/Indicators/qml/MenuItemFactory.qml+1−1 po/ast.po+5−22 po/bg.po+1−1 po/br.po+164 po/fi.po+5−22 po/gl.po+5−22 po/ru.po+12−29 po/ug.po+8−24 po/unity8.pot+7−3 qml/Components/AnimationControllerWithSignals.qml−64 qml/Components/Carousel.qml+13−19 qml/Components/FilterGrid.qml+38−24 qml/Components/ResponsiveGridView.qml+1 qml/Components/Stage.qml−571 qml/Dash/Card.qml+99−79 qml/Dash/CardCarousel.qml+1−1 qml/Dash/CardFilterGrid.qml+8−14 qml/Dash/CardHeader.qml+41−31 qml/Dash/DashContent.qml+5−10 qml/Dash/DashRenderer.qml+5−9 qml/Dash/GenericScopeView.qml+29−30 qml/Dash/Previews/PreviewRatingDisplay.qml+4−2 qml/Dash/Previews/PreviewRatingInput.qml+6−4 qml/Dash/ScopeDelegateMapper.qml−30 qml/Greeter/Clock.qml+3−6 qml/Greeter/Greeter.qml+115−42 qml/Greeter/GreeterContent.qml+1−10 qml/Greeter/Infographics.qml+3 qml/Hud/Hud.qml+1−1 qml/Notifications/NotificationMenuItemFactory.qml+2−1 qml/Shell.qml+64−57 qml/graphics/dropshadow_left@20.png qml/Hud/graphics/dropshadow_right@20.png → qml/graphics/dropshadow_right@20.png qml/graphics/phone_background.jpg qml/graphics/tablet_background.jpg run+15−1 src/main.cpp+3 tests/CMakeLists.txt+2−2 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/__init__.py+50−1 tests/autopilot/unity8/shell/emulators/__init__.py+2−6 tests/autopilot/unity8/shell/emulators/dash.py+34−6 tests/autopilot/unity8/shell/emulators/main_window.py+32−2 tests/autopilot/unity8/shell/tests/test_emulators.py+27−17 tests/autopilot/unity8/shell/tests/test_notifications.py+17−61 tests/autopilot/unity8/shell/tests/test_system_integration.py+3−2 tests/autopilot/unity8/shell/tests/test_upstart.py+2−2 tests/mocks/Unity/Application/ApplicationScreenshotProvider.cpp+3 tests/mocks/Unity/Application/CMakeLists.txt+14 tests/plugins/DashViews/CMakeLists.txt+23−35 tests/plugins/DashViews/listviewwithpageheadertestsectionexternalmodel.cpp → tests/plugins/DashViews/listviewwithpageheadersectionexternalmodeltest.cpp+1−1 tests/plugins/DashViews/listviewwithpageheadertestsection.cpp → tests/plugins/DashViews/listviewwithpageheadersectiontest.cpp+1−1 tests/plugins/DashViews/tst_listviewwithpageheaderqml.qml → tests/plugins/DashViews/tst_ListViewWithPageHeaderQML.qml tests/plugins/Ubuntu/Gestures/CMakeLists.txt+2−10 tests/plugins/Utils/CMakeLists.txt+2−1 tests/qmltests/CMakeLists.txt+2−6 tests/qmltests/Components/CMakeLists.txt+3−10 tests/qmltests/Components/tst_AnimationControllerWithSignals.qml−142 tests/qmltests/Components/tst_Carousel.qml+34−7 tests/qmltests/Components/tst_FilterGrid.qml+1−1 tests/qmltests/Components/tst_LazyImage.qml+1 tests/qmltests/Components/tst_Stage.qml−278 tests/qmltests/Components/tst_Stage/AppControl.qml−42 tests/qmltests/Components/tst_Stage/FakeApplicationScreenshot.qml−53 tests/qmltests/Dash/Previews/tst_PreviewRatingInput.qml+21−2 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+5−4 tests/qmltests/Greeter/tst_Clock.qml+3−3 tests/qmltests/Greeter/tst_Phone.qml+10−50 tests/qmltests/Greeter/tst_Tablet.qml+36−8 tests/qmltests/Launcher/tst_Launcher.qml+1 tests/qmltests/Panel/Indicators/tst_MenuItemFactory.qml+30−4 tests/qmltests/Panel/Indicators/tst_MessageMenuItemFactory.qml+1 tests/qmltests/SideStage/tst_SideStage.qml−52 tests/qmltests/tst_Shell.qml+12−1 tools/CMakeLists.txt+2 tools/scopetool.cpp+1 tools/unlock-device+42
.bazaar/plugins/makecheck_unity_phablet.py+1−1
@@ -18,7 +18,7 @@ from bzrlib import branch, errors
1818from bzrlib.urlutils import dirname, local_path_from_url
1919
2020def execute_makecheck(local_branch, master_branch, old_revision_number, old_revision_id, future_revision_number, future_revision_id, tree_delta, future_tree):
21− if not master_branch.basis_tree().has_filename("Shell.qml"):
21+ if not master_branch.basis_tree().has_filename("qml/Shell.qml"):
2222 return
2323
2424 os.chdir(local_path_from_url(master_branch.base))
CODING−4
@@ -167,10 +167,6 @@ NB: Manual test or interation doesn't make sense for all components or classes,
167167particularly the non-graphical ones. In such cases no "tryFoo" make target is
168168provided.
169169
170−NB: The tests expect the locale to be the C locale, they may fail if run
171−under a different locale, if you get failures others can not reproduce
172−prepend 'LC_ALL=C' to the 'make test' command
173−
174170Running autopilot tests
175171=======================
176172
build+1
@@ -54,6 +54,7 @@ install_dependencies() {
5454 unity-scope-home \
5555 unity-lens-applications \
5656 unity-plugin-scopes \
57+ xvfb \
5758 || exit 5
5859}
5960
cmake/modules/QmlTest.cmake+75−17
@@ -58,13 +58,22 @@ 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} ${ARGN})
63+endmacro(add_qml_benchmark)
64+
6165macro(add_qml_test SUBPATH COMPONENT_NAME)
66+ add_qml_test_internal(${SUBPATH} ${COMPONENT_NAME} 0 ${ARGN})
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
6573 cmake_parse_arguments(qmltest "${options}" "" "${multi_value_keywords}" ${ARGN})
6674
6775 set(qmltest_TARGET test${COMPONENT_NAME})
76+ set(qmltest_xvfb_TARGET xvfbtest${COMPONENT_NAME})
6877 set(qmltest_FILE ${SUBPATH}/tst_${COMPONENT_NAME})
6978
7079 set(qmltestrunner_imports "")
@@ -82,24 +91,74 @@ macro(add_qml_test SUBPATH COMPONENT_NAME)
8291
8392 string(TOLOWER "${CMAKE_GENERATOR}" cmake_generator_lower)
8493 if(cmake_generator_lower STREQUAL "unix makefiles")
85− set(qmltest_command
86− env ${qmltest_ENVIRONMENT}
87− ${qmltestrunner_exe} -input ${CMAKE_CURRENT_SOURCE_DIR}/${qmltest_FILE}.qml
88− ${qmltestrunner_imports}
89− -o ${CMAKE_BINARY_DIR}/${qmltest_TARGET}.xml,xunitxml
90− -o -,txt
91− $(FUNCTION)
92− )
94+ set(function_ARGS $(FUNCTION))
95+ else()
96+ set(function_ARGS "")
97+ endif()
98+
99+ if (${ITERATIONS} GREATER 0)
100+ set(ITERATIONS_STRING "-iterations" ${ITERATIONS})
93101 else()
94− set(qmltest_command
95− env ${qmltest_ENVIRONMENT}
96− ${qmltestrunner_exe} -input ${CMAKE_CURRENT_SOURCE_DIR}/${qmltest_FILE}.qml
97− ${qmltestrunner_imports}
98− -o ${CMAKE_BINARY_DIR}/${qmltest_TARGET}.xml,xunitxml
99− -o -,txt
100− )
102+ set(ITERATIONS_STRING "")
101103 endif()
102104
105+ set(qmltest_command
106+ env ${qmltest_ENVIRONMENT}
107+ ${qmltestrunner_exe} -input ${CMAKE_CURRENT_SOURCE_DIR}/${qmltest_FILE}.qml
108+ ${qmltestrunner_imports}
109+ ${ITERATIONS_STRING}
110+ -o ${CMAKE_BINARY_DIR}/${qmltest_TARGET}.xml,xunitxml
111+ -o -,txt
112+ ${function_ARGS}
113+ )
114+ find_program( HAVE_GCC gcc )
115+ if (NOT ${HAVE_GCC} STREQUAL "")
116+ exec_program( gcc ARGS "-dumpmachine" OUTPUT_VARIABLE ARCH_TRIPLET )
117+ set(LD_PRELOAD_PATH "LD_PRELOAD=/usr/lib/${ARCH_TRIPLET}/mesa/libGL.so.1")
118+ endif()
119+ set(qmltest_xvfb_command
120+ env ${qmltest_ENVIRONMENT} ${LD_PRELOAD_PATH}
121+ xvfb-run --server-args "-screen 0 1024x768x24" --auto-servernum
122+ ${qmltestrunner_exe} -input ${CMAKE_CURRENT_SOURCE_DIR}/${qmltest_FILE}.qml
123+ ${qmltestrunner_imports}
124+ -o ${CMAKE_BINARY_DIR}/${qmltest_TARGET}.xml,xunitxml
125+ -o -,txt
126+ ${function_ARGS}
127+ )
128+
129+ add_qmltest_target(${qmltest_TARGET} "${qmltest_command}" TRUE ${qmltest_NO_ADD_TEST})
130+ add_qmltest_target(${qmltest_xvfb_TARGET} "${qmltest_xvfb_command}" ${qmltest_NO_TARGETS} TRUE)
131+ add_manual_qml_test(${SUBPATH} ${COMPONENT_NAME} ${ARGN})
132+endmacro(add_qml_test_internal)
133+
134+macro(add_binary_qml_test CLASS_NAME LD_PATH DEPS)
135+ set(testCommand
136+ LD_LIBRARY_PATH=${LD_PATH}
137+ ${CMAKE_CURRENT_BINARY_DIR}/${CLASS_NAME}TestExec
138+ -o ${CMAKE_BINARY_DIR}/${CLASSNAME}Test.xml,xunitxml
139+ -o -,txt)
140+
141+ add_qmltest_target(test${CLASS_NAME} "${testCommand}" FALSE TRUE)
142+ add_dependencies(test${CLASS_NAME} ${CLASS_NAME}TestExec ${DEPS})
143+
144+ find_program( HAVE_GCC gcc )
145+ if (NOT ${HAVE_GCC} STREQUAL "")
146+ exec_program( gcc ARGS "-dumpmachine" OUTPUT_VARIABLE ARCH_TRIPLET )
147+ set(LD_PRELOAD_PATH "LD_PRELOAD=/usr/lib/${ARCH_TRIPLET}/mesa/libGL.so.1")
148+ endif()
149+ set(xvfbtestCommand
150+ ${LD_PRELOAD_PATH}
151+ LD_LIBRARY_PATH=${LD_PATH}
152+ xvfb-run --server-args "-screen 0 1024x768x24" --auto-servernum
153+ ${CMAKE_CURRENT_BINARY_DIR}/${CLASS_NAME}TestExec
154+ -o ${CMAKE_BINARY_DIR}/${CLASS_NAME}Test.xml,xunitxml
155+ -o -,txt)
156+
157+ add_qmltest_target(xvfbtest${CLASS_NAME} "${xvfbtestCommand}" FALSE TRUE)
158+ add_dependencies(qmluitests xvfbtest${CLASS_NAME})
159+endmacro(add_binary_qml_test)
160+
161+macro(add_qmltest_target qmltest_TARGET qmltest_command qmltest_NO_TARGETS qmltest_NO_ADD_TEST)
103162 add_custom_target(${qmltest_TARGET} ${qmltest_command})
104163
105164 if(NOT "${qmltest_PROPERTIES}" STREQUAL "")
@@ -130,5 +189,4 @@ macro(add_qml_test SUBPATH COMPONENT_NAME)
130189 endif()
131190 endif("${qmltest_NO_TARGETS}" STREQUAL "FALSE")
132191
133− add_manual_qml_test(${SUBPATH} ${COMPONENT_NAME} ${ARGN})
134−endmacro(add_qml_test)
192+endmacro(add_qmltest_target)
data/CMakeLists.txt+1−2
@@ -1,8 +1,7 @@
11# generate desktop file
22configure_file(${SHELL_APP}.desktop.in ${CMAKE_CURRENT_BINARY_DIR}/${SHELL_APP}.desktop @ONLY)
3−configure_file(${SCOPE_TOOL}.desktop.in ${CMAKE_CURRENT_BINARY_DIR}/${SCOPE_TOOL}.desktop @ONLY)
43
54# install desktop files
6−install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${SHELL_APP}.desktop ${CMAKE_CURRENT_BINARY_DIR}/${SCOPE_TOOL}.desktop
5+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${SHELL_APP}.desktop
76 DESTINATION ${CMAKE_INSTALL_DATADIR}/applications
87 )
data/unity-scope-tool.desktop.indeleted−8
@@ -1,8 +0,0 @@
1−[Desktop Entry]
2−Type=Application
3−Name=Unity Scope Tool
4−Comment=Developer tool for Unity scopes.
5−Exec=@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_BINDIR@/unity-scope-tool
6−Terminal=false
7−Icon=unity-scope-tool
8−X-Ubuntu-Touch=true
data/unity8.conf+10−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
@@ -12,6 +12,9 @@ respawn
1212expect stop
1313
1414pre-start script
15+ # Tell unity-mir to raise SIGSTOP after we start
16+ initctl set-env UNITY_MIR_EMITS_SIGSTOP=1
17+
1518 if [ -n "$MIR_SOCKET" ]; then
1619 if [ -z "$UNITY_MIR_SOCKET" ]; then
1720 # Save original value of MIR_SOCKET in case we are restarted,
@@ -42,6 +45,12 @@ pre-start script
4245 fi
4346
4447 initctl emit scope-ui-starting
48+ initctl emit --no-wait indicator-services-start
4549end script
4650
4751exec ${BINARY:-unity8} $ARGS
52+
53+post-stop script
54+ initctl set-env --global MIR_SOCKET=$UNITY_MIR_SOCKET
55+ initctl unset-env --global UNITY_MIR_SOCKET
56+end script
debian/changelog+200
@@ -1,3 +1,203 @@
1+unity8 (7.86+14.10.20140429.2-0ubuntu1) utopic; urgency=medium
2+
3+ [ Andrea Cimitan ]
4+ * Update upstart job to reflect latest unity-mir changes
5+ * Fix locale in qml tests and fixtimeformattertest (LP: #1301038)
6+ * Fix 1309135 (LP: #1309135)
7+
8+ [ Michał Sawicz ]
9+ * Split out unity8-common package
10+ * Don't wait for indicator services to start, and drop Scope Tool's
11+ .desktop file. (LP: #1310172)
12+
13+ [ Michael Terry ]
14+ * Stop clock from hiding when the 'show dash' button is pressed in
15+ greeter. (LP: #1308139)
16+ * Make swipe teases in the greeter more helpful and obvious (LP:
17+ #1267623)
18+
19+ [ Nick Dedekind ]
20+ * Fixed datetime indicator appointment colour (LP: #1307048)
21+
22+ [ Albert Astals ]
23+ * Improve Card creation time by adding loaders that make sure only
24+ what's needed is loaded (LP: #1297197)
25+ * CategoryDelegateRange: Fix condition for detecting overshooting
26+ * Make xvfbtests work in the DashView plugins
27+ * Fix binding loop in FilterGrid height
28+
29+ [ Victor R. Ruiz ]
30+ * Move autopilot notification code to a helper method.
31+
32+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 29 Apr 2014 15:21:33 +0000
33+
34+unity8 (7.85+14.04.20140416-0ubuntu1) trusty; urgency=low
35+
36+ [ Albert Astals ]
37+ * Fix last item X position Fixes clicking on the last item sometimes
38+ not working (LP: #1301871)
39+ * Use upstart in ./run Makes it so that you can use the lock button on
40+ the device without getting that nasty hwc crash
41+ * Remove AnimationControllerWithSignals.
42+ * Use the correct delegate base item for the Carousel test
43+ * Some simplification in DashContent Kill the ScopeDelegateMapper in
44+ favour of a simple if (that will eventually go away). Removal of all
45+ the fake scopes in the tests that added nothing of value to the
46+ tests. Removal of movementEnded signal that was unused. Removal of
47+ movementStarted and positionedAtBeginning signals that were being
48+ used as function calls. Rework DashContent tests so they what the
49+ function does what it is supposed to do instead of just making sure
50+ QML signals work .
51+ * Improve Card creation time by adding loaders that make sure only
52+ what's needed is loaded In my computer it goes from RESULT :
53+ qmltestrunner::benchmark_time:"cardTitleArtSubtitleMascotSummaryMode
54+ l": 3.217 msecs per iteration (total: 3,218, iterations: 1000)
55+ RESULT :
56+ qmltestrunner::benchmark_time:"cardTitleArtSubtitleMascotModel":
57+ 1.647 msecs per iteration (total: 1,648, iterations: 1000) RESULT :
58+ qmltestrunner::benchmark_time:"cardTitleArtSubtitleModel": 1.514
59+ msecs per iteration (total: 1,515, iterations: 1000) RESULT :
60+ qmltestrunner::benchmark_time:"cardTitleArtModel": 1.471 msecs per
61+ iteration (total: 1,471, iterations: 1000) RESULT :
62+ qmltestrunner::benchmark_time:"cardArtModel": 1.447 msecs per
63+ iteration (total: 1,448, iterations: 1000) RESULT :
64+ qmltestrunner::benchmark_time:"cardTitleModel": 1.276 msecs per
65+ iteration (total: 1,276, iterations: 1000) to RESULT :
66+ qmltestrunner::benchmark_time:"cardTitleArtSubtitleMascotSummaryMode
67+ l": 2.916 msecs per iteration (total: 2,917, iterations: 1000)
68+ RESULT :
69+ qmltestrunner::benchmark_time:"cardTitleArtSubtitleMascotModel":
70+ 1.504 msecs per iteration (total: 1,504, iterations: 1000) RESULT :
71+ qmltestrunner::benchmark_time:"cardTitleArtSubtitleModel": 1.060
72+ msecs per iteration (total: 1,061, iterations: 1000) RESULT :
73+ qmltestrunner::benchmark_time:"cardTitleArtModel": 1.052 msecs per
74+ iteration (total: 1,053, iterations: 1000) RESULT :
75+ qmltestrunner::benchmark_time:"cardArtModel": 0.727 msecs per
76+ iteration (total: 728, iterations: 1000) RESULT :
77+ qmltestrunner::benchmark_time:"cardTitleModel": 0.817 msecs per
78+ iteration (total: 818, iterations: 1000) (LP: #1297197)
79+
80+ [ Allan LeSage ]
81+ * DashApps emulator get_applications should return a list ordered by
82+ visible y, x.
83+
84+ [ Andrea Cimitan ]
85+ * Workaround for lp1301309 until fixes for palette in ui toolkit (LP:
86+ #1301309)
87+
88+ [ Leo Arias ]
89+ * Reverted the change that returns application cards instead of
90+ titles.
91+
92+ [ Nick Dedekind ]
93+ * Indicator services started by unity8 upstart configuration rather
94+ than manual emmision from indicator manager.
95+
96+ [ Mirco Müller ]
97+ * Fix notification ap-test assertions.
98+
99+ [ Michael Terry ]
100+ * Use new tablet and phone backgrounds from Design.
101+
102+ [ Michael Zanetti ]
103+ * workaround the QTestLogger assertion issue with make tryXyz and our
104+ custom uqmlscene
105+
106+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 16 Apr 2014 13:45:01 +0000
107+
108+unity8 (7.85+14.04.20140415.2-0ubuntu1) trusty; urgency=low
109+
110+ [ Michael Terry ]
111+ * When an application requests focus, handle it in Shell.qml by hiding
112+ the greeter and stopping any edge demo. (LP: #1227753)
113+
114+ [ Leo Arias ]
115+ * Use subprocess.check_call when caling url-dispatcher, so an error
116+ will be raised if it fails.
117+ * Test application life cycle with fake apps, instead of messaging and
118+ address book.
119+
120+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 15 Apr 2014 12:47:11 +0000
121+
122+unity8 (7.85+14.04.20140410.1-0ubuntu1) trusty; urgency=medium
123+
124+ [ Didier Roche ]
125+ * Resync trunk with previous revert upload
126+
127+ [ Michał Sawicz ]
128+ * Set the Qt.ImhNoPredictiveText flag on wifi password field, fixes
129+ lp:1291575 (LP: #1291575)
130+
131+ [ Albert Astals ]
132+ * Take into account the originY when specifying the delegate ranges
133+ Fixes bug #1300302 (LP: #1300302)
134+
135+ [ CI bot ]
136+ * Resync trunk
137+
138+ [ Allan LeSage ]
139+ * Swiping open an indicator must show its correct title--protect
140+ against lp:1253804 . (LP: #1253804)
141+
142+ [ Alexander Sack ]
143+ * Fix TypeError: issue seen in system_integration autopilot test on
144+ image 279. (LP: #1303685)
145+
146+ [ Bill Filler ]
147+ * Set the Qt.ImhNoPredictiveText flag on wifi password field, fixes
148+ lp:1291575 (LP: #1291575)
149+
150+ [ Leo Arias ]
151+ * Added a search autopilot helper.
152+
153+ [ Michael Terry ]
154+ * Provide a all-in-one script for getting a device to an unlocked
155+ state.
156+
157+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Thu, 10 Apr 2014 10:03:31 +0000
158+
159+unity8 (7.85+14.04.20140404.is.7.85+14.04.20140403.1-0ubuntu1) trusty; urgency=medium
160+
161+ * Revert to previous version as it's linked to latest sdk change which
162+ is making gallery-app AP tests failing on the CI dashboard
163+
164+ -- Didier Roche <didrocks@ubuntu.com> Tue, 08 Apr 2014 13:53:47 +0200
165+
166+unity8 (7.85+14.04.20140404-0ubuntu1) trusty; urgency=low
167+
168+ [ Albert Astals ]
169+ * Adapt to new TabBar
170+
171+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Fri, 04 Apr 2014 15:03:00 +0000
172+
173+unity8 (7.85+14.04.20140403.1-0ubuntu1) trusty; urgency=low
174+
175+ [ Michael Terry ]
176+ * Re-enable test_networkmanager_integration autopilot test on phone
177+ platforms
178+
179+ [ CI bot ]
180+ * Resync trunk
181+
182+ [ Leo Arias ]
183+ * Reverted the open_preview autopilot helper to return a Preview
184+ object.
185+
186+ [ Albert Astals ]
187+ * If not running in Mir load the "fake" application manager (LP:
188+ #1301547)
189+ * Remove unused properties from DashRenderer
190+
191+ [ Michael Zanetti ]
192+ * Fix tests after right edge merge. Drop old stages tests. Fix right
193+ edge tests if someone doesn't have the GRID_UNIT_PX exported. make
194+ GenericScopeView test more robust that broke because the ordering
195+ changed
196+ * add "make xvfbtestSomething" target to run qml tests in xvfb
197+ * make the "make test" commit hook work again
198+
199+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Thu, 03 Apr 2014 10:38:53 +0000
200+
1201unity8 (7.85+14.04.20140401.3-0ubuntu1) trusty; urgency=medium
2202
3203 [ Michał Sawicz ]
debian/control+34−25
@@ -11,37 +11,34 @@ Build-Depends: cmake,
1111 libgl1-mesa-dev[!armhf] | libgl-dev[!armhf],
1212 libgl1-mesa-dri,
1313 libgles2-mesa-dev[armhf],
14+ libglib2.0-dev,
1415 libgsettings-qt-dev,
1516 libhud-client2-dev,
16− libnih-dbus-dev,
17− libnih-dev,
1817 libpulse-dev,
1918 libqmenumodel-dev (>= 0.2.7),
19+ libqt5xmlpatterns5-dev,
2020 libunity-api-dev (>= 7.80.6),
2121 libunity-mir-dev,
22− libupstart-dev,
2322 libusermetricsoutput1-dev,
2423 libxcb1-dev,
25− libglib2.0-dev,
2624 pkg-config,
27− python:any (>= 2.7),
2825 python-setuptools,
2926 python3-all:any,
3027 python3-setuptools,
28+ python:any (>= 2.7),
3129 qt5-default,
3230 qtbase5-dev,
3331 qtbase5-dev-tools,
3432 qtbase5-private-dev,
3533 qtdeclarative5-dev,
3634 qtdeclarative5-dev-tools,
37− qtdeclarative5-qtmultimedia-plugin,
3835 qtdeclarative5-private-dev,
36+ qtdeclarative5-qtmultimedia-plugin,
3937 qtdeclarative5-qtquick2-plugin,
4038 qtdeclarative5-test-plugin,
4139 qtdeclarative5-ubuntu-settings-components,
4240 qtdeclarative5-ubuntu-ui-toolkit-plugin,
4341 qtdeclarative5-xmllistmodel-plugin,
44− libqt5xmlpatterns5-dev,
4542 ttf-ubuntu-font-family,
4643Standards-Version: 3.9.4
4744Homepage: http://launchpad.net/unity
@@ -63,7 +60,8 @@ Description: Indicators client test application
6360
6461Package: unity-scope-tool
6562Architecture: any
66−Depends: unity8 (= ${binary:Version}),
63+Depends: unity8-common (= ${source:Version}),
64+ unity8-private (= ${binary:Version}),
6765 ${misc:Depends},
6866 ${shlibs:Depends},
6967Description: Development tool for Unity scopes
@@ -74,27 +72,18 @@ Package: unity8
7472Architecture: any
7573Provides: indicator-renderer,
7674Depends: gsettings-desktop-schemas,
75+ libcap2-bin,
7776 libglib2.0-bin,
78− libunity-mir1,
77+ libunity-mir1 (>= 0.4),
7978 qmenumodel-qml (>= 0.2.7),
8079 qtdeclarative5-dee-plugin,
8180 qtdeclarative5-gsettings1.0,
8281 qtdeclarative5-ubuntu-settings-components,
83− qtdeclarative5-ubuntu-thumbnailer0.1 | ubuntu-thumbnailer-impl,
84− ubuntu-thumbnailer-impl-0,
85− qtdeclarative5-ubuntu-ui-toolkit-plugin,
86− qtdeclarative5-unity-notifications-plugin | unity-notifications-impl,
8782 qtdeclarative5-xmllistmodel-plugin,
88− unity-application-impl-2,
8983 unity-launcher-impl-3,
90− unity-notifications-impl-3,
91− unity8-fake-env | unity-application-impl,
84+ unity8-common (= ${source:Version}),
9285 unity8-private (= ${binary:Version}),
9386 unity8-private | unity-launcher-impl,
94− unity-plugin-scopes | unity-scopes-impl,
95− unity-scopes-impl-0,
96−# for setcap:
97− libcap2-bin,
9887 ${misc:Depends},
9988 ${shlibs:Depends},
10089Recommends: unity-scope-click,
@@ -102,10 +91,30 @@ Recommends: unity-scope-click,
10291 unity-scope-scopes,
10392Breaks: indicator-network (<< 0.5.1),
10493 ubuntu-touch-session (<< 0.107),
105−Replaces: ubuntu-touch-session (<< 0.82~)
94+Replaces: ubuntu-touch-session (<< 0.82~),
10695Description: Unity 8 shell
10796 The Unity 8 shell is the primary user interface for Ubuntu devices.
10897
98+Package: unity8-common
99+Architecture: all
100+Depends: qtdeclarative5-ubuntu-thumbnailer0.1 | ubuntu-thumbnailer-impl,
101+ qtdeclarative5-ubuntu-ui-toolkit-plugin,
102+ qtdeclarative5-unity-notifications-plugin | unity-notifications-impl,
103+ ubuntu-thumbnailer-impl-0,
104+ unity-application-impl-2,
105+ unity-notifications-impl-3,
106+ unity-plugin-scopes | unity-scopes-impl,
107+ unity-scopes-impl-0,
108+ unity8-fake-env | unity-application-impl,
109+ ${misc:Depends},
110+Breaks: unity8 (<< 7.86),
111+Replaces: unity8 (<< 7.86),
112+Description: Unity 8 shell (common files)
113+ The Unity 8 shell is the primary user interface for Ubuntu devices.
114+ .
115+ This package contains the QML, graphics and locale files shared between
116+ unity8 and unity-scope-tool.
117+
109118Package: unity8-autopilot
110119Architecture: all
111120Depends: gir1.2-glib-2.0,
@@ -122,12 +131,12 @@ Depends: gir1.2-glib-2.0,
122131 python3-fixtures,
123132 python3-gi,
124133 ubuntu-ui-toolkit-autopilot,
125− unity8 (>= ${source:Version}),
126− unity8-fake-env (>= ${source:Version}),
134+ unity8 (= ${source:Version}),
135+ unity8-fake-env (= ${source:Version}),
127136 url-dispatcher-tools,
128137 ${misc:Depends},
129− ${python:Depends},
130138 ${python3:Depends},
139+ ${python:Depends},
131140 ${shlibs:Depends},
132141Description: Test package for Unity 8 shell
133142 Autopilot tests for the unity8 package
@@ -149,9 +158,9 @@ Architecture: any
149158Multi-Arch: same
150159Pre-Depends: ${misc:Pre-Depends},
151160Depends: gsettings-ubuntu-schemas,
161+ libunity-core-6.0-9,
152162 ${misc:Depends},
153163 ${shlibs:Depends},
154− libunity-core-6.0-9,
155164Provides: unity-launcher-impl,
156165 unity-launcher-impl-3,
157166Description: Unity 8 private libs
debian/unity-scope-tool.install−1
@@ -1,3 +1,2 @@
11usr/bin/unity-scope-tool
2−usr/share/applications/unity-scope-tool.desktop
32usr/share/unity8/ScopeTool.qml
debian/unity8-autopilot.install+1
@@ -1 +1,2 @@
11usr/lib/python*/*/unity*
2+usr/share/unity8/unlock-device
debian/unity8-common.installnew file+5
@@ -0,0 +1,5 @@
1+usr/share/locale
2+usr/share/unity8/Components
3+usr/share/unity8/Dash
4+usr/share/unity8/Notifications
5+usr/share/unity8/graphics
debian/unity8-fake-env.install+1
@@ -1,3 +1,4 @@
11usr/lib/*/unity8/qml/mocks
2+usr/lib/*/unity8/qml/nonmirplugins
23usr/lib/*/unity8/qml/scopefakes
34usr/share/unity8/mocks
debian/unity8-private.install+1−1
@@ -1,7 +1,7 @@
11usr/lib/*/unity8/qml/AccountsService
2+usr/lib/*/unity8/qml/DashViews
23usr/lib/*/unity8/qml/HudClient
34usr/lib/*/unity8/qml/LightDM
4−usr/lib/*/unity8/qml/DashViews
55usr/lib/*/unity8/qml/Powerd
66usr/lib/*/unity8/qml/SessionBroadcast
77usr/lib/*/unity8/qml/Ubuntu
debian/unity8.install+1−6
@@ -1,15 +1,10 @@
1+data/unity8.conf usr/share/upstart/sessions/
12usr/bin/unity8
23usr/share/applications/unity8.desktop
3−usr/share/locale
44usr/share/unity8/Bottombar
5−usr/share/unity8/Components
6−usr/share/unity8/Dash
75usr/share/unity8/Greeter
86usr/share/unity8/Hud
97usr/share/unity8/Launcher
10−usr/share/unity8/Notifications
118usr/share/unity8/Panel
129usr/share/unity8/Shell.qml
1310usr/share/unity8/Stages
14−usr/share/unity8/graphics
15−data/unity8.conf usr/share/upstart/sessions/
include/paths.h.in+10
@@ -57,6 +57,16 @@ inline QStringList overrideImportPaths() {
5757 return paths;
5858}
5959
60+inline QStringList nonMirImportPaths() {
61+ QStringList paths;
62+ if (isRunningInstalled()) {
63+ paths << QString("@CMAKE_INSTALL_PREFIX@/@SHELL_INSTALL_QML@/nonmirplugins");
64+ } else {
65+ paths << QString("@CMAKE_BINARY_DIR@/nonmirplugins");
66+ }
67+ return paths;
68+}
69+
6070inline QStringList fallbackImportPaths() {
6171 QStringList paths;
6272 if (isRunningInstalled()) {
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
plugins/Unity/Indicators/qml/MenuItemFactory.qml+1−1
@@ -273,7 +273,7 @@ Item {
273273 text: menuData && menuData.label || ""
274274 iconSource: menuData && menuData.icon || "image://theme/calendar"
275275 time: timeFormatter.timeString
276− eventColor: menu && menu.ext.hasOwnProperty("xCanonicalColor") ? menu.ext.xCanonicalColor : Qt.rgba(0.0, 0.0, 0.0, 0.0)
276+ eventColor: getExtendedProperty(extendedData, "xCanonicalColor", Qt.rgba(0.0, 0.0, 0.0, 0.0))
277277 enabled: menuData && menuData.sensitive || false
278278
279279 onMenuModelChanged: {
po/ast.po+5−22
@@ -8,15 +8,14 @@ msgstr ""
88"Project-Id-Version: unity8\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-03-01 16:51+0000\n"
11+"PO-Revision-Date: 2014-04-04 02:16+0000\n"
1212"Last-Translator: Xuacu Saturio <xuacusk8@gmail.com>\n"
1313"Language-Team: Asturian <ast@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=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2014-03-28 06:48+0000\n"
19−"X-Generator: Launchpad (build 16967)\n"
17+"X-Launchpad-Export-Date: 2014-04-08 07:13+0000\n"
18+"X-Generator: Launchpad (build 16976)\n"
2019
2120#: plugins/Unity/Launcher/launcheritem.cpp:43
2221#: plugins/Unity/Launcher/launcheritem.cpp:73
@@ -91,12 +90,12 @@ msgstr "Ver menos"
9190
9291#: qml/Dash/DashApps.qml:38
9392msgid "Recent"
94−msgstr ""
93+msgstr "Reciente"
9594
9695#: qml/Dash/PreviewListView.qml:52
9796#, qt-format
9897msgid "%1 Preview"
99−msgstr ""
98+msgstr "Vista previa de %1"
10099
101100#: qml/Dash/Previews/PreviewActionCombo.qml:34
102101msgid "More..."
@@ -167,19 +166,3 @@ msgstr "Guetar"
167166#, qt-format
168167msgid "Please enter %1"
169168msgstr "Introduz %1"
170−
171−#, qt-format
172−#~ msgid "(%1)"
173−#~ msgstr "(%1)"
174−
175−#~ msgid "Comments:"
176−#~ msgstr "Comentarios:"
177−
178−#~ msgid "Review"
179−#~ msgstr "Revisar"
180−
181−#, qt-format
182−#~ msgid "%1 review"
183−#~ msgid_plural "%1 reviews"
184−#~ msgstr[0] "%1 reseña"
185−#~ msgstr[1] "%1 reseñes"
po/bg.po+1−1
@@ -14,7 +14,7 @@ msgstr ""
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
17−"X-Launchpad-Export-Date: 2014-04-02 06:50+0000\n"
17+"X-Launchpad-Export-Date: 2014-04-03 06:06+0000\n"
1818"X-Generator: Launchpad (build 16967)\n"
1919
2020#: plugins/Unity/Launcher/launcheritem.cpp:43
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/fi.po+5−22
@@ -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-03-09 19:22+0000\n"
11+"PO-Revision-Date: 2014-04-05 09:57+0000\n"
1212"Last-Translator: Jiri Grönroos <Unknown>\n"
1313"Language-Team: Finnish <fi@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=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2014-03-28 06:48+0000\n"
19−"X-Generator: Launchpad (build 16967)\n"
17+"X-Launchpad-Export-Date: 2014-04-08 07:13+0000\n"
18+"X-Generator: Launchpad (build 16976)\n"
2019
2120#: plugins/Unity/Launcher/launcheritem.cpp:43
2221#: plugins/Unity/Launcher/launcheritem.cpp:73
@@ -89,12 +88,12 @@ msgstr "Näytä vähemmän"
8988
9089#: qml/Dash/DashApps.qml:38
9190msgid "Recent"
92−msgstr ""
91+msgstr "Viimeisimmät"
9392
9493#: qml/Dash/PreviewListView.qml:52
9594#, qt-format
9695msgid "%1 Preview"
97−msgstr ""
96+msgstr "%1 esikatselu"
9897
9998#: qml/Dash/Previews/PreviewActionCombo.qml:34
10099msgid "More..."
@@ -165,19 +164,3 @@ msgstr "Hae"
165164#, qt-format
166165msgid "Please enter %1"
167166msgstr "Kirjoita %1"
168−
169−#~ msgid "Comments:"
170−#~ msgstr "Kommentit:"
171−
172−#~ msgid "Review"
173−#~ msgstr "Arvostelu"
174−
175−#, qt-format
176−#~ msgid "%1 review"
177−#~ msgid_plural "%1 reviews"
178−#~ msgstr[0] "%1 arvostelu"
179−#~ msgstr[1] "%1 arvostelua"
180−
181−#, qt-format
182−#~ msgid "(%1)"
183−#~ msgstr "(%1)"
po/gl.po+5−22
@@ -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-03-08 06:58+0000\n"
11+"PO-Revision-Date: 2014-04-08 07:04+0000\n"
1212"Last-Translator: Marcos Lans <Unknown>\n"
1313"Language-Team: Galician <gl@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=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2014-03-28 06:48+0000\n"
19−"X-Generator: Launchpad (build 16967)\n"
17+"X-Launchpad-Export-Date: 2014-04-09 07:31+0000\n"
18+"X-Generator: Launchpad (build 16976)\n"
2019
2120#: plugins/Unity/Launcher/launcheritem.cpp:43
2221#: plugins/Unity/Launcher/launcheritem.cpp:73
@@ -89,12 +88,12 @@ msgstr "Ver menos"
8988
9089#: qml/Dash/DashApps.qml:38
9190msgid "Recent"
92−msgstr ""
91+msgstr "Recentes"
9392
9493#: qml/Dash/PreviewListView.qml:52
9594#, qt-format
9695msgid "%1 Preview"
97−msgstr ""
96+msgstr "%1 previsualización"
9897
9998#: qml/Dash/Previews/PreviewActionCombo.qml:34
10099msgid "More..."
@@ -165,19 +164,3 @@ msgstr "Buscar"
165164#, qt-format
166165msgid "Please enter %1"
167166msgstr "Introduza %1"
168−
169−#, qt-format
170−#~ msgid "(%1)"
171−#~ msgstr "(%1)"
172−
173−#~ msgid "Comments:"
174−#~ msgstr "Comentarios:"
175−
176−#~ msgid "Review"
177−#~ msgstr "Opinión"
178−
179−#, qt-format
180−#~ msgid "%1 review"
181−#~ msgid_plural "%1 reviews"
182−#~ msgstr[0] "%1 opinión"
183−#~ msgstr[1] "%1 opinións"
po/ru.po+12−29
@@ -8,16 +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-01-26 15:56+0000\n"
11+"PO-Revision-Date: 2014-04-03 13:07+0000\n"
1212"Last-Translator: Zonov Roman <Unknown>\n"
1313"Language-Team: Russian <ru@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=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
18−"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2014-03-28 06:49+0000\n"
20−"X-Generator: Launchpad (build 16967)\n"
17+"X-Launchpad-Export-Date: 2014-04-04 07:12+0000\n"
18+"X-Generator: Launchpad (build 16976)\n"
2119
2220#: plugins/Unity/Launcher/launcheritem.cpp:43
2321#: plugins/Unity/Launcher/launcheritem.cpp:73
@@ -78,6 +76,8 @@ msgid ""
7876"You have now mastered the edge gestures and can start using the "
7977"phone<br><br>Tap on the screen to start"
8078msgstr ""
79+"Вы научились пользоваться Ubuntu и можете начать использовать свой "
80+"телефон<br><br>Нажмите на экран"
8181
8282#: qml/Components/PageHeader.qml:365
8383msgid "Recent searches"
@@ -85,28 +85,28 @@ msgstr "Последние поисковые запросы"
8585
8686#: qml/Components/SeeMore.qml:35
8787msgid "See more"
88−msgstr ""
88+msgstr "Подробнее"
8989
9090#: qml/Components/SeeMore.qml:58
9191msgid "See less"
92−msgstr ""
92+msgstr "Вкратце"
9393
9494#: qml/Dash/DashApps.qml:38
9595msgid "Recent"
96−msgstr ""
96+msgstr "Недавние"
9797
9898#: qml/Dash/PreviewListView.qml:52
9999#, qt-format
100100msgid "%1 Preview"
101−msgstr ""
101+msgstr "Просмотр %1"
102102
103103#: qml/Dash/Previews/PreviewActionCombo.qml:34
104104msgid "More..."
105−msgstr ""
105+msgstr "Ещё..."
106106
107107#: qml/Dash/Previews/PreviewActionCombo.qml:34
108108msgid "Less..."
109−msgstr ""
109+msgstr "Меньше..."
110110
111111#: qml/Dash/Previews/PreviewRatingInput.qml:81
112112msgid "Rate this"
@@ -159,7 +159,7 @@ msgstr "Показать пароль"
159159
160160#: qml/Notifications/NotificationMenuItemFactory.qml:103
161161msgid "Please enter SIM PIN"
162−msgstr ""
162+msgstr "Введите PIN код"
163163
164164#: qml/Panel/SearchIndicator.qml:27
165165msgid "Search"
@@ -169,20 +169,3 @@ msgstr "Поиск"
169169#, qt-format
170170msgid "Please enter %1"
171171msgstr "Пожалуйста, введите %1"
172−
173−#, qt-format
174−#~ msgid "(%1)"
175−#~ msgstr "(%1)"
176−
177−#~ msgid "Comments:"
178−#~ msgstr "Комментарии:"
179−
180−#~ msgid "Review"
181−#~ msgstr "Отзыв"
182−
183−#, qt-format
184−#~ msgid "%1 review"
185−#~ msgid_plural "%1 reviews"
186−#~ msgstr[0] "%1 отзыв"
187−#~ msgstr[1] "%1 отзыва"
188−#~ msgstr[2] "%1 отзывов"
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 "تەكشۈر"
po/unity8.pot+7−3
@@ -8,7 +8,7 @@ msgid ""
88msgstr ""
99"Project-Id-Version: unity8\n"
1010"Report-Msgid-Bugs-To: \n"
11−"POT-Creation-Date: 2014-03-25 16:48+0100\n"
11+"POT-Creation-Date: 2014-04-10 11:13-0400\n"
1212"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
1313"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1414"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -84,7 +84,7 @@ msgstr ""
8484msgid "See less"
8585msgstr ""
8686
87−#: qml/Dash/DashApps.qml:38
87+#: qml/Dash/DashApps.qml:37
8888msgid "Recent"
8989msgstr ""
9090
@@ -113,6 +113,10 @@ msgstr ""
113113msgid "Send"
114114msgstr ""
115115
116+#: qml/Greeter/Greeter.qml:151
117+msgid "Swipe to unlock"
118+msgstr ""
119+
116120#: qml/Greeter/Lockscreen.qml:149
117121msgid "Emergency Call"
118122msgstr ""
@@ -158,7 +162,7 @@ msgstr ""
158162msgid "Search"
159163msgstr ""
160164
161−#: qml/Shell.qml:417
165+#: qml/Shell.qml:348
162166#, qt-format
163167msgid "Please enter %1"
164168msgstr ""
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+38−24
@@ -28,11 +28,6 @@ import "../Components"
2828Item {
2929 id: root
3030
31− /* If true, the number of elements displayed will be limited by collapsedRowCount.
32− If false, all elements will be displayed, effectively looking the same as a regular
33− ResponsiveGridView. */
34− property bool filter: true
35−
3631 /* Whether, when collapsed, a button should be displayed enabling the user to expand
3732 the grid to its full size. */
3833 readonly property bool expandable: model.count > rowsWhenCollapsed * iconTileGrid.columns
@@ -59,35 +54,54 @@ Item {
5954 property alias cellHeight: iconTileGrid.cellHeight
6055 property alias delegateCreationBegin: iconTileGrid.delegateCreationBegin
6156 property alias delegateCreationEnd: iconTileGrid.delegateCreationEnd
57+ readonly property alias originY: iconTileGrid.originY
6258 readonly property alias flicking: iconTileGrid.flicking
6359 readonly property alias moving: iconTileGrid.moving
6460 readonly property alias pressDelay: iconTileGrid.pressDelay
6561 property alias highlightIndex: iconTileGrid.highlightIndex
6662 readonly property alias currentItem: iconTileGrid.currentItem
63+ readonly property alias filtered: d.filter
64+
65+ QtObject {
66+ id: d
67+ // We do have filter and collapsed properties because we need to decouple
68+ // the real filtering with the animation since the closing animation
69+ // i.e. setFilter(false. true) we still need to not be filtering until
70+ // the animation finishes otherwise we hide the items when the animation
71+ // is still running
72+ property bool filter: true
73+ property bool collapsed: true
74+ }
6775
68− height: !filterAnimation.running ? childrenRect.height : height
76+ height: d.collapsed ? root.collapsedHeight : root.uncollapsedHeight
6977 clip: filterAnimation.running
7078
71− NumberAnimation {
72− property bool filterEndValue
73− id: filterAnimation
74− target: root
75− property: "height"
76− to: filterEndValue ? root.collapsedHeight : root.uncollapsedHeight
77− // Duration and easing here match the ListViewWithPageHeader::m_contentYAnimation
78− // otherwise since both animations can run at the same time you'll get
79− // some visual weirdness.
80− duration: 200
81− easing.type: Easing.InOutQuad
82− onStopped: {
83− root.filter = filterEndValue;
79+ Behavior on height {
80+ id: heightBehaviour
81+ enabled: false
82+ NumberAnimation {
83+ id: filterAnimation
84+ // Duration and easing here match the ListViewWithPageHeader::m_contentYAnimation
85+ // otherwise since both animations can run at the same time you'll get
86+ // some visual weirdness.
87+ duration: 200
88+ easing.type: Easing.InOutQuad
89+ onRunningChanged: {
90+ if (!running) {
91+ d.filter = d.collapsed;
92+ }
93+ heightBehaviour.enabled = false;
94+ }
8495 }
8596 }
8697
87− function startFilterAnimation(filter) {
88− filterAnimation.filterEndValue = filter
89− filterAnimation.start();
90− }
98+ function setFilter(filter, animate) {
99+ heightBehaviour.enabled = animate;
100+ d.collapsed = filter;
101+ if (!animate || !filter) {
102+ d.filter = filter;
103+ }
104+ }
91105
92106 ResponsiveGridView {
93107 id: iconTileGrid
@@ -104,7 +118,7 @@ Item {
104118
105119 model: LimitProxyModel {
106120 model: root.model
107− limit: (filter && !filterAnimation.running) ? rowsWhenCollapsed * iconTileGrid.columns : -1
121+ limit: d.filter ? rowsWhenCollapsed * iconTileGrid.columns : -1
108122 }
109123 }
110124}
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/Components/Stage.qmldeleted−571
@@ -1,571 +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−import Unity.Application 0.1
19−import Ubuntu.Components 0.1
20−import Ubuntu.Gestures 0.1
21−
22−/*
23− Responsible for application switching.
24−
25− Drag from the right edge to switch to the next application.
26− While being dragged, this component will show an animation for the user to
27− visualize that he's changing the stacking of applications (next/new app comes from
28− right edge and current/old app fades out).
29− */
30−Showable {
31− id: stage
32−
33− width: units.gu(40)
34− height: units.gu(71)
35−
36− property int type: ApplicationInfo.MainStage
37− property var applicationManager
38− readonly property var applications:
39− type == ApplicationInfo.MainStage ? applicationManager.mainStageApplications
40− : applicationManager.sideStageApplications
41− property bool fullyShown: false
42− property bool fullyHidden: true
43− readonly property var focusedApplication:
44− type == ApplicationInfo.MainStage ? applicationManager.mainStageFocusedApplication
45− : applicationManager.sideStageFocusedApplication
46− property bool shouldUseScreenshots: true
47−
48− /* Will be true whenever application screenshots are being shown.
49− i.e. during application window transitions */
50− readonly property bool usingScreenshots: {
51− if (newApplicationScreenshot.application != null && oldApplicationScreenshot.application != null) {
52− return newApplicationScreenshot.ready && oldApplicationScreenshot.ready;
53− } else {
54− return newApplicationScreenshot.ready || oldApplicationScreenshot.ready;
55− }
56− }
57−
58− property bool rightEdgeEnabled: true
59− property bool switchingFromFullscreenToFullscreen: false
60−
61− property alias rightEdgeDraggingAreaWidth: rightEdgeDraggingArea.width
62−
63− // Y coordinate for non-fullscreen applications
64− property int normalApplicationY: 0
65−
66− function activateApplication(desktopFile, addDelay) {
67− var application = applicationManager.getApplicationFromDesktopFile(desktopFile, stage.type);
68− if (application == null) {
69− return;
70− } else if (application == stage.focusedApplication) {
71− stage.show();
72− return;
73− }
74−
75− showStartingApplicationAnimation.prepare(application);
76− if (!stage.fullyShown) {
77− // if launching this app reveals the stage, make new application screenshot visible
78− newApplicationScreenshot.visible = true;
79− stage.show();
80− } else {
81− showStartingApplicationAnimation.start();
82− }
83−
84− if (addDelay) {
85− stage.__startingApplicationDesktopFile = desktopFile;
86− }
87− }
88−
89− Connections {
90− target: applicationManager
91− onFocusRequested: activateApplication(appId)
92− }
93−
94− /* Keep a reference to the focused application so that we can safely
95− unfocus it when the stage is not fully shown and refocus it when the stage
96− is fully shown again.
97− */
98− property var focusedApplicationWhenUsingScreenshots
99− onShouldUseScreenshotsChanged: {
100− if (showStartingApplicationAnimation.running || switchToApplicationAnimation.running) {
101− return;
102− }
103−
104− if (shouldUseScreenshots) {
105− stage.__focusApplicationUsingScreenshots(stage.focusedApplication);
106− } else {
107− if (stage.focusedApplicationWhenUsingScreenshots) {
108− stage.__focusActualApplication(stage.focusedApplicationWhenUsingScreenshots);
109− }
110− }
111− }
112−
113−
114− property Item newApplicationScreenshot: ApplicationScreenshot {
115− parent: stage
116− width: stage.width
117− height: stage.height - y
118− }
119− property Item oldApplicationScreenshot: ApplicationScreenshot {
120− parent: stage
121− width: stage.width
122− height: stage.height - y
123− }
124−
125− function __getApplicationIndex(application) {
126− if (!application)
127− return -1
128− for (var i = 0; i < stage.applications.count; i++ ) {
129− if (stage.applications.get(i).desktopFile == application.desktopFile) {
130− return i
131− }
132− }
133− return -1
134− }
135−
136−
137− property string __startingApplicationDesktopFile
138−
139− Timer {
140− id: delayedHideScreenshots
141− interval: 500 // on Galaxy Nexus, Telephony app is slowest application to draw a frame so needs this delay
142− onTriggered: {
143− __hideScreenshots();
144− }
145− }
146−
147− Timer {
148− id: hideScreenshotsAfterBriefDelay
149− // Since Mir & Qt have independent event loops, Mir often applies a focus request a frame later than when Qt
150− // asks for it. To work around, delay removing the screenshots a touch so Mir has definitely shown the app.
151− interval: 10
152− onTriggered: stage.__hideScreenshots();
153− }
154−
155− function __hideScreenshots() {
156− newApplicationScreenshot.clearApplication();
157− oldApplicationScreenshot.clearApplication();
158− newApplicationScreenshot.visible = false;
159− oldApplicationScreenshot.visible = false;
160− }
161−
162− function __focusApplication(application) {
163− if (!application) return
164− if (shouldUseScreenshots) {
165− stage.__focusApplicationUsingScreenshots(application);
166− } else {
167− stage.__focusActualApplication(application);
168− }
169− }
170−
171−
172− function __focusActualApplication(application) {
173− if (application == null) {
174− return;
175− }
176−
177− if (stage.focusedApplication == application) {
178− /* FIXME: this case is reached when showing the sidestage, we
179− need to refocus the side stage app in order to show it.
180− FIXME: this case is reached when doing a right edge swipe
181− while in the dash to bring back the stage.
182− FIXME: this case is reached when showStartingApplicationAnimation
183− finishes after the newly started application has been focused
184− automatically by the ApplicationManager.
185− */
186− if (stage.type == ApplicationInfo.SideStage) {
187− applicationManager.focusApplication(application);
188− }
189− if (!delayedHideScreenshots.running) {
190− hideScreenshotsAfterBriefDelay.start();
191− }
192− } else {
193− /* FIXME: calling ApplicationManager::focusApplication does not focus
194− the application immediately nor show the application's surface
195− immediately. Delay hiding the screenshots until it has done so.
196− */
197− delayedHideScreenshots.stop();
198− applicationManager.focusApplication(application);
199− }
200− stage.focusedApplicationWhenUsingScreenshots = null;
201− }
202−
203− function __focusApplicationUsingScreenshots(application) {
204− delayedHideScreenshots.stop();
205− if (application == null) {
206− return;
207− }
208−
209− if (newApplicationScreenshot.application != null) {
210− stage.focusedApplicationWhenUsingScreenshots = application;
211− return;
212− }
213−
214− newApplicationScreenshot.setApplication(application);
215− newApplicationScreenshot.scheduleUpdate();
216− newApplicationScreenshot.opacity = 1.0;
217− newApplicationScreenshot.x = 0.0;
218− newApplicationScreenshot.y = __getApplicationY(application);
219− newApplicationScreenshot.scale = 1.0;
220− newApplicationScreenshot.visible = true;
221− stage.focusedApplicationWhenUsingScreenshots = application;
222− stage.switchingFromFullscreenToFullscreen = false;
223− }
224−
225− function __getApplicationY(application) {
226− return Qt.binding( function(){
227− return (application && application.fullscreen
228− && application.stage !== ApplicationInfo.SideStage)
229− ? 0 : normalApplicationY;
230− } )
231− }
232−
233− /* FIXME: should hide the screenshots when the focused application's surface
234− is visible. There is no API in QtUbuntu to support that yet.
235− */
236− Connections {
237− target: stage
238− onFocusedApplicationChanged: {
239− if (stage.focusedApplication != null) {
240− /* Move application to the beginning of the list of running applications */
241− var index = stage.__getApplicationIndex(stage.focusedApplication);
242− applicationManager.moveRunningApplicationStackPosition(index, 0, stage.type);
243−
244− var startingApplication = applicationManager.getApplicationFromDesktopFile(
245− stage.__startingApplicationDesktopFile, stage.type);
246−
247− if (stage.focusedApplication == startingApplication) {
248− stage.__startingApplicationDesktopFile = "";
249−
250− /* Screenshots might still be in use at this point and their
251− use can be stopped before delayedHideScreenshots is complete.
252− Prepare for that case by setting appropriately
253− stage.focusedApplicationWhenUsingScreenshots.
254− */
255− stage.focusedApplicationWhenUsingScreenshots = stage.focusedApplication;
256−
257− /* FIXME: should hide the screenshots when the focused application's window
258− is visible. There is no API in qtubuntu to support that yet. Therefore
259− we delay their hiding to avoid a state where neither the screenshot nor
260− the application's window is visible.
261− */
262− delayedHideScreenshots.start();
263− } else {
264− stage.focusedApplicationWhenUsingScreenshots = null;
265− hideScreenshotsAfterBriefDelay.start();
266− }
267− }
268− }
269− }
270−
271− Connections {
272− target: stage.applications
273− onCountChanged: { __discardObsoleteScreenshots(); }
274− ignoreUnknownSignals: true
275− }
276− onFullyHiddenChanged: { __discardObsoleteScreenshots(); }
277− function __discardObsoleteScreenshots() {
278− if (stage.fullyHidden && stage.applications && stage.applications.count == 0) {
279− stage.focusedApplicationWhenUsingScreenshots = undefined;
280− stage.__hideScreenshots();
281− }
282− }
283−
284−
285− SequentialAnimation {
286− id: showStartingApplicationAnimation
287−
288− function prepare(newApplication) {
289− var oldApplication = stage.focusedApplication;
290− newApplicationScreenshot.setApplication(newApplication);
291− newApplicationScreenshot.updateFromCache();
292− oldApplicationScreenshot.setApplication(oldApplication);
293− oldApplicationScreenshot.scheduleUpdate();
294− newApplicationScreenshot.withBackground = true; // FIXME: should be withBackground = newApplication.state == Application.Starting
295− newApplicationScreenshot.y = __getApplicationY(newApplication);
296− oldApplicationScreenshot.y = __getApplicationY(oldApplication);
297− stage.focusedApplicationWhenUsingScreenshots = newApplication;
298− stage.switchingFromFullscreenToFullscreen = false;
299− }
300−
301− PropertyAction {target: newApplicationScreenshot; property: "visible"; value: false}
302− PropertyAction {target: oldApplicationScreenshot; property: "visible"; value: false}
303− PropertyAction {target: newApplicationScreenshot; property: "z"; value: 0}
304− PropertyAction {target: oldApplicationScreenshot; property: "z"; value: 1}
305− PropertyAction {target: newApplicationScreenshot; property: "scale"; value: 0.8}
306− PropertyAction {target: oldApplicationScreenshot; property: "scale"; value: 1.0}
307− PropertyAction {target: newApplicationScreenshot; property: "opacity"; value: 0.0}
308− PropertyAction {target: oldApplicationScreenshot; property: "opacity"; value: 1.0}
309− PropertyAction {target: newApplicationScreenshot; property: "x"; value: 0.0}
310− PropertyAction {target: oldApplicationScreenshot; property: "x"; value: 0.0}
311−
312− ParallelAnimation {
313− PropertyAction {target: newApplicationScreenshot; property: "visible"; value: true}
314− NumberAnimation {
315− target: newApplicationScreenshot
316− property: "scale"
317− duration: 500
318− easing.type: Easing.OutQuad
319− to: 1.0
320− }
321− NumberAnimation {
322− target: newApplicationScreenshot
323− property: "opacity"
324− duration: 500
325− easing.type: Easing.OutQuad
326− to: 1.0
327− }
328−
329− PropertyAction {target: oldApplicationScreenshot; property: "visible"; value: true}
330−
331− NumberAnimation {
332− target: oldApplicationScreenshot
333− property: "x"
334− duration: 400
335− easing.type: Easing.OutQuad
336− to: stage.width
337− }
338− }
339−
340− ScriptAction {
341− script: {
342− // FIXME: only here to support case where application does
343− // not exist yet but is about to
344− if (stage.focusedApplicationWhenUsingScreenshots) {
345− stage.__focusApplication(stage.focusedApplicationWhenUsingScreenshots);
346− }
347− }
348− }
349− }
350−
351−
352− SequentialAnimation {
353− id: switchToApplicationAnimation
354−
355− function prepare(newApplication) {
356− var oldApplication = stage.focusedApplication;
357− newApplicationScreenshot.setApplication(newApplication);
358− newApplicationScreenshot.updateFromCache();
359− oldApplicationScreenshot.setApplication(oldApplication);
360− oldApplicationScreenshot.scheduleUpdate();
361− newApplicationScreenshot.withBackground = false;
362− newApplicationScreenshot.y = __getApplicationY(newApplication);
363− oldApplicationScreenshot.y = __getApplicationY(oldApplication);
364− stage.focusedApplicationWhenUsingScreenshots = newApplication;
365− stage.switchingFromFullscreenToFullscreen = newApplication && newApplication.fullscreen;
366− }
367−
368− PropertyAction {target: newApplicationScreenshot; property: "visible"; value: false}
369− PropertyAction {target: oldApplicationScreenshot; property: "visible"; value: false}
370− PropertyAction {target: newApplicationScreenshot; property: "z"; value: 1}
371− PropertyAction {target: oldApplicationScreenshot; property: "z"; value: 0}
372− PropertyAction {target: newApplicationScreenshot; property: "scale"; value: 1.0}
373− PropertyAction {target: oldApplicationScreenshot; property: "scale"; value: 1.0}
374− PropertyAction {target: newApplicationScreenshot; property: "opacity"; value: 1.0}
375− PropertyAction {target: oldApplicationScreenshot; property: "opacity"; value: 1.0}
376− PropertyAction {target: newApplicationScreenshot; property: "x"; value: stage.width}
377− PropertyAction {target: oldApplicationScreenshot; property: "x"; value: 0.0}
378− PropertyAction {target: newApplicationScreenshot; property: "visible"; value: true}
379− PropertyAction {target: oldApplicationScreenshot; property: "visible"; value: true}
380−
381− ParallelAnimation {
382− NumberAnimation {
383− target: oldApplicationScreenshot
384− property: "scale"
385− duration: 500
386− easing.type: Easing.OutQuad
387− to: 0.8
388− }
389− NumberAnimation {
390− target: oldApplicationScreenshot
391− property: "opacity"
392− duration: 500
393− easing.type: Easing.OutQuad
394− to: 0.0
395− }
396−
397− NumberAnimation {
398− target: newApplicationScreenshot
399− property: "x"
400− duration: 500
401− easing.type: Easing.OutQuad
402− to: 0
403− }
404− }
405−
406− ScriptAction {
407− script: {
408− // move previously focused application to the end of the list of running applications
409− var oldApplication = oldApplicationScreenshot.application;
410− if (oldApplication != null) {
411− var oldApplicationIndex = stage.__getApplicationIndex(oldApplicationScreenshot.application);
412− applicationManager.moveRunningApplicationStackPosition(
413− oldApplicationIndex, stage.applications.count - 1, stage.type);
414− }
415−
416− stage.__focusApplication(stage.focusedApplicationWhenUsingScreenshots);
417− }
418− }
419− }
420−
421−
422− function __dragValueToProgress(dragValue, startValue, applicationsCount) {
423− if (applicationsCount == 0) {
424− return 0;
425− } else if (applicationsCount == 1) {
426− // prevent the current app from moving more than 1/4 of the screen
427− var elasticValue = __elastic(dragValue, startValue/4);
428− return elasticValue / startValue;
429− } else {
430− return dragValue / startValue;
431− }
432− }
433−
434− function __elastic(x, limit) {
435− return limit * ( 1 - Math.pow( ( limit - 1 ) / limit, x ) )
436− }
437−
438− property real __startValue: stage.width
439−
440− AnimationControllerWithSignals {
441− id: switchToApplicationAnimationController
442−
443− onAnimationCompletedAtBeginning: {
444− animation = null;
445− stage.__focusApplication(oldApplicationScreenshot.application);
446− }
447− }
448−
449− EdgeDragEvaluator {
450− id: dragEvaluator
451− trackedPosition: rightEdgeDraggingArea.touchX
452− direction: rightEdgeDraggingArea.direction
453− maxDragDistance: stage.width
454− }
455−
456− EdgeDragArea {
457− id: rightEdgeDraggingArea
458−
459− enabled: {
460− if (status == DirectionalDragArea.WaitingForTouch) {
461− return switchToApplicationAnimationController.completed
462− && stage.applications.count > 0 && stage.rightEdgeEnabled
463− && !delayedHideScreenshots.running
464− } else {
465− return true
466− }
467− }
468− width: units.gu(2)
469− anchors.top: parent.top
470− anchors.bottom: parent.bottom
471− anchors.right: parent.right
472−
473− direction: Direction.Leftwards
474−
475− onDistanceChanged: {
476− if (status !== DirectionalDragArea.Recognized)
477− return
478−
479− var targetProgress = __dragValueToProgress(-distance, __startValue,
480− stage.applications.count)
481− var delta = targetProgress - switchToApplicationAnimationController.progress
482−
483− // When the gesture finally gets recognized, the finger will likely be
484− // reasonably far from the edge. If we made the progress immediately
485− // follow the finger position it would be visually unpleasant as there
486− // would be a jump to meet the finger's current position.
487− //
488− // The trick is not to go all the way (1.0) as it would cause the
489− // aforementioned jump.
490− switchToApplicationAnimationController.progress += 0.4 * delta
491− }
492−
493− property int lastStatus: -1
494−
495− onStatusChanged: {
496− if (status == DirectionalDragArea.Undecided) {
497− onUndecided();
498− } else if (status == DirectionalDragArea.WaitingForTouch) {
499− if (lastStatus == DirectionalDragArea.Undecided) {
500− // got rejected
501− switchToApplicationAnimationController.completeToBeginningWithSignal();
502− } else {
503− onGestureEnded();
504− }
505− }
506− lastStatus = status;
507− }
508−
509− function onUndecided() {
510− dragEvaluator.reset();
511−
512− var nextApplication = null;
513− if (stage.applications.count > 1) {
514− nextApplication = stage.applications.get(1);
515− }
516− switchToApplicationAnimationController.progress = 0
517− switchToApplicationAnimation.prepare(nextApplication);
518− switchToApplicationAnimationController.animation = switchToApplicationAnimation;
519−
520− }
521−
522− function onGestureEnded() {
523− if (stage.applications.count > 1 && dragEvaluator.shouldAutoComplete()) {
524− switchToApplicationAnimationController.completeToEndWithSignal();
525− } else {
526− switchToApplicationAnimationController.completeToBeginningWithSignal();
527− }
528− }
529− }
530−
531−
532− // FIXME: very bad hack to detect when an application was closed or crashed
533− property var __previouslyFocusedApplication
534− Connections {
535− target: stage
536− onFocusedApplicationChanged: {
537− if (stage.focusedApplication == null
538− && __getApplicationIndex(stage.__previouslyFocusedApplication) == -1) {
539− stage.__onFocusedApplicationClosed();
540− }
541− stage.__previouslyFocusedApplication = stage.focusedApplication;
542− }
543− }
544−
545− function __onFocusedApplicationClosed() {
546− if (!stage.shown) {
547− return;
548− }
549−
550− if (stage.applications.count > 0) {
551− // Do not change the focused application from within onFocusedApplicationChanged.
552− // This causes binding loop warnings besides being a bad practice in general
553− // (setting a property from within its "changed" signal handling)
554− // Hence the timer.
555− focusTopmostAppTimer.start();
556− } else {
557− stage.hide();
558− }
559− }
560−
561− Timer {
562− id: focusTopmostAppTimer
563− interval: 1; repeat: false
564− onTriggered: {
565− if (stage.applications.count > 0) {
566− stage.__focusApplication(stage.applications.get(0));
567− }
568− }
569− }
570−
571−}
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+8−14
@@ -21,18 +21,17 @@ DashRenderer {
2121 id: genericFilterGrid
2222
2323 expandable: filterGrid.expandable
24− collapsedRowCount: Math.min(2, cardTool && cardTool.template && cardTool.template["collapsed-rows"] || 2)
2524 collapsedHeight: filterGrid.collapsedHeight
26− columns: filterGrid.columns
27− rows: filter ? collapsedRowCount : uncollapsedRowCount
2825 margins: filterGrid.margins
2926 uncollapsedHeight: filterGrid.uncollapsedHeight
27+ originY: filterGrid.originY
3028 verticalSpacing: units.gu(1)
3129 currentItem: filterGrid.currentItem
3230 height: filterGrid.height
31+ filtered: filterGrid.filtered
3332
34− function startFilterAnimation(filter) {
35− filterGrid.startFilterAnimation(filter)
33+ function setFilter(filter, animate) {
34+ filterGrid.setFilter(filter, animate)
3635 }
3736
3837 FilterGrid {
@@ -43,18 +42,18 @@ DashRenderer {
4342 delegateHeight: cardTool.cardHeight
4443 verticalSpacing: genericFilterGrid.verticalSpacing
4544 model: genericFilterGrid.model
46− filter: genericFilterGrid.filter
47− collapsedRowCount: genericFilterGrid.collapsedRowCount
45+ collapsedRowCount: Math.min(2, cardTool && cardTool.template && cardTool.template["collapsed-rows"] || 2)
4846 delegateCreationBegin: genericFilterGrid.delegateCreationBegin
4947 delegateCreationEnd: genericFilterGrid.delegateCreationEnd
50− delegate: Item {
48+ delegate: Loader {
49+ asynchronous: true
5150 width: filterGrid.cellWidth
5251 height: filterGrid.cellHeight
5352 Card {
5453 id: card
5554 width: cardTool.cardWidth
5655 height: cardTool.cardHeight
57− headerHeight: cardTool.headerHeight
56+ fixedHeaderHeight: cardTool.headerHeight
5857 anchors.horizontalCenter: parent.horizontalCenter
5958 objectName: "delegate" + index
6059 cardData: model
@@ -67,10 +66,5 @@ DashRenderer {
6766 onPressAndHold: genericFilterGrid.pressAndHold(index, card.y)
6867 }
6968 }
70−
71− onFilterChanged: {
72− genericFilterGrid.filter = filter
73− filter = Qt.binding(function() { return genericFilterGrid.filter })
74− }
7569 }
7670}
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+5−10
@@ -27,13 +27,9 @@ Item {
2727 readonly property alias currentIndex: dashContentList.currentIndex
2828 property alias previewOpen: previewListView.open
2929
30− property ScopeDelegateMapper scopeMapper : ScopeDelegateMapper {}
3130 property ListModel searchHistory
3231
33− signal movementStarted()
34− signal movementEnded()
3532 signal scopeLoaded(string scopeId)
36− signal positionedAtBeginning()
3733 signal gotoScope(string scopeId)
3834 signal openScope(var scope)
3935
@@ -71,7 +67,7 @@ Item {
7167 dashContentList.currentIndex = index
7268
7369 if (reset) {
74− dashContent.positionedAtBeginning()
70+ dashContentList.currentItem.item.positionAtBeginning()
7571 }
7672 }
7773
@@ -114,8 +110,7 @@ Item {
114110 highlightRangeMode: ListView.StrictlyEnforceRange
115111 // TODO Investigate if we can switch to a smaller cache buffer when/if UbuntuShape gets more performant
116112 cacheBuffer: 1073741823
117− onMovementStarted: dashContent.movementStarted()
118− onMovementEnded: dashContent.movementEnded()
113+ onMovementStarted: currentItem.item.showHeader();
119114 clip: parent.x != 0
120115
121116 // If the number of items is less than the current index, then need to reset to another item.
@@ -135,7 +130,9 @@ Item {
135130 width: ListView.view.width
136131 height: ListView.view.height
137132 asynchronous: true
138− 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"
139136 objectName: scope.id + " loader"
140137
141138 readonly property bool moving: item ? item.moving : false
@@ -154,8 +151,6 @@ Item {
154151 item.scope = Qt.binding(function() { return scope })
155152 item.isCurrent = Qt.binding(function() { return visible && ListView.isCurrentItem })
156153 item.tabBarHeight = dashPageHeader.implicitHeight;
157− dashContentList.movementStarted.connect(item.movementStarted)
158− dashContent.positionedAtBeginning.connect(item.positionedAtBeginning)
159154 dashContent.scopeLoaded(item.scope.id)
160155 }
161156 Connections {
qml/Dash/DashRenderer.qml+5−9
@@ -20,17 +20,11 @@ Item {
2020 // Can the item be expanded?
2121 property bool expandable: false
2222
23− // In case it can be expanded, should we filter it
24− property bool filter: true
25−
26− property int collapsedRowCount: 1
23+ // In case it can be expanded, is it filtered
24+ property bool filtered: true
2725
2826 property int collapsedHeight: height
2927
30− property int columns: 1
31−
32− property int rows: 1
33−
3428 property int margins: 0
3529
3630 property int uncollapsedHeight: height
@@ -39,6 +33,8 @@ Item {
3933
4034 property var delegateCreationEnd: undefined
4135
36+ property real originY: 0
37+
4238 property real verticalSpacing: 0
4339
4440 // The current item of the renderer
@@ -62,6 +58,6 @@ Item {
6258 /// @param itemY is y of the held delegate
6359 signal pressAndHold(int index, real itemY)
6460
65− function startFilterAnimation(filter) {
61+ function setFilter(filter, animate) {
6662 }
6763}
qml/Dash/GenericScopeView.qml+29−30
@@ -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"
@@ -121,7 +122,7 @@ FocusScope {
121122 showDivider: false
122123
123124 readonly property bool expandable: rendererLoader.item ? rendererLoader.item.expandable : false
124− readonly property bool filtered: rendererLoader.item ? rendererLoader.item.filter : true
125+ readonly property bool filtered: rendererLoader.item ? rendererLoader.item.filtered : true
125126 readonly property string category: categoryId
126127 readonly property var item: rendererLoader.item
127128
@@ -167,9 +168,7 @@ FocusScope {
167168 item.objectName = Qt.binding(function() { return categoryId })
168169 if (item.expandable) {
169170 var shouldFilter = categoryId != categoryView.expandedCategoryId;
170− if (shouldFilter != item.filter) {
171− item.filter = shouldFilter;
172− }
171+ item.setFilter(shouldFilter, false /*animate*/);
173172 }
174173 updateDelegateCreationRange();
175174 item.cardTool = cardTool;
@@ -218,11 +217,8 @@ FocusScope {
218217 var shrinkingVisible = shouldFilter && y + item.collapsedHeight < categoryView.height;
219218 var growingVisible = !shouldFilter && y + height < categoryView.height;
220219 if (!previewListView.open || !shouldFilter) {
221− if (shrinkingVisible || growingVisible) {
222− item.startFilterAnimation(shouldFilter)
223− } else {
224− item.filter = shouldFilter;
225− }
220+ var animate = shrinkingVisible || growingVisible;
221+ item.setFilter(shouldFilter, animate)
226222 if (!shouldFilter && !previewListView.open) {
227223 categoryView.maximizeVisibleArea(index, item.uncollapsedHeight);
228224 }
@@ -237,26 +233,29 @@ FocusScope {
237233 }
238234
239235 function updateDelegateCreationRange() {
240− // Do not update the range if we are overshooting up or down, since we'll come back
241− // to the stable position and delete/create items without any reason
242− if (categoryView.contentY < categoryView.originY) {
243− return;
244− } else if (categoryView.contentY + categoryView.height > categoryView.contentHeight) {
245− return;
236+ if (categoryView.moving) {
237+ // Do not update the range if we are overshooting up or down, since we'll come back
238+ // to the stable position and delete/create items without any reason
239+ if (categoryView.contentY < categoryView.originY) {
240+ return;
241+ } else if (categoryView.contentHeight - categoryView.originY > categoryView.height &&
242+ categoryView.contentY + categoryView.height > categoryView.contentHeight) {
243+ return;
244+ }
246245 }
247246
248247 if (item && item.hasOwnProperty("delegateCreationBegin")) {
249248 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
249+ // Not visible (item at top of the list viewport)
250+ item.delegateCreationBegin = item.originY + baseItem.height
251+ item.delegateCreationEnd = item.originY + baseItem.height
253252 } else if (baseItem.y >= categoryView.height) {
254− // Not visible (item at bottom of the list)
255− item.delegateCreationBegin = 0
256− item.delegateCreationEnd = 0
253+ // Not visible (item at bottom of the list viewport)
254+ item.delegateCreationBegin = item.originY
255+ item.delegateCreationEnd = item.originY
257256 } else {
258− item.delegateCreationBegin = Math.max(-baseItem.y, 0)
259− item.delegateCreationEnd = Math.min(categoryView.height + item.delegateCreationBegin, baseItem.height)
257+ item.delegateCreationBegin = item.originY + Math.max(-baseItem.y, 0)
258+ item.delegateCreationEnd = item.originY + Math.min(categoryView.height + item.delegateCreationBegin, baseItem.height)
260259 }
261260 }
262261 }
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+6−4
@@ -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 }
@@ -153,8 +155,8 @@ PreviewWidget {
153155 objectName: "submitButton"
154156
155157 readonly property bool readyToSubmit: {
156− if (reviewTextArea.text === "" ||
157− (widgetData["visible"] === "both" && rating.value < 0)) return false;
158+ if ((widgetData["required"] !== "review" && rating.value < 0) ||
159+ (widgetData["required"] !== "rating" && reviewTextArea.text === "")) return false;
158160 else return true;
159161 }
160162
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/Greeter/Clock.qml+3−6
@@ -24,14 +24,11 @@ Item {
2424 implicitWidth: childrenRect.width
2525 implicitHeight: childrenRect.height
2626
27− // Allows to set the current Date. Will be overwritten if active
27+ // Allows to set the current Date. Will be overwritten if visible
2828 property date currentDate
2929
30− // If active, time will be updated through the indicators service
31− property bool active: clock.enabled && clock.visible
32−
3330 Component.onCompleted: {
34− if (active) {
31+ if (visible) {
3532 currentDate = new Date()
3633 }
3734 }
@@ -42,7 +39,7 @@ Item {
4239
4340 busName: "com.canonical.indicator.datetime"
4441 actionsObjectPath: "/com/canonical/indicator/datetime"
45− menuObjectPath: clock.active ? "/com/canonical/indicator/datetime/phone" : ""
42+ menuObjectPath: clock.visible ? "/com/canonical/indicator/datetime/phone" : ""
4643
4744 Indicators.RootActionState {
4845 menu: timeModel.model
qml/Greeter/Greeter.qml+115−42
@@ -28,10 +28,10 @@ Showable {
2828 property url defaultBackground
2929
3030 // 1 when fully shown and 0 when fully hidden
31− property real showProgress: MathUtils.clamp((width + x) / width, 0, 1)
31+ property real showProgress: MathUtils.clamp((width - Math.abs(x)) / width, 0, 1)
3232
3333 showAnimation: StandardAnimation { property: "x"; to: 0 }
34− hideAnimation: StandardAnimation { property: "x"; to: -width }
34+ hideAnimation: __leftHideAnimation
3535
3636 property alias dragHandleWidth: dragHandle.width
3737 property alias model: greeterContentLoader.model
@@ -40,19 +40,81 @@ Showable {
4040 readonly property bool narrowMode: !multiUser && height > width
4141 readonly property bool multiUser: LightDM.Users.count > 1
4242
43− readonly property bool leftTeaserPressed: greeterContentLoader.status == Loader.Ready &&
44− greeterContentLoader.item.leftTeaserPressed
45− readonly property bool rightTeaserPressed: greeterContentLoader.status == Loader.Ready &&
46− greeterContentLoader.item.rightTeaserPressed
47−
4843 readonly property int currentIndex: greeterContentLoader.currentIndex
4944
45+ property var __leftHideAnimation: StandardAnimation { property: "x"; to: -width }
46+ property var __rightHideAnimation: StandardAnimation { property: "x"; to: width }
47+
5048 signal selected(int uid)
5149 signal unlocked(int uid)
50+ signal tease()
51+
52+ function hideRight() {
53+ if (shown) {
54+ hideAnimation = __rightHideAnimation
55+ hide()
56+ }
57+ }
58+
59+ onRequiredChanged: {
60+ // Reset hide animation to default once we're finished with it
61+ if (!required) {
62+ // Put back on left for reliable show direction and so that
63+ // if normal hide() is called, we don't animate from right.
64+ x = -width
65+ hideAnimation = __leftHideAnimation
66+ }
67+ }
68+
69+ // Bi-directional revealer
70+ DraggingArea {
71+ id: dragHandle
72+ anchors.fill: parent
73+ enabled: greeter.narrowMode || !greeter.locked
74+ orientation: Qt.Horizontal
75+ propagateComposedEvents: true
76+
77+ Component.onCompleted: {
78+ // set evaluators to baseline of dragValue == 0
79+ leftEvaluator.reset()
80+ rightEvaluator.reset()
81+ }
82+
83+ function maybeTease() {
84+ if (!greeter.locked || greeter.narrowMode)
85+ greeter.tease();
86+ }
87+
88+ onClicked: maybeTease()
89+ onDragStart: maybeTease()
90+ onPressAndHold: {} // eat event, but no need to tease, as drag will cover it
91+
92+ onDragEnd: {
93+ if (rightEvaluator.shouldAutoComplete())
94+ greeter.hideRight()
95+ else if (leftEvaluator.shouldAutoComplete())
96+ greeter.hide();
97+ else
98+ greeter.show(); // undo drag
99+ }
100+
101+ onDragValueChanged: {
102+ // dragValue is kept as a "step" value since we do this x adjusting on the fly
103+ greeter.x += dragValue
104+ }
105+
106+ EdgeDragEvaluator {
107+ id: rightEvaluator
108+ trackedPosition: dragHandle.dragValue + greeter.x
109+ maxDragDistance: parent.width
110+ direction: Direction.Rightwards
111+ }
52112
53− onRightTeaserPressedChanged: {
54− if (rightTeaserPressed && (!locked || narrowMode) && x == 0) {
55− teasingTimer.start();
113+ EdgeDragEvaluator {
114+ id: leftEvaluator
115+ trackedPosition: dragHandle.dragValue + greeter.x
116+ maxDragDistance: parent.width
117+ direction: Direction.Leftwards
56118 }
57119 }
58120
@@ -82,45 +144,56 @@ Showable {
82144 }
83145 }
84146
85− Timer {
86− id: teasingTimer
87− interval: 200
88− }
147+ onTease: showLabelAnimation.start()
89148
90− states: [
91− State {
92− name: "teasing"
93− when: teasingTimer.running
94− PropertyChanges {
95− target: greeter
96− x: -dragHandle.hintDisplacement
149+ Label {
150+ id: swipeHint
151+ visible: greeter.shown
152+ property real baseOpacity: 0.5
153+ opacity: 0.0
154+ anchors.horizontalCenter: parent.horizontalCenter
155+ anchors.bottom: parent.bottom
156+ anchors.bottomMargin: units.gu(5)
157+ text: i18n.tr("Swipe to unlock")
158+ color: "white"
159+ font.weight: Font.Light
160+
161+ SequentialAnimation on opacity {
162+ id: showLabelAnimation
163+ running: false
164+ loops: 2
165+
166+ StandardAnimation {
167+ from: 0.0
168+ to: swipeHint.baseOpacity
169+ duration: UbuntuAnimation.SleepyDuration
97170 }
98− }
99− ]
100− transitions: [
101− Transition {
102− from: "*"
103− to: "*"
104− NumberAnimation {
105− target: greeter
106− property: "x"
107− duration: 300
108− easing.type: Easing.OutCubic
171+ PauseAnimation { duration: UbuntuAnimation.BriskDuration }
172+ StandardAnimation {
173+ from: swipeHint.baseOpacity
174+ to: 0.0
175+ duration: UbuntuAnimation.SleepyDuration
109176 }
110177 }
111− ]
112−
113− DragHandle {
114− id: dragHandle
178+ }
115179
180+ // right side shadow
181+ Image {
182+ anchors.left: parent.right
116183 anchors.top: parent.top
117184 anchors.bottom: parent.bottom
118− anchors.right: parent.right
119−
120− hintDisplacement: units.gu(2)
121−
122− enabled: greeter.narrowMode || !greeter.locked
185+ visible: parent.required
186+ fillMode: Image.Tile
187+ source: "../graphics/dropshadow_right.png"
188+ }
123189
124− direction: Direction.Leftwards
190+ // left side shadow
191+ Image {
192+ anchors.right: parent.left
193+ anchors.top: parent.top
194+ anchors.bottom: parent.bottom
195+ visible: parent.required
196+ fillMode: Image.Tile
197+ source: "../graphics/dropshadow_left.png"
125198 }
126199}
qml/Greeter/GreeterContent.qml+1−10
@@ -20,15 +20,11 @@ import Ubuntu.Components 0.1
2020import LightDM 0.1 as LightDM
2121import "../Components"
2222
23−MouseArea {
23+Item {
2424 id: root
2525 anchors.fill: parent
2626
2727 property bool ready: background.source == "" || background.status == Image.Ready || background.status == Image.Error
28− property bool leftTeaserPressed: teasingMouseArea.pressed &&
29− teasingMouseArea.mouseX < teasingMouseArea.width / 2
30− property bool rightTeaserPressed: teasingMouseArea.pressed &&
31− teasingMouseArea.mouseX > teasingMouseArea.width / 2
3228
3329 signal selected(int uid)
3430 signal unlocked(int uid)
@@ -74,11 +70,6 @@ MouseArea {
7470 opacity: 0.4
7571 }
7672
77− MouseArea {
78− id: teasingMouseArea
79− anchors.fill: parent
80− }
81−
8273 Loader {
8374 id: loginLoader
8475 objectName: "loginLoader"
qml/Greeter/Infographics.qml+3
@@ -457,5 +457,8 @@ Item {
457457 !circleGrowAnimTimer.running)
458458 infographic.model.nextDataSource()
459459 }
460+
461+ onClicked: mouse.accepted = false
462+ onPressed: mouse.accepted = false
460463 }
461464}
qml/Hud/Hud.qml+1−1
@@ -491,6 +491,6 @@ Item {
491491 anchors.top: hudContentClipper.top
492492 anchors.bottom: hudContentClipper.bottom
493493 fillMode: Image.Tile
494− source: "graphics/dropshadow_right.png"
494+ source: "../graphics/dropshadow_right.png"
495495 }
496496}
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+64−57
@@ -148,8 +148,8 @@ FocusScope {
148148
149149 available: !greeter.shown && !lockscreen.shown
150150 hides: [stages, launcher, panel.indicators]
151− shown: disappearingAnimationProgress !== 1.0
152− enabled: disappearingAnimationProgress === 0.0 && edgeDemo.dashEnabled
151+ shown: disappearingAnimationProgress !== 1.0 && greeterWrapper.showProgress !== 1.0
152+ enabled: disappearingAnimationProgress === 0.0 && greeterWrapper.showProgress === 0.0 && edgeDemo.dashEnabled
153153
154154 anchors {
155155 fill: parent
@@ -159,18 +159,14 @@ FocusScope {
159159 contentScale: 1.0 - 0.2 * disappearingAnimationProgress
160160 opacity: 1.0 - disappearingAnimationProgress
161161 property real disappearingAnimationProgress: {
162− if (greeter.shown) {
163− return greeter.showProgress;
162+ if (stages.overlayMode) {
163+ return 0;
164164 } else {
165− if (stages.overlayMode) {
166− return 0;
167− }
168165 return stages.showProgress
169166 }
170167 }
171168
172− // FIXME: only necessary because stages.showProgress and
173− // greeterRevealer.animatedProgress are not animated
169+ // FIXME: only necessary because stages.showProgress is not animated
174170 Behavior on disappearingAnimationProgress { SmoothedAnimation { velocity: 5 }}
175171 }
176172 }
@@ -240,6 +236,8 @@ FocusScope {
240236 function show(focusApp) {
241237 shown = true;
242238 panel.indicators.hide();
239+ edgeDemo.stopDemo();
240+ greeter.hide();
243241 if (!ApplicationManager.focusedApplicationId && ApplicationManager.count > 0 && focusApp) {
244242 ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
245243 }
@@ -267,12 +265,6 @@ FocusScope {
267265 stages.hide();
268266 }
269267 }
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()
276268 }
277269
278270 onApplicationAdded: {
@@ -366,52 +358,66 @@ FocusScope {
366358 }
367359 }
368360
369− Greeter {
370− id: greeter
371− objectName: "greeter"
372−
373− available: true
374− hides: [launcher, panel.indicators, hud]
375− shown: true
376−
377− defaultBackground: shell.background
361+ Rectangle {
362+ anchors.fill: parent
363+ color: "black"
364+ opacity: greeterWrapper.showProgress * 0.8
365+ }
378366
367+ Item {
368+ // Just a tiny wrapper to adjust greeter's x without messing with its own dragging
369+ id: greeterWrapper
370+ x: launcher.progress
379371 y: panel.panelHeight
380372 width: parent.width
381373 height: parent.height - panel.panelHeight
382374
383− dragHandleWidth: shell.edgeSize
375+ Behavior on x {SmoothedAnimation{velocity: 600}}
384376
385− onShownChanged: {
386− if (shown) {
387− lockscreen.reset();
388− // If there is only one user, we start authenticating with that one here.
389− // If there are more users, the Greeter will handle that
390− if (LightDM.Users.count == 1) {
391− LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
377+ readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
378+
379+ Greeter {
380+ id: greeter
381+ objectName: "greeter"
382+
383+ available: true
384+ hides: [launcher, panel.indicators, hud]
385+ shown: true
386+
387+ defaultBackground: shell.background
388+
389+ width: parent.width
390+ height: parent.height
391+
392+ dragHandleWidth: shell.edgeSize
393+
394+ onShownChanged: {
395+ if (shown) {
396+ lockscreen.reset();
397+ // If there is only one user, we start authenticating with that one here.
398+ // If there are more users, the Greeter will handle that
399+ if (LightDM.Users.count == 1) {
400+ LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
401+ }
402+ greeter.forceActiveFocus();
392403 }
393− greeter.forceActiveFocus();
394404 }
395− }
396405
397− onUnlocked: greeter.hide()
398− onSelected: {
399− // Update launcher items for new user
400− var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
401− AccountsService.user = user;
402− LauncherModel.setUser(user);
403− }
404−
405− onLeftTeaserPressedChanged: {
406− if (leftTeaserPressed) {
407− launcher.tease();
406+ onUnlocked: greeter.hide()
407+ onSelected: {
408+ // Update launcher items for new user
409+ var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
410+ AccountsService.user = user;
411+ LauncherModel.setUser(user);
408412 }
409− }
410413
411− Binding {
412− target: ApplicationManager
413− property: "suspended"
414− value: greeter.shown && greeter.showProgress == 1
414+ onTease: launcher.tease()
415+
416+ Binding {
417+ target: ApplicationManager
418+ property: "suspended"
419+ value: greeter.shown && greeterWrapper.showProgress == 1
420+ }
415421 }
416422 }
417423
@@ -542,7 +548,7 @@ FocusScope {
542548 anchors.bottom: parent.bottom
543549 width: parent.width
544550 dragAreaWidth: shell.edgeSize
545− available: (!greeter.shown || greeter.narrowMode) && edgeDemo.launcherEnabled
551+ available: edgeDemo.launcherEnabled
546552
547553 onShowDashHome: {
548554 if (edgeDemo.running)
@@ -557,14 +563,15 @@ FocusScope {
557563 launcher.hide();
558564 }
559565 }
566+ if (greeter.shown) {
567+ greeter.hideRight();
568+ launcher.hide();
569+ }
560570 }
561571 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)
572+ onLauncherApplicationSelected: {
573+ if (!edgeDemo.running)
574+ shell.activateApplication(appId)
568575 }
569576 onShownChanged: {
570577 if (shown) {
qml/graphics/dropshadow_left@20.pngnew file

Binary file not shown.

qml/Hud/graphics/dropshadow_right@20.png → qml/graphics/dropshadow_right@20.pngrenamed

Binary file not 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
src/main.cpp+3
@@ -117,6 +117,9 @@ int startShell(int argc, const char** argv, void* server)
117117
118118 QUrl source(::qmlDirectory()+"Shell.qml");
119119 prependImportPaths(view->engine(), ::overrideImportPaths());
120+ if (!isUbuntuMirServer) {
121+ prependImportPaths(view->engine(), ::nonMirImportPaths());
122+ }
120123 appendImportPaths(view->engine(), ::fallbackImportPaths());
121124
122125 view->setSource(source);
tests/CMakeLists.txt+2−2
@@ -1,7 +1,7 @@
1−# QML tests that do not require graphical capabitlies.
1+# QML tests that do not require graphical capabilities.
22add_custom_target(qmlunittests)
33
4−# QML tests that require graphical capabitlies.
4+# QML tests that require graphical capabilities.
55add_custom_target(qmluitests)
66
77add_custom_target(qmltests)
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/__init__.py+50−1
@@ -21,8 +21,9 @@
2121
2222from time import sleep
2323from functools import wraps
24−import logging
24+from gi.repository import Notify
2525
26+import logging
2627
2728logger = logging.getLogger(__name__)
2829
@@ -48,6 +49,7 @@ def disable_qml_mocking(fn):
4849 return fn(*args, **kwargs)
4950 return wrapper
5051
52+
5153class DragMixin(object):
5254 def _drag(self, x1, y1, x2, y2):
5355 # XXX This ugly code is here just temporarily, waiting for drag
@@ -69,3 +71,50 @@ class DragMixin(object):
6971 self.touch._finger_move(int(x2), int(y2))
7072 except AttributeError:
7173 self.touch._device.finger_move(int(x2), int(y2))
74+
75+
76+def create_ephemeral_notification(
77+ summary='',
78+ body='',
79+ icon=None,
80+ hints=[],
81+ urgency='NORMAL'
82+):
83+ """Create an ephemeral (non-interactive) notification
84+
85+ :param summary: Summary text for the notification
86+ :param body: Body text to display in the notification
87+ :param icon: Path string to the icon to use
88+ :param hint_strings: List of tuples containing the 'name' and value
89+ for setting the hint strings for the notification
90+ :param urgency: Urgency string for the noticiation, either: 'LOW',
91+ 'NORMAL', 'CRITICAL'
92+ """
93+ Notify.init('Unity8')
94+
95+ logger.info(
96+ "Creating ephemeral: summary(%s), body(%s), urgency(%r) "
97+ "and Icon(%s)",
98+ summary,
99+ body,
100+ urgency,
101+ icon
102+ )
103+
104+ notification = Notify.Notification.new(summary, body, icon)
105+
106+ for hint in hints:
107+ key, value = hint
108+ notification.set_hint_string(key, value)
109+ logger.info("Adding hint to notification: (%s, %s)", key, value)
110+ notification.set_urgency(_get_urgency(urgency))
111+
112+ return notification
113+
114+
115+def _get_urgency(urgency):
116+ """Translates urgency string to enum."""
117+ _urgency_enums = {'LOW': Notify.Urgency.LOW,
118+ 'NORMAL': Notify.Urgency.NORMAL,
119+ 'CRITICAL': Notify.Urgency.CRITICAL}
120+ return _urgency_enums.get(urgency.upper())
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+34−6
@@ -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."""
@@ -142,6 +157,7 @@ class GenericScopeView(emulators.UnityEmulatorBase):
142157
143158 :parameter category: The name of the category where the application is.
144159 :app_name: The name of the application.
160+ :return: The opened preview.
145161
146162 """
147163 category_element = self._get_category_element(category)
@@ -150,8 +166,11 @@ class GenericScopeView(emulators.UnityEmulatorBase):
150166 # Some categories do not show previews, like recent apps.
151167 # --elopio - 2014-1-14
152168 self.pointing_device.click_object(icon)
153− return self.get_root_instance().wait_select_single(
169+ preview_list = self.get_root_instance().wait_select_single(
154170 'PreviewListView', objectName='dashContentPreviewList')
171+ preview_list.x.wait_for(0)
172+ return preview_list.select_single(
173+ Preview, objectName='preview{}'.format(preview_list.currentIndex))
155174
156175 def _get_category_element(self, category):
157176 try:
@@ -172,11 +191,20 @@ class DashApps(GenericScopeView):
172191
173192 """
174193 category_element = self._get_category_element(category)
175− application_tiles = category_element.select_many('Card')
176− # TODO return them on the same order they are displayed.
177− # --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+
178201 result = []
179− for card in application_tiles:
202+ for card in application_cards:
180203 if card.objectName != 'cardToolCard':
181− result.append(card)
204+ card_header = card.select_single('CardHeader')
205+ result.append(card_header.title)
182206 return result
207+
208+
209+class Preview(emulators.UnityEmulatorBase):
210+ """Autopilot custom proxy object for generic previews."""
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+27−17
@@ -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()
@@ -141,14 +157,16 @@ class GenericScopeViewEmulatorTestCase(DashBaseTestCase):
141157
142158 def test_open_preview(self):
143159 preview = self.generic_scope.open_preview('0', 'Title.0.0')
144− preview.x.wait_for(0)
160+ self.assertIsInstance(preview, dash_emulators.Preview)
161+ self.assertTrue(preview.isCurrent)
162+
145163
146164class DashAppsEmulatorTestCase(DashBaseTestCase):
147165
148166 available_applications = [
149167 'Title.2.0', 'Title.2.1', 'Title.2.2', 'Title.2.3', 'Title.2.4',
150168 'Title.2.5', 'Title.2.6', 'Title.2.7', 'Title.2.8', 'Title.2.9',
151− 'Title.2.9', 'Title.2.10', 'Title.2.11']
169+ 'Title.2.10', 'Title.2.11', 'Title.2.12']
152170
153171 def setUp(self):
154172 # Set up the fake scopes before launching unity.
@@ -165,28 +183,20 @@ class DashAppsEmulatorTestCase(DashBaseTestCase):
165183 self.assertEqual(
166184 'No category found with name unexisting category', str(exception))
167185
168− def test_get_applications_should_return_list_with_names(self):
186+ def test_get_applications_should_return_correct_applications(self):
169187 category = '2'
170188 expected_apps_count = self._get_number_of_application_slots(category)
171189 expected_applications = self.available_applications[
172190 :expected_apps_count]
173−
174191 applications = self.applications_scope.get_applications(category)
175− applications_titles = []
176− for application in applications:
177− cardHeader = application.select_single('CardHeader')
178− applications_titles.append(cardHeader.title)
179−
180− self.assertThat(applications, HasLength(expected_apps_count))
181− for expected in expected_applications:
182− self.assertThat(applications_titles, Contains(expected))
192+ self.assertEqual(expected_applications, applications)
183193
184194 def _get_number_of_application_slots(self, category):
185195 category_element = self.applications_scope._get_category_element(
186196 category)
187197 grid = category_element.select_single('CardFilterGrid')
188− return grid.columns * grid.rows
189−
190− def test_open_preview(self):
191− preview = self.applications_scope.open_preview('2', 'Title.2.1')
192− preview.x.wait_for(0)
198+ filtergrid = grid.select_single('FilterGrid')
199+ if (grid.filtered):
200+ return filtergrid.collapsedRowCount * filtergrid.columns
201+ else:
202+ return filtergrid.uncollapsedRowCount * filtergrid.columns
tests/autopilot/unity8/shell/tests/test_notifications.py+17−61
@@ -21,6 +21,7 @@
2121
2222from __future__ import absolute_import
2323
24+from unity8 import shell
2425from unity8.process_helpers import unlock_unity
2526from unity8.shell.tests import UnityTestCase, _get_device_emulation_scenarios
2627
@@ -64,7 +65,8 @@ class NotificationsBase(UnityTestCase):
6465 if os.path.abspath(__file__).startswith('/usr/'):
6566 return '/usr/share/unity8/graphics/' + icon_name
6667 else:
67− return os.path.dirname(__file__) + "/../../../../../qml/graphics/" + icon_name
68+ return os.path.dirname(__file__) + (
69+ "/../../../../../qml/graphics/" + icon_name)
6870
6971 def _get_notifications_list(self):
7072 return self.main_window.select_single(
@@ -196,7 +198,7 @@ class InteractiveNotificationBase(NotificationsBase):
196198 get_notification = lambda: notify_list.wait_select_single(
197199 'Notification', objectName='notification1')
198200 notification = get_notification()
199− self._assert_notification(notification, None, None, True, True, 1.0)
201+ self._assert_notification(notification, summary, body, True, True, 1.0)
200202 initial_height = notification.height
201203 self.touch.tap_object(notification.select_single(objectName="button1"))
202204 self.assertThat(
@@ -437,7 +439,7 @@ class EphemeralNotificationsTests(NotificationsBase):
437439 )
438440 ]
439441
440− notification = self._create_ephemeral_notification(
442+ notification = shell.create_ephemeral_notification(
441443 summary,
442444 body,
443445 icon_path,
@@ -474,7 +476,7 @@ class EphemeralNotificationsTests(NotificationsBase):
474476 )
475477 ]
476478
477− notification = self._create_ephemeral_notification(
479+ notification = shell.create_ephemeral_notification(
478480 summary,
479481 None,
480482 None,
@@ -486,7 +488,6 @@ class EphemeralNotificationsTests(NotificationsBase):
486488
487489 notification = lambda: notify_list.wait_select_single(
488490 'Notification', objectName='notification1')
489−
490491 self._assert_notification(
491492 notification(),
492493 summary,
@@ -517,7 +518,7 @@ class EphemeralNotificationsTests(NotificationsBase):
517518 body_critical = 'Dude, this is so urgent you have no idea :)'
518519 icon_path_critical = self._get_icon_path('avatars/anna_olsson.png')
519520
520− notification_normal = self._create_ephemeral_notification(
521+ notification_normal = shell.create_ephemeral_notification(
521522 summary_normal,
522523 body_normal,
523524 icon_path_normal,
@@ -525,7 +526,7 @@ class EphemeralNotificationsTests(NotificationsBase):
525526 )
526527 notification_normal.show()
527528
528− notification_low = self._create_ephemeral_notification(
529+ notification_low = shell.create_ephemeral_notification(
529530 summary_low,
530531 body_low,
531532 icon_path_low,
@@ -533,7 +534,7 @@ class EphemeralNotificationsTests(NotificationsBase):
533534 )
534535 notification_low.show()
535536
536− notification_critical = self._create_ephemeral_notification(
537+ notification_critical = shell.create_ephemeral_notification(
537538 summary_critical,
538539 body_critical,
539540 icon_path_critical,
@@ -594,7 +595,7 @@ class EphemeralNotificationsTests(NotificationsBase):
594595 summary = 'Summary-Body'
595596 body = 'This is a superfluous notification'
596597
597− notification = self._create_ephemeral_notification(summary, body)
598+ notification = shell.create_ephemeral_notification(summary, body)
598599 notification.show()
599600
600601 notification = notify_list.wait_select_single(
@@ -618,7 +619,7 @@ class EphemeralNotificationsTests(NotificationsBase):
618619
619620 summary = 'Summary-Only'
620621
621− notification = self._create_ephemeral_notification(summary)
622+ notification = shell.create_ephemeral_notification(summary)
622623 notification.show()
623624
624625 notification = notify_list.wait_select_single(
@@ -638,7 +639,7 @@ class EphemeralNotificationsTests(NotificationsBase):
638639 body = 'This is the original content of this notification-bubble.'
639640 icon_path = self._get_icon_path('avatars/funky.png')
640641
641− notification = self._create_ephemeral_notification(
642+ notification = shell.create_ephemeral_notification(
642643 summary,
643644 body,
644645 icon_path
@@ -662,7 +663,7 @@ class EphemeralNotificationsTests(NotificationsBase):
662663 icon_path = self._get_icon_path('avatars/amanda.png')
663664 notification.update(summary, body, icon_path)
664665 notification.show()
665− self._assert_notification(get_notification(), summary, body)
666+ self._assert_notification(get_notification(), summary, body, True, False, 1.0)
666667
667668 def test_update_notification_layout_change(self):
668669 """Notification must allow updating its contents and layout while
@@ -678,7 +679,7 @@ class EphemeralNotificationsTests(NotificationsBase):
678679 icon_path = self._get_icon_path('avatars/anna_olsson.png')
679680 hint_icon = self._get_icon_path('applicationIcons/phone-app.png')
680681
681− notification = self._create_ephemeral_notification(
682+ notification = shell.create_ephemeral_notification(
682683 summary,
683684 body,
684685 icon_path
@@ -709,7 +710,7 @@ class EphemeralNotificationsTests(NotificationsBase):
709710 notification.show()
710711
711712 self.assertThat(get_notification, Eventually(NotEquals(None)))
712− self._assert_notification(get_notification(), summary, body, False)
713+ self._assert_notification(get_notification(), summary, body, False, False, 1.0)
713714
714715 def test_append_hint(self):
715716 """Notification has to accumulate body-text using append-hint."""
@@ -722,7 +723,7 @@ class EphemeralNotificationsTests(NotificationsBase):
722723 body = 'Hey Bro Coly!'
723724 icon_path = self._get_icon_path('avatars/amanda.png')
724725 body_sum = body
725− notification = self._create_ephemeral_notification(
726+ notification = shell.create_ephemeral_notification(
726727 summary,
727728 body,
728729 icon_path,
@@ -758,7 +759,7 @@ class EphemeralNotificationsTests(NotificationsBase):
758759 for new_body in bodies:
759760 body = new_body
760761 body_sum += '\n' + body
761− notification = self._create_ephemeral_notification(
762+ notification = shell.create_ephemeral_notification(
762763 summary,
763764 body,
764765 icon_path,
@@ -779,48 +780,3 @@ class EphemeralNotificationsTests(NotificationsBase):
779780 False,
780781 1.0
781782 )
782−
783− def _create_ephemeral_notification(
784− self,
785− summary="",
786− body="",
787− icon=None,
788− hints=[],
789− urgency="NORMAL"
790− ):
791− """Create an ephemeral (non-interactive) notification
792−
793− :param summary: Summary text for the notification
794− :param body: Body text to display in the notification
795− :param icon: Path string to the icon to use
796− :param hint_strings: List of tuples containing the 'name' and value
797− for setting the hint strings for the notification
798− :param urgency: Urgency string for the noticiation, either: 'LOW',
799− 'NORMAL', 'CRITICAL'
800−
801− """
802− logger.info(
803− "Creating ephemeral: summary(%s), body(%s), urgency(%r) "
804− "and Icon(%s)",
805− summary,
806− body,
807− urgency,
808− icon
809− )
810−
811− n = Notify.Notification.new(summary, body, icon)
812−
813− for hint in hints:
814− key, value = hint
815− n.set_hint_string(key, value)
816− logger.info("Adding hint to notification: (%s, %s)", key, value)
817− n.set_urgency(self._get_urgency(urgency))
818−
819− return n
820−
821− def _get_urgency(self, urgency):
822− """Translates urgency string to enum."""
823− _urgency_enums = {'LOW': Notify.Urgency.LOW,
824− 'NORMAL': Notify.Urgency.NORMAL,
825− 'CRITICAL': Notify.Urgency.CRITICAL}
826− return _urgency_enums.get(urgency.upper())
tests/autopilot/unity8/shell/tests/test_system_integration.py+3−2
@@ -22,6 +22,7 @@
2222import subprocess
2323import unittest
2424
25+from autopilot import platform
2526from unity8.shell.emulators import UnityEmulatorBase
2627from unity8.shell.tests import UnityTestCase, _get_device_emulation_scenarios
2728
@@ -31,10 +32,10 @@ class SystemIntegrationTests(UnityTestCase):
3132
3233 scenarios = _get_device_emulation_scenarios()
3334
34− @unittest.skip("Disabled until we can investigate jenkins failure")
35+ @unittest.skipIf(platform.model() == "Desktop", "Test is broken on otto, see bug 1281634.")
3536 def test_networkmanager_integration(self):
3637 self.launch_unity()
3738
3839 # invoke policykit to check permissions
39− pid = subprocess.check_output("pidof -s unity8", shell=True)
40+ pid = subprocess.check_output(["pidof", "-s", "unity8"], universal_newlines=True)
4041 subprocess.check_call("pkcheck --action-id org.freedesktop.NetworkManager.enable-disable-network --process " + pid, shell=True)
tests/autopilot/unity8/shell/tests/test_upstart.py+2−2
@@ -82,7 +82,7 @@ class UpstartIntegrationTests(UnityTestCase):
8282 ))
8383
8484 def test_no_sigstop(self):
85− self.patch_environment("UPSTART_JOB", "foo")
85+ self.patch_environment("UNITY_MIR_EMITS_SIGSTOP", "")
8686 self._launch_unity()
8787 self._set_proxy()
8888
@@ -91,7 +91,7 @@ class UpstartIntegrationTests(UnityTestCase):
9191 logger.debug("Unity loaded and ready.")
9292
9393 def test_expect_sigstop(self):
94− self.patch_environment("UPSTART_JOB", "unity8")
94+ self.patch_environment("UNITY_MIR_EMITS_SIGSTOP", "1")
9595 self._launch_unity()
9696 self.assertThat(lambda: os.WIFSTOPPED(self._get_status()), Eventually(Equals(True)), "Unity8 should raise SIGSTOP when ready")
9797
tests/mocks/Unity/Application/ApplicationScreenshotProvider.cpp+3
@@ -54,6 +54,9 @@ QImage ApplicationScreenshotProvider::requestImage(const QString &imageId, QSize
5454
5555 if (app->stage() == ApplicationInfo::SideStage) {
5656 QByteArray gus = qgetenv("GRID_UNIT_PX");
57+ if (gus.isEmpty() || gus.toInt() == 0) {
58+ gus = "8";
59+ }
5760 image = image.scaledToWidth(gus.toInt() * 48);
5861 } else {
5962 // Lets scale main stage applications to be the size of the screen/window.
tests/mocks/Unity/Application/CMakeLists.txt+14
@@ -35,3 +35,17 @@ install(TARGETS FakeUnityApplicationQml
3535install(FILES qmldir InputFilterArea.qml OSKController.qml
3636 DESTINATION ${SHELL_INSTALL_QML}/mocks/Unity/Application
3737 )
38+
39+# Duplicate the mock to nonmirplugins
40+# In shadow builds we just link it, in real installs we also install it to the other path
41+add_custom_target(OSKControllerQmlFile2 ALL
42+ COMMAND mkdir -p ${CMAKE_BINARY_DIR}/nonmirplugins/Unity && rm -f ${CMAKE_BINARY_DIR}/nonmirplugins/Unity/Application && ln -s ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_BINARY_DIR}/nonmirplugins/Unity
43+ DEPENDS qmldir
44+)
45+install(TARGETS FakeUnityApplicationQml
46+ DESTINATION ${SHELL_INSTALL_QML}/nonmirplugins/Unity/Application
47+ )
48+
49+install(FILES qmldir InputFilterArea.qml OSKController.qml
50+ DESTINATION ${SHELL_INSTALL_QML}/nonmirplugins/Unity/Application
51+ )
tests/plugins/DashViews/CMakeLists.txt+23−35
@@ -25,46 +25,41 @@ remove_definitions(-DQT_NO_KEYWORDS)
2525add_definitions(-DDASHVIEWSTEST_FOLDER="${CMAKE_CURRENT_SOURCE_DIR}")
2626add_definitions(-DBUILT_PLUGINS_DIR="${CMAKE_BINARY_DIR}/plugins")
2727
28−add_executable(listviewwithpageheadertestExec listviewwithpageheadertest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/listviewwithpageheader.cpp)
29−qt5_use_modules(listviewwithpageheadertestExec Test Core Qml)
30−target_link_libraries(listviewwithpageheadertestExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
31−add_custom_target(testlistviewwithpageheader ${CMAKE_CURRENT_BINARY_DIR}/listviewwithpageheadertestExec -o ${CMAKE_BINARY_DIR}/testlistviewwithpageheader.xml,xunitxml -o -,txt)
28+macro(add_lvwph_test FILENAME TESTNAME)
29+ add_executable(${TESTNAME}TestExec
30+ ${FILENAME}test.cpp
31+ ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/listviewwithpageheader.cpp)
32+ qt5_use_modules(${TESTNAME}TestExec Test Core Qml)
33+ target_link_libraries(${TESTNAME}TestExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
3234
33−add_executable(listviewwithpageheadertestsectionExec listviewwithpageheadertestsection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/listviewwithpageheader.cpp)
34−qt5_use_modules(listviewwithpageheadertestsectionExec Test Core Qml)
35−target_link_libraries(listviewwithpageheadertestsectionExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
36−add_custom_target(testlistviewwithpageheadersection ${CMAKE_CURRENT_BINARY_DIR}/listviewwithpageheadertestsectionExec -o ${CMAKE_BINARY_DIR}/testlistviewwithpageheadersection.xml,xunitxml -o -,txt)
35+ add_binary_qml_test(${TESTNAME} "" "DashViews-qml")
36+endmacro()
3737
38−add_executable(listviewwithpageheadertestsectionexternalmodelExec listviewwithpageheadertestsectionexternalmodel.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/listviewwithpageheader.cpp)
39−qt5_use_modules(listviewwithpageheadertestsectionexternalmodelExec Test Core Qml)
40−target_link_libraries(listviewwithpageheadertestsectionexternalmodelExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
41−add_custom_target(testlistviewwithpageheadersectionexternalmodel ${CMAKE_CURRENT_BINARY_DIR}/listviewwithpageheadertestsectionexternalmodelExec -o ${CMAKE_BINARY_DIR}/testlistviewwithpageheadersectionexternalmodel.xml,xunitxml -o -,txt)
38+add_lvwph_test(listviewwithpageheader ListViewWithPageHeader)
39+add_lvwph_test(listviewwithpageheadersection ListViewWithPageHeaderSection)
40+add_lvwph_test(listviewwithpageheadersectionexternalmodel ListViewWithPageHeaderSectionExternalModel)
4241
4342macro(add_dashview_try_test FILENAME TESTNAME)
44− add_executable(${FILENAME}testExec
43+ add_executable(${TESTNAME}TestExec
4544 ${FILENAME}test.cpp
4645 ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/${FILENAME}.cpp
4746 ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/abstractdashview.cpp
4847 )
49− qt5_use_modules(${FILENAME}testExec Test Core Qml)
50− target_link_libraries(${FILENAME}testExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
51− add_custom_target(test${TESTNAME}
52− ${CMAKE_CURRENT_BINARY_DIR}/${FILENAME}testExec
53− -o ${CMAKE_BINARY_DIR}/test${TESTNAME}.xml,xunitxml
54− -o -,txt
55− DEPENDS ${FILENAME}testExec DashViews-qml
56− )
48+ qt5_use_modules(${TESTNAME}TestExec Test Core Qml)
49+ target_link_libraries(${TESTNAME}TestExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
5750
58− add_executable(${FILENAME}tryExec
51+ add_binary_qml_test(${TESTNAME} "" "DashViews-qml")
52+
53+ add_executable(${TESTNAME}tryExec
5954 ${FILENAME}try.cpp
6055 ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/${FILENAME}.cpp
6156 ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/DashViews/abstractdashview.cpp
6257 )
63− qt5_use_modules(${FILENAME}tryExec Test Core Qml)
64− target_link_libraries(${FILENAME}tryExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
58+ qt5_use_modules(${TESTNAME}tryExec Test Core Qml)
59+ target_link_libraries(${TESTNAME}tryExec ${Qt5Gui_LIBRARIES} ${Qt5Quick_LIBRARIES})
6560 add_custom_target(try${TESTNAME}
66− ${CMAKE_CURRENT_BINARY_DIR}/${FILENAME}tryExec
67− DEPENDS ${FILENAME}tryExec DashViews-qml
61+ ${CMAKE_CURRENT_BINARY_DIR}/${TESTNAME}tryExec
62+ DEPENDS ${TESTNAME}tryExec DashViews-qml
6863 )
6964endmacro()
7065
@@ -72,13 +67,6 @@ add_dashview_try_test(verticaljournal VerticalJournal)
7267add_dashview_try_test(horizontaljournal HorizontalJournal)
7368add_dashview_try_test(organicgrid OrganicGrid)
7469
70+set(qmltest_DEFAULT_TARGETS qmluitests)
7571set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
76−add_qml_test(. listviewwithpageheaderqml IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
77−
78−add_dependencies(qmluitests testlistviewwithpageheader)
79−add_dependencies(qmluitests testlistviewwithpageheadersection)
80−add_dependencies(qmluitests testlistviewwithpageheadersectionexternalmodel)
81−add_dependencies(qmluitests testlistviewwithpageheaderqml)
82−add_dependencies(qmluitests testVerticalJournal)
83−add_dependencies(qmluitests testHorizontalJournal)
84−add_dependencies(qmluitests testOrganicGrid)
72+add_qml_test(. ListViewWithPageHeaderQML IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
tests/plugins/DashViews/listviewwithpageheadertestsectionexternalmodel.cpp → tests/plugins/DashViews/listviewwithpageheadersectionexternalmodeltest.cpprenamed+1−1
@@ -248,4 +248,4 @@ private:
248248
249249QTEST_MAIN(ListViewWithPageHeaderTestSection)
250250
251−#include "listviewwithpageheadertestsectionexternalmodel.moc"
251+#include "listviewwithpageheadersectionexternalmodeltest.moc"
tests/plugins/DashViews/listviewwithpageheadertestsection.cpp → tests/plugins/DashViews/listviewwithpageheadersectiontest.cpprenamed+1−1
@@ -2166,4 +2166,4 @@ private:
21662166
21672167QTEST_MAIN(ListViewWithPageHeaderTestSection)
21682168
2169−#include "listviewwithpageheadertestsection.moc"
2169+#include "listviewwithpageheadersectiontest.moc"
tests/plugins/DashViews/tst_listviewwithpageheaderqml.qml → tests/plugins/DashViews/tst_ListViewWithPageHeaderQML.qmlrenamed

The content is the same.

tests/plugins/Ubuntu/Gestures/CMakeLists.txt+2−10
@@ -15,19 +15,11 @@ add_custom_target(UbuntuGesturesTestQmlFiles ALL
1515add_definitions(-DUBUNTU_GESTURES_PLUGIN_DIR="${CMAKE_BINARY_DIR}/plugins")
1616
1717macro(add_gesture_ui_test CLASSNAME)
18− set(testCommand
19− LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures
20− ${CMAKE_CURRENT_BINARY_DIR}/${CLASSNAME}TestExec
21− -o ${CMAKE_BINARY_DIR}/${CLASSNAME}Test.xml,xunitxml
22− -o -,txt)
23−
24− add_custom_target(test${CLASSNAME} ${testCommand})
25− add_dependencies(qmluitests test${CLASSNAME})
26− add_dependencies(test${CLASSNAME} ${CLASSNAME}TestExec UbuntuGesturesTestQmlFiles)
27−
2818 add_executable(${CLASSNAME}TestExec tst_${CLASSNAME}.cpp GestureTest.cpp)
2919 qt5_use_modules(${CLASSNAME}TestExec Test Core Qml Gui Quick)
3020 target_link_libraries(${CLASSNAME}TestExec UbuntuGestureQml)
21+
22+ add_binary_qml_test(${CLASSNAME} ${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures UbuntuGesturesTestQmlFiles)
3123 add_manual_qml_test(. ${CLASSNAME} IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
3224endmacro(add_gesture_ui_test)
3325
tests/plugins/Utils/CMakeLists.txt+2−1
@@ -11,7 +11,8 @@ macro(run_tests)
1111 -o ${CMAKE_BINARY_DIR}/${_test}.xml,xunitxml
1212 -o -,txt)
1313 add_test(NAME ${_test} COMMAND ${testCommand})
14− add_custom_target(${_test} ${testCommand})
14+ set_tests_properties(${_test} PROPERTIES ENVIRONMENT "LC_ALL=C")
15+ add_custom_target(${_test} env "LC_ALL=C" ${testCommand})
1516 add_executable(${_test}Exec ${_test}.cpp modeltest.cpp)
1617 qt5_use_modules(${_test}Exec Test Core Qml)
1718 target_link_libraries(${_test}Exec
tests/qmltests/CMakeLists.txt+2−6
@@ -11,17 +11,15 @@ 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
1817set(qmltest_DEFAULT_TARGETS qmluitests)
1918set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
20−set(qmltest_DEFAULT_PROPERTIES "")
19+set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "LC_ALL=C")
2120
2221add_qml_test(. Shell IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks
2322 ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
24−add_manual_qml_test(Components DragHandle IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
2523add_qml_test(Components Carousel)
2624add_qml_test(Components DraggingArea)
2725add_qml_test(Components EdgeDemoOverlay)
@@ -33,13 +31,11 @@ add_qml_test(Components ResponsiveGridView)
3331add_qml_test(Components Revealer)
3432add_qml_test(Components SeeMore)
3533add_qml_test(Components Showable)
36−add_qml_test(Components Stage IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS}
37− ${CMAKE_BINARY_DIR}/tests/mocks
38− ${CMAKE_BINARY_DIR}/plugins)
3934add_qml_test(Components PageHeaderLabel)
4035add_qml_test(Dash Dash IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
4136add_qml_test(Dash DashContent IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
4237add_qml_test(Dash Card)
38+add_qml_benchmark(Dash CardBenchmark 1000)
4339add_qml_test(Dash CardHeader)
4440add_qml_test(Dash CardTool)
4541add_qml_test(Dash GenericScopeView IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
tests/qmltests/Components/CMakeLists.txt+3−10
@@ -6,19 +6,12 @@ include_directories(
66 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
77 )
88
9−set(testCommand
10− LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures
11− ${CMAKE_CURRENT_BINARY_DIR}/DragHandleTestExec
12− -o ${CMAKE_BINARY_DIR}/DragHandleTest.xml,xunitxml
13− -o -,txt)
14−
15−add_custom_target(testDragHandle ${testCommand})
16−add_dependencies(qmluitests testDragHandle)
17−add_dependencies(testDragHandle DragHandleTestExec)
18−
199add_executable(DragHandleTestExec tst_DragHandle.cpp)
2010qt5_use_modules(DragHandleTestExec Test Core Qml Gui Quick)
2111target_link_libraries(DragHandleTestExec UbuntuGestureQml)
2212
13+add_binary_qml_test(DragHandle ${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures "")
14+add_manual_qml_test(. DragHandle IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
15+
2316add_definitions(-DUBUNTU_GESTURES_PLUGIN_DIR="${CMAKE_BINARY_DIR}/plugins")
2417add_definitions(-DTEST_DIR="${CMAKE_CURRENT_LIST_DIR}")
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/Components/tst_FilterGrid.qml+1−1
@@ -50,6 +50,7 @@ Rectangle {
5050 CheckBox {
5151 id: filterCheckBox
5252 checked: true
53+ onCheckedChanged: filterGrid.setFilter(checked, false /*animate*/)
5354 }
5455 }
5556 }
@@ -99,7 +100,6 @@ Rectangle {
99100 maximumNumberOfColumns: 3
100101 collapsedRowCount:
101102 collapsedRowCountSelector.values[collapsedRowCountSelector.selectedIndex]
102− filter: filterCheckBox.checked
103103 minimumHorizontalSpacing: units.gu(1)
104104 delegateWidth: units.gu(6)
105105 delegateHeight: units.gu(6)
tests/qmltests/Components/tst_LazyImage.qml+1
@@ -152,6 +152,7 @@ Rectangle {
152152
153153 function test_lazyimage(data) {
154154 data.func();
155+ waitForRendering(baseRect);
155156
156157 if (data.transition) {
157158 // wait for the transition to complete
tests/qmltests/Components/tst_Stage.qmldeleted−278
@@ -1,278 +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 ".."
20−import "../../../qml/Components"
21−import Unity.Application 0.1
22−import Ubuntu.Components 0.1
23−import Unity.Test 0.1 as UT
24−import "tst_Stage"
25−
26−Rectangle {
27− width: units.gu(70)
28− height: stageRect.height
29−
30− // Even though we replace the ApplicationScreenshot instances in Stage
31− // with our own fake ones, ApplicationScreenshot.qml still gives out a warning
32− // if shell.importUbuntuApplicationAvailable is missing.
33− // For we have this here for the sake of keeping a clean log output.
34− Item {
35− id: shell
36− property bool importUbuntuApplicationAvailable: false
37− }
38−
39− // A fake ApplicationManager implementation to be passed to Stage
40− QtObject {
41− id: fakeAppManager
42− property ListModel mainStageApplications: ListModel {}
43− property ListModel sideStageApplications: ListModel {}
44− property var mainStageFocusedApplication
45−
46− property Component fakeAppWindowComponent: Component {
47− Rectangle {
48− width: stage.width
49− height: stage.height
50− property alias text : txt.text
51− Text {id:txt}
52− }
53− }
54−
55− function activateApplication(desktopFile, argument) {
56− var appWindow = fakeAppWindowComponent.createObject(fakeWindowContainer)
57− var application = {
58− 'icon': "foo",
59− 'handle': desktopFile,
60− 'name': "Foo",
61− 'fullscreen': false,
62− 'desktopFile': desktopFile,
63− 'stage': ApplicationInfo.MainStage,
64− 'argument': 0,
65− 'color': desktopFile,
66− 'window': appWindow
67− };
68− appWindow.color = application.color
69− appWindow.text = desktopFile + " actual";
70−
71− mainStageApplications.append(application)
72− updateZOrderOfWindows(mainStageApplications)
73−
74− return application
75− }
76−
77− function focusApplication(application) {
78− mainStageFocusedApplication = application
79− }
80−
81− function getApplicationFromDesktopFile(desktopFile, stageType) {
82− var sideStage = (stage == ApplicationInfo.SideStage);
83− var applications = sideStage ? sideStageApplications
84− : mainStageApplications;
85−
86− for (var i = 0; i < applications.count; i++ ) {
87− var application = applications.get(i);
88− if (application.desktopFile === desktopFile) {
89− return application;
90− }
91− }
92− return null;
93− }
94−
95− function moveRunningApplicationStackPosition(from, to, stage) {
96− var sideStage = (stage == ApplicationInfo.SideStage);
97− var applications = sideStage ? sideStageApplications
98− : mainStageApplications;
99−
100− if (from !== to && applications.count > 0 && from >= 0 && to >= 0) {
101− applications.move(from, to, 1);
102− }
103−
104− updateZOrderOfWindows(applications)
105− }
106−
107− function updateZOrderOfWindows(applications) {
108− var nextZ = 100
109− for (var i = 0; i < applications.count; i++ ) {
110− var application = applications.get(i);
111− application.window.z = nextZ--;
112− }
113− }
114−
115− function deacticateApplication(desktopFile) {
116− for (var i = 0; i < mainStageApplications.count; i++ ) {
117− var application = mainStageApplications.get(i)
118− if (application.desktopFile === desktopFile) {
119− focusApplication(null)
120− application.window.destroy();
121− mainStageApplications.remove(i)
122− updateZOrderOfWindows(mainStageApplications)
123− return;
124− }
125− }
126− }
127− }
128−
129− Rectangle {
130− id: stageRect
131− x:0
132− y:0
133− width: childrenRect.width
134− height: childrenRect.height
135−
136− color: "grey"
137−
138− // This is where the fake windows are held.
139− // They stay behind the stage, so that the stage's screenshots are shown
140− // on top of them
141− // On a real usage scenario, the current application's surface is composited behind
142− // the shell's surface (where Stage lives). fakeWindowContainer simulates this stacking
143− Item {
144− id: fakeWindowContainer
145− anchors.fill: parent
146− }
147−
148−
149− // A black rectangle behind the stage so that the window switch animations
150− // look good, just like in Stage's real usage.
151− Rectangle {
152− anchors.fill: parent
153− color: "black"
154− visible: stage.usingScreenshots
155− }
156−
157− Stage {
158− id: stage
159− y: 0
160− shouldUseScreenshots: false
161− applicationManager: fakeAppManager
162− rightEdgeDraggingAreaWidth: units.gu(2)
163− fullyShown: true
164− fullyHidden: false
165− newApplicationScreenshot: FakeApplicationScreenshot {
166− id: newAppScreenshot
167− parent: stage
168− width: stage.width
169− height: stage.height - stage.y}
170− oldApplicationScreenshot: FakeApplicationScreenshot {
171− id: oldAppScreenshot
172− parent: stage
173− width: stage.width
174− height: stage.height - stage.y}
175− }
176− }
177−
178− Rectangle {
179− id: controlsRect
180− anchors.top: parent.top
181− anchors.bottom: parent.bottom
182− anchors.left: stageRect.right
183− anchors.right: parent.right
184− color: "lightgrey"
185−
186− Column {
187− id: controls
188− spacing: units.gu(1)
189− AppControl {id: redControl; desktopFile:"red"}
190− AppControl {id: greenControl; desktopFile:"green"}
191− AppControl {id: blueControl; desktopFile:"blue"}
192− }
193− }
194−
195− UT.UnityTestCase {
196− name: "Stage"
197− when: windowShown
198−
199− function isCurrentAppFadingOut() {
200− // it should get a bit translucent and smaller
201− return oldAppScreenshot.opacity < 0.99
202− && oldAppScreenshot.opacity >= 0.1
203− && oldAppScreenshot.scale < 0.99
204− && oldAppScreenshot.scale >= 0.1
205− && oldAppScreenshot.visible
206− }
207−
208− function init() {
209− redControl.checked = false;
210− greenControl.checked = false;
211− blueControl.checked = false;
212− // give some room for animations to start
213− wait(50)
214− // wait until animations end, if any
215− tryCompare(stage, "usingScreenshots", false)
216− }
217−
218− /* If you flick from the right edge of the stage leftwards it should cause an
219− application switch. */
220− function test_dragFromRightEdgeToSwitchApplication() {
221− redControl.checked = true
222−
223− tryCompare(stage, "usingScreenshots", true) // wait for the animation to start
224− tryCompare(stage, "usingScreenshots", false) // and then for it to end
225− compare(fakeAppManager.mainStageFocusedApplication.desktopFile, "red")
226− compare(fakeAppManager.mainStageApplications.get(0).desktopFile, "red")
227−
228− greenControl.checked = true
229−
230− tryCompare(stage, "usingScreenshots", true) // wait for the animation to start
231− tryCompare(stage, "usingScreenshots", false) // and then for it to end
232− compare(fakeAppManager.mainStageFocusedApplication.desktopFile, "green")
233− compare(fakeAppManager.mainStageApplications.get(0).desktopFile, "green")
234−
235− var touchX = stage.width - (stage.rightEdgeDraggingAreaWidth / 2)
236− var touchY = stage.height / 2
237− touchFlick(stage, touchX, touchY, stage.width * 0.25, touchY)
238−
239− // wait until animations end, if any
240− tryCompare(stage, "usingScreenshots", false)
241−
242− // "red" should be the new topmost, focused, application
243− compare(fakeAppManager.mainStageFocusedApplication.desktopFile, "red")
244− compare(fakeAppManager.mainStageApplications.get(0).desktopFile, "red")
245− }
246−
247− /* When an application is launched, it needs a background before it's drawn on screen
248− so that the user does not see the previous running app while the new one is launching.
249− When switching between applications, backgrounds are unnecessary, 'cause the
250− applications are in front of them. */
251− function test_background() {
252− redControl.checked = true
253− tryCompare(stage, "usingScreenshots", true) // wait for the animation to start
254−
255− compare(newAppScreenshot.withBackground, true, "starting app screenshot does not have background enabled")
256−
257− tryCompare(stage, "usingScreenshots", false) // wait for the animation to finish
258−
259− greenControl.checked = true
260− tryCompare(stage, "usingScreenshots", true) // wait for the animation to start
261− tryCompare(stage, "usingScreenshots", false) // and finish
262−
263− var draggingAreaCenterX = stage.width - (stage.rightEdgeDraggingAreaWidth / 2)
264− var draggingAreaCenterY = stage.height / 2
265− var finalTouchX = draggingAreaCenterX - units.gu(5)
266− touchFlick(stage, draggingAreaCenterX, draggingAreaCenterY,
267− finalTouchX, draggingAreaCenterY,
268− true /* beginTouch */, false /* endTouch */)
269−
270− // wait for the animation to start
271− tryCompare(stage, "usingScreenshots", true)
272−
273− compare(newAppScreenshot.withBackground, false, "switched app does have background enabled")
274−
275− touchRelease(stage, finalTouchX, draggingAreaCenterY)
276− }
277− }
278−}
tests/qmltests/Components/tst_Stage/AppControl.qmldeleted−42
@@ -1,42 +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 Ubuntu.Components 0.1
19−
20−Row {
21− id: root
22− property string desktopFile
23− spacing: units.gu(1)
24−
25− property alias checked: checkbox.checked
26−
27− Label {text: root.desktopFile
28− anchors.verticalCenter: parent.verticalCenter
29− width:units.gu(5)}
30−
31− CheckBox {
32− id: checkbox
33− onCheckedChanged: {
34− if (checked) {
35− var app = fakeAppManager.activateApplication(root.desktopFile)
36− stage.activateApplication(root.desktopFile)
37− } else {
38− fakeAppManager.deacticateApplication(root.desktopFile)
39− }
40− }
41− }
42−}
tests/qmltests/Components/tst_Stage/FakeApplicationScreenshot.qmldeleted−53
@@ -1,53 +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−Item {
20− property var application
21− property bool withBackground: true
22− property bool ready: application != undefined
23−
24− function setApplication(app) {
25− application = app
26− }
27−
28− function clearApplication() {
29− setApplication(undefined)
30− }
31−
32− function scheduleUpdate() {}
33−
34− function updateFromCache() {}
35−
36− Rectangle {
37− id: rect
38− anchors.fill: parent
39− Text {id:text}
40− }
41−
42− onApplicationChanged: {
43− if (application) {
44− rect.color = application.color
45− text.text = application.desktopFile + " screenshot"
46− rect.visible = true
47− } else {
48− rect.color = "white"
49− text.text = "<NULL> screenshot"
50− rect.visible = false
51− }
52− }
53−}
tests/qmltests/Dash/Previews/tst_PreviewRatingInput.qml+21−2
@@ -36,6 +36,8 @@ Rectangle {
3636 property var triggeredDataReviewOnly: { "rating": invalidInputRating, "review": validInputText, "author": null }
3737
3838 property var widgetDataBoth: { "visible": "both", "required": "both" }
39+ property var widgetDataBothRequireRating: { "visible": "both", "required": "rating" }
40+ property var widgetDataBothRequireReview: { "visible": "both", "required": "review" }
3941 property var widgetDataRating: { "visible": "rating", "required": "rating" }
4042 property var widgetDataRatingBroken: { "visible": "rating", "required": "review" }
4143 property var widgetDataReview: { "visible": "review", "required": "review" }
@@ -85,6 +87,14 @@ Rectangle {
8587 {tag: "both, rating-only review", widgetData: widgetDataBoth, inputRating: validInputRating, inputText: invalidInputText, emitted: false},
8688 {tag: "both, review-only review", widgetData: widgetDataBoth, inputRating: invalidInputRating, inputText: validInputText, emitted: false},
8789 {tag: "both, complete review", widgetData: widgetDataBoth, inputRating: validInputRating, inputText: validInputText, emitted: true},
90+ {tag: "both require rating, null review", widgetData: widgetDataBothRequireRating, inputRating: invalidInputRating, inputText: invalidInputText, emitted: false},
91+ {tag: "both require rating, rating-only review", widgetData: widgetDataBothRequireRating, inputRating: validInputRating, inputText: invalidInputText, emitted: true},
92+ {tag: "both require rating, review-only review", widgetData: widgetDataBothRequireRating, inputRating: invalidInputRating, inputText: validInputText, emitted: false},
93+ {tag: "both require rating, complete review", widgetData: widgetDataBothRequireRating, inputRating: validInputRating, inputText: validInputText, emitted: true},
94+ {tag: "both require review, null review", widgetData: widgetDataBothRequireReview, inputRating: invalidInputRating, inputText: invalidInputText, emitted: false},
95+ {tag: "both require review, rating-only review", widgetData: widgetDataBothRequireReview, inputRating: validInputRating, inputText: invalidInputText, emitted: false},
96+ {tag: "both require review, review-only review", widgetData: widgetDataBothRequireReview, inputRating: invalidInputRating, inputText: validInputText, emitted: true},
97+ {tag: "both require review, complete review", widgetData: widgetDataBothRequireReview, inputRating: validInputRating, inputText: validInputText, emitted: true},
8898 {tag: "rating, null review", widgetData: widgetDataRating, inputRating: invalidInputRating, inputText: invalidInputText, emitted: false},
8999 {tag: "rating, rating-only review", widgetData: widgetDataRating, inputRating: validInputRating, inputText: invalidInputText, emitted: true},
90100 {tag: "rating, review-only review", widgetData: widgetDataRating, inputRating: invalidInputRating, inputText: validInputText, emitted: false},
@@ -114,7 +124,9 @@ Rectangle {
114124 verify(rating.visible === true);
115125
116126 rating.value = data.inputRating;
117− if (data.widgetData["required"] !== "rating" || data.inputRating < 0) {
127+ if (data.widgetData["required"] !== "rating" ||
128+ data.widgetData["visible"] !== "rating" ||
129+ data.inputRating < 0) {
118130 compare(spy.count, 0);
119131 } else {
120132 compare(spy.count, 1);
@@ -129,7 +141,14 @@ Rectangle {
129141 reviewTextArea.text = data.inputText;
130142 mouseClick(submitButton, submitButton.width / 2, submitButton.height / 2);
131143 switch (data.widgetData["required"]) {
132− case "rating": compare(spy.count, 0); break;
144+ case "rating": {
145+ if (rating.visible === false || data.inputRating < 0) {
146+ compare(spy.count, 0);
147+ } else {
148+ compare(spy.count, 1);
149+ }
150+ break;
151+ }
133152 case "both":
134153 default: {
135154 if (data.inputRating < 0 || data.inputText === "") {
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+5−4
@@ -64,9 +64,10 @@ Item {
6464
6565 UT.UnityTestCase {
6666 name: "GenericScopeView"
67− when: scopes.loaded
67+ when: scopes.loaded && windowShown
6868
6969 function init() {
70+ genericScopeView.scope = scopes.get(2)
7071 shell.width = units.gu(120)
7172 genericScopeView.categoryView.positionAtBeginning();
7273 tryCompare(genericScopeView.categoryView, "contentY", 0)
@@ -139,7 +140,7 @@ Item {
139140 mouseClick(header, header.width / 2, header.height / 2);
140141 tryCompareFunction(function() { middleHeight = category.height; return category.height > initialHeight; }, true);
141142 tryCompare(category, "filtered", false);
142− verify(category.height > middleHeight);
143+ tryCompareFunction(function() { return category.height > middleHeight; }, true);
143144
144145 mouseClick(header, header.width / 2, header.height / 2);
145146 verify(category.expandable);
@@ -164,7 +165,7 @@ Item {
164165
165166 mouseClick(header2, header2.width / 2, header2.height / 2);
166167 tryCompare(category2, "filtered", false);
167− tryCompare(category2FilterGrid, "filter", false);
168+ tryCompare(category2FilterGrid, "filtered", false);
168169
169170 categoryListView.positionAtBeginning();
170171
@@ -181,7 +182,7 @@ Item {
181182 mouseClick(header0, header0.width / 2, header0.height / 2);
182183 tryCompare(category0, "filtered", false);
183184 tryCompare(category2, "filtered", true);
184− tryCompare(category2FilterGrid, "filter", true);
185+ tryCompare(category2FilterGrid, "filtered", true);
185186 mouseClick(header0, header0.width / 2, header0.height / 2);
186187 tryCompare(category0, "filtered", true);
187188 tryCompare(category2, "filtered", true);
tests/qmltests/Greeter/tst_Clock.qml+3−3
@@ -71,10 +71,10 @@ Rectangle {
7171 var dateString = Qt.formatDate(dateObj, Qt.DefaultLocaleLongDate)
7272 var timeString = Qt.formatTime(dateObj)
7373
74− clock.enabled = false
74+ clock.visible = false
7575 var timeModel = findInvisibleChild(clock, "timeModel")
7676
77− compare(timeModel.menuObjectPath, "", "Clock shouldn't be connected to Indicators when not active.")
77+ compare(timeModel.menuObjectPath, "", "Clock shouldn't be connected to Indicators when not visible.")
7878
7979 clock.currentDate = dateObj
8080
@@ -83,7 +83,7 @@ Rectangle {
8383 var timeLabel = findChild(clock, "timeLabel")
8484 compare(timeLabel.text, timeString, "Not the expected time")
8585
86− clock.enabled = true
86+ clock.visible = true
8787
8888 verify(timeModel.menuObjectPath != "", "Should be connected to Indicators.")
8989 }
tests/qmltests/Greeter/tst_Phone.qml+10−50
@@ -61,6 +61,12 @@ Item {
6161 signalName: "unlocked"
6262 }
6363
64+ SignalSpy {
65+ id: teaseSpy
66+ target: greeter
67+ signalName: "tease"
68+ }
69+
6470 UT.UnityTestCase {
6571 name: "Greeter"
6672 when: windowShown
@@ -82,56 +88,10 @@ Item {
8288 }
8389
8490 function test_teasingArea(data) {
85− tryCompare(greeter, "leftTeaserPressed", false)
86− tryCompare(greeter, "rightTeaserPressed", false)
87− mousePress(greeter, data.posX, greeter.height - units.gu(1))
88− tryCompare(greeter, "leftTeaserPressed", data.leftPressed)
89− tryCompare(greeter, "rightTeaserPressed", data.rightPressed)
90− mouseRelease(greeter, data.posX, greeter.height - units.gu(1))
91− tryCompare(greeter, "leftTeaserPressed", false)
92− tryCompare(greeter, "rightTeaserPressed", false)
93− }
94−
95− function test_teaseLockedUnlocked_data() {
96− return [
97− {tag: "unlocked", locked: false, moved: false},
98− {tag: "locked", locked: true, moved: false},
99− {tag: "moved", locked: false, moved: true}
100− ];
101− }
102−
103− function test_teaseLockedUnlocked(data) {
104− tryCompare(greeter, "rightTeaserPressed", false);
105− tryCompare(greeter, "x", 0);
106− greeter.locked = data.locked;
107−
108− // simulate greeter being in the middle of a swipe
109− if (data.moved) {
110− greeter.x = -units.gu(4);
111− tryCompare(greeter, "x", -units.gu(4));
112− }
113−
114− mouseClick(greeter, greeter.width - units.gu(5), greeter.height - units.gu(1));
115− greeter.minX = 0; // This is needed because the transition actually makes x jump once before animating
116−
117− if (!data.moved) {
118− // Check if it has been moved over by 2 GUs. Give it a 2 pixel grace area
119− // because animation duration and teaseTimer are the same duration and
120− // might cause slight offsets
121− tryCompareFunction(function() { return greeter.minX <= -units.gu(2) + 2}, true);
122− } else {
123− // waiting 100ms to make sure nothing moves
124− wait(100);
125− compare(greeter.minX, 0, "Greeter teasing not disabled even though it's locked or moving.");
126− }
127−
128− // Restore position in case we moved it for the test
129− if (data.moved) {
130− greeter.x = 0;
131− }
132−
133− // Wait until we're back to 0
134− tryCompareFunction(function() { return greeter.x;}, 0);
91+ teaseSpy.clear()
92+ mouseClick(greeter, data.posX, greeter.height - units.gu(1))
93+ teaseSpy.wait()
94+ tryCompare(teaseSpy, "count", 1)
13595 }
13696
13797 function test_statsWelcomeScreen() {
tests/qmltests/Greeter/tst_Tablet.qml+36−8
@@ -54,6 +54,12 @@ Item {
5454 signalName: "selected"
5555 }
5656
57+ SignalSpy {
58+ id: teaseSpy
59+ target: greeter
60+ signalName: "tease"
61+ }
62+
5763 UT.UnityTestCase {
5864 name: "Greeter"
5965 when: windowShown
@@ -325,14 +331,36 @@ Item {
325331 }
326332
327333 function test_teasingArea(data) {
328− tryCompare(greeter, "leftTeaserPressed", false)
329− tryCompare(greeter, "rightTeaserPressed", false)
330− mousePress(greeter, data.posX, greeter.height - units.gu(1))
331− tryCompare(greeter, "leftTeaserPressed", data.leftPressed)
332− tryCompare(greeter, "rightTeaserPressed", data.rightPressed)
333− mouseRelease(greeter, data.posX, greeter.height - units.gu(1))
334− tryCompare(greeter, "leftTeaserPressed", false)
335− tryCompare(greeter, "rightTeaserPressed", false)
334+ teaseSpy.clear()
335+ mouseClick(greeter, data.posX, greeter.height - units.gu(1))
336+ teaseSpy.wait()
337+ tryCompare(teaseSpy, "count", 1)
338+ }
339+
340+ function test_teaseLockedUnlocked_data() {
341+ return [
342+ {tag: "unlocked", locked: false, narrow: false},
343+ {tag: "locked", locked: true, narrow: false},
344+ ];
345+ }
346+
347+ function test_teaseLockedUnlocked(data) {
348+ teaseSpy.clear()
349+ greeter.locked = data.locked;
350+
351+ mouseClick(greeter, greeter.width - units.gu(5), greeter.height - units.gu(1));
352+
353+ if (!data.locked || data.narrow) {
354+ teaseSpy.wait()
355+ tryCompare(teaseSpy, "count", 1);
356+ } else {
357+ // waiting 100ms to make sure nothing happens
358+ wait(100);
359+ compare(teaseSpy.count, 0, "Greeter teasing not disabled even though it's locked.");
360+ }
361+
362+ // Reset value
363+ greeter.locked = false;
336364 }
337365
338366 function test_dbus_set_active_entry() {
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+30−4
@@ -19,6 +19,7 @@ import QtTest 1.0
1919import Unity.Test 0.1 as UT
2020import QMenuModel 0.1
2121import Unity.Indicators 0.1 as Indicators
22+import Utils 0.1 as Utils
2223
2324Item {
2425 id: testView
@@ -42,8 +43,13 @@ Item {
4243 }
4344 }
4445
46+ Utils.GDateTimeFormatter {
47+ id: timeFormatter
48+ }
49+
4550 UT.UnityTestCase {
4651 name: "MenuItemFactory"
52+ when: windowShown
4753
4854 property QtObject menuData: QtObject {
4955 property string label: "root"
@@ -357,8 +363,10 @@ Item {
357363
358364 function test_create_alarmMenu_data() {
359365 return [
360− {label: "testLabel1", enabled: true, icon: "file:///testIcon1" },
361− {label: "testLabel2", enabled: false, icon: "file:///testIcon2" },
366+ {label: "testLabel1", enabled: true, icon: "file:///testIcon1", color: Qt.rgba(0, 0, 0, 0),
367+ time: new Date(2014, 04, 14).getTime()*1000, timeFormat: "%a %d %b %l:%M %p"},
368+ {label: "testLabel2", enabled: false, icon: "file:///testIcon2", color: Qt.rgba(1, 0, 0, 0),
369+ time: new Date(2015, 12, 31).getTime()*1000, timeFormat: "%A" },
362370 ];
363371 }
364372
@@ -367,6 +375,12 @@ Item {
367375 menuData.label = data.label;
368376 menuData.sensitive = data.enabled;
369377 menuData.icon = data.icon;
378+ menuData.ext = {
379+ 'xCanonicalTime': data.time,
380+ 'xCanonicalTimeFormat': data.timeFormat
381+ };
382+ timeFormatter.format = data.timeFormat;
383+ timeFormatter.time = data.time;
370384
371385 loader.data = menuData;
372386 loader.sourceComponent = factory.load(menuData);
@@ -375,13 +389,16 @@ Item {
375389
376390 compare(loader.item.text, data.label, "Label does not match data");
377391 compare(loader.item.iconSource, data.icon, "Icon does not match data");
392+ compare(loader.item.time, timeFormatter.timeString, "Time does not match data");
378393 compare(loader.item.enabled, data.enabled, "Enabled does not match data");
379394 }
380395
381396 function test_create_appointmentMenu_data() {
382397 return [
383− {label: "testLabel1", enabled: true, icon: "file:///testIcon1" },
384− {label: "testLabel2", enabled: false, icon: "file:///testIcon2" },
398+ {label: "testLabel1", enabled: true, icon: "file:///testIcon1", color: Qt.rgba(0, 0, 0, 0),
399+ time: new Date(2014, 04, 14).getTime()*1000, timeFormat: "%a %d %b %l:%M %p"},
400+ {label: "testLabel2", enabled: false, icon: "file:///testIcon2", color: Qt.rgba(1, 0, 0, 0),
401+ time: new Date(2015, 12, 31).getTime()*1000, timeFormat: "%A" },
385402 ];
386403 }
387404
@@ -390,6 +407,13 @@ Item {
390407 menuData.label = data.label;
391408 menuData.sensitive = data.enabled;
392409 menuData.icon = data.icon;
410+ menuData.ext = {
411+ 'xCanonicalColor': data.colour,
412+ 'xCanonicalTime': data.time,
413+ 'xCanonicalTimeFormat': data.timeFormat
414+ };
415+ timeFormatter.format = data.timeFormat;
416+ timeFormatter.time = data.time;
393417
394418 loader.data = menuData;
395419 loader.sourceComponent = factory.load(menuData);
@@ -398,6 +422,8 @@ Item {
398422
399423 compare(loader.item.text, data.label, "Label does not match data");
400424 compare(loader.item.iconSource, data.icon, "Icon does not match data");
425+ compare(loader.item.time, timeFormatter.timeString, "Time does not match data");
426+ compare(loader.item.eventColor, data.color, "Colour does not match data");
401427 compare(loader.item.enabled, data.enabled, "Enabled does not match data");
402428 }
403429
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/SideStage/tst_SideStage.qmldeleted−52
@@ -1,52 +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 Unity.Test 0.1 as UT
20−import ".."
21−import "../../../qml/SideStage"
22−import Ubuntu.Components 0.1
23−
24−UT.UnityTestCase {
25− name: "SideStage"
26−
27− SideStage {
28− id: sideStage
29− width: units.gu(30)
30− height: units.gu(30)
31− handleExpanded: sideStageRevealer.pressed
32− handleSizeCollapsed: units.gu(2)
33− handleSizeExpanded: units.gu(5)
34− }
35−
36− function test_handle_data() {
37− return [
38− {tag: "expanded", expanded: true},
39− {tag: "collapsed", expanded: false}
40− ]
41− }
42−
43− function test_handle(data) {
44− sideStage.handleExpanded = data.expanded
45−
46− if (data.expanded) {
47− tryCompare(findChild(sideStage, "sideStageHandle"), "width", sideStage.handleSizeExpanded);
48− } else {
49− tryCompare(findChild(sideStage, "sideStageHandle"), "width", sideStage.handleSizeCollapsed);
50− }
51− }
52−}
tests/qmltests/tst_Shell.qml+12−1
@@ -192,7 +192,7 @@ Item {
192192 verify(mainAppId != "");
193193 var mainApp = ApplicationManager.findApplication(mainAppId);
194194 verify(mainApp);
195− tryCompare(mainApp.state, ApplicationInfo.Running);
195+ tryCompare(mainApp, "state", ApplicationInfo.Running);
196196
197197 // Try to suspend while proximity is engaged...
198198 Powerd.displayPowerStateChange(Powerd.Off, Powerd.UseProximity);
@@ -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/scopetool.cpp+1
@@ -88,6 +88,7 @@ int main(int argc, char *argv[])
8888
8989 QUrl source(::qmlDirectory() + "ScopeTool.qml");
9090 prependImportPaths(view->engine(), ::overrideImportPaths());
91+ prependImportPaths(view->engine(), ::nonMirImportPaths());
9192 appendImportPaths(view->engine(), ::fallbackImportPaths());
9293
9394 view->setSource(source);
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