on xenial help sign in
Merge trunk.
commita4550c506013c96adaf00a5b5d53898936216c0c authorMichał Sawicz <michal.sawicz@canonical.com> dateWed 30 Apr 2014, 10:33 (12 years ago) parente0e51f4e1ea44b
42 files changed+498−354
CODING−4 cmake/modules/QmlTest.cmake+2−4 data/CMakeLists.txt+1−2 data/unity-scope-tool.desktop.in−8 data/unity8.conf+9−1 debian/changelog+33 debian/control+5−3 debian/unity-scope-tool.install−1 plugins/Unity/Indicators/qml/MenuItemFactory.qml+1−1 po/unity8.pot+7−3 qml/Components/FilterGrid.qml+37−24 qml/Dash/CardFilterGrid.qml+3−8 qml/Dash/DashRenderer.qml+3−3 qml/Dash/GenericScopeView.qml+13−15 qml/Dash/Previews/PreviewRatingInput.qml+2−2 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/Shell.qml+59−45 qml/graphics/dropshadow_left@20.png qml/Hud/graphics/dropshadow_right@20.png → qml/graphics/dropshadow_right@20.png tests/autopilot/unity8/shell/__init__.py+50−1 tests/autopilot/unity8/shell/tests/test_emulators.py+1−1 tests/autopilot/unity8/shell/tests/test_notifications.py+14−57 tests/autopilot/unity8/shell/tests/test_upstart.py+2−2 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+1 tests/plugins/Utils/CMakeLists.txt+2−1 tests/qmltests/CMakeLists.txt+1−1 tests/qmltests/Components/CMakeLists.txt+1 tests/qmltests/Components/tst_FilterGrid.qml+1−1 tests/qmltests/Dash/Previews/tst_PreviewRatingInput.qml+21−2 tests/qmltests/Dash/tst_GenericScopeView.qml+3−3 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/Panel/Indicators/tst_MenuItemFactory.qml+29−4
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
cmake/modules/QmlTest.cmake+2−4
@@ -59,11 +59,11 @@ macro(add_manual_qml_test SUBPATH COMPONENT_NAME)
5959endmacro(add_manual_qml_test)
6060
6161macro(add_qml_benchmark SUBPATH COMPONENT_NAME ITERATIONS)
62− add_qml_test_internal(${SUBPATH} ${COMPONENT_NAME} ${ITERATIONS})
62+ add_qml_test_internal(${SUBPATH} ${COMPONENT_NAME} ${ITERATIONS} ${ARGN})
6363endmacro(add_qml_benchmark)
6464
6565macro(add_qml_test SUBPATH COMPONENT_NAME)
66− add_qml_test_internal(${SUBPATH} ${COMPONENT_NAME} 0)
66+ add_qml_test_internal(${SUBPATH} ${COMPONENT_NAME} 0 ${ARGN})
6767endmacro(add_qml_test)
6868
6969macro(add_qml_test_internal SUBPATH COMPONENT_NAME ITERATIONS)
@@ -156,8 +156,6 @@ macro(add_binary_qml_test CLASS_NAME LD_PATH DEPS)
156156
157157 add_qmltest_target(xvfbtest${CLASS_NAME} "${xvfbtestCommand}" FALSE TRUE)
158158 add_dependencies(qmluitests xvfbtest${CLASS_NAME})
159−
160− add_manual_qml_test(. ${CLASS_NAME} IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
161159endmacro(add_binary_qml_test)
162160
163161macro(add_qmltest_target qmltest_TARGET qmltest_command qmltest_NO_TARGETS qmltest_NO_ADD_TEST)
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+9−1
@@ -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,7 +45,12 @@ pre-start script
4245 fi
4346
4447 initctl emit scope-ui-starting
45− initctl emit indicator-services-start
48+ initctl emit --no-wait indicator-services-start
4649end script
4750
4851exec ${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+33
@@ -1,3 +1,36 @@
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+
134unity8 (7.85+14.04.20140416-0ubuntu1) trusty; urgency=low
235
336 [ Albert Astals ]
debian/control+5−3
@@ -74,7 +74,7 @@ Provides: indicator-renderer,
7474Depends: gsettings-desktop-schemas,
7575 libcap2-bin,
7676 libglib2.0-bin,
77− libunity-mir1,
77+ libunity-mir1 (>= 0.4),
7878 qmenumodel-qml (>= 0.2.7),
7979 qml-module-qtquick-xmllistmodel,
8080 qtdeclarative5-gsettings1.0,
@@ -106,6 +106,8 @@ Depends: qtdeclarative5-ubuntu-thumbnailer0.1 | ubuntu-thumbnailer-impl,
106106 unity-scopes-impl-0,
107107 unity8-fake-env | unity-application-impl,
108108 ${misc:Depends},
109+Breaks: unity8 (<< 7.86),
110+Replaces: unity8 (<< 7.86),
109111Description: Unity 8 shell (common files)
110112 The Unity 8 shell is the primary user interface for Ubuntu devices.
111113 .
@@ -128,8 +130,8 @@ Depends: gir1.2-glib-2.0,
128130 python3-fixtures,
129131 python3-gi,
130132 ubuntu-ui-toolkit-autopilot,
131− unity8 (>= ${source:Version}),
132− unity8-fake-env (>= ${source:Version}),
133+ unity8 (= ${source:Version}),
134+ unity8-fake-env (= ${source:Version}),
133135 url-dispatcher-tools,
134136 ${misc:Depends},
135137 ${python3:Depends},
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
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/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/FilterGrid.qml+37−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
@@ -65,30 +60,48 @@ Item {
6560 readonly property alias pressDelay: iconTileGrid.pressDelay
6661 property alias highlightIndex: iconTileGrid.highlightIndex
6762 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+ }
6875
69− height: !filterAnimation.running ? childrenRect.height : height
76+ height: d.collapsed ? root.collapsedHeight : root.uncollapsedHeight
7077 clip: filterAnimation.running
7178
72− NumberAnimation {
73− property bool filterEndValue
74− id: filterAnimation
75− target: root
76− property: "height"
77− to: filterEndValue ? root.collapsedHeight : root.uncollapsedHeight
78− // Duration and easing here match the ListViewWithPageHeader::m_contentYAnimation
79− // otherwise since both animations can run at the same time you'll get
80− // some visual weirdness.
81− duration: 200
82− easing.type: Easing.InOutQuad
83− onStopped: {
84− 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+ }
8595 }
8696 }
8797
88− function startFilterAnimation(filter) {
89− filterAnimation.filterEndValue = filter
90− filterAnimation.start();
91− }
98+ function setFilter(filter, animate) {
99+ heightBehaviour.enabled = animate;
100+ d.collapsed = filter;
101+ if (!animate || !filter) {
102+ d.filter = filter;
103+ }
104+ }
92105
93106 ResponsiveGridView {
94107 id: iconTileGrid
@@ -105,7 +118,7 @@ Item {
105118
106119 model: LimitProxyModel {
107120 model: root.model
108− limit: (filter && !filterAnimation.running) ? rowsWhenCollapsed * iconTileGrid.columns : -1
121+ limit: d.filter ? rowsWhenCollapsed * iconTileGrid.columns : -1
109122 }
110123 }
111124}
qml/Dash/CardFilterGrid.qml+3−8
@@ -28,9 +28,10 @@ DashRenderer {
2828 verticalSpacing: units.gu(1)
2929 currentItem: filterGrid.currentItem
3030 height: filterGrid.height
31+ filtered: filterGrid.filtered
3132
32− function startFilterAnimation(filter) {
33− filterGrid.startFilterAnimation(filter)
33+ function setFilter(filter, animate) {
34+ filterGrid.setFilter(filter, animate)
3435 }
3536
3637 FilterGrid {
@@ -41,7 +42,6 @@ DashRenderer {
4142 delegateHeight: cardTool.cardHeight
4243 verticalSpacing: genericFilterGrid.verticalSpacing
4344 model: genericFilterGrid.model
44− filter: genericFilterGrid.filter
4545 collapsedRowCount: Math.min(2, cardTool && cardTool.template && cardTool.template["collapsed-rows"] || 2)
4646 delegateCreationBegin: genericFilterGrid.delegateCreationBegin
4747 delegateCreationEnd: genericFilterGrid.delegateCreationEnd
@@ -66,10 +66,5 @@ DashRenderer {
6666 onPressAndHold: genericFilterGrid.pressAndHold(index, card.y)
6767 }
6868 }
69−
70− onFilterChanged: {
71− genericFilterGrid.filter = filter
72− filter = Qt.binding(function() { return genericFilterGrid.filter })
73− }
7469 }
7570}
qml/Dash/DashRenderer.qml+3−3
@@ -20,8 +20,8 @@ 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
23+ // In case it can be expanded, is it filtered
24+ property bool filtered: true
2525
2626 property int collapsedHeight: height
2727
@@ -58,6 +58,6 @@ Item {
5858 /// @param itemY is y of the held delegate
5959 signal pressAndHold(int index, real itemY)
6060
61− function startFilterAnimation(filter) {
61+ function setFilter(filter, animate) {
6262 }
6363}
qml/Dash/GenericScopeView.qml+13−15
@@ -122,7 +122,7 @@ FocusScope {
122122 showDivider: false
123123
124124 readonly property bool expandable: rendererLoader.item ? rendererLoader.item.expandable : false
125− readonly property bool filtered: rendererLoader.item ? rendererLoader.item.filter : true
125+ readonly property bool filtered: rendererLoader.item ? rendererLoader.item.filtered : true
126126 readonly property string category: categoryId
127127 readonly property var item: rendererLoader.item
128128
@@ -168,9 +168,7 @@ FocusScope {
168168 item.objectName = Qt.binding(function() { return categoryId })
169169 if (item.expandable) {
170170 var shouldFilter = categoryId != categoryView.expandedCategoryId;
171− if (shouldFilter != item.filter) {
172− item.filter = shouldFilter;
173− }
171+ item.setFilter(shouldFilter, false /*animate*/);
174172 }
175173 updateDelegateCreationRange();
176174 item.cardTool = cardTool;
@@ -219,11 +217,8 @@ FocusScope {
219217 var shrinkingVisible = shouldFilter && y + item.collapsedHeight < categoryView.height;
220218 var growingVisible = !shouldFilter && y + height < categoryView.height;
221219 if (!previewListView.open || !shouldFilter) {
222− if (shrinkingVisible || growingVisible) {
223− item.startFilterAnimation(shouldFilter)
224− } else {
225− item.filter = shouldFilter;
226− }
220+ var animate = shrinkingVisible || growingVisible;
221+ item.setFilter(shouldFilter, animate)
227222 if (!shouldFilter && !previewListView.open) {
228223 categoryView.maximizeVisibleArea(index, item.uncollapsedHeight);
229224 }
@@ -238,12 +233,15 @@ FocusScope {
238233 }
239234
240235 function updateDelegateCreationRange() {
241− // Do not update the range if we are overshooting up or down, since we'll come back
242− // to the stable position and delete/create items without any reason
243− if (categoryView.contentY < categoryView.originY) {
244− return;
245− } else if (categoryView.contentHeight > categoryView.height && categoryView.contentY + categoryView.height > categoryView.contentHeight) {
246− 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+ }
247245 }
248246
249247 if (item && item.hasOwnProperty("delegateCreationBegin")) {
qml/Dash/Previews/PreviewRatingInput.qml+2−2
@@ -155,8 +155,8 @@ PreviewWidget {
155155 objectName: "submitButton"
156156
157157 readonly property bool readyToSubmit: {
158− if (reviewTextArea.text === "" ||
159− (widgetData["visible"] === "both" && rating.value < 0)) return false;
158+ if ((widgetData["required"] !== "review" && rating.value < 0) ||
159+ (widgetData["required"] !== "rating" && reviewTextArea.text === "")) return false;
160160 else return true;
161161 }
162162
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/Shell.qml+59−45
@@ -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 }
@@ -362,52 +358,66 @@ FocusScope {
362358 }
363359 }
364360
365− Greeter {
366− id: greeter
367− objectName: "greeter"
368−
369− available: true
370− hides: [launcher, panel.indicators, hud]
371− shown: true
372−
373− defaultBackground: shell.background
361+ Rectangle {
362+ anchors.fill: parent
363+ color: "black"
364+ opacity: greeterWrapper.showProgress * 0.8
365+ }
374366
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
375371 y: panel.panelHeight
376372 width: parent.width
377373 height: parent.height - panel.panelHeight
378374
379− dragHandleWidth: shell.edgeSize
375+ Behavior on x {SmoothedAnimation{velocity: 600}}
380376
381− onShownChanged: {
382− if (shown) {
383− lockscreen.reset();
384− // If there is only one user, we start authenticating with that one here.
385− // If there are more users, the Greeter will handle that
386− if (LightDM.Users.count == 1) {
387− 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();
388403 }
389− greeter.forceActiveFocus();
390404 }
391− }
392−
393− onUnlocked: greeter.hide()
394− onSelected: {
395− // Update launcher items for new user
396− var user = LightDM.Users.data(uid, LightDM.UserRoles.NameRole);
397− AccountsService.user = user;
398− LauncherModel.setUser(user);
399− }
400405
401− onLeftTeaserPressedChanged: {
402− if (leftTeaserPressed) {
403− 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);
404412 }
405− }
406413
407− Binding {
408− target: ApplicationManager
409− property: "suspended"
410− 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+ }
411421 }
412422 }
413423
@@ -538,7 +548,7 @@ FocusScope {
538548 anchors.bottom: parent.bottom
539549 width: parent.width
540550 dragAreaWidth: shell.edgeSize
541− available: (!greeter.shown || greeter.narrowMode) && edgeDemo.launcherEnabled
551+ available: edgeDemo.launcherEnabled
542552
543553 onShowDashHome: {
544554 if (edgeDemo.running)
@@ -553,6 +563,10 @@ FocusScope {
553563 launcher.hide();
554564 }
555565 }
566+ if (greeter.shown) {
567+ greeter.hideRight();
568+ launcher.hide();
569+ }
556570 }
557571 onDashSwipeChanged: if (dashSwipe && stages.shown) dash.setCurrentScope("clickscope", false, true)
558572 onLauncherApplicationSelected: {
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.

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/tests/test_emulators.py+1−1
@@ -196,7 +196,7 @@ class DashAppsEmulatorTestCase(DashBaseTestCase):
196196 category)
197197 grid = category_element.select_single('CardFilterGrid')
198198 filtergrid = grid.select_single('FilterGrid')
199− if (grid.filter):
199+ if (grid.filtered):
200200 return filtergrid.collapsedRowCount * filtergrid.columns
201201 else:
202202 return filtergrid.uncollapsedRowCount * filtergrid.columns
tests/autopilot/unity8/shell/tests/test_notifications.py+14−57
@@ -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(
@@ -354,7 +356,7 @@ class EphemeralNotificationsTests(NotificationsBase):
354356 )
355357 ]
356358
357− notification = self._create_ephemeral_notification(
359+ notification = shell.create_ephemeral_notification(
358360 summary,
359361 body,
360362 icon_path,
@@ -391,7 +393,7 @@ class EphemeralNotificationsTests(NotificationsBase):
391393 )
392394 ]
393395
394− notification = self._create_ephemeral_notification(
396+ notification = shell.create_ephemeral_notification(
395397 summary,
396398 None,
397399 None,
@@ -433,7 +435,7 @@ class EphemeralNotificationsTests(NotificationsBase):
433435 body_critical = 'Dude, this is so urgent you have no idea :)'
434436 icon_path_critical = self._get_icon_path('avatars/anna_olsson.png')
435437
436− notification_normal = self._create_ephemeral_notification(
438+ notification_normal = shell.create_ephemeral_notification(
437439 summary_normal,
438440 body_normal,
439441 icon_path_normal,
@@ -441,7 +443,7 @@ class EphemeralNotificationsTests(NotificationsBase):
441443 )
442444 notification_normal.show()
443445
444− notification_low = self._create_ephemeral_notification(
446+ notification_low = shell.create_ephemeral_notification(
445447 summary_low,
446448 body_low,
447449 icon_path_low,
@@ -449,7 +451,7 @@ class EphemeralNotificationsTests(NotificationsBase):
449451 )
450452 notification_low.show()
451453
452− notification_critical = self._create_ephemeral_notification(
454+ notification_critical = shell.create_ephemeral_notification(
453455 summary_critical,
454456 body_critical,
455457 icon_path_critical,
@@ -510,7 +512,7 @@ class EphemeralNotificationsTests(NotificationsBase):
510512 summary = 'Summary-Body'
511513 body = 'This is a superfluous notification'
512514
513− notification = self._create_ephemeral_notification(summary, body)
515+ notification = shell.create_ephemeral_notification(summary, body)
514516 notification.show()
515517
516518 notification = notify_list.wait_select_single(
@@ -534,7 +536,7 @@ class EphemeralNotificationsTests(NotificationsBase):
534536
535537 summary = 'Summary-Only'
536538
537− notification = self._create_ephemeral_notification(summary)
539+ notification = shell.create_ephemeral_notification(summary)
538540 notification.show()
539541
540542 notification = notify_list.wait_select_single(
@@ -554,7 +556,7 @@ class EphemeralNotificationsTests(NotificationsBase):
554556 body = 'This is the original content of this notification-bubble.'
555557 icon_path = self._get_icon_path('avatars/funky.png')
556558
557− notification = self._create_ephemeral_notification(
559+ notification = shell.create_ephemeral_notification(
558560 summary,
559561 body,
560562 icon_path
@@ -594,7 +596,7 @@ class EphemeralNotificationsTests(NotificationsBase):
594596 icon_path = self._get_icon_path('avatars/anna_olsson.png')
595597 hint_icon = self._get_icon_path('applicationIcons/phone-app.png')
596598
597− notification = self._create_ephemeral_notification(
599+ notification = shell.create_ephemeral_notification(
598600 summary,
599601 body,
600602 icon_path
@@ -638,7 +640,7 @@ class EphemeralNotificationsTests(NotificationsBase):
638640 body = 'Hey Bro Coly!'
639641 icon_path = self._get_icon_path('avatars/amanda.png')
640642 body_sum = body
641− notification = self._create_ephemeral_notification(
643+ notification = shell.create_ephemeral_notification(
642644 summary,
643645 body,
644646 icon_path,
@@ -674,7 +676,7 @@ class EphemeralNotificationsTests(NotificationsBase):
674676 for new_body in bodies:
675677 body = new_body
676678 body_sum += '\n' + body
677− notification = self._create_ephemeral_notification(
679+ notification = shell.create_ephemeral_notification(
678680 summary,
679681 body,
680682 icon_path,
@@ -695,48 +697,3 @@ class EphemeralNotificationsTests(NotificationsBase):
695697 False,
696698 1.0
697699 )
698−
699− def _create_ephemeral_notification(
700− self,
701− summary="",
702− body="",
703− icon=None,
704− hints=[],
705− urgency="NORMAL"
706− ):
707− """Create an ephemeral (non-interactive) notification
708−
709− :param summary: Summary text for the notification
710− :param body: Body text to display in the notification
711− :param icon: Path string to the icon to use
712− :param hint_strings: List of tuples containing the 'name' and value
713− for setting the hint strings for the notification
714− :param urgency: Urgency string for the noticiation, either: 'LOW',
715− 'NORMAL', 'CRITICAL'
716−
717− """
718− logger.info(
719− "Creating ephemeral: summary(%s), body(%s), urgency(%r) "
720− "and Icon(%s)",
721− summary,
722− body,
723− urgency,
724− icon
725− )
726−
727− n = Notify.Notification.new(summary, body, icon)
728−
729− for hint in hints:
730− key, value = hint
731− n.set_hint_string(key, value)
732− logger.info("Adding hint to notification: (%s, %s)", key, value)
733− n.set_urgency(self._get_urgency(urgency))
734−
735− return n
736−
737− def _get_urgency(self, urgency):
738− """Translates urgency string to enum."""
739− _urgency_enums = {'LOW': Notify.Urgency.LOW,
740− 'NORMAL': Notify.Urgency.NORMAL,
741− 'CRITICAL': Notify.Urgency.CRITICAL}
742− return _urgency_enums.get(urgency.upper())
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/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+1
@@ -20,6 +20,7 @@ macro(add_gesture_ui_test CLASSNAME)
2020 target_link_libraries(${CLASSNAME}TestExec UbuntuGestureQml)
2121
2222 add_binary_qml_test(${CLASSNAME} ${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures UbuntuGesturesTestQmlFiles)
23+ add_manual_qml_test(. ${CLASSNAME} IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
2324endmacro(add_gesture_ui_test)
2425
2526macro(add_gesture_test CLASSNAME)
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+1−1
@@ -16,7 +16,7 @@ add_qml_test(utils/Unity/Test UnityTest)
1616
1717set(qmltest_DEFAULT_TARGETS qmluitests)
1818set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
19−set(qmltest_DEFAULT_PROPERTIES "")
19+set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "LC_ALL=C")
2020
2121add_qml_test(. Shell IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks
2222 ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
tests/qmltests/Components/CMakeLists.txt+1
@@ -11,6 +11,7 @@ qt5_use_modules(DragHandleTestExec Test Core Qml Gui Quick)
1111target_link_libraries(DragHandleTestExec UbuntuGestureQml)
1212
1313add_binary_qml_test(DragHandle ${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures "")
14+add_manual_qml_test(. DragHandle IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
1415
1516add_definitions(-DUBUNTU_GESTURES_PLUGIN_DIR="${CMAKE_BINARY_DIR}/plugins")
1617add_definitions(-DTEST_DIR="${CMAKE_CURRENT_LIST_DIR}")
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/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/tst_GenericScopeView.qml+3−3
@@ -140,7 +140,7 @@ Item {
140140 mouseClick(header, header.width / 2, header.height / 2);
141141 tryCompareFunction(function() { middleHeight = category.height; return category.height > initialHeight; }, true);
142142 tryCompare(category, "filtered", false);
143− verify(category.height > middleHeight);
143+ tryCompareFunction(function() { return category.height > middleHeight; }, true);
144144
145145 mouseClick(header, header.width / 2, header.height / 2);
146146 verify(category.expandable);
@@ -165,7 +165,7 @@ Item {
165165
166166 mouseClick(header2, header2.width / 2, header2.height / 2);
167167 tryCompare(category2, "filtered", false);
168− tryCompare(category2FilterGrid, "filter", false);
168+ tryCompare(category2FilterGrid, "filtered", false);
169169
170170 categoryListView.positionAtBeginning();
171171
@@ -182,7 +182,7 @@ Item {
182182 mouseClick(header0, header0.width / 2, header0.height / 2);
183183 tryCompare(category0, "filtered", false);
184184 tryCompare(category2, "filtered", true);
185− tryCompare(category2FilterGrid, "filter", true);
185+ tryCompare(category2FilterGrid, "filtered", true);
186186 mouseClick(header0, header0.width / 2, header0.height / 2);
187187 tryCompare(category0, "filtered", true);
188188 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/Panel/Indicators/tst_MenuItemFactory.qml+29−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,6 +43,10 @@ Item {
4243 }
4344 }
4445
46+ Utils.GDateTimeFormatter {
47+ id: timeFormatter
48+ }
49+
4550 UT.UnityTestCase {
4651 name: "MenuItemFactory"
4752 when: windowShown
@@ -358,8 +363,10 @@ Item {
358363
359364 function test_create_alarmMenu_data() {
360365 return [
361− {label: "testLabel1", enabled: true, icon: "file:///testIcon1" },
362− {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" },
363370 ];
364371 }
365372
@@ -368,6 +375,12 @@ Item {
368375 menuData.label = data.label;
369376 menuData.sensitive = data.enabled;
370377 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;
371384
372385 loader.data = menuData;
373386 loader.sourceComponent = factory.load(menuData);
@@ -376,13 +389,16 @@ Item {
376389
377390 compare(loader.item.text, data.label, "Label does not match data");
378391 compare(loader.item.iconSource, data.icon, "Icon does not match data");
392+ compare(loader.item.time, timeFormatter.timeString, "Time does not match data");
379393 compare(loader.item.enabled, data.enabled, "Enabled does not match data");
380394 }
381395
382396 function test_create_appointmentMenu_data() {
383397 return [
384− {label: "testLabel1", enabled: true, icon: "file:///testIcon1" },
385− {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" },
386402 ];
387403 }
388404
@@ -391,6 +407,13 @@ Item {
391407 menuData.label = data.label;
392408 menuData.sensitive = data.enabled;
393409 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;
394417
395418 loader.data = menuData;
396419 loader.sourceComponent = factory.load(menuData);
@@ -399,6 +422,8 @@ Item {
399422
400423 compare(loader.item.text, data.label, "Label does not match data");
401424 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");
402427 compare(loader.item.enabled, data.enabled, "Enabled does not match data");
403428 }
404429