Merge lp:~zsombi/ubuntu-ui-toolkit/pickerProblems into lp:ubuntu-ui-toolkit/staging

Proposed by Zsombor Egri on 2015-09-23
Status: Merged
Approved by: Christian Dywan on 2015-09-25
Approved revision: 1655
Merged at revision: 1651
Proposed branch: lp:~zsombi/ubuntu-ui-toolkit/pickerProblems
Merge into: lp:ubuntu-ui-toolkit/staging
Diff against target: 1328 lines (+1049/-23)
14 files modified
components.api (+83/-0)
examples/ubuntu-ui-toolkit-gallery/Pickers.qml (+28/-1)
src/Ubuntu/Components/Pickers/1.2/Picker.qml (+5/-4)
src/Ubuntu/Components/Pickers/1.2/PickerDelegate.qml (+0/-3)
src/Ubuntu/Components/Pickers/1.3/Picker.qml (+11/-5)
src/Ubuntu/Components/Pickers/1.3/PickerDelegate.qml (+1/-3)
src/Ubuntu/Components/Themes/Ambiance/1.2/PickerDelegateStyle.qml (+3/-0)
src/Ubuntu/Components/Themes/Ambiance/1.2/PickerStyle.qml (+3/-1)
src/Ubuntu/Components/Themes/Ambiance/1.3/PickerDelegateStyle.qml (+3/-0)
src/Ubuntu/Components/Themes/Ambiance/1.3/PickerStyle.qml (+3/-1)
tests/qmlapicheck.sh (+1/-1)
tests/resources/pickers/PickerTest.qml (+5/-4)
tests/unit_x11/tst_components/tst_datepicker13.qml (+569/-0)
tests/unit_x11/tst_components/tst_picker13.qml (+334/-0)
To merge this branch: bzr merge lp:~zsombi/ubuntu-ui-toolkit/pickerProblems
Reviewer Review Type Date Requested Status
Christian Dywan 2015-09-23 Approve on 2015-09-25
PS Jenkins bot continuous-integration Approve on 2015-09-24
Nekhelesh Ramananthan (community) Needs Information on 2015-09-23
Review via email: mp+272062@code.launchpad.net

Commit Message

Fix Picker's pathItemCount binding loops in 1.3.

To post a comment you must log in.
Nekhelesh Ramananthan (nik90) wrote :

This issue is present even with QtQuick 2.4 and U.C 1.2. Any chance we could see it backported?

review: Needs Information
Christian Dywan (kalikiana) wrote :

First off, this does fix Reminders, so far so good.

What's the use case for the new API? It doesn't affect either of the use cases where the binding loops are causing pickers to be empty.
I do second Nekhelesh, the problem seems to be identical on 1.2, including Reminders which only recently switched to 1.3 and did not behave any different wrt this issue.

review: Needs Information
Zsombor Egri (zsombi) wrote :

> This issue is present even with QtQuick 2.4 and U.C 1.2. Any chance we could
> see it back ported?

Unfortunately to solve it we would need to change the API. PathView behaviour has changed (no longer loads all the items from the model, only the visible ones) therefore the calculation of the items visible in the view cannot be done using the created delegate size. This is a limitation we must live with in 1.2 :(

Zsombor Egri (zsombi) wrote :

> First off, this does fix Reminders, so far so good.
>
> What's the use case for the new API? It doesn't affect either of the use cases
> where the binding loops are causing pickers to be empty.

The binding loop doesn't cause DatePicker to be empty, but causes the Picker's current item not being entered sometimes. I managed to reproduce it 2 from 10 runs of the gallery. This MR is mostly there to rule out - and finally get rid of - the binding loops causing DatePicker to be hidden when the page is loaded asynchronously. The API brings the PathView pathItemCount calculation to be binding-loop free (see the comment I had for Nekhelesh why).

> I do second Nekhelesh, the problem seems to be identical on 1.2, including
> Reminders which only recently switched to 1.3 and did not behave any different
> wrt this issue.

Yes, because the binding loop has been brought in by us switching to Qt 5.4. We have this binding loop since then, when PathView no longer loads all the items from the model but creates only the ones which are specified by the pathItemCount. The property value has been calculated based on an item's height, and the number of items created depends on the pathItemCount. Thus the binding loop.

What we could do in 1.2 is to bring the same property as private, then the public API doesn't change and we can get rid of the binding loop. I could live with that :)

Christian Dywan (kalikiana) wrote :

For the record, Zsombor and I discussed 1.2 and decided we can have it without the public API addition, and for 1.3 we should have at least one example because right now we're not really showing off or testing custom delegates.

1651. By Zsombor Egri on 2015-09-24

change ported to 1.2

1652. By Zsombor Egri on 2015-09-24

gallery extended with custom delegate pickers

1653. By Zsombor Egri on 2015-09-24

staging sync

1654. By Zsombor Egri on 2015-09-24

adding missing Pickers API

1655. By Zsombor Egri on 2015-09-24

fixing inheritance in documentation

Nekhelesh Ramananthan (nik90) wrote :

Thnx a lot for backporting the fix to 1.2. The binding loop issue didnt cause any visual issues for the clock app, but was filling the clock app logs.

Christian Dywan (kalikiana) wrote :

Very cute custom delegate examples. Love it!

Also, I couldn't believe my eyes when I saw the .api additions. It didn't even occur to me that the namespace was absent from the script. Nice catch!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'components.api'
2--- components.api 2015-09-24 05:10:12 +0000
3+++ components.api 2015-09-24 20:06:01 +0000
4@@ -272,6 +272,34 @@
5 property url source
6 property QSizeF sourceSize
7 readonly property int status
8+Ubuntu.Components.Pickers.DatePicker 1.0 0.1: StyledItem
9+ property QDateTime date
10+ readonly property int day
11+ readonly property int hours
12+ property var locale
13+ property QDateTime maximum
14+ property QDateTime minimum
15+ readonly property int minutes
16+ property string mode
17+ readonly property int month
18+ readonly property bool moving
19+ readonly property int seconds
20+ readonly property int week
21+ readonly property int year
22+Ubuntu.Components.Pickers.DatePicker 1.3: StyledItem
23+ property QDateTime date
24+ readonly property int day
25+ readonly property int hours
26+ property var locale
27+ property QDateTime maximum
28+ property QDateTime minimum
29+ readonly property int minutes
30+ property string mode
31+ readonly property int month
32+ readonly property bool moving
33+ readonly property int seconds
34+ readonly property int week
35+ readonly property int year
36 Ubuntu.Components.DateUtils 0.1 1.0 1.3
37 Ubuntu.Components.Popups.DefaultSheet 1.0 0.1: SheetBase
38 property bool doneButton
39@@ -281,6 +309,36 @@
40 property bool doneButton
41 signal closeClicked()
42 signal doneClicked()
43+Ubuntu.Components.Pickers.Dialer 1.0 0.1: StyledItem
44+ readonly property QtObject centerContent
45+ readonly property Item centerItem
46+ property double handSpace
47+ readonly property var hands
48+ property double maximumValue
49+ signal handUpdated(var hand)
50+ property double minimumValue
51+ property double size
52+Ubuntu.Components.Pickers.Dialer 1.3: StyledItem
53+ readonly property QtObject centerContent
54+ readonly property Item centerItem
55+ property double handSpace
56+ readonly property var hands
57+ property double maximumValue
58+ signal handUpdated(var hand)
59+ property double minimumValue
60+ property double size
61+Ubuntu.Components.Pickers.DialerHand 1.0 0.1: StyledItem
62+ readonly property Dialer dialer
63+ property DialerHandGroup hand
64+ readonly property int index
65+ default readonly property QtObject overlay
66+ property double value
67+Ubuntu.Components.Pickers.DialerHand 1.3: StyledItem
68+ readonly property Dialer dialer
69+ property DialerHandGroup hand
70+ readonly property int index
71+ default readonly property QtObject overlay
72+ property double value
73 Ubuntu.Components.Popups.Dialog 1.0 0.1: PopupBase
74 property Item caller
75 property double callerMargin
76@@ -741,6 +799,31 @@
77 property double triggerSize
78 Ubuntu.PerformanceMetrics.PerformanceOverlay 1.0 0.1: Item
79 property bool active
80+Ubuntu.Components.Pickers.Picker 1.0 0.1: StyledItem
81+ property bool circular
82+ property Component delegate
83+ property bool live
84+ function var positionViewAtIndex(var index)
85+ property var model
86+ readonly property bool moving
87+ property int selectedIndex
88+Ubuntu.Components.Pickers.Picker 1.3: StyledItem
89+ property bool circular
90+ property Component delegate
91+ property double itemHeight
92+ property bool live
93+ function var positionViewAtIndex(var index)
94+ property var model
95+ readonly property bool moving
96+ property int selectedIndex
97+Ubuntu.Components.Pickers.PickerDelegate 1.0 0.1: AbstractButton
98+ readonly property Picker picker
99+Ubuntu.Components.Pickers.PickerDelegate 1.3: AbstractButton
100+ readonly property Picker picker
101+Ubuntu.Components.Pickers.PickerPanel 1.0 0.1: Object singleton
102+ function var openDatePicker(var caller, var property, var mode)
103+Ubuntu.Components.Pickers.PickerPanel 1.3: Object singleton
104+ function var openDatePicker(var caller, var property, var mode)
105 Ubuntu.Components.Popups.Popover 1.0 0.1: PopupBase
106 property bool autoClose
107 property Item caller
108
109=== modified file 'examples/ubuntu-ui-toolkit-gallery/Pickers.qml'
110--- examples/ubuntu-ui-toolkit-gallery/Pickers.qml 2015-04-29 08:39:19 +0000
111+++ examples/ubuntu-ui-toolkit-gallery/Pickers.qml 2015-09-24 20:06:01 +0000
112@@ -14,13 +14,15 @@
113 * along with this program. If not, see <http://www.gnu.org/licenses/>.
114 */
115
116-import QtQuick 2.0
117+import QtQuick 2.4
118 import Ubuntu.Components 1.3
119 import Ubuntu.Components.Pickers 1.3
120
121 Template {
122 objectName: "pickersTemplate"
123 id: root
124+
125+ property var stringListModel: ["starred", "media-record", "like", "language-chooser", "go-home", "email", "contact-group", "notification", "active-call"]
126 TemplateSection {
127 className: "Picker"
128 documentation: "qml-ubuntu-components-pickers-picker.html"
129@@ -42,6 +44,19 @@
130
131 onSelectedIndexChanged: print("index=" + selectedIndex)
132 }
133+ Picker {
134+ circular: false
135+ model: stringListModel
136+ itemHeight: units.gu(3)
137+ delegate: PickerDelegate {
138+ Icon {
139+ anchors.centerIn: parent
140+ name: modelData
141+ width: units.gu(2)
142+ height: units.gu(2)
143+ }
144+ }
145+ }
146 }
147
148 TemplateRow {
149@@ -67,6 +82,18 @@
150 selectedIndex = 3;
151 }
152 }
153+ Picker {
154+ model: stringListModel
155+ itemHeight: units.gu(3)
156+ delegate: PickerDelegate {
157+ Icon {
158+ anchors.centerIn: parent
159+ name: modelData
160+ width: units.gu(2)
161+ height: units.gu(2)
162+ }
163+ }
164+ }
165 }
166
167 TemplateRow {
168
169=== modified file 'src/Ubuntu/Components/Pickers/1.2/Picker.qml'
170--- src/Ubuntu/Components/Pickers/1.2/Picker.qml 2015-04-30 08:32:44 +0000
171+++ src/Ubuntu/Components/Pickers/1.2/Picker.qml 2015-09-24 20:06:01 +0000
172@@ -155,8 +155,6 @@
173 selectedIndex = loader.item.currentIndex;
174 }
175
176- implicitWidth: units.gu(8)
177- implicitHeight: units.gu(20)
178 activeFocusOnPress: true
179
180 style: Theme.createStyleComponent("PickerStyle.qml", picker)
181@@ -164,6 +162,9 @@
182 /*! \internal */
183 property int __clickedIndex: -1
184
185+ /*! \internal - required by bug1419667 */
186+ property real __itemHeight: units.gu(4)
187+
188 // bind style instance's view property to the Loader's item
189 Binding {
190 target: __styleInstance
191@@ -302,11 +303,11 @@
192 preferredHighlightBegin: 0.5
193 preferredHighlightEnd: 0.5
194
195- pathItemCount: pView.height / (pView.currentItem ? pView.currentItem.height : 1) + 1
196+ pathItemCount: pView.height / picker.__itemHeight
197 snapMode: PathView.SnapToItem
198 flickDeceleration: 100
199
200- property int contentHeight: pathItemCount * (pView.currentItem ? pView.currentItem.height : 1)
201+ property int contentHeight: pathItemCount * picker.__itemHeight
202 path: Path {
203 startX: pView.width / 2
204 startY: -(pView.contentHeight - pView.height) / 2
205
206=== modified file 'src/Ubuntu/Components/Pickers/1.2/PickerDelegate.qml'
207--- src/Ubuntu/Components/Pickers/1.2/PickerDelegate.qml 2015-04-30 08:32:44 +0000
208+++ src/Ubuntu/Components/Pickers/1.2/PickerDelegate.qml 2015-09-24 20:06:01 +0000
209@@ -37,9 +37,6 @@
210 */
211 readonly property alias picker: internal.picker
212
213- implicitHeight: units.gu(4)
214- implicitWidth: picker ? internal.itemList.width : 0
215-
216 /*! \internal */
217 onClicked: {
218 if (internal.itemList.currentIndex === index) return;
219
220=== modified file 'src/Ubuntu/Components/Pickers/1.3/Picker.qml'
221--- src/Ubuntu/Components/Pickers/1.3/Picker.qml 2015-05-22 13:54:38 +0000
222+++ src/Ubuntu/Components/Pickers/1.3/Picker.qml 2015-09-24 20:06:01 +0000
223@@ -21,6 +21,7 @@
224 \qmltype Picker
225 \inqmlmodule Ubuntu.Components.Pickers 1.0
226 \ingroup ubuntu-pickers
227+ \inherits StyledItem
228 \brief Picker is a slot-machine style value selection component.
229
230 The Picker lists the elements specified by the \l model using the \l delegate
231@@ -80,7 +81,6 @@
232 \list
233 \li [1] Circular picker does not react on touch generated flicks (on touch
234 enabled devices) when nested into a Flickable -
235- \l {https://bugreports.qt-project.org/browse/QTBUG-13690} and
236 \l {https://bugreports.qt-project.org/browse/QTBUG-30840}
237 \li [2] Circular picker sets \l selectedIndex to 0 when the model is cleared,
238 contrary to linear one, which sets it to -1 -
239@@ -129,6 +129,14 @@
240 readonly property bool moving: (loader.item ? loader.item.moving : false) || movingPoll.indexChanging
241
242 /*!
243+ \since Ubuntu.Components.Pickers 1.3
244+ The property specifies the defautl height of the PickerDelegates. It is
245+ recommended to change the delegate height through this property rather than
246+ changing it from the delegate itself.
247+ */
248+ property real itemHeight: units.gu(4)
249+
250+ /*!
251 The function positions the picker's view to the given index without animating
252 the view. The component must be ready when calling the function, e.g. to make
253 sure the Picker shows up at the given index, do the following:
254@@ -155,8 +163,6 @@
255 selectedIndex = loader.item.currentIndex;
256 }
257
258- implicitWidth: units.gu(8)
259- implicitHeight: units.gu(20)
260 activeFocusOnPress: true
261
262 theme.version: Ubuntu.toolkitVersion
263@@ -303,11 +309,11 @@
264 preferredHighlightBegin: 0.5
265 preferredHighlightEnd: 0.5
266
267- pathItemCount: pView.height / (pView.currentItem ? pView.currentItem.height : 1) + 1
268+ pathItemCount: pView.height / picker.itemHeight
269 snapMode: PathView.SnapToItem
270 flickDeceleration: 100
271
272- property int contentHeight: pathItemCount * (pView.currentItem ? pView.currentItem.height : 1)
273+ property int contentHeight: pathItemCount * picker.itemHeight
274 path: Path {
275 startX: pView.width / 2
276 startY: -(pView.contentHeight - pView.height) / 2
277
278=== modified file 'src/Ubuntu/Components/Pickers/1.3/PickerDelegate.qml'
279--- src/Ubuntu/Components/Pickers/1.3/PickerDelegate.qml 2015-05-22 13:54:38 +0000
280+++ src/Ubuntu/Components/Pickers/1.3/PickerDelegate.qml 2015-09-24 20:06:01 +0000
281@@ -21,6 +21,7 @@
282 \qmltype PickerDelegate
283 \inqmlmodule Ubuntu.Components.Pickers 1.0
284 \ingroup ubuntu-pickers
285+ \inherits AbstractButton
286 \brief PickerDelegate component serves as base for Picker delegates.
287
288 PickerDelegate is a holder component for delegates used in a Picker element.
289@@ -37,9 +38,6 @@
290 */
291 readonly property alias picker: internal.picker
292
293- implicitHeight: units.gu(4)
294- implicitWidth: picker ? internal.itemList.width : 0
295-
296 /*! \internal */
297 onClicked: {
298 if (internal.itemList.currentIndex === index) return;
299
300=== modified file 'src/Ubuntu/Components/Themes/Ambiance/1.2/PickerDelegateStyle.qml'
301--- src/Ubuntu/Components/Themes/Ambiance/1.2/PickerDelegateStyle.qml 2015-04-24 14:43:08 +0000
302+++ src/Ubuntu/Components/Themes/Ambiance/1.2/PickerDelegateStyle.qml 2015-09-24 20:06:01 +0000
303@@ -27,6 +27,9 @@
304 property Item picker: styledItem.picker
305 property Item highlightItem: itemList.highlightItem
306
307+ implicitHeight: picker.__itemHeight
308+ implicitWidth: itemList.width
309+
310 Binding {
311 target: styledItem
312 when: fadingEnabled
313
314=== modified file 'src/Ubuntu/Components/Themes/Ambiance/1.2/PickerStyle.qml'
315--- src/Ubuntu/Components/Themes/Ambiance/1.2/PickerStyle.qml 2015-04-24 14:43:08 +0000
316+++ src/Ubuntu/Components/Themes/Ambiance/1.2/PickerStyle.qml 2015-09-24 20:06:01 +0000
317@@ -40,7 +40,7 @@
318 /*!
319 Thickness of the highlight component
320 */
321- property real highlightThickness: units.gu(5)
322+ property real highlightThickness: styledItem.__itemHeight + units.gu(1)
323
324 /*!
325 The content holder exposed to the Picker so tumbler list can be reparented to it.
326@@ -54,6 +54,8 @@
327 property Item view: Item{}
328
329 anchors.fill: parent
330+ implicitWidth: units.gu(8)
331+ implicitHeight: units.gu(20)
332
333 // frame
334 UbuntuShape {
335
336=== modified file 'src/Ubuntu/Components/Themes/Ambiance/1.3/PickerDelegateStyle.qml'
337--- src/Ubuntu/Components/Themes/Ambiance/1.3/PickerDelegateStyle.qml 2015-04-25 07:36:13 +0000
338+++ src/Ubuntu/Components/Themes/Ambiance/1.3/PickerDelegateStyle.qml 2015-09-24 20:06:01 +0000
339@@ -27,6 +27,9 @@
340 property Item picker: styledItem.picker
341 property Item highlightItem: itemList.highlightItem
342
343+ implicitHeight: picker.itemHeight
344+ implicitWidth: itemList.width
345+
346 Binding {
347 target: styledItem
348 when: fadingEnabled
349
350=== modified file 'src/Ubuntu/Components/Themes/Ambiance/1.3/PickerStyle.qml'
351--- src/Ubuntu/Components/Themes/Ambiance/1.3/PickerStyle.qml 2015-07-22 13:14:43 +0000
352+++ src/Ubuntu/Components/Themes/Ambiance/1.3/PickerStyle.qml 2015-09-24 20:06:01 +0000
353@@ -40,7 +40,7 @@
354 /*!
355 Thickness of the highlight component
356 */
357- property real highlightThickness: units.gu(5)
358+ property real highlightThickness: styledItem.itemHeight + units.gu(1)
359
360 /*!
361 The content holder exposed to the Picker so tumbler list can be reparented to it.
362@@ -54,6 +54,8 @@
363 property Item view: Item{}
364
365 anchors.fill: parent
366+ implicitWidth: units.gu(8)
367+ implicitHeight: units.gu(20)
368
369 // frame
370 UbuntuShape {
371
372=== modified file 'tests/qmlapicheck.sh'
373--- tests/qmlapicheck.sh 2015-07-24 08:02:46 +0000
374+++ tests/qmlapicheck.sh 2015-09-24 20:06:01 +0000
375@@ -23,7 +23,7 @@
376 exit 1
377 fi
378
379-CPP="Ubuntu.Components Ubuntu.Components.ListItems Ubuntu.Components.Popups Ubuntu.Components.Styles Ubuntu.Components.Themes Ubuntu.Layouts Ubuntu.PerformanceMetrics Ubuntu.Test"
380+CPP="Ubuntu.Components Ubuntu.Components.ListItems Ubuntu.Components.Popups Ubuntu.Components.Pickers Ubuntu.Components.Styles Ubuntu.Components.Themes Ubuntu.Layouts Ubuntu.PerformanceMetrics Ubuntu.Test"
381 echo Dumping QML API of C++ components
382 test -s $BUILD_DIR/components.api.new && rm $BUILD_DIR/components.api.new
383 env ALARM_BACKEND=memory QML2_IMPORT_PATH=$BUILD_DIR/qml \
384
385=== modified file 'tests/resources/pickers/PickerTest.qml'
386--- tests/resources/pickers/PickerTest.qml 2015-03-03 13:20:06 +0000
387+++ tests/resources/pickers/PickerTest.qml 2015-09-24 20:06:01 +0000
388@@ -1,5 +1,5 @@
389 /*
390- * Copyright 2012 Canonical Ltd.
391+ * Copyright 2015 Canonical Ltd.
392 *
393 * This program is free software; you can redistribute it and/or modify
394 * it under the terms of the GNU Lesser General Public License as published by
395@@ -14,9 +14,9 @@
396 * along with this program. If not, see <http://www.gnu.org/licenses/>.
397 */
398
399-import QtQuick 2.0
400-import Ubuntu.Components 1.1
401-import Ubuntu.Components.Pickers 1.0
402+import QtQuick 2.4
403+import Ubuntu.Components 1.3
404+import Ubuntu.Components.Pickers 1.3
405
406 MainView {
407 width: units.gu(40)
408@@ -94,6 +94,7 @@
409 model: pickerModel
410 circular: false
411 live: true
412+ itemHeight: units.gu(10)
413 delegate: PickerDelegate {
414 Label {
415 anchors.fill: parent
416
417=== added file 'tests/unit_x11/tst_components/tst_datepicker13.qml'
418--- tests/unit_x11/tst_components/tst_datepicker13.qml 1970-01-01 00:00:00 +0000
419+++ tests/unit_x11/tst_components/tst_datepicker13.qml 2015-09-24 20:06:01 +0000
420@@ -0,0 +1,569 @@
421+/*
422+ * Copyright 2015 Canonical Ltd.
423+ *
424+ * This program is free software; you can redistribute it and/or modify
425+ * it under the terms of the GNU Lesser General Public License as published by
426+ * the Free Software Foundation; version 3.
427+ *
428+ * This program is distributed in the hope that it will be useful,
429+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
430+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
431+ * GNU Lesser General Public License for more details.
432+ *
433+ * You should have received a copy of the GNU Lesser General Public License
434+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
435+ */
436+
437+import QtQuick 2.4
438+import QtTest 1.0
439+import Ubuntu.Test 1.0
440+import Ubuntu.Components 1.3
441+import Ubuntu.Components.Pickers 1.3
442+
443+Item {
444+ id: testSuite
445+ width: units.gu(40)
446+ height: units.gu(71)
447+
448+ Component {
449+ id: testComponent
450+ DatePicker {
451+ width: testSuite.width
452+ }
453+ }
454+ Loader {
455+ id: pickerLoader
456+ asynchronous: false
457+ width: parent.width
458+ }
459+
460+ UbuntuTestCase {
461+ name: "DatePicker13API"
462+ when: windowShown
463+
464+ readonly property DatePicker picker: pickerLoader.item
465+
466+ function init() {
467+ pickerLoader.sourceComponent = testComponent;
468+ tryCompareFunction(function(){return pickerLoader.status}, Loader.Ready);
469+ waitPickerMoving();
470+ }
471+ function cleanup() {
472+ pickerLoader.sourceComponent = undefined;
473+ }
474+ function waitPickerMoving() {
475+ waitForRendering(picker);
476+ tryCompareFunction(function(){return picker.moving}, false);
477+ }
478+
479+ function getPickerLabel(picker, name) {
480+ var pickerItem = findChild(picker, name);
481+ var pickerCurrent = findChild(pickerItem, "Picker_ViewLoader");
482+ // note: find Label, Picker's label uses medium font size. The lookup must be changed
483+ // if the fontSize is changed!
484+ var pickerLabel = findChildWithProperty(pickerCurrent.item.currentItem, "fontSize", "medium");
485+ verify(pickerLabel, ("Label of %1 not accessible").arg(name));
486+ return pickerLabel;
487+ }
488+ function getPickerModel(picker, name) {
489+ var pickerItem = findInvisibleChild(picker, name);
490+ return pickerItem ? pickerItem.model : undefined;
491+ }
492+ function setHMS(date, h, m, s) {
493+ date.setHours(h);
494+ date.setMinutes(m);
495+ date.setSeconds(s);
496+ return date;
497+ }
498+
499+ function initTestCase() {
500+ pickerLoader.sourceComponent = testComponent;
501+ tryCompareFunction(function(){return pickerLoader.status}, Loader.Ready);
502+ compare(picker.mode, "Years|Months|Days", "default mode");
503+ var date = (new Date()).midnight();
504+ compare(picker.date, date, "default mode");
505+ compare(picker.year, date.getFullYear(), "default year");
506+ compare(picker.month, date.getMonth(), "default month");
507+ compare(picker.day, date.getDate(), "default day");
508+ compare(picker.week, date.getWeek(), "default week");
509+ compare(picker.hours, date.getHours(), "default hour");
510+ compare(picker.minutes, date.getMinutes(), "default minute");
511+ compare(picker.seconds, date.getSeconds(), "default second");
512+ var endDate = Date.prototype.midnight.call(new Date());
513+ endDate.setFullYear(endDate.getFullYear() + 50);
514+ compare(picker.minimum, picker.date, "default minimum is same as date property");
515+ compare(picker.maximum, endDate, "default maximum is a 50 years window");
516+ compare(picker.locale, Qt.locale(), "default locale is the same as system locale");
517+ }
518+
519+ function test_1_changeModeYM() {
520+ var newMode = "Years|Months";
521+ var pickerCount = 2 + 1; // +1 is the Repeater
522+ picker.mode = newMode;
523+ waitPickerMoving();
524+ var positioner = findChild(picker, "PickerRow_Positioner");
525+ compare(positioner.children.length, pickerCount, "invalid amount of pickers");
526+ }
527+
528+ function test_1_changeModeMD() {
529+ var newMode = "Days|Months";
530+ var pickerCount = 2 + 1; // +1 is the Repeater
531+ picker.mode = newMode;
532+ waitPickerMoving();
533+ var positioner = findChild(picker, "PickerRow_Positioner");
534+ compare(positioner.children.length, pickerCount, "invalid amount of pickers");
535+ }
536+
537+ function test_1_changeModeYD() {
538+ ignoreWarning('Invalid DatePicker mode: Years|Days')
539+ var newMode = "Years|Days";
540+ var pickerCount = 2 + 1; // +1 is the Repeater
541+ picker.mode = newMode;
542+ // no rendering is expected, no need to wait
543+ var positioner = findChild(picker, "PickerRow_Positioner");
544+ expectFailContinue("", "Invalid mode");
545+ compare(positioner.children.length, pickerCount, "invalid amount of pickers");
546+ }
547+
548+ function test_1_changeModeY() {
549+ var newMode = "Years";
550+ var pickerCount = 1 + 1; // +1 is the Repeater
551+ picker.mode = newMode;
552+ waitPickerMoving();
553+ var positioner = findChild(picker, "PickerRow_Positioner");
554+ compare(positioner.children.length, pickerCount, "invalid amount of pickers");
555+ }
556+
557+ function test_1_changeModeM() {
558+ var newMode = "Months";
559+ var pickerCount = 1 + 1; // +1 is the Repeater
560+ picker.mode = newMode;
561+ waitPickerMoving();
562+ var positioner = findChild(picker, "PickerRow_Positioner");
563+ compare(positioner.children.length, pickerCount, "invalid amount of pickers");
564+ }
565+
566+ function test_1_changeModeD() {
567+ var newMode = "Days";
568+ var pickerCount = 1 + 1; // +1 is the Repeater
569+ picker.mode = newMode;
570+ waitPickerMoving();
571+ var positioner = findChild(picker, "PickerRow_Positioner");
572+ compare(positioner.children.length, pickerCount, "invalid amount of pickers");
573+ }
574+
575+ function test_1_changeModeHMS() {
576+ var newMode = "Hours|Minutes|Seconds";
577+ var pickerCount = 3 + 1; // +1 is the Repeater
578+ picker.mode = newMode;
579+ waitPickerMoving();
580+ var positioner = findChild(picker, "PickerRow_Positioner");
581+ compare(positioner.children.length, pickerCount, "invalid amount of pickers");
582+ }
583+
584+ function test_1_changeModeHM() {
585+ var newMode = "Hours|Minutes";
586+ var pickerCount = 2 + 1; // +1 is the Repeater
587+ picker.mode = newMode;
588+ waitPickerMoving();
589+ var positioner = findChild(picker, "PickerRow_Positioner");
590+ compare(positioner.children.length, pickerCount, "invalid amount of pickers");
591+ }
592+
593+ function test_1_changeModeMS() {
594+ var newMode = "Minutes|Seconds";
595+ var pickerCount = 2 + 1; // +1 is the Repeater
596+ picker.mode = newMode;
597+ waitPickerMoving();
598+ var positioner = findChild(picker, "PickerRow_Positioner");
599+ compare(positioner.children.length, pickerCount, "invalid amount of pickers");
600+ }
601+
602+ function test_1_changeModeHS() {
603+ ignoreWarning('Invalid DatePicker mode: Hours|Seconds')
604+ var newMode = "Hours|Seconds";
605+ var pickerCount = 2 + 1; // +1 is the Repeater
606+ picker.mode = newMode;
607+ waitPickerMoving();
608+ var positioner = findChild(picker, "PickerRow_Positioner");
609+ expectFailContinue("", "cannot set mode to Hours|Minutes");
610+ compare(positioner.children.length, pickerCount, "invalid amount of pickers");
611+ }
612+
613+ function test_1_changeModeH() {
614+ var newMode = "Hours";
615+ var pickerCount = 1 + 1; // +1 is the Repeater
616+ picker.mode = newMode;
617+ waitPickerMoving();
618+ var positioner = findChild(picker, "PickerRow_Positioner");
619+ compare(positioner.children.length, pickerCount, "invalid amount of pickers");
620+ }
621+
622+ function test_1_changeModeMinute() {
623+ var newMode = "Minutes";
624+ var pickerCount = 1 + 1; // +1 is the Repeater
625+ picker.mode = newMode;
626+ waitPickerMoving();
627+ var positioner = findChild(picker, "PickerRow_Positioner");
628+ compare(positioner.children.length, pickerCount, "invalid amount of pickers");
629+ }
630+
631+ function test_1_changeModeS() {
632+ var newMode = "Seconds";
633+ var pickerCount = 1 + 1; // +1 is the Repeater
634+ picker.mode = newMode;
635+ waitPickerMoving();
636+ var positioner = findChild(picker, "PickerRow_Positioner");
637+ compare(positioner.children.length, pickerCount, "invalid amount of pickers");
638+ }
639+
640+ function test_1_changeModeYMDHMS() {
641+ ignoreWarning('Date and Time picking not allowed at the same time.')
642+ var newMode = "Years|Months|Days|Hours|Minutes|Seconds";
643+ var pickerCount = 6 + 1; // +1 is the Repeater
644+ picker.mode = newMode;
645+ waitPickerMoving();
646+ var positioner = findChild(picker, "PickerRow_Positioner");
647+ expectFailContinue("", "cannot combine date and time pickers");
648+ compare(positioner.children.length, pickerCount, "invalid amount of pickers");
649+ }
650+
651+ function test_1_changeModeYH() {
652+ ignoreWarning('Date and Time picking not allowed at the same time.')
653+ var newMode = "Years|Hours";
654+ var pickerCount = 2 + 1; // +1 is the Repeater
655+ picker.mode = newMode;
656+ waitPickerMoving();
657+ var positioner = findChild(picker, "PickerRow_Positioner");
658+ expectFailContinue("", "cannot combine date and time pickers");
659+ compare(positioner.children.length, pickerCount, "invalid amount of pickers");
660+ }
661+
662+ function test_1_changeModeUnhandled() {
663+ ignoreWarning('Unhandled mode flag: Whatever. Mode will not be set!')
664+ var newMode = "Years|Whatever";
665+ var pickerCount = 2 + 1; // +1 is the Repeater
666+ picker.mode = newMode;
667+ // no rendering is expected, no need to wait
668+ var positioner = findChild(picker, "PickerRow_Positioner");
669+ expectFailContinue("", "unhandled mode flag should not pass");
670+ compare(positioner.children.length, pickerCount, "invalid amount of pickers");
671+ }
672+
673+ function test_1_changeLocale() {
674+ var prevLocale = picker.locale;
675+ var locale = Qt.locale("hu_HU");
676+ picker.minimum = new Date(2012, 11, 1);
677+ picker.date = new Date(2012, 11, 1);
678+ picker.locale = Qt.locale("hu_HU");
679+ waitPickerMoving();
680+ var label = getPickerLabel(picker, "PickerRow_MonthPicker");
681+ compare(label.text, locale.monthName(picker.date.getMonth(), Locale.LongFormat), "locale for month wrong");
682+
683+ label = getPickerLabel(picker, "PickerRow_DayPicker");
684+ var dayModel = getPickerModel(picker, "PickerRow_DayPicker");
685+ compare(label.text, dayModel.text(picker.date.getDate() - 1, testSuite.width), "locale for day name wrong");
686+ }
687+
688+ function test_1_changeMinimumBeforeDate() {
689+ var date = new Date(picker.date);
690+ var originalDate = new Date(date);
691+ date.setFullYear(date.getFullYear() - 1);
692+ date.setDate(1);
693+ picker.minimum = date;
694+ // no rendering is expected, so no need to wait
695+
696+ var year = getPickerLabel(picker, "PickerRow_YearPicker");
697+ compare(year.text, originalDate.getFullYear().toString(), "year differs");
698+ var month = getPickerLabel(picker, "PickerRow_MonthPicker");
699+ compare(month.text, picker.locale.monthName(originalDate.getMonth(), Locale.LongFormat), "month differs");
700+ var day = getPickerLabel(picker, "PickerRow_DayPicker");
701+ var dayModel = getPickerModel(picker, "PickerRow_DayPicker");
702+ compare(day.text, dayModel.text(originalDate.getDate() - 1), "day differs");
703+ }
704+
705+ function test_1_changeMaximumAfterDate() {
706+ var date = new Date(picker.date);
707+ var originalDate = new Date(date);
708+ date.setFullYear(date.getFullYear() + 1);
709+ date.setDate(1);
710+ picker.maximum = date;
711+ waitPickerMoving();
712+ var year = getPickerLabel(picker, "PickerRow_YearPicker");
713+ compare(year.text, originalDate.getFullYear().toString(), "year differs");
714+ var month = getPickerLabel(picker, "PickerRow_MonthPicker");
715+ compare(month.text, picker.locale.monthName(originalDate.getMonth(), Locale.LongFormat), "month differs");
716+ var day = getPickerLabel(picker, "PickerRow_DayPicker");
717+ var dayModel = getPickerModel(picker, "PickerRow_DayPicker");
718+ compare(day.text, dayModel.text(originalDate.getDate() - 1), "day differs");
719+ }
720+
721+ // make infinite
722+ function test_1_changeMinimumInvalid() {
723+ picker.minimum = Date.prototype.getInvalidDate.call();
724+ // no rendering is expected
725+ compare(picker.minimum, picker.date, "invalid minimum hasn't been adjusted to date");
726+ }
727+
728+ // make infinite
729+ function test_1_changeMaximumInvalid() {
730+ picker.maximum = Date.prototype.getInvalidDate.call();
731+ waitPickerMoving();
732+ // check if the year picker model is autoExtending
733+ var yearModel = getPickerModel(picker, "PickerRow_YearPicker");
734+ compare(yearModel.autoExtend, true, "the year picker is not auto-extending one");
735+ }
736+
737+ function test_1_changeDate() {
738+ var date = new Date();
739+ date.setFullYear(picker.date.getFullYear() + 2);
740+ date.setMonth(5);
741+ date.setDate(21);
742+ picker.date = date;
743+ picker.mode = "Years|Months|Days";
744+ waitPickerMoving();
745+
746+ var yearLabel = getPickerLabel(picker, "PickerRow_YearPicker", date.getFullYear());
747+ var monthLabel = getPickerLabel(picker, "PickerRow_MonthPicker");
748+ var monthModel = getPickerModel(picker, "PickerRow_MonthPicker");
749+ var dayLabel = getPickerLabel(picker, "PickerRow_DayPicker");
750+ var dayModel = getPickerModel(picker, "PickerRow_DayPicker");
751+ compare(yearLabel.text, date.getFullYear().toString(), "different year value");
752+ compare(monthLabel.text, monthModel.text(date.getMonth()), "different month value");
753+ compare(dayLabel.text, dayModel.text(date.getDate() - 1), "different day value");
754+ }
755+
756+ function test_1_changeDateToNextMonth() {
757+ picker.minimum = new Date(2013, 9, 1);
758+ picker.date = new Date(2013, 09, 31);
759+ picker.locale = Qt.locale("hu_HU")
760+ waitPickerMoving();
761+
762+ // click on the month picker to set the next month
763+ var monthPicker = findChild(picker, "PickerRow_MonthPicker");
764+ var monthCurrent = findChild(monthPicker, "Picker_ViewLoader");
765+ var my = monthPicker.y + (monthPicker.height / 2) + monthCurrent.item.currentItem.height;
766+ var mx = monthPicker.x + monthPicker.width / 2;
767+ mouseClick(picker, mx, my);
768+ waitPickerMoving();
769+
770+ var yearLabel = getPickerLabel(picker, "PickerRow_YearPicker");
771+ var monthLabel = getPickerLabel(picker, "PickerRow_MonthPicker");
772+ var monthModel = getPickerModel(picker, "PickerRow_MonthPicker");
773+ var dayLabel = getPickerLabel(picker, "PickerRow_DayPicker");
774+ var dayModel = getPickerModel(picker, "PickerRow_DayPicker");
775+ compare(yearLabel.text, "2013", "different year value");
776+ // November
777+ compare(monthLabel.text, monthModel.text(10), "different month value");
778+ // the 30th
779+ compare(dayLabel.text, dayModel.text(29), "different day value");
780+
781+ // set it back
782+ my = monthPicker.y + (monthPicker.height / 2) - monthCurrent.item.currentItem.height;
783+ mouseClick(picker, mx, my);
784+ waitPickerMoving();
785+
786+ compare(yearLabel.text, "2013", "different year value");
787+ // October
788+ monthLabel = getPickerLabel(picker, "PickerRow_MonthPicker");
789+ compare(monthLabel.text, monthModel.text(9), "different month value");
790+ // the 30th
791+ dayLabel = getPickerLabel(picker, "PickerRow_DayPicker");
792+ compare(dayLabel.text, dayModel.text(29), "different day value");
793+ }
794+
795+ function test_2_disabledYear() {
796+ var date = new Date(2013, 4, 1);
797+ var minDate = new Date(2013, 0, 1);
798+ var maxDate = new Date(2013, 11, 31);
799+ picker.minimum = minDate;
800+ picker.maximum = maxDate;
801+ waitPickerMoving();
802+ picker.date = date;
803+ waitPickerMoving();
804+
805+ var yearPicker = findChild(picker, "PickerRow_YearPicker");
806+ compare(yearPicker.enabled, false, "year picker should be disabled");
807+ }
808+
809+ function test_2_disabledYearAndMonth() {
810+ var date = new Date(2013, 11, 1);
811+ var minDate = new Date(2013, 11, 1);
812+ var maxDate = new Date(2013, 11, 31);
813+ picker.minimum = minDate;
814+ picker.maximum = maxDate;
815+ picker.date = date;
816+ waitPickerMoving();
817+
818+ var yearPicker = findChild(picker, "PickerRow_YearPicker");
819+ compare(yearPicker.enabled, false, "year picker should be disabled");
820+ var monthPicker = findChild(picker, "PickerRow_MonthPicker");
821+ compare(monthPicker.enabled, false, "month picker should be disabled");
822+ }
823+
824+ function test_2_linearDayPicker() {
825+ var date = new Date(2013, 11, 1);
826+ var minDate = new Date(2013, 11, 2);
827+ var maxDate = new Date(2013, 11, 31);
828+ picker.minimum = minDate;
829+ picker.maximum = maxDate;
830+ picker.date = date;
831+
832+ var yearPicker = findChild(picker, "PickerRow_YearPicker");
833+ compare(yearPicker.enabled, false, "year picker should be disabled");
834+ var monthPicker = findChild(picker, "PickerRow_MonthPicker");
835+ compare(monthPicker.enabled, false, "month picker should be disabled");
836+ var dayPickerModel = getPickerModel(picker, "PickerRow_DayPicker");
837+ expectFailContinue("", "day picker is always circular");
838+ compare(dayPickerModel.circular, false, "day picker should be linear");
839+ }
840+
841+ function test_3_changeHours() {
842+ picker.mode = "Hours|Minutes|Seconds";
843+ waitPickerMoving();
844+ var date = new Date(picker.date);
845+ date.setHours((date.getHours() + 10) % 24);
846+ picker.date = date;
847+ waitPickerMoving();
848+
849+ var hoursLabel = getPickerLabel(picker, "PickerRow_HoursPicker");
850+ verify(hoursLabel, "hour label undefined");
851+ compare(hoursLabel.text, ("00" + date.getHours()).slice(-2), "hours differ");
852+ var minutesLabel = getPickerLabel(picker, "PickerRow_MinutesPicker");
853+ verify(minutesLabel, "minutes label undefined");
854+ compare(minutesLabel.text, ("00" + date.getMinutes()).slice(-2), "minutes differ");
855+ var secondsLabel = getPickerLabel(picker, "PickerRow_SecondsPicker");
856+ verify(secondsLabel, "seconds label undefined");
857+ compare(secondsLabel.text, ("00" + date.getSeconds()).slice(-2), "seconds differ");
858+ }
859+
860+ function test_3_changeMinutes() {
861+ picker.mode = "Hours|Minutes|Seconds";
862+ var date = new Date(picker.date);
863+ date.setMinutes((date.getMinutes() + 40) % 60);
864+ picker.date = date;
865+ waitPickerMoving();
866+
867+ var hoursLabel = getPickerLabel(picker, "PickerRow_HoursPicker");
868+ verify(hoursLabel, "hour label undefined");
869+ compare(hoursLabel.text, ("00" + date.getHours()).slice(-2), "hours differ");
870+ var minutesLabel = getPickerLabel(picker, "PickerRow_MinutesPicker");
871+ verify(minutesLabel, "minutes label undefined");
872+ compare(minutesLabel.text, ("00" + date.getMinutes()).slice(-2), "minutes differ");
873+ var secondsLabel = getPickerLabel(picker, "PickerRow_SecondsPicker");
874+ verify(secondsLabel, "seconds label undefined");
875+ compare(secondsLabel.text, ("00" + date.getSeconds()).slice(-2), "seconds differ");
876+ }
877+
878+ function test_3_changeSeconds() {
879+ picker.mode = "Hours|Minutes|Seconds";
880+ var date = new Date(picker.date);
881+ date.setSeconds((date.getSeconds() + 50) % 60);
882+ picker.date = date;
883+ waitPickerMoving();
884+
885+ var hoursLabel = getPickerLabel(picker, "PickerRow_HoursPicker");
886+ verify(hoursLabel, "hour label undefined");
887+ compare(hoursLabel.text, ("00" + date.getHours()).slice(-2), "hours differ");
888+ var minutesLabel = getPickerLabel(picker, "PickerRow_MinutesPicker");
889+ verify(minutesLabel, "minutes label undefined");
890+ compare(minutesLabel.text, ("00" + date.getMinutes()).slice(-2), "minutes differ");
891+ var secondsLabel = getPickerLabel(picker, "PickerRow_SecondsPicker");
892+ verify(secondsLabel, "seconds label undefined");
893+ compare(secondsLabel.text, ("00" + date.getSeconds()).slice(-2), "seconds differ");
894+ }
895+
896+ function test_4_changeMinimumBeforeDateHMS() {
897+ picker.mode = "Hours|Minutes|Seconds";
898+ var date = new Date(picker.date);
899+ var originalDate = new Date(date);
900+ date.setFullYear(date.getFullYear() - 1);
901+ date.setDate(1);
902+ picker.minimum = date;
903+ waitPickerMoving();
904+
905+ var hoursLabel = getPickerLabel(picker, "PickerRow_HoursPicker");
906+ verify(hoursLabel, "hour label undefined");
907+ compare(hoursLabel.text, ("00" + originalDate.getHours()).slice(-2), "hours differ");
908+ var minutesLabel = getPickerLabel(picker, "PickerRow_MinutesPicker");
909+ verify(minutesLabel, "minutes label undefined");
910+ compare(minutesLabel.text, ("00" + originalDate.getMinutes()).slice(-2), "minutes differ");
911+ var secondsLabel = getPickerLabel(picker, "PickerRow_SecondsPicker");
912+ verify(secondsLabel, "seconds label undefined");
913+ compare(secondsLabel.text, ("00" + originalDate.getSeconds()).slice(-2), "seconds differ");
914+ }
915+
916+ function test_4_changeMaximumAfterDateHMS() {
917+ picker.mode = "Hours|Minutes|Seconds";
918+ var date = new Date(picker.date);
919+ var originalDate = new Date(date);
920+ date.setFullYear(date.getFullYear() + 1);
921+ date.setDate(1);
922+ picker.maximum = date;
923+ waitPickerMoving();
924+ var hoursLabel = getPickerLabel(picker, "PickerRow_HoursPicker");
925+ verify(hoursLabel, "hour label undefined");
926+ compare(hoursLabel.text, ("00" + originalDate.getHours()).slice(-2), "hours differ");
927+ var minutesLabel = getPickerLabel(picker, "PickerRow_MinutesPicker");
928+ verify(minutesLabel, "minutes label undefined");
929+ compare(minutesLabel.text, ("00" + originalDate.getMinutes()).slice(-2), "minutes differ");
930+ var secondsLabel = getPickerLabel(picker, "PickerRow_SecondsPicker");
931+ verify(secondsLabel, "seconds label undefined");
932+ compare(secondsLabel.text, ("00" + originalDate.getSeconds()).slice(-2), "seconds differ");
933+ }
934+
935+ function test_4_disabledHour() {
936+ picker.mode = "Hours|Minutes|Seconds";
937+ var date = setHMS(new Date(), 12, 10, 45);
938+ var minDate = setHMS(new Date(), 12, 0, 0);
939+ var maxDate = setHMS(new Date(), 12, 59, 59);
940+ picker.minimum = minDate;
941+ picker.maximum = maxDate;
942+ waitPickerMoving();
943+ picker.date = date;
944+
945+ var hoursPicker = findChild(picker, "PickerRow_HoursPicker");
946+ compare(hoursPicker.enabled, false, "hours picker should be disabled");
947+ }
948+
949+ function test_4_disabledHoursAndMinutes() {
950+ picker.mode = "Hours|Minutes|Seconds";
951+ var date = setHMS(new Date(), 12, 10, 45);
952+ var minDate = setHMS(new Date(), 12, 10, 0);
953+ var maxDate = setHMS(new Date(), 12, 10, 59);
954+ picker.minimum = minDate;
955+ picker.maximum = maxDate;
956+ picker.date = date;
957+ waitPickerMoving();
958+
959+ var hoursPicker = findChild(picker, "PickerRow_HoursPicker");
960+ compare(hoursPicker.enabled, false, "hours picker should be disabled");
961+ var minutesPicker = findChild(picker, "PickerRow_MinutesPicker");
962+ compare(minutesPicker.enabled, false, "minutes picker should be disabled");
963+ }
964+
965+ function test_4_linearSecondsPicker() {
966+ // skip the test temporaily
967+ // https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1315241
968+ skip("Disabled due to flakyness on ppc64el target");
969+ picker.mode = "Hours|Minutes|Seconds";
970+ var date = setHMS(new Date(), 12, 10, 45);
971+ var minDate = setHMS(new Date(), 12, 10, 1);
972+ var maxDate = setHMS(new Date(), 12, 10, 59);
973+ picker.minimum = minDate;
974+ picker.maximum = maxDate;
975+ picker.date = date;
976+ waitPickerMoving();
977+
978+ var hoursPicker = findChild(picker, "PickerRow_HoursPicker");
979+ compare(hoursPicker.enabled, false, "hours picker should be disabled");
980+ var minutesPicker = findChild(picker, "PickerRow_MinutesPicker");
981+ compare(minutesPicker.enabled, false, "minutes picker should be disabled");
982+ var secondsPickerModel = getPickerModel(picker, "PickerRow_SecondsPicker");
983+ tryCompare(secondsPickerModel, "resetting", false);
984+ tryCompare(secondsPickerModel, "count", maxDate.getSeconds() - minDate.getSeconds() + 1);
985+ compare(secondsPickerModel.circular, false, "day picker should be linear");
986+ }
987+
988+ }
989+}
990
991=== added file 'tests/unit_x11/tst_components/tst_picker13.qml'
992--- tests/unit_x11/tst_components/tst_picker13.qml 1970-01-01 00:00:00 +0000
993+++ tests/unit_x11/tst_components/tst_picker13.qml 2015-09-24 20:06:01 +0000
994@@ -0,0 +1,334 @@
995+/*
996+ * Copyright 2015 Canonical Ltd.
997+ *
998+ * This program is free software; you can redistribute it and/or modify
999+ * it under the terms of the GNU Lesser General Public License as published by
1000+ * the Free Software Foundation; version 3.
1001+ *
1002+ * This program is distributed in the hope that it will be useful,
1003+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1004+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1005+ * GNU Lesser General Public License for more details.
1006+ *
1007+ * You should have received a copy of the GNU Lesser General Public License
1008+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1009+ */
1010+
1011+import QtQuick 2.4
1012+import QtTest 1.0
1013+import Ubuntu.Test 1.0
1014+import Ubuntu.Components 1.3
1015+import Ubuntu.Components.Pickers 1.3
1016+
1017+Item {
1018+ id: testSuite
1019+ width: units.gu(40)
1020+ height: units.gu(71)
1021+
1022+ Component {
1023+ id: linearShort
1024+ Picker {
1025+ objectName: "linear"
1026+ circular: false
1027+ model: objectModel
1028+ delegate: PickerDelegate {
1029+ Label {text: modelData}
1030+ }
1031+ }
1032+ }
1033+ Component {
1034+ id: linearLong
1035+ Picker {
1036+ objectName: "linear"
1037+ circular: false
1038+ model: longerModel
1039+ delegate: PickerDelegate {
1040+ Label {text: modelData}
1041+ }
1042+ }
1043+ }
1044+ Component {
1045+ id: objectModelled
1046+ Picker {
1047+ objectName: "objectModelled"
1048+ model: objectModel
1049+ selectedIndex: 2
1050+ delegate: PickerDelegate {
1051+ Label {text: modelData}
1052+ }
1053+ }
1054+ }
1055+ Component {
1056+ id: simpleModelled
1057+ Picker {
1058+ objectName: "simpleModelled"
1059+ model: emptyModel
1060+ selectedIndex: 2
1061+ delegate: PickerDelegate {
1062+ Label {text: modelData}
1063+ }
1064+ }
1065+ }
1066+ Component {
1067+ id: linearDynPicker
1068+ Picker {
1069+ objectName: "linearDynPicker"
1070+ model: dynamicModel
1071+ circular: false
1072+ delegate: PickerDelegate {
1073+ Label {text: modelData}
1074+ }
1075+ }
1076+ }
1077+ Component {
1078+ id: circularDynPicker
1079+ Picker {
1080+ objectName: "circularDynPicker"
1081+ model: dynamicModel
1082+ circular: true
1083+ delegate: PickerDelegate {
1084+ Label { text: modelData ? modelData : "" }
1085+ }
1086+ }
1087+ }
1088+ Component {
1089+ id: defaultPicker
1090+ Picker {
1091+ }
1092+ }
1093+
1094+ Loader {
1095+ id: pickerLoader
1096+ asynchronous: false
1097+ }
1098+
1099+ ListModel {
1100+ id: emptyModel
1101+ }
1102+
1103+ ListModel {
1104+ id: dynamicModel
1105+ Component.onCompleted: reset()
1106+ function reset() {
1107+ clear();
1108+ for (var i = 0; i < 100; i++) {
1109+ append({"label": "line" + i});
1110+ }
1111+ }
1112+ }
1113+
1114+ ListModel {
1115+ id: objectModel
1116+ ListElement {
1117+ label: "line1"
1118+ }
1119+ ListElement {
1120+ label: "line2"
1121+ }
1122+ ListElement {
1123+ label: "line3"
1124+ }
1125+ }
1126+
1127+ ListModel {
1128+ id: longerModel
1129+ ListElement {
1130+ label: "line1"
1131+ }
1132+ ListElement {
1133+ label: "line2"
1134+ }
1135+ ListElement {
1136+ label: "line3"
1137+ }
1138+ ListElement {
1139+ label: "line4"
1140+ }
1141+ ListElement {
1142+ label: "line5"
1143+ }
1144+ ListElement {
1145+ label: "line6"
1146+ }
1147+ }
1148+
1149+ SignalSpy {
1150+ id: spy
1151+ signalName: "onSelectedIndexChanged"
1152+ }
1153+
1154+ UbuntuTestCase {
1155+ id: testCase
1156+ name: "Picker13API"
1157+ when: windowShown
1158+
1159+ function testPicker(component) {
1160+ pickerLoader.sourceComponent = component;
1161+ tryCompareFunction(function(){return pickerLoader.status}, Loader.Ready);
1162+ waitForRendering(pickerLoader.item);
1163+ tryCompareFunction(function(){return pickerLoader.item.moving}, false);
1164+ return pickerLoader.item;
1165+ }
1166+
1167+ function waitPickerScrolling(picker) {
1168+ tryCompareFunction(function(){return picker.moving}, false);
1169+ }
1170+
1171+ function getPickerList(picker) {
1172+ return findChild(picker, picker.circular ? "Picker_WrapAround" : "Picker_Linear");
1173+ }
1174+
1175+ function cleanup() {
1176+ pickerLoader.sourceComponent = null;
1177+ dynamicModel.reset();
1178+ spy.clear();
1179+ spy.target = null;
1180+ }
1181+
1182+ function initTestCase() {
1183+ var picker = testPicker(defaultPicker);
1184+ compare(picker.circular, true, "default circular");
1185+ compare(picker.model, undefined, "default model");
1186+ compare(picker.delegate, null, "default delegate");
1187+ compare(picker.selectedIndex, 0, "default selectedIndex");
1188+ compare(picker.itemHeight, units.gu(4), "default itemHeight");
1189+ compare(picker.__styleInstance.highlightThickness, units.gu(5), "default highlight thickness");
1190+ }
1191+
1192+ function test_selectedIndex_data() {
1193+ return [
1194+ {tag: "Runtime model", picker: defaultPicker, model: emptyModel, expectedIndex: 0},
1195+ {tag: "Empty modelled", picker: simpleModelled, expectedIndex: 0},
1196+ {tag: "Object modelled", picker: objectModelled, expectedIndex: 2},
1197+ ];
1198+ }
1199+ function test_selectedIndex(data) {
1200+ var picker = testPicker(data.picker);
1201+ if (data.nmodel) {
1202+ picker.model = data.model;
1203+ }
1204+ compare(picker.selectedIndex, data.expectedIndex, "unexpected selectedIndex");
1205+ }
1206+
1207+ function test_updateModel_data() {
1208+ return [
1209+ {tag: "Empty picker", picker: defaultPicker},
1210+ {tag: "Object modelled picker", picker: objectModelled},
1211+ ];
1212+ }
1213+ function test_updateModel(data) {
1214+ var picker = testPicker(data.picker);
1215+ picker.selectedIndex = 1;
1216+ spy.target = picker;
1217+ picker.model = [];
1218+ spy.wait();
1219+ }
1220+
1221+ function test_clickMovesSelection_data() {
1222+ return [
1223+ {tag: "Linear long", component: linearLong, circular: false, firstClick: 0, secondClick: 1},
1224+ {tag: "Circular long", component: linearLong, circular: true, firstClick: 1, secondClick: 2},
1225+ {tag: "Linear short", component: linearShort, circular: false, firstClick: 0, secondClick: 1},
1226+ {tag: "Circular short", component: linearShort, circular: true, firstClick: 0, secondClick: 1},
1227+ ];
1228+ }
1229+ function test_clickMovesSelection(data) {
1230+ var picker = testPicker(data.component);
1231+ spy.target = picker;
1232+ picker.circular = data.circular;
1233+
1234+ mouseClick(picker, units.gu(1), units.gu(1));
1235+ tryCompare(spy, "count", data.firstClick);
1236+ mouseClick(picker, units.gu(1), units.gu(18));
1237+ tryCompare(spy, "count", data.secondClick);
1238+ }
1239+
1240+ function test_pickerCircularChange() {
1241+ var picker = testPicker(defaultPicker);
1242+ var expectedList = picker.circular ? "Picker_WrapAround" : "Picker_Linear";
1243+ verify(findChild(picker, expectedList) !== undefined, "Picker must use " + expectedList);
1244+
1245+ picker.circular = !picker.circular;
1246+ expectedList = picker.circular ? "Picker_WrapAround" : "Picker_Linear";
1247+ verify(findChild(picker, expectedList) !== undefined, "circular changed, Picker must use " + expectedList);
1248+ }
1249+
1250+ function test_modelCropping_data() {
1251+ return [
1252+ {tag: "Linear, selected @50, remove from 40", component: linearDynPicker, selected: 50, remove: 40, removeCount: dynamicModel.count - 40, count: 40, expected: 39 },
1253+ {tag: "Linear, selected @50, remove 50", component: linearDynPicker, selected: 50, remove: 50, removeCount: 1, count: dynamicModel.count - 1, expected: 49 },
1254+ {tag: "Linear, selected @50, remove 10 from 45", component: linearDynPicker, selected: 50, remove: 45, removeCount: 10, count: dynamicModel.count - 10, expected: 44 },
1255+ {tag: "Linear, selected @50, remove 10 from 51", component: linearDynPicker, selected: 50, remove: 51, removeCount: 10, count: dynamicModel.count - 10, expected: 50 },
1256+
1257+ {tag: "Circular, selected @50, remove from 40", component: circularDynPicker, selected: 50, remove: 40, removeCount: dynamicModel.count - 40, count: 40, expected: 39 },
1258+ {tag: "Circular, selected @50, remove 50", component: circularDynPicker, selected: 50, remove: 50, removeCount: 1, count: dynamicModel.count - 1, expected: 49 },
1259+ {tag: "Circular, selected @50, remove 10 from 45", component: circularDynPicker, selected: 50, remove: 45, removeCount: 10, count: dynamicModel.count - 10, expected: 44 },
1260+ {tag: "Circular, selected @50, remove 10 from 51", component: circularDynPicker, selected: 50, remove: 51, removeCount: 10, count: dynamicModel.count - 10, expected: 50 },
1261+ ]
1262+ }
1263+
1264+ function test_modelCropping(data) {
1265+ var picker = testPicker(data.component);
1266+ picker.selectedIndex = data.selected;
1267+ waitPickerScrolling(picker);
1268+ var list = getPickerList(picker);
1269+
1270+ picker.model.remove(data.remove, data.removeCount);
1271+ waitPickerScrolling(picker);
1272+ compare(list.count, data.count, "bad removal from list");
1273+ compare(picker.selectedIndex, data.expected, "bad index of " + data.tag);
1274+ }
1275+
1276+ function test_modelReset_data() {
1277+ return [
1278+ {tag: "Linear", picker: linearDynPicker},
1279+ {tag: "Circular", picker: circularDynPicker},
1280+ ];
1281+ }
1282+ function test_modelReset(data) {
1283+ var picker = testPicker(data.picker);
1284+ var list = getPickerList(picker);
1285+ picker.model.reset();
1286+ waitPickerScrolling(picker);
1287+ compare(list.currentIndex, 0, "picker's itemList selection not reset");
1288+ compare(picker.selectedIndex, 0, "picker's selection not reset");
1289+ }
1290+
1291+ function test_modelClear_data() {
1292+ return [
1293+ {tag: "Linear", picker: linearDynPicker},
1294+ {tag: "Circular", picker: circularDynPicker},
1295+ ];
1296+ }
1297+ function test_modelClear(data) {
1298+ var picker = testPicker(data.picker);
1299+ var list = getPickerList(picker);
1300+ picker.model.clear();
1301+ if (data.tag == "Linear") {
1302+ // picker's ListView needs some events to be processed before it gets the final value for currentIndex
1303+ waitForRendering(list, 500);
1304+ }
1305+ waitPickerScrolling(picker);
1306+ if (data.tag == "Circular") {
1307+ expectFailContinue(data.tag, "PathView issue: https://bugreports.qt-project.org/browse/QTBUG-35400");
1308+ }
1309+ compare(list.currentIndex, -1, "picker's itemList selection not reset");
1310+ if (data.tag == "Circular") {
1311+ expectFailContinue(data.tag, "PathView issue: https://bugreports.qt-project.org/browse/QTBUG-35400");
1312+ }
1313+ compare(picker.selectedIndex, -1, "picker's selection not reset");
1314+ }
1315+
1316+ function test_itemHeight_data() {
1317+ return [
1318+ {tag: "Linear", picker: linearDynPicker, itemHeight: units.gu(6)},
1319+ {tag: "Circular", picker: circularDynPicker, itemHeight: units.gu(6)},
1320+ ];
1321+ }
1322+ function test_itemHeight(data) {
1323+ var picker = testPicker(data.picker);
1324+ picker.itemHeight = data.itemHeight;
1325+ compare(picker.__styleInstance.highlightThickness, picker.itemHeight + units.gu(1));
1326+ }
1327+ }
1328+}

Subscribers

People subscribed via source and target branches